diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..4d2bb184e7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,47 @@ +--- +BasedOnStyle: LLVM +# defaults from LLVM +# BreakBeforeBraces: Attach +# AllowShortIfStatementsOnASingleLine: Never +# IndentCaseLabels: false +# AlignAfterOpenBracket: Align +# AlignTrailingComments: true +# MaxEmptyLinesToKeep: 1 +# ReflowComments: falsedefault +# SortIncludes: true +# BreakBeforeBinaryOperators: None +# BraceWrapping: +# AfterClass: false +# AfterControlStatement: false +# AfterFunction: false +# AfterNamespace: false +# BeforeCatch: false +# BeforeElse: false +# IndentBraces: false + +# OpenSC modifications +TabWidth: 8 +IndentWidth: 8 +ContinuationIndentWidth: 16 +AlignAfterOpenBracket: DontAlign +UseTab: Always +AlignConsecutiveMacros: true +AlignEscapedNewlines: DontAlign +AllowShortFunctionsOnASingleLine: None +AlwaysBreakAfterReturnType: AllDefinitions +## This prevents reflowing intentionally short lines but +## it can be allowed only after we will have some baseline +ColumnLimit: 0 +#ColumnLimit: 110 +IndentCaseBlocks: false +AlignArrayOfStructures: Left + +BreakBeforeBraces: Custom +BraceWrapping: + AfterObjCDeclaration: true + AfterUnion: true + AfterFunction: true + +--- +Language: Json +DisableFormat: true diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..f7da0c085a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +root = true + +[*] +charset = utf-8 +max_line_length = 110 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{c,h,sh}] +indent_style = tab +tab_width = 8 + +[{Makefile.am,configure.ac}] +indent_style = tab +tab_width = 8 + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 diff --git a/.github/.codeql.yml b/.github/.codeql.yml new file mode 100644 index 0000000000..5f103122cd --- /dev/null +++ b/.github/.codeql.yml @@ -0,0 +1,7 @@ +name: "CodeQL configuration file" + +query-filters: + - exclude: + # This reports all the uses of the DES, but this is needed for + # interoperability with cards not supporting AES + id: cpp/weak-cryptographic-algorithm diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..99402d1592 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,103 @@ +name: Bug report +description: Report a problem or unexpected behavior in OpenSC +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for reporting a bug in OpenSC! + Please fill out the sections below to help us diagnose the issue. + See https://github.com/OpenSC/OpenSC/wiki/How-to-write-a-good-bug-report on what to include here. + + - type: textarea + id: description + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. + placeholder: What went wrong? + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: How can we reproduce the issue? + placeholder: | + 1. + 2. + 3. + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected behavior + description: What you expected to happen. + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual behavior + description: What actually happened. + validations: + required: true + + - type: input + id: opensc_version + attributes: + label: OpenSC version + placeholder: e.g. 0.25.1 + description: Only the latest release and development versions are supported. + validations: + required: true + + - type: input + id: os + attributes: + label: Operating system + placeholder: e.g. Ubuntu 22.04, Windows 11, macOS 14 + validations: + required: true + + - type: input + id: card + attributes: + label: Smart card model or security token + placeholder: Try `opensc-tool -n` if unkown + + - type: input + id: application + attributes: + label: Application used with smart card + placeholder: e.g. Firefox, Windows Logon, Apple Mail, ssh, ... + + - type: textarea + id: logs + attributes: + label: Logs and debug output + description: | + Please include relevant logs or debug output. + You can enable verbose output setting the environment variable `OPENSC_DEBUG` to `3`. + - `set OPENSC_DEBUG="3"` (Windows CMD) + - `export OPENSC_DEBUG="3"` (Unix shell) + + Useful commands for getting debug information are: + - `opensc-tool --atr --name -vvv` + - `pkcs11-tool --test --login` + - `certutil -scinfo` (Windows only) + - `sc_auth identities` (macOS only) + render: shell + + - type: checkboxes + id: checklist + attributes: + label: Checklist + options: + - label: I am using a supported OpenSC version + required: true + - label: I have searched existing issues to avoid duplicates + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..ced270e88c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false + +contact_links: + - name: Start a discussion + url: https://github.com/OpenSC/OpenSC/discussions/new/choose + about: | + Ask questions, start discussions, or propose ideas that don't belong in an issue. + + - name: Use the mailing lists + url: https://github.com/OpenSC/OpenSC/wiki/Mailing-lists + about: Write us an email or read the archive. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000000..e5b5601259 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,38 @@ +name: Feature request +description: Suggest an enhancement or new feature for OpenSC +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + Have an idea to improve OpenSC? We'd love to hear it. + + - type: textarea + id: problem + attributes: + label: Problem statement + description: What problem does this feature solve? + validations: + required: true + + - type: textarea + id: solution + attributes: + label: Proposed solution + description: Describe the solution you'd like to see. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Any alternative solutions or workarounds you've considered. + + - type: textarea + id: context + attributes: + label: Additional context + description: | + Related standards, card types, use cases, or references. + For new smart cards, include the output of `opensc-tool --atr --name -vvv` diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..6f8e139a7d --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,17 @@ + + +##### Checklist + +- [ ] Documentation is added or updated +- [ ] New files have a LGPL 2.1 license statement +- [ ] PKCS#11 module is tested +- [ ] Windows minidriver is tested +- [ ] macOS token is tested diff --git a/.github/add_signing_key.sh b/.github/add_signing_key.sh new file mode 100755 index 0000000000..1a90b2fd66 --- /dev/null +++ b/.github/add_signing_key.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +set -ex -o xtrace + +pushd .github/ +KEY_CHAIN=mac-build.keychain + +# Create the keychain with a password +security create-keychain -p travis $KEY_CHAIN + +# Make the custom keychain default, so xcodebuild will use it for signing +security default-keychain -s $KEY_CHAIN + +# Unlock the keychain for one hour +security unlock-keychain -p travis $KEY_CHAIN +security set-keychain-settings -t 3600 -u $KEY_CHAIN + +# Add certificates to keychain and allow codesign to access them +curl -L https://developer.apple.com/certificationauthority/AppleWWDRCA.cer > AppleWWDRCA.cer +security import AppleWWDRCA.cer \ + -k ~/Library/Keychains/$KEY_CHAIN \ + -T /usr/bin/codesign -T /usr/bin/productsign +security import DeveloperIDApplication.p12 \ + -k ~/Library/Keychains/$KEY_CHAIN -P $KEY_PASSWORD \ + -T /usr/bin/codesign -T /usr/bin/productsign +security import DeveloperIDInstaller.p12 \ + -k ~/Library/Keychains/$KEY_CHAIN -P $KEY_PASSWORD \ + -T /usr/bin/codesign -T /usr/bin/productsign +security unlock-keychain -p travis $KEY_CHAIN + +# https://docs.travis-ci.com/user/common-build-problems/#mac-macos-sierra-1012-code-signing-errors +security set-key-partition-list -S apple-tool:,apple: -s -k travis $KEY_CHAIN +popd diff --git a/.github/build.sh b/.github/build.sh new file mode 100755 index 0000000000..0fad7fc550 --- /dev/null +++ b/.github/build.sh @@ -0,0 +1,129 @@ +#!/bin/bash + +set -ex -o xtrace + +export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig; + +if [ -x "/bin/sudo" ]; then + SUDO="sudo" +fi + +SUFFIX="-${GITHUB_SHA:0:7}" +if [ "$GITHUB_REF_TYPE" == "tag" ]; then + if [[ "$GITHUB_REF_NAME" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + # Tag matches the version scheme without suffix -- no suffix needed + SUFFIX="" + elif [[ "$GITHUB_REF_NAME" =~ ^[0-9]+\.[0-9]+\.[0-9]+(.+)$ ]]; then + # rc suffix after version. Use the suffix part only + SUFFIX="${BASH_REMATCH[1]}" + fi +fi +if [ -n "$SUFFIX" ]; then + ./bootstrap.ci -s "$SUFFIX" +else + ./bootstrap +fi + +if [ "$RUNNER_OS" == "macOS" ]; then + if [ "$1" == "libressl" ]; then + export OPENSSL_LIBS="-L/opt/homebrew/opt/libressl/lib -lcrypto" + export OPENSSL_CFLAGS="-I/opt/homebrew/opt/libressl/include" + else + ./MacOSX/build + exit $? + fi +fi + +if [ "$1" == "mingw" -o "$1" == "mingw32" ]; then + mkdir -p src/minidriver/CNG + wget https://raw.githubusercontent.com/open-eid/minidriver/master/cardmod.h -O src/minidriver/CNG/cardmod.h + if [ "$1" == "mingw" ]; then + HOST=x86_64-w64-mingw32 + elif [ "$1" == "mingw32" ]; then + HOST=i686-w64-mingw32 + fi + unset CC + unset CXX + CFLAGS="-I$PWD/src/minidriver/CNG -Wno-error=unknown-pragmas" \ + CPPFLAGS="-DNTDDI_VERSION=0x06010000" \ + ./configure --host=$HOST --with-completiondir=/tmp --disable-openssl --disable-readline --disable-zlib --enable-minidriver --enable-notify --prefix=$PWD/win32/opensc || cat config.log; + make -j 4 V=1 + # no point in running tests on mingw +else + if [ "$1" == "ix86" ]; then + export CFLAGS="-m32" + export LDFLAGS="-m32" + fi + if [ "$1" == "fips" ]; then + export OPENSSL_FORCE_FIPS_MODE=1 + fi + # normal procedure + + CONFIGURE_FLAGS="--disable-dependency-tracking" + if [ "$RUNNER_OS" != "macOS" ]; then + CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-doc" + fi + if [ "$1" != "clang-tidy" ]; then + CONFIGURE_FLAGS="$CONFIGURE_FLAGS CLANGTIDY=/bin/no-clang-tidy" + fi + if [ "$1" == "piv-sm" ]; then + CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-piv-sm" + fi + if [ "$1" == "valgrind" -o "$2" == "valgrind" ]; then + CONFIGURE_FLAGS="$CONFIGURE_FLAGS --disable-notify --enable-valgrind --disable-integration-tests" + fi + if [ "$1" == "no-shared" ]; then + CONFIGURE_FLAGS="$CONFIGURE_FLAGS --disable-shared" + fi + if [ "$1" == "no-openssl" ]; then + CONFIGURE_FLAGS="$CONFIGURE_FLAGS --disable-openssl" + fi + if [ "$1" == "no-readers" ]; then + CONFIGURE_FLAGS="$CONFIGURE_FLAGS --disable-pcsc --disable-cryptotokenkit --disable-openct --disable-ctapi" + fi + export CFLAGS="-DDEBUG_PROFILE=1 $CFLAGS" + ./configure $CONFIGURE_FLAGS + make -j 4 V=1 + # 32b build has some issues to find openssl correctly + if [ "$1" == "valgrind" ]; then + set +e + make check-valgrind-memcheck + RV=$? + if [ $RV -ne 0 ]; then + ./.github/dump-logs.sh + exit $RV + fi + set -e + elif [ "$1" != "ix86" ]; then + set +e + make check + RV=$? + if [ $RV -ne 0 ]; then + ./.github/dump-logs.sh + exit $RV + fi + set -e + fi +fi + +# this is broken in old ubuntu +if [ "$1" == "dist" -o "$2" == "dist" ]; then + set +e + DISTCHECK_CONFIGURE_FLAGS="$CONFIGURE_FLAGS" + make distcheck + RV=$? + if [ $RV -ne 0 ]; then + ./.github/dump-logs.sh $SUFFIX + exit $RV + fi + set -e + make dist +fi + +if [ "$RUNNER_OS" != "macOS" ]; then + $SUDO make install +fi +if [ "$1" == "mingw" -o "$1" == "mingw32" ]; then + # pack installed files + wine "C:/Program Files/Inno Setup 5/ISCC.exe" win32/OpenSC.iss +fi diff --git a/.github/cleanup-macos.sh b/.github/cleanup-macos.sh new file mode 100755 index 0000000000..67b1d9f82d --- /dev/null +++ b/.github/cleanup-macos.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex -o xtrace + +if [ -n "$KEY_PASSWORD" ]; then + .github/remove_signing_key.sh +fi diff --git a/.github/codespell_ignore_words.txt b/.github/codespell_ignore_words.txt new file mode 100644 index 0000000000..96efc88620 --- /dev/null +++ b/.github/codespell_ignore_words.txt @@ -0,0 +1,23 @@ +ans +ba +comandos +componentes +datas +direccion +ede +fase +feld +gost +hist +ist +keypair +nmake +oder +parm +parms +requiere +responde +sie +signatur +standarts +algorithmus diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..251ff26b4a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + # https://learn.microsoft.com/en-us/vcpkg/concepts/dependabot + - package-ecosystem: "vcpkg" + directory: "/.github/" # Location of vcpkg.json file + schedule: + interval: "weekly" + + # https://docs.github.com/en/code-security/how-tos/secure-your-supply-chain/secure-your-dependencies/keeping-your-actions-up-to-date-with-dependabot + - package-ecosystem: "github-actions" + # Workflow files stored in the default location of `.github/workflows` + # You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`. + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/dump-logs.sh b/.github/dump-logs.sh new file mode 100755 index 0000000000..ad0f385a24 --- /dev/null +++ b/.github/dump-logs.sh @@ -0,0 +1,13 @@ +SUFFIX=$1 +if [ -n "$SUFFIX" ]; then + REL="opensc-*$SUFFIX/_build/sub/" +fi + +echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" +for F in ${REL}tests/*.log ${REL}src/tests/unittests/*.log; do + echo "::group::$F" + tail -n50 $F + echo "::endgroup::" +done +echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" + diff --git a/.github/ports/openpace/CMakeLists.txt b/.github/ports/openpace/CMakeLists.txt new file mode 100644 index 0000000000..253e91c10d --- /dev/null +++ b/.github/ports/openpace/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 3.15) +project(openpace C) + +find_package(OpenSSL REQUIRED) + +add_library(eac STATIC + ca_lib.c + cv_cert.c + cvc_lookup.c + x509_lookup.c + eac_asn1.c + eac.c + eac_ca.c + eac_dh.c + eac_ecdh.c + eac_kdf.c + eac_lib.c + eac_print.c + eac_util.c + misc.c + objects.c + pace.c + pace_lib.c + pace_mappings.c + ri.c + ri_lib.c + ssl_compat.c + ta.c + ta_lib.c +) + +target_include_directories(eac + PUBLIC + $ + $ +) + +target_link_libraries(eac PUBLIC OpenSSL::Crypto) + +target_compile_definitions(eac PRIVATE + HAVE_ASN1_STRING_GET0_DATA + HAVE_DECL_OPENSSL_ZALLOC + HAVE_DH_GET0_KEY + HAVE_DH_GET0_PQG + HAVE_DH_SET0_KEY + HAVE_DH_SET0_PQG + HAVE_ECDSA_SIG_GET0 + HAVE_ECDSA_SIG_SET0 + HAVE_EC_KEY_METHOD + HAVE_RSA_GET0_KEY + HAVE_RSA_SET0_KEY + HAVE_EC_POINT_GET_AFFINE_COORDINATES + HAVE_EC_POINT_SET_AFFINE_COORDINATES + HAVE_EVP_PKEY_DUP + "X509DIR=\"/\"" + "CVCDIR=\"/\"" +) + +install(TARGETS eac ARCHIVE DESTINATION lib) +install(DIRECTORY eac DESTINATION include) diff --git a/.github/ports/openpace/portfile.cmake b/.github/ports/openpace/portfile.cmake new file mode 100644 index 0000000000..5333965238 --- /dev/null +++ b/.github/ports/openpace/portfile.cmake @@ -0,0 +1,17 @@ +vcpkg_check_linkage(ONLY_STATIC_LIBRARY) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO frankmorgner/openpace + REF ${VERSION} + SHA512 9236de244e4c306223c7051f3c2cd0df155ca473d69d55b22f97e4050ed9c30a037511851e3afcb75745f3e95c5b79d2d96e3e2f8e23e692a07f3e0530cf5b40 + HEAD_REF master +) + +file(COPY "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt" DESTINATION "${SOURCE_PATH}/src") + +vcpkg_cmake_configure(SOURCE_PATH "${SOURCE_PATH}/src") +vcpkg_cmake_install() + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/COPYING") diff --git a/.github/ports/openpace/vcpkg.json b/.github/ports/openpace/vcpkg.json new file mode 100644 index 0000000000..29290da4ef --- /dev/null +++ b/.github/ports/openpace/vcpkg.json @@ -0,0 +1,18 @@ +{ + "name": "openpace", + "version": "1.1.4", + "description": "C implementation of the PACE protocol as defined in BSI TR-03110", + "homepage": "https://github.com/frankmorgner/openpace", + "license": "GPL-3.0-or-later", + "dependencies": [ + "openssl", + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ] +} diff --git a/.github/push_artifacts.sh b/.github/push_artifacts.sh new file mode 100755 index 0000000000..8b24427e91 --- /dev/null +++ b/.github/push_artifacts.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +set -ex -o xtrace + +if [ -z "$GH_TOKEN" ]; then + exit 0 +fi + +# Check the GH_TOKEN is not expired +curl --fail --silent -XGET -H "authorization: token $GH_TOKEN" 'https://api.github.com/repos/OpenSC/Nightly' || \ + ( echo "The GH_TOKEN is expired -- please renew it!"; exit 1 ) + +BUILDPATH=${PWD} +BRANCH="`git log --max-count=1 --date=short --abbrev=8 --pretty=format:"%cd_%h"`" + +git clone --single-branch https://${GH_TOKEN}@github.com/OpenSC/Nightly.git > /dev/null 2>&1 +pushd Nightly +git checkout -b "${BRANCH}" + +for file in ${BUILDPATH}/win32/Output/OpenSC*.exe ${BUILDPATH}/opensc*.tar.gz ${BUILDPATH}/OpenSC*.dmg ${BUILDPATH}/OpenSC*.msi ${BUILDPATH}/OpenSC*.zip +do + if [ -f ${file} ] + then + # github only allows a maximum file size of 50MB + MAX_MB_FILESIZE=50 + if [ $(du -m "$file" | cut -f 1) -ge $MAX_MB_FILESIZE ] + then + split -b ${MAX_MB_FILESIZE}m ${file} `basename ${file}`. + else + cp ${file} . + fi + git add `basename ${file}`* + fi +done + +git commit --message "$1" +i=0 +while [ $i -le 10 ] && ! git push --quiet --set-upstream origin "${BRANCH}" +do + sleep $[ ( $RANDOM % 32 ) + 1 ] + git pull --rebase origin --strategy-option ours "${BRANCH}" + i=$(( $i + 1 )) +done +popd diff --git a/.github/remove_signing_key.sh b/.github/remove_signing_key.sh new file mode 100755 index 0000000000..54f2ece051 --- /dev/null +++ b/.github/remove_signing_key.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +set -ex -o xtrace + +pushd .github/ +security delete-keychain mac-build.keychain +rm -f DeveloperIDApplication.p12 DeveloperIDInstaller.p12 +popd diff --git a/.github/restart-pcscd.sh b/.github/restart-pcscd.sh new file mode 100644 index 0000000000..d6c3d4dd5d --- /dev/null +++ b/.github/restart-pcscd.sh @@ -0,0 +1,49 @@ +#!/bin/bash -e + +# This file is made to be sourced into other test scripts and not executed +# manually because it sets trap to restore pcscd to working state + +# Set PCSCD_DEBUG="-d -a" to debug APDUs before sourcing this file + +# register cleanup handler +function pcscd_cleanup { + echo "Process terminated: resetting pcscd" + sudo pkill pcscd + if which systemctl && systemctl is-system-running; then + sudo systemctl start pcscd.socket + fi +} +trap pcscd_cleanup EXIT + + + +# set up polkit rule to let user "runner" access PC/SC remotely for testing +if [ ! -f "/usr/share/polkit-1/rules.d/03-polkit-pcscd.rules" ]; then + echo 'polkit.addRule(function(action, subject) { if ((action.id == "org.debian.pcsc-lite.access_pcsc" || action.id == "org.debian.pcsc-lite.access_card") && subject.user == "runner") { return polkit.Result.YES; } });' > /usr/share/polkit-1/rules.d/03-polkit-pcscd.rules; +fi + + +# stop the pcscd service and run it from console to see possible errors +if which systemctl && systemctl is-system-running; then + sudo systemctl stop pcscd.service pcscd.socket +else + sudo pkill pcscd || echo "no pcscd process was running" +fi +sudo /usr/sbin/pcscd -f $PCSCD_DEBUG 2>&1 | sed -e 's/^/pcscd: /' & + + +# Try to wait up to 30 seconds for pcscd to come up and create PID file +for ((i=1;i<=30;i++)); do + echo "Waiting for pcscd to start: $i s" + if [ -f "/var/run/pcscd/pcscd.pid" ]; then + echo "PCSC PID: `cat /var/run/pcscd/pcscd.pid`" + break + fi + sleep 1 +done + + +# if it did not come up, warn, but continue +if [ ! -f "/var/run/pcscd/pcscd.pid" ]; then + echo "WARNING: The pcscd pid file does not exist ... trying anyway" +fi diff --git a/.github/setup-centos.sh b/.github/setup-centos.sh new file mode 100755 index 0000000000..80d81d683a --- /dev/null +++ b/.github/setup-centos.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +set -ex -o xtrace + +# Generic dependencies +DEPS="make /usr/bin/xsltproc docbook-style-xsl autoconf automake libtool bash-completion vim-common softhsm openssl diffutils gawk nss-softokn nss-tools gcc gcc-c++ pcsc-lite-devel readline-devel openssl-devel zlib-ng-devel libcmocka-devel" + +dnf install -y 'dnf-command(config-manager)' +dnf config-manager --set-enabled crb +dnf install -y $DEPS + +XFAIL_TESTS="test-pkcs11-tool-test-threads.sh" + +if [ "$1" == "fips" ]; then + echo "# userspace fips" > /etc/system-fips + # We do not need the kernel part, but in case we ever do: + # mkdir -p /var/tmp/userspace-fips + # echo 1 > /var/tmp/userspace-fips/fips_enabled + # mount --bind /var/tmp/userspace-fips/fips_enabled \ + # /proc/sys/crypto/fips_enabled + update-crypto-policies --set FIPS + + # FIPS mode does not have implementation of 3DES and other ancient algorithms + sed -i -e "/TESTS += sm/a XFAIL_TESTS=sm" src/tests/unittests/Makefile.am + XFAIL_TESTS+=" test-pkcs11-tool-test.sh test-pkcs11-tool-unwrap-wrap-test.sh" +fi + +# In FIPS mode, OpenSSL doesn't allow RSA-PKCS, this is hardcoded into OpenSSL +# and we cannot influence it. Hence, the test is expected to fail in FIPS mode. +if [[ -f "/proc/sys/crypto/fips_enabled" && $(cat /proc/sys/crypto/fips_enabled) == "1" ]]; then + XFAIL_TESTS+=" test-pkcs11-tool-test.sh test-pkcs11-tool-unwrap-wrap-test.sh" +fi + +sed -i -e "/XFAIL_TESTS/,$ { + s/.*XFAIL_TESTS.*/XFAIL_TESTS=$XFAIL_TESTS/ + q +}" tests/Makefile.am + diff --git a/.github/setup-fedora.sh b/.github/setup-fedora.sh new file mode 100755 index 0000000000..0c97dafc4a --- /dev/null +++ b/.github/setup-fedora.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +set -ex -o xtrace + +# Generic dependencies +DEPS="make /usr/bin/xsltproc docbook-style-xsl autoconf automake libtool bash-completion vim-common softhsm openssl diffutils openpace openpace-devel gawk nss-softokn nss-tools" + +if [ "$1" == "clang" ]; then + DEPS="$DEPS clang" +else + DEPS="$DEPS gcc gcc-c++" +fi + +# 64bit or 32bit dependencies +if [ "$1" == "ix86" ]; then + DEPS="$DEPS pcsc-lite-devel*.i686 readline-devel*.i686 openssl-devel*.i686 zlib-ng-devel*.i686 libcmocka-devel*.i686 glibc-devel*i686 libgcc.i686 libatomic.i686" +else + DEPS="$DEPS pcsc-lite-devel readline-devel openssl-devel zlib-ng-devel libcmocka-devel" +fi + +if [ "$1" == "kryoptic" ]; then + DEPS="$DEPS kryoptic" +fi + +sudo dnf install -y $DEPS + +XFAIL_TESTS="test-pkcs11-tool-test-threads.sh test-pkcs11-tool-test.sh" + +# In FIPS mode, OpenSSL doesn't allow RSA-PKCS, this is hardcoded into OpenSSL +# and we cannot influence it. Hence, the test is expected to fail in FIPS mode. +if [[ -f "/proc/sys/crypto/fips_enabled" && $(cat /proc/sys/crypto/fips_enabled) == "1" ]]; then + XFAIL_TESTS+=" test-pkcs11-tool-unwrap-wrap-test.sh" +fi + +sed -i -e "/XFAIL_TESTS/,$ { + s/XFAIL_TESTS.*/XFAIL_TESTS=$XFAIL_TESTS/ + q +}" tests/Makefile.am diff --git a/.github/setup-java.sh b/.github/setup-java.sh new file mode 100755 index 0000000000..32bc9e2d31 --- /dev/null +++ b/.github/setup-java.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +set -ex -o xtrace + +# github already selected the right java in https://github.com/actions/setup-java +if [ -z "$GITHUB_ACTIONS" ]; then + # Select the right java + sudo update-java-alternatives -s java-1.8.0-openjdk-amd64 + sudo update-alternatives --get-selections | grep ^java + export PATH="/usr/lib/jvm/java-8-openjdk-amd64/bin/:$PATH" + export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/ +fi +env | grep -i JAVA + +# VSmartcard +./.github/setup-vsmartcard.sh + +# Javacard SDKs +if [ ! -d "oracle_javacard_sdks" ]; then + git clone https://github.com/martinpaljak/oracle_javacard_sdks.git +fi +export JC_HOME=$PWD/oracle_javacard_sdks/jc222_kit +export JC_CLASSIC_HOME=$PWD/oracle_javacard_sdks/jc305u3_kit + +# jCardSim +if [ ! -d "jcardsim" ]; then + # https://github.com/licel/jcardsim/pull/174 + git clone https://github.com/Jakuje/jcardsim.git +fi +pushd jcardsim +env | grep -i JAVA +if [ ! -f target/jcardsim-3.0.5-SNAPSHOT.jar ]; then + mvn initialize && mvn clean install +fi +popd diff --git a/.github/setup-libressl.sh b/.github/setup-libressl.sh new file mode 100755 index 0000000000..8f38966ae4 --- /dev/null +++ b/.github/setup-libressl.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +set -ex -o xtrace + +# WARNING: Change this also in .github/workflows/linux.yml +V=libressl-4.2.1 + +sudo apt-get remove -y libssl-dev + +if [ ! -d "$V" ]; then + # letsencrypt CA does not seem to be included in CI runner + wget --no-check-certificate https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/$V.tar.gz + tar xzf $V.tar.gz + + pushd $V + ./configure --prefix=/usr/local + make -j $(nproc) + popd +fi + +pushd $V +sudo make install +popd + +# update dynamic linker to find the libraries in non-standard path +echo "/usr/local/lib64" | sudo tee /etc/ld.so.conf.d/openssl.conf +sudo ldconfig diff --git a/.github/setup-linux.sh b/.github/setup-linux.sh new file mode 100755 index 0000000000..bb031e9c39 --- /dev/null +++ b/.github/setup-linux.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +set -ex -o xtrace + +if [ -x "/bin/sudo" ]; then + SUDO="sudo" +fi + +if [ -f "/etc/fedora-release" ]; then + . .github/setup-fedora.sh + exit 0 +fi + +FORCE_INSTALL=0 +if [ "$1" == "force-install" -o "$2" == "force-install" ]; then + FORCE_INSTALL=1 +fi + +WINE_DEPS="" +# Generic dependencies +DEPS="docbook-xsl xsltproc gengetopt help2man pcscd check pcsc-tools libtool make autoconf autoconf-archive automake pkg-config git xxd openssl valgrind" + +if [ "$1" == "clang" ]; then + DEPS="$DEPS clang" +fi + +# 64bit or 32bit dependencies +if [ "$1" == "ix86" ]; then + DEPS="$DEPS gcc-multilib libpcsclite-dev:i386 libcmocka-dev:i386 libssl-dev:i386 zlib1g-dev:i386 libreadline-dev:i386 softhsm2:i386" + FORCE_INSTALL=1 +else + DEPS="$DEPS libpcsclite-dev libcmocka-dev libssl-dev zlib1g-dev libreadline-dev softhsm2" +fi + +if [ "$1" == "debug" -o "$2" == "debug" ]; then + DEPS="debuginfod $DEPS" +fi + +if [ "$1" == "clang-tidy" ]; then + DEPS="$DEPS clang-tidy" +elif [ "$1" == "cac" ]; then + DEPS="$DEPS libglib2.0-dev libnss3-dev gnutls-bin libusb-dev libudev-dev flex libnss3-tools" +elif [ "$1" == "oseid" ]; then + DEPS="$DEPS socat gawk" +elif [ "$1" == "pico-hsm" ]; then + DEPS="$DEPS cmake" +elif [ "$1" == "piv" -o "$1" == "isoapplet" -o "$1" == "gidsapplet" -o "$1" == "openpgp" ]; then + if [ "$1" == "piv" ]; then + DEPS="$DEPS cmake" + fi + DEPS="$DEPS ant openjdk-8-jdk maven" +elif [ "$1" == "mingw" -o "$1" == "mingw32" ]; then + # Note, that this list is somehow magic and adding libwine, libwine:i386 or wine64 + # will make the following sections break without any useful logs. See GH#2458 + WINE_DEPS="wine wine32 xvfb wget libc6:i386 libgcc-s1:i386 libstdc++6:i386" + if [ "$1" == "mingw" ]; then + WINE_DEPS="$WINE_DEPS binutils-mingw-w64-x86-64 gcc-mingw-w64-x86-64 mingw-w64" + elif [ "$1" == "mingw32" ]; then + WINE_DEPS="$WINE_DEPS binutils-mingw-w64-i686 gcc-mingw-w64-i686" + fi + FORCE_INSTALL=1 +elif [ "$1" == "softokn" ]; then + DEPS="$DEPS libnss3" +fi + +# The Github Ubuntu images since 20211122.1 are broken +# https://github.com/actions/virtual-environments/issues/4589 +if [ "$1" == "mingw" -o "$1" == "mingw32" -o "$1" == "ix86" ]; then + $SUDO rm -f /etc/apt/sources.list.d/microsoft-prod.list + $SUDO apt-get update -qq + $SUDO apt-get purge -yqq libmono* moby* mono* php* libgdiplus libpcre2-posix3 libzip4 + $SUDO dpkg --add-architecture i386 +fi + +# make sure we do not get prompts +export DEBIAN_FRONTEND=noninteractive +export DEBCONF_NONINTERACTIVE_SEEN=true +$SUDO apt-get update -qq + +$SUDO apt-get install -y build-essential $DEPS + +# install libressl if needed +if [ "$1" == "libressl" -o "$2" == "libressl" ]; then + ./.github/setup-libressl.sh &> /tmp/libressl.log + RET=$? + if [ $RET -ne 0 ]; then + cat /tmp/libressl.log + exit $RET + fi +fi + +if [ "$1" == "mingw" -o "$1" == "mingw32" ]; then + $SUDO apt-get install --allow-downgrades -y $WINE_DEPS + if [ ! -f "$(winepath 'C:/Program Files/Inno Setup 5/ISCC.exe')" ]; then + /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 + export DISPLAY=:99.0 + [ -d isetup ] || mkdir isetup + pushd isetup + [ -f isetup-5.5.6.exe ] || wget http://files.jrsoftware.org/is/5/isetup-5.5.6.exe + sleep 5 # make sure the X server is ready ? + wine isetup-5.5.6.exe /SILENT /VERYSILENT /SP- /SUPPRESSMSGBOXES /NORESTART + popd + fi +fi diff --git a/.github/setup-macos.sh b/.github/setup-macos.sh new file mode 100755 index 0000000000..8c291d6722 --- /dev/null +++ b/.github/setup-macos.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -ex -o xtrace + +brew install automake gengetopt help2man libtool +if [ "$1" == "libressl" ]; then + brew install libressl +fi + +# openSCToken +export PATH="/usr/local/opt/ccache/libexec:$PATH" +git clone https://github.com/frankmorgner/OpenSCToken.git + +if [ -n "$KEY_PASSWORD" ]; then + echo $DEV_ID_APPLICATION | base64 --decode > .github/DeveloperIDApplication.p12 + echo $DEV_ID_INSTALLER | base64 --decode > .github/DeveloperIDInstaller.p12 + .github/add_signing_key.sh; +else + unset CODE_SIGN_IDENTITY INSTALLER_SIGN_IDENTITY; +fi diff --git a/.github/setup-valgrind.sh b/.github/setup-valgrind.sh new file mode 100644 index 0000000000..5b195a66f6 --- /dev/null +++ b/.github/setup-valgrind.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# +# To be sourced to the test scripts to run the OpenSC tools under valgrind +# +if [ "$1" == "valgrind" -o "$2" == "valgrind" ]; then + # the glib raises dozens of memory related issues so we will rebuild opensc without notify support + ./configure --disable-notify + make clean && make -j 4 V=1 + + # suppression file contains supressions for softhsm providing us with uninitialized mechanism flags + # https://github.com/opendnssec/SoftHSMv2/commit/f94aaffc879ade97a51b8e1308af42f86be1885f + export VALGRIND="valgrind -q --error-exitcode=1 --leak-check=full --keep-debuginfo=yes --trace-children=yes --gen-suppressions=all --suppressions=$PWD/tests/opensc.supp" + # this should help us getting better traces as some of pcsclite and avoid false positives + export LD_PRELOAD=$(ldconfig -p | grep libpcsclite.so.1 | tr ' ' '\n' | grep /) + + # The valgrind should be able to find the debuginfo on the fly + export DEBUGINFOD_URLS="https://debuginfod.ubuntu.com" +fi diff --git a/.github/setup-vsmartcard.sh b/.github/setup-vsmartcard.sh new file mode 100755 index 0000000000..0684ef1bbe --- /dev/null +++ b/.github/setup-vsmartcard.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -ex -o xtrace + +if [ ! -d "vsmartcard" ]; then + git clone https://github.com/frankmorgner/vsmartcard.git + pushd vsmartcard/virtualsmartcard + autoreconf -vis && ./configure && make -j2 + popd +fi +pushd vsmartcard/virtualsmartcard +sudo make install +popd diff --git a/.github/setup-wix.ps1 b/.github/setup-wix.ps1 new file mode 100644 index 0000000000..7123e03d48 --- /dev/null +++ b/.github/setup-wix.ps1 @@ -0,0 +1,19 @@ +param( + [string]$version = '7.0.0' +) + +$ErrorActionPreference = 'Stop' + +function Exec([scriptblock]$cmd) { + & $cmd + if ($LASTEXITCODE -ne 0) { throw "Command failed (exit $LASTEXITCODE)" } +} + +Exec { dotnet tool install -g --version $version wix } +Exec { wix eula accept wix7 } +Exec { wix extension add -g WixToolset.UI.wixext/$version } +Exec { wix extension add -g WixToolset.Util.wixext/$version } +Set-Location win32 +Exec { dotnet new console --force --name CustomAction } +Exec { dotnet add CustomAction package WixToolset.WcaUtil --version $version --package-directory packages } +Set-Location .. diff --git a/.github/test-cac.sh b/.github/test-cac.sh new file mode 100755 index 0000000000..ade8168174 --- /dev/null +++ b/.github/test-cac.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +set -ex -o xtrace + +source .github/setup-valgrind.sh + +# install the opensc +sudo make install +export LD_LIBRARY_PATH=/usr/local/lib + +# VSmartcard +./.github/setup-vsmartcard.sh + +# libcacard +if [ ! -d "libcacard" ]; then + git clone https://gitlab.freedesktop.org/spice/libcacard.git + pushd libcacard + ./autogen.sh --prefix=/usr && make -j2 + popd +fi +pushd libcacard +sudo make install +popd + +# prepare pcscd +PCSCD_DEBUG="-d -a" +. .github/restart-pcscd.sh + +# virt_cacard +if [ ! -d "virt_cacard" ]; then + git clone https://github.com/Jakuje/virt_cacard.git + pushd virt_cacard + ./autogen.sh && ./configure && make + popd +fi +pushd virt_cacard +./setup-softhsm2.sh +export SOFTHSM2_CONF=$PWD/softhsm2.conf +./virt_cacard 2>&1 | sed -e 's/^/virt_cacard: /;' & +PID=$! +popd + +# run the tests +pushd src/tests/p11test/ +sleep 5 +$VALGRIND ./p11test -s 0 -p 12345678 -o virt_cacard.json +popd + +diff -u3 src/tests/p11test/virt_cacard{_ref,}.json + +kill -9 $PID diff --git a/.github/test-gidsapplet.sh b/.github/test-gidsapplet.sh new file mode 100755 index 0000000000..6b3bb506ac --- /dev/null +++ b/.github/test-gidsapplet.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +set -ex -o xtrace + +source .github/setup-valgrind.sh + +# install the opensc +sudo make install +export LD_LIBRARY_PATH=/usr/local/lib + +# setup java stuff +. .github/setup-java.sh + +# GidsApplet +if [ ! -d "GidsApplet" ]; then + git clone https://github.com/vletoux/GidsApplet.git; +fi +javac -classpath jcardsim/target/jcardsim-3.0.5-SNAPSHOT.jar GidsApplet/src/import4096/com/mysmartlogon/gidsApplet/Config.java GidsApplet/src/main/com/mysmartlogon/gidsApplet/*.java; +echo "com.licel.jcardsim.card.applet.0.AID=A000000397425446590201" > gids_jcardsim.cfg; +echo "com.licel.jcardsim.card.applet.0.Class=com.mysmartlogon.gidsApplet.GidsApplet" >> gids_jcardsim.cfg; +echo "com.licel.jcardsim.card.ATR=3B80800101" >> gids_jcardsim.cfg; +echo "com.licel.jcardsim.vsmartcard.host=localhost" >> gids_jcardsim.cfg; +echo "com.licel.jcardsim.vsmartcard.port=35963" >> gids_jcardsim.cfg; + + +# prepare pcscd +. .github/restart-pcscd.sh + + +# start the applet and run couple of commands against that +java -noverify -cp GidsApplet/src/main/:jcardsim/target/jcardsim-3.0.5-SNAPSHOT.jar com.licel.jcardsim.remote.VSmartCard gids_jcardsim.cfg >/dev/null & +PID=$!; +sleep 5; +$VALGRIND opensc-tool --card-driver default --send-apdu 80b80000190bA0000003974254465902010bA00000039742544659020100; +$VALGRIND opensc-tool -n; +$VALGRIND gids-tool --initialize --pin 123456 --admin-key 000000000000000000000000000000000000000000000000 --serial 00000000000000000000000000000000; +kill -9 $PID diff --git a/.github/test-isoapplet.sh b/.github/test-isoapplet.sh new file mode 100755 index 0000000000..6c211440a3 --- /dev/null +++ b/.github/test-isoapplet.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +set -ex -o xtrace + +source .github/setup-valgrind.sh + +isoapplet_version="$1" + +isoapplet_pkgdir="xyz/wendland/javacard/pki/isoapplet" + +# install the opensc +sudo make install +export LD_LIBRARY_PATH=/usr/local/lib + +# setup java stuff +./.github/setup-java.sh + +# The ISO applet +if [ ! -d IsoApplet ]; then + if [ "$isoapplet_version" = "v0" ]; then + isoapplet_branch="main-javacard-v2.2.2" + git clone https://github.com/philipWendland/IsoApplet.git --branch $isoapplet_branch --depth 1 + elif [ "$isoapplet_version" = "v1" ]; then + #isoapplet_branch="main" + git clone https://github.com/philipWendland/IsoApplet.git --revision 9cf87af45dc2949730a700a52a2dceb87353734c --depth 1 + else + echo "Unknown IsoApplet version: $isoapplet_version" + exit 1 + fi + + # enable IsoApplet key import patch + sed "s/DEF_PRIVATE_KEY_IMPORT_ALLOWED = false/DEF_PRIVATE_KEY_IMPORT_ALLOWED = true/g" -i "IsoApplet/src/${isoapplet_pkgdir}/IsoApplet.java" +fi +javac -classpath jcardsim/target/jcardsim-3.0.5-SNAPSHOT.jar IsoApplet/src/${isoapplet_pkgdir}/*.java +echo "com.licel.jcardsim.card.applet.0.AID=F276A288BCFBA69D34F31001" > isoapplet_jcardsim.cfg +echo "com.licel.jcardsim.card.applet.0.Class=${isoapplet_pkgdir//\//.}.IsoApplet" >> isoapplet_jcardsim.cfg +echo "com.licel.jcardsim.card.ATR=3B80800101" >> isoapplet_jcardsim.cfg +echo "com.licel.jcardsim.vsmartcard.host=localhost" >> isoapplet_jcardsim.cfg +echo "com.licel.jcardsim.vsmartcard.port=35963" >> isoapplet_jcardsim.cfg + + +# prepare pcscd +. .github/restart-pcscd.sh + + +# start the applet and run couple of commands against that +java -noverify -cp IsoApplet/src/:jcardsim/target/jcardsim-3.0.5-SNAPSHOT.jar com.licel.jcardsim.remote.VSmartCard isoapplet_jcardsim.cfg >/dev/null & +PID=$! +sleep 5 + +# Does OpenSC see the uninitialized card? +$VALGRIND pkcs11-tool -L | tee opensc.log +# report as "token not recognized" +grep "(token not recognized)" opensc.log + +# Does OpenSC see the uninitialized card with options for InitToken? +cat >opensc.conf < openpgp_jcardsim.cfg; +echo "com.licel.jcardsim.card.applet.0.Class=openpgpcard.OpenPGPApplet" >> openpgp_jcardsim.cfg; +echo "com.licel.jcardsim.card.ATR=3B80800101" >> openpgp_jcardsim.cfg; +echo "com.licel.jcardsim.vsmartcard.host=localhost" >> openpgp_jcardsim.cfg; +echo "com.licel.jcardsim.vsmartcard.port=35963" >> openpgp_jcardsim.cfg; + + +# prepare pcscd +PCSCD_DEBUG="-d -a" +. .github/restart-pcscd.sh + +sleep 5 +echo "Is pcscd running:" +ps -ef | grep pcscd + +echo "Test for /var/run/pcscd/" +if [ -d /var/run/pcscd/ ] ; then + ls -la /var/run/pcscd/* + if [ -f /var/run/pcscd/pcscd.pid ] ; then + echo "/var/run/pcscd/pcscd.pid `cat /var/run/pcscd/pcscd.pid`" + fi +fi + +echo "Test for /run/pcscd/" +if [ -d /run/pcscd/ ] ; then + ls -la /run/pcscd/* + if [ -f /run/pcscd/pcscd.pid ] ; then + echo "/run/pcscd/pcscd.pid `cat /run/pcscd/pcscd.pid`" + fi + +fi +ps -ef | grep pcsc + +# start the applet and run couple of commands against that +java -noverify -cp ykneo-openpgp/applet/bin:jcardsim/target/jcardsim-3.0.5-SNAPSHOT.jar com.licel.jcardsim.remote.VSmartCard openpgp_jcardsim.cfg >/dev/null & +PID=$!; +echo java pid $PID +sleep 5; +$VALGRIND opensc-tool --card-driver default --send-apdu 80b800002210D276000124010200000000000001000010D276000124010200000000000001000000; +$VALGRIND opensc-tool -n; +$VALGRIND openpgp-tool --verify CHV3 --pin 12345678 --gen-key 2; +$VALGRIND pkcs15-init --verify --auth-id 3 --pin 12345678 --delete-objects privkey,pubkey --id 2 --generate-key rsa/2048; +$VALGRIND pkcs11-tool -l -t -p 123456; + +# generate new keys and run p11test +$VALGRIND openpgp-tool --verify CHV3 --pin 12345678 --gen-key 1; +$VALGRIND openpgp-tool --verify CHV3 --pin 12345678 --gen-key 3; +pushd src/tests/p11test/ +sleep 5 +# signing key 1 is on slot 1 +$VALGRIND ./p11test -v -s 0 -p 123456 -o openpgp_s0.json +$VALGRIND ./p11test -v -s 1 -p 123456 -o openpgp_s1.json +popd +diff -u3 src/tests/p11test/openpgp_s0{_ref,}.json +diff -u3 src/tests/p11test/openpgp_s1{_ref,}.json + +kill -9 $PID +ps -ef | grep pcsc diff --git a/.github/test-oseid.sh b/.github/test-oseid.sh new file mode 100755 index 0000000000..d422ef1a7c --- /dev/null +++ b/.github/test-oseid.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +set -ex -o xtrace + +source .github/setup-valgrind.sh + +# install the opensc +sudo make install +export LD_LIBRARY_PATH=/usr/local/lib + +if [ ! -d oseid ]; then + git clone https://github.com/popovec/oseid +fi +pushd oseid/src/ +make -f Makefile.console +if [ ! -d tmp ]; then + mkdir tmp +fi +socat -d -d pty,link=tmp/OsEIDsim.socket,raw,echo=0 "exec:build/console/console ...,pty,raw,echo=0" & +PID=$! +sleep 1 +echo "# OsEIDsim" > tmp/reader.conf +echo 'FRIENDLYNAME "OsEIDsim"' >> tmp/reader.conf +echo "DEVICENAME $PWD/tmp/OsEIDsim.socket" >> tmp/reader.conf +echo "LIBPATH $PWD/build/console/libOsEIDsim.so.0.0.1" >> tmp/reader.conf +echo "CHANNELID 1" >> tmp/reader.conf +sudo mv tmp/reader.conf /etc/reader.conf.d/reader.conf +cat /etc/reader.conf.d/reader.conf +popd + +# set up polkit rule to let user "runner" access PC/SC remotely for testing +if [ ! -f "/usr/share/polkit-1/rules.d/03-polkit-pcscd.rules" ]; then + echo 'polkit.addRule(function(action, subject) { if ((action.id == "org.debian.pcsc-lite.access_pcsc" || action.id == "org.debian.pcsc-lite.access_card") && subject.user == "runner") { return polkit.Result.YES; } });' > /usr/share/polkit-1/rules.d/03-polkit-pcscd.rules; +fi + +sudo /etc/init.d/pcscd restart + +# Needed for tput to not report warnings +export TERM=xterm-256color + +pushd oseid/tools +echo | ./OsEID-tool INIT +./OsEID-tool RSA-CREATE-KEYS +./OsEID-tool RSA-UPLOAD-KEYS +./OsEID-tool RSA-DECRYPT-TEST +./OsEID-tool RSA-SIGN-PKCS11-TEST +./OsEID-tool EC-CREATE-KEYS +./OsEID-tool EC-UPLOAD-KEYS +./OsEID-tool EC-SIGN-TEST +./OsEID-tool EC-SIGN-PKCS11-TEST +./OsEID-tool EC-ECDH-TEST +./OsEID-tool UNWRAP-WRAP-TEST +./OsEID-tool DES-AES-UPLOAD-KEYS +#./OsEID-tool SYM-CRYPT-TEST +./OsEID-tool ERASE-CARD + +# initialize card for p11test + +pkcs15-init -C --so-pin 00000000 --so-puk 00000000 +pkcs15-init --store-pin --id 01 --pin 11111111 --puk 11111111 --so-pin 00000000 +pkcs15-init -F +pkcs15-init --generate-key rsa/2048 --key-usage sign --pin 11111111 --auth-id 01 --id 1 --label 'RSA2k key' +pkcs15-init --generate-key rsa/2048 --key-usage decrypt --pin 11111111 --auth-id 01 --id 2 --label 'RSA2k encryption key' +pkcs15-init --generate-key ec/prime256v1 --key-usage sign --pin 11111111 --auth-id 01 --id 3 +popd + +pushd src/tests/p11test/ + +$VALGRIND ./p11test -s 0 -p 11111111 -o oseid.json +diff -u3 oseid_ref.json oseid.json +popd + +# cleanup -- this would break later uses of pcscd +kill -9 $PID +rm oseid/src/card_mem +sudo rm /etc/reader.conf.d/reader.conf diff --git a/.github/test-packit.sh b/.github/test-packit.sh new file mode 100755 index 0000000000..e721059175 --- /dev/null +++ b/.github/test-packit.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -ex -o xtrace + +# Install build requirements +REQUIREMENTS=$(sed -n -e '/^BuildRequires*/p' packaging/opensc.spec | sed 's/[^ ]* //') +dnf install -y ${REQUIREMENTS} + +# Run packit +packit --debug build locally diff --git a/.github/test-pico-hsm.sh b/.github/test-pico-hsm.sh new file mode 100755 index 0000000000..ed6ae1f071 --- /dev/null +++ b/.github/test-pico-hsm.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +set -ex -o xtrace + +source .github/setup-valgrind.sh + +# install the opensc +sudo make install +export LD_LIBRARY_PATH=/usr/local/lib + +# vsmartcard +./.github/setup-vsmartcard.sh + +if [ ! -d pico-hsm ]; then + git clone --depth 1 --branch v6.6 https://github.com/polhenarejos/pico-hsm.git + git -C pico-hsm submodule update --init --recursive +fi +if [ ! -d pico-hsm/build ]; then + mkdir pico-hsm/build + cmake -DENABLE_EMULATION=1 -S pico-hsm -B pico-hsm/build +fi +if [ ! -f pico-hsm/build/pico_hsm ]; then + make -C pico-hsm/build +fi + +# prepare pcscd +#PCSCD_DEBUG="-d -a" +. .github/restart-pcscd.sh + +sleep 2 +rm -f memory.flash +tar -xf pico-hsm/tests/memory.tar.gz +pico-hsm/build/pico_hsm & +PID=$! +sleep 2 + +# run the tests +pushd src/tests/p11test/ +sleep 5 +$VALGRIND sc-hsm-tool --initialize --so-pin 3537363231383830 --pin 648219 +$VALGRIND ./p11test -s 0 -p 648219 -o pico-hsm.json +diff -u3 pico-hsm_ref.json pico-hsm.json +popd + +# cleanup -- this would break later uses of pcscd +kill -9 $PID diff --git a/.github/test-piv.sh b/.github/test-piv.sh new file mode 100755 index 0000000000..b926f1ecd3 --- /dev/null +++ b/.github/test-piv.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +set -ex -o xtrace + +source .github/setup-valgrind.sh + +# install the opensc +sudo make install +export LD_LIBRARY_PATH=/usr/local/lib + +# setup java stuff and virutal smartcard +. .github/setup-java.sh + +# The PIV Applet +if [ ! -d "PivApplet" ]; then + git clone --recursive https://github.com/arekinath/PivApplet.git +fi +pushd PivApplet +JC_HOME=${JC_CLASSIC_HOME} ant dist +popd + +# yubico-piv-tool is needed for PIV Applet management +if [ ! -d "yubico-piv-tool" ]; then + git clone https://github.com/Yubico/yubico-piv-tool.git + pushd yubico-piv-tool + if [ ! -d "build" ]; then + mkdir build + pushd build + cmake .. && make + popd + fi + popd +fi +pushd yubico-piv-tool/build + sudo make install + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib/x86_64-linux-gnu +popd + + +# prepare pcscd +. .github/restart-pcscd.sh + + +# start the applet and run couple of commands against that +java -noverify -cp PivApplet/bin/:jcardsim/target/jcardsim-3.0.5-SNAPSHOT.jar com.licel.jcardsim.remote.VSmartCard PivApplet/test/jcardsim.cfg >/dev/null & +PID=$! +sleep 5 + +$VALGRIND opensc-tool --card-driver default --send-apdu 80b80000120ba000000308000010000100050000020F0F7f +$VALGRIND opensc-tool -n + +PIN="123456" +yubico-piv-tool -v 9999 -r 'Virtual PCD 00 00' -P "$PIN" -s 9e -a generate -A RSA2048 | tee 9e.pub +yubico-piv-tool -v 9999 -r 'Virtual PCD 00 00' -P "$PIN" -s 9e -S'/CN=barCard/OU=test/O=example.com/' -averify-pin -aselfsign < 9e.pub | tee 9e.cert +yubico-piv-tool -v 9999 -r 'Virtual PCD 00 00' -P "$PIN" -s 9e -aimport-certificate < 9e.cert + +yubico-piv-tool -v 9999 -r 'Virtual PCD 00 00' -P "$PIN" -s 9a -a generate -A RSA2048 | tee 9a.pub +yubico-piv-tool -v 9999 -r 'Virtual PCD 00 00' -P "$PIN" -s 9a -S'/CN=bar/OU=test/O=example.com/' -averify-pin -aselfsign < 9a.pub | tee 9a.cert +yubico-piv-tool -v 9999 -r 'Virtual PCD 00 00' -P "$PIN" -s 9a -aimport-certificate < 9a.cert + +yubico-piv-tool -v 9999 -r 'Virtual PCD 00 00' -P "$PIN" -s 9c -a generate -A ECCP256 | tee 9c.pub +yubico-piv-tool -v 9999 -r 'Virtual PCD 00 00' -P "$PIN" -s 9c -S'/CN=bar/OU=test/O=example.com/' -averify-pin -aselfsign < 9c.pub | tee 9c.cert +yubico-piv-tool -v 9999 -r 'Virtual PCD 00 00' -P "$PIN" -s 9c -aimport-certificate < 9c.cert + +yubico-piv-tool -v 9999 -r 'Virtual PCD 00 00' -P "$PIN" -s 9d -a generate -A ECCP256 | tee 9d.pub +yubico-piv-tool -v 9999 -r 'Virtual PCD 00 00' -P "$PIN" -s 9d -S'/CN=bar/OU=test/O=example.com/' -averify-pin -aselfsign < 9d.pub | tee 9d.cert +yubico-piv-tool -v 9999 -r 'Virtual PCD 00 00' -P "$PIN" -s 9d -aimport-certificate < 9d.cert + +$VALGRIND pkcs11-tool -l -O -p "$PIN" +$VALGRIND pkcs11-tool -l -t -p "$PIN" + +# run p11test +pushd src/tests/p11test/ +sleep 5 +$VALGRIND ./p11test -v -s 0 -p "$PIN" -o piv.json +popd +diff -u3 src/tests/p11test/piv{_ref,}.json + +kill -9 $PID diff --git a/.github/vcpkg.json b/.github/vcpkg.json new file mode 100644 index 0000000000..b756fd0377 --- /dev/null +++ b/.github/vcpkg.json @@ -0,0 +1,17 @@ +{ + "name": "opensc", + "dependencies": [ + "openssl" + ], + "features": { + "zlib": { + "description": "Compressed data support (PIV, actalis)", + "dependencies": ["zlib"] + }, + "openpace": { + "description": "EAC/PACE support via OpenPACE", + "dependencies": ["openpace"] + } + }, + "builtin-baseline": "56bb2411609227288b70117ead2c47585ba07713" +} diff --git a/.github/workflows/centos.yml b/.github/workflows/centos.yml new file mode 100644 index 0000000000..ef286374bd --- /dev/null +++ b/.github/workflows/centos.yml @@ -0,0 +1,44 @@ +name: CentOS Stream Linux + +on: + pull_request: + paths: + - '**.c' + - '**.h' + - '**.sh' + - .github/workflows/centos.yml + - '**.am' + - doc/** + - configure.ac + push: + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + c10s: + runs-on: ubuntu-latest + container: + image: quay.io/centos/centos:stream10 + + steps: + - uses: actions/checkout@v7 + - run: .github/setup-centos.sh + - run: .github/build.sh dist + - name: Upload test logs + uses: actions/upload-artifact@v7 + if: failure() + with: + name: c10s-test-logs + path: | + tests/*.log + src/tests/unittests/*.log + + c10s-fips: + runs-on: ubuntu-latest + container: + image: quay.io/centos/centos:stream10 + steps: + - uses: actions/checkout@v7 + - run: .github/setup-centos.sh fips + - run: .github/build.sh fips diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml new file mode 100644 index 0000000000..e7c248cb07 --- /dev/null +++ b/.github/workflows/cifuzz.yml @@ -0,0 +1,29 @@ +name: CIFuzz +on: + pull_request: + paths: + - '**.c' + - '**.h' + - .github/workflows/cifuzz.yml +jobs: + Fuzzing: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + id: build + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'opensc' + dry-run: false + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'opensc' + fuzz-seconds: 600 + dry-run: false + - name: Upload Crash + uses: actions/upload-artifact@v7 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts + path: ./out/artifacts diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..72bc715afc --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,53 @@ +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '30 16 * * 0' + +permissions: {} +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v7 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + config-file: ./.github/.codeql.yml + + - run: .github/setup-linux.sh force-install + - run: .github/build.sh + + - name: Upload test logs + uses: actions/upload-artifact@v7 + if: failure() + with: + name: codeql-logs + path: | + tests/*.log + src/tests/unittests/*.log + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml new file mode 100644 index 0000000000..3ee6866dfa --- /dev/null +++ b/.github/workflows/codespell.yml @@ -0,0 +1,21 @@ +--- +name: Codespell + +on: + pull_request: + push: + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + codespell: + name: Check for spelling errors + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v7 + - uses: codespell-project/actions-codespell@master + with: + skip: ./src/tests/fuzzing/corpus,compat_* + ignore_words_file: .github/codespell_ignore_words.txt diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml new file mode 100644 index 0000000000..873c7d52e0 --- /dev/null +++ b/.github/workflows/coverity.yml @@ -0,0 +1,28 @@ +name: Coverity CI + +# We only want to test master or explicitly via coverity branch +on: + push: + branches: [master, coverity] + + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + build: + runs-on: ubuntu-latest + env: + HAS_COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN != '' }} + steps: + - uses: actions/checkout@v7 + - run: .github/setup-linux.sh force-install + - run: ./bootstrap + - run: ./configure --disable-dependency-tracking --enable-piv-sm + - uses: vapier/coverity-scan-action@v1 + if: ${{ env.HAS_COVERITY_SCAN_TOKEN == 'true' }} + with: + project: OpenSC%2FOpenSC + token: ${{ secrets.COVERITY_SCAN_TOKEN }} + email: 'viktor.tarasov@gmail.com' + command: 'make' diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml new file mode 100644 index 0000000000..fc0453657c --- /dev/null +++ b/.github/workflows/doc.yml @@ -0,0 +1,40 @@ +--- +name: Make sure HTML files are updated + +on: + pull_request: + paths: + - '**.xml' + - '**.xml.in' + - .github/workflows/doc.yml + +jobs: + build: + runs-on: ubuntu-latest + container: + image: fedora:latest + steps: + # Do this before checkout otherwise we will not have a git repository + - run: dnf install -y git + - uses: actions/checkout@v7 + - run: .github/setup-fedora.sh + # git checkout to revert changes to tests/Makefile.am done by the setup + - run: | + git config --global --add safe.directory $GITHUB_WORKSPACE && + git checkout tests/Makefile.am && + ./bootstrap && + ./configure --prefix="/usr" --enable-doc && + cd doc/tools && + rm tools.html && + make tools.html && + cd ../files && + rm files.html && + make files.html && + cd ../../ && + git diff --exit-code --color || ( + echo "The documentation files were changed!" + echo -n "Regenerate the HTML files with " + echo -n "\`make tools.html\` in \`doc/tools\` and " + echo -n "\`make files.html\`in \`doc/files\` or apply the above patch" + exit 1 + ) diff --git a/.github/workflows/external-pkcs11.yaml b/.github/workflows/external-pkcs11.yaml new file mode 100644 index 0000000000..bc9f71a06d --- /dev/null +++ b/.github/workflows/external-pkcs11.yaml @@ -0,0 +1,106 @@ +name: Tests of external pkcs11 modules + +on: + pull_request: + paths: + - '**.c' + - '**.h' + - '**.sh' + - .github/workflows/external-pkcs11.yaml + - '**.am' + - doc/** + - configure.ac + push: + +permissions: + contents: read # to fetch code (actions/checkout) + +env: + BASE_DEPS: | + build-essential docbook-xsl xsltproc gengetopt help2man pcscd check + pcsc-tools libtool make autoconf autoconf-archive automake pkg-config + git xxd openssl valgrind socat gawk + libglib2.0-dev libnss3-dev gnutls-bin libusb-dev libudev-dev flex + libnss3-tools + libpcsclite-dev libcmocka-dev libssl-dev zlib1g-dev libreadline-dev softhsm2 + +jobs: + ####################### + ## pkcs11-tool tests ## + ####################### + + test-kryoptic: + runs-on: ubuntu-24.04 + container: + image: fedora:latest + env: + SOURCE_PATH: ./ + BUILD_PATH: ./ + steps: + - name: Checkout Repository + uses: actions/checkout@v7 + - name: Setup Fedora + run: .github/setup-fedora.sh kryoptic + - name: Install OpenSC + run: .github/build.sh + - name: Test with Kryoptic + run: tests/test-kryoptic.sh + + test-softokn: + runs-on: ubuntu-24.04 + env: + SOURCE_PATH: ./ + BUILD_PATH: ./ + steps: + - uses: actions/checkout@v7 + - uses: awalsh128/cache-apt-pkgs-action@v1.6.1 + with: + packages: ${{ env.BASE_DEPS }} + version: apt-20-base + execute_install_scripts: true + - name: Setup Ubuntu + run: .github/setup-linux.sh softokn + - name: Build OpenSC + run: .github/build.sh + - name: Test NSS Softokn + env: + LD_LIBRARY_PATH: /usr/local/lib + run: tests/test-softokn.sh + + test-softhsm: + runs-on: ubuntu-24.04 + env: + SOURCE_PATH: ./ + BUILD_PATH: ./ + steps: + - uses: actions/checkout@v7 + - uses: awalsh128/cache-apt-pkgs-action@v1.6.1 + with: + packages: ${{ env.BASE_DEPS }} + version: apt-20-base + execute_install_scripts: true + - name: Setup Ubuntu + run: .github/setup-linux.sh + - name: Build OpenSC + run: .github/build.sh + - name: Test SoftHSM + env: + LD_LIBRARY_PATH: /usr/local/lib + run: tests/test-softhsm.sh + + test-interoperability: + runs-on: ubuntu-24.04 + container: + image: fedora:latest + env: + SOURCE_PATH: ./ + BUILD_PATH: ./ + steps: + - name: Checkout Repository + uses: actions/checkout@v7 + - name: Setup Fedora + run: .github/setup-fedora.sh kryoptic + - name: Install OpenSC + run: .github/build.sh + - name: Interoperability tests + run: tests/test-pkcs11-tool-unwrap-wrap-interoperability-test.sh diff --git a/.github/workflows/fedora.yml b/.github/workflows/fedora.yml new file mode 100644 index 0000000000..2ba53bbecf --- /dev/null +++ b/.github/workflows/fedora.yml @@ -0,0 +1,43 @@ +name: Fedora Linux + +on: + pull_request: + paths: + - '**.c' + - '**.h' + - '**.sh' + - .github/workflows/fedora.yml + - '**.am' + - doc/** + - configure.ac + push: + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + fedora: + runs-on: ubuntu-latest + container: + image: fedora:latest + steps: + - uses: actions/checkout@v7 + - run: .github/setup-fedora.sh + - run: .github/build.sh dist + - name: Upload test logs + uses: actions/upload-artifact@v7 + if: failure() + with: + name: fedora-test-logs + path: | + tests/*.log + src/tests/unittests/*.log + + fedora-ix86: + runs-on: ubuntu-latest + container: + image: fedora:latest + steps: + - uses: actions/checkout@v7 + - run: .github/setup-fedora.sh ix86 + - run: .github/build.sh ix86 diff --git a/.github/workflows/formatter.yml b/.github/workflows/formatter.yml new file mode 100644 index 0000000000..0ed80a355d --- /dev/null +++ b/.github/workflows/formatter.yml @@ -0,0 +1,20 @@ +name: Check Code Style + +on: + pull_request: + paths: + - '**.c' + - '**.h' + - .github/workflows/formatter.yml + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 100 + - uses: yshui/git-clang-format-lint@master + #with: + # base: origin/master diff --git a/.github/workflows/linux-strict.yml b/.github/workflows/linux-strict.yml new file mode 100644 index 0000000000..6901bd398c --- /dev/null +++ b/.github/workflows/linux-strict.yml @@ -0,0 +1,88 @@ +--- +name: Linux (latest, strict) + +on: + pull_request: + paths: + - '**.c' + - '**.h' + - '**.sh' + - .github/workflows/linux-strict.yml + - '**.am' + - doc/** + - configure.ac + push: + +permissions: + contents: read # to fetch code (actions/checkout) + +# -Wno-incompatible-library-redeclaration -- strlcat and strlcpy compat +# functions make the build fail with +# incompatible redeclaration of library function 'strlcat' +# even if there is no implementation in Linux. +# Worth investigating +# -Wno-strict-prototypes -- is needed for the configure to pass +# otherwise it fails detect dlopen() +# -Wno-variadic-macros -- we are ok using the GNU extension in log.c +# -Wno-gnu-zero-variadic-macro-arguments -- related +# -Wno-incompatible-pointer-types-discards-qualifiers -- again with logging +# -Wno-unused-function -- the inline compat functions for openssl +# are included in files where they are needed and clang does not like it +# -Wno-unused-parameter -- too many to fix them right now +# -Wformat-pedantic - this complains about missing casts to (void *) which is +# pointles +# -Wno-pedantic -- this complains about void and function pointers casts +# -Wno-incompatible-function-pointer-types -- this looks just buggy as it +# complains about missing const, which is there +env: + CFLAGS: >- + -pedantic -Werror -Wall -Wextra + -Wno-strict-prototypes -Wno-empty-translation-unit + -Wno-incompatible-library-redeclaration + -Wno-variadic-macros -Wno-unused-function -Wno-unused-parameter + -Wno-incompatible-pointer-types-discards-qualifiers + -Wno-gnu-zero-variadic-macro-arguments + -Wno-format-pedantic + -Wno-pedantic + -Wno-incompatible-function-pointer-types + CLANG_CFLAGS: >- + -Wshorten-64-to-32 + BASE_DEPS: | + build-essential docbook-xsl xsltproc gengetopt help2man pcscd check + pcsc-tools libtool make autoconf autoconf-archive automake pkg-config + git xxd openssl valgrind socat gawk + libpcsclite-dev libcmocka-dev libssl-dev zlib1g-dev libreadline-dev softhsm2 + + +jobs: + build-strict: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + name: [fedora, debian, ubuntu] + compiler: [clang, gcc] + include: + - name: fedora + container: fedora:rawhide + - name: debian + container: debian:sid + - name: ubuntu + container: ubuntu:latest + container: ${{ matrix.container }} + steps: + - uses: actions/checkout@v7 + - run: .github/setup-linux.sh ${{ matrix.compiler }} force-install + - run: .github/build.sh piv-sm dist + env: + CC: ${{ matrix.compiler }} + CFLAGS: ${{ env.CFLAGS }} ${{ matrix.compiler == 'clang' && env.CLANG_CFLAGS || '' }} + - name: Upload test logs + uses: actions/upload-artifact@v7 + if: failure() + with: + name: ${{ matrix.name }}-${{ matrix.compiler }}-strict-test-logs + path: | + config.log + tests/*.log + src/tests/unittests/*.log diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 0000000000..b11347f2f7 --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,291 @@ +--- +name: Linux + +on: + pull_request: + paths: + - '**.c' + - '**.h' + - '**.sh' + - .github/workflows/linux.yml + - '**.am' + - doc/** + - configure.ac + push: + +permissions: + contents: read # to fetch code (actions/checkout) + + +env: + BASE_DEPS: | + build-essential docbook-xsl xsltproc gengetopt help2man pcscd check + pcsc-tools libtool make autoconf autoconf-archive automake pkg-config + git xxd openssl valgrind socat gawk + libglib2.0-dev libnss3-dev gnutls-bin libusb-dev libudev-dev flex + libnss3-tools + libpcsclite-dev libcmocka-dev libssl-dev zlib1g-dev libreadline-dev softhsm2 + JAVA_DEPS: | + ant openjdk-8-jdk maven cmake + JCARDSIM: https://github.com/Jakuje/jcardsim.git + +jobs: + build: + name: build-${{ matrix.dist }}-${{ matrix.arch }}-${{ matrix.build }} + strategy: + fail-fast: false + matrix: + dist: ['22.04', '24.04'] + arch: [amd64, arm64] + build: [dist, valgrind, piv-sm] # piv-sm will also include valgrind tests + exclude: + - build: dist + arch: arm64 + runs-on: ubuntu-${{ matrix.dist }}${{ matrix.arch == 'arm64' && '-arm' || '' }} + steps: + - uses: actions/checkout@v7 + - run: .github/setup-linux.sh ${{ matrix.build != 'dist' && 'debug' || '' }} # install debug with valgrind builds + - run: .github/build.sh ${{ matrix.build }} ${{ matrix.build == 'piv-sm' && 'valgrind' || '' }} # configure piv-sm with valgrind tests + - name: Upload test logs + uses: actions/upload-artifact@v7 + if: failure() + with: + name: ubuntu-${{ matrix.dist }}-${{ matrix.arch }}-${{ matrix.build }}-test-logs + path: | + tests/*.log + src/tests/unittests/*.log + - &use_cache + uses: actions/cache@v6 + id: cache-build + with: + path: ./* + key: ${{ runner.os }}-${{ matrix.dist }}-${{ matrix.arch }}-${{ matrix.build }}-${{ github.sha }} + - name: Upload build artifacts + if: matrix.build == 'dist' + uses: actions/upload-artifact@v7 + with: + name: opensc-build-${{ matrix.dist }}-${{ matrix.arch }} + path: + opensc*.tar.gz + + build-no-shared: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: awalsh128/cache-apt-pkgs-action@v1.6.1 + with: + packages: ${{ env.BASE_DEPS }} + version: apt-latest-base + execute_install_scripts: true + - run: .github/setup-linux.sh + - run: .github/build.sh no-shared valgrind + + build-no-readers: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: .github/setup-linux.sh + - run: .github/build.sh no-readers + + build-no-openssl: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: awalsh128/cache-apt-pkgs-action@v1.6.1 + with: + packages: ${{ env.BASE_DEPS }} + version: apt-latest-base + execute_install_scripts: true + - run: .github/setup-linux.sh + - run: .github/build.sh no-openssl valgrind + + build-ix86: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v7 + - uses: awalsh128/cache-apt-pkgs-action@v1.6.1 + with: + packages: ${{ env.BASE_DEPS }} + version: apt-24.04-amd64-base + execute_install_scripts: true + - run: .github/setup-linux.sh ix86 + - run: .github/build.sh ix86 + + build-mingw: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - run: .github/setup-linux.sh mingw force-install + - run: .github/build.sh mingw + - name: Cache build artifacts + uses: actions/upload-artifact@v7 + with: + name: opensc-build-mingw + path: + win32/Output/OpenSC*.exe + + build-mingw32: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - run: .github/setup-linux.sh mingw32 + - run: .github/build.sh mingw32 + - name: Cache build artifacts + uses: actions/upload-artifact@v7 + with: + name: opensc-build-mingw32 + path: + win32/Output/OpenSC*.exe + + build-clang-tidy: + runs-on: ubuntu-24.04 + needs: [build] + steps: + - uses: awalsh128/cache-apt-pkgs-action@v1.6.1 + with: + packages: ${{ env.BASE_DEPS }} + version: apt-24.04-amd64-base + execute_install_scripts: true + - uses: actions/cache@v6 + id: cache-build + with: + path: ./* + key: ${{ runner.os }}-24.04-amd64-valgrind-${{ github.sha }} + - run: .github/setup-linux.sh clang-tidy + - run: .github/build.sh clang-tidy + + test-java: + name: test-${{ matrix.target }}${{ matrix.ver }}-${{ matrix.dist }}-${{ matrix.build }} + strategy: + fail-fast: false + matrix: + dist: ['24.04'] + arch: [amd64] + build: [valgrind] + target: [piv, gidsapplet, openpgp] + ver: [''] + include: [ + { dist: '22.04', arch: amd64, build: 'piv-sm', target: 'piv' }, + { dist: '24.04', arch: amd64, build: 'piv-sm', target: 'piv' }, + { dist: '24.04', arch: amd64, build: 'valgrind', target: 'isoapplet', ver: 'v0' }, + { dist: '24.04', arch: amd64, build: 'valgrind', target: 'isoapplet', ver: 'v1' } + ] + runs-on: ubuntu-${{ matrix.dist }} + needs: [build] + steps: + - *use_cache + - run: git clone $JCARDSIM + - uses: actions/setup-java@v5 + with: + distribution: 'semeru' + java-version: '8' + cache: 'maven' + - run: .github/setup-linux.sh ${{ matrix.target }} debug + - run: .github/test-${{ matrix.target }}.sh ${{ matrix.ver }} + - run: .github/test-${{ matrix.target }}.sh ${{ matrix.ver }} valgrind + + test: + name: test-${{ matrix.target }}-${{ matrix.dist }} + strategy: + fail-fast: false + matrix: + dist: ['22.04', '24.04'] + arch: [amd64] + build: [valgrind] + target: [cac, oseid, pico-hsm] + runs-on: ubuntu-${{ matrix.dist }} + needs: [build] + steps: + - *use_cache + - run: .github/setup-linux.sh ${{ matrix.target }} debug + - run: .github/test-${{ matrix.target }}.sh + - run: .github/test-${{ matrix.target }}.sh valgrind + + ####################### + ## LibreSSL pipeline ## + ####################### + build-libressl: + runs-on: ubuntu-latest + env: + LIBRESSL_VERSION: 4.2.1 + steps: + - uses: actions/checkout@v7 + - uses: awalsh128/cache-apt-pkgs-action@v1.6.1 + with: + packages: ${{ env.BASE_DEPS }} + version: apt-latest-base + execute_install_scripts: true + - name: Restore LibreSSL build + uses: actions/cache/restore@v6 + id: libressl-cache + with: + path: | + libressl-${{ env.LIBRESSL_VERSION }}/ + key: libressl-${{ env.LIBRESSL_VERSION }} + - run: .github/setup-linux.sh libressl + - run: .github/build.sh dist libressl valgrind + - uses: actions/upload-artifact@v7 + if: failure() + with: + name: libressl-logs + path: | + config.log + tests/*.log + src/tests/unittests/*.log + - uses: actions/cache@v6 + id: cache-build + if: ${{ success() }} + with: + path: ./* + key: ${{ runner.os }}-libressl-${{ github.sha }} + - if: ${{ steps.libressl-cache.outputs.cache-hit != 'true' }} + name: Cache LibreSSL build + uses: actions/cache/save@v6 + with: + path: | + libressl-${{ env.LIBRESSL_VERSION }}/ + key: libressl-${{ env.LIBRESSL_VERSION }} + + test-libressl: + name: test-${{ matrix.target }}-libressl + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: [cac, oseid] + needs: [build-libressl] + steps: + - uses: awalsh128/cache-apt-pkgs-action@v1.6.1 + with: + packages: ${{ env.BASE_DEPS }} + version: apt-latest-base + execute_install_scripts: true + - uses: actions/cache@v6 + id: cache-build + with: + path: ./* + key: ${{ runner.os }}-libressl-${{ github.sha }} + - run: .github/setup-linux.sh ${{ matrix.target }} libressl + - run: .github/test-${{ matrix.target }}.sh + - run: .github/test-${{ matrix.target }}.sh valgrind + + #################### + ## Push artifacts ## + #################### + push-artifacts: + runs-on: ubuntu-latest + needs: [build, build-mingw] + steps: + - uses: actions/cache@v6 + id: cache-build + with: + path: ./* + key: ${{ runner.os }}-24.04-amd64-dist-${{ github.sha }} + - name: Pull mingw build artifacts + uses: actions/download-artifact@v8 + with: + name: opensc-build-mingw + - run: git config --global user.email "builds@github.com" + - run: git config --global user.name "Github Actions"; + - run: .github/push_artifacts.sh "Github Actions ${GITHUB_REF}" + if: ${{ github.repository == 'OpenSC/OpenSC' }} diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000000..95f615e8e0 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,108 @@ +name: OSX + +on: + pull_request: + paths: + - '**.c' + - '**.h' + - '**.m' + - '**.sh' + - .github/workflows/macos.yml + - '**.am' + - MacOSX/** + - configure.ac + push: + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + build: + strategy: + matrix: + os: [macos-26, macos-15, macos-14] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v7 + - name: Checkout OpenSSL sources + uses: actions/checkout@v7 + with: + repository: openssl/openssl + ref: openssl-3.5 + path: openssl + - name: Get OpenSSL commit hash + id: openssl_hash + run: echo "hash=$(cd openssl && git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + - uses: actions/cache@v6 + id: cache + with: + path: openssl_bin + key: ${{ matrix.os }}-OpenSSL-${{ steps.openssl_hash.outputs.hash }} + - run: sh MacOSX/build-openssl-macos.sh + if: steps.cache.outputs.cache-hit != 'true' + - run: .github/setup-macos.sh + env: + KEY_PASSWORD: ${{ secrets.DEV_ID_PASSWORD }} + DEV_ID_INSTALLER: ${{ secrets.DEV_ID_INSTALLER }} + DEV_ID_APPLICATION: ${{ secrets.DEV_ID_APPLICATION }} + - run: .github/build.sh + env: + CODE_SIGN_IDENTITY: ${{ secrets.CODE_SIGN_IDENTITY }} + DEVELOPMENT_TEAM: ${{ secrets.DEVELOPMENT_TEAM }} + INSTALLER_SIGN_IDENTITY: ${{ secrets.INSTALLER_SIGN_IDENTITY }} + - run: MacOSX/notarize + if: ${{ startsWith(github.ref, 'refs/tags/') && github.repository == 'OpenSC/OpenSC' }} + env: + DEVELOPMENT_TEAM: ${{ secrets.DEVELOPMENT_TEAM }} + NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }} + APPLE_ID: ${{ secrets.APPLE_ID }} + - run: .github/cleanup-macos.sh + env: + KEY_PASSWORD: ${{ secrets.DEV_ID_PASSWORD }} + - name: Cache build artifacts + uses: actions/upload-artifact@v7 + with: + name: opensc-build-${{ matrix.os }} + path: + OpenSC*.dmg + - name: Upload test logs + uses: actions/upload-artifact@v7 + if: failure() + with: + name: ${{ matrix.os }}-logs + path: | + config.log + + build-libressl: + strategy: + matrix: + os: [macos-26, macos-15, macos-14] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v7 + - run: .github/setup-macos.sh libressl + - run: .github/build.sh libressl + - run: .github/cleanup-macos.sh + - name: Upload test logs + uses: actions/upload-artifact@v7 + if: failure() + with: + name: ${{ matrix.os }}-libressl-logs + path: | + config.log + + push-artifacts: + runs-on: macos-latest + needs: [build] + steps: + - uses: actions/checkout@v7 + - name: Pull build artifacts + uses: actions/download-artifact@v8 + with: + name: opensc-build-macos-15 + - run: git config --global user.email "builds@github.com" + - run: git config --global user.name "Github Actions"; + - run: .github/push_artifacts.sh "Github Actions ${GITHUB_REF}" + env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} + if: ${{ github.repository == 'OpenSC/OpenSC' }} diff --git a/.github/workflows/rpmbuild.yaml b/.github/workflows/rpmbuild.yaml new file mode 100644 index 0000000000..a0093c2027 --- /dev/null +++ b/.github/workflows/rpmbuild.yaml @@ -0,0 +1,67 @@ +name: RPM Build +on: + pull_request: + paths: + - '**.c' + - '**.h' + - '**.am' + - '**.sh' + - .github/workflows/rpmbuild.yaml + - packaging/opensc.spec + - configure.ac + push: + +jobs: + rpmbuild: + name: Release tarball and run RPM Build + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + name: [fedora, centos10] + include: + - name: fedora + container: quay.io/fedora/fedora:rawhide + - name: centos10 + container: quay.io/centos/centos:stream10 + container: ${{ matrix.container }} + steps: + - name: Get Date for DNF cache entry + id: get-date + run: | + echo "date=$(/bin/date -u "+%Y%V")" >> $GITHUB_OUTPUT + shell: bash + + - name: Cache DNF packages (separate from the main job -- different dependencies) + uses: actions/cache@v6 + with: + path: | + /var/cache/libdnf5 + /var/cache/dnf + key: ${{ runner.os }}-${{ matrix.container }}-dnf-${{ steps.get-date.outputs.date }} + + - name: Install git to have sensible checkout + run: | + dnf -y install git rpm-build + + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Install Dependencies + run: | + if [ "${{ matrix.name }}" = "centos10" ]; then + dnf install -y 'dnf-command(config-manager)' + dnf config-manager --set-enabled crb + fi + dnf -y builddep packaging/opensc.spec + + - name: RPM Build + run: | + git config --global --add safe.directory /__w/opensc/opensc + ./bootstrap + ./configure + make dist + mkdir -p rpmbuild/SOURCES + cp opensc-*.tar.gz src/pkcs11/opensc.module rpmbuild/SOURCES/ + rpmbuild --define "_topdir $PWD/rpmbuild" -ba packaging/opensc.spec diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000..b3f681540a --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,21 @@ +name: 'Close stale issues and PRs' + +permissions: + issues: write + pull-requests: write + +on: + schedule: + - cron: '30 1 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v10 + with: + stale-issue-message: 'This issue has been marked as stale due to inactivity. If there is no further activity, comment, or update within the next 30 days, this issue will be automatically closed. If this issue is still relevant, please leave a comment or provide an update to keep it open. Thank you for your contributions.' + stale-pr-message: 'This pull request has been marked as stale due to inactivity. If there are outstanding review comments, requested changes, failing tests, or missing information, please address them and provide the necessary updates. If no further activity occurs within the next 30 days, this pull request will be automatically closed. If this pull request is still active, please leave a comment or push an update to keep it open. Thank you for your contribution.' + start-date: '2026-07-01T00:00:00Z' # ISO 8601 or RFC 2822 + days-before-stale: 120 + days-before-close: 30 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000000..51bc439ae8 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,110 @@ +name: Windows + +on: + pull_request: + paths: + - '**.c' + - '**.h' + - '**.sh' + - .github/workflows/windows.yml + - '**.am' + - '**.mak' + - configure.ac + push: + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + build: + runs-on: ${{ matrix.image }} + strategy: + matrix: + platform: [x86, x64, arm64] + configuration: [Light, Release] + image: [windows-2025] + include: + - platform: x86 + setenv: amd64_x86 + - platform: x64 + setenv: amd64 + - platform: arm64 + setenv: amd64_arm64 + env: + VCPKG_DEFAULT_TRIPLET: ${{ matrix.platform }}-windows-static + BUILD_NUMBER: ${{ github.run_number }} + steps: + - name: Checkout + uses: actions/checkout@v7 + - name: Package suffix + shell: bash + run: | + SUFFIX="-${GITHUB_SHA:0:7}" + if [[ "${{ github.ref_type }}" == "tag" ]]; then + if [[ "${{ github.ref_name }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + SUFFIX="" # regular release tag + elif [[ "${{ github.ref_name }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(.+)$ ]]; then + SUFFIX="${BASH_REMATCH[1]}" # rc release tag + fi + fi + PACKAGE_SUFFIX="${SUFFIX}${{ matrix.configuration == 'Light' && '-Light' || '' }}" + sed -i.bak "s|^PACKAGE_SUFFIX=\([-~]*[0-9a-zA-Z]*\)$|PACKAGE_SUFFIX=$PACKAGE_SUFFIX|g" VERSION.mk + cat VERSION.mk + while IFS='=' read -r key value; do + # Skip empty or comment lines + [[ -z "$key" || "$key" =~ ^# ]] && continue + export "$key=$value" + done < VERSION.mk + echo "ARTIFACT_NAME=${PRODUCT_NAME}-${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_FIX}${PACKAGE_SUFFIX}_${{ matrix.platform }}" >> $GITHUB_ENV + - name: Install CPDK + run: choco install windows-cryptographic-provider-development-kit -y > $null + - name: Cache vcpkg + if: matrix.configuration == 'Release' + uses: actions/cache@v6 + with: + path: ${{ github.workspace }}/vcpkg_cache + key: vcpkg-${{ matrix.configuration }}-${{ matrix.platform }}-${{ hashFiles('.github/vcpkg.json', '.github/ports/**') }} + - name: Prepare vcpkg + if: matrix.configuration == 'Release' + env: + VCPKG_BINARY_SOURCES: clear;files,${{ github.workspace }}/vcpkg_cache,readwrite + run: | + $installed = "${{ github.workspace }}\vcpkg_installed" + echo "VCPKG_INSTALLED=$installed" >> $env:GITHUB_ENV + C:\vcpkg\vcpkg install ` + --overlay-ports=${{ github.workspace }}/.github/ports ` + --x-feature=zlib ` + --x-feature=openpace ` + --x-manifest-root ${{ github.workspace }}/.github ` + --x-install-root $installed + - name: Install WIX + run: powershell -ExecutionPolicy Bypass -File .github/setup-wix.ps1 + - name: Build OpenSC + run: | + $vsPath = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath + & "$vsPath\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.setenv }} "&&" nmake /nologo /f Makefile.mak opensc.msi + - name: Archive artifacts + id: upload-unsigned-artifact + uses: actions/upload-artifact@v7 + with: + name: ${{ env.ARTIFACT_NAME }} + path: ./win32/*.msi + - name: Push to Signpath.io + if: ${{ startsWith(github.ref, 'refs/tags/') && github.repository == 'OpenSC/OpenSC' }} + uses: signpath/github-action-submit-signing-request@v2 + with: + api-token: '${{ secrets.SIGNPATH_API_TOKEN }}' + organization-id: '8d2463fe-39bd-4a41-bb72-f008b4b1fe17' + project-slug: 'OpenSC' + signing-policy-slug: 'release-signing' + github-artifact-id: '${{ steps.upload-unsigned-artifact.outputs.artifact-id }}' + wait-for-completion: false + - name: Archive debug artifacts + uses: actions/upload-artifact@v7 + with: + name: ${{ env.ARTIFACT_NAME }}-dbg + path: | + ./src/**/*.pdb + ./win32/*.pdb + !./src/**/vc*.pdb + !./win32/vc*.pdb diff --git a/.gitignore b/.gitignore index d224b76e5b..1843f0564f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ core archive acinclude.m4 aclocal.m4 +aminclude_static.am autom4te.cache compile confdefs.h @@ -22,8 +23,10 @@ mkinstalldirs so_locations stamp-h* tags +test-driver .deps .libs +.dirstamp .#*# .*.bak .*.orig @@ -38,18 +41,17 @@ tags *.la *.lib *.lo +*.manifest *.orig *.pdb *.rej *.u -*.rc *.pc *~ *.o *.gz *.bz2 *.[0-9] -*.html *.gif *.css *.out @@ -57,69 +59,59 @@ tags *.obj *.exp *.res +*.ggo ChangeLog +.cache +compile_commands.json +.dirstamp +VERSION.mk.bak -doc/tools/cardos-tool -doc/tools/cryptoflex-tool +doc/tools/*-tool doc/tools/eidenv -doc/tools/iasecc-tool -doc/tools/netkey-tool -doc/tools/openpgp-tool doc/tools/opensc-explorer -doc/tools/opensc-tool -doc/tools/gids-tool -doc/tools/piv-tool -doc/tools/pkcs11-tool +doc/tools/pkcs11-register doc/tools/pkcs15-crypt doc/tools/pkcs15-init -doc/tools/pkcs15-tool -doc/tools/sc-hsm-tool -doc/tools/westcos-tool -doc/tools/dnie-tool +doc/tools/opensc-asn1 +doc/tools/opensc-notify +doc/files/opensc.conf.5.xml +doc/files/pkcs15-profile.5.xml -etc/opensc.conf.win -etc/opensc.conf +etc/opensc.conf.example src/common/compat_getopt_main src/minidriver/opensc-minidriver.inf -src/tools/cardos-tool -src/tools/iasecc-tool -src/tools/openpgp-tool -src/tools/sc-hsm-tool -src/tools/westcos-tool -src/tools/pkcs15-tool +src/tools/*-tool src/tools/pkcs15-crypt src/tools/pkcs15-init -src/tools/piv-tool src/tools/eidenv src/tools/opensc-explorer -src/tools/opensc-tool -src/tools/gids-tool -src/tools/rutoken-tool src/tools/cardos-info -src/tools/cryptoflex-tool -src/tools/netkey-tool -src/tools/pkcs11-tool -src/tools/dnie-tool -src/tools/npa-tool src/tools/sceac-example +src/tools/opensc-notify +src/tools/opensc-notify.plist +src/tools/org.opensc.notify.desktop +src/tools/pkcs11-register +src/tools/pkcs11-register.plist +src/tools/pkcs11-register.desktop +src/tools/opensc-asn1 +src/tools/org.opensc-project.mac.opensc-notify.plist +src/tools/org.opensc-project.mac.pkcs11-register.plist win32/OpenSC.iss -win32/OpenSC.wxs -win32/winconfig.h win32/OpenSC.msi win32/OpenSC.wixobj win32/OpenSC.wixpdb -MacOSX/build-package -MacOSX/Distribution.xml +MacOSX/Distribution*.xml +MacOSX/resources/Welcome.html *.dmg *.pkg -OpenSC.tokend/ build/ engine_pkcs11/ libp11/ target/ +target_startup/ src/scconf/test-conf @@ -128,5 +120,37 @@ src/tests/lottery src/tests/p15dump src/tests/pintest src/tests/prngtest +src/tests/p11test/p11test -version.m4.ci +tests/*.log +tests/*.trs +src/tests/unittests/*.log +src/tests/unittests/*.trs +src/tests/unittests/asn1 +src/tests/unittests/cachedir +src/tests/unittests/compression +src/tests/unittests/openpgp-tool +src/tests/unittests/pkcs15filter +src/tests/unittests/simpletlv +src/tests/unittests/sm +src/tests/unittests/check_macro_reference_loop +src/tests/unittests/decode_ecdsa_signature +src/tests/unittests/hextobin + +src/tests/fuzzing/fuzz_asn1_print +src/tests/fuzzing/fuzz_asn1_sig_value +src/tests/fuzzing/fuzz_card +src/tests/fuzzing/fuzz_piv_tool +src/tests/fuzzing/fuzz_pkcs11 +src/tests/fuzzing/fuzz_pkcs11_uri +src/tests/fuzzing/fuzz_pkcs15_crypt +src/tests/fuzzing/fuzz_pkcs15_decode +src/tests/fuzzing/fuzz_pkcs15_encode +src/tests/fuzzing/fuzz_pkcs15_reader +src/tests/fuzzing/fuzz_pkcs15_tool +src/tests/fuzzing/fuzz_pkcs15init +src/tests/fuzzing/fuzz_scconf_parse_string + +openssl_arm64 +openssl_bin +openpace_bin diff --git a/.ignoreRevsFile b/.ignoreRevsFile new file mode 100644 index 0000000000..c308fdf0a1 --- /dev/null +++ b/.ignoreRevsFile @@ -0,0 +1,15 @@ +70771735ae10180bb039043b9a1b00b66bf00fc1 # Remove trailing whitespace and reformat to improve readability +ebee0cc63896c13741c1181defb911c1aa8c8872 # Reformat long lines for readability +010d3f4a5b3fdbf9c393b1a85d261fcb3ff5d207 # profile: Reformat for readability +38f437f182475ec01c52ff1bce725c14dfcbdf92 # setcos: Reformat for readability +45e3daacb2380f38b7d09b3cfd6ec1e3240e24fb # Reformat to UTF8 +152b988c5a91eb4761734012d8fb7d0a53e3d830 # p11test: Reformat +b820bdf5b3799aa15c0a29150e64c227d1c0abfc # tcos: Reformat tcos_decipher +69544553c36f0613f6283e0eeb3f9eb549825986 # tcos: Reformat insert_pin() for readability +d3451faa2190e9f4c12bb00adf00149b7af1d18d # tcos: Reformat insert_key +71d1f69a3a8b7e6436c1747945227fc66f4c6fda # Reformat tcos_compute_signature() for better readability +1819ca33d616f4b50d8a2e6444a15a0a5a4c157b # tcos_decipher: Reformat to improve readability +7e0ef7c16c99f80bb90292ea3190eca5b426e251 # ramework-pkcs15.c: Reformat +0c9717a82e8cd39c755ce5491cf4c3d77bd08022 # Reformat: remove extra spaces and tabs +1a36ce7586e50de149aa05a9cde638df1b4f746f # Apply OpenSC:ci .clang-format +f443c391b03d06c821a7a725279c3cd135f9c13b # PIV whitespace cleanup and addtion of // clang-format off|on diff --git a/.packit.yaml b/.packit.yaml new file mode 100644 index 0000000000..e8c30f2a5e --- /dev/null +++ b/.packit.yaml @@ -0,0 +1,23 @@ +upstream_project_url: https://github.com/OpenSC/OpenSC + +specfile_path: packaging/opensc.spec +files_to_sync: + - packaging/opensc.spec + - .packit.yaml +upstream_package_name: opensc +downstream_package_name: opensc +merge_pr_in_ci: false + +notifications: + pull_request: + successful_build: true + +jobs: +- job: copr_build + trigger: pull_request + metadata: + targets: + - fedora-development-x86_64 + - fedora-development-aarch64 + - fedora-development-ppc64le + - fedora-development-s390x diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 287349191b..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,103 +0,0 @@ -language: c - -sudo: false - -addons: - apt_packages: - - binutils-mingw-w64-i686 - - binutils-mingw-w64-x86-64 - - docbook-xsl - - gcc-mingw-w64-i686 - - gcc-mingw-w64-x86-64 - - libpcsclite-dev - - mingw-w64 - - wine - - xsltproc - - gengetopt - - help2man - coverity_scan: - project: - name: "OpenSC/OpenSC" - description: "Build submitted via Travis CI" - notification_email: viktor.tarasov@gmail.com - build_command: "make -j 4" - branch_pattern: coverity_scan - -env: - global: - # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created - # via the "travis encrypt" command using the project repo's public key - - secure: "UkHn7wy4im8V1nebCWbAetnDSOLRUbOlF6++ovk/7Bnso1/lnhXHelyzgRxfD/oI68wm9nnRV+RQEZ9+72Ug1CyvHxyyxxkwal/tPeHH4B/L+aGdPi0id+5OZSKIm77VP3m5s102sJMJgH7DFd03+nUd0K26p0tk8ad4j1geV4c=" - -matrix: - fast_finish: true - include: - - compiler: clang - os: osx - - compiler: gcc - os: osx - - compiler: clang - os: linux - env: ENABLE_DOC=--enable-doc - - compiler: gcc - os: linux - env: ENABLE_DOC=--enable-doc - - os: linux - env: HOST=x86_64-w64-mingw32 - - os: linux - env: HOST=i686-w64-mingw32 - -before_install: - - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - brew update; - brew uninstall libtool; - brew install libtool; - brew install gengetopt help2man; - fi - -before_script: - # we run a weekly cron job in travis on the coverity branch - # just synchronize it with master to get a new report - - if [ "${COVERITY_SCAN_BRANCH}" == 1 ]; then - git reset --hard origin/master; - fi - - ./bootstrap - - if [ -z "$HOST" ]; then - CFLAGS="-Werror" ./configure $ENABLE_DOC --enable-dnie-ui; - else - if [ ! -f "$(winepath 'C:/Program Files (x86)/Inno Setup 5/ISCC.exe')" ]; then - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16; - export DISPLAY=:99.0; - wget http://files.jrsoftware.org/is/5/isetup-5.5.6.exe; - wine isetup-5.5.6.exe /SILENT /VERYSILENT /SP- /SUPPRESSMSGBOXES /NORESTART; - fi; - unset CC; - unset CXX; - ./configure --host=$HOST --disable-openssl --disable-readline --disable-zlib --prefix=${TRAVIS_BUILD_DIR}/win32/opensc || cat config.log; - fi - -script: - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then - if [ $TRAVIS_OS_NAME == osx ]; then - ./MacOSX/build; - else - make; - fi; - fi - - if [ -z "$HOST" -a "${COVERITY_SCAN_BRANCH}" != 1 ]; then - make check; - make dist; - fi - - if [ ! -z "$HOST" -a "${COVERITY_SCAN_BRANCH}" != 1 ]; then - make install; - wine "C:/Program Files (x86)/Inno Setup 5/ISCC.exe" win32/OpenSC.iss; - fi - -after_script: - # kill process started during compilation to finish the build, see - # https://github.com/moodlerooms/moodle-plugin-ci/issues/33 for details - - if [ ! -z "$HOST" ]; then - killall services.exe; - fi - -cache: ccache diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..875a102bef --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,27 @@ +# AI Agent Instructions for OpenSC + +Welcome, autonomous agents. If you are analyzing the OpenSC repository, finding bugs, or preparing to submit +an issue or pull request, you MUST adhere strictly to the following rules: + +## 1. Issue Verification (Test Against `master`) +- **Rule:** Do NOT report bugs based solely on a release version. + OpenSC only supports the latest release and does not backport fixes. +- **Action:** Before preparing a report, you must pull the latest `master` branch, + build the project, and verify the bug is still present. + In the report, include function names, source code files and line numbers (ideally with link to github) + where the issue is demonstrated. + If the issue is already fixed in `master`, discard your report entirely. + +## 2. Security Vulnerabilities MUST Remain Private +- **Rule:** Do NOT submit public issues, public pull requests, or public comments regarding anything that + could be construed as a security vulnerability. + AI agents are strictly prohibited from publicly disclosing security reports. +- **Action:** If you suspect you have found a security vulnerability, **STOP**. + You must read `SECURITY.md` in the root of this repository and instruct your human operator to follow + the private disclosure steps outlined there. + +## 3. Proposing Solutions for Non-Security Bugs +- **Rule:** Do NOT submit "bug reports" without proposing a concrete fix. +- **Action:** If you find a verified, non-security bug on the `master` branch, analyze the root cause. + Your output must include a functional solution, ideally provided as a unified diff (patch) or submitted + directly as a Pull Request. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d587910ab8..546f7acd1f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,72 @@ -# Reporting Bugs +# Formatting style -Please read about [reporting bugs](https://github.com/OpenSC/OpenSC/wiki/How-to-report-bugs-so-that-they-can-be-fixed) before opening an issue. +The OpenSC formatting rules are described in `.clang-format` in the root +directory. It is based on [LLVM](https://llvm.org/docs/CodingStandards.html) +style with couple of modifications: + + * Tabs + * Tabs are used instead of spaces + * Tab is 8 spaces wide + * The maximum line width is 110 characters + * Opening braces follow the condition/expression except for the functions + +Examples: + +``` +void +function_name(int arg) +{ + int var = 0; + int rc = 0; + + if (arg) { + var = do_something(); + } + if (rc = call_some_function(arg) || + rc = call_some_other_long_funct(arg) || + rc = call_one_more_func(arg)) { + /* Note the two Tabs on the line above ! */ + return rc; + } + return var; +} +``` + +To check your changes if they follow the formatting style (before submitting +a PR), you can use `clang-format` tool or `git-clang-format`, which can check +only the parts of the code you changed in your branch + +``` +$ git-clang-format --diff --commit upstream/master +``` + +# Testing locally + +To learn how to run the tests from Github actions locally in containers, see +[`containers`](containers/README.md). + +# Spelling + +One of the GitHub actions checks spelling using +[codespell](https://github.com/codespell-project/codespell). +If you need to ignore some words, such as variable names or +words in languages other than English, add them to file +`codespell_ignore_words.txt`. + +Note that [codespell](https://github.com/codespell-project/codespell#usage) +expects words to be lower case: +> **Important note:** The list passed to -I is case-sensitive +> based on how it is listed in the codespell dictionaries. + +After installing +[codespell](https://github.com/codespell-project/codespell#installation), +you can run it from the command line as: +```sh +codespell -I .github/codespell_ignore_words.txt +``` + +# Release process + +The release process is described in [OpenSC wiki](https://github.com/OpenSC/OpenSC/wiki/OpenSC-Release-Howto) + +TODO tarball signing: https://github.com/OpenSC/OpenSC/issues/1129 diff --git a/COPYING b/COPYING index b1e3f5a263..e8c3f50515 100644 --- a/COPYING +++ b/COPYING @@ -1,8 +1,8 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + 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. @@ -10,7 +10,7 @@ as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] - Preamble + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public @@ -55,7 +55,7 @@ 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 @@ -111,8 +111,8 @@ 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 + + GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other @@ -146,7 +146,7 @@ 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 @@ -158,7 +158,7 @@ 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 @@ -216,7 +216,7 @@ 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. @@ -267,7 +267,7 @@ Library will still fall under Section 6.) 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 @@ -329,7 +329,7 @@ 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 @@ -370,7 +370,7 @@ 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 @@ -422,7 +422,7 @@ 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 @@ -432,7 +432,7 @@ 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 + 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. @@ -455,8 +455,8 @@ 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 - + 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 @@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + 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. @@ -501,4 +501,3 @@ necessary. Here is a sample; alter the names: That's all there is to it! - diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md deleted file mode 100644 index 109d58838f..0000000000 --- a/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,37 +0,0 @@ -### Expected behaviour - -What should happen? - - -### Actual behaviour - -What happens instead? - - -### Steps to reproduce - -1. -2. -3. - - -### Logs - -Debug output is essential to identify the problem. You can enable debugging by -editing the file `opensc.conf`: -``` - # A debug level of 3 catches most problems - # Some sensitive data may be logged, too, (e.g. PIN codes) - debug = 3; - - # Where to write the debug output (default: `stdout`) - #debug_file = opensc-debug.log -``` - -Please use Gist (https://gist.github.com/) or a similar code paster for longer -logs. Before pasting here, remove your sensitive data from your log (e.g. PIN -code or certificates). - -``` -Paste Log output with less than 10 lines here -``` diff --git a/MacOSX/Distribution.xml.in b/MacOSX/Distribution.xml.in index fbd4b22cd9..f6a265f2e2 100644 --- a/MacOSX/Distribution.xml.in +++ b/MacOSX/Distribution.xml.in @@ -1,22 +1,28 @@ + - + @PACKAGE_STRING@ - - - + - - - + + + - - - + + OpenSC.pkg + + + OpenSCToken.pkg + + + OpenSC-startup.pkg - OpenSC.pkg diff --git a/MacOSX/Makefile.am b/MacOSX/Makefile.am index b0fa2130e2..b43d69c859 100644 --- a/MacOSX/Makefile.am +++ b/MacOSX/Makefile.am @@ -1,8 +1,19 @@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in -EXTRA_DIST = build build-package.in Distribution.xml.in libtool-bundle opensc-uninstall \ +EXTRA_DIST = build \ + build-openssl-macos.sh \ + Distribution.xml.in \ + libtool-bundle \ + notarize \ + OpenSC_applescripts.entitlements \ + OpenSC_binaries.entitlements \ + OpenSC_Notify.applescript \ + OpenSC_Uninstaller.applescript \ + opensc-uninstall \ + target.plist \ + target_startup.plist \ + target_token.plist \ resources \ resources/background.jpg \ resources/Welcome.html.in \ scripts \ - scripts/postinstall \ - OpenSC_Uninstaller.applescript + scripts/postinstall diff --git a/MacOSX/OpenSC_Notify.applescript b/MacOSX/OpenSC_Notify.applescript new file mode 100644 index 0000000000..d2910c226b --- /dev/null +++ b/MacOSX/OpenSC_Notify.applescript @@ -0,0 +1,2 @@ +do shell script "killall opensc-notify || true" +do shell script "nohup /Library/OpenSC/bin/opensc-notify > /dev/null 2>&1 &" diff --git a/MacOSX/OpenSC_applescripts.entitlements b/MacOSX/OpenSC_applescripts.entitlements new file mode 100644 index 0000000000..548ce436ae --- /dev/null +++ b/MacOSX/OpenSC_applescripts.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.automation.apple-events + + + diff --git a/MacOSX/OpenSC_binaries.entitlements b/MacOSX/OpenSC_binaries.entitlements new file mode 100644 index 0000000000..fba0ded45e --- /dev/null +++ b/MacOSX/OpenSC_binaries.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.disable-library-validation + + + diff --git a/MacOSX/build b/MacOSX/build index e51dd25e40..58d49b9a61 100755 --- a/MacOSX/build +++ b/MacOSX/build @@ -1,8 +1,193 @@ #!/bin/bash +# Build the macOS installer for the token and command line tools. +# +# This is only tested and supported on macOS 10.10 or later, using Xcode 6.0.1. +# Building should also work on older macOS versions with slight changes; YMMV. + +# You need to install the following packages from homebrew or macports or fink: +# autoconf automake libtool pkg-config help2man gengetopt + +export MACOSX_DEPLOYMENT_TARGET="10.13" +FORCE_OPENSSL_BUILD="1" + set -ex -# generate configure test -x ./configure || ./bootstrap -# configure once to set the version in build script -./configure -# build and package installer -bash ./MacOSX/build-package $@ +BUILDPATH=${PWD} + +# keep in sync with MacOSX/notarize +while IFS='=' read -r key value; do + # Skip empty or comment lines + [[ -z "$key" || "$key" =~ ^# ]] && continue + export "$key=$value" +done < $BUILDPATH/VERSION.mk + +# keep in sync with MacOSX/notarize +export PACKAGE_VERSION=${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_FIX}${PACKAGE_SUFFIX} +PREFIX=/Library/OpenSC +export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/lib/pkgconfig + +if test "$FORCE_OPENSSL_BUILD" == "1" || ! pkg-config libcrypto --atleast-version=1.1.1; then + # OpenSSL is not installed + if ! test -e $BUILDPATH/openssl_bin/$PREFIX/lib/pkgconfig; then + # Build OpenSSL manually, because Apple's binaries are deprecated + sh $BUILDPATH/MacOSX/build-openssl-macos.sh -b $BUILDPATH -p $PREFIX + fi + PKGPREFIX="PKG_CONFIG_PATH=$BUILDPATH/openssl_bin/$PREFIX/lib/pkgconfig PKG_CONFIG_SYSROOT_DIR=$BUILDPATH/openssl_bin" + export OPENSSL_CFLAGS="`env $PKGPREFIX pkg-config --static --cflags libcrypto`" + export OPENSSL_LIBS="` env $PKGPREFIX pkg-config --static --libs libcrypto`" + export CRYPTO_CFLAGS="$OPENSSL_CFLAGS" + export CRYPTO_LIBS="$OPENSSL_LIBS" +fi + +export CFLAGS="$CFLAGS -arch x86_64 -arch arm64" +export LDFLAGS="$LDFLAGS -arch x86_64 -arch arm64" +export OBJCFLAGS=$CFLAGS + +if ! test -e $BUILDPATH/openpace_bin/$PREFIX/lib/pkgconfig; then + if ! test -e openpace; then + git clone --depth=1 https://github.com/frankmorgner/openpace.git -b 1.1.4 + fi + cd openpace + autoreconf -vis + CFLAGS="${CFLAGS} -DOPENSSL_SUPPRESS_DEPRECATED" ./configure --disable-shared --prefix=$PREFIX HELP2MAN=/usr/bin/true + touch src/cvc-create.1 src/cvc-print.1 + make DESTDIR=$BUILDPATH/openpace_bin install + cd .. +fi +PKGPREFIX="PKG_CONFIG_PATH=$BUILDPATH/openssl_bin/$PREFIX/lib/pkgconfig:$BUILDPATH/openpace_bin/$PREFIX/lib/pkgconfig PKG_CONFIG_SYSROOT_DIR=$BUILDPATH/openpace_bin" +export OPENPACE_CFLAGS="`env $PKGPREFIX pkg-config --static --cflags libeac` $OPENSSL_CFLAGS" +export OPENPACE_LIBS="` env $PKGPREFIX pkg-config --static --libs libeac` $OPENSSL_LIBS" + +if ! test -e ${BUILDPATH}/target/$PREFIX/lib/pkgconfig; then + ./configure --prefix=$PREFIX \ + --sysconfdir=$PREFIX/etc \ + --enable-cvcdir=$PREFIX/etc/cvc \ + --enable-x509dir=$PREFIX/etc/x509 \ + --enable-openssl-secure-malloc=65536 \ + --disable-dependency-tracking \ + --enable-shared \ + --disable-static \ + --enable-strict \ + --disable-assert \ + --enable-sm # TODO: remove this (must be sensible default in master) + + # always make clean + make clean + + # compile + make -j 4 + + # copy files + rm -rf ${BUILDPATH}/target + make install DESTDIR=${BUILDPATH}/target + + # remove garbage + rm -f ${BUILDPATH}/target/$PREFIX/lib/*.la + + # generate .bundle (required by Adobe Acrobat) + ./MacOSX/libtool-bundle ${BUILDPATH}/target/$PREFIX/lib/opensc-pkcs11.so ${BUILDPATH}/target/$PREFIX/lib +fi + + +if ! test -e NotificationProxy; then + git clone https://github.com/frankmorgner/NotificationProxy.git +fi +if test -n "${CODE_SIGN_IDENTITY}" -a -n "${DEVELOPMENT_TEAM}"; then + xcodebuild -target NotificationProxy -configuration Release -project NotificationProxy/NotificationProxy.xcodeproj install DSTROOT=$BUILDPATH/target/Library/OpenSC/ MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} \ + CODE_SIGN_IDENTITY="${CODE_SIGN_IDENTITY}" DEVELOPMENT_TEAM="${DEVELOPMENT_TEAM}" OTHER_CODE_SIGN_FLAGS="--timestamp --options=runtime" CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO CODE_SIGN_STYLE=Manual +else + xcodebuild -target NotificationProxy -configuration Release -project NotificationProxy/NotificationProxy.xcodeproj install DSTROOT=$BUILDPATH/target/Library/OpenSC/ MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} +fi +mkdir -p "$BUILDPATH/target/Applications/Utilities" +osacompile -o "$BUILDPATH/target/Applications/Utilities/OpenSC Notify.app" "MacOSX/OpenSC_Notify.applescript" +if test -n "${CODE_SIGN_IDENTITY}"; then + codesign --force --sign "${CODE_SIGN_IDENTITY}" --entitlements MacOSX/OpenSC_applescripts.entitlements --deep --timestamp --options runtime "$BUILDPATH/target/Applications/Utilities/OpenSC Notify.app" +fi + + +imagedir=$(mktemp -d) + +# Prepare target root +mkdir -p ${BUILDPATH}/target/usr/local/bin +cp MacOSX/opensc-uninstall ${BUILDPATH}/target/usr/local/bin + +# Prepare startup root +mkdir -p ${BUILDPATH}/target_startup/Library/LaunchAgents +cp src/tools/org.opensc-project.mac.pkcs11-register.plist ${BUILDPATH}/target_startup/Library/LaunchAgents +cp src/tools/org.opensc-project.mac.opensc-notify.plist ${BUILDPATH}/target_startup/Library/LaunchAgents + +# Build OpenSCToken if possible +if test -e OpenSCToken -a -n "${CODE_SIGN_IDENTITY}" -a -n "${DEVELOPMENT_TEAM}"; then + cd OpenSCToken + # make sure OpenSCToken builds with the same dependencies as before + if ! test -e OpenSC; then + git clone --depth=1 file://$PWD/../../OpenSC + else + cd OpenSC && git pull && cd .. + fi + mkdir -p build + if ! test -e build/openssl; then + # build/openssl/lib/libcrypto.a is hardcoded in OpenSCToken + ln -sf $BUILDPATH/openssl_bin/$PREFIX build/openssl + # in OpenSCToken's variant of OpenSC we still use OpenSSL flags from above + fi + if ! test -e build/openpace; then + # build/openpace/lib/libeac.a is hardcoded in OpenSCToken + ln -sf $BUILDPATH/openpace_bin/$PREFIX build/openpace + # in OpenSCToken's variant of OpenSC we still use OpenPACE flags from above + fi + BP=${BUILDPATH} + . ./bootstrap + BUILDPATH=${BP} + xcodebuild -target OpenSCTokenApp -configuration Debug -project OpenSCTokenApp.xcodeproj install DSTROOT=${BUILDPATH}/target_token \ + CODE_SIGN_IDENTITY="${CODE_SIGN_IDENTITY}" DEVELOPMENT_TEAM="${DEVELOPMENT_TEAM}" OTHER_CODE_SIGN_FLAGS="--timestamp --options=runtime" CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO CODE_SIGN_STYLE=Manual + cd .. + + COMPONENT_TOKEN="--component-plist MacOSX/target_token.plist" +else + # if no OpenSCToken is checked out, then we create a dummy package + mkdir -p ${BUILDPATH}/target_token +fi + +if test -n "${CODE_SIGN_IDENTITY}"; then + for d in ${BUILDPATH}/target/Library/OpenSC/bin ${BUILDPATH}/target/Library/OpenSC/lib + do + # find executable files and run codesign on them + find ${d} -type f -perm +111 -print -exec \ + codesign --force --sign "${CODE_SIGN_IDENTITY}" --entitlements MacOSX/OpenSC_binaries.entitlements --deep --timestamp --options runtime {} \; + done +fi + + +# Build package +pkgbuild --root ${BUILDPATH}/target --component-plist MacOSX/target.plist --scripts MacOSX/scripts --identifier org.opensc-project.mac --version ${PACKAGE_VERSION} --install-location / OpenSC.pkg +pkgbuild --root ${BUILDPATH}/target_token $COMPONENT_TOKEN --identifier org.opensc-project.mac.opensctoken --version ${PACKAGE_VERSION} --install-location / OpenSCToken.pkg +pkgbuild --root ${BUILDPATH}/target_startup --component-plist MacOSX/target_startup.plist --identifier org.opensc-project.startup --version ${PACKAGE_VERSION} --install-location / OpenSC-startup.pkg + +# Build product +productbuild --distribution MacOSX/Distribution.xml --package-path . --resources MacOSX/resources "${imagedir}/OpenSC ${PACKAGE_VERSION}.pkg" + +# Sign installer +if test -n "${INSTALLER_SIGN_IDENTITY}"; then + productsign --sign "${INSTALLER_SIGN_IDENTITY}" "${imagedir}/OpenSC ${PACKAGE_VERSION}.pkg" "${BUILDPATH}/OpenSC ${PACKAGE_VERSION}.pkg" + mv "${BUILDPATH}/OpenSC ${PACKAGE_VERSION}.pkg" "${imagedir}/OpenSC ${PACKAGE_VERSION}.pkg" +fi + +# Build "Uninstaller" +osacompile -o "${imagedir}/OpenSC Uninstaller.app" "MacOSX/OpenSC_Uninstaller.applescript" +if test -n "${CODE_SIGN_IDENTITY}"; then + codesign --force --sign "${CODE_SIGN_IDENTITY}" --entitlements MacOSX/OpenSC_applescripts.entitlements --deep --timestamp --options runtime "${imagedir}/OpenSC Uninstaller.app" +fi + +# Create .dmg +rm -f OpenSC-${PACKAGE_VERSION}.dmg +i=0 +while ! hdiutil create -srcfolder "${imagedir}" -volname "${PACKAGE_NAME}" -fs JHFS+ OpenSC-${PACKAGE_VERSION}.dmg +do + i=$[$i+1] + if [ $i -gt 2 ] + then + exit 1 + fi +done +rm -rf ${imagedir} diff --git a/MacOSX/build-openssl-macos.sh b/MacOSX/build-openssl-macos.sh new file mode 100644 index 0000000000..dd2b63fefe --- /dev/null +++ b/MacOSX/build-openssl-macos.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +BUILDPATH=${PWD} +PREFIX=/Library/OpenSC +export MACOSX_DEPLOYMENT_TARGET="10.13" + +# parse arguments: options -b/--buildpath and -p/--prefix +while [ $# -gt 0 ]; do + case "$1" in + -b|--buildpath) + BUILDPATH="$2" + if ! shift 2; then + echo "Error: Missing argument for $1" >&2 + exit 1 + fi + ;; + -p|--prefix) + PREFIX="$2" + if ! shift 2; then + echo "Error: Missing argument for $1" >&2 + exit 1 + fi + ;; + *) + echo "Unknown option: $1" >&2 + echo "Usage: $0 [-b|--buildpath ] [-p|--prefix ]" >&2 + exit 1 + ;; + esac +done + +pushd $BUILDPATH +if ! test -e openssl; then + git clone --depth=1 https://github.com/openssl/openssl.git -b openssl-3.5 +fi + +pushd openssl +./Configure darwin64-x86_64 no-shared no-module no-apps --prefix=$PREFIX enable-ec_nistp_64_gcc_128 +make clean +make -j 4 +make DESTDIR=$BUILDPATH/openssl_bin install_sw +make clean + +./Configure darwin64-arm64 no-shared no-module no-apps --prefix=$PREFIX enable-ec_nistp_64_gcc_128 +make -j 4 +make DESTDIR=$BUILDPATH/openssl_arm64 install_sw + +lipo -create $BUILDPATH/openssl_arm64/$PREFIX/lib/libcrypto.a $BUILDPATH/openssl_bin/$PREFIX/lib/libcrypto.a -output libcrypto.a +lipo -create $BUILDPATH/openssl_arm64/$PREFIX/lib/libssl.a $BUILDPATH/openssl_bin/$PREFIX/lib/libssl.a -output libssl.a +mv libcrypto.a $BUILDPATH/openssl_bin/$PREFIX/lib/libcrypto.a +mv libssl.a $BUILDPATH/openssl_bin/$PREFIX/lib/libssl.a + +popd +popd \ No newline at end of file diff --git a/MacOSX/build-package.in b/MacOSX/build-package.in deleted file mode 100755 index e0ab9fb30f..0000000000 --- a/MacOSX/build-package.in +++ /dev/null @@ -1,119 +0,0 @@ -#!/bin/bash -# Building the installer is only tested and supported on 10.9+ with Xcode 6.0.1 -# Built package targets 10.10 -# Building should also work on older versions with older revisions or slight changes, YMMV - -# You need to have the following from homebrew or macports or fink: -# autoconf automake libtool pkg-config - -set -ex -test -x ./configure || ./bootstrap -BUILDPATH=${PWD} - -# Locate the latest OSX SDK -SDK_PATH=$(xcrun --sdk macosx --show-sdk-path) - -# Set SDK path -export CFLAGS="$CFLAGS -isysroot $SDK_PATH -arch x86_64 -mmacosx-version-min=10.10" - -export SED=/usr/bin/sed -PREFIX=/Library/OpenSC -export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/lib/pkgconfig - -if ! pkg-config libcrypto --atleast-version=1.0.1; then - # OpenSSL is not installed - if ! test -e $BUILDPATH/openssl_bin/$PREFIX/lib/pkgconfig; then - # Build OpenSSL manually, because Apple's binaries are deprecated - if ! test -e openssl; then - git clone --depth=1 https://github.com/openssl/openssl.git -b OpenSSL_1_0_2-stable - fi - cd openssl - KERNEL_BITS=64 ./config --prefix=$PREFIX -mmacosx-version-min=10.10 - make clean - make update - make depend - make - make INSTALL_PREFIX=$BUILDPATH/openssl_bin install_sw - cd .. - fi - export OPENSSL_CFLAGS="`env PKG_CONFIG_PATH=$BUILDPATH/openssl_bin/$PREFIX/lib/pkgconfig PKG_CONFIG_SYSROOT_DIR=$BUILDPATH/openssl_bin pkg-config --static --cflags libcrypto`" - export OPENSSL_LIBS="` env PKG_CONFIG_PATH=$BUILDPATH/openssl_bin/$PREFIX/lib/pkgconfig PKG_CONFIG_SYSROOT_DIR=$BUILDPATH/openssl_bin pkg-config --static --libs libcrypto`" -fi - -if ! test -e $BUILDPATH/openpace_bin/$PREFIX/lib/pkgconfig; then - if ! test -e openpace; then - git clone --depth=1 https://github.com/frankmorgner/openpace.git - fi - cd openpace - autoreconf -vis - ./configure --disable-shared --prefix=$PREFIX CRYPTO_CFLAGS="$OPENSSL_CFLAGS" CRYPTO_LIBS="$OPENSSL_LIBS" - make DESTDIR=$BUILDPATH/openpace_bin install - cd .. - export OPENPACE_CFLAGS="`env PKG_CONFIG_PATH=$BUILDPATH/openpace_bin/$PREFIX/lib/pkgconfig PKG_CONFIG_SYSROOT_DIR=$BUILDPATH/openpace_bin pkg-config --static --cflags libeac` $OPENSSL_CFLAGS" - export OPENPACE_LIBS="` env PKG_CONFIG_PATH=$BUILDPATH/openpace_bin/$PREFIX/lib/pkgconfig PKG_CONFIG_SYSROOT_DIR=$BUILDPATH/openpace_bin pkg-config --static --libs libeac` $OPENSSL_LIBS" -fi - -if ! test -e ${BUILDPATH}/target/$PREFIX/lib/pkgconfig; then - ./configure --prefix=$PREFIX \ - --sysconfdir=$PREFIX/etc \ - --enable-cvcdir=$PREFIX/etc/cvc \ - --enable-x509dir=$PREFIX/etc/x509 \ - --disable-dependency-tracking \ - --enable-shared \ - --disable-static \ - --enable-strict \ - --disable-assert \ - --enable-sm # TODO: remove this (must be sensible default in master) - - # always make clean - make clean - - # compile - make -j 2 - - # copy files - rm -rf target - make install DESTDIR=${BUILDPATH}/target - - # remove garbage - rm -f target/$PREFIX/lib/*.la - - # generate .bundle (required by Adobe Acrobat) - ./MacOSX/libtool-bundle target/$PREFIX/lib/opensc-pkcs11.so target/$PREFIX/lib -fi - -# Check out OpenSC.tokend, if not already fetched. -if ! test -e OpenSC.tokend; then - git clone http://github.com/OpenSC/OpenSC.tokend.git -fi - -# Create the symlink to OpenSC sources -test -L OpenSC.tokend/build/opensc-src || ln -sf ${BUILDPATH}/src OpenSC.tokend/build/opensc-src - -# Build and copy OpenSC.tokend -xcodebuild -target OpenSC -configuration Deployment -project OpenSC.tokend/Tokend.xcodeproj install DSTROOT=${PWD}/target - -# Prepare target root -# The "UnInstaller" -mkdir -p target/usr/local/bin -cp MacOSX/opensc-uninstall target/usr/local/bin - -# Build package -pkgbuild --root target --scripts MacOSX/scripts --identifier org.opensc-project.mac --version @PACKAGE_VERSION@ --install-location / OpenSC.pkg -# Build product -productbuild --distribution MacOSX/Distribution.xml --package-path . --resources MacOSX/resources "OpenSC @PACKAGE_VERSION@.pkg" - -# Build "uninstaller" -osacompile -o "OpenSC Uninstaller.app" "MacOSX/OpenSC_Uninstaller.applescript" - -# Create .dmg -rm -f OpenSC-@PACKAGE_VERSION@.dmg -i=0 -while ! hdiutil create -srcfolder "OpenSC @PACKAGE_VERSION@.pkg" -srcfolder "OpenSC Uninstaller.app" -volname "@PACKAGE_NAME@" OpenSC-@PACKAGE_VERSION@.dmg -do - i=$[$i+1] - if [ $i -gt 2 ] - then - exit 1 - fi -done diff --git a/MacOSX/notarize b/MacOSX/notarize new file mode 100755 index 0000000000..42c9e20f27 --- /dev/null +++ b/MacOSX/notarize @@ -0,0 +1,23 @@ +#!/bin/bash +# Notarize and staple the macOS installer image. +# +# This is only tested and supported on macOS 10.10 or later, using Xcode 6.0.1. +# Building should also work on older macOS versions with slight changes; YMMV. + +set -ex +BUILDPATH=${PWD} + +# keep in sync with MacOSX/build +while IFS='=' read -r key value; do + # Skip empty or comment lines + [[ -z "$key" || "$key" =~ ^# ]] && continue + export "$key=$value" +done < $BUILDPATH/VERSION.mk + +# keep in sync with MacOSX/build +export PACKAGE_VERSION=${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_FIX}${PACKAGE_SUFFIX} + +if test -n "${NOTARIZATION_PASSWORD}" -a -n "${DEVELOPMENT_TEAM}"; then + xcrun notarytool submit --team-id ${DEVELOPMENT_TEAM} --apple-id ${APPLE_ID} --password ${NOTARIZATION_PASSWORD} --wait OpenSC-${PACKAGE_VERSION}.dmg + xcrun stapler staple OpenSC-${PACKAGE_VERSION}.dmg +fi diff --git a/MacOSX/opensc-uninstall b/MacOSX/opensc-uninstall index 2dcfcec9e7..317c919ece 100755 --- a/MacOSX/opensc-uninstall +++ b/MacOSX/opensc-uninstall @@ -6,9 +6,19 @@ if [ "$(id -u)" != "0" ]; then exit 1 fi -# Remove symlinks to commands -for file in /Library/OpenSC/bin/*; do - test -L "/usr/local/bin/$(basename $file)" && rm -f "/usr/local/bin/$(basename $file)" +pluginkit -r -i org.opensc-project.mac.opensctoken.OpenSCTokenApp.OpenSCToken + +for f in \ + /Library/OpenSC/bin/* \ + /Library/OpenSC/etc/bash_completion.d/* \ + /Library/OpenSC/share/doc/opensc \ + /Library/OpenSC/share/man/man1/* \ + /Library/OpenSC/share/man/man5/* +do + a=/Library/OpenSC + b=/usr/local + l="${f/$a/$b}" + test -L "$l" && rm -f "$l" done # Remove pkcs11 libraries @@ -16,15 +26,30 @@ rm -f /usr/local/lib/opensc-pkcs11.so rm -f /usr/local/lib/onepin-opensc-pkcs11.so # Remove installed files +rm -rf /Applications/OpenSCTokenApp.app +rm -rf "/Applications/OpenSC Notify.app" +rm -rf /Applications/Utilities/OpenSCTokenApp.app +rm -rf "/Applications/Utilities/OpenSC Notify.app" rm -rf /Library/OpenSC rm -rf /Library/Security/tokend/OpenSC.tokend +rm -f /Library/LaunchAgents/org.opensc-project.mac.pkcs11-register.plist +rm -f /Library/LaunchAgents/org.opensc-project.mac.opensc-notify.plist rm -rf /System/Library/Security/tokend/OpenSC.tokend -# delete receipts on 10.6+ -for file in /var/db/receipts/org.opensc-project.mac.bom /var/db/receipts/org.opensc-project.mac.plist; do - test -f $file && rm -f $file +# Remove LaunchAgents +for label in \ + org.opensc-project.mac.pkcs11-register \ + org.opensc-project.mac.opensc-notify +do + launchctl asuser "$(id -u "${SUDO_USER:-$USER}")" launchctl remove "$label" done +# delete receipts on 10.6+ +pkgutil --forget org.opensc-project.mac > /dev/null 2>/dev/null +pkgutil --forget org.opensc-project.tokend > /dev/null 2>/dev/null +pkgutil --forget org.opensc-project.mac.opensctoken > /dev/null 2>/dev/null +pkgutil --forget org.opensc-project.startup > /dev/null 2>/dev/null + # remove this script rm -f /usr/local/bin/opensc-uninstall echo "OpenSC has been removed from your system. See you again!" diff --git a/MacOSX/scripts/postinstall b/MacOSX/scripts/postinstall index fed5aa8649..fc6524d62f 100755 --- a/MacOSX/scripts/postinstall +++ b/MacOSX/scripts/postinstall @@ -1,22 +1,63 @@ #!/bin/bash -cp /Library/OpenSC/lib/opensc-pkcs11.so /usr/local/lib/opensc-pkcs11.so -cp /Library/OpenSC/lib/onepin-opensc-pkcs11.so /usr/local/lib/onepin-opensc-pkcs11.so -if [ -e "/Library/OpenSC/etc/opensc.conf.md5" ] -then - read cs_fromfile file < "/Library/OpenSC/etc/opensc.conf.md5" - cs_calculated=$( md5 -q "/Library/OpenSC/etc/opensc.conf") - if [ "$cs_fromfile" = "$cs_calculated" ] - then - mv /Library/OpenSC/etc/opensc.conf.orig /Library/OpenSC/etc/opensc.conf - md5 -r /Library/OpenSC/etc/opensc.conf > /Library/OpenSC/etc/opensc.conf.md5 - fi -else - mv /Library/OpenSC/etc/opensc.conf.orig /Library/OpenSC/etc/opensc.conf - md5 -r /Library/OpenSC/etc/opensc.conf > /Library/OpenSC/etc/opensc.conf.md5 -fi -for f in /Library/OpenSC/bin/* +# copy libs to /usr/local/lib +cp /Library/OpenSC/lib/opensc-pkcs11.so \ + /Library/OpenSC/lib/onepin-opensc-pkcs11.so \ + /usr/local/lib/ + +# install opensc.conf if it hasn't been locally modified +# shellcheck disable=SC2043 +for f in /Library/OpenSC/etc/opensc.conf; do + if [ -e "${f}.md5" ]; then + read -r cs_fromfile _ < "${f}.md5" + cs_calculated="$(md5 -q "${f}")" + if [ "$cs_fromfile" != "$cs_calculated" ]; then + echo "config ${f} was locally modified since last install, skipping" 2>&1 + continue + fi + fi + cp "${f}.orig" "$f" + md5 -r "$f" >"${f}.md5" +done + +# symlink other files to /usr/local +for f in \ + /Library/OpenSC/bin/* \ + /Library/OpenSC/etc/bash_completion.d/* \ + /Library/OpenSC/share/doc/* +do + [ -e "$f" ] || continue # keep this or set "shopt -s nullglob" + a=/Library/OpenSC + b=/usr/local + l="${f/$a/$b}" # parameter expansion, returns $f where $a is replaced by $b + mkdir -p "$(dirname "$l")" + ln -sf "$f" "$l" +done + +# correct past issue where a literal shell glob character was symlinked +# e.g. /usr/local/share/man/man1/* -> /Library/OpenSC/share/man/man1/* +# maybe remove this step post 2022? +for f in \ + '/usr/local/share/man/man1/*' \ + '/usr/local/share/man/man5/*' do - ln -sf $f /usr/local/bin + [ -L "$f" ] || continue # skip unless $f is a symlink + t="$(readlink "$f")" + [ -e "$t" ] && continue # skip if the symlink target actually exists + a=/usr/local + b=/Library/OpenSC + [ "$t" = "${f/$a/$b}" ] || continue # skip unless the target is in the corresponding /Library/OpenSC subdirectory + # we can now assume that we originally made $f and can safely remove it + unlink "$f" done + +# register the launch agents +for f in \ + /Library/LaunchAgents/org.opensc-project.mac.pkcs11-register.plist \ + /Library/LaunchAgents/org.opensc-project.mac.opensc-notify.plist +do + [ -e "$f" ] || continue + /bin/launchctl asuser "$(id -u "$USER")" /bin/launchctl load "$f" || true +done + exit 0 diff --git a/MacOSX/target.plist b/MacOSX/target.plist new file mode 100644 index 0000000000..ef1b4b82c6 --- /dev/null +++ b/MacOSX/target.plist @@ -0,0 +1,18 @@ + + + + + + BundleHasStrictIdentifier + + BundleIsRelocatable + + BundleIsVersionChecked + + BundleOverwriteAction + upgrade + RootRelativeBundlePath + Library/OpenSC/Applications/NotificationProxy.app + + + diff --git a/MacOSX/target_startup.plist b/MacOSX/target_startup.plist new file mode 100644 index 0000000000..5dd5da85fd --- /dev/null +++ b/MacOSX/target_startup.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/MacOSX/target_token.plist b/MacOSX/target_token.plist new file mode 100644 index 0000000000..2c2aa16f66 --- /dev/null +++ b/MacOSX/target_token.plist @@ -0,0 +1,27 @@ + + + + + + BundleHasStrictIdentifier + + BundleIsRelocatable + + BundleIsVersionChecked + + BundleOverwriteAction + upgrade + ChildBundles + + + BundleOverwriteAction + + RootRelativeBundlePath + Applications/Utilities/OpenSCTokenApp.app/Contents/PlugIns/OpenSCToken.appex + + + RootRelativeBundlePath + Applications/Utilities/OpenSCTokenApp.app + + + diff --git a/Makefile.am b/Makefile.am index 3ffc465bc0..9c6c53a5b6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,14 +11,18 @@ MAINTAINERCLEANFILES = \ $(srcdir)/m4/ltversion.m4 $(srcdir)/m4/lt~obsolete.m4 \ $(srcdir)/m4/ltoptions.m4 \ $(srcdir)/packaged -EXTRA_DIST = Makefile.mak +EXTRA_DIST = Makefile.mak VERSION.mk + +DISTCHECK_CONFIGURE_FLAGS = --with-completiondir=/tmp SUBDIRS = etc src win32 doc MacOSX +if ENABLE_INTEGRATION_TESTS +SUBDIRS += tests +endif + dist_noinst_SCRIPTS = bootstrap bootstrap.ci dist_noinst_DATA = README \ - solaris/Makefile solaris/README solaris/checkinstall.in \ - solaris/opensc.conf-dist solaris/pkginfo.in solaris/proto \ packaging/debian.templates/changelog \ packaging/debian.templates/compat \ packaging/debian.templates/control \ @@ -28,6 +32,10 @@ dist_noinst_DATA = README \ packaging/debian.templates/rules dist_doc_DATA = NEWS +include $(top_srcdir)/aminclude_static.am +clean-local: code-coverage-clean +distclean-local: code-coverage-dist-clean + Generate-ChangeLog: rm -f ChangeLog.tmp "$(srcdir)/ChangeLog" test -n "$(GIT)" diff --git a/Makefile.mak b/Makefile.mak index f76f33e608..0b4f486322 100644 --- a/Makefile.mak +++ b/Makefile.mak @@ -2,5 +2,8 @@ SUBDIRS = etc win32 src default: all +opensc.msi: all + cd win32 && $(MAKE) /nologo /f Makefile.mak opensc.msi && cd .. + all clean:: - @for %i in ( $(SUBDIRS) ) do @cmd /c "cd %i && $(MAKE) /nologo /f Makefile.mak $@" + @for %%i in ( $(SUBDIRS) ) do ( cd %%i && $(MAKE) /nologo /f Makefile.mak $@ && cd ..) diff --git a/NEWS b/NEWS index de485baa72..13d19b1571 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,791 @@ NEWS for OpenSC -- History of user visible changes -# New in 0.17.0; 2016-07-18 +# New in 0.27.1; 2026-03-31 + +* Bugfix release to fix up infrastructure issues. + +# New in 0.27.0; 2026-03-30 + +## Security +* CVE-2025-13763: Several uses of potentially uninitialized memory detected by fuzzers +* CVE-2025-49010: Possible write beyond buffer bounds during processing of GET RESPONSE APDU +* CVE-2025-66215: Possible write beyond buffer bounds in oberthur driver +* CVE-2025-66038: Possible read beyond buffer bounds when parsing historical bytes in PIV driver +* CVE-2025-66037: Possible buffer overrun while parsing SPKI +* More low-severity data handling issues when parsing profile configuration + +## General improvements +* Added support for PKCS#11 3.2 in tools and pkcs11-spy and p11test(#3510) +* Added support for Ed448, X448 mechanisms and improve support for + Edwards and montgomery keys in general (#3090) +* Support CKA_PUBKEY_KEY_INFO PKCS#11 attribute (#3090) +* Various refactoring of autotools build system +* Remove obsolete tokend support (#3285) +* Run tests against different software PKCS#11 tokens kryoptic and NSS softokn (#3365) +* Removed internal caching for current EF/DF (#3403) +* Correctly detect OS-level FIPS mode in OpenSSL automatically (#3551) + or through custom configuration file (#3525) +* Added support for Brainpool twisted curves to pkcs11-tool and SC-HSM (#3601) + +## PC/SC +* Handle case when smart card is removed and inserted between two subsequent calls to + `refresh_attributes()` (#2803) + +## EsteID +* Add support for EstEID 2025 (#3392) +* Implement FinEID 4.0/4.1 support (#3505) +* Add Latvian IDEMIA Cosmo X card support (#3503) +* Check if PIN is locked and hint CKF_USER_PIN_TO_BE_CHANGED (#3490) +* Remove obsolete FinEID cards (#3522) +* Add Latvian Cosmo 8.2 card support (#3521) + +## D-Trust +* Prevent unncecessary pin prompts on pinpad readers (#3266) +* Support for D-Trust Card 5.1 & 5.4 (#3137) +* Implement PIN change and unblock in dtrust-tool (#3137) + +## Belpic +* Add supports for belpic applet version 1.8 (#3308) + +## OpenPGP +* Implement key derived PIN format (KDF-DO) as per OpenPGP card spec v3.3 (#3398) + +## IDPrime +* Implement 5110+ FIPS and 5110 CC (940) derive support (#3483) + +## Windows +* Update to Wix 6 (#3435) +* Fix C_WaitForSlotEvent() not working in Windows (#2919) +* remove pkcs11-register from autostart (#3354) + +## MacOS +* Installer images are now notarized (#3536) + +## pkcs11-tool +* Added support for ML-DSA, ML-KEM, SLH-DSA keys from PKCS#11 3.2 (#3510) +* Improve support for Edwards and montgomery keys and + add derive key support for CKK_MONTGOMERY (#3090) +* Add support for ChaCha20 and Poly1305 (#3339) +* Add support for AES CTR in decrypt_data() and encrypt_data() (#3338) +* Add initial support for PKCS#11 URIs (#3289) +* Print more information about RSA keys (#3623) + +# New in 0.26.1; 2025-01-14 + +## General improvements +* Align allocations of sc_mem_secure_alloc (#3281) +* Fix -O3 gcc optimization failure on amd64 and ppc64el (#3299) + +## pkcs11-spy +* Avoid crash while spying C_GetInterface() (#3275) + +## TCOS +* Fix reading certificate (#3296) + +# New in 0.26.0; 2024-11-13 + +## Security +* CVE-2024-45615: Usage of uninitialized values in libopensc and pkcs15init (#3225) +* CVE-2024-45616: Uninitialized values after incorrect check or usage of APDU response values in libopensc (#3225) +* CVE-2024-45617: Uninitialized values after incorrect or missing checking return values of functions in libopensc (#3225) +* CVE-2024-45618: Uninitialized values after incorrect or missing checking return values of functions in pkcs15init (#3225) +* CVE-2024-45619: Incorrect handling length of buffers or files in libopensc (#3225) +* CVE-2024-45620: Incorrect handling of the length of buffers or files in pkcs15init (#3225) +* CVE-2024-8443: Heap buffer overflow in OpenPGP driver when generating key (#3219) + +## General improvements +* Fix reselection of DF after error in PKCS#15 layer (#3067) +* Unify OpenSSL logging throughout code (#2922) +* Extend the p11test to support kryoptic (#3141) +* Fix for error in PCSC reconnection (#3150) +* Fixed various issues reported by OSS-Fuzz and Coverity in drivers, PKCS#11 and PKCS#15 layer + +## PKCS#15 +* Documentation for PKCS#15 profile files (#3132) + +## minidriver +* Support PinCacheAlwaysPrompt usable for PIV cards (#3167) + +## pkcs11-tool +* Show URI when listing token information (#3125) and objects (#3130) +* Do not limit size of objects to 5000 bytes (#3174) +* Add support for AES CMAC (#3184) +* Add support for AES GCM encryption (#3195) +* Add support for RSA OAEP encryption (#3175) +* Add support for HKDF (#3193) +* Implement better support for wrapping and unwrapping (#3198) +* Add support for EdDSA sign and verify (#2979) + +## pkcs15-crypt +* Fix PKCS#1 encoding function to correctly detect padding type (#3075) + +## piv-tool +* Fix RSA key generation (#3158) +* Avoid possible state change when matching unknown card (#3112) + +## sc-hsm-tool +* Cleanse buffer with plaintext key share (#3226) + +## pkcs11-register +* Fix pkcs11-register defaults on macOS and Windows (#3053) + +## IDPrime +* Fix identification of IDPrime 840 cards (#3146) +* Fix container mapping for IDPrime 940 cards (#3220) +* Reorder ATRs for matching cards (#3154) + +## OpenPGP +* Fix state tracking after erasing card (#3024) + +## Belpic +* Disable Applet V1.8 (#3109) + +## MICARDO +* Deactivate driver (#3152) + +## SmartCard-HSM +* Fix signing with secp521r1 signature (#3157) + +## eOI +* Set model via `sc_card_ctl` function (#3189) + +## Rutoken +* increase the minimum PIN size to support Rutoken ECP BIO (#3208) + +## JPKI +* Adjust parameters for public key in PKCS#15 emulator (#3182) + +## D-Trust +* Add support for ECDSA signatures and ECDH key agreement for D-Trust Signatures Cards 4.1/4.4 (#3240, #3248) + +# New in 0.25.1; 2024-04-05 + +## General improvements +* Add missing file to dist tarball to build documentation (#3063) + +## minidriver +* Fix RSA decryption with PKCS#1 v1.5 padding (#3077) +* Fix crash when app is not set (#3084) + +# New in 0.25.0; 2024-03-06 +## Security +* [CVE-2023-5992](https://github.com/OpenSC/OpenSC/wiki/CVE-2023-5992): Side-channel leaks while stripping encryption PKCS#1.5 padding in OpenSC (#2948) +* [CVE-2024-1454](https://github.com/OpenSC/OpenSC/wiki/CVE-2024-1454): Potential use-after-free in AuthentIC driver during card enrollment in pkcs15init (#2962) + +## General improvements +* Update OpenSSL 1.1.1 to 3.0 in MacOS build (#2930) +* Remove support for old card drivers Akis, GPK, Incrypto34 and Westcos, disable Cyberflex driver (#2885) +* Fix 64b to 32b conversions (#2993) +* Improvements for the p11test (#2991) +* Fix reader initialization without SCardControl (#3007) +* Make RSA PKCS#1 v1.5 depadding constant-time (#2948) +* Add option for disabling PKCS#1 v1.5 depadding (type 01 and 02) on the card (#2975) +* Enable MSI signing via Signpath CI integration for Windows (#2799) +* Fixed various issues reported by OSS-Fuzz and Coverity in drivers, PKCS#11 and PKCS#15 layer + +## minidriver +* Fix wrong hash selection (#2932) + +## pkcs11-tool +* Simplify printing EC keys parameters (#2960) +* Add option to import GENERIC key (#2955) +* Add support for importing Ed25518/448 keys (#2985) +## drust-tool +* Add tool for D-Trust cards (#3026, #3051) +## IDPrime +* Support uncompressed certificates on IDPrime 940 (#2958) +* Enhance IDPrime logging (#3003) +* Add SafeNet 5110+ FIPS token support (#3048) +## D-Trust Signature Cards +* Add support for RSA D-Trust Signature Card 4.1 and 4.4 (#2943) +## EstEID +* Remove expired EstEID 3.* card support (#2950) +## ePass2003 +* Allow SW implementation with more SHA2 hashes and ECDSA (#3012) +* Fix EC key generation (#3045) +## SmartCard-HSM +* Fix SELECT APDU command (#2978) +## MyEID +* Update for PKCS#15 profile (#2965) +## Rutoken +* Support for RSA 4096 key algorithm (#3011) +## OpenPGP +* Fix decryption requiting Manage Security Environment for authentication key (#3042) + +# New in 0.24.0; 2023-12-13 +## Security +* CVE-2023-40660: Fix Potential PIN bypass (#2806, frankmorgner/OpenSCToken#50, #2807) +* CVE-2023-40661: Important dynamic analyzers reports +* CVE-2023-4535: Out-of-bounds read in MyEID driver handling encryption using symmetric keys (f1993dc4) +## General improvements +* Fix compatibility of EAC with OpenSSL 3.0 (#2674) +* Enable `use_file_cache` by default (#2501) +* Use custom libctx with OpenSSL >= 3.0 (#2712, #2715) +* Fix record-based files (#2604) +* Fix several race conditions (#2735) +* Run tests under Valgrind (#2756) +* Test signing of data bigger than 512 bytes (#2789) +* Update to OpenPACE 1.1.3 (#2796) +* Implement logout for some of the card drivers (#2807) +* Fix wrong popup position of opensc-notify (#2901) +* Fixed various issues reported by OSS-Fuzz and Coverity regarding card drivers, PKCS#11 and PKCS#15 init +## PKCS#11 +* Check card presence state in `C_GetSessionInfo` (#2740) +* Remove `onepin-opensc-pkcs11` module (#2681) +* Do not use colons in the token info label (#2760) +* Present profile objects in all slots with the CKA_TOKEN attribute to resolve issues with NSS (#2928, #2924) +* Use secure memory for PUK (#2906) +* Don't logout to preserve concurrent access from different processes (#2907) +* Add more examples to manual page (#2936) +* Present profile objects in all virtual slots (#2928) +* Provide CKA_TOKEN attribute for profile objects (#2924) +* Improve --slot parameter documentation (#2951) +## PKCS#15 +* Honor cache offsets when writing file cache (#2858) +* Prevent needless amount of PIN prompts from pkcs15init layer (#2916) +* Propagate CKA_EXTRACTABLE and SC_PKCS15_PRKEY_ACCESS_SENSITIVE from and back to PKCS#11 (#2936) +## Minidriver +* Fix for private keys that do not need a PIN (#2722) +* Unbreak decipher when the first null byte of PKCS#1.5 padding is missing (#2939) +## pkcs11-tool +* Fix RSA key import with OpenSSL 3.0 (#2656) +* Add support for attribute filtering when listing objects (#2687) +* Add support for `--private` flag when writing certificates (#2768) +* Add support for non-AEAD ciphers to the test mode (#2780) +* Show CKA_SIGN attribute for secret keys (#2862) +* Do not attempt to read CKA_ALWAYS_AUTHENTICATE on secret keys (#2864, #2913) +* Show Sign/VerifyRecover attributes (#2888) +* Add option to import generic keys (#2955) +## westcos-tool +* Generate 2k RSA keys by default (b53fc5cd) +## pkcs11-register +* Disable autostart on Linux by default (#2680) +## IDPrime +* Add support for IDPrime MD 830, 930 and 940 (#2666) +* Add support for SafeNet eToken 5110 token (#2812) +* Process index even without keyrefmap and use correct label for second PIN (#2878) +* Add support for Gemalto IDPrime 940C (#2941) +## EPass2003 +* Change of PIN requires verification of the PIN (#2759) +* Fix incorrect CMAC computation for subkeys (#2759, issue #2734) +* Use true random number for mutual authentication for SM (#2766) +* Add verification of data coming from the token in the secure messaging mode (#2772) +* Avoid success when using unsupported digest and fix data length for RAW ECDSA signatures (#2845) +## OpenPGP +* Fix select data command (#2753, issue #2752) +* Unbreak ed/curve25519 support (#2892) +## eOI +* Add support for Slovenian eID card (eOI) (#2646) +## Italian CNS +* Add support for IDEMIA (Oberthur) tokens (#2483) +## PIV +* Add support for Swissbit iShield FIDO2 Authenticator (#2671) +* Implement PIV secure messaging (#2053) +## SkeID +* Add support for Slovak eID cards (#2672) +## isoApplet +* Support ECDSA with off-card hashing (#2642) +## MyEID +* Fix WRAP operation when using T0 (#2695) +* Identify changes on the card and enable `use_file_cache` (#2798) +* Workaround for unwrapping using 2K RSA key (#2921) +## SC-HSM +* Add support for `opensc-tool --serial` (#2675) +* Fix unwrapping of 4096 keys with handling reader limits (#2682) +* Indicate supported hashes and MGF1s (#2827) + +# Addendum for 0.22.0; 2023-09-01 +* fixed security problems + * CVE-2021-42778 Heap double free in sc_pkcs15_free_tokeninfo + * CVE-2021-42779 Heap use after free in sc_file_valid + * CVE-2021-42780 Use after return in insert_pin function + * CVE-2021-42781 Heap buffer overflow in pkcs15-oberthur.c + * CVE-2021-42782 Stack buffer overflow issues in various places + * CVE-2021-34193 is a duplicate CVE covering the 5 individual CVEs listed above + +# New in 0.23.0; 2022-11-29 +## General improvements +* Support signing of data with a length of more than 512 bytes (#2314) +* By default, disable support for old card drivers (#2391) and remove support for old drivers MioCOS and JCOP (#2374) +* Bump minimal required OpenSSL version to 1.1.1 and add support for OpenSSL 3.0 (#2438, #2506) +* Compatibility with LibreSSL (#2495, #2595) +* Remove support for DSA (#2503) +* Extend p11test to support symmetric keys (#2430) +* Notice detached reader on macOS (#2418) +* Support for OAEP padding (#2475, #2484) +* Fix for PSS salt length (#2478) +* Improve fuzzing by adding new tests (#2417, #2500, #2520, #2550, #2637) +* Fixed various issues reported by OSS-Fuzz and Coverity regarding card drivers, PKCS#11 and PKCS#15 init +* Fix issues with OpenPACE (#2472) +* Containers support for local testing +* Add support for encryption and decryption using symmetric keys (#2473, #2607) +* Stop building support for Gost algorithms with OpenSSL 3.0 as they require deprecated API (#2586) +* Fix detection of disconnected readers in PCSC (#2600) +* Add configuration option for on-disk caching of private data (#2588) +* Skip building empty binaries when dependencies are missing and remove needless linking (#2617) +* Define arm64 as a supported architecture in the Installer package (#2610) +## PKCS#11 +* Implement `C_CreateObject` for EC keys and fix signature verification for `CKM_ECDSA_SHAx` cards (#2420) +## pkcs11-tool +* Add more elliptic curves (#2301) +* Add support for symmetric encrypt and decrypt, wrap and unwrap operations, and initialization vector (#2268) +* Fix consistent handling of secret key attributes (#2497) +* Add support for signing and verifying with HMAC (#2385) +* Add support for SHA3 (#2467) +* Make object selectable via label (#2570) +* Do not require an R/W session for some operations and add `--session-rw` option (#2579) +* Print more information: CKA_UNIQUE_ID attribute, SHA3 HMACs and serial number for certificates (#2644, #2643, #2641) +* Add new option --undestroyable to create keys with CKA_DESTROYABLE=FALSE (#2645) +## sc-hsm-tool +* Add options for public key authentication (#2301) +## Minidriver +* Fix reinit of the card (#2525) +* Add an entry for Italian CNS (e) (#2548) +* Fix detection of ECC mechanisms (#2523) +* Fix ATRs before adding them to the windows registry (#2628) +## NQ-Applet +* Add support for the JCOP4 Cards with NQ-Applet (#2425) +## ItaCNS +* Add support for ItaCMS v1.1 (key length 2048) (#2371) +## Belpic +* Add support for applet v1.8 (#2455) +## Starcos +* Add ATR for V3.4 (#2464) +* Add PKCS#15 emulator for 3.x cards with eSign app (#2544) +* Add (fix) support for eGK v 2.1 (#2871) +## ePass2003 +* Fix PKCS#15 initialization (#2403) +* Add support for FIPS (#2543) +* Fix matching with newer versions and tokens initialized with OpenSC (#2575) +## MyEID +* Support logout operation (#2557) +* Support for symmetric encryption and decryption (#2473, #2607) +## GIDS +* Fix decipher for TPM (#1881) +## OpenPGP +* Get the list of supported algorithms from algorithm information on the card (#2287) +* Support for 3 certificates with OpenPGP 3+ (#2103) +## nPA +* Fix card detection (#2463) +## Rutoken +* Fix formatting rtecp cards (#2599) +## PIV +* Add new PIVKey ATRs for current cards (#2602) + + +# New in 0.22.0; 2021-08-10 +## General improvements + * Use standard paths for file cache on Linux (#2148) and OSX (#2214) + * Various issues of memory/buffer handling in legacy drivers mostly reported by oss-fuzz and coverity (tcos, oberthur, isoapplet, iasecc, westcos, gpk, flex, dnie, mcrd, authentic, belpic) + * Add threading test to `pkcs11-tool` (#2067) + * Add support to generate generic secret keys (#2140) + * `opensc-explorer`: Print information about LCS (Life cycle status byte) (#2195) + * Add support for Apple's arm64 (M1) binaries, removed TokenD. A separate installer with TokenD (and without arm64 binaries) will be available (#2179). + * Support for gcc11 and its new strict aliasing rules (#2241, #2260) + * Initial support for building with OpenSSL 3.0 (#2343) + * pkcs15-tool: Write data objects in binary mode (#2324) + * Avoid limited size of log messages (#2352) +## PKCS#11 + * Support for ECDSA verification (#2211) + * Support for ECDSA with different SHA hashes (#2190) + * Prevent issues in p11-kit by not returning unexpected return codes (#2207) + * Add support for PKCS#11 3.0: The new interfaces, profile objects and functions (#2096, #2293) + * Standardize the version 2 on 2.20 in the code (#2096) + * Fix CKA_MODIFIABLE and CKA_EXTRACTABLE (#2176) + * Copy arguments of C_Initialize (#2350) +## Minidriver + * Fix RSA-PSS signing (#2234) +## OpenPGP + * Fix DO deletion (#2215) + * Add support for (X)EdDSA keys (#1960) +## IDPrime + * Add support for applet version 3 and fix RSA-PSS mechanisms (#2205) + * Add support for applet version 4 (#2332) +## MyEID + * New configuration option for opensc.conf to disable pkcs1_padding (#2193) + * Add support for ECDSA with different hashes (#2190) + * Enable more mechanisms (#2178) + * Fixed asking for a user pin when formatting a card (#1737) +## IAS/ECC + * Added support for French CPx Healthcare cards (#2217) +## CardOS + * Added ATR for new CardOS 5.4 version (#2296) + +# New in 0.21.0; 2020-11-24 +## General Improvements +* fixed security problems + * CVE-2020-26570 (6903aebfddc466d966c7b865fae34572bf3ed23e) + * CVE-2020-26571 + * CVE-2020-26572 (9d294de90d1cc66956389856e60b6944b27b4817) +* Bump minimal required OpenSSL version to 1.0.1 (#1658) +* Implement basic unit tests for asn1 library, compression and simpletlv parser (#1830) +* Allow generating code coverage +* Improve fuzzing by providing corpus from real cards (#1830) +* Implement support for OAEP encryption +* New separate debug level for PIN commands (d06f23e8) +* Fix handling of card/reader insertion/removal events in pcscd +* Many bugfixes reported by oss-fuzz, coverity and lgtm.com +* Fixes of removed readers handling (#1970) +* Fix Firefox crash because of invalid pcsc context (#2077) +## PKCS#11 +* Return CKR_TOKEN_NOT_RECOGNIZED for not recognized cards (#2030) +* Propagate ignore_user_content to PKCS#11 layer not to confuse applications (#2040) +## Minidriver +* Fix check of ATR length (2-to 33 characters inclusive) (#2146) +## MacOS +* Add installer signing for PR and master +* Avoid app bundle relocations after installation +* Move OpenSC to MacOS Utilities folder (#2063) +## OpenSC tools +### pkcs11-tool +* Make SHA256 default for OAEP encryption +* pkcs11-tool: allow using SW tokens (#2113) +### opensc-explorer +* `asn1` accepts offsets and decode records (#2090) +* `cat` accepts records (#2090) +## OpenPGP +* Add new ec curves supported by GNUK (#1853) +* First steps supporting OpenPGP 3.4 +* Add support for EC key import (#1821) +## Rutoken +* Add ATR for Rutoken ECP SC NFC (#2122) +## CardOS +* Improve detection of various CardOS 5 configurations (#1987) +## DNIe +* Add new DNIe CA structure for the secure channel (#2109) +## ePass2003 +* Improve ECC support (#1859) +* Fixed erase sequence (#2097) +## IAS-ECC (#2070): +* Fixed support for Idemia Cosmo cards with AWP middleware interoperability (previously broken). +* Added support for Idemia Cosmo v8 cards. +* PIN padding settings are now used from PKCS#15 info when available. +* Added PIN-pad support for PIN unblock. +## IDPrime +* New driver for Gemalto IDPrime (only some types) (#1772) +## eDo +* New driver with initial support for Polish eID card (e-dowód, eDO) (#2023) +## MCRD +* Remove unused and broken RSA EstEID support (#2095) +## TCOS +* Add missing encryption certificates (#2083) +## PIV +* Add ATR of DOD Yubikey (#2115) +* fixed PIV global pin bug (#2142) +## CAC1 +* Support changing PIN with CAC Alt tokens (#2129) + +# New in 0.20.0; 2019-12-29 +## General Improvements +* fixed security problems + * CVE-2019-6502 (#1586) + * CVE-2019-15946 (a3fc769) + * CVE-2019-15945 (412a614) + * CVE-2019-19480 (6ce6152284c47ba9b1d4fe8ff9d2e6a3f5ee02c7) + * CVE-2019-19481 (b75c002cfb1fd61cd20ec938ff4937d7b1a94278) + * CVE-2019-19479 (c3f23b836e5a1766c36617fe1da30d22f7b63de2) +* Support RSA-PSS signature mechanisms using RSA-RAW (#1435) +* Added memory locking for secrets (#1491) +* added support for terminal colors (#1534) +* PC/SC driver: Fixed error handling in case of changing (#1537) or removing the card reader (#1615) +* macOS installer + * Add installer option to deselect tokend (#1607) + * Make OpenSCToken available on 10.12+ and the default on 10.15+ (2017626ed237dbdd4683a4b9410fc610618200c5) +* Configuration + * rename `md_read_only` to `read_only` and use it for PKCS#11 and Minidriver (#1467) + * allow global use of ignore_private_certificate (#1623) +* Build Environment + * Bump openssl requirement to 0.9.8 (##1459) + * Added support for fuzzing with AFL (#1580) and libFuzzer/OSS-Fuzz (#1697) + * Added CI tests for simulating GIDS, OpenPGP, PIV, IsoApplet (#1568) and MyEID (#1677) and CAC (#1757) + * Integrate clang-tidy with `make check` (#1673) + * Added support for reproducible builds (#1839) +## PKCS#11 +* Implement write protection (CKF_WRITE_PROTECTED) based on the card profile (#1467) +* Added C_WrapKey and C_UnwrapKey implementations (#1393) +* Handle CKA_ALWAYS_AUTHENTICATE when creating key objects. (#1539) +* Truncate long PKCS#11 labels with ... (#1629) +* Fixed recognition of a token when being unplugged and reinserted (#1875) +## Minidriver +* Register for CardOS5 cards (#1750) +* Add support for RSA-PSS (263b945) +## OpenSC tools +* Harmonize the use of option `-r`/`--reader` (#1548) +* `goid-tool`: GoID personalization with fingerprint +* `openpgp-tool` + * replace the options `-L`/` --key-length` with `-t`/`--key-type` (#1508) + * added options `-C`/`--card-info` and `-K`/`--key-info` (#1508) +* `opensc-explorer` + * add command `pin_info` (#1487) + * extend `random` to allow writing to a file (#1487) +* `opensc-minidriver-test.exe`: Tests for Microsoft CryptoAPI (#1510) +* `opensc-notify`: Autostart on Windows +* `pkcs11-register`: + * Auto-configuration of applications for use of OpenSC PKCS#11 (#1644) + * Autostart on Windows, macOS and Linux (#1644) +* `opensc-tool`: Show ATR also for cards not recognized by OpenSC (#1625) +* `pkcs11-spy`: + * parse CKM_AES_GCM + * Add support for CKA_OTP_* and CKM_*_PSS values + * parse EC Derive parameters (#1677) +* `pkcs11-tool` + * Support for signature verification via `--verify` (#1435) + * Add object type `secrkey` for `--type` option (#1575) + * Implement Secret Key write object (#1648) + * Add GOSTR3410-2012 support (#1654) + * Add support for testing CKM_RSA_PKCS_OAEP (#1600) + * Add extractable option to key import (#1674) + * list more key access flags when listing keys (#1653) + * Add support for `CKA_ALLOWED_MECHANISMS` when creating new objects and listing keys (#1628) +* `pkcs15-crypt`: * Handle keys with user consent (#1529) +## CAC1 +New separate CAC1 driver using the old CAC specification (#1502) +## CardOS +* Add support for 4K RSA keys in CardOS 5 (#1776) +* Fixed decryption with CardOS 5 (#1867) +## Coolkey +* Enable CoolKey driver to handle 2048-bit keys. (#1532) +## EstEID +* adds support for a minimalistic, small and fast card profile based on IAS-ECC issued since December 2018 (#1635) +## GIDS +* GIDS Decipher fix (#1881) +* Allow RSA 4K support (#1891) +## MICARDO +* Remove long expired EstEID 1.0/1.1 card support (#1470) +## MyEID +* Add support for unwrapping a secret key with an RSA key or secret key (#1393) +* Add support for wrapping a secret key with a secret key (#1393) +* Support for MyEID 4K RSA (#1657) +* Support for OsEID (#1677). +## Gemalto GemSafe +* add new PTeID ATRs (#1683) +* Add support for 4K RSA keys (#1863, #1872) +## OpenPGP +* OpenPGP Card v3 ECC support (#1506) +## Rutoken +* Add Rutoken ECP SC (#1652) +* Add Rutoken Lite (#1728) +## SC-HSM +* Add SmartCard-HSM 4K ATR (#1681) +* Add missing secp384r1 curve parameter (#1696) +## Starcos +* Fixed decipher with 2.3 (#1496) +* Added ATR for 2nd gen. eGK (#1668) +* Added new ATR for 3.5 (#1882) +* Detect and allow Globalplatform PIN encoding (#1882) +## TCOS +* Fix TCOS IDKey support (#1880) +* add encryption certificate for IDKey (#1892) +## Infocamere, Postecert, Cnipa +* Removed profiles (#1584) +## ACS ACOS5 +* Remove incomplete acos5 driver (#1622). + +# New in 0.19.0; 2018-09-13 +## General Improvements +* fixed multiple security problems (out of bound writes/reads, #1447): + * CVE-2018-16391 + * CVE-2018-16392 + * CVE-2018-16393 + * CVE-2018-16418 + * CVE-2018-16419 + * CVE-2018-16420 + * CVE-2018-16421 + * CVE-2018-16422 + * CVE-2018-16423 + * CVE-2018-16424 + * CVE-2018-16425 + * CVE-2018-16426 + * CVE-2018-16427 +* Improved documentation: + * New manual page for opensc.conf(5) + * Added several missing switches in manual pages and fixed formatting +* Win32 installer: + * automatically start SCardSvr + * added newer OpenPGP ATRs +* macOS installer: use HFS+ for backward compatibility +* Remove outdated solaris files +* PC/SC driver: + * Workaround OMNIKEY 3x21 and 6121 Smart Card Readers wrongly identified as pinpad readers in macOS +* Workaround cards returning short signatures without leading zeroes +* bash completion + * make location directory configurable + * Use a new correct path by default +* build: support for libressl-2.7+ +* Configuration + * Distribute minimal opensc.conf + * `pkcs11_enable_InitToken made` global configuration option + * Modify behavior of `OPENSC_DRIVER` environment variable to restrict driver list instead of forcing one driver and skipping vital parts of configuration + * Removed configuration options `zero_ckaid_for_ca_certs`, `force_card_driver`, `reopen_debug_file`, `paranoid-memory` + * Generalized configuration option `ignored_readers` +* If card initialization fails, continue card detection with other card drivers (#1251) +* Fixed long term card operations on Windows 8 and later (#1043) +* reader-pcsc: allow fixing the length of a PIN +* fixed multithreading issue on Window with OpenPACE OIDs +## PKCS#11 +* fixed crash during `C_WaitForSlotEvent` (#1335) +## Minidriver +* Allow cancelling the PIN pad prompt before starting the reader transaction. Whether to start the transaction immediately or not is user-configurable for each application +## OpenSC tools +* `opensc-notify` + * add Exit button to tray icon + * User better description (GenericName) and a generic application icon + * Do not display in the application list +* `pkcs15-tool` + * added support for reading ECDSA ssh keys +* `p11test` + * Filter certificates other than `CKC_X_509` +* `openpgp-tool` + * allow calling -d multiple times + * clarify usage text +## sc-hsm +* Implement RSA PSS +* Add support for SmartCard-HSM 4K (V3.0) +## CAC +* Remove support for CAC1 cards +* Ignore unknown tags in properties buffer +* Use GET PROPERTIES to recognize buffer formats +* Unbreak encoding last tag-len-value in the data objects +* Support HID Alt tokens without CCC + * They present certificates in OIDs of first AID and use other undocumented applets + * Inspect the tokens through the ACA applet and GET ACR APDU +## Coolkey +* Unbreak Get Challenge functionality +* Make uninitialized cards working as expected with ESC +## OpenPGP +* add serial number to card name +* include detailed version into card name +* define & set LCS (lifecycle support) as extended capability +* extend manufacturer list in pkcs15-openpgp.c +* correctly parse hist_bytes +* Make deciphering with AUT-key possible for OpenPGP Card >v3.2 (fixes #1352) +* Add supported algorithms for OpenPGP Card (Fixes #1432) +## Starcos +* added support for 2nd generation eGK (#1451) +## CardOS +* create PIN in MF (`pkcs15init`) +## German ID card +* fixed identifying unknown card as German ID card (#1360) +## PIV +* Context Specific Login Using Pin Pad Reader Fix +* Better Handling of Reset using Discovery Object + +# New in 0.18.0; 2018-05-16 +## General Improvements +* PKCS#15 + * fixed parsing ECC parameters from TokenInfo (#1134) + * Added PKCS#15 emulator for DIN 66291 profile + * Cope with empty serial number in TokenInfo +* Build Environment + * Treat compiler warnings as errors (use `--disable-strict` to avoid) + * MacOS + * optionally use CTK in package builder + * fixed detection of OpenPACE package + * macOS High Sierra: fixed dmg creation + * fixed DNIe UI compatibility +* Windows: Use Dedicated md/pkcs11 installation folders instead of installing to System32/SysWOW64 +* fixed (possible) memory leaks for PIV, JPKI, PKCS#11, Minidriver +* fixed many issues reported via compiler warnings, coverity scan and clang's static analyzer +* beautify printed ASN.1 data, add support for ASN.1 time types +* SimpleTLV: Skip correctly two bytes after reading 2b size (#1231) +* added support for `keep_alive` commands for cards with multiple applets to be enabled via `opensc.conf` +* added support for bash completion for arguments that expect filenames +* added keyword `old` for selecting `card_drivers` via `opensc.conf` +* improved documentation manuals for OpenSC tools +* use `leave` as default for `disconnect_action` for PC/SC readers +## PKCS#11 +* Make OpenSC PKCS#11 Vendor Defined attributes, mechanisms etc unique +## Minidriver +* added CNS ATR (#1153) +* Add multiple PINs support to minidriver +* protect MD entry points with `CriticalSection` +## Tokend +* Configuration value for not propagating certificates that require user authentication (`ignore_private_certificate`) +## CryptoTokenKit +* Added support for PIN pad +* fixed codesigning of opensc tools +* Added complete support for system integration with https://github.com/frankmorgner/OpenSCToken +## OpenSC Tools +* `cardos-tool` + * List human-readable version for CardOS 5.3 +* `pkcs11-tool` + * fixed overwriting digestinfo + hash for RSA-PKCS Signature + * Enable support for RSA-PSS signatures in pkcs11-tool + * Add support for RSA-OAEP + * Fixed #1286 + * Add missing pkcs11-tool options to man page + * allow mechanism to be specified in hexadecimal + * fixed default module path on Windows to use opensc-pkcs11.dll +* `pkcs11-spy` + * Add support for RSA-OAEP + * Add support for RSA-PSS +* `pkcs15init` + * Fix rutokenS FCP parsing (#1259) +* `egk-tool` + * Read data from German Health Care Card (Elektronische Gesundheitskarte, eGK) +* `opensc-asn1` + * Parse ASN.1 from files +* `opensc-tool`/`opensc-explorer` + * Allow extended APDUs +## Authentic +* Correctly handle APDUs with more than 256 bytes (#1205) +## Coolkey +* Copy labels from certificate objects to the keys +## Common Access Card +* Fixed infinite reading of certificate +* Added support for Alt token card +## MyEID +* support for RAW RSA signature for 2048 bit keys +## IAS/ECC +* Support for new MinInt agent card +## PIV +* Get cardholder name from the first certificate if token label not specified +* implemented keep alive command (#1256) +* fixed signature creation with `CKA_ALWAYS_AUTHENTICATE` (i.e. PKCS#11 `C_Login(CKU_CONTEXT_SPECIFIC)`) +## CardOS +* fixed card name for CardOS 5 +* added ATR `"3b:d2:18:00:81:31:fe:58:c9:02:17"` +* Try forcing `max_send_size` for PSO:DEC +## DNIe +* DNIe: card also supports 1920 bits (#1247) +## GIDS +* Fix GIDS admin authentication +## epass 3000 +* Add ECC support +* Fix #1073 +* Fix #1115 +* Fix buffer underrun in decipher +* Fix #1306 +## Starcos +* added serial number for 3.4 +* fixed setting key reference for 3.4 +* added support for PIN status queries for 3.4 +## EstEID +* ECDSA/ECDH token support +* Fix crash when certificate read failed (#1176) +* Cleanup expired EstEID card ATR-s +* Fix reading EstEID certificates with T=0 (#1193) +## OpenPGP +* Added support for PIN logout and status +* factory reset is possible if LCS is supported +* Added support for OpenPGP card V3 +* fixed selecting Applet +* implemented keep alive command +* Retrieve OpenPGP applet version from OpenPGP applet on YubiKey token (#1262) +## German ID card +* fixed recognition of newer cards +## SC-HSM +* Don't block generic contactless ATR +* changed default labels of GoID +* added PIN commands for GoID 1.0 +## Starcos +* Added Support for Starcos 3.4 and 3.5 +## MioCOS +* disabled by default, use `card_drivers = old;` to enable; driver will be removed soon. +## BlueZ PKCS#15 applet +* disabled by default, use `card_drivers = old;` to enable; driver will be removed soon. + +# New in 0.17.0; 2017-07-18 ## Support for new Cards * CAC (Common Access Card) * GoID (SC-HSM with built-in PIN pad and fingerprint sensor) @@ -75,7 +860,7 @@ NEWS for OpenSC -- History of user visible changes * Fixed --id for `C_GenerateKey`, DES and DES3 keygen mechanism (#857) * Added `--derive-pass-der` option * Added `--generate-random` option - * Add GOSTR3410 keypair generation + * Add GOSTR3410 key pair generation * `npa-tool` (new) * Allows read/write access to EAC tokens * Allows PIN management for EAC tokens @@ -180,7 +965,7 @@ New in 0.16.0; 2016-05-15 first support for Gids smart card * dnie * Feitian PKI card - new ATRs + new ATRs * IsoApplet (fixes) * starcos @@ -203,7 +988,7 @@ New in 0.15.0; 2015-05-11 allow extended length APDUs accept no output for 'SELECT' MF and 'SELECT' DF_NAME APDUs fixed sc_driver_version check - adjusted send/receive size accoriding to card capabilities + adjusted send/receive size according to card capabilities in iso7816 make SELECT agnosting to sc_path_t's aid * asn1 support multi-bytes tags @@ -323,7 +1108,7 @@ New in 0.14.0; 2014-05-31 ECC public key encoding ECC ecpointQ * pkcs15init - introduce 'max-unblocks' PIN init parameter + introduce 'max-unblocks' PIN init parameter keep cert. blob in cert-info data file 'content' and 'prop-attrs' in the card profile in profile more AC operations are parsed @@ -345,7 +1130,7 @@ New in 0.14.0; 2014-05-31 documentation for --list-token-slots * default driver do not send possibly arbitrary APDU-s to an unknown card. - by default 'default' card driver is disabled + by default 'default' card driver is disabled * sc-hsm Added support for persistent EC public keys generated from certificate signing requests @@ -383,7 +1168,7 @@ New in 0.14.0; 2014-05-31 * myeid fixed file-id in myeid.profile * entersafe - fix a bug when writing public key + fix a bug when writing public key * EstEID match card only based on presence of application. * pteid @@ -483,7 +1268,7 @@ New in 0.12.0; 2010-12-22 card initialized by Siemens software. Removed "--split-key" option, as it is no longer needed. * Improved debugging support: debug level 3 will show everything - except of ASN1 and card matching debugging (usualy not needed). + except of ASN1 and card matching debugging (usually not needed). * Massive changes to libopensc. This library is now internal, only used by opensc-pkcs11.so and command line tools. Header files are no longer installed, library should not be used by other applications. @@ -498,7 +1283,7 @@ New in 0.12.0; 2010-12-22 certificate for card label. * Possibility to change the default behavior for card resets via opensc.conf. - + New in 0.11.12; 2009-12-18; Andreas Jellinghaus * Document integer problem in OpenSC and implement workaround * Improve entersafe profile to support private data objects @@ -529,12 +1314,12 @@ New in 0.11.7; 2009-02-26; Andreas Jellinghaus New in 0.11.6; 2008-08-27; Andreas Jellinghaus * Improved security fix: don't match for "OpenSC" in the card label. * New support for Feitian ePass3000 by Weitao Sun. -* GemSafeV1 improved to handle key_ref other than 3 by Douglas E. Engert +* GemSafeV1 improved to handle key_ref other than 3 by Douglas E. Engert New in 0.11.5; 2008-07-31; Andreas Jellinghaus * Apply security fix for cardos driver and extend pkcs15-tool to test cards for the security vulnerability and update them. -* Build system rewritten (NOTICE: configure options was modified). +* Build system rewritten (NOTICE: configure options was modified). The build system can produce outputs for *NIX, cygwin and native windows (using mingw). * ruToken now supported. diff --git a/README.md b/README.md index 1bcbba6606..90b1f05434 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,57 @@ # OpenSC documentation -Wiki is [available online](https://github.com/OpenSC/OpenSC/wiki) +Manual pages for the +[OpenSC command line tools](https://htmlpreview.github.io/?https://github.com/OpenSC/OpenSC/blob/master/doc/tools/tools.html) +as well as for the +[OpenSC configuration files](https://htmlpreview.github.io/?https://github.com/OpenSC/OpenSC/blob/master/doc/files/files.html) +are available online and typically distributed along with your installation. -Please take a look at the documentation before trying to use OpenSC. +The [OpenSC Wiki](https://github.com/OpenSC/OpenSC/wiki) includes, among others, information for: + * [Windows Quick Start](https://github.com/OpenSC/OpenSC/wiki/Windows-Quick-Start) + * [macOS Quick Start](https://github.com/OpenSC/OpenSC/wiki/macOS-Quick-Start) + * [Compiling and Installing on Unix flavors](https://github.com/OpenSC/OpenSC/wiki/Compiling-and-Installing-on-Unix-flavors) + * [Frequently Asked Questions](https://github.com/OpenSC/OpenSC/wiki/Frequently-Asked-Questions) + * More user and developer provided documentation -[![Travis CI Build Status](https://travis-ci.org/OpenSC/OpenSC.svg)](https://travis-ci.org/OpenSC/OpenSC) [![AppVeyor CI Build Status](https://ci.appveyor.com/api/projects/status/github/OpenSC/OpenSC?branch=master&svg=true)](https://ci.appveyor.com/project/LudovicRousseau/OpenSC) [![Coverity Scan Status](https://scan.coverity.com/projects/4026/badge.svg)](https://scan.coverity.com/projects/4026) +# Downloads + +## Latest release + +The [latest stable version of OpenSC](https://github.com/OpenSC/OpenSC/releases/latest) is available on Github. It is available as + + * Windows installer for Intel 64 bit and 32 bit programs (`OpenSC*_x64.msi` and `OpenSC*_x86.msi`) and Arm + (`OpenSC*_arm64.msi`) + * macOS installer (`OpenSC*.dmg`) + * Source code distribution (`opensc*.tar.gz`) + +## Nightly build + +The latest source code is available through [GitHub](https://github.com/OpenSC/OpenSC/archive/master.zip). +Nightly builds are available by their git hash in branches of [OpenSC/Nightly](https://github.com/OpenSC/Nightly). + + +# Build and testing status + +[![Linux build](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml/badge.svg)](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml) +[![Windows build](https://github.com/OpenSC/OpenSC/actions/workflows/windows.yml/badge.svg)](https://github.com/OpenSC/OpenSC/actions/workflows/windows.yml) +[![OSX build](https://github.com/OpenSC/OpenSC/actions/workflows/macos.yml/badge.svg)](https://github.com/OpenSC/OpenSC/actions/workflows/macos.yml) +[![Coverity Scan Status](https://scan.coverity.com/projects/4026/badge.svg)](https://scan.coverity.com/projects/4026) +[![CodeQL](https://github.com/OpenSC/OpenSC/actions/workflows/codeql.yml/badge.svg?event=push)](https://github.com/OpenSC/OpenSC/actions/workflows/codeql.yml) +[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/opensc.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:opensc) +[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3908/badge)](https://bestpractices.coreinfrastructure.org/projects/3908) + +Build and test status of specific cards: + +| Cards | Status | +|----------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| CAC | [![CAC](https://gitlab.com/redhat-crypto/OpenSC/badges/cac/pipeline.svg)](https://gitlab.com/redhat-crypto/OpenSC/pipelines) | +| [virt_CACard](https://github.com/Jakuje/virt_cacard) | [![virt_CACard](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml/badge.svg)](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml) | +| [Coolkey](https://github.com/dogtagpki/coolkey/tree/master/applet) | [![Coolkey](https://gitlab.com/redhat-crypto/OpenSC/badges/coolkey/pipeline.svg)](https://gitlab.com/redhat-crypto/OpenSC/pipelines) | +| [PivApplet](https://github.com/arekinath/PivApplet) | [![PIV](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml/badge.svg)](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml) | +| [OpenPGP Applet](https://github.com/Yubico/ykneo-openpgp/) | [![OpenPGP](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml/badge.svg)](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml) | +| [GidsApplet](https://github.com/vletoux/GidsApplet/) | [![GIDS](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml/badge.svg)](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml) | +| [IsoApplet](https://github.com/philipWendland/IsoApplet/) | [![IsoApplet](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml/badge.svg)](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml) | +| [OsEID (MyEID)](https://sourceforge.net/projects/oseid/) | [![OsEID (MyEID)](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml/badge.svg)](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml) | +| SmartCard-HSM | [![SmartCard-HSM](https://gitlab.com/redhat-crypto/OpenSC/badges/sc-hsm/pipeline.svg)](https://gitlab.com/redhat-crypto/OpenSC/pipelines) | +| [Pico HSM](https://github.com/polhenarejos/pico-hsm/) | [![Pico HSM](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml/badge.svg)](https://github.com/OpenSC/OpenSC/actions/workflows/linux.yml) | +| ePass2003 | [![ePass2003](https://gitlab.com/redhat-crypto/OpenSC/badges/epass2003/pipeline.svg)](https://gitlab.com/redhat-crypto/OpenSC/pipelines) | diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..cede552696 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,51 @@ +# Security Policy + +## Supported Versions + +OpenSC releases are made roughly once a year, unless important security is discovered. + +OpenSC does not release micro updates for previously released versions and does not +backport security fixes into them. Only the last release is supported. + +| Version | Supported | +| -------- | ------------------ | +| 0.27.0+ | :white_check_mark: | +| < 0.27.0 | :x: | + +## Reporting a Vulnerability + +If you discovered security vulnerability in supported version of OpenSC, +you can either report it with a button "Report a vulnerability" in +[Security tab](https://github.com/OpenSC/OpenSC/security/advisories/new) +(Do not create normal public issue with security relevant information!) +or you can send email to any recently active +project developers frankmorgner(at)gmail.com, deengert(at)gmail.com and/or +jakuje(at)gmail.com . + +You can expect update on the issue no later than in two weeks. + +If you use GitHub's private vulnerability reporting, make sure to submit +ONE advisory per issue. The CVE assignment requires that the issue is +self-contained. Assigning a CVE number for multiple issues (even if from +similar class of weaknesses) is complicated. + +## What Qualifies as a Security Issue + +Please use the private vulnerability reporting process only for issues that have **security impact**. This includes, but is not limited to: + +- Memory safety issues such as buffer overflows, use-after-free, or out-of-bounds writes +- Authentication or authorization bypasses +- Cryptographic weaknesses +- Information disclosure vulnerabilities (e.g., private keys, or PINs) +- Privilege escalation or unintended access to protected functionality +- Code execution vulnerabilities, including arbitrary code execution or command injection + +The following types of issues should not be reported as security vulnerabilities and should instead be filed as normal public issues: + +- Build failures or installation problems +- General bugs without security impact +- Feature requests or usability improvements +- Performance issues +- Minor crashes without a clear security implication + +If you are unsure whether your finding qualifies as a security issue, please err on the side of caution and report it through the private channel. diff --git a/VERSION.mk b/VERSION.mk new file mode 100644 index 0000000000..37c7e74d5e --- /dev/null +++ b/VERSION.mk @@ -0,0 +1,17 @@ +PRODUCT_NAME=OpenSC +PRODUCT_TARNAME=opensc +PRODUCT_BUGREPORT=https://github.com/OpenSC/OpenSC/issues +PRODUCT_URL=https://github.com/OpenSC/OpenSC +PACKAGE_VERSION_MAJOR=0 +PACKAGE_VERSION_MINOR=27 +PACKAGE_VERSION_FIX=1 +PACKAGE_VERSION_REVISION=0 +PACKAGE_SUFFIX= +PACKAGE_SCM_REVISION=No Git revision info available + +VS_FF_LEGAL_COPYRIGHT=OpenSC Project +VS_FF_LEGAL_COMPANY_NAME=OpenSC Project +VS_FF_LEGAL_COMPANY_URL=https://github.com/OpenSC +VS_FF_COMMENTS=Provided under the terms of the GNU Lesser General Public License (LGPLv2.1+). +VS_FF_PRODUCT_NAME=OpenSC smartcard framework +VS_FF_PRODUCT_UPDATES=https://github.com/OpenSC/OpenSC/releases diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 6aa47b547e..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,138 +0,0 @@ -version: 0.16.0.{build} - -platform: - - x86 - - x64 - -configuration: - - Release - - Debug - - Light-Release - - Light-Debug - -environment: - matrix: - - VSVER: 14 - - VSVER: 12 - - VSVER: 10 - -matrix: - allow_failures: - # not included in AppVeyor right now - - platform: x64 - VSVER: 10 - -install: - - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` - https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` - Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` - throw "There are newer queued builds for this pull request, failing early." } - - date /T & time /T - - set PATH=C:\cygwin\bin;%PATH% - - set OPENSSL_VER=1_0_2f - - set OPENPACE_VER=1.0.1 - - set ZLIB_VER_DOT=1.2.8 - - ps: $env:PACKAGE_NAME=(git describe --tags) - - ps: >- - If ($env:Platform -Match "x86") { - $env:VCVARS_PLATFORM="x86" - $env:OPENSSL_PF="Win32" - $env:ARTIFACT="OpenSC-win32_vs${env:VSVER}-${env:CONFIGURATION}.msi" - } Else { - $env:VCVARS_PLATFORM="amd64" - $env:OPENSSL_PF="Win64" - $env:ARTIFACT="OpenSC-win64_vs${env:VSVER}-${env:CONFIGURATION}.msi" - } - - ps: >- - If ($env:Configuration -Like "*Debug*") { - $env:NMAKE_EXTRA="DEBUG_DEF=/DDEBUG ${env:NMAKE_EXTRA}" - } - - ps: >- - If (!($env:Configuration -Like "*Light*")) { - If (!(Test-Path -Path "C:\OpenSSL-${env:OPENSSL_PF}" )) { - Start-FileDownload https://slproweb.com/download/${env:OPENSSL_PF}OpenSSL-${env:OPENSSL_VER}.exe -FileName C:\WinOpenSSL.exe - C:\WinOpenSSL.exe /SILENT /VERYSILENT /SP- /SUPPRESSMSGBOXES /NORESTART - } - $env:NMAKE_EXTRA="OPENSSL_DEF=/DENABLE_OPENSSL ${env:NMAKE_EXTRA}" - If (!(Test-Path C:\zlib )) { - appveyor DownloadFile "https://github.com/madler/zlib/archive/v${env:ZLIB_VER_DOT}.zip" -FileName zlib.zip - 7z x zlib.zip -oC:\ - Rename-Item -path "c:\zlib-${env:ZLIB_VER_DOT}" -newName "zlib" - } - If (!(Test-Path C:\openpace )) { - appveyor DownloadFile "https://github.com/frankmorgner/openpace/archive/${env:OPENPACE_VER}.zip" -FileName openpace.zip - 7z x openpace.zip -oC:\ - Rename-Item -path "c:\openpace-${env:OPENPACE_VER}" -newName "openpace" - } - } - - ps: $env:VSCOMNTOOLS=(Get-Content ("env:VS" + "$env:VSVER" + "0COMNTOOLS")) - - echo "Using Visual Studio %VSVER%.0 at %VSCOMNTOOLS%" - - call "%VSCOMNTOOLS%\..\..\VC\vcvarsall.bat" %VCVARS_PLATFORM% - - appveyor DownloadFile "http://download.microsoft.com/download/2/C/9/2C93059C-0532-42DF-8C24-9AEAFF00768E/cngsdk.msi" - - cngsdk.msi /quiet - - uname -a - - set - -build_script: - - ps: >- - if (!($env:Configuration -Like "*Light*")) { - If (!(Test-Path -Path "C:\zlib-${env:OPENSSL_PF}" )) { - # build zlib.lib as a static library - xcopy C:\zlib C:\zlib-${env:OPENSSL_PF} /e /i /y /s - cd C:\zlib-${env:OPENSSL_PF} - (Get-Content win32/Makefile.msc).replace('-MD', '-MT') | Set-Content win32/Makefile.msc - If ($env:Platform -Match "x86") { - nmake -f win32/Makefile.msc LOC="-DASMV -DASMINF" OBJA="inffas32.obj match686.obj" zlib.lib - } Else { - nmake -f win32/Makefile.msc AS=ml64 LOC="-DASMV -DASMINF -I." OBJA="inffasx64.obj gvmat64.obj inffas8664.obj" zlib.lib - } - } - $env:NMAKE_EXTRA="ZLIBSTATIC_DEF=/DENABLE_ZLIB_STATIC ZLIB_INCL_DIR=/IC:\zlib-${env:OPENSSL_PF} ZLIB_LIB=C:\zlib-${env:OPENSSL_PF}\zlib.lib ${env:NMAKE_EXTRA}" - If (!(Test-Path -Path "C:\openpace-${env:OPENSSL_PF}" )) { - # build libeac.lib as a static library - xcopy C:\openpace C:\openpace-${env:OPENSSL_PF} /e /i /y /s - cd C:\openpace-${env:OPENSSL_PF}\src - cl /IC:\OpenSSL-${env:OPENSSL_PF}\include /I. /DX509DIR=\`"/\`" /DCVCDIR=\`"/\`" /W3 /D_CRT_SECURE_NO_DEPRECATE /DWIN32_LEAN_AND_MEAN /GS /MT /c ca_lib.c cv_cert.c cvc_lookup.c x509_lookup.c eac_asn1.c eac.c eac_ca.c eac_dh.c eac_ecdh.c eac_kdf.c eac_lib.c eac_print.c eac_util.c misc.c pace.c pace_lib.c pace_mappings.c ri.c ri_lib.c ta.c ta_lib.c objects.c - lib /out:libeac.lib ca_lib.obj cv_cert.obj cvc_lookup.obj x509_lookup.obj eac_asn1.obj eac.obj eac_ca.obj eac_dh.obj eac_ecdh.obj eac_kdf.obj eac_lib.obj eac_print.obj eac_util.obj misc.obj pace.obj pace_lib.obj pace_mappings.obj ri.obj ri_lib.obj ta.obj ta_lib.obj objects.obj - cd C:\projects\OpenSC - } - $env:NMAKE_EXTRA="OPENPACE_DEF=/DENABLE_OPENPACE OPENPACE_DIR=C:\openpace-${env:OPENSSL_PF} ${env:NMAKE_EXTRA}" - } - - bash -c "exec 0- - If ($env:Configuration -Like "*Debug*") { - Get-ChildItem -recurse c:\projects\OpenSC -exclude vc*.pdb *.pdb | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } - } - -cache: - - C:\OpenSSL-Win32 -> appveyor.yml - - C:\OpenSSL-Win64 -> appveyor.yml - - C:\zlib -> appveyor.yml - - C:\zlib-Win32 -> appveyor.yml - - C:\zlib-Win64 -> appveyor.yml - - C:\openpace -> appveyor.yml - - C:\openpace-Win32 -> appveyor.yml - - C:\openpace-Win64 -> appveyor.yml - -deploy: - - provider: GitHub - tag: $(APPVEYOR_REPO_TAG_NAME) - release: OpenSC-$(APPVEYOR_REPO_TAG_NAME) - description: 'release OpenSC $(APPVEYOR_REPO_TAG_NAME)' - auth_token: - secure: NGaTqWohBQa7fgE62rEm2sp9jkv6S9FRc3YEi3T5CpaoyIY6K89FJjqzaoPLr8vj - artifact: /OpenSC-.*\.msi/ - draft: false - prerelease: true - on: - branch: /0.16.0-rc.*/ # here branch is release tag - appveyor_repo_tag: true # deploy on tag push only diff --git a/bootstrap.ci b/bootstrap.ci index 50fc1cc106..f062445ed5 100755 --- a/bootstrap.ci +++ b/bootstrap.ci @@ -10,19 +10,12 @@ OPTIONS: -h Show this message -s Package suffix -S Use package suffix as 'g' appended with the date of last commit - -r Package version revision - -R Use package version revision as Git commit number from 'git describe' result - -b Package branch - -B Use package branch as current Git branch EOF } SUFFIX= -REVISION= -VERBOSE= -KEEPVERSION= -while getopts “:hs:Sr:RK†OPTION +while getopts “:hs:S†OPTION do case $OPTION in h) @@ -35,12 +28,6 @@ do S) SUFFIX=g`git log -1 --pretty=fuller --date=iso | grep CommitDate: | sed -E 's/^CommitDate:\s(.*)/\1/' | sed -E 's/(.*)-(.*)-(.*) (.*):(.*):(.*)\s+.*/\1\2\3\4\5\6/'` ;; - r) - REVISION=$OPTARG - ;; - R) - REVISION=`git describe | perl -ne 'print $1 if /^[\.0-9]*-([0-9]*)-g[a-z0-9]*$/;'` - ;; ?) usage exit @@ -49,31 +36,17 @@ do done set -e -set -x + if [ -f Makefile ]; then make distclean fi -rm -rf *~ *.cache config.guess config.log config.status config.sub depcomp ltmain.sh version.m4.ci +rm -rf *~ *.cache config.guess config.log config.status config.sub depcomp ltmain.sh VERSION.mk.bak -if [ -n "$SUFFIX" ] || [ -n "$REVISION" ] +if [ -n "$SUFFIX" ] then - cp -a version.m4 version.m4.tmp - - if [ -n "$SUFFIX" ] - then - echo Set package suffix "$SUFFIX" - sed 's/^define(\[PACKAGE_SUFFIX\],\s*\[\([-~]*[0-9a-zA-Z]*\)\])$/define(\[PACKAGE_SUFFIX\], \['$SUFFIX'\])/g' version.m4.tmp > version.m4.ci - cp version.m4.ci version.m4.tmp - fi - - if [ -n "$REVISION" ] - then - echo Set package revision "$REVISION" - sed 's/^define(\[PACKAGE_VERSION_REVISION\],\s*\[\([-~]*[0-9a-zA-Z]*\)\])$/define(\[PACKAGE_VERSION_REVISION\], \['$REVISION'\])/g' version.m4.tmp > version.m4.ci - fi - - rm -f version.m4.tmp + echo Set package suffix "$SUFFIX" + sed -i.bak 's|^PACKAGE_SUFFIX=\([-~]*[0-9a-zA-Z]*\)$|PACKAGE_SUFFIX='$SUFFIX'|g' VERSION.mk fi ./bootstrap diff --git a/configure.ac b/configure.ac index ba45c2109b..02ffc6b94f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,31 +1,26 @@ dnl -*- mode: m4; -*- -AC_PREREQ(2.60) - -define([PRODUCT_NAME], [OpenSC]) -define([PRODUCT_TARNAME], [opensc]) -define([PRODUCT_BUGREPORT], [https://github.com/OpenSC/OpenSC/issues]) -define([PACKAGE_VERSION_MAJOR], [0]) -define([PACKAGE_VERSION_MINOR], [17]) -define([PACKAGE_VERSION_FIX], [0]) -define([PACKAGE_SUFFIX], []) - -define([VS_FF_LEGAL_COPYRIGHT], [OpenSC Project]) -define([VS_FF_LEGAL_COMPANY_NAME], [OpenSC Project]) -define([VS_FF_LEGAL_COMPANY_URL], [https://github.com/OpenSC]) -define([VS_FF_COMMENTS], [Provided under the terms of the GNU Lesser General Public License (LGPLv2.1+).]) -define([VS_FF_PRODUCT_NAME], [OpenSC smartcard framework]) -define([VS_FF_PRODUCT_UPDATES], [https://github.com/OpenSC/OpenSC/releases]) -define([VS_FF_PRODUCT_URL], [https://github.com/OpenSC/OpenSC]) - -m4_include(version.m4) -m4_sinclude(version.m4.ci) - -AC_INIT([PRODUCT_NAME],[PACKAGE_VERSION_MAJOR.PACKAGE_VERSION_MINOR.PACKAGE_VERSION_FIX[]PACKAGE_SUFFIX],[PRODUCT_BUGREPORT],[PRODUCT_TARNAME]) +AC_PREREQ(2.68) + +m4_esyscmd([awk -F= ' + /^[[:space:]]*#/ { next } + /^[[:space:]]*$/ { next } + { + key = $1; val = $2; + gsub(/^[[:space:]]+|[[:space:]]+$/, "", key); + gsub(/^[[:space:]]+|[[:space:]]+$/, "", val); + printf("m4_define([%s],[%s])\n", key, val); + } +' "VERSION.mk"]) + +# Hint: MacOSX/build contains minimum OpenSSL reference +m4_define([openssl_minimum_version], [1.1.1]) + +AC_INIT([PRODUCT_NAME],[PACKAGE_VERSION_MAJOR.PACKAGE_VERSION_MINOR.PACKAGE_VERSION_FIX[]PACKAGE_SUFFIX],[PRODUCT_BUGREPORT],[PRODUCT_TARNAME],[PRODUCT_URL]) AC_CONFIG_AUX_DIR([.]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) -AM_INIT_AUTOMAKE(foreign 1.10) +AM_INIT_AUTOMAKE(foreign 1.10 [subdir-objects]) OPENSC_VERSION_MAJOR="PACKAGE_VERSION_MAJOR" OPENSC_VERSION_MINOR="PACKAGE_VERSION_MINOR" @@ -37,16 +32,14 @@ OPENSC_VS_FF_COMPANY_URL="VS_FF_LEGAL_COMPANY_URL" OPENSC_VS_FF_COMMENTS="VS_FF_COMMENTS" OPENSC_VS_FF_PRODUCT_NAME="VS_FF_PRODUCT_NAME" OPENSC_VS_FF_PRODUCT_UPDATES="VS_FF_PRODUCT_UPDATES" -OPENSC_VS_FF_PRODUCT_URL="VS_FF_PRODUCT_URL" # LT Version numbers, remember to change them just *before* a release. # (Code changed: REVISION++) -# (Oldest interface removed: OLDEST++) +# (Oldest interface changed/removed: OLDEST++) # (Interfaces added: CURRENT++, REVISION=0) -OPENSC_LT_CURRENT="5" -OPENSC_LT_OLDEST="5" +OPENSC_LT_CURRENT="13" +OPENSC_LT_OLDEST="13" OPENSC_LT_REVISION="0" -OPENSC_LT_AGE="0" OPENSC_LT_AGE="$((${OPENSC_LT_CURRENT}-${OPENSC_LT_OLDEST}))" AC_CONFIG_SRCDIR([src/libopensc/sc.c]) @@ -77,12 +70,11 @@ AC_ARG_WITH( ) if test "${enable_optimization}" = "no"; then - CFLAGS="-O0 -g" + CFLAGS="${CFLAGS} -O0 -g" fi dnl Check for some target-specific stuff test -z "${WIN32}" && WIN32="no" -test -z "${CYGWIN}" && CYGWIN="no" case "${host}" in *-*-solaris*) @@ -95,18 +87,14 @@ case "${host}" in WIN_LIBPREFIX="lib" ;; *-cygwin*) + CPPFLAGS="${CPPFLAGS} -DWIN32_LEAN_AND_MEAN" AC_MSG_CHECKING([cygwin mode to use]) - CYGWIN="yes" if test "${with_cygwin_native}" = "yes"; then AC_MSG_RESULT([Using native win32]) - CPPFLAGS="${CPPFLAGS} -DWIN32_LEAN_AND_MEAN" - CFLAGS="${CFLAGS} -mno-cygwin" WIN32="yes" else AC_MSG_RESULT([Using cygwin]) - CPPFLAGS="${CPPFLAGS} -DCRYPTOKI_FORCE_WIN32" WIN_LIBPREFIX="cyg" - AC_DEFINE([USE_CYGWIN], [1], [Define if you are on Cygwin]) fi ;; esac @@ -123,9 +111,6 @@ case "${host}" in PROFILE_DIR_DEFAULT="\$(pkgdatadir)" ;; esac -AC_DEFINE_UNQUOTED([DEBUG_FILE], ["${DEBUG_FILE}"], [Debug file]) -AC_DEFINE_UNQUOTED([PROFILE_DIR], ["${PROFILE_DIR}"], [Directory of profiles]) -AC_DEFINE_UNQUOTED([PROFILE_DIR_DEFAULT], ["${PROFILE_DIR_DEFAULT}"], [Default directory of profiles]) case "${host}" in *-mingw*) @@ -133,9 +118,46 @@ case "${host}" in ;; esac +dnl with Firefox's osclientcerts.so we skip registering our PKCS#11 module on Windows and macOS by default +case "${host}" in + *-*-darwin*) + PKCS11_REGISTER_SKIP_FIREFOX="on" + ;; + *-mingw*|*-winnt*|*-cygwin*) + PKCS11_REGISTER_SKIP_FIREFOX="on" + ;; + *) + PKCS11_REGISTER_SKIP_FIREFOX="off" + ;; +esac + +AX_CODE_COVERAGE() + +AX_CHECK_COMPILE_FLAG([-Wunknown-warning-option], [have_unknown_warning_option="yes"], [have_unknown_warning_option="no"]) +AM_CONDITIONAL([HAVE_UNKNOWN_WARNING_OPTION], [test "${have_unknown_warning_option}" = "yes"]) +AX_CHECK_COMPILE_FLAG([-Wshorten-64-to-32], [have_shorten_warning_option="yes"], [have_shorten_warning_option="no"]) +AM_CONDITIONAL([HAVE_SHORTEN_WARNING_OPTION], [test "${have_shorten_warning_option}" = "yes"]) + +AX_VALGRIND_CHECK() + +if test ${VALGRIND_ENABLED} = "yes"; then + # to avoid false positive leaks from pcsclite + PCSCLITE_PATH=$(ldconfig -p | grep libpcsclite.so.1 | tr ' ' '\n' | grep / | head -n 1) + AC_SUBST([PCSCLITE_PATH]) +fi + +AC_ARG_ENABLE( + [fuzzing], + [AS_HELP_STRING([--enable-fuzzing],[enable compile of fuzzing tests @<:@disabled@:>@, note that CC, CFLAGS and FUZZING_LIBS should be set accordingly, e.g. to something like CC="clang" CFLAGS="-fsanitize=fuzzer-no-link" FUZZING_LIBS="-fsanitize=fuzzer"])], + , + [enable_fuzzing="no"] +) + +AC_ARG_VAR([FUZZING_LIBS], [linker flags for fuzzing]) + AC_ARG_ENABLE( [strict], - [AS_HELP_STRING([--disable-strict],[disable strict compile mode @<:@disabled@:>@])], + [AS_HELP_STRING([--disable-strict],[disable strict compile mode @<:@enabled@:>@])], , [enable_strict="yes"] ) @@ -170,11 +192,18 @@ AC_ARG_ENABLE( AC_ARG_ENABLE( [openssl], - [AS_HELP_STRING([--enable-openssl],[enable openssl linkage @<:@detect@:>@])], + [AS_HELP_STRING([--enable-openssl],[enable OpenSSL linkage @<:@detect@:>@])], , [enable_openssl="detect"] ) +AC_ARG_ENABLE([openssl-secure-malloc], + [AS_HELP_STRING([--openssl-secure-malloc=], + [Enable OpenSSL secure memory by specifying its size in bytes, must be a power of 2 @<:@disabled@:>@])], + [], [enable_openssl_secure_malloc=no]) +AS_IF([test $enable_openssl_secure_malloc != no], + [AC_DEFINE_UNQUOTED([OPENSSL_SECURE_MALLOC_SIZE],[$enable_openssl_secure_malloc],[Size of OpenSSL secure memory in bytes, must be a power of 2])]) + AC_ARG_ENABLE( [openpace], [AS_HELP_STRING([--enable-openpace],[enable OpenPACE linkage @<:@detect@:>@])], @@ -224,6 +253,13 @@ AC_ARG_ENABLE( [enable_sm="yes"] ) +AC_ARG_ENABLE( + [piv-sm], + [AS_HELP_STRING([--enable-piv-sm],[enable SM in PIV card driver linkage @<:@disabled@:>@])], + , + [enable_piv_sm="no"] +) + AC_ARG_ENABLE( [man], [AS_HELP_STRING([--disable-man],[disable installation of manuals @<:@enabled for none Windows@:>@])], @@ -238,6 +274,20 @@ AC_ARG_ENABLE( [enable_doc="no"] ) +AC_ARG_ENABLE( + [tests], + [AS_HELP_STRING([--enable-tests],[enable tests @<:@enabled@:>@])], + , + [enable_tests="yes"] +) + +AC_ARG_ENABLE( + [integration_tests], + [AS_HELP_STRING([--enable-integration-tests],[enable integration tests with software token @<:@enabled@:>@])], + , + [enable_integration_tests="yes"] +) + AC_ARG_ENABLE( [dnie-ui], [AS_HELP_STRING([--enable-dnie-ui],[enable use of external user interface program to request DNIe pin@<:@disabled@:>@])], @@ -246,10 +296,24 @@ AC_ARG_ENABLE( ) AC_ARG_ENABLE( - [werror-declaration-after-statement], - [AS_HELP_STRING([--disable-werror-declaration-after-statement],[disable -Werror 'declaration-after-statement' @<:@enabled@:>@])], + [notify], + [AS_HELP_STRING([--enable-notify],[enable notifications @<:@detect@:>@])], , - [werror_declaration_after_statement="yes"] + [enable_notify="detect"] +) + +AC_ARG_ENABLE( + [autostart-items], + [AS_HELP_STRING([--enable-autostart-items],[enable autostart items @<:@enabled@:>@])], + , + [enable_autostart="no"] +) + +AC_ARG_ENABLE( + [cmocka], + [AS_HELP_STRING([--enable-cmocka],[Build tests in src/tests/p11test directory @<:@detect@:>@])], + , + [enable_cmocka="detect"] ) AC_ARG_WITH( @@ -259,6 +323,13 @@ AC_ARG_WITH( [xslstylesheetsdir="detect"] ) +AC_ARG_WITH( + [completiondir], + [AS_HELP_STRING([--with-completiondir=PATH],[Directory of Bash completion @<:@detect@:>@])], + [completiondir="${withval}"], + [completiondir="detect"] +) + AC_ARG_WITH( [pcsc-provider], [AS_HELP_STRING([--with-pcsc-provider=PATH],[Path to system pcsc provider @<:@system default@:>@])], @@ -278,8 +349,8 @@ reader_count="" for rdriver in "${enable_pcsc}" "${enable_cryptotokenkit}" "${enable_openct}" "${enable_ctapi}"; do test "${rdriver}" = "yes" && reader_count="${reader_count}x" done -if test "${reader_count}" != "x"; then - AC_MSG_ERROR([Only one of --enable-pcsc, --enable-cryptotokenkit, --enable-openct, --enable-ctapi can be specified!]) +if test -n "${reader_count}" && test "${reader_count}" != "x"; then + AC_MSG_ERROR([At most one of --enable-pcsc, --enable-cryptotokenkit, --enable-openct, --enable-ctapi can be specified!]) fi dnl Checks for programs. @@ -330,7 +401,6 @@ AC_MSG_RESULT([${xslstylesheetsdir}]) AC_MSG_CHECKING([git checkout]) GIT_CHECKOUT="no" if test -n "${GIT}" -a -d "${srcdir}/.git"; then - AC_DEFINE([HAVE_CONFIG_VERSION_H], [1], [extra version available in config-version.h]) GIT_CHECKOUT="yes" fi AC_MSG_RESULT([${GIT_CHECKOUT}]) @@ -347,28 +417,26 @@ if test "${GIT_CHECKOUT}" = "yes"; then OPENSC_SCM_REVISION="OpenSC-${REVISION_DESCRIPTION}, ${HASH_COMMIT_DATE}" OPENSC_VERSION_REVISION="$(${GIT} rev-list ${GIT_TAG_COMMIT}..HEAD --count || echo 0)" else - OPENSC_SCM_REVISION="No Git revision info available" - OPENSC_VERSION_REVISION="0" + OPENSC_SCM_REVISION="PACKAGE_SCM_REVISION" + OPENSC_VERSION_REVISION="PACKAGE_VERSION_REVISION" fi dnl C Compiler features AC_C_INLINE dnl Checks for header files. -AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_HEADER_ASSERT AC_CHECK_HEADERS([ \ errno.h fcntl.h stdlib.h \ inttypes.h string.h strings.h \ - sys/time.h unistd.h getopt.h sys/mman.h + sys/time.h unistd.h sys/mman.h \ + sys/endian.h endian.h ]) dnl Checks for typedefs, structures, and compiler characteristics. -AC_C_CONST AC_TYPE_UID_T AC_TYPE_SIZE_T -AC_HEADER_TIME dnl Checks for library functions. AC_FUNC_ERROR_AT_LINE @@ -376,9 +444,37 @@ AC_FUNC_STAT AC_FUNC_VPRINTF AC_CHECK_FUNCS([ \ getpass gettimeofday getline memset mkdir \ - strdup strerror getopt_long getopt_long_only \ - strlcpy strlcat strnlen + strdup strerror memset_s explicit_bzero \ + strnlen sigaction ]) + +# +# Check for __builtin_uadd_overflow +# +AC_MSG_CHECKING([compiler support for __builtin_*_overflow()]) +AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[return (__builtin_uadd_overflow(UINT_MAX, UINT_MAX, &(unsigned int){ 0 }));]] + )], + [AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_BUILTIN_OVERFLOW], [1], [define if the compiler supports __builtin_*_overflow().]) + ], + [AC_MSG_RESULT([no]) + ]) + +# Do not check for strlcpy and strlcat in Linux because it is not implemented +# and autotools can not detect it in AC_CHECK_DECLS because build does not fail +# in this test. +# https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=22192 +case "${host_os}" in + linux*) + ;; + *) + AC_CHECK_DECLS([strlcpy, strlcat], [], [], [[#include ]]) + ;; +esac + AC_CHECK_SIZEOF(void *) if test "${ac_cv_sizeof_void_p}" = 8; then LIBRARY_BITNESS="64" @@ -402,9 +498,20 @@ AC_CHECK_LIB( if test "${WIN32}" = "no"; then dnl dl support - AC_SEARCH_LIBS([dlopen], [dl dld], [], [ - AC_MSG_ERROR([unable to find the dlopen() function]) - ]) + AC_ARG_VAR([LDL_LIBS], [linker flags for ldl]) + if test -z "${LDL_LIBS}"; then + AC_CHECK_LIB( + [dl], + [dlopen], + [LDL_LIBS="-ldl"], + AC_CHECK_LIB( + [dld], + [dlopen], + [LDL_LIBS="-ldld"], + AC_MSG_ERROR([unable to find the dlopen() function]) + ) + ) + fi dnl Special check for pthread support. AX_PTHREAD( @@ -442,15 +549,83 @@ fi if test "${enable_dnie_ui}" = "yes"; then AC_DEFINE([ENABLE_DNIE_UI], [1], [Enable the use of external user interface program to request DNIe user pin]) + case "${host}" in + *-*-darwin*) + LDFLAGS="${LDFLAGS} -framework Carbon" + ;; + esac case "${host}" in *-apple-*) - if test "${enable_dnie_ui}" = "yes"; then - LDFLAGS="${LDFLAGS} -framework Carbon" - fi + LDFLAGS="${LDFLAGS} -framework CoreFoundation" ;; esac fi +case "${host}" in + *-*-darwin*|*-mingw*|*-winnt*|*-cygwin*) + have_notify="yes" + ;; + *) + PKG_CHECK_MODULES( [GIO2], [gio-2.0], + [ have_notify="yes" + have_gio2="yes" ], + [ have_notify="no" + have_gio2="no" ]) + saved_CFLAGS="${CFLAGS}" + CFLAGS="${CFLAGS} ${GIO2_CFLAGS}" + AC_CHECK_HEADERS(gio/gio.h, [], + [ AC_MSG_WARN([glib2 headers not found]) + have_notify="no" + have_gio2="no" ]) + CFLAGS="${saved_CFLAGS}" + saved_LIBS="$LIBS" + LIBS="$LIBS ${GIO2_LIBS}" + AC_MSG_CHECKING([for g_application_send_notification]) + AC_TRY_LINK_FUNC(g_application_send_notification, [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_WARN([Cannot link against glib2]) + have_notify="no" + have_gio2="no" ]) + LIBS="$saved_LIBS" + # we do not need glib-2.0 + GIO2_LIBS=$(echo "$GIO2_LIBS" | sed 's/-lglib-2.0//g') + ;; +esac + +case "${enable_notify}" in + no) + have_notify="no" + ;; + detect) + if test "${have_notify}" = "yes"; then + enable_notify="yes" + else + enable_notify="no" + fi + ;; +esac + +if test "${enable_notify}" = "yes"; then + if test "${have_notify}" = "yes"; then + AC_DEFINE([ENABLE_NOTIFY], [1], [Use notification libraries and header files]) + if test "${have_gio2}" = "yes"; then + AC_DEFINE([ENABLE_GIO2], [1], [Use glib2 libraries and header files]) + OPTIONAL_NOTIFY_CFLAGS="${GIO2_CFLAGS}" + OPTIONAL_NOTIFY_LIBS="${GIO2_LIBS}" + fi + else + AC_MSG_ERROR([notification linkage required, but no notification provider was found]) + fi +fi + +have_cmocka="yes" +PKG_CHECK_MODULES([CMOCKA], [cmocka >= 1.0.1],,[have_cmocka="no"]) +AC_CHECK_HEADER([setjmp.h]) +AC_CHECK_HEADER([cmocka.h],, [have_cmocka="no"], +[#include +#include +#include +]) + AC_ARG_VAR([ZLIB_CFLAGS], [C compiler flags for zlib]) AC_ARG_VAR([ZLIB_LIBS], [linker flags for zlib]) if test -z "${ZLIB_LIBS}"; then @@ -531,21 +706,16 @@ fi PKG_CHECK_MODULES( [OPENSSL], - [libcrypto >= 0.9.7], + [libcrypto >= openssl_minimum_version], [have_openssl="yes"], - [PKG_CHECK_MODULES( - [OPENSSL], - [openssl >= 0.9.7], - [have_openssl="yes"], - [AC_CHECK_LIB( - [crypto], - [RSA_version], - [ - have_openssl="yes" - OPENSSL_LIBS="-lcrypto" - ], - [have_openssl="no"] - )] + [AC_CHECK_LIB( + [crypto], + [RSA_get_version], + [ + have_openssl="yes" + OPENSSL_LIBS="-lcrypto" + ], + [have_openssl="no"] )] ) @@ -577,6 +747,23 @@ else OPENSSL_LIBS="" fi +if test "${enable_cmocka}" = "detect"; then + if test "${have_cmocka}" = "yes" -a "${have_openssl}" = "yes"; then + enable_cmocka="yes" + else + enable_cmocka="no" + fi +fi + +if test "${enable_cmocka}" = "yes"; then + if test "${have_cmocka}" != "yes"; then + AC_MSG_ERROR([Tests required, but cmocka is not available]) + fi +fi + +if test "${enable_fuzzing}" = "yes"; then + AC_DEFINE([FUZZING_ENABLED], [1], [Define if fuzzing is enabled]) +fi PKG_CHECK_EXISTS([libeac], [PKG_CHECK_MODULES([OPENPACE], [libeac >= 0.9])], @@ -595,13 +782,14 @@ AC_MSG_CHECKING([for EAC_CTX_init_pace]) AC_TRY_LINK_FUNC(EAC_CTX_init_pace, [ AC_MSG_RESULT([yes]) ], [ AC_MSG_WARN([Cannot link against libeac]) have_openpace="no" ]) +AC_CHECK_FUNCS([EAC_OBJ_nid2obj]) CPPFLAGS="$saved_CPPFLAGS" LIBS="$saved_LIBS" AC_ARG_ENABLE(cvcdir, - AC_HELP_STRING([--enable-cvcdir=DIR], + AS_HELP_STRING([--enable-cvcdir=DIR], [directory containing CV certificates (default is determined by libeac)]), [cvcdir="${enableval}"], [cvcdir=false]) @@ -623,7 +811,7 @@ AC_SUBST(CVCDIR) AC_DEFINE_UNQUOTED([CVCDIR], ["${CVCDIR}"], [CVC directory]) AC_ARG_ENABLE(x509dir, - AC_HELP_STRING([--enable-x509dir=DIR], + AS_HELP_STRING([--enable-x509dir=DIR], [directory containing X.509 certificates (default is determined by libeac)]), [x509dir="${enableval}"], [x509dir=false]) @@ -673,6 +861,14 @@ else OPENPACE_LIBS="" fi +if test "${enable_piv_sm}" = "yes"; then + if test "${enable_sm}" = "yes" -a "${enable_openssl}" = "yes" ; then + AC_DEFINE([ENABLE_PIV_SM], [1], [Enable PIV SM]) + else + AC_MSG_ERROR([ENABLE_PIV_SM requires ENABLE_SM and ENABLE_OPENSSL]) + fi +fi + if test "${enable_openct}" = "yes"; then PKG_CHECK_MODULES( [OPENCT], @@ -688,13 +884,6 @@ fi if test "${enable_pcsc}" = "yes"; then if test "${WIN32}" != "yes"; then - PKG_CHECK_EXISTS( - [libpcsclite], - [PKG_CHECK_MODULES([PCSC], [libpcsclite >= 1.8.22], - [AC_DEFINE([PCSCLITE_GOOD], [1], [Sufficient version of PCSC-Lite with all the required features])], - [:] - )] - ) if test -z "${PCSC_CFLAGS}"; then case "${host}" in *-*-darwin*) @@ -750,6 +939,25 @@ if test "${enable_cryptotokenkit}" = "yes"; then AC_DEFINE([ENABLE_CRYPTOTOKENKIT], [1], [Define if CryptoTokenKit is to be enabled]) fi +if test "${completiondir}" = "detect"; then + echo completion ${completiondir} + PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0], + [completiondir="`pkg-config --variable=completionsdir bash-completion`"], + [completiondir="${sysconfdir}/bash_completion.d"]) +fi +AC_SUBST([completiondir]) + +AC_ARG_ENABLE(p11_system_config_modules, + AS_HELP_STRING([--enable-p11_system_config_modules=DIR], + [directory containing P11-Kit configuration files (default is determined by libp11-kit)]), + [p11_system_config_modules="${enableval}"], + [p11_system_config_modules=false]) +if test "${p11_system_config_modules}" = false ; then + p11_system_config_modules="`$PKG_CONFIG p11-kit-1 --variable=p11_system_config_modules`" +fi +AM_CONDITIONAL([ENABLE_P11KIT], [test -n "${p11_system_config_modules}"]) +P11_SYSTEM_CONFIG_MODULES="${p11_system_config_modules}" +AC_SUBST(P11_SYSTEM_CONFIG_MODULES) AC_SUBST(DYN_LIB_EXT) AC_SUBST(LIBDIR) @@ -773,22 +981,26 @@ if test "${enable_sm}" = "yes"; then DEFAULT_SM_MODULE="${LIB_PRE}smm-local${DYN_LIB_EXT}" case "${host}" in *-mingw*|*-winnt*|*-cygwin*) - DEFAULT_SM_MODULE_PATH="\# module_path = \"\";" + DEFAULT_SM_MODULE_PATH="%PROGRAMFILES%\\\OpenSC Project\\\OpenSC\\\tools" ;; *) - DEFAULT_SM_MODULE_PATH="module_path = \$(libdir);" + DEFAULT_SM_MODULE_PATH="${libdir}" ;; esac - AC_DEFINE_UNQUOTED([DEFAULT_SM_MODULE], ["${DEFAULT_SM_MODULE}"], [Default SM module]) - AC_DEFINE_UNQUOTED([DEFAULT_SM_MODULE_PATH], ["${DEFAULT_SM_MODULE_PATH}"], [Default SM module path]) fi if test "${with_pkcs11_provider}" = "detect"; then - DEFAULT_PKCS11_PROVIDER="opensc-pkcs11${DYN_LIB_EXT}" + if test "${WIN32}" != "yes"; then + DEFAULT_PKCS11_PROVIDER="${libdir}/opensc-pkcs11${DYN_LIB_EXT}" + DEFAULT_ONEPIN_PKCS11_PROVIDER="${libdir}/onepin-opensc-pkcs11${DYN_LIB_EXT}" + else + DEFAULT_PKCS11_PROVIDER="%PROGRAMFILES%\\\OpenSC Project\\\OpenSC\\\pkcs11\\\opensc-pkcs11.dll" + DEFAULT_ONEPIN_PKCS11_PROVIDER="%PROGRAMFILES%\\\OpenSC Project\\\OpenSC\\\pkcs11\\\onepin-opensc-pkcs11.dll" + fi else DEFAULT_PKCS11_PROVIDER="${with_pkcs11_provider}" + DEFAULT_ONEPIN_PKCS11_PROVIDER="${with_pkcs11_provider}" fi -AC_DEFINE_UNQUOTED([DEFAULT_PKCS11_PROVIDER], ["${DEFAULT_PKCS11_PROVIDER}"], [Default PKCS11 provider]) if test "${enable_man}" = "detect"; then if test "${WIN32}" = "yes"; then @@ -807,12 +1019,16 @@ if test "${enable_man}" = "yes" -o "${enable_doc}" = "yes"; then AC_MSG_RESULT([ok]) fi -AC_ARG_VAR([HELP2MAN], - [absolute path to help2man used for man page generation of npa-tool]) -AC_PATH_PROG(HELP2MAN, help2man, not found) AC_ARG_VAR([GENGETOPT], [absolute path to gengetopt used for command line parsing of npa-tool]) AC_PATH_PROG(GENGETOPT, gengetopt, not found) +AC_ARG_VAR([CLANGTIDY], + [absolute path to clang-tidy used for static code analysis]) +AC_PATH_PROG(CLANGTIDY, clang-tidy, not found) +TIDY_CHECKS="-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling" + +AX_FUNC_GETOPT_LONG +#AH_BOTTOM([#include "common/compat_getopt.h"]) OPENSC_FEATURES="" if test "${enable_thread_locking}" = "yes"; then @@ -873,8 +1089,8 @@ AC_DEFINE_UNQUOTED([OPENSC_VS_FF_COMPANY_NAME], ["${OPENSC_VS_FF_COMPANY_NAME}"] AC_DEFINE_UNQUOTED([OPENSC_VS_FF_COMMENTS], ["${OPENSC_VS_FF_COMMENTS}"], [OpenSC version-info Comments]) AC_DEFINE_UNQUOTED([OPENSC_VS_FF_PRODUCT_NAME], ["${OPENSC_VS_FF_PRODUCT_NAME}"], [OpenSC version-info ProductName]) AC_DEFINE_UNQUOTED([OPENSC_VS_FF_PRODUCT_UPDATES], ["${OPENSC_VS_FF_PRODUCT_UPDATES}"], [OpenSC version-info UpdateURL]) -AC_DEFINE_UNQUOTED([OPENSC_VS_FF_PRODUCT_URL], ["${OPENSC_VS_FF_PRODUCT_URL}"], [OpenSC version-info ProductURL]) AC_DEFINE_UNQUOTED([OPENSC_VS_FF_COMPANY_URL], ["${OPENSC_VS_FF_COMPANY_URL}"], [OpenSC version-info UpdateURL]) +AC_DEFINE_UNQUOTED([OPENSC_ARCH_SUFFIX], [""], [OpenSC architecture suffix]) pkcs11dir="\$(libdir)/pkcs11" @@ -890,7 +1106,6 @@ AC_SUBST([OPENSC_VS_FF_COMPANY_NAME]) AC_SUBST([OPENSC_VS_FF_COMMENTS]) AC_SUBST([OPENSC_VS_FF_PRODUCT_NAME]) AC_SUBST([OPENSC_VS_FF_PRODUCT_UPDATES]) -AC_SUBST([OPENSC_VS_FF_PRODUCT_URL]) AC_SUBST([OPENSC_VS_FF_COMPANY_URL]) AC_SUBST([OPENSC_LT_CURRENT]) AC_SUBST([OPENSC_LT_REVISION]) @@ -899,6 +1114,8 @@ AC_SUBST([OPENSC_LT_OLDEST]) AC_SUBST([WIN_LIBPREFIX]) AC_SUBST([DEFAULT_PCSC_PROVIDER]) AC_SUBST([DEFAULT_PKCS11_PROVIDER]) +AC_SUBST([DEFAULT_ONEPIN_PKCS11_PROVIDER]) +AC_SUBST([PKCS11_REGISTER_SKIP_FIREFOX]) AC_SUBST([OPTIONAL_ZLIB_CFLAGS]) AC_SUBST([OPTIONAL_ZLIB_LIBS]) AC_SUBST([OPTIONAL_READLINE_CFLAGS]) @@ -914,6 +1131,9 @@ AC_SUBST([DEFAULT_SM_MODULE_PATH]) AC_SUBST([DEBUG_FILE]) AC_SUBST([PROFILE_DIR]) AC_SUBST([PROFILE_DIR_DEFAULT]) +AC_SUBST([OPTIONAL_NOTIFY_CFLAGS]) +AC_SUBST([OPTIONAL_NOTIFY_LIBS]) +AC_SUBST([TIDY_CHECKS]) AM_CONDITIONAL([ENABLE_MAN], [test "${enable_man}" = "yes"]) AM_CONDITIONAL([ENABLE_THREAD_LOCKING], [test "${enable_thread_locking}" = "yes"]) @@ -921,68 +1141,68 @@ AM_CONDITIONAL([ENABLE_ZLIB], [test "${enable_zlib}" = "yes"]) AM_CONDITIONAL([ENABLE_READLINE], [test "${enable_readline}" = "yes"]) AM_CONDITIONAL([ENABLE_OPENSSL], [test "${enable_openssl}" = "yes"]) AM_CONDITIONAL([ENABLE_OPENPACE], [test "${enable_openpace}" = "yes"]) +AM_CONDITIONAL([ENABLE_NOTIFY], [test "${enable_notify}" = "yes"]) AM_CONDITIONAL([ENABLE_CRYPTOTOKENKIT], [test "${enable_cryptotokenkit}" = "yes"]) AM_CONDITIONAL([ENABLE_OPENCT], [test "${enable_openct}" = "yes"]) AM_CONDITIONAL([ENABLE_DOC], [test "${enable_doc}" = "yes"]) +AM_CONDITIONAL([ENABLE_TESTS], [test "${enable_tests}" = "yes"]) +AM_CONDITIONAL([ENABLE_INTEGRATION_TESTS], [test "${enable_integration_tests}" = "yes"]) AM_CONDITIONAL([WIN32], [test "${WIN32}" = "yes"]) -AM_CONDITIONAL([CYGWIN], [test "${CYGWIN}" = "yes"]) AM_CONDITIONAL([ENABLE_MINIDRIVER], [test "${enable_minidriver}" = "yes"]) AM_CONDITIONAL([ENABLE_MINIDRIVER_SETUP_CUSTOMACTION], [test "${enable_minidriver_ca}" = "yes"]) AM_CONDITIONAL([ENABLE_SM], [test "${enable_sm}" = "yes"]) AM_CONDITIONAL([ENABLE_DNIE_UI], [test "${enable_dnie_ui}" = "yes"]) AM_CONDITIONAL([ENABLE_NPATOOL], [test "${ENABLE_NPATOOL}" = "yes"]) +AM_CONDITIONAL([ENABLE_AUTOSTART], [test "${enable_autostart}" = "yes"]) +AM_CONDITIONAL([ENABLE_CMOCKA], [test "${enable_cmocka}" = "yes"]) AM_CONDITIONAL([GIT_CHECKOUT], [test "${GIT_CHECKOUT}" = "yes"]) +AM_CONDITIONAL([ENABLE_FUZZING], [test "${enable_fuzzing}" = "yes"]) +AM_CONDITIONAL([ENABLE_SHARED], [test "${enable_shared}" = "yes"]) +AS_IF([test "${enable_shared}" = "yes"], [AC_DEFINE([ENABLE_SHARED], [1], [Enable shared libraries])]) +AM_CONDITIONAL([ENABLE_STATIC], [test "${enable_static}" = "yes"]) +AS_IF([test "${enable_static}" = "yes"], [AC_DEFINE([ENABLE_static], [1], [Enable static libraries])]) if test "${enable_pedantic}" = "yes"; then enable_strict="yes"; - CFLAGS="${CFLAGS} -pedantic -Wextra" + CFLAGS="-pedantic ${CFLAGS}" fi if test "${enable_strict}" = "yes"; then - CFLAGS="${CFLAGS} -Wall -Wextra -Wno-unused-parameter" -fi -if test "$GCC" = "yes"; then - # This should be resolved not ignored. - CFLAGS="-fno-strict-aliasing ${CFLAGS}" -fi - -if test "${werror_declaration_after_statement}" = "yes"; then - CFLAGS="${CFLAGS} -Werror=declaration-after-statement" + CFLAGS="-Wall -Wextra -Wno-unused-parameter -Werror -Wstrict-aliasing=2 ${CFLAGS}" fi AC_CONFIG_FILES([ Makefile doc/Makefile doc/tools/Makefile + doc/files/Makefile etc/Makefile + tests/Makefile src/Makefile src/common/Makefile + src/ui/Makefile src/libopensc/Makefile src/sm/Makefile src/pkcs11/Makefile - src/pkcs11/versioninfo-pkcs11.rc - src/pkcs11/versioninfo-pkcs11-spy.rc src/pkcs11/opensc-pkcs11.pc + src/pkcs11/opensc.module src/pkcs15init/Makefile src/scconf/Makefile src/tests/Makefile src/tests/regression/Makefile + src/tests/p11test/Makefile + src/tests/fuzzing/Makefile + src/tests/unittests/Makefile src/tools/Makefile - src/tools/versioninfo-tools.rc src/smm/Makefile src/minidriver/Makefile - src/minidriver/versioninfo-minidriver.rc src/minidriver/opensc-minidriver.inf win32/Makefile - win32/versioninfo.rc - win32/versioninfo-customactions.rc - win32/winconfig.h win32/OpenSC.iss - win32/OpenSC.wxs MacOSX/Makefile - MacOSX/build-package MacOSX/Distribution.xml MacOSX/resources/Welcome.html ]) + AC_OUTPUT cat < + + + + + + diff --git a/doc/files/Makefile.am b/doc/files/Makefile.am new file mode 100644 index 0000000000..e2d11f6d5a --- /dev/null +++ b/doc/files/Makefile.am @@ -0,0 +1,38 @@ +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in + +dist_noinst_DATA = pkcs15-profile.5.xml.in opensc.conf.5.xml.in files.xml +if ENABLE_DOC +html_DATA = files.html +endif + +if ENABLE_MAN +man5_MANS = pkcs15-profile.5 opensc.conf.5 +endif + +opensc.conf.5.xml opensc.conf.5: $(srcdir)/opensc.conf.5.xml.in + @sed \ + -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ + -e 's|@docdir[@]|$(docdir)|g' \ + -e 's|@libdir[@]|$(libdir)|g' \ + -e 's|@DYN_LIB_EXT[@]|$(DYN_LIB_EXT)|g' \ + -e 's|@DEFAULT_PCSC_PROVIDER[@]|$(DEFAULT_PCSC_PROVIDER)|g' \ + -e 's|@PROFILE_DIR_DEFAULT[@]|$(PROFILE_DIR_DEFAULT)|g' \ + -e 's|@DEFAULT_SM_MODULE[@]|$(DEFAULT_SM_MODULE)|g' \ + < $< > opensc.conf.5.xml + $(AM_V_GEN)$(XSLTPROC) --nonet --path "$(srcdir)/..:$(xslstylesheetsdir)/manpages" --xinclude -o $@ man.xsl opensc.conf.5.xml 2>/dev/null + +pkcs15-profile.5.xml pkcs15-profile.5: $(srcdir)/pkcs15-profile.5.xml.in + @sed \ + -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' \ + < $< > pkcs15-profile.5.xml + $(AM_V_GEN)$(XSLTPROC) --nonet --path "$(srcdir)/..:$(xslstylesheetsdir)/manpages" --xinclude -o $@ man.xsl pkcs15-profile.5.xml 2>/dev/null + +files.html: $(srcdir)/files.xml $(wildcard $(srcdir)/*.5.xml) opensc.conf.5.xml pkcs15-profile.5.xml + $(AM_V_GEN)$(XSLTPROC) --nonet --path "$(builddir):$(srcdir)/..:$(xslstylesheetsdir)/html" --xinclude -o $(srcdir)/$@ html.xsl $< 2>/dev/null + +%.5: $(srcdir)/%.5.xml + $(AM_V_GEN)sed -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' < $< \ + | $(XSLTPROC) --nonet --path "$(srcdir)/..:$(xslstylesheetsdir)/manpages" --xinclude -o $@ man.xsl - 2>/dev/null + +clean-local: + -rm -rf $(html_DATA) $(man5_MANS) *.5.xml diff --git a/doc/files/files.html b/doc/files/files.html new file mode 100644 index 0000000000..8373b5c3cc --- /dev/null +++ b/doc/files/files.html @@ -0,0 +1,1740 @@ +OpenSC Manual Pages: Section 5

OpenSC Manual Pages: Section 5


Table of Contents

opensc.conf — configuration file for OpenSC
pkcs15-profile — format of profile for pkcs15-init

Name

opensc.conf — configuration file for OpenSC

Description

+ OpenSC obtains configuration data from the following sources in the following order +

  1. + command-line options +

  2. + environment variables +

  3. + Windows registry key in + HKEY_CURRENT_USER (if available) +

  4. + Windows registry key in + HKEY_LOCAL_MACHINE (if available) +

  5. + system-wide configuration file + (/etc/opensc.conf) +

+

+ The configuration file, opensc.conf, is composed + of blocks, which, in general, have the + following format: +

+key [, name...] {
+	block_contents
+}
+			

+ block_contents is one or more + block_items where a + block_item is one of +

  • + # comment string +

  • + key [, name...] = value; +

  • + block +

+

+ At the root level, opensc.conf should contain + one or more application specific configuration blocks: +

+app application {
+	block_contents
+}
+			

+ application + specifies one of: +

  • + filename: Configuration block for the application with specified file path. +

  • + default: The fall-back configuration block for all applications +

  • + opensc-pkcs11: Configuration block for the PKCS#11 module (opensc-pkcs11.so) +

  • + onepin-opensc-pkcs11: Configuration block for the PKCS#11 one-PIN-module (onepin-opensc-pkcs11.so) +

  • + cardmod: Configuration block for Windows' minidriver (opensc-minidriver.dll) +

  • + cardos-tool, + cryptoflex-tool, + dnie-tool, + egk-tool, + eidenv, + gids-tool, + iasecc-tool, + netkey-tool, + npa-tool, + openpgp-tool, + opensc-asn1, + opensc-explorer, + opensc-notify, + opensc-tool, + piv-tool, + pkcs11-tool, + pkcs15-crypt, + pkcs15-init, + pkcs15-tool, + sc-hsm-tool, + westcos-tool: + Configuration block for OpenSC tools +

+

Configuration Options

+ debug = num; +

+ Amount of debug info to print (Default: + 0). A greater value means more + debug info. +

+ The environment variable + OPENSC_DEBUG overwrites this + setting. +

+ debug_file = filename; +

+ The file to which debug output will be written + (Default: stderr). Special + values stdout and + stderr are recognized. +

+ profile_dir = filename; +

+ PKCS#15 initialization/personalization profiles + directory for + pkcs15-init(1). + + (Default: /usr/share/opensc). +

+ If this configuration value is not found on + Windows, the registry key + Software\OpenSC + Project\OpenSC\ProfileDir is + checked. +

+ disable_colors = bool; +

+ Disable colors of log messages (Default: + false if attached to a console, + true otherwise). +

+ disable_popups = bool; +

+ Disable pop-ups of built-in GUI (Default: false). +

+ enable_default_driver = bool; +

+ Enable default card driver (Default: + false). Default card driver is + explicitly enabled for + opensc-explorer(1). + + and + opensc-tool(1). + +

+ card_drivers = name... ; +

+ Allowlist of card drivers to load at start-up. + The special value internal (the + default) will load all statically linked drivers. +

+ If an unknown (i.e. not internal or old) driver is + supplied, a separate configuration + block has to be written for the driver. A special + value old will load all + statically linked drivers that may be removed in + the future. +

+ The list of supported card driver names can be + retrieved from the output of opensc-tool + --list-drivers. +

+ The environment variable + OPENSC_DRIVER overwrites this + setting. +

+ ignored_readers = name... ; +

+ List of readers to ignore (Default: empty). If any + of the comma separated strings listed is matched in + a reader name (case sensitive, partial matching + possible), the reader is ignored by OpenSC. Use + opensc-tool --list-readers to + see all currently connected readers. +

+ reader_driver name { + block_contents + } + +

+ Configuration of the smart card reader driver where name is one of: +

+

+ See the section called “Configuration of Smart Card Reader Driverâ€. +

+ card_driver name { + block_contents + } + +

+ Configuration of the card driver where name is one of: +

+

+ card_atr hexstring { + block_contents + } + +

+ In addition to the built-in list of known cards in + the card driver, you can configure a new card for + the driver using the card_atr + block. +

+ For details see the section called “Configuration based on ATRâ€. +

+ disable_hw_pkcs1_padding = value; +

+ Disabling PKCS#1 v1.5 padding in HW when card supports doing raw RSA operations. + Known parameters: +

  • + no: PKCS#1 v1.5 padding is enabled in HW when card supports it. +

  • + sign: PKCS#1 v1.5 padding + is disabled only for signatures (PKCS#1 v1.5 type 1). +

  • + decipher: PKCS#1 v1.5 padding + is disabled only for decryption (PKCS#1 v1.5 type 2). +

  • + both: PKCS#1 v1.5 padding + is disabled both for signatures and decryption (PKCS#1 v1.5 type 1 and 2). +

+ (Default: decipher). +

+ openssl_config = path; +

+ Specify a OpenSSL configuration file to be loaded into the + OpenSC's library context. This should not be needed for general + operation, but is needed if you need to configure some + specifics of OpenSSL, without the need to rebuild OpenSC (for + example configure and load a FIPS provider). + For more information about configuring OpenSSL, see + config(5). + + and + fips_config(5). + . + (Default: empty). +

+ secure_messaging name { + block_contents + } + +

+ Configuration options for the secure messaging profile name: +

+ module_name = filename; +

+ Name of external SM module (Default: libsmm-local.so). +

+ module_path = filename; +

+ Directory with external SM module + (Default: /usr/lib64). +

+ If this configuration value is not + found on Windows, the registry key + Software\OpenSC + Project\OpenSC\SmDir is + checked. +

+ module_data = value; +

+ Specific data to tune the module initialization. +

+ mode = value; +

+ Secure messaging mode. Known parameters: +

  • + transmit: + In this mode the + procedure to securize + an APDU is called by + the OpenSC general APDU + transmit procedure. In + this mode all APDUs, + except the ones + filtered by the card + specific procedure, are + securized. +

  • + acl: + In this mode APDU are + securized only if + needed by the ACLs of + the command to be + executed. +

+

+ flags = value; +

+ Secure messaging type specific flags. +

+ kmc = hexstring; +

+ Default KMC of the GP Card Manager for the Oberthur's Java cards. +

+ ifd_serial = hexstring; +
+ keyset[_aid]_num_enc = + value; + keyset[_aid]_num_mac = + value; +

+ Keyset values from IAM profiles of + the Gemalto IAS/ECC cards with an + optional application identifier +

+ framework name { + block_contents + } + +

+ Internal configuration options where name is one of: +

+

+ pkcs11 { + block_contents + } + +

+ Parameters for the OpenSC PKCS11 module. +

+ For details see the section called “Configuration of PKCS#11â€. +

Configuration of Smart Card Reader Driver

Configuration Options for all Reader Drivers

+ max_send_size = num; + max_recv_size = num; +

+ Limit command and response sizes + (Default: + max_send_size + = 255, + max_recv_size + = 256) . Some + Readers don't propagate their + transceive capabilities correctly. + max_send_size and max_recv_size + allow setting the limits manually, + for example to enable extended + length capabilities. +

+ enable_escape bool; +

+ Detect reader capabilities with + escape commands (wrapped APDUs with + CLA=0xFF as defined by PC/SC pt. 3 + and BSI TR-03119, e.g. for getting + the UID, escaped PIN commands and + the reader's firmware version, + Default: false) +

Configuration of CT-API Readers

+ module filename { + ports = nums; + } + +

+ Load the specified CT-API module with the specified number of ports. +

Configuration of PC/SC Readers

+ connect_exclusive = bool; +

+ Connect to reader in exclusive mode + (Default: false)? + This option has no effect in Windows' minidriver. +

+ disconnect_action = action; +

+ What to do when disconnecting from + a card (SCardDisconnect). Valid + values are + leave, + reset, + unpower (Default: + leave). + This option has no effect in Windows' minidriver. +

+ transaction_end_action = action; +

+ What to do at the end of a + transaction (SCardEndTransaction). + Valid values + are leave, + reset, + unpower (Default: + leave). + This option has no effect in Windows' minidriver. +

+ reconnect_action = action; +

+ What to do when reconnection to a + card (SCardReconnect). Valid values + are leave, + reset, + unpower (Default: + leave). + This option has no effect in Windows' minidriver. +

+ enable_pinpad = bool; +

+ Enable pinpad if detected (PC/SC + v2.0.2 Part 10, Default: + true) +

+ fixed_pinlength = num; +

+ Some pinpad readers can only handle + one exact length of the PIN. + fixed_pinlength + sets this value so that OpenSC + expands the padding to this length + (Default: 0, + i.e. not fixed). +

+ provider_library = filename; +

+ Use specific PC/SC provider + (Default: + libpcsclite.so.1). +

Configuration of OpenCT Readers

+ readers = num; +

+ Virtual readers to allocate (Default: 2). +

Configuration Options for MyEID Card

+ disable_hw_pkcs1_padding = bool; +

+ The MyEID card can internally + encapsulate the data (hash code) + into a DigestInfo ASN.1 structure + according to the selected hash + algorithm (currently only for SHA1). + DigestInfo is padded to RSA key + modulus length according to PKCS#1 + v1.5, block type 01h. Size of the + DigestInfo must not exceed 40% + of the RSA key modulus length. If + this limit is unsatisfactory (for + example someone needs RSA 1024 + with SHA512), the user can disable + this feature. In this case, the + card driver will do everything + necessary before sending the data + (hash code) to the card. +

+ PKCS#1 v1.5 padding in HW can be globally disabled by option disable_hw_pkcs1_padding. + When the global option is used to disable padding, the padding will be disabled even though the MyEID-specific option does not turn it off. +

Configuration Options for German ID Card

+ can = value; +

+ German ID card requires the CAN to + be verified before QES PIN. This, + however, is not part of the PKCS#15 + profile of the card. So for + verifying the QES PIN we actually + need both. The CAN may be given + here. If the CAN is not given here, + it will be prompted on the command + line or on the reader (depending on + the reader's capabilities). +

+ st_dv_certificate = filename; + st_certificate = filename; + st_key = filename; +

+ QES is only possible with a Comfort + Reader (CAT-K), which holds a + cryptographic key to authenticate + itself as signature terminal (ST). + We usually will use the reader's + capability to sign the data. + However, during development you + may specify soft certificates and + keys for a ST. +

+ An example PKI can be found in the + example data for the + German + ID card emulator +

Configuration Options for DNIe

+ user_consent_enabled = bool; +

+ Configure the warning message when + performing a signature operation + with the DNIe. Only used if + compiled with + --enable-dnie-ui +

+ user_consent_app = filename; +

+ Specify the pinentry application to + use if warning is configured to be + displayed using pinentry (Default: + /usr/bin/pinentry). + Only used if compiled with + --enable-dnie-ui +

Configuration Options for Polish eID Card

+ can = value; +

+ CAN (Card Access Number – 6 digit number + printed on the right bottom corner of the + front side of the document) is required + to establish connection with the card. + It might be overwritten by EDO_CAN + environment variable. Currently, it is not + possible to set it in any other way. +

Configuration Options for Slovenian eID Card

+ can = value; +

+ CAN (Card Access Number – 6 digit number + printed on the right bottom corner of the + front side of the document) is required + to establish connection with the card. + It might be overwritten by EOI_CAN + environment variable. As CAN is also stored on the card + (in encrypted form) it can be used to automatically establish + secure connection, but only if the card is accessed over the + contact interface. +

Configuration Options for D-Trust Signature Cards

+ can = value; +

+ CAN (Card Access Number – 6 digit number printed on the front side of + the card) is required to establish connection with the card. It might + be overwritten by DTRUST_CAN environment variable. + If not specified here, the CAN will be read from a card specific cache, + prompted on the card readers PIN pad or prompted interactively in the + following order. +

  1. Environment variable DTRUST_CAN

  2. This configuration file option

  3. PKCS#15 card-specific cache

  4. Pin pad of a PACE-capable card reader

+ can_use_cache = bool; +

+ Cache the CAN number of a card once it was provided by the user in the + directory file_cache_dir. The cached CAN is used + later to avoid interactively prompting for the CAN. This feature is + enabled by default. +

Configuration Options for PIV Card

Configuration Options for Lithuanian ID Card

+ can = value; +

+ Lithuanian ID card requires the card access + number (CAN) to be verified before reading it or + performing any operation. It is printed on the + front side bottom right corner of the card. +

+ Note that alternatively it can also be set by + lteid-tool. For command + line tools it can also be provided via + LTEID_CAN environment variable. +

Configuration based on ATR

+

+ atrmask = hexstring; +

+ The mask is logically AND'd with an + card ATR prior to comparison with + the ATR reference value above. + Using this mask allows identifying + and configuring multiple ATRs as + the same card model. +

+ driver = name; +

+ When enabled, overrides all + possible settings from the card + drivers built-in card configuration + list. +

+ name = name; +

+ Set card name for card drivers that + allows it. +

+ type = num; +

+ Allows setting the exact type of + the card internally used by the + card driver. Allowed values can be + found in the source code of + cards.h. +

+ flags = value... ; +

+ Card flags as an hex value. + Multiple values are OR'd together. + Depending on card driver, this + allows fine-tuning the capabilities + in the card driver for your card. +

+ Optionally, some known parameters + can be specified as strings: +

  • + rng: + On-board random number + source +

  • + keep_alive: + Request the card driver + to send a "keep alive" + command before each + transaction to make + sure that the required + applet is still + selected. +

+

+ pkcs15emu = name; +

+ When using PKCS#15 emulation, force + the emulation driver for specific + cards. Required for external + drivers, but can be used with + built-in drivers, too. +

+ force_protocol = value; +

+ Force protocol selection for + specific cards. Known parameters: +

  • + t0 +

  • + t1 +

  • + raw +

+

+ read_only = bool; +

+ Mark card as read/only card in + PKCS#11/Minidriver/BaseCSP interface + (Default: false). +

+ md_supports_X509_enrollment = bool; +

+ Indicate X509 enrollment support at + Minidriver/BaseCSP interface + (Default: false). +

+ md_guid_as_id = bool; +

+ Use the GUID generated for the key + as id in the PKCS#15 structure + (Default: false, i.e. auto generated) +

+ md_guid_as_label = bool; +

+ Use the GUID generated for the key + as label in the PKCS#15 structure + (Default: false, + i.e. no label set). +

+ md_supports_container_key_gen = bool; +

+ Card allows generating key pairs on the card (Default: false). +

+ md_supports_container_key_import = bool; +

+ Card allows importing private keys + (Default: false). +

+ md_pinpad_dlg_title = value; +

+ Window title of the PIN pad dialog + (Default: "Windows + Security"). +

+ md_pinpad_dlg_icon = filename; +

+ Filename of the icon for the PIN + pad dialog; use + "" for no icon + (Default: Built-in smart card icon). +

+ md_pinpad_dlg_main = value; +

+ Main instruction of the PIN pad + dialog (Default: "OpenSC + Smart Card Provider"). +

+ md_pinpad_dlg_content_user = value; +

+ Content of the PIN pad dialog for + role "user" (Default: + "Please enter your PIN on the PIN + pad."). +

+ md_pinpad_dlg_content_user_sign = value; +

+ Content of the PIN pad dialog for + role "user+signature" (Default: + "Please enter your digital signature + PIN on the PIN pad."). +

+ md_pinpad_dlg_content_admin = value; +

+ Content of the PIN pad dialog for + role "admin" (Default: + "Please enter your PIN to unblock the + user PIN on the PIN pad.") +

+ md_pinpad_dlg_expanded = value; +

+ Expanded information of the PIN pad + dialog (Default: "This window will be + closed automatically after the PIN has been + submitted on the PIN pad (timeout typically + after 30 seconds).") +

+ md_pinpad_dlg_enable_cancel = bool; +

+ Allow the user to cancel the PIN + pad dialog (Default: + false). + + If this value is set to + true, the user needs to + click "OK" to start the PIN verification on the + PIN pad. The user can choose the default + behavior by enabling or disabling the checkbox + of the dialog. The setting is saved by the + program's full path + (program_path) that + uses OpenSC. +

+ The registry key HKCU\Software\OpenSC + Project\OpenSC\md_pinpad_dlg_enable_cancel\program_path + overwrites this setting with a + DWORD set to either + 1 (enabled) or + 0 (disabled). +

+ md_pinpad_dlg_timeout = num; +

+ Time in seconds for the progress + bar of the PIN pad dialog to tick. + 0 removes the + progress bar (Default: + 30). +

+ notify_card_inserted = value; + notify_card_inserted_text = value; +

+ Notification title and text when + card was inserted (Default: + "Smart card + detected", ATR of + the card). +

+ notify_card_removed = value; + notify_card_removed_text = value; +

+ Notification title and text when + card was removed (Default: + "Smart card + removed", name of + smart card reader). +

+ notify_pin_good = value; + notify_pin_good_text = value; +

+ Notification title and text when + PIN was verified (Default: + "PIN verified", + "Smart card is + unlocked"). +

+ notify_pin_bad = value; + notify_pin_bad_text = value; +

+ Notification title and text when + PIN was wrong (Default: + "PIN not + verified", + "Smart card is + locked"). +

+

Configuration of PKCS#15 Framework

+ use_file_caching = value; +

+ Whether to cache the card's files (e.g. + certificates) on disk in + file_cache_dir. + Possible parameters: +

  • + yes: Cache all files (public and private). +

  • + public: Cache only public files. +

  • + no: File caching disabled. +

+ (Default: public + for the following card drivers + atrust-acos (deactivated driver), + belpic, + cac1, + cac, + coolkey, + dnie, + edo, + esteid2018, + esteid2025, + flex (deactivated driver), + cyberflex (deactivated driver), + gemsafeV1, + idprime, + itacns, + jpki, + MaskTech, + mcrd (deactivated driver), + npa, + nqapplet, + tcos, + dtrust and otherwise no). +

+ If caching is done by a system process, the + cached files may be placed inaccessible from + the user account. Use a globally readable and + writable location if you wish to share the + cached information. Note that the cached files + may contain personal data such as name and mail + address. +

+ The file cache may need to be wiped in order to + detect changes, e.g. after renewal of an on-card + certificate. This is the reason why caching is only + activated for tokens which are known to have a + static profile. +

+ file_cache_dir = filename; +

+ Where to cache the card's files. The default values are: +

  • + $XDG_CACHE_HOME/opensc/ (If $XDG_CACHE_HOME is defined) +

  • + $HOME/.cache/opensc/ (Unix) +

  • + $USERPROFILE\.eid-cache\ (Windows) +

+

+ If caching is done by a system process, the + cached files may be placed inaccessible from + a user account. Use a globally readable and + writable location if you wish to share the + cached information. Note that the cached files + may contain personal data such as name and mail + address. +

+ The PIV-II card driver supports the history object's + list of retired keys and certificates if they are + readable in the file cache directory. + If the specified object's URL is + "http://"DNS name"/"ASCII-HEX OffCardKeyHistoryFile, + then the searches for the file name + OffCardKeyHistoryFile + in the cache directory. +

+ use_pin_caching = bool; +

+ Use PIN caching (Default: true)? +

+ pin_cache_counter = num; +

+ How many times to use a PIN from cache before + re-authenticating it (Default: + 10)? +

+ pin_cache_ignore_user_consent = bool; +

+ Older PKCS#11 applications not supporting + CKA_ALWAYS_AUTHENTICATE may + need to set this to get signatures to work with + some cards (Default: false). +

+ It is recommended to enable also PIN caching using + use_pin_caching option for OpenSC + to be able to provide PIN for the card when needed. +

+ pin_protected_certificate = value; +

+ How to handle a PIN-protected certificate. Known + parameters: +

  • + protect: The certificate stays PIN-protected. +

  • + declassify: Allow + reading the certificate without + enforcing verification of the PIN. +

  • + ignore: Ignore PIN-protected certificates. +

+ (Default: protect. +

+ enable_pkcs15_emulation = bool; +

+ Enable pkcs15 emulation (Default: + true). +

+ try_emulation_first = bool; +

+ Prefer pkcs15 emulation code before the normal + pkcs15 processing (Default: + no). Some cards work in + emu-only mode, and do not depend on this + option. +

+ enable_builtin_emulation = bool; +

+ Enable builtin emulators (Default: + true). +

+ builtin_emulators = emulators; +

+ List of the builtin pkcs15 emulators to test + (Default: internal) +

+ Special value of internal will try all not disabled builtin pkcs15 emulators. +

+ Special value of old will try all disabled pkcs15 emulators. +

+ pkcs11_enable_InitToken = bool; +

+ Enable initialization and card recognition + (Default: false). +

+ emulate name { + block_contents + } + +

+ Configuration options for a PKCS#15 emulator + where name is a + short name for an external card driver. +

+ module = filename; +

+ For pkcs15 emulators loaded from an + external shared library/DLL, you need to + specify the path name of the module and + customize the card_atr example above + correctly. +

+ function = name; +

+ Get the init function name of the + emulator (Default: + sc_pkcs15_init_func_ex) +

+ application hexstring { + block_contents + } + +

+ Configuration of the on-card-application where + hexstring is the + application identifier (AID). +

+ type = name; +

+ Type of application where + name is one + of: +

  • + generic +

  • + protected +

+

+ Used to distinguish the common access + application and application for which + authentication to perform some + operation cannot be obtained with the + common procedures (ex. object creation + protected by secure messaging). Used + by PKCS#11 module configured to expose + restricted number of slots. (for ex. + configured to expose only User PIN + slot, User and Sign PINs slots, ...) +

+ model = name; +
+ disable = bool; +

+ Do not expose application in PKCS#15 + framework (Default: + false) +

+ user_pin = name; +

+ Name of the User PIN object that will be used as the main PIN. +

+ sign_pin = name; +

+ Name of the PIN object that will be used for signing. +

Configuration of PKCS#11

+ max_virtual_slots = num; +

+ Maximum Number of virtual slots (Default: + 16). If there are more slots + than defined here, the remaining slots will be + hidden from PKCS#11. +

+ slots_per_card = num; +

+ Maximum number of PIN slots per smart card (Default: + 4). If the card has fewer PINs + than defined here, the remaining number of slots + will be empty. For Firefox, Chrome and Chromium, the + slots_per_card is set to 1, + to avoid prompting for unrelated PINs. Typically, this + effectively disables signature PINs and keys. +

+ lock_login = bool; +

+ By default, the OpenSC PKCS#11 module will not lock + your card once you authenticate to the card via + C_Login (Default: + false). + + Thus the other users or other applications is not + prevented from connecting to the card and perform + crypto operations (which may be possible because + you have already authenticated with the card). This + setting is not very secure. +

+ Also, if your card is not locked, you can enconter + problems due to limitation of the OpenSC framework, + that still is not thoroughly tested in the multi + threads environment. +

+ Your settings will be more secure if you choose to + lock your card. Nevertheless this behavior is a + known violation of PKCS#11 specification. Now once + one application has started using your card with + C_Login, no other application + can use it, until the first is done and calls + C_Logout or + C_Finalize. In the case of many + PKCS#11 application this does not happen until you + exit the application. +

+ Thus it is impossible to use several smart card + aware applications at the same time, e.g. you + cannot run both Firefox + and Thunderbird at the + same time, if both are configured to use your smart + card. +

+ atomic = bool; +

+ By default, interacting with the OpenSC PKCS#11 + module may change the state of the token, e.g. + whether a user is logged in or not (Default: + false). +

+ Thus other users or other applications may change + or use the state of the token unknowingly. Other + applications may create signatures abusing an + existing login or they may logout unnoticed. +

+ With this setting enabled the login state of the + token is tracked and cached (including the PIN). + Every transaction is preceded by restoring the + login state. After every transaction a logout is + performed. This setting by default also enables + lock_login to disable access for + other applications during the atomic transactions. +

+ Please note that any PIN-pad should be disabled + (see enable_pinpad), because the + user would have to input his PIN for every + transaction. +

+ init_sloppy = bool; +

+ With this setting disabled, the OpenSC PKCS#11 + module will initialize the slots available when the + application calls C_GetSlotList. + With this setting enabled, the slots will also get + initialized when C_GetSlotInfo + is called (Default: true). +

+ This setting is a workaround for + Java which does not call + C_GetSlotList when configured + with a static slot instead of + slotListIndex. +

+ user_pin_unblock_style = mode; +

+ User PIN unblock style mode + is one of: +

  • + none (Default): PIN + unblock is not possible with PKCS#11 API +

  • + set_pin_in_unlogged_session: + C_SetPIN in unlogged + session: PUK is passed as the + OldPin argument of the + C_SetPIN call. +

  • + set_pin_in_specific_context: + C_SetPIN in the + CKU_SPECIFIC_CONTEXT + logged session: PUK is passed as the + OldPin argument of the + C_SetPIN call. +

  • + init_pin_in_so_session: + C_InitPIN in + CKU_SO logged session: + User PIN 'UNBLOCK' is protected by SOPIN. + (PUK == SOPIN). +

+

+ create_puk_slot = bool; +

+ Create slot for unblocking PIN with PUK (Default: + false). This way PKCS#11 API can + be used to login with PUK and change a PIN. May + cause problems with some applications like + Firefox and + Thunderbird. +

+ create_slots_for_pins = mode... ; +

+ Symbolic names of PINs for which slots are created + where mode is a list of: +

  • + all (Default): All + non-SO-PIN, non-unblocking PINs +

  • + user: The first + global or first local PIN +

  • + sign: The second PIN + (first local, second global or second + local) +

+

+ Card can contain more then one PINs or more then + one on-card application with its own PINs. + Normally, to access all of them with the PKCS#11 + API a slot has to be created for all of them. Many + slots could be annoying for some of widely used + application, like FireFox. This configuration + parameter allows to select the PIN(s) for which + PKCS#11 slot will be created. +

+ Only PINs initialised, non-SO-PIN, non-unblocking + are associated with symbolic name. +

+ For the module to simulate the opensc-onepin module + behavior the following option + create_slots_for_pins = "user"; +

Environment

+ OPENSC_CONF +

+ Filename for a user defined configuration file +

+ If this environment variable is not found on + Windows, the registry key + Software\OpenSC + Project\OpenSC\ConfigFile is + checked. +

+ OPENSC_DEBUG +

+ See + debug = num; + +

+ OPENSC_DRIVER +

+ See + card_drivers = name... ; + +

+ OPENSC_DEEPBIND +

+ The OpenSC and its tools are dynamically loading various + libraries (pcsclite, dynamic drivers and other pkcs11 + modules) using + dlopen(3). +

+ When this environment variable is set to + 1 + for the process running OpenSC, the calls will include + RTLD_DEEPBIND flag, which should + cause better symbol isolation between OpenSC and + loaded libraries. On the other hand, this is incompatible + with various static analysis tools as well as some + browser builds. +

+ This option may be useful, if OpenSC needs to load a library + that pollutes the processes' global namespace and thereby + causes the unintended use of a different function with the + same name. In particular, this may happen when + pkcs11-spy + is used for logging between application and PKCS#11 library. +

+ CARDMOD_LOW_LEVEL_DEBUG +

+ Write minidriver debug information to + C:\tmp\md.log, if set to + 1. +

+ If this environment variable is not found on + Windows, the registry key + Software\OpenSC + Project\OpenSC\MiniDriverDebug is + checked. +

+ PIV_EXT_AUTH_KEY, + PIV_9A_KEY, + PIV_9C_KEY, + PIV_9D_KEY, + PIV_9E_KEY +

+ PIV configuration during initialization with + piv-tool. +

+ PIV_USE_SM, + PIV_PAIRING_CODE +

+ PIV configuration during initialization + See Configuration Options for PIV Card. +

Files

+ /etc/opensc.conf +

+ System-wide configuration file +

+ /usr/share/doc/opensc/opensc.conf +

+ Extended example configuration file +


Name

pkcs15-profile — format of profile for pkcs15-init

Description

+ The pkcs15-init utility for PKCS #15 smart card + personalization is controlled via profiles. When starting, it will read two + such profiles at the moment, a generic application profile, and a card + specific profile. The generic profile must be specified on the command line, + while the card-specific file is selected based on the type of card detected. +

+ The generic application profile defines general information about the card + layout, such as the path of the application DF, various PKCS #15 files within + that directory, and the access conditions on these files. It also defines + general information about PIN, key and certificate objects. Currently, there + is only one such generic profile, pkcs15.profile. +

+ The card specific profile contains additional information required during + card initialization, such as location of PIN files, key references etc. + Profiles currently reside in /usr/share/opensc +

+ Basic PKCS#15 terminology: +

  1. + MF (Master File) is root of the filesystem hierarchy +

  2. + DF(PKCS#15) is directory containing the PKCS#15 files and directories +

  3. + EF(ODF) (Object Directory File) is elementary file containing pointers + to other elementary files (PrKDFs, PuKDFs, SKDFs, CDFs, DODFs, AODFs) +

  4. + PrKDF (Private Key Directory File) is elementary file containing + pointers to the private keys and additional information about the private keys +

  5. + PubKDF (Public Key Directory File) is elementary file containing pointers to the public + keys and additional information about the public keys +

  6. + CDF (Certificate Directory File) is elementary file containing pointers to the + certificates and additional information about the certificates +

  7. + EF(TokenInfo) is elementary file with generic information about the card +

+

Syntax and semantics

+ The block syntax of profile files is in general similar to the configuration file. + The profile file, is composed of blocks, which, in general, have the following format: +

+key [, name...] {
+	block_contents
+}
+			

+ block_contents is one or more + block_items where a + block_item is one of +

  • + # comment string +

  • + key [, name...] = value; +

  • + block +

+

+ At the root level, the profile contains several configuration blocks. + The block keys are as follows: +

  • + cardinfo: Configuration for general information about card. +

  • + pkcs15: Control for some of the general aspects of the PKCS#15 put onto the card. +

  • + option: Profile options to modify the behavior of profile. +

  • + PIN: Configuration and limits for particular PIN type. +

  • + filesystem: Specification for filesystem that is to be created on the card. +

  • + macros +

+

Profile file configuration

Configuration of Card Information

+ cardinfo { + block_contents + } + +

+ Configuration for general information about card: +

+ label = name; +

+ Card label (Default: OpenSC Card). +

+ manufacturer = name; +

+ Card manufacturer (Default: OpenSC Project). +

+ min-pin-length = int; +

+ Minimal length of PIN (Default: 4). +

+ max-pin-length = int; +

+ Maximal length of PIN, should be overridden in the per-card profile + (Default: 8). +

+ pin-encoding = value; +

+ Encoding type of PIN. Known parameters: +

  • + BCD: + binary-coded decimal +

  • + ascii-numeric: + ASCII numerical values +

  • + utf8 +

  • + half-nibble-bcd +

  • + iso9564-1 +

+ (Default: ascii-numeric). +

+ pin-pad-char = value; +

+ Character used for padding the PIN when needed (Default: 0x00). +

+ pin-domains = bool; +

+ Some cards need to keep all their PINs in separate directories. + The particular keys in that domain will be put below the DF of the specified PIN. + (Default: no) +

Configuration of PKCS#15

+ pkcs15 { + block_contents + } + +

+ Control for some of the general aspects of the PKCS#15 put onto the card. + Parameters in this block are: +

+ direct-certificates = bool; +

+ The PKCS#15 system must contain at least one CDF, it contains the certificates + directly or references to certificates. This options defines whether the certificates + should be put directly in the CDF itself or not (Default: no). +

+ encode-df-length = bool; +

+ Save length of DF into ODF file. Useful if we store certificates directly in the CDF + for better better performance and robustness (Default: no). +

+ do-last-update = value; +

+ Store information about last update in the EF(TokenInfo) (Default: yes). +

+ pkcs15-id-style = value; +

+ Method to calculate ID of the crypto objects. Known parameters: +

native
  • + native: + 'E' + number_of_present_objects_of_the_same_type +

  • + mozilla: + SHA1(modulus) for RSA +

  • + rfc2459 + SHA1(SequenceASN1 of public key components as ASN1 integers) +

+

+ minidriver-support-style = value; +

+ Style of pkcs15-init support of minidriver. Known parameters: +

  • + none +

  • + gemalto +

+ (Default: none) +

Configuration of Profile Option

+ option name { + block_contents + } + +

+ The name specifies profile options to modify the behavior of profile, it can be +

+

+

  • + default: + option specifies default settings and this block with option is always processed, +

  • + onepin: + option for using 1 user PIN, creation/deletion/generation is controlled by the user PIN and thus by the user (as a result, only 1 user PIN is possible), +

  • + small + option suitable for cards with small memory. +

+

+

+ The options are used by pkcs15-init tool by --profile name, -p name: +

+

+

  • + pkcs15+default: + the default (not needed to specify it) +

  • + pkcs15+onepin: + for the onepin profile option +

  • + pkcs15+small + for the small profile option +

+

+

+ The option block can contain following sub-blocks: +

+
+ macros { block_contents } +

+ Macros are specified in form of name = value; pairs. +

+ pkcs15 { block_contents } +

+ Inner block for configuration of PKCS#15 structure. +

Configuration of PINs

+ PIN name { + block_contents + } + +

+ The name specifies PIN type, it can be +

+

+

  • + pin or user-pin + (no need to set file path or reference as it is done dynamically) +

  • + puk or user-puk +

  • + sopin or so-pin +

  • + sopuk or so-puk +

+

+

+ Known parameters are: +

+ attempts = int; +

+ Defines number of attempts for the given PIN (Default: 3). +

+ flags = value...; +

+ Flags define properties of the PIN. Possible flags: +

  • + case-sensitive +

  • + local +

  • + change-disabled +

  • + unblock-disabled +

  • + initialized +

  • + needs-padding +

  • + unblockingPin +

  • + soPin +

  • + disable-allowed +

  • + integrity-protected +

  • + confidentiality-protected +

  • + exchangeRefData +

+ (Default: local,initialized,needs-padding). +

+ auth-id = value; +

+ Value used for auth ID (Default: 0). +

+ min-length = int; +

+ Minimal length of PIN (Default: value min-pin-length set in cardinfo block). +

+ max-length = int; +

+ Maximal length of PIN (Default: value max-pin-length set in cardinfo block). +

+ reference = int; +

+ Value of reference of the PIN (Default: set in particular card driver). +

+ file = name; +

+ File with PIN, obsolete option (Default: None). +

+ offset = int; +

+ Offset of PIN in PIN file, obsolete option (Default: 0). +

+ encoding = value; +

+ Encoding type of PIN. Possible values: +

  • + BCD +

  • + ascii-numeric +

  • + utf8 +

  • + half-nibble-bcd +

  • + iso9564-1 +

+ (Default: value pin-encoding set in cardinfo block). +

+ stored-length = int; +

+ (Default: value max-pin-length set in cardinfo block). +

+ max-unlocks = int; +

+ (Default: 0). +

+ Values in this block can be set by macros. That allows to specify the particular values with the usage of option. +

Configuration of Filesystem

filesystem { + block_contents + } +

+ This block contains the specification for filesystem that is to be created on the card. + The filesystem consists of several nested blocks representing DF and EF files. + When the DFs or EFs are specified in card specific profile, this is added to the file system info specified in the main profile. +

+ EF name { + block_contents + } + +

+ This block defines elementary file in PKCS#15 file hierarchy. + The name can be one of: +

  • + PKCS15-TokenInfo +

  • + PKCS15-ODF +

  • + PKCS15-UnusedSpace +

  • + PKCS15-PRKDF +

  • + PKCS15-PUKDF +

  • + PKCS15-PUKDF-TRUSTED +

  • + PKCS15-SKDF +

  • + PKCS15-CDF +

  • + PKCS15-CDF-TRUSTED +

  • + PKCS15-CDF-USEFUL +

  • + PKCS15-DODF +

  • + PKCS15-AODF +

+

+

+ The EF block can contain: +

+
+ type = EF; +

+ Type must match type of file. +

+ acl = value; +

+ Value of ACL (Access Control List) (Default: NONE) +

+ file-id = EF; +

+ File ID, relative path. +

+ structure = value; +

+ File structure is one of: +

  • + TRANSPARENT +

  • + LINEAR-FIXED +

  • + LINEAR-FIXED-TLV +

  • + LINEAR-VARIABLE +

  • + LINEAR-VARIABLE-TLV +

  • + CYCLIC +

  • + CYCLIC-TLV +

+

+ DF name { + block_contents + } + +

+ This block defines directory file in PKCS#15 file hierarchy. + The name can be one of: +

  • + MF +

  • + PKCS15-AppDF +

  • + Special cases for those DFs handled separately by the PKCS15 logic +

+

+

+ The DF block can contain: +

+
+ type = DF; +

+ Type must match type of file. +

+ path = value; +

+ Specification of path of the directory file. +

+ file-id = value; +

+ File ID, relative path. +

+ aid = value; +

+ Value of AID, in XX:XX:XX:...:XX:XX:XX notation. +

+ acl = value; +

+ Type must match type of file. +

+ size = int; +

+ Size of the file in bytes. +

+ EF name { block_contents } +

+ Block specifying nested elementary file. +

+ Typically, the root DF is MF. +

+ It is mandatory that profile file contains DF entry for MF (Master File). + Otherwise the profile file is incomplete and cannot be used. +

+ The DF can contain other DF or MF blocks. + For examples how the filesystem structure may look like, + please refer to pkcs15.profile or any other present profile file. +

See also

+ pkcs15-init(1), + pkcs15-crypt(1) +

diff --git a/doc/files/files.xml b/doc/files/files.xml new file mode 100644 index 0000000000..3a01e1a970 --- /dev/null +++ b/doc/files/files.xml @@ -0,0 +1,10 @@ + + + + + OpenSC Manual Pages: Section 5 + + + + diff --git a/doc/files/opensc.conf.5.xml.in b/doc/files/opensc.conf.5.xml.in new file mode 100644 index 0000000000..58ab8e88a9 --- /dev/null +++ b/doc/files/opensc.conf.5.xml.in @@ -0,0 +1,1992 @@ + + + + opensc.conf + 5 + OpenSC + OpenSC File Formats + opensc + + + + opensc.conf + configuration file for OpenSC + + + + Description + + OpenSC obtains configuration data from the following sources in the following order + + + command-line options + + + environment variables + + + Windows registry key in + HKEY_CURRENT_USER (if available) + + + Windows registry key in + HKEY_LOCAL_MACHINE (if available) + + + system-wide configuration file + (@sysconfdir@/opensc.conf) + + + + + The configuration file, opensc.conf, is composed + of blocks, which, in general, have the + following format: + +key, name { + block_contents +} + + block_contents is one or more + block_items where a + block_item is one of + + + # comment string + + + key, name = value; + + + block + + + + + At the root level, opensc.conf should contain + one or more application specific configuration blocks: + +app application { + block_contents +} + + application + specifies one of: + + + filename: Configuration block for the application with specified file path. + + + default: The fall-back configuration block for all applications + + + opensc-pkcs11: Configuration block for the PKCS#11 module (opensc-pkcs11@DYN_LIB_EXT@) + + + onepin-opensc-pkcs11: Configuration block for the PKCS#11 one-PIN-module (onepin-opensc-pkcs11@DYN_LIB_EXT@) + + + cardmod: Configuration block for Windows' minidriver (opensc-minidriver.dll) + + + cardos-tool, + cryptoflex-tool, + dnie-tool, + egk-tool, + eidenv, + gids-tool, + iasecc-tool, + netkey-tool, + npa-tool, + openpgp-tool, + opensc-asn1, + opensc-explorer, + opensc-notify, + opensc-tool, + piv-tool, + pkcs11-tool, + pkcs15-crypt, + pkcs15-init, + pkcs15-tool, + sc-hsm-tool, + westcos-tool: + Configuration block for OpenSC tools + + + + + + + Configuration Options + + + + + + + Amount of debug info to print (Default: + 0). A greater value means more + debug info. + + + The environment variable + OPENSC_DEBUG overwrites this + setting. + + + + + + + + The file to which debug output will be written + (Default: stderr). Special + values stdout and + stderr are recognized. + + + + + + + + PKCS#15 initialization/personalization profiles + directory for + + pkcs15-init + 1. + + (Default: @PROFILE_DIR_DEFAULT@). + + + If this configuration value is not found on + Windows, the registry key + Software\OpenSC + Project\OpenSC\ProfileDir is + checked. + + + + + + + + Disable colors of log messages (Default: + false if attached to a console, + true otherwise). + + + + + + + + Disable pop-ups of built-in GUI (Default: false). + + + + + + + + Enable default card driver (Default: + false). Default card driver is + explicitly enabled for + + opensc-explorer + 1. + + and + + opensc-tool + 1. + + + + + + + + + Allowlist of card drivers to load at start-up. + The special value internal (the + default) will load all statically linked drivers. + + + If an unknown (i.e. not internal or old) driver is + supplied, a separate configuration + block has to be written for the driver. A special + value old will load all + statically linked drivers that may be removed in + the future. + + + The list of supported card driver names can be + retrieved from the output of opensc-tool + --list-drivers. + + + The environment variable + OPENSC_DRIVER overwrites this + setting. + + + + + + + + List of readers to ignore (Default: empty). If any + of the comma separated strings listed is matched in + a reader name (case sensitive, partial matching + possible), the reader is ignored by OpenSC. Use + opensc-tool --list-readers to + see all currently connected readers. + + + + + + + + + Configuration of the smart card reader driver where name is one of: + + + ctapi: See + + + + pcsc: See + + + openct: See + + + cryptotokenkit: Configuration block for CryptoTokenKit readers + + + + + See . + + + + + + + + + Configuration of the card driver where name is one of: + + + npa: See + + + dnie: See + + + edo: See + + + eoi: See + + + myeid: See + + + dtrust: See + + + Any other value: Configuration block for an externally loaded card driver + + + + + + + + + + + In addition to the built-in list of known cards in + the card driver, you can configure a new card for + the driver using the + block. + + + For details see . + + + + + + + + + Disabling PKCS#1 v1.5 padding in HW when card supports doing raw RSA operations. + Known parameters: + + + no: PKCS#1 v1.5 padding is enabled in HW when card supports it. + + + sign: PKCS#1 v1.5 padding + is disabled only for signatures (PKCS#1 v1.5 type 1). + + + decipher: PKCS#1 v1.5 padding + is disabled only for decryption (PKCS#1 v1.5 type 2). + + + both: PKCS#1 v1.5 padding + is disabled both for signatures and decryption (PKCS#1 v1.5 type 1 and 2). + + + (Default: decipher). + + + + + + + + Specify a OpenSSL configuration file to be loaded into the + OpenSC's library context. This should not be needed for general + operation, but is needed if you need to configure some + specifics of OpenSSL, without the need to rebuild OpenSC (for + example configure and load a FIPS provider). + For more information about configuring OpenSSL, see + + config + 5. + + and + + fips_config + 5. + . + (Default: empty). + + + + + + + + Configuration options for the secure messaging profile name: + + + + + + + + Name of external SM module (Default: @DEFAULT_SM_MODULE@). + + + + + + + + Directory with external SM module + (Default: @libdir@). + + + If this configuration value is not + found on Windows, the registry key + Software\OpenSC + Project\OpenSC\SmDir is + checked. + + + + + + + + Specific data to tune the module initialization. + + + + + + + + Secure messaging mode. Known parameters: + + + transmit: + In this mode the + procedure to securize + an APDU is called by + the OpenSC general APDU + transmit procedure. In + this mode all APDUs, + except the ones + filtered by the card + specific procedure, are + securized. + + + acl: + In this mode APDU are + securized only if + needed by the ACLs of + the command to be + executed. + + + + + + + + + + Secure messaging type specific flags. + + + + + + + + Default KMC of the GP Card Manager for the Oberthur's Java cards. + + + + + + + + + + + + + + Keyset values from IAM profiles of + the Gemalto IAS/ECC cards with an + optional application identifier + + + + + + + + + + + Internal configuration options where name is one of: + + + pkcs15: See + + + + + + + + + + + Parameters for the OpenSC PKCS11 module. + + + For details see . + + + + + + + Configuration of Smart Card Reader Driver + + + Configuration Options for all Reader Drivers + + + + + + + + Limit command and response sizes + (Default: + + = 255, + + = 256) . Some + Readers don't propagate their + transceive capabilities correctly. + max_send_size and max_recv_size + allow setting the limits manually, + for example to enable extended + length capabilities. + + + + + + + + Detect reader capabilities with + escape commands (wrapped APDUs with + CLA=0xFF as defined by PC/SC pt. 3 + and BSI TR-03119, e.g. for getting + the UID, escaped PIN commands and + the reader's firmware version, + Default: false) + + + + + + + Configuration of CT-API Readers + + + + + + + Load the specified CT-API module with the specified number of ports. + + + + + + + Configuration of PC/SC Readers + + + + + + + Connect to reader in exclusive mode + (Default: false)? + This option has no effect in Windows' minidriver. + + + + + + + + What to do when disconnecting from + a card (SCardDisconnect). Valid + values are + leave, + reset, + unpower (Default: + leave). + This option has no effect in Windows' minidriver. + + + + + + + + What to do at the end of a + transaction (SCardEndTransaction). + Valid values + are leave, + reset, + unpower (Default: + leave). + This option has no effect in Windows' minidriver. + + + + + + + + What to do when reconnection to a + card (SCardReconnect). Valid values + are leave, + reset, + unpower (Default: + leave). + This option has no effect in Windows' minidriver. + + + + + + + + Enable pinpad if detected (PC/SC + v2.0.2 Part 10, Default: + true) + + + + + + + + Some pinpad readers can only handle + one exact length of the PIN. + + sets this value so that OpenSC + expands the padding to this length + (Default: 0, + i.e. not fixed). + + + + + + + + Use specific PC/SC provider + (Default: + @DEFAULT_PCSC_PROVIDER@). + + + + + + + Configuration of OpenCT Readers + + + + + + + Virtual readers to allocate (Default: 2). + + + + + + + + + Configuration Options for MyEID Card + + + + + + + The MyEID card can internally + encapsulate the data (hash code) + into a DigestInfo ASN.1 structure + according to the selected hash + algorithm (currently only for SHA1). + DigestInfo is padded to RSA key + modulus length according to PKCS#1 + v1.5, block type 01h. Size of the + DigestInfo must not exceed 40% + of the RSA key modulus length. If + this limit is unsatisfactory (for + example someone needs RSA 1024 + with SHA512), the user can disable + this feature. In this case, the + card driver will do everything + necessary before sending the data + (hash code) to the card. + + PKCS#1 v1.5 padding in HW can be globally disabled by option disable_hw_pkcs1_padding. + When the global option is used to disable padding, the padding will be disabled even though the MyEID-specific option does not turn it off. + + + + + + + + Configuration Options for German ID Card + + + + + + + German ID card requires the CAN to + be verified before QES PIN. This, + however, is not part of the PKCS#15 + profile of the card. So for + verifying the QES PIN we actually + need both. The CAN may be given + here. If the CAN is not given here, + it will be prompted on the command + line or on the reader (depending on + the reader's capabilities). + + + + + + + + + + QES is only possible with a Comfort + Reader (CAT-K), which holds a + cryptographic key to authenticate + itself as signature terminal (ST). + We usually will use the reader's + capability to sign the data. + However, during development you + may specify soft certificates and + keys for a ST. + + + An example PKI can be found in the + example data for the + German + ID card emulator + + + + + + + Configuration Options for DNIe + + + + + + + Configure the warning message when + performing a signature operation + with the DNIe. Only used if + compiled with + + + + + + + + + Specify the pinentry application to + use if warning is configured to be + displayed using pinentry (Default: + /usr/bin/pinentry). + Only used if compiled with + + + + + + + + Configuration Options for Polish eID Card + + + + + + + CAN (Card Access Number – 6 digit number + printed on the right bottom corner of the + front side of the document) is required + to establish connection with the card. + It might be overwritten by EDO_CAN + environment variable. Currently, it is not + possible to set it in any other way. + + + + + + + Configuration Options for Slovenian eID Card + + + + + + + CAN (Card Access Number – 6 digit number + printed on the right bottom corner of the + front side of the document) is required + to establish connection with the card. + It might be overwritten by EOI_CAN + environment variable. As CAN is also stored on the card + (in encrypted form) it can be used to automatically establish + secure connection, but only if the card is accessed over the + contact interface. + + + + + + + Configuration Options for D-Trust Signature Cards + + + + + + + + CAN (Card Access Number – 6 digit number printed on the front side of + the card) is required to establish connection with the card. It might + be overwritten by DTRUST_CAN environment variable. + If not specified here, the CAN will be read from a card specific cache, + prompted on the card readers PIN pad or prompted interactively in the + following order. + + + Environment variable DTRUST_CAN + This configuration file option + PKCS#15 card-specific cache + Pin pad of a PACE-capable card reader + + + + + + + + + + Cache the CAN number of a card once it was provided by the user in the + directory file_cache_dir. The cached CAN is used + later to avoid interactively prompting for the CAN. This feature is + enabled by default. + + + + + + + + Configuration Options for PIV Card + + + + + + + Configuration Options for Lithuanian ID Card + + + + + + + Lithuanian ID card requires the card access + number (CAN) to be verified before reading it or + performing any operation. It is printed on the + front side bottom right corner of the card. + + Note that alternatively it can also be set by + lteid-tool. For command + line tools it can also be provided via + LTEID_CAN environment variable. + + + + + + + Configuration based on ATR + + + + + + + + The mask is logically AND'd with an + card ATR prior to comparison with + the ATR reference value above. + Using this mask allows identifying + and configuring multiple ATRs as + the same card model. + + + + + + + + When enabled, overrides all + possible settings from the card + drivers built-in card configuration + list. + + + + + + + + Set card name for card drivers that + allows it. + + + + + + + + Allows setting the exact type of + the card internally used by the + card driver. Allowed values can be + found in the source code of + cards.h. + + + + + + + + Card flags as an hex value. + Multiple values are OR'd together. + Depending on card driver, this + allows fine-tuning the capabilities + in the card driver for your card. + + + Optionally, some known parameters + can be specified as strings: + + + rng: + On-board random number + source + + + keep_alive: + Request the card driver + to send a "keep alive" + command before each + transaction to make + sure that the required + applet is still + selected. + + + + + + + + + + When using PKCS#15 emulation, force + the emulation driver for specific + cards. Required for external + drivers, but can be used with + built-in drivers, too. + + + + + + + + Force protocol selection for + specific cards. Known parameters: + + + t0 + + + t1 + + + raw + + + + + + + + + + Mark card as read/only card in + PKCS#11/Minidriver/BaseCSP interface + (Default: false). + + + + + + + + Indicate X509 enrollment support at + Minidriver/BaseCSP interface + (Default: false). + + + + + + + + Use the GUID generated for the key + as id in the PKCS#15 structure + (Default: false, i.e. auto generated) + + + + + + + + Use the GUID generated for the key + as label in the PKCS#15 structure + (Default: false, + i.e. no label set). + + + + + + + + Card allows generating key pairs on the card (Default: false). + + + + + + + + Card allows importing private keys + (Default: false). + + + + + + + + Window title of the PIN pad dialog + (Default: "Windows + Security"). + + + + + + + + Filename of the icon for the PIN + pad dialog; use + "" for no icon + (Default: Built-in smart card icon). + + + + + + + + Main instruction of the PIN pad + dialog (Default: "OpenSC + Smart Card Provider"). + + + + + + + + Content of the PIN pad dialog for + role "user" (Default: + "Please enter your PIN on the PIN + pad."). + + + + + + + + Content of the PIN pad dialog for + role "user+signature" (Default: + "Please enter your digital signature + PIN on the PIN pad."). + + + + + + + + Content of the PIN pad dialog for + role "admin" (Default: + "Please enter your PIN to unblock the + user PIN on the PIN pad.") + + + + + + + + Expanded information of the PIN pad + dialog (Default: "This window will be + closed automatically after the PIN has been + submitted on the PIN pad (timeout typically + after 30 seconds).") + + + + + + + + Allow the user to cancel the PIN + pad dialog (Default: + false). + + If this value is set to + true, the user needs to + click "OK" to start the PIN verification on the + PIN pad. The user can choose the default + behavior by enabling or disabling the checkbox + of the dialog. The setting is saved by the + program's full path + (program_path) that + uses OpenSC. + + + The registry key HKCU\Software\OpenSC + Project\OpenSC\md_pinpad_dlg_enable_cancel\program_path + overwrites this setting with a + DWORD set to either + 1 (enabled) or + 0 (disabled). + + + + + + + + Time in seconds for the progress + bar of the PIN pad dialog to tick. + 0 removes the + progress bar (Default: + 30). + + + + + + + + + Notification title and text when + card was inserted (Default: + "Smart card + detected", ATR of + the card). + + + + + + + + + Notification title and text when + card was removed (Default: + "Smart card + removed", name of + smart card reader). + + + + + + + + + Notification title and text when + PIN was verified (Default: + "PIN verified", + "Smart card is + unlocked"). + + + + + + + + + Notification title and text when + PIN was wrong (Default: + "PIN not + verified", + "Smart card is + locked"). + + + + + + + + Configuration of PKCS#15 Framework + + + + + + + Whether to cache the card's files (e.g. + certificates) on disk in + . + Possible parameters: + + + yes: Cache all files (public and private). + + + public: Cache only public files. + + + no: File caching disabled. + + + (Default: public + for the following card drivers + atrust-acos (deactivated driver), + belpic, + cac1, + cac, + coolkey, + dnie, + edo, + esteid2018, + esteid2025, + flex (deactivated driver), + cyberflex (deactivated driver), + gemsafeV1, + idprime, + itacns, + jpki, + MaskTech, + mcrd (deactivated driver), + npa, + nqapplet, + tcos, + dtrust and otherwise no). + + + If caching is done by a system process, the + cached files may be placed inaccessible from + the user account. Use a globally readable and + writable location if you wish to share the + cached information. Note that the cached files + may contain personal data such as name and mail + address. + + + The file cache may need to be wiped in order to + detect changes, e.g. after renewal of an on-card + certificate. This is the reason why caching is only + activated for tokens which are known to have a + static profile. + + + + + + + + Where to cache the card's files. The default values are: + + + + $XDG_CACHE_HOME/opensc/ (If $XDG_CACHE_HOME is defined) + + + + + $HOME/.cache/opensc/ (Unix) + + + + + $USERPROFILE\.eid-cache\ (Windows) + + + + + + If caching is done by a system process, the + cached files may be placed inaccessible from + a user account. Use a globally readable and + writable location if you wish to share the + cached information. Note that the cached files + may contain personal data such as name and mail + address. + + + The PIV-II card driver supports the history object's + list of retired keys and certificates if they are + readable in the file cache directory. + If the specified object's URL is + "http://"DNS name"/"ASCII-HEX OffCardKeyHistoryFile, + then the searches for the file name + OffCardKeyHistoryFile + in the cache directory. + + + + + + + + Use PIN caching (Default: true)? + + + + + + + + How many times to use a PIN from cache before + re-authenticating it (Default: + 10)? + + + + + + + + Older PKCS#11 applications not supporting + CKA_ALWAYS_AUTHENTICATE may + need to set this to get signatures to work with + some cards (Default: false). + + + It is recommended to enable also PIN caching using + use_pin_caching option for OpenSC + to be able to provide PIN for the card when needed. + + + + + + + + How to handle a PIN-protected certificate. Known + parameters: + + + protect: The certificate stays PIN-protected. + + + declassify: Allow + reading the certificate without + enforcing verification of the PIN. + + + ignore: Ignore PIN-protected certificates. + + + (Default: protect. + + + + + + + + Enable pkcs15 emulation (Default: + true). + + + + + + + + Prefer pkcs15 emulation code before the normal + pkcs15 processing (Default: + no). Some cards work in + emu-only mode, and do not depend on this + option. + + + + + + + + Enable builtin emulators (Default: + true). + + + + + + + + List of the builtin pkcs15 emulators to test + (Default: internal) + + + Special value of internal will try all not disabled builtin pkcs15 emulators. + + + Special value of old will try all disabled pkcs15 emulators. + + + + + + + + Enable initialization and card recognition + (Default: false). + + + + + + + + Configuration options for a PKCS#15 emulator + where name is a + short name for an external card driver. + + + + + + + + For pkcs15 emulators loaded from an + external shared library/DLL, you need to + specify the path name of the module and + customize the card_atr example above + correctly. + + + + + + + + Get the init function name of the + emulator (Default: + sc_pkcs15_init_func_ex) + + + + + + + + + + + + Configuration of the on-card-application where + hexstring is the + application identifier (AID). + + + + + + + + Type of application where + name is one + of: + + + generic + + + protected + + + + + Used to distinguish the common access + application and application for which + authentication to perform some + operation cannot be obtained with the + common procedures (ex. object creation + protected by secure messaging). Used + by PKCS#11 module configured to expose + restricted number of slots. (for ex. + configured to expose only User PIN + slot, User and Sign PINs slots, ...) + + + + + + + + + + + + + Do not expose application in PKCS#15 + framework (Default: + false) + + + + + + + + Name of the User PIN object that will be used as the main PIN. + + + + + + + + Name of the PIN object that will be used for signing. + + + + + + + + + + Configuration of PKCS#11 + + + + + + + Maximum Number of virtual slots (Default: + 16). If there are more slots + than defined here, the remaining slots will be + hidden from PKCS#11. + + + + + + + + Maximum number of PIN slots per smart card (Default: + 4). If the card has fewer PINs + than defined here, the remaining number of slots + will be empty. For Firefox, Chrome and Chromium, the + is set to 1, + to avoid prompting for unrelated PINs. Typically, this + effectively disables signature PINs and keys. + + + + + + + + By default, the OpenSC PKCS#11 module will not lock + your card once you authenticate to the card via + C_Login (Default: + false). + + Thus the other users or other applications is not + prevented from connecting to the card and perform + crypto operations (which may be possible because + you have already authenticated with the card). This + setting is not very secure. + + + Also, if your card is not locked, you can enconter + problems due to limitation of the OpenSC framework, + that still is not thoroughly tested in the multi + threads environment. + + + Your settings will be more secure if you choose to + lock your card. Nevertheless this behavior is a + known violation of PKCS#11 specification. Now once + one application has started using your card with + C_Login, no other application + can use it, until the first is done and calls + C_Logout or + C_Finalize. In the case of many + PKCS#11 application this does not happen until you + exit the application. + + + Thus it is impossible to use several smart card + aware applications at the same time, e.g. you + cannot run both Firefox + and Thunderbird at the + same time, if both are configured to use your smart + card. + + + + + + + + By default, interacting with the OpenSC PKCS#11 + module may change the state of the token, e.g. + whether a user is logged in or not (Default: + false). + + + Thus other users or other applications may change + or use the state of the token unknowingly. Other + applications may create signatures abusing an + existing login or they may logout unnoticed. + + + With this setting enabled the login state of the + token is tracked and cached (including the PIN). + Every transaction is preceded by restoring the + login state. After every transaction a logout is + performed. This setting by default also enables + to disable access for + other applications during the atomic transactions. + + + Please note that any PIN-pad should be disabled + (see ), because the + user would have to input his PIN for every + transaction. + + + + + + + + With this setting disabled, the OpenSC PKCS#11 + module will initialize the slots available when the + application calls C_GetSlotList. + With this setting enabled, the slots will also get + initialized when C_GetSlotInfo + is called (Default: true). + + + This setting is a workaround for + Java which does not call + C_GetSlotList when configured + with a static slot instead of + slotListIndex. + + + + + + + + User PIN unblock style mode + is one of: + + + none (Default): PIN + unblock is not possible with PKCS#11 API + + + set_pin_in_unlogged_session: + C_SetPIN in unlogged + session: PUK is passed as the + OldPin argument of the + C_SetPIN call. + + + set_pin_in_specific_context: + C_SetPIN in the + CKU_SPECIFIC_CONTEXT + logged session: PUK is passed as the + OldPin argument of the + C_SetPIN call. + + + init_pin_in_so_session: + C_InitPIN in + CKU_SO logged session: + User PIN 'UNBLOCK' is protected by SOPIN. + (PUK == SOPIN). + + + + + + + + + + Create slot for unblocking PIN with PUK (Default: + false). This way PKCS#11 API can + be used to login with PUK and change a PIN. May + cause problems with some applications like + Firefox and + Thunderbird. + + + + + + + + Symbolic names of PINs for which slots are created + where mode is a list of: + + + all (Default): All + non-SO-PIN, non-unblocking PINs + + + user: The first + global or first local PIN + + + sign: The second PIN + (first local, second global or second + local) + + + + + Card can contain more then one PINs or more then + one on-card application with its own PINs. + Normally, to access all of them with the PKCS#11 + API a slot has to be created for all of them. Many + slots could be annoying for some of widely used + application, like FireFox. This configuration + parameter allows to select the PIN(s) for which + PKCS#11 slot will be created. + + + Only PINs initialised, non-SO-PIN, non-unblocking + are associated with symbolic name. + + + For the module to simulate the opensc-onepin module + behavior the following option + + + + + + + + + + Environment + + + + OPENSC_CONF + + + Filename for a user defined configuration file + + + If this environment variable is not found on + Windows, the registry key + Software\OpenSC + Project\OpenSC\ConfigFile is + checked. + + + + + OPENSC_DEBUG + + + See + + + + + OPENSC_DRIVER + + + See + + + + + OPENSC_DEEPBIND + + + The OpenSC and its tools are dynamically loading various + libraries (pcsclite, dynamic drivers and other pkcs11 + modules) using + + dlopen + 3 + . + + + When this environment variable is set to + 1 + for the process running OpenSC, the calls will include + RTLD_DEEPBIND flag, which should + cause better symbol isolation between OpenSC and + loaded libraries. On the other hand, this is incompatible + with various static analysis tools as well as some + browser builds. + + + This option may be useful, if OpenSC needs to load a library + that pollutes the processes' global namespace and thereby + causes the unintended use of a different function with the + same name. In particular, this may happen when + pkcs11-spy + is used for logging between application and PKCS#11 library. + + + + + CARDMOD_LOW_LEVEL_DEBUG + + + Write minidriver debug information to + C:\tmp\md.log, if set to + 1. + + + If this environment variable is not found on + Windows, the registry key + Software\OpenSC + Project\OpenSC\MiniDriverDebug is + checked. + + + + + PIV_EXT_AUTH_KEY, + PIV_9A_KEY, + PIV_9C_KEY, + PIV_9D_KEY, + PIV_9E_KEY + + + PIV configuration during initialization with + piv-tool. + + + + + PIV_USE_SM, + PIV_PAIRING_CODE + + + PIV configuration during initialization + See Configuration Options for PIV Card. + + + + + + + + Files + + + + @sysconfdir@/opensc.conf + + + System-wide configuration file + + + + + @docdir@/opensc.conf + + + Extended example configuration file + + + + + + diff --git a/doc/files/pkcs15-profile.5.xml.in b/doc/files/pkcs15-profile.5.xml.in new file mode 100644 index 0000000000..0681c92053 --- /dev/null +++ b/doc/files/pkcs15-profile.5.xml.in @@ -0,0 +1,813 @@ + + + + pkcs15-profile + 5 + OpenSC + OpenSC File Formats + opensc + + + + pkcs15-profile + format of profile for pkcs15-init + + + + Description + + The pkcs15-init utility for PKCS #15 smart card + personalization is controlled via profiles. When starting, it will read two + such profiles at the moment, a generic application profile, and a card + specific profile. The generic profile must be specified on the command line, + while the card-specific file is selected based on the type of card detected. + + + The generic application profile defines general information about the card + layout, such as the path of the application DF, various PKCS #15 files within + that directory, and the access conditions on these files. It also defines + general information about PIN, key and certificate objects. Currently, there + is only one such generic profile, pkcs15.profile. + + + The card specific profile contains additional information required during + card initialization, such as location of PIN files, key references etc. + Profiles currently reside in @pkgdatadir@ + + + Basic PKCS#15 terminology: + + + MF (Master File) is root of the filesystem hierarchy + + + DF(PKCS#15) is directory containing the PKCS#15 files and directories + + + EF(ODF) (Object Directory File) is elementary file containing pointers + to other elementary files (PrKDFs, PuKDFs, SKDFs, CDFs, DODFs, AODFs) + + + PrKDF (Private Key Directory File) is elementary file containing + pointers to the private keys and additional information about the private keys + + + PubKDF (Public Key Directory File) is elementary file containing pointers to the public + keys and additional information about the public keys + + + CDF (Certificate Directory File) is elementary file containing pointers to the + certificates and additional information about the certificates + + + EF(TokenInfo) is elementary file with generic information about the card + + + + + + + Syntax and semantics + + The block syntax of profile files is in general similar to the configuration file. + The profile file, is composed of blocks, which, in general, have the following format: + +key, name { + block_contents +} + + block_contents is one or more + block_items where a + block_item is one of + + + # comment string + + + key, name = value; + + + block + + + + + At the root level, the profile contains several configuration blocks. + The block keys are as follows: + + + cardinfo: Configuration for general information about card. + + + pkcs15: Control for some of the general aspects of the PKCS#15 put onto the card. + + + option: Profile options to modify the behavior of profile. + + + PIN: Configuration and limits for particular PIN type. + + + filesystem: Specification for filesystem that is to be created on the card. + + + macros + + + + + Profile file configuration + + + Configuration of Card Information + + + + + + + Configuration for general information about card: + + + + + + + + Card label (Default: OpenSC Card). + + + + + + + + Card manufacturer (Default: OpenSC Project). + + + + + + + + Minimal length of PIN (Default: 4). + + + + + + + + Maximal length of PIN, should be overridden in the per-card profile + (Default: 8). + + + + + + + + Encoding type of PIN. Known parameters: + + + BCD: + binary-coded decimal + + + ascii-numeric: + ASCII numerical values + + + utf8 + + + half-nibble-bcd + + + iso9564-1 + + + (Default: ascii-numeric). + + + + + + + + Character used for padding the PIN when needed (Default: 0x00). + + + + + + + + Some cards need to keep all their PINs in separate directories. + The particular keys in that domain will be put below the DF of the specified PIN. + (Default: no) + + + + + + + + + + Configuration of PKCS#15 + + + + + + + Control for some of the general aspects of the PKCS#15 put onto the card. + Parameters in this block are: + + + + + + + + The PKCS#15 system must contain at least one CDF, it contains the certificates + directly or references to certificates. This options defines whether the certificates + should be put directly in the CDF itself or not (Default: no). + + + + + + + + Save length of DF into ODF file. Useful if we store certificates directly in the CDF + for better better performance and robustness (Default: no). + + + + + + + + Store information about last update in the EF(TokenInfo) (Default: yes). + + + + + + + + Method to calculate ID of the crypto objects. Known parameters: + + + native: + 'E' + number_of_present_objects_of_the_same_type + + + mozilla: + SHA1(modulus) for RSA + + + rfc2459 + SHA1(SequenceASN1 of public key components as ASN1 integers) + + (Default: native) + + + + + + + + + Style of pkcs15-init support of minidriver. Known parameters: + + + none + + + gemalto + + + (Default: none) + + + + + + + + + + Configuration of Profile Option + + + + + + The name specifies profile options to modify the behavior of profile, it can be + + + + + default: + option specifies default settings and this block with option is always processed, + + + onepin: + option for using 1 user PIN, creation/deletion/generation is controlled by the user PIN and thus by the user (as a result, only 1 user PIN is possible), + + + small + option suitable for cards with small memory. + + + + + The options are used by pkcs15-init tool by --profile name, -p name: + + + + + pkcs15+default: + the default (not needed to specify it) + + + pkcs15+onepin: + for the onepin profile option + + + pkcs15+small + for the small profile option + + + + + The option block can contain following sub-blocks: + + + + + + + + + + Macros are specified in form of name = value; pairs. + + + + + + + + Inner block for configuration of PKCS#15 structure. + + + + + + + + + + Configuration of PINs + + + + + + The name specifies PIN type, it can be + + + + + pin or user-pin + (no need to set file path or reference as it is done dynamically) + + + puk or user-puk + + + sopin or so-pin + + + sopuk or so-puk + + + + + + Known parameters are: + + + + + + + + Defines number of attempts for the given PIN (Default: 3). + + + + + + + + Flags define properties of the PIN. Possible flags: + + + case-sensitive + + + local + + + change-disabled + + + unblock-disabled + + + initialized + + + needs-padding + + + unblockingPin + + + soPin + + + disable-allowed + + + integrity-protected + + + confidentiality-protected + + + exchangeRefData + + + (Default: local,initialized,needs-padding). + + + + + + + + Value used for auth ID (Default: 0). + + + + + + + + Minimal length of PIN (Default: value min-pin-length set in cardinfo block). + + + + + + + + Maximal length of PIN (Default: value max-pin-length set in cardinfo block). + + + + + + + + Value of reference of the PIN (Default: set in particular card driver). + + + + + + + + File with PIN, obsolete option (Default: None). + + + + + + + + Offset of PIN in PIN file, obsolete option (Default: 0). + + + + + + + + Encoding type of PIN. Possible values: + + + BCD + + + ascii-numeric + + + utf8 + + + half-nibble-bcd + + + iso9564-1 + + + (Default: value pin-encoding set in cardinfo block). + + + + + + + + (Default: value max-pin-length set in cardinfo block). + + + + + + + + (Default: 0). + + + + + + + + Values in this block can be set by macros. That allows to specify the particular values with the usage of option. + + + + + Configuration of Filesystem + + + This block contains the specification for filesystem that is to be created on the card. + The filesystem consists of several nested blocks representing DF and EF files. + When the DFs or EFs are specified in card specific profile, this is added to the file system info specified in the main profile. + + + + + + + This block defines elementary file in PKCS#15 file hierarchy. + The name can be one of: + + + PKCS15-TokenInfo + + + PKCS15-ODF + + + PKCS15-UnusedSpace + + + PKCS15-PRKDF + + + PKCS15-PUKDF + + + PKCS15-PUKDF-TRUSTED + + + PKCS15-SKDF + + + PKCS15-CDF + + + PKCS15-CDF-TRUSTED + + + PKCS15-CDF-USEFUL + + + PKCS15-DODF + + + PKCS15-AODF + + + + + The EF block can contain: + + + + + + + + + + Type must match type of file. + + + + + + + + Value of ACL (Access Control List) (Default: NONE) + + + + + + + + File ID, relative path. + + + + + + + + File structure is one of: + + + TRANSPARENT + + + LINEAR-FIXED + + + LINEAR-FIXED-TLV + + + LINEAR-VARIABLE + + + LINEAR-VARIABLE-TLV + + + CYCLIC + + + CYCLIC-TLV + + + + + + + + + + + + This block defines directory file in PKCS#15 file hierarchy. + The name can be one of: + + + MF + + + PKCS15-AppDF + + + Special cases for those DFs handled separately by the PKCS15 logic + + + + + The DF block can contain: + + + + + + + + + + Type must match type of file. + + + + + + + + Specification of path of the directory file. + + + + + + + + File ID, relative path. + + + + + + + + Value of AID, in XX:XX:XX:...:XX:XX:XX notation. + + + + + + + + Type must match type of file. + + + + + + + + Size of the file in bytes. + + + + + + + + Block specifying nested elementary file. + + + + + Typically, the root DF is MF. + + + It is mandatory that profile file contains DF entry for MF (Master File). + Otherwise the profile file is incomplete and cannot be used. + + + + + + The DF can contain other DF or MF blocks. + For examples how the filesystem structure may look like, + please refer to pkcs15.profile or any other present profile file. + + + + + + + + See also + + + pkcs15-init + 1 + , + + pkcs15-crypt + 1 + + + + + diff --git a/doc/html.xsl b/doc/html.xsl index a9a95ee251..665d45f46a 100644 --- a/doc/html.xsl +++ b/doc/html.xsl @@ -3,8 +3,9 @@ ]> - + +

OpenSC Manual Pages: Section 1


Table of Contents

cardos-tool — displays information about Card OS-based security tokens or format them +
cryptoflex-tool — utility for manipulating Schlumberger Cryptoflex data structures
dnie-tool — displays information about DNIe based security tokens
egk-tool — displays information on the German electronic health card (elektronische Gesundheitskarte, eGK) +
eidenv — utility for accessing visible data from + electronic identity cards
gids-tool — smart card utility for GIDS cards
iasecc-tool — displays information about IAS/ECC card +
lteid-tool — Lithuanian National eID card (asmens tapatybės kortelė) utility
netkey-tool — administrative utility for Netkey E4 cards
npa-tool — displays information of an ID card or ePassport. +
openpgp-tool — utility for accessing visible data OpenPGP smart cards + and compatible tokens
opensc-asn1 — parse ASN.1 data +
opensc-explorer — + generic interactive utility for accessing smart card + and similar security token functions +
opensc-notify — monitor smart card events and send notifications +
opensc-tool — generic smart card utility
piv-tool — smart card utility for HSPD-12 PIV cards
pkcs11-tool — utility for managing and using PKCS #11 security tokens
pkcs15-crypt — perform crypto operations using PKCS#15 smart cards
pkcs15-init — smart card personalization utility
pkcs15-tool — utility for manipulating PKCS #15 data structures + on smart cards and similar security tokens
sc-hsm-tool — smart card utility for SmartCard-HSM
westcos-tool — utility for manipulating data structures + on westcos smart cards

Name

cardos-tool — displays information about Card OS-based security tokens or format them +

Synopsis

cardos-tool [OPTIONS]

Description

+ The cardos-tool utility is used to display information about +smart cards and similar security tokens based on Siemens Card/OS M4. +

Options

+

+ --format, + -f +

Format the card or token.

+ --help, + -h +

Print help message on screen.

+ --info, + -i +

Display information about the card or token.

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --startkey arg, + -s arg +

Specify startkey for format.

+ --change-startkey arg, + -S arg +

Change Startkey with given APDU command.

+ --verbose, + -v +

Causes cardos-tool to be more verbose. + Specify this flag several times to enable debug output in the opensc library.

+ --wait, + -w +

Causes cardos-tool to wait for the token + to be inserted into reader.

+

Authors

cardos-tool was written by + Andreas Jellinghaus .


Name

cryptoflex-tool — utility for manipulating Schlumberger Cryptoflex data structures

Synopsis

cryptoflex-tool [OPTIONS]

Description

+ cryptoflex-tool is used to manipulate PKCS + data structures on Schlumberger Cryptoflex smart cards. Users + can create, list and read PINs and keys stored on the smart card. + User PIN authentication is performed for those operations that require it. +

Options

+

+ --app-df num, + -a num +

Specifies the DF to operate in

+ --create-key-files arg, + -c arg +

Creates new RSA key files for arg keys

+ --create-pin-files id, + -P id +

Creates new PIN file for CHVid

+ --exponent exp, + -e exp +

Specifies the RSA exponent, exp, + to use in key generation. The default value is 3.

+ --generate-key, + -g +

Generate a new RSA key pair

+ --key-num num, + -k num +

Specifies the key number to operate on. The default is + key number 1.

+ --list-keys, + -l +

Lists all keys stored in a public key file

+ --modulus-length length, + -m length +

Specifies the modulus length to use + in key generation. The default value is 1024.

+ --prkey-file id, + -p id +

Specifies the private key file id, id, + to use

+ --pubkey-file id, + -u id +

Specifies the public key file id, id, + to use

+ --read-key, + -R +

Reads a public key from the card, allowing the user to + extract and store or use the public key +

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --verbose, + -v +

Causes cryptoflex-tool to be more + verbose. Specify this flag several times to enable debug output in + the opensc library.

+ --verify-pin, + -V +

Verifies CHV1 before issuing commands

+ --wait, + -w +

Causes cryptoflex-tool to + wait for a card insertion.

+

See also

+ pkcs15-tool(1) +

Authors

cryptoflex-tool was written by + Juha Yrjölä .


Name

dnie-tool — displays information about DNIe based security tokens

Synopsis

dnie-tool [OPTIONS]

Description

+ The dnie-tool utility is used to display additional information about DNIe, the Spanish National eID card. +

Options

+

+ --idesp, + -i +

Show the DNIe IDESP value.

+ --data, + -d +

Show DNIe personal information. + Reads and print DNIe number and User Name and SurName

+ --all, + -a +

Displays every available information. + This command is equivalent to -d -i -V -s

+ --serial, + -s +

Displays DNIe Serial Number +

+ --version, + -V +

Show DNIe sw version. + Displays software version for in-card DNIe OS

+ --pin pin, + -p pin +

+ These options can be used to specify the PIN value + on the command line. If the value is set to + env:VARIABLE, the value + of the specified environment variable is used. By default, + the code is prompted on the command line if needed. +

+ Note that on most operation systems, any user can + display the command line of any process on the + system using utilities such as + ps(1). Therefore, you should prefer + passing the codes via an environment variable + on an unsecured system. +

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --wait, + -w +

Causes dnie-tool to wait for the token to be inserted into reader.

+ --verbose, + -v +

Causes dnie-tool to be more verbose. + Specify this flag several times +to enable debug output in the opensc library.

+

Authors

dnie-tool was written by + Juan Antonio Martinez .


Name

egk-tool — displays information on the German electronic health card (elektronische Gesundheitskarte, eGK) +

Synopsis

egk-tool [OPTIONS]

Description

+ The egk-tool utility is used to display information stored on the German elektronic health card (elektronische Gesundheitskarte, eGK). +

Options

+

+ --help, + -h

Print help and exit.

+ --version, + -V

Print version and exit.

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --verbose, + -v +

+ Causes egk-tool to be more verbose. + Specify this flag several times to be more verbose. +

+

'Gesundheitsanwendung', Health Care Application (HCA)

--pd

+ Show 'Persönliche Versichertendaten' (XML). +

--vd

+ Show 'Allgemeine Versichertendaten' (XML). +

--gvd

+ Show 'Geschützte Versichertendaten' (XML). +

--vsd-status

+ Show 'Versichertenstammdaten-Status'. +

Authors

egk-tool was written by + Frank Morgner .


Name

eidenv — utility for accessing visible data from + electronic identity cards

Synopsis

eidenv [OPTIONS]

Description

+ The eidenv utility is used for + accessing data from electronic identity cards (like + national eID cards) which might not be present in + PKCS#15 objects but available in custom files on the + card. The data can be printed on screen or used by + other programs via environment variables. +

Options

+

+ --exec prog, + -x prog +

Executes the given program with + data in environment variables.

+ --help, + -h +

Print help message on screen.

+ --print, + -p +

Prints all data + fields from the card, like validity + period, document number etc.

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --stats, + -t +

Prints key usage statistics + (only for Estonian ID card).

+ --version, + -v +

Prints the version + of the utility and exits.

+ --wait, + -w +

Wait for a card to be inserted

+

Authors

eidenv utility was written by + Stef Hoeben and Martin Paljak .


Name

gids-tool — smart card utility for GIDS cards

Synopsis

gids-tool [OPTIONS]

+ The gids-tool utility can be used from the command line to perform + miscellaneous smart card operations on a GIDS smart card. +

Options

+

+ -X, + --initialize +

Initialize token.

+ --admin-key argument +

Define the administrator key

+ --pin pin +

+ This option can be used to specify the PIN value + on the command line. If the value is set to + env:VARIABLE, the value + of the specified environment variable is used. By default, + the code is prompted on the command line if needed. +

+ Note that on most operation systems, any user can + display the command line of any process on the + system using utilities such as + ps(1). Therefore, you should prefer + passing the codes via an environment variable + on an unsecured system. +

+ --serial-number argument +

Define serial number.

+ -U, + --unblock +

Unblock the user PIN after an administrator + authentication.

+ -C, + --change-admin-key +

Change the administrator key.

+ --new-admin-key argument +

Define the new administrator key.

+ --reader argument, + -r argument +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + argument is an ATR, the + reader with a matching card will be chosen. +

+ -w, + --wait +

Wait for a card to be inserted.

+ -v, + --verbose +

Verbose operation. Use several times to + enable debug output.

+

See also

+ opensc-tool(1) +

Authors

gids-tool was written by + Vincent Le Toux .


Name

iasecc-tool — displays information about IAS/ECC card +

Synopsis

iasecc-tool [OPTIONS]

Description

+ The iasecc-tool utility is used to display information about IAS/ECC v1.0.1 smart cards. +

Options

+

+ --reader arg, +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --list-applications, +

Get list of the on-card applications.

+ --aid hex-aid, +

Select hex-aid before processing.

+ --list-sdos sdo-type, +

List SDOs of the given sdo-type, + present in default or selected application.

+ --verbose, + -v +

Causes cardos-tool to be more verbose. + Specify this flag several times to enable debug output in the opensc library.

+ --wait, + -w +

Causes iasecc-tool to wait for the token + to be inserted into reader.

+

Authors

iasecc-tool was written by + Viktor Tarasov .


Name

lteid-tool — Lithuanian National eID card (asmens tapatybės kortelė) utility

Synopsis

lteid-tool [OPTIONS]

Description

+ The lteid-tool utility is used to set up Lithuanian National eID card access; + unblock it after incorrect PIN entry attempts. +

+ When invoked without any options, lteid-tool will try to determine if card is + ready for use or card access number (CAN) needs to be entered first. Card access number is a 6 + digit number printed on the front side bottom right corner of the card. Type it in and it will + be persisted for subsequent uses of the card. +

Options

+

+ --help, + -h +

Display tool options.

+ --verbose, + -v +

Causes lteid-tool to be more verbose. + Specify this flag several times to enable debug output.

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --wait, + -w +

Causes lteid-tool to wait for the token + to be inserted into reader.

+ --can can + -c can +

+ This option can be used to specify the CAN value + on the command line. If the value is set to + env:VARIABLE, the value + of the specified environment variable is used. By default, + the code is prompted on the command line if needed. +

+ --pin pin + -p pin +

+ This option can be used to specify the PIN value + on the command line. If the value is set to + env:VARIABLE, the value + of the specified environment variable is used. By default, + the code is prompted on the command line if needed. +

+ --puk puk + -u puk +

+ This option can be used to specify the PUK value + on the command line. If the value is set to + env:VARIABLE, the value + of the specified environment variable is used. By default, + the code is prompted on the command line if needed. +

+ --change-pin + -C +

+ Change PIN. +

+ --resume + -R +

+ Resume eID PIN use after 2 incorrect attempts using current PIN. +

+ --unblock + -U +

+ Unblock eID PIN use after 3 incorrect attempts using PUK. +

+ --verify-can + -V +

+ Verify Card Access Number (CAN). Use with + --can=XXXXXX option to + non-interactively verify and store CAN. +

+

See also

+ opensc.conf(5) +

Authors

lteid-tool was written by + Tadas Sasnauskas .


Name

netkey-tool — administrative utility for Netkey E4 cards

Synopsis

netkey-tool [OPTIONS] [COMMAND]

Description

The netkey-tool utility can be used from the + command line to perform some smart card operations with NetKey E4 cards + that cannot be done easily with other OpenSC-tools, such as changing local + PINs, storing certificates into empty NetKey E4 cert-files or displaying + the initial PUK-value.

Options

+

+ --help, + -h +

Displays a short help message.

+ --pin pin, + -p pin +

Specifies the current value of the global PIN.

+ --puk pin, + -u pin +

Specifies the current value of the global PUK.

+ --pin0 pin, + -0 pin +

Specifies the current value of the local PIN0 (aka local PIN).

+ --pin1 pin, + -1 pin +

Specifies the current value of the local PIN1 (aka local PUK).

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ -v +

Causes netkey-tool to be more verbose. This + options may be specified multiple times to increase verbosity.

+

PIN format

With the -p, -u, -0 or the -1 + one of the cards pins may be specified. You may use plain ascii-strings (i.e. 123456) or a hex-string + (i.e. 31:32:33:34:35:36). A hex-string must consist of exactly n 2-digit hexnumbers separated by n-1 colons. + Otherwise it will be interpreted as an ascii string. For example :12:34: and 1:2:3:4 are both pins of + length 7, while 12:34 and 01:02:03:04 are pins of length 2 and 4.

Commands

When used without any options or commands, netkey-tool will + display information about the smart cards pins and certificates. This will not change + your card in any aspect (assumed there are no bugs in netkey-tool). + In particular the tries-left counters of the pins are investigated without doing + actual pin-verifications.

If you specify the global PIN via the --pin option, + netkey-tool will also display the initial value of the cards + global PUK. If your global PUK was changed netkey-tool will still + display its initial value. There's no way to recover a lost global PUK once it was changed. + There's also no way to display the initial value of your global PUK without knowing the + current value of your global PIN.

For most of the commands that netkey-tool can execute, you have + to specify one pin. One notable exception is the nullpin command, but + this command can only be executed once in the lifetime of a NetKey E4 card.

+

+ cert number filename +

This command will read one of your cards certificates (as specified by + number) and save this certificate into file filename + in PEM-format. Certificates on a NetKey E4 card are readable without a pin, so you don't + have to specify one.

+ cert filename number +

This command will read the first PEM-encoded certificate from file + filename and store this into your smart cards certificate file + number. Some of your smart cards certificate files might be readonly, so + this will not work with all values of number. If a certificate file is + writable you must specify a pin in order to change it. If you try to use this command + without specifying a pin, netkey-tool will tell you which one is + needed.

+ change + { pin | puk | pin0 | pin1 } + new-pin +

This changes the value of the specified pin to the given new value. + You must specify either the current value of the pin or another pin to be able to do + this and if you don't specify a correct one, netkey-tool will tell + you which one is needed.

+ nullpin initial-pin +

This command can be executed only if the global PIN of your card is + in nullpin-state. There's no way to return back to nullpin-state once you have changed + your global PIN. You don't need a pin to execute the nullpin-command. After a successful + nullpin-command netkey-tool will display your cards initial + PUK-value.

+ unblock + { pin | pin0 | pin1 } +

This unblocks the specified pin. You must specify another pin + to be able to do this and if you don't specify a correct one, + netkey-tool will tell you which one is needed.

+

See also

+ opensc-explorer(1) +

Authors

netkey-tool was written by + Peter Koch .


Name

npa-tool — displays information of an ID card or ePassport. +

Synopsis

npa-tool [OPTIONS]

Description

+ The npa-tool utility is used to display information + stored on an ID card or on a passport and to perform some write and + verification operations. +

+ Extended Access Control version 2 is performed according to ICAO Doc + 9303 or BSI TR-03110 so that an ICAO compliant machine readable + travel document (MRTD) as well as EAC compliant ID + cards, for example the German ID card (neuer Personalausweis, + nPA), may be read. +

Options

+

+ --help, + -h

Print help and exit.

+ --version, + -V

Print version and exit.

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --verbose, + -v +

+ Causes npa-tool to be more verbose. + Specify this flag several times to be more verbose. +

+

Password Authenticated Connection Establishment (PACE)

+ --pin [STRING], + -p [STRING] +

+ Run PACE with (transport) eID-PIN. +

+ --puk [STRING], + -u [STRING] +

+ Run PACE with PUK. +

+ --can [STRING], + -c [STRING] +

+ Run PACE with Card Access Number (CAN). +

+ --mrz [STRING], + -m [STRING] +

+ Run PACE with Machine Readable Zone (MRZ). + Enter the MRZ without newlines. +

--env

+ Specify whether to use environment variables PIN, + PUK, CAN, MRZ, + and NEWPIN. + You may want to clean your environment before enabling this. + (default=off) +

PIN management

+ --new-pin [STRING], + -N [STRING] +

+ Install a new PIN. +

+ --resume, + -R +

+ Resume eID-PIN (uses CAN to activate last retry). + (default=off) +

+ --unblock, + -U +

+ Unblock PIN (uses PUK to activate three more retries). + (default=off) +

Terminal Authentication (TA) and Chip Authentication (CA)

+ --cv-certificate FILENAME, + -C FILENAME +

+ Specify Card Verifiable (CV) certificate + to create a certificate chain. + The option can be given multiple times, in which case the + order is important. +

--cert-desc HEX_STRING

+ Certificate description to show for Terminal Authentication. +

--chat HEX_STRING

+ Specify the Card Holder Authorization Template + (CHAT) to use. + If not given, it defaults to the terminal's CHAT. + Use 7F4C0E060904007F000703010203530103 + to trigger EAC on the CAT-C (Komfortleser). +

+ --auxiliary-data HEX_STRING, + -A HEX_STRING +

+ Specify the terminal's auxiliary data. + If not given, the default is determined by verification + of validity, age and community ID. +

+ --private-key FILENAME, + -P FILENAME +

+ Specify the terminal's private key. +

--cvc-dir DIRECTORY

+ Specify where to look for the certificate of the + Country Verifying Certification Authority + (CVCA). + If not given, it defaults to + /home/fm/.local/etc/eac/cvc. +

+ --x509-dir DIRECTORY

+ Specify where to look for the X.509 certificate. + If not given, it defaults to + /home/fm/.local/etc/eac/x509. +

--disable-ta-checks

+ Disable checking the validity period of CV certificates. + (default=off) +

--disable-ca-checks

+ Disable passive authentication. (default=off) +

Card application

--application app

+ What application to select on the card, use eID for the + electronic identification application and eMRTD for the + ePassport application. (default=eID) +

Read and write data groups

--read-all-dgs

Read all available data groups.

--read-dg1

Read data group 1.

--read-dg2

Read data group 2.

--read-dg3

Read data group 3.

--read-dg4

Read data group 4.

--read-dg5

Read data group 5.

--read-dg6

Read data group 6.

--read-dg7

Read data group 7.

--read-dg8

Read data group 8.

--read-dg9

Read data group 9.

--read-dg10

Read data group 10.

--read-dg11

Read data group 11.

--read-dg12

Read data group 12.

--read-dg13

Read data group 13.

--read-dg14

Read data group 14.

--read-dg15

Read data group 15.

--read-dg16

Read data group 16.

--read-dg17

Read data group 17.

--read-dg18

Read data group 18.

--read-dg19

Read data group 19.

--read-dg20

Read data group 20.

--read-dg21

Read data group 21.

+ --write-dg17 HEX_STRING

Write data group 17.

+ --write-dg18 HEX_STRING

Write data group 18.

+ --write-dg19 HEX_STRING

Write data group 19.

+ --write-dg20 HEX_STRING

Write data group 20.

--write-dg21 HEX_STRING

Write data group 21.

Verification of validity, age and community ID

--verify-validity YYYYMMDD

+ Verify chip's validity with a reference date. +

--older-than YYYYMMDD

+ Verify age with a reference date. +

--verify-community HEX_STRING

+ Verify community ID with a reference ID. +

Special options, not always useful

+ --break, + -b +

+ Brute force PIN, CAN or PUK. + Use together with options -p, + -a, or -u. + (default=off) +

+ --translate FILENAME, + -t FILENAME +

+ Specify the file with APDUs of HEX_STRINGs to send + through the secure channel. + (default=`stdin') +

--tr-03110v201

+ Force compliance to BSI TR-03110 version 2.01. (default=off) +

--disable-all-checks

+ Disable all checking of fly-by-data. (default=off) +

Authors

npa-tool was written by + Frank Morgner .


Name

openpgp-tool — utility for accessing visible data OpenPGP smart cards + and compatible tokens

Synopsis

openpgp-tool [OPTIONS]

Description

+ The openpgp-tool utility is used for + accessing data from the OpenPGP v1.1 and v2.0 smart cards + and compatible tokens like e.g. GPF CryptoStick v1.x, + which might not be present in + PKCS#15 objects but available in custom files on the + card. The data can be printed on screen or used by + other programs via environment variables. +

Options

+

+ --card-info, + -C +

+ Show card information. +

+ --del-key arg +

+ Delete key indicated by arg. + arg can be 1, + 2, 3, or + all. +

+ --do arg, + -d arg +

+ Dump private data object (DO) + indicated by arg. + arg can be in the form + x, + 10x, or + 010x + to access DO 010x, + where x is 1, + 2, 3, or + 4. +

+ --erase, + -E +

+ Erase (i.e. reset) the card. +

+ --exec prog, + -x prog +

+ Execute the given program with data in environment variables. +

+ --gen-key arg, + -G arg +

+ Generate key with the ID given as arg. + arg can be one of 1, + 2, or 3. +

+ --help, + -h +

+ Print help message on screen. +

+ --key-info, + -K +

+ Show information of keys on the card. +

+ --key-type keytype, + -t keytype +

+ Specify the type of the key to be generated. + Supported values for keytype are + rsa for RSA with 2048 bits, + rsaLENGTH + for RSA with a bit length of LENGTH. + + If not given, it defaults to rsa2048. +

+ --pin pin +

+ This option can be used to specify the PIN value + on the command line. If the value is set to + env:VARIABLE, the value + of the specified environment variable is used. By default, + the code is prompted on the command line if needed. +

+ Note that on most operation systems, any user can + display the command line of any process on the + system using utilities such as + ps(1). Therefore, you should prefer + passing the codes via an environment variable + on an unsecured system. +

+ --pretty +

+ Print values in pretty format. +

+ --raw +

+ Print values in raw format, as they are stored on the card. +

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --user-info, + -U +

+ Show card holder information. +

+ --verify pintype +

+ Verify PIN (CHV1, CHV2 or CHV3). +

+ --version, + -V +

+ Print the version of the utility and exit. +

+ --verbose, + -v +

+ Verbose operation. Use several times to enable debug output. +

+ --wait, + -w +

+ Wait for a card to be inserted. +

+

Authors

openpgp-tool utility was written by + Peter Marschall .


Name

opensc-asn1 — parse ASN.1 data +

Synopsis

opensc-asn1 [OPTIONS] [FILES]

Description

+ The opensc-asn1 utility is used to parse ASN.1 data. +

Options

+

+ --help, + -h

Print help and exit.

+ --version, + -V

Print version and exit.

+

Authors

opensc-asn1 was written by + Frank Morgner .


Name

opensc-explorer — + generic interactive utility for accessing smart card + and similar security token functions +

Synopsis

opensc-explorer [OPTIONS] [SCRIPT]

Description

+ The opensc-explorer utility can be + used to perform miscellaneous operations + such as exploring the contents of or sending arbitrary + APDU commands to a smart card or similar security token. +

+ If a SCRIPT is given, + opensc-explorer runs in non-interactive mode, + reading the commands from SCRIPT, + one command per line. + If no script is given, opensc-explorer + runs in interactive mode, reading commands from standard input. +

Options

+ The following are the command-line options for + opensc-explorer. There are additional + interactive commands available once it is running. +

+ --card-driver driver, + -c driver +

+ Use the given card driver. + The default is to auto-detect the correct card driver. + The literal value ? lists + all available card drivers and terminates + opensc-explorer. +

+ --mf path, + -m path +

+ Select the file referenced by the given path on startup. + The default is the path to the standard master file, + 3F00. If path + is empty (e.g. opensc-explorer --mf ""), + then no file is explicitly selected. +

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --verbose, -v +

+ Cause opensc-explorer to be more + verbose. Specify this flag several times to enable + debug output in the opensc library. +

+ --wait, -w +

+ Wait for a card to be inserted. +

+

Commands

+ opensc-explorer supports commands with arguments + at its interactive prompt or in script files passed via the command line + parameter SCRIPT. +

+ Similar to a command shell like e.g. bash, + each input line is split into white-space separated words. + Of these words, the first one is used as the command, + while the remaining ones are treated as arguments to that command. +

+ The following commands are supported: +

+ # + ... +

+ Treat line as a comment. + Ignore anything until the end of the line introduced by + #. +

+ apdu + data... +

+ Send a custom APDU command to the card. + data is a series of + sequences of hexadecimal values and strings enclosed + in double quotes ("..."). +

+ asn1 + file-id + [rec-no] + [offs] +

+ Parse and print the ASN.1 encoded content of the working EF + specified by file-id. + If the optional parameter + rec-no is given and the file is + a record-oriented EF, parse and print only the record + indicated by this parameter. + If the optional parameter + offs is given, start parsing + and printing the file or record at the offset indicated + by the value given. + If this parameter is not given, the default offset is + 0. +

+ cat + [ file-id | sfi:short-id ] + [rec-no] +

+ Print the contents of the working EF specified by + file-id or the short file id + short-id. + If the optional second parameter + rec-no is given, + only print the record indicated by this parameter. + If no argument is given, print the the contents + of the currently selected EF. +

+ cd + { .. | file-id | aid:DF-name } +

+ Change to another DF specified by the argument passed. + If the argument given is .., + then move up one level in the file system hierarchy. + If it is a file-id, + which must be a DF directly + beneath the current DF, then change to that DF. + If it is an application identifier given as + aid:DF-name, + then jump to the MF of the application denoted by + DF-name. +

+ change + CHVpin-ref + [ + [old-pin] + new-pin + ] +

+ Change the PIN specified by pin-ref + from the value given by old-pin and + change its value to new-pin. References + should be given in decimal notation. +

+ old-pin and + new-pin can be + sequences of hexadecimal values, + strings enclosed in double quotes ("..."), + empty (""), or absent. + If absent, the values are read from the card reader's pin pad. +

+ Examples: +

change CHV2 00:00:00:00:00:00 "foobar"

+ Change PIN CHV2 + to the new value foobar, + giving the old value 00:00:00:00:00:00. +

change CHV2 "foobar"

+ Set PIN CHV2 + to the new value foobar. +

change CHV2

+ Change PIN CHV2 using the card reader's pinpad. +

+

+ create + file-id + size +

+ Create a new EF. + file-id specifies the numeric id, and + size the size of the EF to create. +

+ debug + [level] +

+ Set OpenSC debug level to level. +

+ If level is omitted, + show the current debug level. +

+ delete + file-id +

+ Remove the EF or DF specified by + file-id. +

+ do_get + hex-tag + [output] +

+ Copy the contents of the card's data object + (DO) + specified by hex-tag + to the local host computer's file named + output. +

+ If output is not given, + the contents of hex-tag + will be displayed as hex-dump. +

+ do_put + hex-tag + data +

+ Change the contents of the card's data object + (DO) + specified by hex-tag + to data. +

+ data is either a + sequence of hexadecimal values or a string enclosed + in double quotes ("..."). +

+ echo + string... +

+ Print the strings given. +

+ erase +

+ Erase the card, if the card supports it. +

+ get + file-id + [output] +

+ Copy an EF to a local file. + The local file is specified by + output + while the card file is specified by + file-id. +

+ If output is omitted, + the name of the output file will be derived from the + full card path to file-id. +

+ get_record + file-id + rec-no + [output] +

+ Copy a record of a record-oriented EF to a local file. + The local file is specified by + output + while the card file and the record are specified by + file-id and + rec-no, +

+ If output is omitted, + the name of the output file will be derived from the + full card path to file-id. + and the rec-no. +

+ help + [pattern] +

+ Display the list of available commands, their options + and parameters together with a short help text. + If pattern is given, + the commands shown are limited to those matching + pattern. +

+ info + [file-id] +

+ Display attributes of a file specified by + file-id. + If file-id is not supplied, + the attributes of the current file are displayed. +

+ ls + [pattern...] +

+ List files in the current DF. + If no pattern is given, + then all files are listed. + If one or more patterns are given, + only files matching at least one + pattern are listed. +

+ find + [ + start-id + [end-id] + ] +

+ Find all files in the current DF. + Files are found by selecting all file identifiers in the range + from start-fid + to end-fid. +

+ If not given, the default value for + start-fid is 0000, + while the default for end-fid is + FFFF. +

+ find_tags + [ + start-tag + [end-tag] + ] +

+ Find all tags of data objects in the current context. + Tags are found by using GET DATA in the range from + from start-tag + to end-tag. +

+ If not given, the default value for + start-tag is 0000, + while the default for end-tag is + FFFF. +

+ mkdir + file-id + size +

+ Create a DF. + file-id specifies the numeric id, + and size the size of the DF to create. +

+ pin_info + key-typekey-id +

+ Get information on a PIN or key from the card, where + key-type can be one of + CHV, KEY, + AUT or PRO. + key-id is a number + representing the key or PIN reference (decimal notation). +

+ put + file-id + input +

+ Copy a local file to the card. + The local file is specified by input + while the card file is specified by + file-id. +

+ quit +

Exit the program.

+ random + count + [output-file] +

+ Generate count bytes + of random data. + If output-file is given, + write the data to the host computer's file denoted + by it, otherwise show the data as hex dump. +

+ rm + file-id +

+ Remove the EF or DF specified by + file-id. +

+ unblock + CHVpin-ref + [ + puk + [new-pin] + ] +

+ Unblock the PIN denoted by pin-ref + (decimal notation) + using the PUK puk, and potentially + change its value to new-pin. +

+ puk and + new-pin can be + sequences of hexadecimal values, + strings enclosed in double quotes ("..."), + empty (""), or absent. + If absent, the values are read from the card reader's pin pad. +

+ Examples: +

unblock CHV2 00:00:00:00:00:00 "foobar"

+ Unblock PIN CHV2 using PUK + 00:00:00:00:00:00 + and set it to the new value foobar. +

unblock CHV2 00:00:00:00:00:00 ""

+ Unblock PIN CHV2 using PUK + 00:00:00:00:00:00 keeping the old value. +

unblock CHV2 "" "foobar"

+ Set new value of PIN CHV2 + to foobar. +

unblock CHV2 00:00:00:00:00:00

+ Unblock PIN CHV2 using PUK + 00:00:00:00:00:00. + The new PIN value is prompted by pinpad. +

unblock CHV2 ""

+ Set PIN CHV2. + The new PIN value is prompted by pinpad. +

unblock CHV2

+ Unblock PIN CHV2. + The unblock code and new PIN value are prompted by pinpad. +

+

+ update_binary + file-id + offs + data +

+ Binary update of the file specified by + file-id with the literal data + data starting from offset specified + by offs. +

+ data can be supplied as a sequence + of hexadecimal values or as a string enclosed in double quotes + ("..."). +

+ update_record + file-id + rec-nr + rec-offs + data +

+ Update record specified by rec-nr + of the file specified by file-id + with the literal data data + starting from offset specified by + rec-offs. +

+ data can be supplied as a sequence + of hexadecimal values or as a string enclosed in double quotes + ("..."). +

+ verify + key-typekey-id + [key] +

+ Present a PIN or key to the card, where + key-type can be one of + CHV, KEY, + AUT or PRO. + key-id is a number representing + the key or PIN reference (decimal notation). + key is the key or PIN to be verified, + formatted as a colon-separated sequence of hexadecimal values + or a string enclosed in double quotes ("..."). +

+ If key is omitted, the exact action + depends on the card reader's features: + if the card readers supports PIN input via a pin pad, + then the PIN will be verified using the card reader's pin pad. + If the card reader does not support PIN input, + then the PIN will be asked interactively. +

+ Examples: +

verify CHV2 31:32:33:34:00:00:00:00

+ Verify CHV2 using the hex value + 31:32:33:34:00:00:00:00 +

verify CHV1 "secret"

+ Verify CHV1 + using the string value secret. +

verify KEY2

+ Verify KEY2, + get the value from the card reader's pin pad. +

+

+ sm + { open | close } +

+ Call the card's open or + close Secure Messaging handler. +

+

See also

+ opensc-tool(1) +

Authors

opensc-explorer was written by + Juha Yrjölä .


Name

opensc-notify — monitor smart card events and send notifications +

Synopsis

opensc-notify [OPTIONS]

Description

+ The opensc-notify utility is used to + monitor smart card events and send the appropriate notification. +

Options

+

+ --help, + -h

Print help and exit.

+ --version, + -V

Print version and exit.

+

Mode: customized

+ Send customized notifications. +

+ --title [STRING], + -t [STRING] +

+ Specify the title of the notification. +

+ --message [STRING], + -m [STRING] +

+ Specify the main text of the notification. +

Mode: standard

+ Manually send standard notifications. +

+ --notify-card-inserted, + -I

+ See notify_card_inserted + in opensc.conf (default=off). +

+ --notify-card-removed, + -R

+ See notify_card_removed + in opensc.conf (default=off). +

+ --notify-pin-good, + -G

+ See notify_pin_good + in opensc.conf (default=off). +

+ --notify-pin-bad, + -B

+ See notify_pin_bad + in opensc.conf (default=off). +

Authors

opensc-notify was written by + Frank Morgner .


Name

opensc-tool — generic smart card utility

Synopsis

opensc-tool [OPTIONS]

Description

+ The opensc-tool utility can be used from the command line to perform + miscellaneous smart card operations such as getting the card ATR or + sending arbitrary APDU commands to a card. +

Options

+

+ --version +

Print the OpenSC package release version.

+ --atr, + -a +

Print the Answer To Reset (ATR) of the card. + Output is in hex byte format

+ --card-driver driver, + -c driver +

+ Use the given card driver. + The default is to auto-detect the correct card driver. + The literal value ? lists + all available card drivers. +

+ --list-algorithms, +

Lists algorithms supported by card

+ --info, + -i +

Print information about OpenSC, such as version and enabled components.

+ --list-drivers, + -D +

List all installed card drivers.

+ --list-files, + -f +

Recursively list all files stored on card.

+ --list-readers, + -l +

List all configured readers.

+ --name, + -n +

Print the name of the inserted card (driver).

+ --get-conf-entry conf, + -G conf +

Get configuration key, format: section:name:key

+ --set-conf-entry conf, + -S conf +

Set configuration key, format: section:name:key:value

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --reset [type], +

Resets the card in reader. + The default reset type is cold, + but warm reset is also possible.

+ --send-apdu apdu, + -s apdu +

+ Sends an arbitrary APDU to the card in the format + AA:BB:CC:DD:EE:FF.... Use this option + multiple times to send more than one APDU. +

+ The built-in card drivers may send additional APDUs + for detection and initialization. To avoid this + behavior, you may additionally specify + --card-driver default. +

+ --serial +

Print the card serial number (normally the ICCSN). + Output is in hex byte format

+ --verbose, + -v +

Causes opensc-tool to be more verbose. + Specify this flag several times to enable debug output in the opensc library.

+ --wait, + -w +

Wait for a card to be inserted.

+

See also

+ opensc-explorer(1) +

Authors

opensc-tool was written by + Juha Yrjölä .


Name

piv-tool — smart card utility for HSPD-12 PIV cards

Synopsis

piv-tool [OPTIONS]

Description

+ The piv-tool utility can be used from the command line to perform + miscellaneous smart card operations on a HSPD-12 PIV smart card as defined in + + NIST SP 800-73-5 a multi-part document. + + It is intended for use with test cards only. It can load objects and generate + key pairs, as well as send arbitrary APDU commands to a card after having authenticated + to the card using the card key provided by the card vendor. +

Options

+

Description

+ --serial +

Print the card serial number derived from the CHUID object, + if any. Output is in hex byte format.

+ --name, + -n +

Print the name of the inserted card (driver)

+ --admin argument, + -A argument +

Authenticate to the card using a 2DES, 3DES or AES key. + The argument of the form +

 {A|M}:ref:alg

+ is required, were A uses "EXTERNAL AUTHENTICATION" + and M uses "MUTUAL AUTHENTICATION". + ref is normally 9B, + and alg is 03 for 3DES, + 01 for 2DES, 08 for AES-128, + 0A for AES-192 or 0C for AES-256. + The key is provided by the card vendor. The environment variable + PIV_EXT_AUTH_KEY must point to either a binary file + matching the length of the key or a text file containing + the key in the format: + XX:XX:XX:XX:XX... +

+ --genkey argument, + -G argument +

Generate a key pair on the card and output the public key. + The argument of the form +

ref:alg

+ is required, where ref is 9A, + 9C, 9D, 9E and + 82-95 for retired keys. + alg is 05, 06, + 07, 11 or 14 + for RSA 3072, RSA 1024, RSA 2048, ECC 256 or ECC 384 respectively. + Some non standard cards/tokens also support + 16, E0, E1 + for RSA 4096, EDDSA 25519 or XEDDSA 25519. +

+ --object ContainerID, + -O ContainerID +

Load an object onto the card. + The ContainerID is as defined in + NIST SP 800-73-5 Part 1 Table 2. "Data Model Containers" + (without leading 0x). Example: CHUID object is 3000 +

+ --cert ref, + -C ref +

Load a certificate onto the card. + ref as in --genkey +

+ --compresscert ref, + -Z ref +

Load a certificate that has been gzipped onto the card. + ref as in --genkey +

+ --out file, + -o file + <title>Description</title>

Output file for any operation that produces output. +

+ --in file, + -i file +

Input file for any operation that requires an input file. +

+ --key-slots-discovery file +

Print properties of the key slots. Needs 'admin' authentication. +

+ --send-apdu apdu, + -s apdu +

Sends an arbitrary APDU to the card in the format + AA:BB:CC:DD:EE:FF.... + This option may be repeated.

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --wait, + -w +

Wait for a card to be inserted

+ --verbose, + -v +

Causes piv-tool to be more verbose. + Specify this flag several times to enable debug output in the opensc + library.

+

Examples

Authenticate to the card and create History Object to use the 20 retired keys

piv-tool -A argument -s "00:DB:3F:FF:0F:5C:03:5F:C1:0C:53:08:C1:01:14:C2:01:00:FE:00"

See also

+ opensc-tool(1) +

Authors

piv-tool was written by + Douglas E. Engert .


Name

pkcs11-tool — utility for managing and using PKCS #11 security tokens

Synopsis

pkcs11-tool [OPTIONS]

Description

+ The pkcs11-tool utility is used to manage the + data objects on smart cards and similar PKCS #11 security tokens. + Users can list and read PINs, keys and certificates stored on the + token. User PIN authentication is performed for those operations + that require it. +

Options

+

+ --attr-from filename +

Extract information from filename + (DER-encoded certificate file) and create the corresponding + attributes when writing an object to the token. Example: the + certificate subject name is used to create the CKA_SUBJECT + attribute.

+ --change-pin, + -c +

Change the user PIN on the token

+ --unlock-pin +

Unlock User PIN (without --login + unlock in logged in session; otherwise --login-type + has to be 'context-specific').

+ --hash, + -h +

Hash some data.

+ --hash-algorithm mechanism +

+ Specify hash algorithm used with RSA-PKCS-PSS signature or RSA-OAEP decryption. + Allowed values are "SHA-1", "SHA256", "SHA384", "SHA512", and some tokens may + also allow "SHA224". Default is "SHA-1". +

+ Note that the input to RSA-PKCS-PSS has to be of the size equal to + the specified hash algorithm. E.g., for SHA256 the signature input must + be exactly 32 bytes long (for mechanisms SHA256-RSA-PKCS-PSS there is no + such restriction). For RSA-OAEP, the plaintext input size mLen must be + at most keyLen - 2 - 2*hashLen. For example, for RSA 3072-bit key and + SHA384, the longest plaintext to encrypt with RSA-OAEP is (with all + sizes in bytes): 384 - 2 - 2*48 = 286, aka 286 bytes. +

+ --id id, + -d id +

Specify the id of the object to operate on.

+ --init-pin +

Initializes the user PIN. This option + differs from --change-pin in that it sets the user PIN + for the first time. Once set, the user PIN can be changed + using --change-pin.

+ --init-token +

Initialize a token: set the token label as + well as a Security Officer PIN (the label must be specified + using --label).

+ --input-file filename, + -i filename +

Specify the path to a file for input.

+ --keypairgen, + -k +

Generate a new key pair (public and private pair.)

+ --keygen +

Generate a new key.

+ --key-type specification +

Specify the type and (not always compulsory) flavour (byte-wise symmetric key length, bit-wise asymmetric key length, + elliptic curve identifier, etc.) of the key to create, for example RSA:2048, EC:prime256v1, EC:ED25519, EC:X448, GOSTR3410-2012-256:B, + DES:8, DES3:24, AES:16, AES:, CHACHA20, POLY1305 or GENERIC:64. If the key type was incompletely specified, possible values are listed.

+ --usage-sign +

Specify 'sign' key usage flag (sets SIGN in privkey, sets VERIFY in pubkey).

+ --usage-decrypt +

Specify 'decrypt' key usage flag.

+ For RSA keys, sets DECRYPT in privkey and ENCRYPT in pubkey. For secret + keys, sets both DECRYPT and ENCRYPT. +

+ --usage-derive +

Specify 'derive' key usage flag (EC only).

+ --usage-wrap +

Specify 'wrap' key usage flag.

+ --usage-encapsulate +

Specify 'encapsulate' key usage flag (sets DECAPSULATE in privkey, sets ENCAPSULATE in pubkey).

+ --label name, + -a name +

Specify the name of the object to operate on + (or the token label when --init-token + is used).

+ --list-mechanisms, + -M +

Display a list of mechanisms supported by the token.

+ --list-objects, + -O +

Display a list of objects.

The options --keytype, --label + , --id or --application-id can be + used to filter the listed objects.

+ --list-slots, + -L +

Display a list of available slots on the token.

+ --list-token-slots, + -T +

List slots with tokens.

+ --list-interfaces +

List interfaces of PKCS #11 3.0 library.

+ --session-rw, +

Forces to open the PKCS#11 session with CKF_RW_SESSION.

+ --login, + -l +

Authenticate to the token before performing + other operations. This option is not needed if a PIN is + provided on the command line.

+ --login-type +

Specify login type ('so', 'user', 'context-specific'; + default:'user').

+ --mechanism mechanism, + -m mechanism +

Use the specified mechanism + for token operations. See -M for a list + of mechanisms supported by your token. The mechanism can also be specified in + hexadecimal, e.g., 0x80001234.

+ --mgf function +

Use the specified Message Generation + Function (MGF) function + for RSA-PKCS-PSS signatures or RSA-OAEP decryptions. Supported arguments are MGF1-SHA1 + to MGF1-SHA512 if supported by the driver. + The default is based on the hash selection. +

+ --module mod +

Specify a PKCS#11 module (or library) to + load.

+ --moz-cert filename, + -z filename +

Test a Mozilla-like key pair generation + and certificate request. Specify the filename + to the certificate file.

+ --output-file filename, + -o filename +

Specify the path to a file for output.

+ --pin pin, + -p pin +

Use the given pin for + token operations. If set to + env:VARIABLE, the value of the + environment variable VARIABLE is + used. WARNING: Be careful using this option + as other users may be able to read the command line from + the system or if it is embedded in a script. If set to + env:VARIABLE, the value of the + environment variable VARIABLE is + used.

This option will also set + the --login option.

+ --public-key-info +

Read public key from the module using "CKA_PUBLIC_KEY_INFO" + (use with --read-object --type public). + The output is a "SubjectPublicKeyInfo" i.e. as used in certificates + and openssl.

+ --puk puk +

Supply User PUK on the command line.

+ --new-pin pin +

Supply new User PIN on the command line.

+ --sensitive +

Set the CKA_SENSITIVE attribute (object cannot be revealed in plaintext).

+ --extractable +

Set the CKA_EXTRACTABLE attribute (object can be extracted)

+ --undestroyable +

Set the CKA_DESTROYABLE attribute to false (object cannot be destroyed)

+ --set-id id, + -e id +

Set the CKA_ID of the object.

+ --show-info, + -I +

Display general token information.

+ --sign, + -s +

Sign some data.

+ --decrypt, +

Decrypt some data.

+ --encrypt, +

Encrypt some data.

+ --unwrap, +

Unwrap key.

+ --wrap, +

Wrap key.

+ --derive, +

Derive a secret key using another key and some data.

+ --derive-pass-der, +

Derive ECDHpass DER encoded pubkey for compatibility with some PKCS#11 implementations

+ --salt-len bytes +

Specify how many bytes of salt should + be used in RSA-PSS signatures. Accepts two special values: + "-1" means salt length equals to digest length, + "-2" or "-3" means use maximum permissible length. + For verify operation "-2" means that the salt length is automatically recovered from signature. + The value "-2" for the verify operation is supported for opensc pkcs#11 module only. + Default is digest length (-1).

+ --slot id +

Specify the id of the slot to use (accepts HEX format with 0x.. prefix or decimal number).

+ --slot-description description +

Specify the description of the slot to use.

+ --slot-index index +

Specify the index of the slot to use.

+ --object-index index +

Specify the index of the object to use.

+ --use-locking +

Tell pkcs11 module it should use OS thread locking. +

+ --test-threads options +

Test a pkcs11 module's thread implication. (See source code). +

+ --token-label label +

Specify the label of token. + Will be used the first slot, that has the inserted token with this + label.

+ --so-pin pin +

Use the given pin as the + Security Officer PIN for some token operations (token + initialization, user PIN initialization, etc). If set to + env:VARIABLE, the value of the + environment variable VARIABLE is + used. The same warning as --pin also + applies here.

+ --test, + -t +

Perform some tests on the token. This + option is most useful when used with either --login + or --pin.

+ --test-hotplug +

Test hotplug capabilities (C_GetSlotList + + C_WaitForSlotEvent).

+ --private +

Set the CKA_PRIVATE attribute (object is only + viewable after a login).

+ --always-auth +

Set the CKA_ALWAYS_AUTHENTICATE attribute to a private key object. + If set, the user has to supply the PIN for each use (sign or decrypt) with the key.

+ --allowed-mechanisms mechanisms +

Sets the CKA_ALLOWED_MECHANISMS attribute + to a key objects when importing an object or generating + a keys. The argument accepts comma-separated list of + algorithmsm, that can be used with the given key.

+ --test-ec +

Test EC (best used with the --login + or --pin option).

+ --test-fork +

Test forking and calling C_Initialize() in the + child.

+ --type type, + -y type +

Specify the type of object to operate on. + Valid value are cert, privkey, + pubkey, secrkey + and data.

+ --verbose, -v +

Cause pkcs11-tool to be + more verbose.

NB! This does not affect + OpenSC debugging level! To set OpenSC PKCS#11 module into debug + mode, set the OPENSC_DEBUG environment variable to a + non-zero number.

+ --verify, +

Verify signature of some data.

+ --read-object, + -r +

Get object's CKA_VALUE attribute (use with + --type).

+ --delete-object, + -b +

Delete an object.

+ --application-label label +

Specify the application label of the data object (use with + --type data).

+ --application-id id +

Specify the application ID of the data object (use with + --type data).

+ --issuer data +

Specify the issuer in hexadecimal format (use with + --type cert).

+ --subject data +

Specify the subject in hexadecimal format (use with + --type cert/privkey/pubkey).

+ --signature-file filename +

The path to the signature file for signature verification

+ --signature-format format +

Format for ECDSA signature: 'rs' (default), + 'sequence', 'openssl'.

+ --write-object filename, + -w filename +

Write a key or certificate object to the token. + filename points to the DER-encoded certificate or key file. +

+ --generate-random num +

Get num bytes of random data. +

+ --allow-sw +

Allow using software mechanisms that do not have the CKF_HW flag set. + May be required when using software tokens and emulators. +

+ --iv data +

Initialization vector for symmetric ciphers. + The data is hexadecimal number, i.e. "000013aa7bffa0". +

+ --mac-general-param num +

Sets the length of the MAC for the general-length MACing mechanisms + to num bytes. +

+ --aad data +

Additional authenticated data for AEAD ciphers. + The data is an hexadecimal number. +

+ --tag-bits-len num +

Sets the length of the tag for AEAD ciphers to + num bits. +

+ --salt-file filename +

Specify the file containing the salt for HKDF (optional)

+ --info-file filename +

Specify the file containing the info for HKDF (optional)

+ --uri value +

Specify the PKCS#11 URI for module, slot, token or object

+ --uri-with-slot-id +

Include SlotId in PKCS#11 URI

+

Environment

+ OPENSC_DEEPBIND +

+ The OpenSC and its tools are dynamically loading various + libraries (pcsclite, dynamic drivers and other pkcs11 + modules) using + dlopen(3). +

+ When this environment variable is set to + 1 + for the process running OpenSC, the calls will include + RTLD_DEEPBIND flag, which should + cause better symbol isolation between OpenSC and + loaded libraries. On the other hand, this is incompatible + with various static analysis tools as well as some + browser builds. +

+ This option may be useful, if OpenSC needs to load a library + that pollutes the processes' global namespace and thereby + causes the unintended use of a different function with the + same name. In particular, this may happen when + pkcs11-spy + is used for logging between application and PKCS#11 library. +

Examples

+ Perform a basic functionality test of the card: +

pkcs11-tool --test --login

+ + List all certificates on the smart card: +

pkcs11-tool --list-objects --type cert

+ + Read the certificate with ID CERT_ID + in DER format from smart card and convert it to PEM via OpenSSL: +

+pkcs11-tool --read-object --id $CERT_ID --type cert \
+					--output-file cert.der
+openssl x509 -inform DER -in cert.der -outform PEM > cert.pem
+			

+ + Write a certificate to token: +

pkcs11-tool --login --write-object certificate.der --type cert

+ + Generate new RSA Key pair: +

pkcs11-tool --login --keypairgen --key-type RSA:2048

+ + Generate new extractable RSA Key pair: +

pkcs11-tool --login --keypairgen --key-type RSA:2048 --extractable

+ + Generate an elliptic curve key pair with OpenSSL and import it to the card as $ID: +

openssl genpkey -out EC_private.der -outform DER \
+	-algorithm EC -pkeyopt ec_paramgen_curve:P-521
+pkcs11-tool --write-object EC_private.der --id "$ID" \
+	--type privkey --label "EC private key" -p "$PIN"
+openssl pkey -in EC_private.der -out EC_public.der \
+	-pubout -inform DER -outform DER
+pkcs11-tool --write-object EC_public.der --id "$ID" \
+	--type pubkey  --label "EC public key" -p $PIN

+ + List private keys: +

pkcs11-tool --login --list-objects --type privkey

+ + Sign some data stored in file data + using the private key with ID ID and + using the RSA-PKCS mechanism: +

+pkcs11-tool --sign --id $ID --mechanism RSA-PKCS \
+	--input-file data --output-file data.sig
+			

+ The same is also possible by piping the data from stdin rather than specifying a input file: +

+dd if=data bs=128 count=1 \
+	| pkcs11-tool --sign --id $ID --mechanism RSA-PKCS \
+	> data.sig
+			

+ + Verify the signed data: +

+pkcs11-tool --id ID --verify -m RSA-PKCS \
+	--input-file data --signature-file data.sig
+			

+ + To encrypt file using the AES key with ID 85 and using mechanism AES-CBC with padding: +

+pkcs11-tool --login --encrypt --id 85 -m AES-CBC-PAD \
+	--iv "00000000000000000000000000000000" \
+	-i file.txt -o encrypted_file.data
+				

+ Decipher the encrypted file: +

+pkcs11-tool --login --decrypt --id 85 -m AES-CBC-PAD \
+	--iv "00000000000000000000000000000000" \
+	--i encrypted_file.data -o decrypted.txt
+				

+ + Use the key with ID 75 using mechanism AES-CBC-PAD, with initialization vector + "00000000000000000000000000000000" to wrap the key with ID 76 into output file + exported_aes.key +

+pkcs11-tool --login --wrap --id 75 --mechanism AES-CBC-PAD \
+	--iv "00000000000000000000000000000000" \
+	--application-id 76 \
+	--output-file exported_aes.key
+				

+ Use the key with ID 22 and mechanism RSA-PKCS to unwrap key from file + aes_wrapped.key. After a successful unwrap operation, + a new AES key is created on token. ID of this key is set to 90 and label of this + key is set to unwrapped-key + Note: for the MyEID card, the AES key size must be present in key + specification i.e. AES:16 +

+pkcs11-tool --login --unwrap --mechanism RSA-PKCS --id 22 \
+	-i aes_wrapped.key --key-type AES: \
+	--application-id 90 --application-label unwrapped-key
+				

+ + Use the SO-PIN to initialize or re-set the PIN: +

+pkcs11-tool --login --login-type so --init-pin
+				

+

Authors

pkcs11-tool was written by + Olaf Kirch .


Name

pkcs15-crypt — perform crypto operations using PKCS#15 smart cards

Synopsis

pkcs15-crypt [OPTIONS]

Description

+ The pkcs15-crypt utility can be used from the + command line to perform cryptographic operations such as computing + digital signatures or decrypting data, using keys stored on a PKCS#15 + compliant smart card. +

Options

+

+ --version, +

Print the OpenSC package release version.

+ --aid aid +

Specify the AID of the on-card PKCS#15 application + to bind to. The aid must be in hexadecimal + form.

+ --decipher, + -c +

Decrypt the contents of the file specified by + the --input option. The result of the + decryption operation is written to the file specified by the + --output option. If this option is not given, + the decrypted data is printed to standard output, displaying + non-printable characters using their hex notation xNN (see also + --raw).

+ --input file, + -i file +

Specifies the input file to use. Defaults to stdin if + not specified.

+ --key id, + -k id +

Selects the ID of the key to use.

+ --output file, + -o file +

Any output will be sent to the specified file. Defaults + to stdout if not specified.

+ --pin pin, + -p pin +

When the cryptographic operation requires a + PIN to access the key, pkcs15-crypt will + prompt the user for the PIN on the terminal. Using this option + allows you to specify the PIN on the command line.

Note that on most operating systems, the command line of + a process can be displayed by any user using the ps(1) + command. It is therefore a security risk to specify + secret information such as PINs on the command line. + If you specify '-' as PIN, it will be read from STDIN.

+ --pkcs1 +

By default, pkcs15-crypt + assumes that input data has been padded to the correct length + (i.e. when computing an RSA signature using a 1024 bit key, + the input must be padded to 128 bytes to match the modulus + length). When giving the --pkcs1 option, + however, pkcs15-crypt will perform the + required padding using the algorithm outlined in the + PKCS #1 standard version 1.5.

+ --raw, + -R +

Outputs raw 8 bit data.

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --md5 + --sha-1 + --sha-224 + --sha-256 + --sha-384 + --sha-512 +

These options tell pkcs15-crypt + that the input file is the result of the specified hash operation. + By default, an MD5 hash is expected. Again, the data must be in binary + representation.

+ --sign, + -s +

Perform digital signature operation on + the data read from a file specified using the --input + option. By default, the contents of the file are assumed to + be the result of an MD5 hash operation. + Note that pkcs15-crypt + expects the data in binary representation, not ASCII.

The digital signature is stored, in binary representation, + in the file specified by the --output option. If + this option is not given, the signature is printed on standard + output, displaying non-printable characters using their hex notation + xNN + (see also --raw).

+ --signature-format, + --f +

When signing with ECDSA key this option indicates + to pkcs15-crypt the signature output format. + Possible values are 'rs'(default) -- two concatenated + integers (PKCS#11), 'sequence' or 'openssl' -- DER encoded sequence + of two integers (OpenSSL).

+ --wait, + -w +

Causes pkcs15-crypt to + wait for a card insertion.

+ --verbose, + -v +

Causes pkcs15-crypt to be more + verbose. Specify this flag several times to enable debug output + in the OpenSC library.

+

See also

+ pkcs15-init(1), + pkcs15-tool(1) +

Authors

pkcs15-crypt was written by + Juha Yrjölä .


Name

pkcs15-init — smart card personalization utility

Synopsis

pkcs15-init [OPTIONS]

Description

+ The pkcs15-init utility can be used to create a PKCS #15 + structure on a smart card, and add key or certificate objects. Details of the + structure that will be created are controlled via profiles. +

+ The profile used by default is pkcs15. Alternative + profiles can be specified via the -p switch. +

PIN Usage

+ pkcs15-init can be used to create a PKCS #15 structure on + your smart card, create PINs, and install keys and certificates on the card. + This process is also called personalization. +

+ An OpenSC card can have one security officer PIN, and zero or more user PINs. + PIN stands for Personal Identification Number, and is a secret code you need + to present to the card before being allowed to perform certain operations, + such as using one of the stored RSA keys to sign a document, or modifying + the card itself. +

+ Usually, PINs are a sequence of decimal digits, but some cards will accept + arbitrary ASCII characters. Be aware however that using characters other + than digits will make the card unusable with PIN pad readers, because those + usually have keys for entering digits only. +

+ The security officer (SO) PIN is special; it is used to protect meta data + information on the card, such as the PKCS #15 structure itself. Setting + the SO PIN is optional, because the worst that can usually happen is that + someone finding your card can mess it up. To extract any of your secret + keys stored on the card, an attacker will still need your user PIN, at + least for the default OpenSC profiles. However, it is possible to create + card profiles that will allow the security officer to override user PINs. +

+ For each PIN, you can specify a PUK (also called unblock PIN). + The PUK can be used to overwrite or unlock a PIN if too many incorrect values + have been entered in a row. +

+ For some cards that use the PKCS#15 emulation, the attributes of private objects + are protected and cannot be parsed without authentication (usually with User PIN). + This authentication need to be done immediately after the card binding. + In such cases --verify-pin has to be used. +

Modes of operation

Initialization

This is the first step during card personalization, and will create the + basic files on the card. To create the initial PKCS #15 structure, invoke the + utility as +

+ pkcs15-init --create-pkcs15

+ You will then be asked for the security officer PIN and PUK. Simply + pressing return at the SO PIN prompt will skip installation of an SO PIN. +

+ If the card supports it, you should erase the contents of the card with + pkcs15-init --erase-card before creating the PKCS#15 structure. +

User PIN Installation

+ Before installing any user objects such as private keys, you need at least one + PIN to protect these objects. you can do this using +

+ pkcs15-init --store-pin --id " nn +

+ where nn is a PKCS #15 ID in hexadecimal notation. Common + values are 01, 02, etc. +

+ Entering the command above will ask you for the user's PIN and PUK. If you do + not wish to install an unblock PIN, simply press return at the PUK prompt. +

+ To set a label for this PIN object (which can be used by applications to display + a meaningful prompt to the user), use the --label command line option. +

Key generation

+ pkcs15-init lets you generate a new key and store it on the card. + You can do this using: +

+ pkcs15-init --generate-key "keyspec" --auth-id "nn" +

+ where keyspec describes the algorithm and the parameters + of the key to be created. For example, rsa:2048 generates a RSA key + with 2048-bit modulus, default is 3072. If you are generating an EC key, the curve designation must + be specified, for example ec:prime256v1. + The following curves do not need parameters: + Edwards 25519 curves use one of these equivalent names: Ed25519(preferred), ed25519 or edwards25519 + and for the 448 curve use: Ed448. + For Montgomery 25519 curves use one of these equivalent names: X25519(preferred), cv25519 or curve25519 + and for the 448 curve use: X448. + For symmetric key, + the length of key is specified in bytes, for example AES:32 + or DES3:24. +

+ nn is the ID of a user PIN installed previously, + e.g. 01. +

+ In addition to storing the private portion of the key on the card, + pkcs15-init will also store the public portion of the + key as a PKCS #15 public key object. +

Private Key Upload

+ You can use a private key generated by other means and upload it to the card. + For instance, to upload a private key contained in a file named + okir.pem, which is in PEM format, you would use +

+ pkcs15-init --store-private-key okir.pem --id 45 --auth-id 01 +

+ In addition to storing the private portion of the key on the card, + pkcs15-init will also store the public portion of the + key as a PKCS #15 public key object. +

+ Note that usage of --id option in the pkcs15-init + commands to generate or to import a new key is deprecated. + Better practice is to let the middleware to derive the identifier from the key material. + (SHA1(modulus) for RSA, ...). + This allows easily set up relation between 'related' objects + (private/public keys and certificates). +

+ In addition to the PEM key file format, pkcs15-init also + supports DER encoded keys, and PKCS #12 files. The latter is the file format + used by Netscape Navigator (among others) when exporting certificates to + a file. A PKCS #12 file usually contains the X.509 certificate corresponding + to the private key. If that is the case, pkcs15-init will + store the certificate instead of the public key portion. +

Public Key Upload

+ You can also upload individual public keys to the card using the + --store-public-key option, which takes a filename as an + argument. This file is supposed to contain the public key. If you don't + specify a key file format using the --format option, + pkcs15-init will assume PEM format. The only other + supported public key file format is DER. +

+ Since the corresponding public keys are always uploaded automatically + when generating a new key, or when uploading a private key, you will + probably use this option only very rarely. +

Certificate Upload

+ You can upload certificates to the card using the + --store-certificate option, which takes a filename as + an argument. This file is supposed to contain the PEM encoded X.509 + certificate. +

Uploading PKCS #12 bags

+ Most browsers nowadays use PKCS #12 format files when you ask them to + export your key and certificate to a file. pkcs15-init + is capable of parsing these files, and storing their contents on the + card in a single operation. This works just like storing a private key, + except that you need to specify the file format: +

+ pkcs15-init --store-private-key okir.p12 --format pkcs12 --auth-id + 01 +

+ This will install the private key contained in the file okir.p12, + and protect it with the PIN referenced by authentication ID 01. + It will also store any X.509 certificates contained in the file, which is + usually the user certificate that goes with the key, as well as the CA certificate. +

Secret Key Upload

+ You can use a secret key generated by other means and upload it to the card. + For instance, to upload an AES-secret key generated by the system random generator + you would use +

+ pkcs15-init --store-secret-key /dev/urandom --secret-key-algorithm aes:256 --auth-id 01 +

+ By default a random ID is generated for the secret key. You may specify an ID + with the --id if needed. +

Options

+

+ --version, +

Print the OpenSC package release version.

+ --card-profile name, + -c name +

+ Tells pkcs15-init to load the specified card + profile option. You will rarely need this option. +

+ --create-pkcs15, + -C +

+ This tells pkcs15-init to create a PKCS #15 + structure on the card, and initialize any PINs. +

+ --serial SERIAL +

+ Specify the serial number of the card. +

+ --erase-card, + -E +

+ This will erase the card prior to creating the PKCS #15 structure, + if the card supports it. If the card does not support erasing, + pkcs15-init will fail. +

+ --erase-application AID +

+ This will erase the application with the application identifier + AID. +

+ --generate-key keyspec, + -G keyspec +

+ Tells the card to generate new key and store it on the card. + keyspec consists of an algorithm name, + optionally followed by a colon ":", slash "/" or hyphen "-" and + the parameters of the key to be created. + It is a good idea to specify the key ID along with this command, + using the id option, otherwise an intrinsic ID + will be calculated from the key material. Look the description of + the 'pkcs15-id-style' attribute in the 'pkcs15.profile' for the details + about the algorithm used to calculate intrinsic ID. + For the multi-application cards the target PKCS#15 application can be + specified by the hexadecimal AID value of the aid option. +

+ --pin pin, + --puk puk, + --so-pin sopin, + --so-puk sopuk +

+ These options can be used to specify the PIN/PUK values + on the command line. If the value is set to + env:VARIABLE, the value + of the specified environment variable is used. By default, + the code is prompted on the command line if needed. +

+ Note that on most operation systems, any user can + display the command line of any process on the + system using utilities such as + ps(1). Therefore, you should prefer + passing the codes via an environment variable + on an unsecured system. +

+ --no-so-pin, +

+ Do not install a SO PIN, and do not prompt for it. +

+ --profile name, + -p name +

+ Tells pkcs15-init to load the specified general + profile. Currently, the only application profile defined is + pkcs15, but you can write your own profiles and + specify them using this option. +

+ The profile name can be combined with one or more profile + options, which slightly modify the profile's behavior. + For instance, the default OpenSC profile supports the + openpin option, which installs a single PIN during + card initialization. This PIN is then used both as the SO PIN as + well as the user PIN for all keys stored on the card. +

+ Profile name and options are separated by a + + character, as in pkcs15+onepin. +

+ --secret-key-algorithm keyspec, +

+ keyspec describes the algorithm and length of the + key to be created or downloaded, such as aes:256. + This will create a 256 bit AES key. +

+ --store-certificate filename, + -X filename +

+ Tells pkcs15-init to store the certificate given + in filename on the card, creating a certificate + object with the ID specified via the --id option. + Without supplied ID an intrinsic ID will be calculated from the + certificate's public key. Look the description of the 'pkcs15-id-style' + attribute in the 'pkcs15.profile' for the details + about the algorithm used to calculate intrinsic ID. + The file is assumed to contain the PEM encoded certificate. + For the multi-application cards the target application can be specified + by the hexadecimal AID value of the aid option. +

+ --store-pin, + -P +

+ Store a new PIN/PUK on the card. +

+ --store-public-key filename +

+ Tells pkcs15-init to download the specified + public key to the card and create a public key object with the + key ID specified via the --id. By default, + the file is assumed to contain the key in PEM format. Alternative + formats can be specified using --format. +

+ --store-private-key filename, + -S filename +

+ Tells pkcs15-init to download the specified + private key to the card. This command will also create a public + key object containing the public key portion. By default, the + file is assumed to contain the key in PEM format. Alternative + formats can be specified using --format. + It is a good idea to specify the key ID along with this command, + using the --id option, otherwise an intrinsic ID + will be calculated from the key material. Look the description of + the 'pkcs15-id-style' attribute in the 'pkcs15.profile' for the details + about the algorithm used to calculate intrinsic ID. + For the multi-application cards the target PKCS#15 application can be + specified by the hexadecimal AID value of the aid option. +

+ --store-secret-key filename, +

+ Tells pkcs15-init to download the specified + secret key to the card. The file is assumed to contain the raw key. + They key type should be specified with --secret-key-algorithm + option. +

+ You may additionally specify the key ID along with this command, + using the --id option, otherwise a random ID is generated. + For the multi-application cards the target PKCS#15 application can be + specified by the hexadecimal AID value of the aid option. +

+ --store-data filename, + -W filename +

+ Store a data object. +

+ --update-certificate filename, + -U filename +

+ Tells pkcs15-init to update the certificate + object with the ID specified via the --id option + with the certificate in filename. + The file is assumed to contain a PEM encoded certificate. +

Pay extra attention when updating mail decryption certificates, as + missing certificates can render e-mail messages unreadable! +

+ --delete-objects arg, + -D arg +

+ Tells pkcs15-init to delete the + specified object. arg + is comma-separated list containing any of + privkey, pubkey, + secrkey, cert, + chain or data. +

+ When data is specified, an + ---application-id must also be + specified, in the other cases an + --id must also be specified +

+ When chain is specified, the + certificate chain starting with the cert with + specified ID will be deleted, until there's a CA + certificate that certifies another cert on the card +

+ --change-attributes arg, + -A arg +

+ Tells pkcs15-init to change the + specified attribute. arg + is either privkey, + pubkey, secrkey, + cert or data. + You also have to specify the --id + of the object. + For now, you can only change the --label, e.g: +

+								pkcs15-init -A cert --id 45 -a 1 --label Jim
+							

+

+ --use-default-transport-keys, + -T +

+ Tells pkcs15-init to not ask for the transport + keys and use default keys, as known by the card driver. +

+ --sanity-check +

+ Tells pkcs15-init to perform a + card specific sanity check and possibly update + procedure. +

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --verbose, + -v +

+ Causes pkcs15-init to be more verbose. Specify this + flag several times to enable debug output in the OpenSC library. +

+ --wait, + -w +

Causes pkcs15-init to + wait for a card insertion.

+ --use-pinpad +

Do not prompt the user; if no PINs supplied, pinpad will be used.

+ --auth-id filename, + -a filename +

+ Specify ID of PIN to use/create +

+ --puk-id ID +

+ Specify ID of PUK to use/create +

+ --label LABEL +

+ Specify label for a PIN, key, certificate or data object when creating a new objects. When deleting objects, this can be used to delete object by label. +

+ --puk-label LABEL +

+ Specify label of PUK +

+ --public-key-label LABEL +

+ Specify public key label (use with --generate-key) +

+ --cert-label LABEL +

+ Specify user cert label (use with --store-private-key) +

+ --application-name arg +

+ Specify application name of data object (use with --store-data-object) +

+ --aid AID +

+ Specify AID of the on-card PKCS#15 application to be binded to (in hexadecimal form) +

+ --output-file filename + -o filename, +

+ Output public portion of generated key to file +

+ --passphrase PASSPHRASE +

+ Specify passphrase for unlocking secret key +

+ --authority +

+ Mark certificate as a CA certificate +

+ --key-usage arg + -u arg, +

+ Specifies the X.509 key usage. + arg is comma-separated + list containing any of + digitalSignature, + nonRepudiation, + keyEncipherment, + dataEncipherment, + keyAgreement, + keyCertSign, + cRLSign. Abbreviated names are + allowed if unique (e.g. + dataEnc). +

+ The alias sign is equivalent to + digitalSignature,keyCertSign,cRLSign +

+ The alias decrypt is equivalent to + keyEncipherment,dataEncipherment +

+ --finalize + -F, +

+ Finish initialization phase of the smart card +

+ --update-last-update +

+ Update 'lastUpdate' attribute of tokenInfo +

+ --ignore-ca-certificates +

+ When storing PKCS#12 ignore CA certificates +

+ --update-existing +

+ Store or update existing certificate +

+ --extractable +

+ Private key stored as an extractable key +

+ --user-consent arg +

+ Specify user-consent. arg is an integer value. + If > 0, the value specifies how many times the + object can be accessed before a new authentication is required. + If zero, the object does not require re-authentication. +

+ --insecure +

+ Insecure mode: do not require a PIN for private key +

+ --md-container-guid GUID +

+ For a new key specify GUID for a MD container +

+ --help + -h, +

+ Display help message +

+

See also

+ pkcs15-profile(5) +

Authors

pkcs15-init was written by + Olaf Kirch .


Name

pkcs15-tool — utility for manipulating PKCS #15 data structures + on smart cards and similar security tokens

Synopsis

pkcs15-tool [OPTIONS]

Description

+ The pkcs15-tool utility is used to manipulate + the PKCS #15 data structures on smart cards and similar security + tokens. Users can list and read PINs, keys and certificates stored + on the token. User PIN authentication is performed for those + operations that require it. +

Options

+

+ --version +

Print the OpenSC package release version.

+ --aid aid +

Specify in a hexadecimal form the AID of the on-card PKCS#15 + application to bind to.

+ --auth-id id, + -a id +

Specifies the auth id of the PIN to use for the + operation. This is useful with the --change-pin operation.

+ --change-pin +

Changes a PIN or PUK stored on the token. User authentication + is required for this operation.

+ --dump, + -D +

List all card objects.

+ --list-info +

List card objects.

+ --list-applications +

List the on-card PKCS#15 applications.

+ --list-certificates, + -c +

List all certificates stored on the token.

+ --list-data-objects, + -C +

List all data objects stored on the token. + For some cards the PKCS#15 attributes of the private data objects are + protected for reading and need the authentication with the User PIN. + In such a case the --verify-pin option has to be used. +

+ --list-keys, + -k +

List all private keys stored on the token. General + information about each private key is listed (eg. key name, id and + algorithm). Actual private key values are not displayed. + For some cards the PKCS#15 attributes of the private keys are protected for reading + and need the authentication with the User PIN. + In such a case the --verify-pin option has to be used.

+ --list-secret-keys +

List all secret (symmetric) keys stored on the token. General + information about each secret key is listed (eg. key name, id and + algorithm). Actual secret key values are not displayed. + For some cards the PKCS#15 attributes of the private keys are protected for reading + and need the authentication with the User PIN. + In such a case the --verify-pin option has to be used.

+ --list-pins +

List all PINs stored on the token. General information + about each PIN is listed (eg. PIN name). Actual PIN values are not shown.

+ --list-public-keys +

List all public keys stored on the token, including + key name, id, algorithm and length information.

+ --short, + -s +

Output lists in compact format.

+ --no-cache +

Disables token data caching.

+ --clear-cache +

Removes the user's cache directory. On + Windows, this option additionally removes the system's + caching directory (requires administrator + privileges).

+ --output filename, + -o filename +

Specifies where key output should be written. + If filename already exists, it will be overwritten. + If this option is not given, keys will be printed to standard output.

+ --raw +

Changes how --read-data-object prints the content + to standard output. By default, when --raw is not given, it will + print the content in hex notation. If --raw is set, it will print + the binary data directly. This does not affect the output that is written to the + file specified by the --output option. Data written to a file will + always be in raw binary.

+ --read-certificate cert +

Reads the certificate with the given id.

+ --read-data-object data, + -R data +

Reads data object with OID, applicationName or label. + The content is printed to standard output in hex notation, unless + the --raw option is given. + If an output file is given with the --output option, + the content is additionally written to the file. + Output to the file is always written in raw binary mode, the + --raw only affects standard output behavior.

+ --read-public-key id +

Reads the public key with id id, + allowing the user to extract and store or use the public key.

+ --read-ssh-key id +

Reads the public key with id id, + writing the output in format suitable for + $HOME/.ssh/authorized_keys.

The key label, if any will be shown in the 'Comment' field.

+ --rfc4716 +

When used in conjunction with option --read-ssh-key the + output format of the public key follows rfc4716.

The default output format is a single line (openssh).

+ --test-update, + -T +

Test if the card needs a security update

+ --update, + -U +

Update the card with a security update

+ --reader arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --unblock-pin, + -u +

Unblocks a PIN stored on the token. Knowledge of the + Pin Unblock Key (PUK) is required for this operation.

+ --verbose, + -v +

Causes pkcs15-tool to be more + verbose. Specify this flag several times to enable debug output + in the OpenSC library.

+ --pin pin, + --new-pin newpin, + --puk puk +

+ These options can be used to specify the PIN/PUK values + on the command line. If the value is set to + env:VARIABLE, the value + of the specified environment variable is used. By default, + the code is prompted on the command line if needed. +

+ Note that on most operation systems, any user can + display the command line of any process on the + system using utilities such as + ps(1). Therefore, you should prefer + passing the codes via an environment variable + on an unsecured system. +

+ --new-pin pin +

Specify New PIN (when changing or unblocking)

+ --verify-pin +

Verify PIN after card binding and before issuing any command + (without 'auth-id' the first non-SO, non-Unblock PIN will be verified)

+ --test-session-pin +

Equivalent to --verify-pin + with additional session PIN generation

+ --wait, + -w +

Causes pkcs15-tool to + wait for a card insertion.

+ --use-pinpad +

Do not prompt the user; if no PINs supplied, pinpad will be used.

+

See also

+ pkcs15-init(1), + pkcs15-crypt(1) +

Authors

pkcs15-tool was written by + Juha Yrjölä .


Name

sc-hsm-tool — smart card utility for SmartCard-HSM

Synopsis

sc-hsm-tool [OPTIONS]

+ The sc-hsm-tool utility can be used from the command line to perform + extended maintenance tasks not available via PKCS#11 or other tools in the OpenSC package. + It can be used to query the status of a SmartCard-HSM, initialize a device, generate and import + Device Key Encryption Key (DKEK) shares and to wrap and unwrap keys. +

Options

+

+ --initialize, + -X +

Initialize token, removing all existing keys, certificates and files.

Use --so-pin to define SO-PIN for first initialization or to verify in subsequent + initializations.

Use --pin to define the initial user pin value.

Use --pin-retry to define the maximum number of wrong user PIN presentations.

Use with --dkek-shares to enable key wrap / unwrap.

Use with --label to define a token label

Use with --public-key-auth and --required-pub-keys to require public key authentication for login

+ --create-dkek-share filename, + -C filename +

Create a DKEK share encrypted under a password and save it to the file + given as parameter.

Use --password to provide a password for encryption rather than prompting for one.

Use --pwd-shares-threshold and --pwd-shares-total to randomly generate a password and split is using a (t, n) threshold scheme.

+ --import-dkek-share filename, + -I filename +

Prompt for user password, read and decrypt DKEK share and import into SmartCard-HSM.

Use --password to provide a password for decryption rather than prompting for one.

Use --pwd-shares-total to specify the number of shares that should be entered to reconstruct the password.

+ --wrap-key filename, + -W filename +

Wrap the key referenced in --key-reference and save with it together with the key description + and certificate to the given file.

Use --pin to provide the user PIN on the command line.

+ --unwrap-key filename, + -U filename +

Read wrapped key, description and certificate from file and import into SmartCard-HSM + under the key reference given in --key-reference.

Determine the key reference using the output of pkcs15-tool -D.

Use --pin to provide a user PIN on the command line.

Use --force to remove any key, key description or certificate in the way.

+ --dkek-shares number-of-shares, + -s number-of-shares +

Define the number of DKEK shares to use for recreating the DKEK.

This is an optional parameter. Using --initialize without + --dkek-shares will disable the DKEK completely.

Using --dkek-shares with 0 shares requests the SmartCard-HSM to + generate a random DKEK. Keys wrapped with this DKEK can only be unwrapped in the + same SmartCard-HSM.

After using --initialize with one or more DKEK shares, the + SmartCard-HSM will remain in the initialized state until all DKEK shares have + been imported. During this phase no new keys can be generated or imported.

+ --pin pin, + --so-pin sopin, +

+ These options can be used to specify the PIN values + on the command line. If the value is set to + env:VARIABLE, the value + of the specified environment variable is used. By default, + the code is prompted on the command line if needed. +

+ Note that on most operation systems, any user can + display the command line of any process on the + system using utilities such as + ps(1). Therefore, you should prefer + passing the codes via an environment variable + on an unsecured system. +

+ --pin-retry value +

Define number of PIN retries for user PIN during initialization. Default is 3.

+ --bio-server1 value +

The hexadecimal AID of of the biometric server for template 1. Switches on the use of the user PIN as session PIN.

+ --bio-server2 value +

The hexadecimal AID of of the biometric server for template 2. Switches on the use of the user PIN as session PIN.

+ --password value +

Define password for DKEK share encryption. If set to + env:VARIABLE, the value of + the environment variable + VARIABLE is used.

+ --pwd-shares-threshold value +

Define threshold for number of password shares required for reconstruction.

+ --pwd-shares-total value +

Define number of password shares.

+ --force +

Force removal of existing key, description and certificate.

+ --label label, + -l label +

Define the token label to be used in --initialize.

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --public-key-auth total-number-of-public-keys, + -K total-number-of-public-keys +

Define the total number of public keys to use for public key authentication when using --initialize. + --public-key-auth is optional, but if it's present, it must be used with --required-pub-keys. +

When the SmartCard-HSM is initialized with these options, it will require M-of-N public key authentication to be used, where + --required-pub-keys sets the M and --public-key-auth sets the N. After the initialization, + the user should use --register-public-key to register the N public keys before the SmartCard-HSM can be used. +

+ --required-pub-keys required-number-of-public-keys, + -n required-number-of-public-keys +

Define the required number of public keys to use for public key authentication when using --initialize. + This is the M in M-of-N public key authentication. See --public-key-auth for more information. +

+ --register-public-key input-public-key-file, + -g input-public-key-file +

Register a public key to be used for M-of-N public key authentication. The file can be exported from + a different SmartCard-HSM with --export-for-pub-key-auth. This can only be used when the + SmartCard-HSM has been initialized with --public-key-auth and --required-pub-keys + and fewer than N public keys have been registered. Use --public-key-auth-status to check the + how many public keys have been registered. +

+ --export-for-pub-key-auth output-public-key-file, + -e output-public-key-file +

Export a public key to be used for M-of-N public key authentication. This should be used with + --key-reference to choose the key to export. The file should be registered on + another SmartCard-HSM using --register-public-key. +

+ --public-key-auth-status + -S +

Print the public key authentication status. This is only valid if the SmartCard-HSM was initialized + to use M-of-N public key authentication. +

+ --wait, + -w +

Wait for a card to be inserted

+ --verbose, + -v +

Causes sc-hsm-tool to be more verbose. + Specify this flag several times to enable debug output in the opensc + library.

+

Examples

Create a DKEK share:

sc-hsm-tool --create-dkek-share dkek-share-1.pbe

Create a DKEK share with random password split up using a (3, 5) threshold scheme:

sc-hsm-tool --create-dkek-share dkek-share-1.pbe --pwd-shares-threshold 3 --pwd-shares-total 5

Initialize SmartCard-HSM to use a single DKEK share:

sc-hsm-tool --initialize --so-pin 3537363231383830 --pin 648219 --dkek-shares 1 --label mytoken

Import DKEK share:

sc-hsm-tool --import-dkek-share dkek-share-1.pbe

Import DKEK share using a password split up using a (3, 5) threshold scheme for encryption:

sc-hsm-tool --import-dkek-share dkek-share-1.pbe --pwd-shares-total 3

Wrap referenced key, description and certificate:

sc-hsm-tool --wrap-key wrap-key.bin --key-reference 1 --pin 648219

Unwrap key into same or in different SmartCard-HSM with the same DKEK:

sc-hsm-tool --unwrap-key wrap-key.bin --key-reference 10 --pin 648219 --force

Initialize SmartCard-HSM to use M-of-N public key authentication with M=2 and N=5

sc-hsm-tool --initialize --required-pub-keys 2 --public-key-auth 5

Export a public key for M-of-N public key authentication to a file

sc-hsm-tool --key-reference 1 --export-for-pub-key-auth ./public_key1.asn1

Register a public key for M-of-N public key authentication from a file

sc-hsm-tool --register-public-key ./public_key1.asn1

See also

+ opensc-tool(1) +

Authors

sc-hsm-tool was written by + Andreas Schwier .


Name

westcos-tool — utility for manipulating data structures + on westcos smart cards

Synopsis

westcos-tool [OPTIONS]

Description

+ The westcos-tool utility is used to manipulate + the westcos data structures on 2 Ko smart cards / tokens. Users can create PINs, + keys and certificates stored on the card / token. User PIN authentication is + performed for those operations that require it. +

Options

+

+ --change-pin, + -n +

Changes a PIN stored on the card. + User authentication is required for this operation.

+ --certificate file, + -t file +

Write certificate file file + in PEM format to the card. + User authentication is required for this operation.

+ --finalize, + -f +

Finalize the card. Once finalized the default key is + invalidated, so PIN and PUK cannot be changed anymore without user + authentication.

Warning, un-finalized cards are insecure because the PIN can be + changed without user authentication (knowledge of default key + is enough).

+ --generate-key, + -g +

Generate a private key on the card. The card must not have + been finalized and a PIN must be installed (i.e. the file for the PIN must + have been created, see option -i). + By default the key length is 2048 bits. User authentication is required for + this operation.

+ --help, + -h +

Print help message on screen.

+ --install-pin, + -i +

Install PIN file in on the card. + You must provide a PIN value with -x.

+ --key-length length, + -l length +

Change the length of private key. + Use with -g.

+ --overwrite-key, + -o +

Overwrite the key if there is already a key on the card.

+ --pin-value pin, + -x pin + --puk-value puk, + -y puk +

+ These options can be used to specify the PIN/PUK values + on the command line. If the value is set to + env:VARIABLE, the value + of the specified environment variable is used. By default, + the code is prompted on the command line if needed. +

+ Note that on most operation systems, any user can + display the command line of any process on the + system using utilities such as + ps(1). Therefore, you should prefer + passing the codes via an environment variable + on an unsecured system. +

+ --read-file filename, + -j filename +

Read the file filename from the card. + The file is written on disk with name filename. + User authentication is required for this operation.

+ --reader arg, + -r arg +

+ Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. +

+ --unblock-pin, + -u +

Unblocks a PIN stored on the card. Knowledge of the + PIN Unblock Key (PUK) is required for this operation.

+ --verbose + -v +

Causes westcos-tool to be more + verbose. Specify this flag several times to enable debug output + in the OpenSC library.

+ --wait, + -w +

Wait for a card to be inserted.

+ --write-file filename, + -k filename +

Put the file with name filename + from disk to card. + On the card the file is written in filename. + User authentication is required for this operation.

+

Authors

westcos-tool was written by + Francois Leblanc .

diff --git a/doc/tools/tools.xml b/doc/tools/tools.xml index e9fe9179a1..d23ab7a5fd 100644 --- a/doc/tools/tools.xml +++ b/doc/tools/tools.xml @@ -1,33 +1,30 @@ +"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> - OpenSC - - - OpenSC tools - - - - - - - - - - - - - - - - + OpenSC Manual Pages: Section 1 - - - OpenSC file formats - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/tools/westcos-tool.1.xml b/doc/tools/westcos-tool.1.xml index 240128cd5c..6b894a6fd3 100644 --- a/doc/tools/westcos-tool.1.xml +++ b/doc/tools/westcos-tool.1.xml @@ -62,8 +62,8 @@ Finalize the card. Once finalized the default key is invalidated, so PIN and PUK cannot be changed anymore without user authentication. - Warning, un-finalized are insecure because PIN can be changed - without user authentication (knowledge of default key + Warning, un-finalized cards are insecure because the PIN can be + changed without user authentication (knowledge of default key is enough). @@ -73,9 +73,9 @@ Generate a private key on the card. The card must not have - been finalized and a PIN must be installed (ie. the file for ithe PIN must - havei been created, see option ). - By default the key length is 1536 bits. User authentication is required for + been finalized and a PIN must be installed (i.e. the file for the PIN must + have been created, see option ). + By default the key length is 2048 bits. User authentication is required for this operation. @@ -115,45 +115,53 @@ - value, - value + pin, + pin + puk, + puk - Set value of PIN. If set to - env:VARIABLE, the value of - the environment variable - VARIABLE is used. + + + These options can be used to specify the PIN/PUK values + on the command line. If the value is set to + env:VARIABLE, the value + of the specified environment variable is used. By default, + the code is prompted on the command line if needed. + + + Note that on most operation systems, any user can + display the command line of any process on the + system using utilities such as + ps(1). Therefore, you should prefer + passing the codes via an environment variable + on an unsecured system. + + - value, - value + filename, + filename - set value of PUK (or value of new PIN for change PIN - command see ). If set to - env:VARIABLE, the value of - the environment variable - VARIABLE is used. - - - - - path, - path - - Read the file path from the card. - The file is written on disk with name path. + Read the file filename from the card. + The file is written on disk with name filename. User authentication is required for this operation. - num, - num + arg, + arg - - Use the given reader. The default is the first reader with a card. - + + + Number of the reader to use. By default, the first + reader with a present card is used. If + arg is an ATR, the + reader with a matching card will be chosen. + + @@ -167,6 +175,7 @@ + Causes westcos-tool to be more @@ -184,12 +193,12 @@ - path, - path + filename, + filename - Put the file with name path + Put the file with name filename from disk to card. - On the card the file is written in path. + On the card the file is written in filename. User authentication is required for this operation. diff --git a/etc/DESCHSMCVCA00001 b/etc/DESCHSMCVCA00001 new file mode 100644 index 0000000000..2848cd2be8 Binary files /dev/null and b/etc/DESCHSMCVCA00001 differ diff --git a/etc/Makefile.am b/etc/Makefile.am index 6a0bb3d9f6..302e8500b1 100644 --- a/etc/Makefile.am +++ b/etc/Makefile.am @@ -1,23 +1,23 @@ -CV_CERTS = DESRCACC100001 +CV_CERTS = DESRCACC100001 DESCHSMCVCA00001 MAINTAINERCLEANFILES = $(srcdir)/Makefile.in -DISTCLEANFILES = opensc.conf +DISTCLEANFILES = opensc.conf.example EXTRA_DIST = $(CV_CERTS) Makefile.mak SUFFIXES = .in -dist_noinst_DATA = opensc.conf.in -nodist_noinst_DATA = opensc.conf +dist_noinst_DATA = opensc.conf opensc.conf.example.in +nodist_noinst_DATA = opensc.conf.example # Make sure we build this every time # as there is no dependency for this. -# Can be removed if MSVC is not requried. +# Can be removed if MSVC is not required. force: -opensc.conf: opensc.conf.in force +opensc.conf.example: opensc.conf.example.in force .in: - @sed \ + $(AM_V_GEN)sed \ -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' \ -e 's|@DEBUG_FILE[@]|$(DEBUG_FILE)|g' \ -e 's|@DEFAULT_PCSC_PROVIDER[@]|$(DEFAULT_PCSC_PROVIDER)|g' \ @@ -30,16 +30,19 @@ opensc.conf: opensc.conf.in force -e 's|@PROFILE_DIR_DEFAULT[@]|$(PROFILE_DIR_DEFAULT)|g' \ < $< > $@ -install-exec-hook: opensc.conf +install-exec-hook: opensc.conf.example $(MKDIR_P) "$(DESTDIR)$(sysconfdir)" if [ -f "$(DESTDIR)$(sysconfdir)/opensc.conf" ]; then \ - $(INSTALL_DATA) opensc.conf "$(DESTDIR)$(sysconfdir)/opensc.conf.new"; \ + $(INSTALL_DATA) $(srcdir)/opensc.conf "$(DESTDIR)$(sysconfdir)/opensc.conf.new"; \ else \ - $(INSTALL_DATA) opensc.conf "$(DESTDIR)$(sysconfdir)/opensc.conf"; \ + $(INSTALL_DATA) $(srcdir)/opensc.conf "$(DESTDIR)$(sysconfdir)/opensc.conf"; \ fi + $(MKDIR_P) "$(DESTDIR)$(docdir)" + $(INSTALL_DATA) opensc.conf.example "$(DESTDIR)$(docdir)/opensc.conf"; -uninstall-hook: opensc.conf +uninstall-hook: opensc.conf.example rm -f "$(DESTDIR)$(sysconfdir)/opensc.conf.new" "$(DESTDIR)$(sysconfdir)/opensc.conf" + rm -f "$(DESTDIR)$(docdir)/opensc.conf" if ENABLE_OPENPACE install-data-local: diff --git a/etc/opensc.conf b/etc/opensc.conf new file mode 100644 index 0000000000..8162091961 --- /dev/null +++ b/etc/opensc.conf @@ -0,0 +1,4 @@ +app default { + # debug = 3; + # debug_file = opensc-debug.txt; +} diff --git a/etc/opensc.conf.example.in b/etc/opensc.conf.example.in new file mode 100644 index 0000000000..3781f48a3d --- /dev/null +++ b/etc/opensc.conf.example.in @@ -0,0 +1,1172 @@ +# Configuration file for OpenSC +# Example configuration file + +# NOTE: All key-value pairs must be terminated by a semicolon. + +# Default values for any application +# These can be overridden by an application +# specific configuration block. +app default { + # Amount of debug info to print + # + # A greater value means more debug info. + # Default: 0 + # + #debug = 3; + + # The file to which debug output will be written + # + # Special values 'stdout' and 'stderr' are recognized. + # Default: stderr + # + # debug_file = @DEBUG_FILE@; + + # PKCS#15 initialization / personalization + # profiles directory for pkcs15-init. + # Default: @PROFILE_DIR_DEFAULT@ + # + # profile_dir = @PROFILE_DIR@; + + # Disable pop-ups of built-in GUI + # + # Default: false + # disable_popups = true; + + # Enable default card driver + # Default card driver is explicitly enabled for the 'opensc-explorer' and 'opensc-tool'. + # + # Default: false + # enable_default_driver = true; + + # List of readers to ignore + # If any of the strings listed below is matched in a reader name (case + # sensitive, partial matching possible), the reader is ignored by OpenSC. + # Use `opensc-tool --list-readers` to see all currently connected readers. + # + # Default: empty + # ignored_readers = "CardMan 1021", "SPR 532"; + + # CT-API module configuration. + reader_driver ctapi { + # module @LIBDIR@@LIB_PRE@towitoko@DYN_LIB_EXT@ { + # CT-API ports: + # 0..3 COM1..4 + # 4 Printer + # 5 Modem + # 6..7 LPT1..2 + # ports = 0; + # } + } + + # The following section shows definitions for PC/SC readers. + reader_driver pcsc { + # Limit command and response sizes. Some Readers don't propagate their + # transceive capabilities correctly. max_send_size and max_recv_size + # allow setting the limits manually, for example to enable extended + # length capabilities. + # Default: max_send_size = 255, max_recv_size = 256; + # max_send_size = 65535; + # max_recv_size = 65536; + # + # Connect to reader in exclusive mode? + # Default: false + # connect_exclusive = true; + # + # What to do when disconnecting from a card (SCardDisconnect) + # Valid values: leave, reset, unpower. + # Default: leave + # disconnect_action = reset; + # + # What to do at the end of a transaction (SCardEndTransaction) + # Valid values: leave, reset, unpower. + # Default: leave + # transaction_end_action = reset; + # + # What to do when reconnection to a card (SCardReconnect) + # Valid values: leave, reset, unpower. + # Note that this affects only the internal reconnect (after a SCARD_W_RESET_CARD). + # A forced reset via sc_reset() always does a full powerup. + # Default: leave + # reconnect_action = reset; + # + # Enable pinpad if detected (PC/SC v2.0.2 Part 10) + # Default: true + # enable_pinpad = false; + # + # Some pinpad readers can only handle one exact length of the PIN. + # fixed_pinlength sets this value so that OpenSC expands the padding to + # this length. + # Default: 0 (i.e. not fixed) + # fixed_pinlength = 6; + # + # Detect reader capabilities with escape commands (wrapped APDUs with + # CLA=0xFF as defined by PC/SC pt. 3 and BSI TR-03119, e.g. for getting + # the UID, escaped PIN commands and the reader's firmware version) + # Default: false + # enable_escape = true; + # + # Use specific pcsc provider. + # Default: @DEFAULT_PCSC_PROVIDER@ + # provider_library = @DEFAULT_PCSC_PROVIDER@ + } + + # Options for OpenCT support + reader_driver openct { + # Virtual readers to allocate. + # Default: 2 + # readers = 5; + # + # Limit command and response sizes. + # Default: n/a + # max_send_size = 255; + # max_recv_size = 256; + } + + # Options for CryptoTokenKit support + reader_driver cryptotokenkit { + # Limit command and response sizes. Some Readers don't propagate their + # transceive capabilities correctly. max_send_size and max_recv_size + # allow setting the limits manually, for example to enable extended + # length capabilities. + # Default: autodetect + # max_send_size = 65535; + # max_recv_size = 65536; + } + + # Allowlist of card drivers to load at start-up + # + # The supported internal card driver names can be retrieved + # from the output of: + # $ opensc-tool --list-drivers + # + # A special value of 'old' will load all + # statically linked drivers that may be removed in the future. + # + # A special value of 'internal' will load all + # statically linked drivers. If an unknown (i.e. not + # internal) driver is supplied, a separate configuration + # configuration block has to be written for the driver. + # Default: internal + # NOTE: When "internal" keyword is used, must be last entry + # + #card_drivers = old, internal; + + # Card driver configuration blocks. + + # For card drivers loaded from an external shared library/DLL, + # you need to specify the path name of the module + # + # card_driver customcos { + # The location of the driver library + # module = @LIBDIR@@LIB_PRE@card_customcos@DYN_LIB_EXT@; + # } + + card_driver npa { + # German ID card requires the CAN to be verified before QES PIN. This, + # however, is not part of the PKCS#15 profile of the card. So for + # verifying the QES PIN we actually need both. The CAN may be given + # here. If the CAN is not given here, it will be prompted on the + # command line or on the reader (depending on the reader's + # capabilities). + # + #can = 222222; + + # QES is only possible with a Comfort Reader (CAT-K), which holds a + # cryptographic key to authenticate itself as signature terminal (ST). + # We usually will use the reader's capability to sign the data. + # However, during development you may specify soft certificates and + # keys for a ST below. + # The following example EAC PKI can be found in vicc's example data: + # https://github.com/frankmorgner/vsmartcard/tree/master/virtualsmartcard/npa-example-data + # + #st_dv_certificate = ZZSTDVCA00001.cvcert; + #st_certificate = ZZSTTERM00001.cvcert; + #st_key = ZZSTTERM00001.pkcs8; + } + + # Configuration block for DNIe + # + # Card DNIe has an option to show an extra warning before + # issuing a signature. + + card_driver dnie { + # Disable / enable warning message when performing a + # signature operation with the DNIe. + # Only used if compiled with --enable-dnie-ui + # user_consent_enabled = yes; + + # Specify the pinentry application to use if warning + # is configured to be displayed using pinentry. + # Default: /usr/bin/pinentry + # Only used if compiled with --enable-dnie-ui + # user_consent_app = "/usr/bin/pinentry"; + } + + card_driver edo { + # CAN is required to establish connection + # with the card. It might be overridden by + # EDO_CAN environment variable. Currently, + # it is not possible to set it in any other way. + # + #can = 123456; + } + + card_driver eoi { + # CAN is required to establish connection + # with the card. When using contact reader + # it is read (and decrypted) from the card. + # When using contactless reader, CAN has to + # be specified here or using the EOI_CAN + # environment variable. + #can = 123456; + # + # To hide unneeded slots it's also + # recommended to set + # create_slots_for_pins = "user,sign"; + # in the 'pkcs11' section below + } + + card_driver PIV-II { + # *NOTE* The following are only usable if OpenSC is configured with --enable-piv-sm + + # "piv_pairing_code" + # Virtual Contact Interface (VCI) an optional feature, to allow + # contactless access to card as if it was using the contact reader. + # VCI requires SM. It may also require a pairing code which may be printed on the + # card, given to card owner when card issued or available from the card when used + # with a contact reader. Contact your card issuing agency for details on what your + # card supports. + # Pairing code can be set in opensc.conf or via environment (recommended). + # It is an 8 digit string. + # Default: no pairing code. + # piv_pairing_code = 12345678; + # May be set via environment: PIV_PAIRING_CODE=12345678 + + # Environment variables override opensc.conf + } + + # In addition to the built-in list of known cards in the + # card driver, you can configure a new card for the driver + # using the card_atr block. The goal is to centralize + # everything related to a certain card to card_atr. + # + # The supported internal card driver names can be retrieved + # from the output of: + # $ opensc-tool --list-drivers + + # Generic format: card_atr + + # New card entry for the flex card driver + # card_atr 3b:f0:0d:ca:fe { + # All parameters for the context are + # optional unless specified otherwise. + + # Context: global, card driver + # + # ATR mask value + # + # The mask is logically AND'd with an + # card ATR prior to comparison with the + # ATR reference value above. Using mask + # allows identifying and configuring + # multiple ATRs as the same card model. + # atrmask = "ff:ff:ff:ff:ff"; + + # Context: card driver + # + # Specify used card driver (REQUIRED). + # + # When enabled, overrides all possible + # settings from the card drivers built-in + # card configuration list. + # driver = "flex"; + + # Set card name for card drivers that allows it. + # name = "My CryptoFlex card"; + + # Card type as an integer value. + # + # Depending on card driver, this allows + # tuning the behaviour of the card driver + # for your card. + # type = "2002"; + + # Card flags as an hex value. + # Multiple values are OR'd together. + # + # Depending on card driver, this allows + # fine-tuning the capabilities in + # the card driver for your card. + # + # Optionally, some known parameters + # can be specified as strings: + # + # rng - On-board random number source + # keep_alive - Request the card driver to send a "keep alive" command before each transaction to make sure that the required applet is still selected. + # + # flags = "rng", "keep_alive", "0x80000000"; + + # + # Context: PKCS#15 emulation layer + # + # When using PKCS#15 emulation, force + # the emulation driver for specific cards. + # + # Required for external drivers, but can + # be used with built-in drivers, too. + # pkcs15emu = "custom"; + + # + # Context: reader driver + # + # Force protocol selection for specific cards. + # Known parameters: t0, t1, raw + # force_protocol = "t0"; + + # Context: minidriver + # + # read_only: Mark card as read/only card in Minidriver/BaseCSP interface (Default: false) + # md_supports_X509_enrollment: Indicate X509 enrollment support at Minidriver/BaseCSP interface (Default: false) + # md_guid_as_id: Use the GUID generated for the key as id in the PKCS#15 structure (Default: false, i.e. auto generated) + # md_guid_as_label: Use the GUID generated for the key as label in the PKCS#15 structure (Default: false, i.e. no label set) + # md_supports_container_key_gen: Card allows generating key pairs on the card (Default: false) + # md_supports_container_key_import: Card allows importing private keys (Default: false) + # + # Window title of the PIN pad dialog + # Default: "Windows Security" + # md_pinpad_dlg_title = "Title"; + # + # Filename of the icon for the PIN pad dialog; use "" for no icon + # Default: Built-in smart card icon + # md_pinpad_dlg_icon = ""; + # + # Main instruction of the PIN pad dialog + # Default: "OpenSC Smart Card Provider" + # md_pinpad_dlg_main = "Main"; + # + # Content of the PIN pad dialog for role "user" + # Default: "Please verify your fingerprint or PIN on the card." + # md_pinpad_dlg_content_user = "Content User"; + # + # Content of the PIN pad dialog for role "user+signature" + # Default: "Please verify your fingerprint or PIN for the digital signature PIN on the card." + # md_pinpad_dlg_content_user_sign = "Content User+Sign"; + # + # Content of the PIN pad dialog for role "admin" + # Default: "Please enter your PIN to unblock the user PIN on the PINPAD." + # md_pinpad_dlg_content_admin = "Content Admin"; + # + # Expanded information of the PIN pad dialog + # Default: "This window will be closed automatically after the PIN has been submitted on the PINPAD (timeout typically after 30 seconds)." + # md_pinpad_dlg_expanded = "Expanded Information"; + # + # Allow the user to cancel the PIN pad dialog by not immediately requesting the PIN on the PIN pad + # Default: false + # md_pinpad_dlg_enable_cancel = true; + # + # Content of the verification of the PIN pad dialog + # Default: "Automatically request PIN immediately on PIN-Pad" + # md_pinpad_dlg_verification = "Verification"; + # + # Time in seconds for the progress bar of the PIN pad dialog to tick. "0" removes the progress bar. + # Default: 30 + # md_pinpad_dlg_timeout = 0; + + # Notification title and text when card was inserted + # Default: "Smart card detected" + # notify_card_inserted = "inserted title"; + # Default: ATR of the card + # notify_card_inserted_text = "inserted text"; + # + # Notification title and text when card was removed + # Default: "Smart card removed" + # notify_card_removed = "card removed"; + # Default: Name of smart card reader + # notify_card_removed_text = "removed text"; + # + # Notification title and text when PIN was verified + # Default: "PIN verified" + # notify_pin_good = "good PIN"; + # Default: "Smart card is unlocked" + # notify_pin_good_text = "good text"; + # + # Notification title and text when PIN was wrong + # Default: "PIN not verified" + # notify_pin_bad = "bad PIN"; + # Default: "Smart card is locked" + # notify_pin_bad_text = "bad text"; + # } + + # Yubikey is known to have the PIV applet and the OpenPGP applet. OpenSC + # can handle both to access keys and certificates, but only one at a time. + card_atr 3b:f8:13:00:00:81:31:fe:15:59:75:62:69:6b:65:79:34:d4 { + name = "Yubikey 4"; + # Select the PKI applet to use ("PIV-II" or "openpgp") + driver = "PIV-II"; + # Recover from other applications accessing a different applet + flags = "keep_alive"; + } + card_atr 3b:fc:13:00:00:81:31:fe:15:59:75:62:69:6b:65:79:4e:45:4f:72:33:e1 { + name = "Yubikey Neo"; + # Select the PKI applet to use ("PIV-II" or "openpgp") + driver = "PIV-II"; + # Recover from other applications accessing a different applet + flags = "keep_alive"; + } + card_atr 3b:8c:80:01:59:75:62:69:6b:65:79:4e:45:4f:72:33:58 { + atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:00:00"; + name = "Yubikey Neo"; + # Select the PKI applet to use ("PIV-II" or "openpgp") + driver = "PIV-II"; + # Recover from other applications accessing a different applet + flags = "keep_alive"; + } + + # Oberthur's AuthentIC v3.2.2 + card_atr 3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:00:70:0A:90:00:8B { + type = 11100; + driver = "authentic"; + name = "AuthentIC v3.1"; + + # Name of SM configuration sub-section + # secure_messaging = local_authentic; + } + + # IAS/ECC cards + card_atr 3B:7F:96:00:00:00:31:B9:64:40:70:14:10:73:94:01:80:82:90:00 { + type = 25001; + driver = "iasecc"; + name = "Gemalto MultiApp IAS/ECC v1.0.1"; + secure_messaging = local_gemalto_iam; + # secure_messaging = local_adele; + read_only = false; + md_supports_X509_enrollment = true; + } + card_atr 3B:7F:96:00:00:00:31:B8:64:40:70:14:10:73:94:01:80:82:90:00 { + type = 25001; + driver = "iasecc"; + name = "Gemalto MultiApp IAS/ECC v1.0.1"; + secure_messaging = local_gemalto_iam; + read_only = false; + md_supports_X509_enrollment = true; + } + #card_atr 3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:08:00:07:90:00:FE { + # type = 25002; + # driver = "iasecc"; + # name = "Oberthur IAS/ECC v1.0.1"; + # # No 'admin' application for this card -- no secure messaging + #} + #card_atr 3B:7F:18:00:00:00:31:B8:64:50:23:EC:C1:73:94:01:80:82:90:00 { + # type = 25003; + # driver = "iasecc"; + # name = "Morpho YpsID S3 IAS/ECC"; + # # secure_messaging = local_morpho_YpsID_S3; + #} + #card_atr 3B:DF:96:00:80:31:FE:45:00:31:B8:64:04:1F:EC:C1:73:94:01:80:82:90:00:EC { + # type = 25005; + # driver = "iasecc"; + # name = "Morpho MI IAS/ECC v1.0.1"; + # read_only = false; + # md_supports_X509_enrollment = true; + # secure_messaging = local_morpho_mi; + #} + card_atr 3B:DF:18:FF:81:91:FE:1F:C3:00:31:B8:64:0C:01:EC:C1:73:94:01:80:82:90:00:B3 { + type = 25004; + driver = "iasecc"; + name = "Amos IAS/ECC v1.0.1"; + read_only = false; + md_supports_X509_enrollment = true; + secure_messaging = local_amos; + } + card_atr 3B:DC:18:FF:81:91:FE:1F:C3:80:73:C8:21:13:66:01:0B:03:52:00:05:38 { + type = 25004; + driver = "iasecc"; + name = "Amos IAS/ECC v1.0.1"; + read_only = false; + md_supports_X509_enrollment = true; + secure_messaging = local_amos_eid; + } + + # SmartCard-HSM with contact-based interface or USB-Stick + card_atr 3B:FE:18:00:00:81:31:FE:45:80:31:81:54:48:53:4D:31:73:80:21:40:81:07:FA { + driver = "sc-hsm"; + read_only = false; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + } + + # SmartCard-HSM with contact-less interface + card_atr 3B:8E:80:01:80:31:81:54:48:53:4D:31:73:80:21:40:81:07:18 { + driver = "sc-hsm"; + read_only = false; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + } + + # SmartCard-HSM 4k with contact-based interface or USB-Stick + card_atr 3b:de:18:ff:81:91:fe:1f:c3:80:31:81:54:48:53:4d:31:73:80:21:40:81:07:1c { + driver = "sc-hsm"; + read_only = false; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + } + + # SmartCard-HSM with fingerprint sensor and PIN pad + card_atr 3B:80:80:01:01 { + force_protocol = "t1"; + read_only = true; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + md_pinpad_dlg_main = "Fingerabdruck oder PIN eingeben"; + md_pinpad_dlg_content_user = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN auf der Karte."; + md_pinpad_dlg_content_user_sign = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN für die digitale Signatur auf der Karte."; + md_pinpad_dlg_content_admin = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + md_pinpad_dlg_expanded = "Dieses Fenster wird automatisch geschlossen, wenn die PIN oder der Fingerabdruck verifiziert wurde (Timeout nach 30 Sekunden). Nutzen Sie das PIN-Pad, um die Eingabe abzubrechen."; + md_pinpad_dlg_timeout = 30; + notify_card_inserted = "GoID erkannt"; + notify_card_inserted_text = ""; + notify_card_removed = "GoID entfernt"; + notify_pin_good = "Fingerabdruck bzw. PIN verifiziert"; + notify_pin_good_text = "GoID ist entsperrt"; + notify_pin_bad = "Fingerabdruck bzw. PIN nicht verifiziert"; + notify_pin_bad_text = "GoID ist gesperrt"; + } + + # GoID with fingerprint sensor and PIN pad + card_atr 3B:84:80:01:47:6f:49:44:00 { + atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:00"; + driver = "sc-hsm"; + force_protocol = "t1"; + read_only = true; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + md_pinpad_dlg_main = "Fingerabdruck oder PIN eingeben"; + md_pinpad_dlg_content_user = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN auf der Karte."; + md_pinpad_dlg_content_user_sign = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN für die digitale Signatur auf der Karte."; + md_pinpad_dlg_content_admin = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + md_pinpad_dlg_expanded = "Dieses Fenster wird automatisch geschlossen, wenn die PIN oder der Fingerabdruck verifiziert wurde (Timeout nach 30 Sekunden). Nutzen Sie das PIN-Pad, um die Eingabe abzubrechen."; + md_pinpad_dlg_timeout = 30; + notify_card_inserted = "GoID erkannt"; + notify_card_inserted_text = ""; + notify_card_removed = "GoID entfernt"; + notify_pin_good = "Fingerabdruck bzw. PIN verifiziert"; + notify_pin_good_text = "GoID ist entsperrt"; + notify_pin_bad = "Fingerabdruck bzw. PIN nicht verifiziert"; + notify_pin_bad_text = "GoID ist gesperrt"; + } + # GoID with fingerprint sensor and PIN pad + card_atr 3B:85:80:01:47:6f:49:44:00:00 { + atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:00:00"; + driver = "sc-hsm"; + force_protocol = "t1"; + read_only = true; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + md_pinpad_dlg_main = "Fingerabdruck oder PIN eingeben"; + md_pinpad_dlg_content_user = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN auf der Karte."; + md_pinpad_dlg_content_user_sign = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN für die digitale Signatur auf der Karte."; + md_pinpad_dlg_content_admin = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + md_pinpad_dlg_expanded = "Dieses Fenster wird automatisch geschlossen, wenn die PIN oder der Fingerabdruck verifiziert wurde (Timeout nach 30 Sekunden). Nutzen Sie das PIN-Pad, um die Eingabe abzubrechen."; + md_pinpad_dlg_timeout = 30; + notify_card_inserted = "GoID erkannt"; + notify_card_inserted_text = ""; + notify_card_removed = "GoID entfernt"; + notify_pin_good = "Fingerabdruck bzw. PIN verifiziert"; + notify_pin_good_text = "GoID ist entsperrt"; + notify_pin_bad = "Fingerabdruck bzw. PIN nicht verifiziert"; + notify_pin_bad_text = "GoID ist gesperrt"; + } + # GoID with fingerprint sensor and PIN pad + card_atr 3B:86:80:01:47:6f:49:44:00:00:00 { + atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00"; + driver = "sc-hsm"; + force_protocol = "t1"; + read_only = true; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + md_pinpad_dlg_main = "Fingerabdruck oder PIN eingeben"; + md_pinpad_dlg_content_user = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN auf der Karte."; + md_pinpad_dlg_content_user_sign = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN für die digitale Signatur auf der Karte."; + md_pinpad_dlg_content_admin = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + md_pinpad_dlg_expanded = "Dieses Fenster wird automatisch geschlossen, wenn die PIN oder der Fingerabdruck verifiziert wurde (Timeout nach 30 Sekunden). Nutzen Sie das PIN-Pad, um die Eingabe abzubrechen."; + md_pinpad_dlg_timeout = 30; + notify_card_inserted = "GoID erkannt"; + notify_card_inserted_text = ""; + notify_card_removed = "GoID entfernt"; + notify_pin_good = "Fingerabdruck bzw. PIN verifiziert"; + notify_pin_good_text = "GoID ist entsperrt"; + notify_pin_bad = "Fingerabdruck bzw. PIN nicht verifiziert"; + notify_pin_bad_text = "GoID ist gesperrt"; + } + # GoID with fingerprint sensor and PIN pad + card_atr 3B:87:80:01:47:6f:49:44:00:00:00:00 { + atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00"; + driver = "sc-hsm"; + force_protocol = "t1"; + read_only = true; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + md_pinpad_dlg_main = "Fingerabdruck oder PIN eingeben"; + md_pinpad_dlg_content_user = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN auf der Karte."; + md_pinpad_dlg_content_user_sign = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN für die digitale Signatur auf der Karte."; + md_pinpad_dlg_content_admin = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + md_pinpad_dlg_expanded = "Dieses Fenster wird automatisch geschlossen, wenn die PIN oder der Fingerabdruck verifiziert wurde (Timeout nach 30 Sekunden). Nutzen Sie das PIN-Pad, um die Eingabe abzubrechen."; + md_pinpad_dlg_timeout = 30; + notify_card_inserted = "GoID erkannt"; + notify_card_inserted_text = ""; + notify_card_removed = "GoID entfernt"; + notify_pin_good = "Fingerabdruck bzw. PIN verifiziert"; + notify_pin_good_text = "GoID ist entsperrt"; + notify_pin_bad = "Fingerabdruck bzw. PIN nicht verifiziert"; + notify_pin_bad_text = "GoID ist gesperrt"; + } + # GoID with fingerprint sensor and PIN pad + card_atr 3B:88:80:01:47:6f:49:44:00:00:00:00:00 { + atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00"; + driver = "sc-hsm"; + force_protocol = "t1"; + read_only = true; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + md_pinpad_dlg_main = "Fingerabdruck oder PIN eingeben"; + md_pinpad_dlg_content_user = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN auf der Karte."; + md_pinpad_dlg_content_user_sign = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN für die digitale Signatur auf der Karte."; + md_pinpad_dlg_content_admin = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + md_pinpad_dlg_expanded = "Dieses Fenster wird automatisch geschlossen, wenn die PIN oder der Fingerabdruck verifiziert wurde (Timeout nach 30 Sekunden). Nutzen Sie das PIN-Pad, um die Eingabe abzubrechen."; + md_pinpad_dlg_timeout = 30; + notify_card_inserted = "GoID erkannt"; + notify_card_inserted_text = ""; + notify_card_removed = "GoID entfernt"; + notify_pin_good = "Fingerabdruck bzw. PIN verifiziert"; + notify_pin_good_text = "GoID ist entsperrt"; + notify_pin_bad = "Fingerabdruck bzw. PIN nicht verifiziert"; + notify_pin_bad_text = "GoID ist gesperrt"; + } + # GoID with fingerprint sensor and PIN pad + card_atr 3B:89:80:01:47:6f:49:44:00:00:00:00:00:00 { + atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00"; + driver = "sc-hsm"; + force_protocol = "t1"; + read_only = true; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + md_pinpad_dlg_main = "Fingerabdruck oder PIN eingeben"; + md_pinpad_dlg_content_user = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN auf der Karte."; + md_pinpad_dlg_content_user_sign = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN für die digitale Signatur auf der Karte."; + md_pinpad_dlg_content_admin = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + md_pinpad_dlg_expanded = "Dieses Fenster wird automatisch geschlossen, wenn die PIN oder der Fingerabdruck verifiziert wurde (Timeout nach 30 Sekunden). Nutzen Sie das PIN-Pad, um die Eingabe abzubrechen."; + md_pinpad_dlg_timeout = 30; + notify_card_inserted = "GoID erkannt"; + notify_card_inserted_text = ""; + notify_card_removed = "GoID entfernt"; + notify_pin_good = "Fingerabdruck bzw. PIN verifiziert"; + notify_pin_good_text = "GoID ist entsperrt"; + notify_pin_bad = "Fingerabdruck bzw. PIN nicht verifiziert"; + notify_pin_bad_text = "GoID ist gesperrt"; + } + # GoID with fingerprint sensor and PIN pad + card_atr 3B:8A:80:01:47:6f:49:44:00:00:00:00:00:00:00 { + atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00"; + driver = "sc-hsm"; + force_protocol = "t1"; + read_only = true; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + md_pinpad_dlg_main = "Fingerabdruck oder PIN eingeben"; + md_pinpad_dlg_content_user = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN auf der Karte."; + md_pinpad_dlg_content_user_sign = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN für die digitale Signatur auf der Karte."; + md_pinpad_dlg_content_admin = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + md_pinpad_dlg_expanded = "Dieses Fenster wird automatisch geschlossen, wenn die PIN oder der Fingerabdruck verifiziert wurde (Timeout nach 30 Sekunden). Nutzen Sie das PIN-Pad, um die Eingabe abzubrechen."; + md_pinpad_dlg_timeout = 30; + notify_card_inserted = "GoID erkannt"; + notify_card_inserted_text = ""; + notify_card_removed = "GoID entfernt"; + notify_pin_good = "Fingerabdruck bzw. PIN verifiziert"; + notify_pin_good_text = "GoID ist entsperrt"; + notify_pin_bad = "Fingerabdruck bzw. PIN nicht verifiziert"; + notify_pin_bad_text = "GoID ist gesperrt"; + } + # GoID with fingerprint sensor and PIN pad + card_atr 3B:8B:80:01:47:6f:49:44:00:00:00:00:00:00:00:00 { + atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00:00"; + driver = "sc-hsm"; + force_protocol = "t1"; + read_only = true; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + md_pinpad_dlg_main = "Fingerabdruck oder PIN eingeben"; + md_pinpad_dlg_content_user = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN auf der Karte."; + md_pinpad_dlg_content_user_sign = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN für die digitale Signatur auf der Karte."; + md_pinpad_dlg_content_admin = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + md_pinpad_dlg_expanded = "Dieses Fenster wird automatisch geschlossen, wenn die PIN oder der Fingerabdruck verifiziert wurde (Timeout nach 30 Sekunden). Nutzen Sie das PIN-Pad, um die Eingabe abzubrechen."; + md_pinpad_dlg_timeout = 30; + notify_card_inserted = "GoID erkannt"; + notify_card_inserted_text = ""; + notify_card_removed = "GoID entfernt"; + notify_pin_good = "Fingerabdruck bzw. PIN verifiziert"; + notify_pin_good_text = "GoID ist entsperrt"; + notify_pin_bad = "Fingerabdruck bzw. PIN nicht verifiziert"; + notify_pin_bad_text = "GoID ist gesperrt"; + } + # GoID with fingerprint sensor and PIN pad + card_atr 3B:8C:80:01:47:6f:49:44:00:00:00:00:00:00:00:00:00 { + atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00:00:00"; + driver = "sc-hsm"; + force_protocol = "t1"; + read_only = true; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + md_pinpad_dlg_main = "Fingerabdruck oder PIN eingeben"; + md_pinpad_dlg_content_user = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN auf der Karte."; + md_pinpad_dlg_content_user_sign = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN für die digitale Signatur auf der Karte."; + md_pinpad_dlg_content_admin = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + md_pinpad_dlg_expanded = "Dieses Fenster wird automatisch geschlossen, wenn die PIN oder der Fingerabdruck verifiziert wurde (Timeout nach 30 Sekunden). Nutzen Sie das PIN-Pad, um die Eingabe abzubrechen."; + md_pinpad_dlg_timeout = 30; + notify_card_inserted = "GoID erkannt"; + notify_card_inserted_text = ""; + notify_card_removed = "GoID entfernt"; + notify_pin_good = "Fingerabdruck bzw. PIN verifiziert"; + notify_pin_good_text = "GoID ist entsperrt"; + notify_pin_bad = "Fingerabdruck bzw. PIN nicht verifiziert"; + notify_pin_bad_text = "GoID ist gesperrt"; + } + + # GoID with fingerprint sensor and PIN pad + card_atr 3B:8D:80:01:47:6f:49:44:00:00:00:00:00:00:00:00:00:00 { + atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00:00:00:00"; + driver = "sc-hsm"; + force_protocol = "t1"; + read_only = true; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + md_pinpad_dlg_main = "Fingerabdruck oder PIN eingeben"; + md_pinpad_dlg_content_user = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN auf der Karte."; + md_pinpad_dlg_content_user_sign = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN für die digitale Signatur auf der Karte."; + md_pinpad_dlg_content_admin = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + md_pinpad_dlg_expanded = "Dieses Fenster wird automatisch geschlossen, wenn die PIN oder der Fingerabdruck verifiziert wurde (Timeout nach 30 Sekunden). Nutzen Sie das PIN-Pad, um die Eingabe abzubrechen."; + md_pinpad_dlg_timeout = 30; + notify_card_inserted = "GoID erkannt"; + notify_card_inserted_text = ""; + notify_card_removed = "GoID entfernt"; + notify_pin_good = "Fingerabdruck bzw. PIN verifiziert"; + notify_pin_good_text = "GoID ist entsperrt"; + notify_pin_bad = "Fingerabdruck bzw. PIN nicht verifiziert"; + notify_pin_bad_text = "GoID ist gesperrt"; + } + + # GoID with fingerprint sensor and PIN pad + card_atr 3B:8E:80:01:47:6f:49:44:00:00:00:00:00:00:00:00:00:00:00 { + atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00:00:00:00:00"; + driver = "sc-hsm"; + force_protocol = "t1"; + read_only = true; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + md_pinpad_dlg_main = "Fingerabdruck oder PIN eingeben"; + md_pinpad_dlg_content_user = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN auf der Karte."; + md_pinpad_dlg_content_user_sign = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN für die digitale Signatur auf der Karte."; + md_pinpad_dlg_content_admin = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + md_pinpad_dlg_expanded = "Dieses Fenster wird automatisch geschlossen, wenn die PIN oder der Fingerabdruck verifiziert wurde (Timeout nach 30 Sekunden). Nutzen Sie das PIN-Pad, um die Eingabe abzubrechen."; + md_pinpad_dlg_timeout = 30; + notify_card_inserted = "GoID erkannt"; + notify_card_inserted_text = ""; + notify_card_removed = "GoID entfernt"; + notify_pin_good = "Fingerabdruck bzw. PIN verifiziert"; + notify_pin_good_text = "GoID ist entsperrt"; + notify_pin_bad = "Fingerabdruck bzw. PIN nicht verifiziert"; + notify_pin_bad_text = "GoID ist gesperrt"; + } + + # GoID with fingerprint sensor and PIN pad + card_atr 3B:8F:80:01:47:6f:49:44:00:00:00:00:00:00:00:00:00:00:00:00 { + atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00:00:00:00:00:00"; + driver = "sc-hsm"; + force_protocol = "t1"; + read_only = true; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + md_pinpad_dlg_main = "Fingerabdruck oder PIN eingeben"; + md_pinpad_dlg_content_user = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN auf der Karte."; + md_pinpad_dlg_content_user_sign = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN für die digitale Signatur auf der Karte."; + md_pinpad_dlg_content_admin = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + md_pinpad_dlg_expanded = "Dieses Fenster wird automatisch geschlossen, wenn die PIN oder der Fingerabdruck verifiziert wurde (Timeout nach 30 Sekunden). Nutzen Sie das PIN-Pad, um die Eingabe abzubrechen."; + md_pinpad_dlg_timeout = 30; + notify_card_inserted = "GoID erkannt"; + notify_card_inserted_text = ""; + notify_card_removed = "GoID entfernt"; + notify_pin_good = "Fingerabdruck bzw. PIN verifiziert"; + notify_pin_good_text = "GoID ist entsperrt"; + notify_pin_bad = "Fingerabdruck bzw. PIN nicht verifiziert"; + notify_pin_bad_text = "GoID ist gesperrt"; + } + + # Disable PKCS#1 v1.5 padding in HW. + # Default: decipher + # disable_hw_pkcs1_padding = both; + + secure_messaging local_authentic { + # name of external SM module + # module_name = @DEFAULT_SM_MODULE@; + # directory with external SM module + # Default: @DEFAULT_SM_MODULE_PATH@ + # module_path = @DEFAULT_SM_MODULE_PATH@; + + # specific data to tune the module initialization + # module_data = "Here can be your SM module init data"; + + # SM mode: + # 'transmit' -- in this mode the procedure to securize an APDU is called by the OpenSC general + # APDU transmit procedure. + # In this mode all APDUs, except the ones filtered by the card specific procedure, + # are securized. + # 'acl' -- in this mode APDU are securized only if needed by the ACLs of the command to be executed. + # + #mode = transmit; + + # SM type specific flags + # flags = 0x78; # 0x78 -- level 3, channel 0 + + # Default KMC of the GP Card Manager for the Oberthur's Java cards + # kmc = "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"; + } + + secure_messaging local_gemalto_iam { + module_name = @DEFAULT_SM_MODULE@; + # module_path = @DEFAULT_SM_MODULE_PATH@; + # module_data = ""; + type = acl; # transmit, acl + + ifd_serial = "11:22:33:44:55:66:77:88"; + + # Keyset values from IAM profiles of the Gemalto IAS/ECC cards + keyset_02_enc = "RW_PRIV_ENC_TEST"; + keyset_02_mac = "RW_PRIV_MAC_TEST"; + + keyset_E828BD080FD2504543432D654944_01_enc = "RO_ENC_TEST_KEY_"; + keyset_E828BD080FD2504543432D654944_01_mac = "RO_MAC_TEST_KEY_"; + + keyset_E828BD080FD2504543432D654944_03_enc = "RW_PUBL_ENC_TEST"; + keyset_E828BD080FD2504543432D654944_03_mac = "RW_PUBL_MAC_TEST"; + } + + secure_messaging local_amos { + module_name = @DEFAULT_SM_MODULE@; + # module_path = @DEFAULT_SM_MODULE_PATH@; + # module_data = ""; + mode = acl; + ifd_serial = "11:22:33:44:55:66:77:88"; + keyset_02_enc = "ENCROECHANTILLON"; + keyset_02_mac = "MACROECHANTILLON"; + } + + secure_messaging local_amos_eid { + module_name = @DEFAULT_SM_MODULE@; + # module_path = @DEFAULT_SM_MODULE_PATH@; + # module_data = ""; + mode = acl; + ifd_serial = "11:22:33:44:55:66:77:88"; + keyset_E828BD080FD2504543432D654944_03_enc = "RW_PUBL_ENC_TEST"; + keyset_E828BD080FD2504543432D654944_03_mac = "RW_PUBL_MAC_TEST"; + } + + secure_messaging local_adele { + module_name = @DEFAULT_SM_MODULE@; + # module_path = @DEFAULT_SM_MODULE_PATH@; + # module_data = ""; + type = acl; # transmit, acl + + ifd_serial = "11:22:33:44:55:66:77:88"; + + # Keyset values from 'Adele' profiles of the IAS/ECC cards + keyset_01_enc = "EMENCECHANTILLON"; + keyset_01_mac = "EMMACECHANTILLON"; + + keyset_02_enc = "AAENCECHANTILLON"; + keyset_02_mac = "AAMACECHANTILLON"; + + keyset_E828BD080FD2500000040301_02_enc = "E2ENCECHANTILLON"; + keyset_E828BD080FD2500000040301_02_mac = "E2MACECHANTILLON"; + + keyset_D2500000044164E86C650101_02_enc = "E1ENCECHANTILLON"; + keyset_D2500000044164E86C650101_02_mac = "E1MACECHANTILLON"; + + keyset_D2500000044164E86C650101_03_enc = "SIENCECHANTILLON"; + keyset_D2500000044164E86C650101_03_mac = "SIMACECHANTILLON"; + } + + # Below are the framework specific configuration blocks. + + # PKCS #15 + framework pkcs15 { + # Whether to use the cache files in the user's + # home directory. + # + # Note: If caching is done by a system process, caching may be placed + # inaccessible from the user account. Use a global caching directory if + # you wish to share the cached information. + # + # Default: `public` for static cards, `false` otherwise + # use_file_caching = public; + # + # set a path for caching + # so you do not use the env variables and for pam_pkcs11 + # (with certificate check) where $HOME is not set + # Default: path in user home + # file_cache_dir = /var/lib/opensc/cache + + # Use PIN caching? + # Default: true + # use_pin_caching = false; + # + # How many times to use a PIN from cache before re-authenticating it? + # Default: 10 + # pin_cache_counter = 3; + # + # Older PKCS#11 applications not supporting CKA_ALWAYS_AUTHENTICATE + # may need to set this to get signatures to work with some cards. + # Default: false + # It is recommended to enable also use_pin_caching to allow OpenSC + # to pass the pin to the card when needed. + # pin_cache_ignore_user_consent = true; + + # How to handle a PIN-protected certificate + # Valid values: protect, declassify, ignore. + # Default: protect + # pin_protected_certificate = declassify; + + # Enable pkcs15 emulation. + # Default: yes + # enable_pkcs15_emulation = no; + # + # Prefer pkcs15 emulation code before + # the normal pkcs15 processing. + # Some cards (like pteid) work in emu-only mode, + # and do not depend on this option. + # + # Default: no + # try_emulation_first = yes; + + # Enable builtin emulators. + # Default: yes + # enable_builtin_emulation = no; + # + # List of the builtin pkcs15 emulators to test + # Special value of 'internal' will try all not disabled builtin pkcs15 emulators. + # Special value of 'old' will try all disabled pkcs15 emulators. + # Default: internal; + # builtin_emulators = old, internal; + + # additional settings per driver + # + # For pkcs15 emulators loaded from an external shared + # library/DLL, you need to specify the path name of the module + # and customize the card_atr example above correctly. + # + # emulate custom { + # The location of the driver library + # module = @LIBDIR@@LIB_PRE@p15emu_custom@DYN_LIB_EXT@; + # } + + # Enable initialization and card recognition in PKCS#11 layer. + # Default: no + # pkcs11_enable_InitToken = yes; + + # some additional application parameters: + # - type (generic, protected) used to distinguish the common access application + # and application for which authentication to perform some operation cannot be + # obtained with the common procedures (ex. object creation protected by secure messaging). + # Used by PKCS#11 module configured to expose restricted number of slots. + # (for ex. configured to expose only User PIN slot, User and Sign PINs slots, ...) + # + # - disable: do not expose application in PKCS15 framework + # default 'false' + # - user_pin and sign_pin: labels of PIN objects that will be used as + # user & sign PINs. Otherwise the first suitable one will be used. + application E828BD080FD25047656E65726963 { + type = generic; + model = "ECC Generic PKI"; + # disable = true + } + + application E828BD080FD2500000040301 { + type = generic; + model = "Adèle Générique"; + } + + application E828BD080FD2504543432D654944 { + type = protected; + model = "ECC eID"; + } + + application E828BD080FD2500000040201 { + type = protected; + model = "Adèle Admin-2"; + } + + # Slovenian eID - low level (pinless, "Prijava brez PIN-a") + application E828BD080F014E585031 { + model = "ChipDocLite"; + disable = true; + user_pin = "Norm PIN"; + } + + # Slovenian eID - high level (QES, "Podpis in prijava") + application E828BD080F014E585030 { + model = "ChipDocLite"; + # disable = true; + user_pin = "Norm PIN"; + sign_pin = "Sig PIN"; + } + } +} + +# Parameters for the OpenSC PKCS11 module +app opensc-pkcs11 { + pkcs11 { + # Maximum Number of virtual slots. + # If there are more slots than defined here, + # the remaining slots will be hidden from PKCS#11. + # Default: 16 + # max_virtual_slots = 32; + + # Maximum number of slots per smart card. + # If the card has fewer keys than defined here, + # the remaining number of slots will be empty. + # Default: 4 + # slots_per_card = 2; + + # (max_virtual_slots/slots_per_card) limits the number of readers + # that can be used on the system. Default is then 16/4=4 readers. + + # By default, the OpenSC PKCS#11 module will not lock your card + # once you authenticate to the card via C_Login. + # + # Thus the other users or other applications is not prevented + # from connecting to the card and perform crypto operations + # (which may be possible because you have already authenticated + # with the card). This setting is not very secure. + # + # Also, if your card is not locked, you can enconter problems + # due to limitation of the OpenSC framework, that still is not + # thoroughly tested in the multi threads environment. + # + # Your settings will be more secure if you choose to lock your + # card. Nevertheless this behavior is a known violation of PKCS#11 + # specification. Now once one application has started using your + # card with C_Login, no other application can use it, until + # the first is done and calls C_Logout or C_Finalize. In the case + # of many PKCS#11 application this does not happen until you exit + # the application. + # Thus it is impossible to use several smart card aware applications + # at the same time, e.g. you cannot run both Firefox and Thunderbird at + # the same time, if both are configured to use your smart card. + # + # Default: false + # lock_login = true; + + # By default, interacting with the OpenSC PKCS#11 module may change the + # state of the token, e.g. whether a user is logged in or not. + # + # Thus other users or other applications may change or use the state of + # the token unknowingly. Other applications may create signatures + # abusing an existing login or they may logout unnoticed. + # + # With this setting enabled the login state of the token is tracked and + # cached (including the PIN). Every transaction is preceded by + # restoring the login state. After every transaction a logout is + # performed. This setting by default also enables `lock_login` (see + # above) to disable access for other applications during the atomic + # transactions. + # + # Please note that any PIN-pad should be disabled (see `enable_pinpad` + # above), because the user would have to input his PIN for every + # transaction. + # + # Default: false + # atomic = true; + + # With this setting disabled, the OpenSC PKCS#11 module will initialize + # the slots available when the application calls `C_GetSlotList`. With + # this setting enabled, the slots will also get initialized when + # C_GetSlotInfo is called. + # + # This setting is a workaround for Java which does not call + # `C_GetSlotList` when configured with a static `slot` instead of + # `slotListIndex`. + # + # Default: true + # init_sloppy = false; + + # User PIN unblock style + # none: PIN unblock is not possible with PKCS#11 API; + # set_pin_in_unlogged_session: C_SetPIN() in unlogged session: + # PUK is passed as the 'OldPin' argument of the C_SetPIN() call. + # set_pin_in_specific_context: C_SetPIN() in the CKU_SPECIFIC_CONTEXT logged session: + # PUK is passed as the 'OldPin' argument of the C_SetPIN() call. + # init_pin_in_so_session: C_InitPIN() in CKU_SO logged session: + # User PIN 'UNBLOCK' is protected by SOPIN. (PUK == SOPIN). + # # Actually this style works only for the PKCS15 contents without SOPIN. + # # For those with SOPIN, this mode will be useful for the cards without + # # modes 00 and 01 of ISO command 'RESET RETRY COUNTER'. --vt + # + # Default: none + # user_pin_unblock_style = set_pin_in_unlogged_session; + + # Create slot for unblocking PIN with PUK + # This way PKCS#11 API can be used to login with PUK and + # change a PIN. + # Warning: causes problems with some applications like + # firefox and thunderbird. Thus turned off by default + # + # Default: false + # create_puk_slot = true; + + # Symbolic names of PINs for which slots are created + # Card can contain more then one PINs or more then one on-card application with + # its own PINs. Normally, to access all of them with the PKCS#11 API a slot has to be + # created for all of them. Many slots could be ennoying for some of widely used application, + # like FireFox. This configuration parameter allows one to select the PIN(s) + # for which PKCS#11 slot will be created. + # Actually recognised following symbolic names: + # 'user', 'sign', 'all' + # Only PINs initialised, non-SoPIN, non-unblocking are associated with symbolic name. + # 'user' is identified as first global or first local PIN. + # 'sign' is identified as second PIN: first local, second global or second local. + # 'all' slot created for all non-sopin, non-unblocking PINs, + # optionally for PUK (see option 'create_puk_slot') + # + # Default: all + # create_slots_for_pins = "user,sign"; + # create_slots_for_pins = "sign"; + # + # For the module to simulate the opensc-onepin module behavior the following option + # must be set: + # create_slots_for_pins = "user" + } +} + +app "C:\Program Files\BraveSoftware\Brave-Browser\Application\brave.exe" { + pkcs11 { + slots_per_card = 1; + } +} + +# Used by OpenSC minidriver on Windows only +app cardmod { +} diff --git a/etc/opensc.conf.in b/etc/opensc.conf.in deleted file mode 100644 index 63abba9bf3..0000000000 --- a/etc/opensc.conf.in +++ /dev/null @@ -1,790 +0,0 @@ -# Configuration file for OpenSC -# Example configuration file - -# NOTE: All key-value pairs must be terminated by a semicolon. - -# Default values for any application -# These can be overridden by an application -# specific configuration block. -app default { - # Amount of debug info to print - # - # A greater value means more debug info. - # Default: 0 - # - #debug = 3; - - # The file to which debug output will be written - # - # Special values 'stdout' and 'stderr' are recognized. - # Default: stderr - # - # debug_file = @DEBUG_FILE@ - - # Reopen debug file at the every debug message. - # - # For Windows it's forced to 'true'. The reason is that - # in Windows file handles can not be shared between DLL-s, - # each DLL has a separate file handle table. - # - # Default: false - # reopen_debug_file = true; - - # PKCS#15 initialization / personalization - # profiles directory for pkcs15-init. - # Default: @PROFILE_DIR_DEFAULT@ - # - # profile_dir = @PROFILE_DIR@; - - # Paranoid memory allocation. - # - # If set to 'true', then refuse to continue when locking of non-pageable - # memory fails. This can cause subtle failures but is more secure when - # you have a swap disk. - # Default: false - # - # paranoid_memory = false; - - # Dsiable pop-ups of built-in GUI - # - # Default: false - # disable_popups = true; - - # Enable default card driver - # Default card driver is explicitely enabled for the 'opensc-explorer' and 'opensc-tool'. - # - # Default: false - # enable_default_driver = true; - - # CT-API module configuration. - reader_driver ctapi { - # module @LIBDIR@@LIB_PRE@towitoko@DYN_LIB_EXT@ { - # CT-API ports: - # 0..3 COM1..4 - # 4 Printer - # 5 Modem - # 6..7 LPT1..2 - # ports = 0; - # } - } - - # The following section shows definitions for PC/SC readers. - reader_driver pcsc { - # Limit command and response sizes. Some Readers don't propagate their - # transceive capabilities correctly. max_send_size and max_recv_size - # allow setting the limits manually, for example to enable extended - # length capabilities. - # Default: max_send_size = 255, max_recv_size = 256; - # max_send_size = 65535; - # max_recv_size = 65536; - # - # Connect to reader in exclusive mode? - # Default: false - # connect_exclusive = true; - # - # What to do when disconnecting from a card (SCardDisconnect) - # Valid values: leave, reset, unpower. - # Default: reset - # disconnect_action = unpower; - # - # What to do at the end of a transaction (SCardEndTransaction) - # Valid values: leave, reset, unpower. - # Default: leave - # transaction_end_action = reset; - # - # What to do when reconnection to a card (SCardReconnect) - # Valid values: leave, reset, unpower. - # Note that this affects only the internal reconnect (after a SCARD_W_RESET_CARD). - # A forced reset via sc_reset() always does a full powerup. - # Default: leave - # reconnect_action = reset; - # - # Enable pinpad if detected (PC/SC v2.0.2 Part 10) - # Default: true - # enable_pinpad = false; - # - # Detect reader capabilities with escape commands (wrapped APDUs with - # CLA=0xFF as defined by PC/SC pt. 3 and BSI TR-03119, e.g. for getting - # the UID, escaped PIN commands and the reader's firmware version) - # Default: false - # enable_escape = true; - # - # Use specific pcsc provider. - # Default: @DEFAULT_PCSC_PROVIDER@ - # provider_library = @DEFAULT_PCSC_PROVIDER@ - } - - # Options for OpenCT support - reader_driver openct { - # Virtual readers to allocate. - # Default: 2 - # readers = 5; - # - # Limit command and response sizes. - # Default: n/a - # max_send_size = 255; - # max_recv_size = 256; - } - - # Options for CryptoTokenKit support - reader_driver cryptotokenkit { - # Limit command and response sizes. Some Readers don't propagate their - # transceive capabilities correctly. max_send_size and max_recv_size - # allow setting the limits manually, for example to enable extended - # length capabilities. - # Default: autodetect - # max_send_size = 65535; - # max_recv_size = 65536; - } - - # Whitelist of card drivers to load at start-up - # - # The supported internal card driver names can be retrieved - # from the output of: - # $ opensc-tool --list-drivers - # - # A special value of 'internal' will load all - # statically linked drivers. If an unknown (ie. not - # internal) driver is supplied, a separate configuration - # configuration block has to be written for the driver. - # Default: internal - # NOTE: When "internal" keyword is used, must be last entry - # - card_drivers = npa, internal; - - # Card driver configuration blocks. - - # For card drivers loaded from an external shared library/DLL, - # you need to specify the path name of the module - # - # card_driver customcos { - # The location of the driver library - # module = @LIBDIR@@LIB_PRE@card_customcos@DYN_LIB_EXT@; - # } - - card_driver npa { - # German ID card requires the CAN to be verified before QES PIN. This, - # however, is not part of the PKCS#15 profile of the card. So for - # verifying the QES PIN we actually need both. The CAN may be given - # here. If the CAN is not given here, it will be prompted on the - # command line or on the reader (depending on the reader's - # capabilities). - # - #can = 222222; - - # QES is only possible with a Comfort Reader (CAT-K), which holds a - # cryptographic key to authenticate itself as signature terminal (ST). - # We usually will use the reader's capability to sign the data. - # However, during developement you may specify soft certificates and - # keys for a ST below. - # The following example EAC PKI can be found in vicc's example data: - # https://github.com/frankmorgner/vsmartcard/tree/master/virtualsmartcard/npa-example-data - # - #st_dv_certificate = ZZSTDVCA00001.cvcert; - #st_certificate = ZZSTTERM00001.cvcert; - #st_key = ZZSTTERM00001.pkcs8; - } - - # Force using specific card driver - # - # If this option is present, OpenSC will use the supplied - # driver with all inserted cards. - # - # Default: autodetect - # - # force_card_driver = customcos; - - # Configuration block for DNIe - # - # Card DNIe has an option to show an extra warning before - # issuing a signature. - - card_driver dnie { - # Disable / enable warning message when performing a - # signature operation with the DNIe. - # Only used if compiled with --enable-dnie-ui - # user_consent_enabled = yes; - - # Specify the pinentry application to use if warning - # is configured to be displayed using pinentry. - # Default: /usr/bin/pinentry - # Only used if compiled with --enable-dnie-ui - # user_consent_app = "/usr/bin/pinentry"; - } - - # In addition to the built-in list of known cards in the - # card driver, you can configure a new card for the driver - # using the card_atr block. The goal is to centralize - # everything related to a certain card to card_atr. - # - # The supported internal card driver names can be retrieved - # from the output of: - # $ opensc-tool --list-drivers - - # Generic format: card_atr - - # New card entry for the flex card driver - # card_atr 3b:f0:0d:ca:fe { - # All parameters for the context are - # optional unless specified otherwise. - - # Context: global, card driver - # - # ATR mask value - # - # The mask is logically AND'd with an - # card ATR prior to comparison with the - # ATR reference value above. Using mask - # allows identifying and configuring - # multiple ATRs as the same card model. - # atrmask = "ff:ff:ff:ff:ff"; - - # Context: card driver - # - # Specify used card driver (REQUIRED). - # - # When enabled, overrides all possible - # settings from the card drivers built-in - # card configuration list. - # driver = "flex"; - - # Set card name for card drivers that allows it. - # name = "My CryptoFlex card"; - - # Card type as an integer value. - # - # Depending on card driver, this allows - # tuning the behaviour of the card driver - # for your card. - # type = "2002"; - - # Card flags as an hex value. - # Multiple values are OR'd together. - # - # Depending on card driver, this allows - # fine-tuning the capabilities in - # the card driver for your card. - # - # Optionally, some known parameters - # can be specified as strings: - # - # rng - On-board random number source - # - # flags = "rng", "0x80000000"; - - # Enable pkcs11 initialization. - # Default: no - # pkcs11_enable_InitToken = yes; - - # - # Context: PKCS#15 emulation layer - # - # When using PKCS#15 emulation, force - # the emulation driver for specific cards. - # - # Required for external drivers, but can - # be used with built-in drivers, too. - # pkcs15emu = "custom"; - - # - # Context: reader driver - # - # Force protocol selection for specific cards. - # Known parameters: t0, t1, raw - # force_protocol = "t0"; - - # Context: minidriver - # - # md_read_only: Mark card as read/only card in Minidriver/BaseCSP interface (Default: false) - # md_supports_X509_enrollment: Indicate X509 enrollment support at Minidriver/BaseCSP interface (Default: false) - # md_guid_as_id: Use the GUID generated for the key as id in the PKCS#15 structure (Default: false - auto generated) - # md_guid_as_label: Use the GUID generated for the key as label in the PKCS#15 structure (Default: false - no label set) - # md_supports_container_key_gen: Card allows generating key pairs on the card (Default: false) - # md_supports_container_key_import: Card allows importing private keys (Default: false) - # } - - # PIV cards need an entry similar to this one: - # card_atr 3B:7D:96:00:00:80:31:80:65:B0:83:11:00:AC:83:00:90:00 { - # name = "PIV-II"; - # driver = "piv"; - # } - - # Estonian ID card and Micardo driver sometimes only play together with T=0 - # In theory only the 'cold' ATR should be specified, as T=0 will - # be the preferred protocol once you boot it up with T=0, but be - # paranoid. - # - # Warm ATR v1 - card_atr 3b:6e:00:ff:45:73:74:45:49:44:20:76:65:72:20:31:2e:30 { - force_protocol = t0; - } - # Cold ATR v1 - card_atr 3b:fe:94:00:ff:80:b1:fa:45:1f:03:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:43 { - force_protocol = t0; - } - # Warm ATR v2 - card_atr 3b:5e:11:ff:45:73:74:45:49:44:20:76:65:72:20:31:2e:30 { - force_protocol = t0; - } - # Cold ATR v2 - card_atr 3b:de:18:ff:c0:80:b1:fe:45:1f:03:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:2b { - force_protocol = t0; - } - # Digi-ID cold ATR. The same card has the same warm ATR as "Cold ATR v1" above - # The card is claimed to only support T=0 but in fact (sometimes) works with T=1, even if not advertised in ATR. - card_atr 3b:6e:00:00:45:73:74:45:49:44:20:76:65:72:20:31:2e:30 { - force_protocol = t0; - } - - # D-Trust cards are also based on micardo and need T=0 for some reason - card_atr 3b:ff:94:00:ff:80:b1:fe:45:1f:03:00:68:d2:76:00:00:28:ff:05:1e:31:80:00:90:00:23 { - force_protocol = t0; - } - card_atr 3b:ff:11:00:ff:80:b1:fe:45:1f:03:00:68:d2:76:00:00:28:ff:05:1e:31:80:00:90:00:a6 { - force_protocol = t0; - } - - # Oberthur's AuthentIC v3.2.2 - card_atr 3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:00:70:0A:90:00:8B { - type = 11100; - driver = "authentic"; - name = "AuthentIC v3.1"; - - # Name of SM configuration sub-section - # secure_messaging = local_authentic; - } - - # IAS/ECC cards - card_atr 3B:7F:96:00:00:00:31:B9:64:40:70:14:10:73:94:01:80:82:90:00 { - type = 25001; - driver = "iasecc"; - name = "Gemalto MultiApp IAS/ECC v1.0.1"; - secure_messaging = local_gemalto_iam; - # secure_messaging = local_adele; - md_read_only = false; - md_supports_X509_enrollment = true; - } - card_atr 3B:7F:96:00:00:00:31:B8:64:40:70:14:10:73:94:01:80:82:90:00 { - type = 25001; - driver = "iasecc"; - name = "Gemalto MultiApp IAS/ECC v1.0.1"; - secure_messaging = local_gemalto_iam; - md_read_only = false; - md_supports_X509_enrollment = true; - } - #card_atr 3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:08:00:07:90:00:FE { - # type = 25002; - # driver = "iasecc"; - # name = "Oberthur IAS/ECC v1.0.1"; - # # No 'admin' application for this card -- no secure messaging - #} - #card_atr 3B:7F:18:00:00:00:31:B8:64:50:23:EC:C1:73:94:01:80:82:90:00 { - # type = 25003; - # driver = "iasecc"; - # name = "Morpho YpsID S3 IAS/ECC"; - # # secure_messaging = local_morpho_YpsID_S3; - #} - #card_atr 3B:DF:96:00:80:31:FE:45:00:31:B8:64:04:1F:EC:C1:73:94:01:80:82:90:00:EC { - # type = 25005; - # driver = "iasecc"; - # name = "Morpho MI IAS/ECC v1.0.1"; - # md_read_only = false; - # md_supports_X509_enrollment = true; - # secure_messaging = local_morpho_mi; - #} - card_atr 3B:DF:18:FF:81:91:FE:1F:C3:00:31:B8:64:0C:01:EC:C1:73:94:01:80:82:90:00:B3 { - type = 25004; - driver = "iasecc"; - name = "Amos IAS/ECC v1.0.1"; - md_read_only = false; - md_supports_X509_enrollment = true; - secure_messaging = local_amos; - } - card_atr 3B:DC:18:FF:81:91:FE:1F:C3:80:73:C8:21:13:66:01:0B:03:52:00:05:38 { - type = 25004; - driver = "iasecc"; - name = "Amos IAS/ECC v1.0.1"; - md_read_only = false; - md_supports_X509_enrollment = true; - secure_messaging = local_amos_eid; - } - - # SmartCard-HSM with contact-based interface or USB-Stick - card_atr 3B:FE:18:00:00:81:31:FE:45:80:31:81:54:48:53:4D:31:73:80:21:40:81:07:FA { - driver = "sc-hsm"; - md_read_only = false; - md_supports_X509_enrollment = true; - md_supports_container_key_gen = true; - md_guid_as_label = true; - } - - # SmartCard-HSM with contact-less interface - card_atr 3B:8E:80:01:80:31:81:54:48:53:4D:31:73:80:21:40:81:07:18 { - driver = "sc-hsm"; - md_read_only = false; - md_supports_X509_enrollment = true; - md_supports_container_key_gen = true; - md_guid_as_label = true; - } - - secure_messaging local_authentic { - # name of external SM module - # module_name = @DEFAULT_SM_MODULE@; - # directory with external SM module - # Default: defined by windows register - @DEFAULT_SM_MODULE_PATH@ - - # specific data to tune the module initialization - # module_data = "Here can be your SM module init data"; - - # SM mode: - # 'transmit' -- in this mode the procedure to securize an APDU is called by the OpenSC general - # APDU transmit procedure. - # In this mode all APDUs, except the ones filtered by the card specific procedure, - # are securized. - # 'acl' -- in this mode APDU are securized only if needed by the ACLs of the command to be executed. - # - #mode = transmit; - - # SM type specific flags - # flags = 0x78; # 0x78 -- level 3, channel 0 - - # Default KMC of the GP Card Manager for the Oberthur's Java cards - # kmc = "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"; - } - - secure_messaging local_gemalto_iam { - module_name = @DEFAULT_SM_MODULE@; - @DEFAULT_SM_MODULE_PATH@ - # module_data = ""; - type = acl; # transmit, acl - - ifd_serial = "11:22:33:44:55:66:77:88"; - - # Keyset values from IAM profiles of the Gemalto IAS/ECC cards - keyset_02_enc = "RW_PRIV_ENC_TEST"; - keyset_02_mac = "RW_PRIV_MAC_TEST"; - - keyset_E828BD080FD2504543432D654944_01_enc = "RO_ENC_TEST_KEY_"; - keyset_E828BD080FD2504543432D654944_01_mac = "RO_MAC_TEST_KEY_"; - - keyset_E828BD080FD2504543432D654944_03_enc = "RW_PUBL_ENC_TEST"; - keyset_E828BD080FD2504543432D654944_03_mac = "RW_PUBL_MAC_TEST"; - } - - secure_messaging local_amos { - module_name = @DEFAULT_SM_MODULE@; - @DEFAULT_SM_MODULE_PATH@ - # module_data = ""; - mode = acl; - ifd_serial = "11:22:33:44:55:66:77:88"; - keyset_02_enc = "ENCROECHANTILLON"; - keyset_02_mac = "MACROECHANTILLON"; - } - - secure_messaging local_amos_eid { - module_name = @DEFAULT_SM_MODULE@; - @DEFAULT_SM_MODULE_PATH@ - # module_data = ""; - mode = acl; - ifd_serial = "11:22:33:44:55:66:77:88"; - keyset_E828BD080FD2504543432D654944_03_enc = "RW_PUBL_ENC_TEST"; - keyset_E828BD080FD2504543432D654944_03_mac = "RW_PUBL_MAC_TEST"; - } - - secure_messaging local_adele { - module_name = @DEFAULT_SM_MODULE@; - @DEFAULT_SM_MODULE_PATH@ - # module_data = ""; - type = acl; # transmit, acl - - ifd_serial = "11:22:33:44:55:66:77:88"; - - # Keyset values from 'Adele' profiles of the IAS/ECC cards - keyset_01_enc = "EMENCECHANTILLON"; - keyset_01_mac = "EMMACECHANTILLON"; - - keyset_02_enc = "AAENCECHANTILLON"; - keyset_02_mac = "AAMACECHANTILLON"; - - keyset_E828BD080FD2500000040301_02_enc = "E2ENCECHANTILLON"; - keyset_E828BD080FD2500000040301_02_mac = "E2MACECHANTILLON"; - - keyset_D2500000044164E86C650101_02_enc = "E1ENCECHANTILLON"; - keyset_D2500000044164E86C650101_02_mac = "E1MACECHANTILLON"; - - keyset_D2500000044164E86C650101_03_enc = "SIENCECHANTILLON"; - keyset_D2500000044164E86C650101_03_mac = "SIMACECHANTILLON"; - } - - # Below are the framework specific configuration blocks. - - # PKCS #15 - framework pkcs15 { - # Whether to use the cache files in the user's - # home directory. - # - # At the moment you have to 'teach' the card - # to the system by running command: pkcs15-tool -L - # - # WARNING: Caching shouldn't be used in setuid root - # applications. - # Default: false - # use_file_caching = true; - # - # set a path for caching - # so you do not use the env variables and for pam_pkcs11 - # (with certificate check) where $HOME is not set - # Default: path in user home - # file_cache_dir = /var/lib/opensc/cache - # - # Use PIN caching? - # Default: true - # use_pin_caching = false; - # - # How many times to use a PIN from cache before re-authenticating it? - # Default: 10 - # pin_cache_counter = 3; - # - # Older PKCS#11 applications not supporting CKA_ALWAYS_AUTHENTICATE - # may need to set this to get signatures to work with some cards. - # Default: false - # pin_cache_ignore_user_consent = true; - # - # Enable pkcs15 emulation. - # Default: yes - # enable_pkcs15_emulation = no; - # - # Prefer pkcs15 emulation code before - # the normal pkcs15 processing. - # Some cards (like esteid and pteid) work in emu-only mode, - # and do not depend on this option. - # - # Default: no - # try_emulation_first = yes; - - # Enable builtin emulators. - # Default: yes - # enable_builtin_emulation = no; - # - # List of the builtin pkcs15 emulators to test - # Default: esteid, openpgp, tcos, starcert, itacns, infocamere, postecert, actalis, atrust-acos, gemsafeGPK, gemsafeV1, tccardos, PIV-II; - # builtin_emulators = openpgp; - - # additional settings per driver - # - # For pkcs15 emulators loaded from an external shared - # library/DLL, you need to specify the path name of the module - # and customize the card_atr example above correctly. - # - # emulate custom { - # The location of the driver library - # module = @LIBDIR@@LIB_PRE@p15emu_custom@DYN_LIB_EXT@; - # } - - # some additional application parameters: - # - type (generic, protected) used to distinguish the common access application - # and application for which authentication to perform some operation cannot be - # obtained with the common procedures (ex. object creation protected by secure messaging). - # Used by PKCS#11 module configurated to expose restricted number of slots. - # (for ex. configurated to expose only User PIN slot, User and Sign PINs slots, ...) - # - # - disable: do not expose application in PKCS15 framework - # default 'false' - application E828BD080FD25047656E65726963 { - type = generic; - model = "ECC Generic PKI"; - # disable = true - } - - application E828BD080FD2500000040301 { - type = generic; - model = "Adèle Générique"; - } - - application E828BD080FD2504543432D654944 { - type = protected; - model = "ECC eID"; - } - - application E828BD080FD2500000040201 { - type = protected; - model = "Adèle Admin-2"; - } - } -} - -# Parameters for the OpenSC PKCS11 module -app opensc-pkcs11 { - pkcs11 { - # Maximum Number of virtual slots. - # If there are more slots than defined here, - # the remaining slots will be hidden from PKCS#11. - # Default: 16 - # max_virtual_slots = 32; - - # Maximum number of slots per smart card. - # If the card has fewer keys than defined here, - # the remaining number of slots will be empty. - # Default: 4 - # slots_per_card = 2; - - # (max_virtual_slots/slots_per_card) limits the number of readers - # that can be used on the system. Default is then 16/4=4 readers. - - # Normally, the pkcs11 module will create - # the full number of slots defined above by - # num_slots. If there are fewer pins/keys on - # the card, the remaining keys will be empty - # (and you will be able to create new objects - # within them). - # Default: true - # hide_empty_tokens = false; - - # By default, the OpenSC PKCS#11 module will not lock your card - # once you authenticate to the card via C_Login. - # - # Thus the other users or other applications is not prevented - # from connecting to the card and perform crypto operations - # (which may be possible because you have already authenticated - # with the card). This setting is not very secure. - # - # Also, if your card is not locked, you can enconter problems - # due to limitation of the OpenSC framework, that still is not - # thoroughly tested in the multi threads environment. - # - # Your settings will be more secure if you choose to lock your - # card. Nevertheless this behavior is a known violation of PKCS#11 - # specification. Now once one application has started using your - # card with C_Login, no other application can use it, until - # the first is done and calls C_Logout or C_Finalize. In the case - # of many PKCS#11 application this does not happen until you exit - # the application. - # Thus it is impossible to use several smart card aware applications - # at the same time, e.g. you cannot run both Firefox and Thunderbird at - # the same time, if both are configured to use your smart card. - # - # Default: false - # lock_login = true; - - # By default, interacting with the OpenSC PKCS#11 module may change the - # state of the token, e.g. whether a user is logged in or not. - # - # Thus other users or other applications may change or use the state of - # the token unknowingly. Other applications may create signatures - # abusing an existing login or they may logout unnoticed. - # - # With this setting enabled the login state of the token is tracked and - # cached (including the PIN). Every transaction is preceeded by - # restoring the login state. After every transaction a logout is - # performed. This setting by default also enables `lock_login` (see - # above) to disable access for other applications during the atomic - # transactions. - # - # Please note that any PIN-pad should be disabled (see `enable_pinpad` - # above), because the user would have to input his PIN for every - # transaction. - # - # Default: false - # atomic = true; - - # With this setting disabled, the OpenSC PKCS#11 module will initialize - # the slots available when the application calls `C_GetSlotList`. With - # this setting enabled, the slots will also get initialized when - # C_GetSlotInfo is called. - # - # This setting is a workaround for Java which does not call - # `C_GetSlotList` when configured with a static `slot` instead of - # `slotListIndex`. - # - # Default: true - # init_sloppy = false; - - # User PIN unblock style - # none: PIN unblock is not possible with PKCS#11 API; - # set_pin_in_unlogged_session: C_SetPIN() in unlogged session: - # PUK is passed as the 'OldPin' argument of the C_SetPIN() call. - # set_pin_in_specific_context: C_SetPIN() in the CKU_SPECIFIC_CONTEXT logged session: - # PUK is passed as the 'OldPin' argument of the C_SetPIN() call. - # init_pin_in_so_session: C_InitPIN() in CKU_SO logged session: - # User PIN 'UNBLOCK' is protected by SOPIN. (PUK == SOPIN). - # # Actually this style works only for the PKCS15 contents without SOPIN. - # # For those with SOPIN, this mode will be usefull for the cards without - # # modes 00 and 01 of ISO command 'RESET RETRY COUNTER'. --vt - # - # Default: none - # user_pin_unblock_style = set_pin_in_unlogged_session; - - # Create slot for unblocking PIN with PUK - # This way PKCS#11 API can be used to login with PUK and - # change a PIN. - # Warning: causes problems with some applications like - # firefox and thunderbird. Thus turned off by default - # - # Default: false - # create_puk_slot = true; - - # Report as 'zero' the CKA_ID attribute of CA certificate - # For the unknown reason the middleware of the manufacturer of gemalto (axalto, gemplus) - # card reports as '0' the CKA_ID of CA cartificates. - # Maybe someone else will need it. (Would be nice to know who and what for -- VTA) - # - # Default: false - # zero_ckaid_for_ca_certs = true; - - # List of readers to ignore - # If any of the strings listed below is matched (case sensitive) in a reader name, - # the reader is ignored by the PKCS#11 module. - # - # Default: empty - # ignored_readers = "CardMan 1021", "SPR 532"; - - # Symbolic names of PINs for which slots are created - # Card can contain more then one PINs or more then one on-card application with - # its own PINs. Normally, to access all of them with the PKCS#11 API a slot has to be - # created for all of them. Many slots could be ennoying for some of widely used application, - # like FireFox. This configuration parameter allows to select the PIN(s) - # for which PKCS#11 slot will be created. - # Actually recognised following symbolic names: - # 'user', 'sign', 'all' - # Only PINs initialised, non-SoPIN, non-unblocking are associated with symbolic name. - # 'user' is identified as first global or first local PIN. - # 'sign' is identified as second PIN: first local, second global or second local. - # 'all' slot created for all non-sopin, non-unblocking PINs, - # optionally for PUK (see option 'create_puk_slot') - # - # Default: all - # create_slots_for_pins = "user,sign"; - # create_slots_for_pins = "sign"; - # - # For the module to simulate the opensc-onepin module behavior the following option - # must be set: - # create_slots_for_pins = "user" - } -} - -app onepin-opensc-pkcs11 { - pkcs11 { - slots_per_card = 1; - } -} - -# Used by OpenSC.tokend on Mac OS X only -app tokend { - # The file to which debug log will be written - # Default: /tmp/opensc-tokend.log - # - # debug_file = /Library/Logs/OpenSC.tokend.log - - framework tokend { - # Score for OpenSC.tokend - # The tokend with the highest score shall be used. - # Default: 300 - # - # score = 10; - } -} - -# Used by OpenSC minidriver on Windows only -app cardmod { -} diff --git a/m4/ax_ac_append_to_file.m4 b/m4/ax_ac_append_to_file.m4 new file mode 100644 index 0000000000..242b3d5200 --- /dev/null +++ b/m4/ax_ac_append_to_file.m4 @@ -0,0 +1,32 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_ac_append_to_file.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_AC_APPEND_TO_FILE([FILE],[DATA]) +# +# DESCRIPTION +# +# Appends the specified data to the specified Autoconf is run. If you want +# to append to a file when configure is run use AX_APPEND_TO_FILE instead. +# +# LICENSE +# +# Copyright (c) 2009 Allan Caffee +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 10 + +AC_DEFUN([AX_AC_APPEND_TO_FILE],[ +AC_REQUIRE([AX_FILE_ESCAPES]) +m4_esyscmd( +AX_FILE_ESCAPES +[ +printf "%s" "$2" >> "$1" +]) +]) diff --git a/m4/ax_ac_print_to_file.m4 b/m4/ax_ac_print_to_file.m4 new file mode 100644 index 0000000000..642dfc1511 --- /dev/null +++ b/m4/ax_ac_print_to_file.m4 @@ -0,0 +1,32 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_ac_print_to_file.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_AC_PRINT_TO_FILE([FILE],[DATA]) +# +# DESCRIPTION +# +# Writes the specified data to the specified file when Autoconf is run. If +# you want to print to a file when configure is run use AX_PRINT_TO_FILE +# instead. +# +# LICENSE +# +# Copyright (c) 2009 Allan Caffee +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 10 + +AC_DEFUN([AX_AC_PRINT_TO_FILE],[ +m4_esyscmd( +AC_REQUIRE([AX_FILE_ESCAPES]) +[ +printf "%s" "$2" > "$1" +]) +]) diff --git a/m4/ax_add_am_macro_static.m4 b/m4/ax_add_am_macro_static.m4 new file mode 100644 index 0000000000..6442d24b7c --- /dev/null +++ b/m4/ax_add_am_macro_static.m4 @@ -0,0 +1,28 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_add_am_macro_static.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_ADD_AM_MACRO_STATIC([RULE]) +# +# DESCRIPTION +# +# Adds the specified rule to $AMINCLUDE. +# +# LICENSE +# +# Copyright (c) 2009 Tom Howard +# Copyright (c) 2009 Allan Caffee +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AC_DEFUN([AX_ADD_AM_MACRO_STATIC],[ + AC_REQUIRE([AX_AM_MACROS_STATIC]) + AX_AC_APPEND_TO_FILE(AMINCLUDE_STATIC,[$1]) +]) diff --git a/m4/ax_am_macros_static.m4 b/m4/ax_am_macros_static.m4 new file mode 100644 index 0000000000..f4cee8c890 --- /dev/null +++ b/m4/ax_am_macros_static.m4 @@ -0,0 +1,38 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_am_macros_static.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_AM_MACROS_STATIC +# +# DESCRIPTION +# +# Adds support for macros that create Automake rules. You must manually +# add the following line +# +# include $(top_srcdir)/aminclude_static.am +# +# to your Makefile.am files. +# +# LICENSE +# +# Copyright (c) 2009 Tom Howard +# Copyright (c) 2009 Allan Caffee +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 11 + +AC_DEFUN([AMINCLUDE_STATIC],[aminclude_static.am]) + +AC_DEFUN([AX_AM_MACROS_STATIC], +[ +AX_AC_PRINT_TO_FILE(AMINCLUDE_STATIC,[ +# ]AMINCLUDE_STATIC[ generated automatically by Autoconf +# from AX_AM_MACROS_STATIC on ]m4_esyscmd([LC_ALL=C date])[ +]) +]) diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 0000000000..dcabb92a14 --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU 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 General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 5 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/m4/ax_check_gnu_make.m4 b/m4/ax_check_gnu_make.m4 new file mode 100644 index 0000000000..785dc96daf --- /dev/null +++ b/m4/ax_check_gnu_make.m4 @@ -0,0 +1,95 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_gnu_make.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_GNU_MAKE([run-if-true],[run-if-false]) +# +# DESCRIPTION +# +# This macro searches for a GNU version of make. If a match is found: +# +# * The makefile variable `ifGNUmake' is set to the empty string, otherwise +# it is set to "#". This is useful for including a special features in a +# Makefile, which cannot be handled by other versions of make. +# * The makefile variable `ifnGNUmake' is set to #, otherwise +# it is set to the empty string. This is useful for including a special +# features in a Makefile, which can be handled +# by other versions of make or to specify else like clause. +# * The variable `_cv_gnu_make_command` is set to the command to invoke +# GNU make if it exists, the empty string otherwise. +# * The variable `ax_cv_gnu_make_command` is set to the command to invoke +# GNU make by copying `_cv_gnu_make_command`, otherwise it is unset. +# * If GNU Make is found, its version is extracted from the output of +# `make --version` as the last field of a record of space-separated +# columns and saved into the variable `ax_check_gnu_make_version`. +# * Additionally if GNU Make is found, run shell code run-if-true +# else run shell code run-if-false. +# +# Here is an example of its use: +# +# Makefile.in might contain: +# +# # A failsafe way of putting a dependency rule into a makefile +# $(DEPEND): +# $(CC) -MM $(srcdir)/*.c > $(DEPEND) +# +# @ifGNUmake@ ifeq ($(DEPEND),$(wildcard $(DEPEND))) +# @ifGNUmake@ include $(DEPEND) +# @ifGNUmake@ else +# fallback code +# @ifGNUmake@ endif +# +# Then configure.in would normally contain: +# +# AX_CHECK_GNU_MAKE() +# AC_OUTPUT(Makefile) +# +# Then perhaps to cause gnu make to override any other make, we could do +# something like this (note that GNU make always looks for GNUmakefile +# first): +# +# if ! test x$_cv_gnu_make_command = x ; then +# mv Makefile GNUmakefile +# echo .DEFAULT: > Makefile ; +# echo \ $_cv_gnu_make_command \$@ >> Makefile; +# fi +# +# Then, if any (well almost any) other make is called, and GNU make also +# exists, then the other make wraps the GNU make. +# +# LICENSE +# +# Copyright (c) 2008 John Darrington +# Copyright (c) 2015 Enrico M. Crisostomo +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 12 + +AC_DEFUN([AX_CHECK_GNU_MAKE],dnl + [AC_PROG_AWK + AC_CACHE_CHECK([for GNU make],[_cv_gnu_make_command],[dnl + _cv_gnu_make_command="" ; +dnl Search all the common names for GNU make + for a in "$MAKE" make gmake gnumake ; do + if test -z "$a" ; then continue ; fi ; + if "$a" --version 2> /dev/null | grep GNU 2>&1 > /dev/null ; then + _cv_gnu_make_command=$a ; + AX_CHECK_GNU_MAKE_HEADLINE=$("$a" --version 2> /dev/null | grep "GNU Make") + ax_check_gnu_make_version=$(echo ${AX_CHECK_GNU_MAKE_HEADLINE} | ${AWK} -F " " '{ print $(NF); }') + break ; + fi + done ;]) +dnl If there was a GNU version, then set @ifGNUmake@ to the empty string, '#' otherwise + AS_VAR_IF([_cv_gnu_make_command], [""], [AS_VAR_SET([ifGNUmake], ["#"])], [AS_VAR_SET([ifGNUmake], [""])]) + AS_VAR_IF([_cv_gnu_make_command], [""], [AS_VAR_SET([ifnGNUmake], [""])], [AS_VAR_SET([ifnGNUmake], ["#"])]) + AS_VAR_IF([_cv_gnu_make_command], [""], [AS_UNSET(ax_cv_gnu_make_command)], [AS_VAR_SET([ax_cv_gnu_make_command], [${_cv_gnu_make_command}])]) + AS_VAR_IF([_cv_gnu_make_command], [""],[$2],[$1]) + AC_SUBST([ifGNUmake]) + AC_SUBST([ifnGNUmake]) +]) diff --git a/m4/ax_code_coverage.m4 b/m4/ax_code_coverage.m4 new file mode 100644 index 0000000000..3d36924b15 --- /dev/null +++ b/m4/ax_code_coverage.m4 @@ -0,0 +1,272 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_code_coverage.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CODE_COVERAGE() +# +# DESCRIPTION +# +# Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS, +# CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LIBS which should be included +# in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LIBADD variables of every +# build target (program or library) which should be built with code +# coverage support. Also add rules using AX_ADD_AM_MACRO_STATIC; and +# $enable_code_coverage which can be used in subsequent configure output. +# CODE_COVERAGE_ENABLED is defined and substituted, and corresponds to the +# value of the --enable-code-coverage option, which defaults to being +# disabled. +# +# Test also for gcov program and create GCOV variable that could be +# substituted. +# +# Note that all optimization flags in CFLAGS must be disabled when code +# coverage is enabled. +# +# Usage example: +# +# configure.ac: +# +# AX_CODE_COVERAGE +# +# Makefile.am: +# +# include $(top_srcdir)/aminclude_static.am +# +# my_program_LIBS = ... $(CODE_COVERAGE_LIBS) ... +# my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ... +# my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ... +# my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ... +# +# clean-local: code-coverage-clean +# distclean-local: code-coverage-dist-clean +# +# This results in a "check-code-coverage" rule being added to any +# Makefile.am which do "include $(top_srcdir)/aminclude_static.am" +# (assuming the module has been configured with --enable-code-coverage). +# Running `make check-code-coverage` in that directory will run the +# module's test suite (`make check`) and build a code coverage report +# detailing the code which was touched, then print the URI for the report. +# +# This code was derived from Makefile.decl in GLib, originally licensed +# under LGPLv2.1+. +# +# LICENSE +# +# Copyright (c) 2012, 2016 Philip Withnall +# Copyright (c) 2012 Xan Lopez +# Copyright (c) 2012 Christian Persch +# Copyright (c) 2012 Paolo Borelli +# Copyright (c) 2012 Dan Winship +# Copyright (c) 2015,2018 Bastien ROUCARIES +# +# 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 program. If not, see . + +#serial 32 + +m4_define(_AX_CODE_COVERAGE_RULES,[ +AX_ADD_AM_MACRO_STATIC([ +# Code coverage +# +# Optional: +# - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting. +# Multiple directories may be specified, separated by whitespace. +# (Default: \$(top_builddir)) +# - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated +# by lcov for code coverage. (Default: +# \$(PACKAGE_NAME)-\$(PACKAGE_VERSION)-coverage.info) +# - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage +# reports to be created. (Default: +# \$(PACKAGE_NAME)-\$(PACKAGE_VERSION)-coverage) +# - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage, +# set to 0 to disable it and leave empty to stay with the default. +# (Default: empty) +# - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov +# instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) +# - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov +# instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +# - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov +# - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the +# collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +# - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov +# instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +# - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering +# lcov instance. (Default: empty) +# - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov +# instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +# - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the +# genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) +# - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml +# instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) +# - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore +# +# The generated report will be titled using the \$(PACKAGE_NAME) and +# \$(PACKAGE_VERSION). In order to add the current git hash to the title, +# use the git-version-gen script, available online. +# Optional variables +# run only on top dir +if CODE_COVERAGE_ENABLED + ifeq (\$(abs_builddir), \$(abs_top_builddir)) +CODE_COVERAGE_DIRECTORY ?= \$(top_builddir) +CODE_COVERAGE_OUTPUT_FILE ?= \$(PACKAGE_NAME)-\$(PACKAGE_VERSION)-coverage.info +CODE_COVERAGE_OUTPUT_DIRECTORY ?= \$(PACKAGE_NAME)-\$(PACKAGE_VERSION)-coverage + +CODE_COVERAGE_BRANCH_COVERAGE ?= +CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= \$(if \$(CODE_COVERAGE_BRANCH_COVERAGE),\ +--rc lcov_branch_coverage=\$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_LCOV_SHOPTS ?= \$(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool \"\$(GCOV)\" +CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= \$(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +CODE_COVERAGE_LCOV_OPTIONS ?= \$(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?= +CODE_COVERAGE_LCOV_RMOPTS ?= \$(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\ +\$(if \$(CODE_COVERAGE_BRANCH_COVERAGE),\ +--rc genhtml_branch_coverage=\$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_GENHTML_OPTIONS ?= \$(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) +CODE_COVERAGE_IGNORE_PATTERN ?= + +GITIGNOREFILES = \$(GITIGNOREFILES) \$(CODE_COVERAGE_OUTPUT_FILE) \$(CODE_COVERAGE_OUTPUT_DIRECTORY) +code_coverage_v_lcov_cap = \$(code_coverage_v_lcov_cap_\$(V)) +code_coverage_v_lcov_cap_ = \$(code_coverage_v_lcov_cap_\$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_cap_0 = @echo \" LCOV --capture\" \$(CODE_COVERAGE_OUTPUT_FILE); +code_coverage_v_lcov_ign = \$(code_coverage_v_lcov_ign_\$(V)) +code_coverage_v_lcov_ign_ = \$(code_coverage_v_lcov_ign_\$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_ign_0 = @echo \" LCOV --remove /tmp/*\" \$(CODE_COVERAGE_IGNORE_PATTERN); +code_coverage_v_genhtml = \$(code_coverage_v_genhtml_\$(V)) +code_coverage_v_genhtml_ = \$(code_coverage_v_genhtml_\$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_genhtml_0 = @echo \" GEN \" \"\$(CODE_COVERAGE_OUTPUT_DIRECTORY)\"; +code_coverage_quiet = \$(code_coverage_quiet_\$(V)) +code_coverage_quiet_ = \$(code_coverage_quiet_\$(AM_DEFAULT_VERBOSITY)) +code_coverage_quiet_0 = --quiet + +# sanitizes the test-name: replaces with underscores: dashes and dots +code_coverage_sanitize = \$(subst -,_,\$(subst .,_,\$(1))) + +# Use recursive makes in order to ignore errors during check +check-code-coverage: + -\$(AM_V_at)\$(MAKE) \$(AM_MAKEFLAGS) -k check + \$(AM_V_at)\$(MAKE) \$(AM_MAKEFLAGS) code-coverage-capture + +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook + \$(code_coverage_v_lcov_cap)\$(LCOV) \$(code_coverage_quiet) \$(addprefix --directory ,\$(CODE_COVERAGE_DIRECTORY)) --capture --output-file \"\$(CODE_COVERAGE_OUTPUT_FILE).tmp\" --test-name \"\$(call code_coverage_sanitize,\$(PACKAGE_NAME)-\$(PACKAGE_VERSION))\" --no-checksum --compat-libtool \$(CODE_COVERAGE_LCOV_SHOPTS) \$(CODE_COVERAGE_LCOV_OPTIONS) + \$(code_coverage_v_lcov_ign)\$(LCOV) \$(code_coverage_quiet) \$(addprefix --directory ,\$(CODE_COVERAGE_DIRECTORY)) --remove \"\$(CODE_COVERAGE_OUTPUT_FILE).tmp\" \"/tmp/*\" \$(CODE_COVERAGE_IGNORE_PATTERN) --output-file \"\$(CODE_COVERAGE_OUTPUT_FILE)\" \$(CODE_COVERAGE_LCOV_SHOPTS) \$(CODE_COVERAGE_LCOV_RMOPTS) + -@rm -f \"\$(CODE_COVERAGE_OUTPUT_FILE).tmp\" + \$(code_coverage_v_genhtml)LANG=C \$(GENHTML) \$(code_coverage_quiet) \$(addprefix --prefix ,\$(CODE_COVERAGE_DIRECTORY)) --output-directory \"\$(CODE_COVERAGE_OUTPUT_DIRECTORY)\" --title \"\$(PACKAGE_NAME)-\$(PACKAGE_VERSION) Code Coverage\" --legend --show-details \"\$(CODE_COVERAGE_OUTPUT_FILE)\" \$(CODE_COVERAGE_GENHTML_OPTIONS) + @echo \"file://\$(abs_builddir)/\$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html\" + +code-coverage-clean: + -\$(LCOV) --directory \$(top_builddir) -z + -rm -rf \"\$(CODE_COVERAGE_OUTPUT_FILE)\" \"\$(CODE_COVERAGE_OUTPUT_FILE).tmp\" \"\$(CODE_COVERAGE_OUTPUT_DIRECTORY)\" + -find . \\( -name \"*.gcda\" -o -name \"*.gcno\" -o -name \"*.gcov\" \\) -delete + +code-coverage-dist-clean: + +A][M_DISTCHECK_CONFIGURE_FLAGS = \$(A][M_DISTCHECK_CONFIGURE_FLAGS) --disable-code-coverage + else # ifneq (\$(abs_builddir), \$(abs_top_builddir)) +check-code-coverage: + +code-coverage-capture: code-coverage-capture-hook + +code-coverage-clean: + +code-coverage-dist-clean: + endif # ifeq (\$(abs_builddir), \$(abs_top_builddir)) +else #! CODE_COVERAGE_ENABLED +# Use recursive makes in order to ignore errors during check +check-code-coverage: + @echo \"Need to reconfigure with --enable-code-coverage\" +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook + @echo \"Need to reconfigure with --enable-code-coverage\" + +code-coverage-clean: + +code-coverage-dist-clean: + +endif #CODE_COVERAGE_ENABLED +# Hook rule executed before code-coverage-capture, overridable by the user +code-coverage-capture-hook: + +.PHONY: check-code-coverage code-coverage-capture code-coverage-dist-clean code-coverage-clean code-coverage-capture-hook +]) +]) + +AC_DEFUN([_AX_CODE_COVERAGE_ENABLED],[ + AX_CHECK_GNU_MAKE([],[AC_MSG_ERROR([not using GNU make that is needed for coverage])]) + AC_REQUIRE([AX_ADD_AM_MACRO_STATIC]) + # check for gcov + AC_CHECK_TOOL([GCOV], + [$_AX_CODE_COVERAGE_GCOV_PROG_WITH], + [:]) + AS_IF([test "X$GCOV" = "X:"], + [AC_MSG_ERROR([gcov is needed to do coverage])]) + AC_SUBST([GCOV]) + + dnl Check if gcc is being used + AS_IF([ test "$GCC" = "no" ], [ + AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage]) + ]) + + AC_CHECK_PROG([LCOV], [lcov], [lcov]) + AC_CHECK_PROG([GENHTML], [genhtml], [genhtml]) + + AS_IF([ test x"$LCOV" = x ], [ + AC_MSG_ERROR([To enable code coverage reporting you must have lcov installed]) + ]) + + AS_IF([ test x"$GENHTML" = x ], [ + AC_MSG_ERROR([Could not find genhtml from the lcov package]) + ]) + + dnl Build the code coverage flags + dnl Define CODE_COVERAGE_LDFLAGS for backwards compatibility + CODE_COVERAGE_CPPFLAGS="-DNDEBUG" + CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" + CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" + CODE_COVERAGE_LIBS="-lgcov" + + AC_SUBST([CODE_COVERAGE_CPPFLAGS]) + AC_SUBST([CODE_COVERAGE_CFLAGS]) + AC_SUBST([CODE_COVERAGE_CXXFLAGS]) + AC_SUBST([CODE_COVERAGE_LIBS]) +]) + +AC_DEFUN([AX_CODE_COVERAGE],[ + dnl Check for --enable-code-coverage + + # allow to override gcov location + AC_ARG_WITH([gcov], + [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])], + [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov], + [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov]) + + AC_MSG_CHECKING([whether to build with code coverage support]) + AC_ARG_ENABLE([code-coverage], + AS_HELP_STRING([--enable-code-coverage], + [Whether to enable code coverage support]),, + enable_code_coverage=no) + + AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test "x$enable_code_coverage" = xyes]) + AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage]) + AC_MSG_RESULT($enable_code_coverage) + + AS_IF([ test "x$enable_code_coverage" = xyes ], [ + _AX_CODE_COVERAGE_ENABLED + ]) + + _AX_CODE_COVERAGE_RULES +]) diff --git a/m4/ax_file_escapes.m4 b/m4/ax_file_escapes.m4 new file mode 100644 index 0000000000..a86fdc326b --- /dev/null +++ b/m4/ax_file_escapes.m4 @@ -0,0 +1,30 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_file_escapes.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_FILE_ESCAPES +# +# DESCRIPTION +# +# Writes the specified data to the specified file. +# +# LICENSE +# +# Copyright (c) 2008 Tom Howard +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AC_DEFUN([AX_FILE_ESCAPES],[ +AX_DOLLAR="\$" +AX_SRB="\\135" +AX_SLB="\\133" +AX_BS="\\\\" +AX_DQ="\"" +]) diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4 index 5fbf9fe0d6..9f35d13914 100644 --- a/m4/ax_pthread.m4 +++ b/m4/ax_pthread.m4 @@ -14,20 +14,24 @@ # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # -# Also sets PTHREAD_CC to any special C compiler that is needed for -# multi-threaded programs (defaults to the value of CC otherwise). (This -# is necessary on AIX to use the special cc_r compiler alias.) +# Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is +# needed for multi-threaded programs (defaults to the value of CC +# respectively CXX otherwise). (This is necessary on e.g. AIX to use the +# special cc_r/CC_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also to link with them as well. For example, you might link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threaded programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" +# CXX="$PTHREAD_CXX" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to @@ -55,6 +59,7 @@ # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2011 Daniel Richard G. +# Copyright (c) 2019 Marc Stevens # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the @@ -82,7 +87,7 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 24 +#serial 31 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ @@ -104,6 +109,7 @@ if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) + AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"]) CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) @@ -123,10 +129,12 @@ fi # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). -# Create a list of thread flags to try. Items starting with a "-" are -# C compiler flags, and other items are library names, except for "none" -# which indicates that we try without any flags at all, and "pthread-config" -# which is a program returning the flags for the Pth emulation library. +# Create a list of thread flags to try. Items with a "," contain both +# C compiler flags (before ",") and linker flags (after ","). Other items +# starting with a "-" are C compiler flags, and remaining items are +# library names, except for "none" which indicates that we try without +# any flags at all, and "pthread-config" which is a program returning +# the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" @@ -194,14 +202,47 @@ case $host_os in # that too in a future libc.) So we'll check first for the # standard Solaris way of linking pthreads (-mt -lpthread). - ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" + ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags" ;; esac +# Are we compiling with Clang? + +AC_CACHE_CHECK([whether $CC is Clang], + [ax_cv_PTHREAD_CLANG], + [ax_cv_PTHREAD_CLANG=no + # Note that Autoconf sets GCC=yes for Clang as well as GCC + if test "x$GCC" = "xyes"; then + AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], + [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ +# if defined(__clang__) && defined(__llvm__) + AX_PTHREAD_CC_IS_CLANG +# endif + ], + [ax_cv_PTHREAD_CLANG=yes]) + fi + ]) +ax_pthread_clang="$ax_cv_PTHREAD_CLANG" + + # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) +# Note that for GCC and Clang -pthread generally implies -lpthread, +# except when -nostdlib is passed. +# This is problematic using libtool to build C++ shared libraries with pthread: +# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460 +# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333 +# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555 +# To solve this, first try -pthread together with -lpthread for GCC + AS_IF([test "x$GCC" = "xyes"], - [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) + [ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"]) + +# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first + +AS_IF([test "x$ax_pthread_clang" = "xyes"], + [ax_pthread_flags="-pthread,-lpthread -pthread"]) + # The presence of a feature test macro requesting re-entrant function # definitions is, on some systems, a strong hint that pthreads support is @@ -224,25 +265,86 @@ AS_IF([test "x$ax_pthread_check_macro" = "x--"], [ax_pthread_check_cond=0], [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) -# Are we compiling with Clang? -AC_CACHE_CHECK([whether $CC is Clang], - [ax_cv_PTHREAD_CLANG], - [ax_cv_PTHREAD_CLANG=no - # Note that Autoconf sets GCC=yes for Clang as well as GCC - if test "x$GCC" = "xyes"; then - AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], - [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ -# if defined(__clang__) && defined(__llvm__) - AX_PTHREAD_CC_IS_CLANG -# endif - ], - [ax_cv_PTHREAD_CLANG=yes]) - fi - ]) -ax_pthread_clang="$ax_cv_PTHREAD_CLANG" +if test "x$ax_pthread_ok" = "xno"; then +for ax_pthread_try_flag in $ax_pthread_flags; do + + case $ax_pthread_try_flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + *,*) + PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"` + PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"` + AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) + PTHREAD_CFLAGS="$ax_pthread_try_flag" + ;; + + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) + PTHREAD_LIBS="-l$ax_pthread_try_flag" + ;; + esac + + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include +# if $ax_pthread_check_cond +# error "$ax_pthread_check_macro must be defined" +# endif + static void *some_global = NULL; + static void routine(void *a) + { + /* To avoid any unused-parameter or + unused-but-set-parameter warning. */ + some_global = a; + } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + AC_MSG_RESULT([$ax_pthread_ok]) + AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi -ax_pthread_clang_warning=no # Clang needs special handling, because older versions handle the -pthread # option in a rather... idiosyncratic way @@ -261,11 +363,6 @@ if test "x$ax_pthread_clang" = "xyes"; then # -pthread does define _REENTRANT, and while the Darwin headers # ignore this macro, third-party headers might not.) - PTHREAD_CFLAGS="-pthread" - PTHREAD_LIBS= - - ax_pthread_ok=yes - # However, older versions of Clang make a point of warning the user # that, in an invocation where only linking and no compilation is # taking place, the -pthread option has no effect ("argument unused @@ -294,7 +391,7 @@ if test "x$ax_pthread_clang" = "xyes"; then # step ax_pthread_save_ac_link="$ac_link" ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' - ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` + ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"` ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" ax_pthread_save_CFLAGS="$CFLAGS" for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do @@ -320,78 +417,7 @@ if test "x$ax_pthread_clang" = "xyes"; then fi # $ax_pthread_clang = yes -if test "x$ax_pthread_ok" = "xno"; then -for ax_pthread_try_flag in $ax_pthread_flags; do - - case $ax_pthread_try_flag in - none) - AC_MSG_CHECKING([whether pthreads work without any flags]) - ;; - - -mt,pthread) - AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) - PTHREAD_CFLAGS="-mt" - PTHREAD_LIBS="-lpthread" - ;; - - -*) - AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) - PTHREAD_CFLAGS="$ax_pthread_try_flag" - ;; - pthread-config) - AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) - AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) - PTHREAD_CFLAGS="`pthread-config --cflags`" - PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" - ;; - - *) - AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) - PTHREAD_LIBS="-l$ax_pthread_try_flag" - ;; - esac - - ax_pthread_save_CFLAGS="$CFLAGS" - ax_pthread_save_LIBS="$LIBS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - - # Check for various functions. We must include pthread.h, - # since some functions may be macros. (On the Sequent, we - # need a special flag -Kthread to make this header compile.) - # We check for pthread_join because it is in -lpthread on IRIX - # while pthread_create is in libc. We check for pthread_attr_init - # due to DEC craziness with -lpthreads. We check for - # pthread_cleanup_push because it is one of the few pthread - # functions on Solaris that doesn't have a non-functional libc stub. - # We try pthread_create on general principles. - - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include -# if $ax_pthread_check_cond -# error "$ax_pthread_check_macro must be defined" -# endif - static void routine(void *a) { a = 0; } - static void *start_routine(void *a) { return a; }], - [pthread_t th; pthread_attr_t attr; - pthread_create(&th, 0, start_routine, 0); - pthread_join(th, 0); - pthread_attr_init(&attr); - pthread_cleanup_push(routine, 0); - pthread_cleanup_pop(0) /* ; */])], - [ax_pthread_ok=yes], - []) - - CFLAGS="$ax_pthread_save_CFLAGS" - LIBS="$ax_pthread_save_LIBS" - - AC_MSG_RESULT([$ax_pthread_ok]) - AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) - - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" -done -fi # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then @@ -438,7 +464,8 @@ if test "x$ax_pthread_ok" = "xyes"; then AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], [ax_cv_PTHREAD_PRIO_INHERIT], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], - [[int i = PTHREAD_PRIO_INHERIT;]])], + [[int i = PTHREAD_PRIO_INHERIT; + return i;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) @@ -460,18 +487,28 @@ if test "x$ax_pthread_ok" = "xyes"; then [#handle absolute path differently from PATH based program lookup AS_CASE(["x$CC"], [x/*], - [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], - [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) + [ + AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"]) + AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])]) + ], + [ + AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC]) + AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])]) + ] + ) + ]) ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" +test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX" AC_SUBST([PTHREAD_LIBS]) AC_SUBST([PTHREAD_CFLAGS]) AC_SUBST([PTHREAD_CC]) +AC_SUBST([PTHREAD_CXX]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then diff --git a/m4/ax_valgrind_check.m4 b/m4/ax_valgrind_check.m4 new file mode 100644 index 0000000000..e0d8a2f3ae --- /dev/null +++ b/m4/ax_valgrind_check.m4 @@ -0,0 +1,239 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_VALGRIND_DFLT(memcheck|helgrind|drd|sgcheck, on|off) +# AX_VALGRIND_CHECK() +# +# DESCRIPTION +# +# AX_VALGRIND_CHECK checks whether Valgrind is present and, if so, allows +# running `make check` under a variety of Valgrind tools to check for +# memory and threading errors. +# +# Defines VALGRIND_CHECK_RULES which should be substituted in your +# Makefile; and $enable_valgrind which can be used in subsequent configure +# output. VALGRIND_ENABLED is defined and substituted, and corresponds to +# the value of the --enable-valgrind option, which defaults to being +# enabled if Valgrind is installed and disabled otherwise. Individual +# Valgrind tools can be disabled via --disable-valgrind-, the +# default is configurable via the AX_VALGRIND_DFLT command or is to use +# all commands not disabled via AX_VALGRIND_DFLT. All AX_VALGRIND_DFLT +# calls must be made before the call to AX_VALGRIND_CHECK. +# +# If unit tests are written using a shell script and automake's +# LOG_COMPILER system, the $(VALGRIND) variable can be used within the +# shell scripts to enable Valgrind, as described here: +# +# https://www.gnu.org/software/gnulib/manual/html_node/Running-self_002dtests-under-valgrind.html +# +# Usage example: +# +# configure.ac: +# +# AX_VALGRIND_DFLT([sgcheck], [off]) +# AX_VALGRIND_CHECK +# +# in each Makefile.am with tests: +# +# @VALGRIND_CHECK_RULES@ +# VALGRIND_SUPPRESSIONS_FILES = my-project.supp +# EXTRA_DIST = my-project.supp +# +# This results in a "check-valgrind" rule being added. Running `make +# check-valgrind` in that directory will recursively run the module's test +# suite (`make check`) once for each of the available Valgrind tools (out +# of memcheck, helgrind and drd) while the sgcheck will be skipped unless +# enabled again on the commandline with --enable-valgrind-sgcheck. The +# results for each check will be output to test-suite-$toolname.log. The +# target will succeed if there are zero errors and fail otherwise. +# +# Alternatively, a "check-valgrind-$TOOL" rule will be added, for $TOOL in +# memcheck, helgrind, drd and sgcheck. These are useful because often only +# some of those tools can be ran cleanly on a codebase. +# +# The macro supports running with and without libtool. +# +# LICENSE +# +# Copyright (c) 2014, 2015, 2016 Philip Withnall +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 23 + +dnl Configured tools +m4_define([valgrind_tool_list], [[memcheck], [helgrind], [drd], [sgcheck]]) +m4_set_add_all([valgrind_exp_tool_set], [sgcheck]) +m4_foreach([vgtool], [valgrind_tool_list], + [m4_define([en_dflt_valgrind_]vgtool, [on])]) + +AC_DEFUN([AX_VALGRIND_DFLT],[ + m4_define([en_dflt_valgrind_$1], [$2]) +])dnl + +AC_DEFUN([AX_VALGRIND_CHECK],[ + AM_EXTRA_RECURSIVE_TARGETS([check-valgrind]) + m4_foreach([vgtool], [valgrind_tool_list], + [AM_EXTRA_RECURSIVE_TARGETS([check-valgrind-]vgtool)]) + + dnl Check for --enable-valgrind + AC_ARG_ENABLE([valgrind], + [AS_HELP_STRING([--enable-valgrind], [Whether to enable Valgrind on the unit tests])], + [enable_valgrind=$enableval],[enable_valgrind=]) + + AS_IF([test "$enable_valgrind" != "no"],[ + # Check for Valgrind. + AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind]) + AS_IF([test "$VALGRIND" = ""],[ + AS_IF([test "$enable_valgrind" = "yes"],[ + AC_MSG_ERROR([Could not find valgrind; either install it or reconfigure with --disable-valgrind]) + ],[ + enable_valgrind=no + ]) + ],[ + enable_valgrind=yes + ]) + ]) + + AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"]) + AC_SUBST([VALGRIND_ENABLED],[$enable_valgrind]) + + # Check for Valgrind tools we care about. + [valgrind_enabled_tools=] + m4_foreach([vgtool],[valgrind_tool_list],[ + AC_ARG_ENABLE([valgrind-]vgtool, + m4_if(m4_defn([en_dflt_valgrind_]vgtool),[off],dnl +[AS_HELP_STRING([--enable-valgrind-]vgtool, [Whether to use ]vgtool[ during the Valgrind tests])],dnl +[AS_HELP_STRING([--disable-valgrind-]vgtool, [Whether to skip ]vgtool[ during the Valgrind tests])]), + [enable_valgrind_]vgtool[=$enableval], + [enable_valgrind_]vgtool[=]) + AS_IF([test "$enable_valgrind" = "no"],[ + enable_valgrind_]vgtool[=no], + [test "$enable_valgrind_]vgtool[" ]dnl +m4_if(m4_defn([en_dflt_valgrind_]vgtool), [off], [= "yes"], [!= "no"]),[ + AC_CACHE_CHECK([for Valgrind tool ]vgtool, + [ax_cv_valgrind_tool_]vgtool,[ + ax_cv_valgrind_tool_]vgtool[=no + m4_set_contains([valgrind_exp_tool_set],vgtool, + [m4_define([vgtoolx],[exp-]vgtool)], + [m4_define([vgtoolx],vgtool)]) + AS_IF([`$VALGRIND --tool=]vgtoolx[ --help >/dev/null 2>&1`],[ + ax_cv_valgrind_tool_]vgtool[=yes + ]) + ]) + AS_IF([test "$ax_cv_valgrind_tool_]vgtool[" = "no"],[ + AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[ + AC_MSG_ERROR([Valgrind does not support ]vgtool[; reconfigure with --disable-valgrind-]vgtool) + ],[ + enable_valgrind_]vgtool[=no + ]) + ],[ + enable_valgrind_]vgtool[=yes + ]) + ]) + AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[ + valgrind_enabled_tools="$valgrind_enabled_tools ]m4_bpatsubst(vgtool,[^exp-])[" + ]) + AC_SUBST([ENABLE_VALGRIND_]vgtool,[$enable_valgrind_]vgtool) + ]) + AC_SUBST([valgrind_tools],["]m4_join([ ], valgrind_tool_list)["]) + AC_SUBST([valgrind_enabled_tools],[$valgrind_enabled_tools]) + +[VALGRIND_CHECK_RULES=' +# Valgrind check +# +# Optional: +# - VALGRIND_SUPPRESSIONS_FILES: Space-separated list of Valgrind suppressions +# files to load. (Default: empty) +# - VALGRIND_FLAGS: General flags to pass to all Valgrind tools. +# (Default: --num-callers=30) +# - VALGRIND_$toolname_FLAGS: Flags to pass to Valgrind $toolname (one of: +# memcheck, helgrind, drd, sgcheck). (Default: various) + +# Optional variables +VALGRIND_SUPPRESSIONS ?= $(addprefix --suppressions=,$(VALGRIND_SUPPRESSIONS_FILES)) +VALGRIND_FLAGS ?= --num-callers=30 +VALGRIND_memcheck_FLAGS ?= --leak-check=full --show-reachable=no +VALGRIND_helgrind_FLAGS ?= --history-level=approx +VALGRIND_drd_FLAGS ?= +VALGRIND_sgcheck_FLAGS ?= + +# Internal use +valgrind_log_files = $(addprefix test-suite-,$(addsuffix .log,$(valgrind_tools))) + +valgrind_memcheck_flags = --tool=memcheck $(VALGRIND_memcheck_FLAGS) +valgrind_helgrind_flags = --tool=helgrind $(VALGRIND_helgrind_FLAGS) +valgrind_drd_flags = --tool=drd $(VALGRIND_drd_FLAGS) +valgrind_sgcheck_flags = --tool=exp-sgcheck $(VALGRIND_sgcheck_FLAGS) + +valgrind_quiet = $(valgrind_quiet_$(V)) +valgrind_quiet_ = $(valgrind_quiet_$(AM_DEFAULT_VERBOSITY)) +valgrind_quiet_0 = --quiet +valgrind_v_use = $(valgrind_v_use_$(V)) +valgrind_v_use_ = $(valgrind_v_use_$(AM_DEFAULT_VERBOSITY)) +valgrind_v_use_0 = @echo " USE " $(patsubst check-valgrind-%-local,%,$''@):; + +# Support running with and without libtool. +ifneq ($(LIBTOOL),) +valgrind_lt = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=execute +else +valgrind_lt = +endif + +# Use recursive makes in order to ignore errors during check +check-valgrind-local: +ifeq ($(VALGRIND_ENABLED),yes) + $(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k \ + $(foreach tool, $(valgrind_enabled_tools), check-valgrind-$(tool)) +else + @echo "Need to reconfigure with --enable-valgrind" +endif + +# Valgrind running +VALGRIND_TESTS_ENVIRONMENT = \ + $(TESTS_ENVIRONMENT) \ + env VALGRIND=$(VALGRIND) \ + G_SLICE=always-malloc,debug-blocks \ + G_DEBUG=fatal-warnings,fatal-criticals,gc-friendly + +VALGRIND_LOG_COMPILER = \ + $(valgrind_lt) \ + $(VALGRIND) $(VALGRIND_SUPPRESSIONS) --error-exitcode=1 $(VALGRIND_FLAGS) + +define valgrind_tool_rule +check-valgrind-$(1)-local: +ifeq ($$(VALGRIND_ENABLED)-$$(ENABLE_VALGRIND_$(1)),yes-yes) +ifneq ($$(TESTS),) + $$(valgrind_v_use)$$(MAKE) check-TESTS \ + TESTS_ENVIRONMENT="$$(VALGRIND_TESTS_ENVIRONMENT)" \ + LOG_COMPILER="$$(VALGRIND_LOG_COMPILER)" \ + LOG_FLAGS="$$(valgrind_$(1)_flags)" \ + TEST_SUITE_LOG=test-suite-$(1).log +endif +else ifeq ($$(VALGRIND_ENABLED),yes) + @echo "Need to reconfigure with --enable-valgrind-$(1)" +else + @echo "Need to reconfigure with --enable-valgrind" +endif +endef + +$(foreach tool,$(valgrind_tools),$(eval $(call valgrind_tool_rule,$(tool)))) + +A''M_DISTCHECK_CONFIGURE_FLAGS ?= +A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-valgrind + +MOSTLYCLEANFILES ?= +MOSTLYCLEANFILES += $(valgrind_log_files) + +.PHONY: check-valgrind $(addprefix check-valgrind-,$(valgrind_tools)) +'] + + AC_SUBST([VALGRIND_CHECK_RULES]) + m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([VALGRIND_CHECK_RULES])]) +]) diff --git a/m4/m4_ax_func_getopt_long.m4 b/m4/m4_ax_func_getopt_long.m4 new file mode 100644 index 0000000000..7b7268878f --- /dev/null +++ b/m4/m4_ax_func_getopt_long.m4 @@ -0,0 +1,62 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_func_getopt_long.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_FUNC_GETOPT_LONG +# +# DESCRIPTION +# +# Check for getopt_long support. +# +# This assume that the standard getopt.h file (from GNU libc) is available +# as src/common/compat_getopt.h. If needed, this file will be linked as getopt.h, but +# we want to default to the system's getopt.h file. (See +# http://sources.redhat.com/ml/automake/2000-09/msg00041.html for an +# explanation about why using the system's getopt.h file is important.) +# +# LICENSE +# +# Copyright (c) 2008 Alexandre Duret-Lutz +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 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 General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 6 + +AU_ALIAS([ADL_FUNC_GETOPT_LONG], [AX_FUNC_GETOPT_LONG]) +AC_DEFUN([AX_FUNC_GETOPT_LONG], + [AC_PREREQ(2.49)dnl + # clean out junk possibly left behind by a previous configuration + rm -f src/getopt.h + # Check for getopt_long support + AC_CHECK_HEADERS([getopt.h]) + AC_CHECK_FUNCS([getopt_long],, + [# FreeBSD has a gnugetopt library for this + AC_CHECK_LIB([gnugetopt],[getopt_long],[AC_DEFINE([HAVE_GETOPT_LONG])], + [# use the OpenSC replacement + AC_CONFIG_LINKS([src/getopt.h:src/common/compat_getopt.h])])])]) diff --git a/packaging/opensc.spec b/packaging/opensc.spec new file mode 100644 index 0000000000..4141bc06a2 --- /dev/null +++ b/packaging/opensc.spec @@ -0,0 +1,235 @@ +Name: opensc +Version: 0.27.1 +Release: %autorelease +Summary: Smart card library and applications + +License: LGPL-2.1-or-later AND BSD-3-Clause +URL: https://github.com/OpenSC/OpenSC/wiki +Source0: https://github.com/OpenSC/OpenSC/releases/download/%{version}/%{name}-%{version}.tar.gz +Source1: opensc.module + +BuildRequires: make +BuildRequires: pcsc-lite-devel +BuildRequires: readline-devel +BuildRequires: openssl-devel +BuildRequires: /usr/bin/xsltproc +BuildRequires: docbook-style-xsl +BuildRequires: autoconf automake libtool gcc +%if 0%{?fedora} || 0%{?rhel} > 10 +BuildRequires: bash-completion-devel +%else +BuildRequires: bash-completion +%endif +BuildRequires: zlib-ng-devel +BuildRequires: p11-kit-devel +# For tests +BuildRequires: libcmocka-devel +BuildRequires: vim-common +%if ! 0%{?rhel} +BuildRequires: openpace-devel +%endif +BuildRequires: softhsm +BuildRequires: openssl +Requires: %{name}-libs = %{version}-%{release} +Requires: pcsc-lite-libs%{?_isa} +Requires: pcsc-lite +Obsoletes: mozilla-opensc-signer < 0.12.0 +Obsoletes: opensc-devel < 0.12.0 +Obsoletes: coolkey <= 1.1.0-36 +# The simclist is bundled in upstream +Provides: bundled(simclist) = 1.5 + +%description +OpenSC provides a set of libraries and utilities to work with smart cards. Its +main focus is on cards that support cryptographic operations, and facilitate +their use in security applications such as authentication, mail encryption and +digital signatures. OpenSC implements the PKCS#11 API so applications +supporting this API (such as Mozilla Firefox and Thunderbird) can use it. On +the card OpenSC implements the PKCS#15 standard and aims to be compatible with +every software/card that does so, too. + +%package libs +Requires: pcsc-lite-libs%{?_isa} +Summary: OpenSC libraries + +%description libs +OpenSC libraries. + + +%prep +%setup -q -n opensc-%{version} + +XFAIL_TESTS="test-pkcs11-tool-test-threads.sh test-pkcs11-tool-test.sh" + +# In FIPS mode, OpenSSL doesn't allow RSA-PKCS, this is hardcoded into OpenSSL +# and we cannot influence it. Hence, the test is expected to fail in FIPS mode. +if [[ -f "/proc/sys/crypto/fips_enabled" && $(cat /proc/sys/crypto/fips_enabled) == "1" ]]; then + XFAIL_TESTS+=" test-pkcs11-tool-unwrap-wrap-test.sh" +fi + +sed -i -e "/XFAIL_TESTS/,$ { + s/XFAIL_TESTS.*/XFAIL_TESTS=$XFAIL_TESTS/ + q +}" tests/Makefile.am + +cp -p src/pkcs15init/README ./README.pkcs15init +cp -p src/scconf/README.scconf . +# No {_libdir} here to avoid multilib conflicts; it's just an example +sed -i -e 's|/usr/local/towitoko/lib/|/usr/lib/ctapi/|' etc/opensc.conf.example.in + + +%build +autoreconf -fvi +%ifarch %{ix86} +sed -i -e 's/opensc.conf/opensc-%{_arch}.conf/g' src/libopensc/Makefile.in +%endif +sed -i -e 's|"/lib /usr/lib\b|"/%{_lib} %{_libdir}|' configure # lib64 rpaths +%set_build_flags +CFLAGS="$CFLAGS -Wstrict-aliasing=2 -Wno-deprecated-declarations" +%configure --disable-static \ + --disable-autostart-items \ + --disable-notify \ + --disable-assert \ + --enable-pcsc \ + --enable-cmocka \ + --enable-sm +%make_build + + +%check +make check || (cat tests/*.log src/tests/unittests/*.log && exit 1) + + +%install +%make_install +install -Dpm 644 %{SOURCE1} $RPM_BUILD_ROOT%{_datadir}/p11-kit/modules/opensc.module + +%ifarch %{ix86} +# To avoid multilib issues, move these files on 32b intel architectures +rm -f $RPM_BUILD_ROOT%{_sysconfdir}/opensc.conf +install -Dpm 644 etc/opensc.conf $RPM_BUILD_ROOT%{_sysconfdir}/opensc-%{_arch}.conf +rm -f $RPM_BUILD_ROOT%{_mandir}/man5/opensc.conf.5 +install -Dpm 644 doc/files/opensc.conf.5 $RPM_BUILD_ROOT%{_mandir}/man5/opensc-%{_arch}.conf.5 +# use NEWS file timestamp as reference for configuration file +touch -r NEWS $RPM_BUILD_ROOT%{_sysconfdir}/opensc-%{_arch}.conf +touch -r NEWS $RPM_BUILD_ROOT%{_mandir}/man5/opensc-%{_arch}.conf.5 +%else +# For backward compatibility, symlink the old location to the new files +ln -s %{_sysconfdir}/opensc.conf $RPM_BUILD_ROOT%{_sysconfdir}/opensc-%{_arch}.conf +%endif + +find $RPM_BUILD_ROOT%{_libdir} -type f -name "*.la" | xargs rm + +rm -rf $RPM_BUILD_ROOT%{_datadir}/doc/opensc + +# Upstream considers libopensc API internal and no longer ships +# public headers and pkgconfig files. +# Remove the symlink as nothing is supposed to link against libopensc. +rm -f $RPM_BUILD_ROOT%{_libdir}/libopensc.so +# remove the .pc file so we do not confuse users #1673139 +rm -f $RPM_BUILD_ROOT%{_libdir}/pkgconfig/*.pc +rm -f $RPM_BUILD_ROOT%{_libdir}/libsmm-local.so + +%if 0%{?rhel} +rm -rf %{buildroot}%{_bindir}/npa-tool +rm -rf %{buildroot}%{_mandir}/man1/npa-tool.1* +%endif + +# the pkcs11-register is not applicable to Fedora/RHEL where we use p11-kit +rm -rf %{buildroot}%{_bindir}/pkcs11-register +rm -rf %{buildroot}%{_mandir}/man1/pkcs11-register.1* + +# Remove the notification files +rm %{buildroot}%{_datadir}/applications/org.opensc.notify.desktop +rm %{buildroot}%{_mandir}/man1/opensc-notify.1* + + +%files +%doc COPYING NEWS README* + +%{_datadir}/bash-completion/* + + +%{_bindir}/cardos-tool +%{_bindir}/cryptoflex-tool +%{_bindir}/eidenv +%{_bindir}/iasecc-tool +%{_bindir}/gids-tool +%{_bindir}/netkey-tool +%if ! 0%{?rhel} +%{_bindir}/npa-tool +%endif +%{_bindir}/openpgp-tool +%{_bindir}/opensc-explorer +%{_bindir}/opensc-tool +%{_bindir}/opensc-asn1 +%{_bindir}/piv-tool +%{_bindir}/pkcs11-tool +%{_bindir}/pkcs15-crypt +%{_bindir}/pkcs15-init +%{_bindir}/pkcs15-tool +%{_bindir}/sc-hsm-tool +%{_bindir}/dnie-tool +%{_bindir}/westcos-tool +%{_bindir}/egk-tool +%{_bindir}/goid-tool +%{_bindir}/dtrust-tool +%{_bindir}/lteid-tool +%{_datadir}/opensc/ +%{_mandir}/man1/cardos-tool.1* +%{_mandir}/man1/cryptoflex-tool.1* +%{_mandir}/man1/eidenv.1* +%{_mandir}/man1/gids-tool.1* +%{_mandir}/man1/goid-tool.1* +%{_mandir}/man1/iasecc-tool.1* +%{_mandir}/man1/netkey-tool.1* +%if ! 0%{?rhel} +%{_mandir}/man1/npa-tool.1* +%endif +%{_mandir}/man1/openpgp-tool.1* +%{_mandir}/man1/opensc-explorer.* +%{_mandir}/man1/opensc-tool.1* +%{_mandir}/man1/opensc-asn1.1* +%{_mandir}/man1/piv-tool.1* +%{_mandir}/man1/pkcs11-tool.1* +%{_mandir}/man1/pkcs15-crypt.1* +%{_mandir}/man1/pkcs15-init.1* +%{_mandir}/man1/pkcs15-tool.1* +%{_mandir}/man1/sc-hsm-tool.1* +%{_mandir}/man1/westcos-tool.1* +%{_mandir}/man1/dnie-tool.1* +%{_mandir}/man1/egk-tool.1* +%{_mandir}/man1/dtrust-tool.1* +%{_mandir}/man1/lteid-tool.1* +%{_mandir}/man5/pkcs15-profile.5* + +%files libs +%ifarch %{ix86} +%{_mandir}/man5/opensc-%{_arch}.conf.5* +%else +%config(noreplace) %{_sysconfdir}/opensc.conf +%{_mandir}/man5/opensc.conf.5* +%endif + +%config(noreplace) %{_sysconfdir}/opensc-%{_arch}.conf +# Co-owned with p11-kit so it is not hard dependency +%dir %{_datadir}/p11-kit +%dir %{_datadir}/p11-kit/modules +%{_datadir}/p11-kit/modules/opensc.module +%{_libdir}/lib*.so.* +%{_libdir}/opensc-pkcs11.so +%{_libdir}/pkcs11-spy.so +%{_libdir}/onepin-opensc-pkcs11.so +%dir %{_libdir}/pkcs11 +%{_libdir}/pkcs11/opensc-pkcs11.so +%{_libdir}/pkcs11/onepin-opensc-pkcs11.so +%{_libdir}/pkcs11/pkcs11-spy.so + +# For OpenPACE +%if ! 0%{?rhel} +%config(noreplace) %{_sysconfdir}/eac/cvc/DESCHSMCVCA00001 +%config(noreplace) %{_sysconfdir}/eac/cvc/DESRCACC100001 +%endif + +%changelog +%autochangelog diff --git a/solaris/Makefile b/solaris/Makefile deleted file mode 100644 index 1ee69c9f8b..0000000000 --- a/solaris/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -PACKAGE=OSCopensc -PACKAGE_NAME=opensc -VERSION=CVS -PWD=pwd -CONFIGURE_PREFIX=$(PWD:sh)/.. -CONFIGURE=${CONFIGURE_PREFIX}/configure -CONFIGURE_ARGS=--prefix=/usr --sysconfdir=/etc/opensc --mandir=/usr/share/man --enable-pcsc --enable-openct -CONFIG_GUESS=${CONFIGURE_PREFIX}/config.guess -UNAME_ARCH=/usr/bin/uname -p -PLATFORM = $(CONFIG_GUESS:sh) -ARCH = $(UNAME_ARCH:sh) - -build: - @echo "Setup platform specific build directory build-${PLATFORM}" - mkdir -p build-${PLATFORM} - ( cd build-${PLATFORM}; CC=cc PCSC_CFLAGS=-I/usr/include/smartcard ${CONFIGURE} ${CONFIGURE_ARGS}; make ) - -dist: - @echo "Setup platform specific dist directory dist-${PLATFORM}" - mkdir -p dist-${PLATFORM} - @echo "Performing Installing in dist directory" - ( cd build-${PLATFORM}; make DESTDIR=`pwd`/../dist-${PLATFORM} install ) -package: - @echo "Setup package meta files" - -cp proto dist-${PLATFORM} - -sed "s|@ARCH@|${ARCH}|" dist-${PLATFORM}/checkinstall - -sed -e "s|@ARCH@|${ARCH}|" \ - -e "s|@VERSION@|${VERSION}|" \ - -e "s|@PACKAGE@|${PACKAGE}|" \ - -e "s|@PACKAGE_NAME@|${PACKAGE_NAME}|" \ - dist-${PLATFORM}/pkginfo - mkdir -p dist-${PLATFORM}/etc/opensc - -cp opensc.conf-dist dist-${PLATFORM}/etc/opensc/opensc.conf - @echo "Creating package" - ( \ - cd dist-${PLATFORM}; \ - pkgmk -o -r . -d . -f proto; \ - pkgtrans -s . ../OSCopensc-${VERSION}-${PLATFORM}.pkg ${PACKAGE} \ - ) diff --git a/solaris/README b/solaris/README deleted file mode 100644 index 2ea098daff..0000000000 --- a/solaris/README +++ /dev/null @@ -1,13 +0,0 @@ -Creating an installable package for Solaris 10 -============================================== - -The files in this directory are an attempt to ease -the building of opensc packages for Solaris 10. - -The basic steps to create a Solaris 10 package are: -# make build -# make dist -# make package - -NOTE: If you are using the GNU compiler you will - need to adjust the Makefile accordingly. diff --git a/solaris/checkinstall.in b/solaris/checkinstall.in deleted file mode 100755 index 9c198b9e40..0000000000 --- a/solaris/checkinstall.in +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -expected_platform="@ARCH@" -platform=`uname -p` -if [ ${platform} != ${expected_platform} ]; then - echo "This package must be installed on ${expected_platform}" - exit 1 -fi -exit 0 diff --git a/solaris/opensc.conf-dist b/solaris/opensc.conf-dist deleted file mode 100644 index 4c0bc81735..0000000000 --- a/solaris/opensc.conf-dist +++ /dev/null @@ -1,286 +0,0 @@ -# Configuration file for OpenSC -# Example configuration file - -# NOTE: All key-value pairs must be terminated by a semicolon. - -# Default values for any application -# These can be overrided by an application -# specific configuration block. -app default { - # Amount of debug info to print - # - # A greater value means more debug info. - # Default: 0 - # - debug = 0; - - # The file to which debug output will be written - # - # A special value of 'stdout' is recognized. - # Default: stdout - # - # debug_file = /tmp/opensc-debug.log; - - # The file to which errors will be written - # - # A special value of 'stderr' is recognized. - # Default: stderr - # - # error_file = /tmp/opensc-errors.log; - - # Where to find the *.profile files for pkcs15init; - - profile_dir = /usr/share/opensc; - - # What reader drivers to load at start-up - # - # A special value of 'internal' will load all - # statically linked drivers. If an unknown (ie. not - # internal) driver is supplied, a separate configuration - # configuration block has to be written for the driver. - # Default: internal - # NOTE: if "internal" keyword is used, must be the - # last entry in reader_drivers list - # - reader_drivers = openct, pcsc, ctapi; - - reader_driver ctapi { - # module /usr/local/towitoko/lib/libtowitoko.so { - # CT-API ports: - # 0..3 COM1..4 - # 4 Printer - # 5 Modem - # 6..7 LPT1..2 - # ports = 0; - # } - } - - # Define parameters specific to your readers. - # The following section shows definitions for PC/SC readers, - # but the same set of variables are applicatable to ctapi and - # openct readers, simply by using "reader_driver ctapi" and - # "reader_driver openct", respectively. - reader_driver pcsc { - # Whether to transform some APDU's from one case to another - # Possible values: - # none: Don't transform any APDU's - # case4as3: For T=0, send a case 4 APDU as case 3, - # (no Lc byte) the card will send back - # a 61xx SW, and we will follow up with a - # GetResponse command - # The SCM SCR111, Sun SCF, and e-gate readers - # seem to require this. - # case1as2: For T=0, send a case 1 APDU as case 2. - # (append an Le byte of 0) - # The Sun SCF and e-gate readers seem to - # require this - # case1as2_always: for any T=0/1, send a case 1 APDU as - # case 2. - # The Sun SCF reader may require this - # Default: none - # - apdu_masquerade = none; - # - # This sets the maximum send and receive sizes. - # Some IFD handlers do not properly handle APDUs with - # large lc or le bytes. - # - max_send_size = 252; - max_recv_size = 252; - # - # EXPERIMENTAL: Enable CCID pinpad support - # implemented (at least) in the libccid driver. - #use_ccid_pin_cmd = true; - } - - # What card drivers to load at start-up - # - # A special value of 'internal' will load all - # statically linked drivers. If an unknown (ie. not - # internal) driver is supplied, a separate configuration - # configuration block has to be written for the driver. - # Default: internal - # NOTE: When "internal" keyword is used, must be last entry - # - # card_drivers = customcos, internal; - - # Card driver configuration blocks. - - # For all drivers, you can specify ATRs of cards that - # should be handled by this driver (in addition to the - # list of compiled-in ATRs). - # - # The supported internal card driver names are - # flex Cryptoflex/Multiflex - # setcos Setec - # etoken Aladdin eToken and other CardOS based cards - # gpk GPK 4K/8K/16K - # mcrd MICARDO 2.1 - # miocos MioCOS 1.1 - # openpgp OpenPGP card - # tcos TCOS 2.0 - # emv EMV compatible cards - - # GPK card driver additional ATR entry: - card_driver gpk { - # atr = 00:11:22; - } - - # For card drivers loaded from an external shared library/DLL, - # you need to specify the path name of the module - # - # card_driver customcos { - # The location of the driver library - # module = /usr/lib/opensc/drivers/card_customcos.so; - # atr = 00:11:22:33:44; - # atr = 55:66:77:88:99:aa:bb; - # } - - # Force using specific card driver - # - # If this option is present, OpenSC will use the supplied - # driver with all inserted cards. - # - # Default: autodetect - # - # force_card_driver = miocos; - - # Below are the framework specific configuration blocks. - - # PKCS #15 - framework pkcs15 { - # Whether to use the cache files in the user's - # home directory. - # - # At the moment you have to 'teach' the card to the - # system by: - # pkcs15-tool -L - # - # WARNING: Caching shouldn't be used in setuid root - # applications. - # Default: false - # - use_caching = true; - # Enable pkcs15 emulation - # Default: yes - enable_pkcs15_emulation = yes; - # Try pkcs15 emulation code first (before the normal - # pkcs15 processing). - # Default: no - try_emulation_first = no; - # Enable builtin emulators - # Default: yes - enable_builtin_emulation = yes; - # list of the builtin pkcs15 emulators to test - # possible values: esteid, openpgp, netkey, netkey, - # starcert, infocamere, postecert - builtin_emulators = esteid, openpgp, netkey, netkey, starcert, infocamere, postecert; - - # additional pkcs15 emulators (dynamic or builtin with - # a different atr etc.) - # emulate foo { - # module = builtin; - # atr = 11:22:33:44; - #} - } - - # Estonian ID card and Micardo driver currently play together with T=0 only. - # In theory only the 'cold' ATR should be specified, as T=0 will be the preferred - # protocol once you boot it up with T=0, but be paranoid. - - # Generic format: card_atr - # Only parameter currently understood is force_protocol - card_atr 3b:6e:00:ff:45:73:74:45:49:44:20:76:65:72:20:31:2e:30 { - force_protocol = t0; - } - card_atr 3b:fe:94:00:ff:80:b1:fa:45:1f:03:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:43 { - force_protocol = t0; - } -} - -# For applications that use SCAM (pam_opensc, sia_opensc) -app scam { - framework pkcs15 { - use_caching = false; - } -} - -# Parameters for the OpenSC PKCS11 module -app opensc-pkcs11 { - pkcs11 { - # Maxmimum number of slots per smart card. - # If the card has fewer keys than defined here, - # the remaining number of slots will be empty. - # - # Note that there is currently a compile time - # maximum on the overall number of slots - # the pkcs11 module is able to handle. - num_slots = 4; - - # Normally, the pkcs11 module will create - # the full number of slots defined above by - # num_slots. If there are fewer pins/keys on - # the card, the remaining keys will be empty - # (and you will be able to create new objects - # within them). - # - # Set this option to true to hide these empty - # slots. - hide_empty_tokens = true; - - # By default, the OpenSC PKCS#11 module will - # try to lock this card once you have authenticated - # to the card via C_Login. This is done so that no - # other user can connect to the card and perform - # crypto operations (which may be possible because - # you have already authenticated with the card). - # - # However, this also means that no other application - # that _you_ run can use the card until your application - # has done a C_Logout or C_Finalize. In the case of - # Netscape or Mozilla, this does not happen until - # you exit the browser. - lock_login = true; - - # Normally, the pkcs11 module will not cache PINs - # presented via C_Login. However, some cards - # may not work properly with OpenSC; for instance - # when you have two keys on your card that get - # stored in two different directories. - # - # In this case, you can turn on PIN caching by setting - # cache_pins = true - # - # Default: false - cache_pins = false; - - # Set this value to false if you want to enfore on-card - # keypair generation - # - # Default: true - soft_keygen_allowed = true; - } -} - -# Parameters for the OpenSC PKCS11-Spy module, that logs all the -# communication between a pkcs11 module and it's calling application: -# app <--> pkcs11-spy <--> pkcs11 module -app pkcs11-spy { - spy { - # Where to log to. - # - # By default, the value of the PKCS11SPY_OUTPUT environment - # variable is used. And if that one isn't defined: stderr - # is used. - # - #output = /tmp/pkcs11-spy.log; - - # Which PKCS11 module to load. - # - # By default, the value of the PKCS11SPY environment - # variable is used. And if that one isn't defined, - # opensc-pkcs11.so is used. - # - #module = opensc-pkcs11.so; - } -} diff --git a/solaris/pkginfo.in b/solaris/pkginfo.in deleted file mode 100644 index 2e1cd1ffe1..0000000000 --- a/solaris/pkginfo.in +++ /dev/null @@ -1,12 +0,0 @@ -PKG="OSCopensc" -NAME="opensc" -VERSION="@VERSION@" -ARCH="@ARCH@" -CLASSES="none" -CATEGORY="drivers" -VENDOR="WW" -PSTAMP="26thFeb2005" -EMAIL="william@wanders.org" -ISTATES="S s 1 2 3" -RSTATES="S s 1 2 3" -BASEDIR="/" diff --git a/solaris/proto b/solaris/proto deleted file mode 100644 index 665d611b61..0000000000 --- a/solaris/proto +++ /dev/null @@ -1,118 +0,0 @@ -d none usr 0755 root sys -d none usr/share 0755 root other -d none usr/share/man 0755 root other -d none usr/share/man/man1 0755 root other -f none usr/share/man/man1/pkcs15-crypt.1 0644 root other -f none usr/share/man/man1/pkcs15-init.1 0644 root other -f none usr/share/man/man1/cryptoflex-tool.1 0644 root other -f none usr/share/man/man1/opensc-config.1 0644 root other -f none usr/share/man/man1/opensc-explorer.1 0644 root other -f none usr/share/man/man1/opensc-tool.1 0644 root other -f none usr/share/man/man1/pkcs15-tool.1 0644 root other -f none usr/share/man/man1/pkcs11-tool.1 0644 root other -f none usr/share/man/man1/cardos-info.1 0644 root other -d none usr/share/man/man3 0755 root other -f none usr/share/man/man3/sc_connect_card.3 0644 root other -f none usr/share/man/man3/sc_detect_card_presence.3 0644 root other -f none usr/share/man/man3/sc_disconnect_card.3 0644 root other -f none usr/share/man/man3/sc_establish_context.3 0644 root other -f none usr/share/man/man3/sc_file.3 0644 root other -f none usr/share/man/man3/sc_file_free.3 0644 root other -f none usr/share/man/man3/sc_file_new.3 0644 root other -f none usr/share/man/man3/sc_list_files.3 0644 root other -f none usr/share/man/man3/sc_lock.3 0644 root other -f none usr/share/man/man3/sc_read_binary.3 0644 root other -f none usr/share/man/man3/sc_read_record.3 0644 root other -f none usr/share/man/man3/sc_release_context.3 0644 root other -f none usr/share/man/man3/sc_select_file.3 0644 root other -f none usr/share/man/man3/sc_pkcs15_compute_signature.3 0644 root other -d none usr/share/man/man5 0755 root other -f none usr/share/man/man5/pkcs15-profile.5 0644 root other -d none usr/share/man/man7 0755 root other -f none usr/share/man/man7/opensc.7 0644 root other -f none usr/share/man/man7/pkcs15.7 0644 root other -d none usr/share/opensc 0755 root other -f none usr/share/opensc/opensc.conf.example 0644 root other -f none usr/share/opensc/cyberflex.profile 0644 root other -f none usr/share/opensc/flex.profile 0644 root other -f none usr/share/opensc/gpk.profile 0644 root other -f none usr/share/opensc/miocos.profile 0644 root other -f none usr/share/opensc/etoken.profile 0644 root other -f none usr/share/opensc/jcop.profile 0644 root other -f none usr/share/opensc/oberthur.profile 0644 root other -f none usr/share/opensc/starcos.profile 0644 root other -f none usr/share/opensc/pkcs15.profile 0644 root other -d none usr/lib 0755 root bin -s none usr/lib/libscconf.so.0=libscconf.so.0.0.9 -f none usr/lib/libscconf.so.0.0.9 0755 root bin -s none usr/lib/libscconf.so=libscconf.so.0.0.9 -f none usr/lib/libscconf.la 0755 root bin -f none usr/lib/libscconf.a 0644 root bin -s none usr/lib/libopensc.so.0=libopensc.so.0.0.9 -f none usr/lib/libopensc.so.0.0.9 0755 root bin -s none usr/lib/libopensc.so=libopensc.so.0.0.9 -d none usr/lib/pkgconfig 0755 root bin -f none usr/lib/pkgconfig/libopensc.pc 0644 root bin -f none usr/lib/pkgconfig/libpkcs15init.pc 0644 root bin -f none usr/lib/pkgconfig/libscconf.pc 0644 root bin -f none usr/lib/libopensc.la 0755 root bin -f none usr/lib/libopensc.a 0644 root bin -d none usr/lib/pkcs11 0755 root bin -s none usr/lib/pkcs11/libpkcs11.so.0=libpkcs11.so.0.0.9 -f none usr/lib/pkcs11/opensc-pkcs11.so 0755 root bin -f none usr/lib/pkcs11/opensc-pkcs11.la 0755 root bin -f none usr/lib/pkcs11/opensc-pkcs11.a 0644 root bin -f none usr/lib/pkcs11/libpkcs11.so.0.0.9 0755 root bin -s none usr/lib/pkcs11/libpkcs11.so=libpkcs11.so.0.0.9 -f none usr/lib/pkcs11/libpkcs11.la 0755 root bin -f none usr/lib/pkcs11/libpkcs11.a 0644 root bin -f none usr/lib/pkcs11/pkcs11-spy.so 0755 root bin -f none usr/lib/pkcs11/pkcs11-spy.la 0755 root bin -f none usr/lib/pkcs11/pkcs11-spy.a 0644 root bin -f none usr/lib/libpkcs15init.so.0.0.9 0755 root bin -s none usr/lib/libpkcs15init.so.0=libpkcs15init.so.0.0.9 -s none usr/lib/libpkcs15init.so=libpkcs15init.so.0.0.9 -f none usr/lib/libpkcs15init.la 0755 root bin -f none usr/lib/libpkcs15init.a 0644 root bin -d none usr/lib/opensc 0755 root bin -f none usr/lib/opensc/engine_opensc.so 0755 root bin -f none usr/lib/opensc/engine_opensc.la 0755 root bin -f none usr/lib/opensc/engine_opensc.a 0644 root bin -f none usr/lib/opensc/engine_pkcs11.so 0755 root bin -f none usr/lib/opensc/engine_pkcs11.la 0755 root bin -f none usr/lib/opensc/engine_pkcs11.a 0644 root bin -d none usr/include 0755 root bin -d none usr/include/opensc 0755 root bin -d none usr/include/opensc/rsaref 0755 root bin -f none usr/include/opensc/rsaref/pkcs11.h 0644 root bin -f none usr/include/opensc/rsaref/pkcs11f.h 0644 root bin -f none usr/include/opensc/rsaref/pkcs11t.h 0644 root bin -f none usr/include/opensc/rsaref/unix.h 0644 root bin -f none usr/include/opensc/rsaref/win32.h 0644 root bin -f none usr/include/opensc/scconf.h 0644 root bin -f none usr/include/opensc/opensc.h 0644 root bin -f none usr/include/opensc/pkcs15.h 0644 root bin -f none usr/include/opensc/cardctl.h 0644 root bin -f none usr/include/opensc/asn1.h 0644 root bin -f none usr/include/opensc/log.h 0644 root bin -f none usr/include/opensc/ui.h 0644 root bin -f none usr/include/opensc/errors.h 0644 root bin -f none usr/include/opensc/types.h 0644 root bin -f none usr/include/opensc/pkcs15-init.h 0644 root bin -f none usr/include/opensc/pkcs11.h 0644 root bin -d none usr/bin 0755 root bin -f none usr/bin/opensc-config 0755 root bin -f none usr/bin/opensc-tool 0755 root bin -f none usr/bin/opensc-explorer 0755 root bin -f none usr/bin/pkcs15-tool 0755 root bin -f none usr/bin/pkcs15-crypt 0755 root bin -f none usr/bin/pkcs11-tool 0755 root bin -f none usr/bin/cardos-info 0755 root bin -f none usr/bin/eidenv 0755 root bin -f none usr/bin/cryptoflex-tool 0755 root bin -f none usr/bin/pkcs15-init 0755 root bin -d none etc 0755 root sys -d none etc/opensc 0755 root sys -f none etc/opensc/opensc.conf 0644 root sys -i checkinstall=checkinstall -i pkginfo=pkginfo diff --git a/src/Makefile.am b/src/Makefile.am index ab1dd1a316..bf71b61f7c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,8 +2,12 @@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak # Order IS important -SUBDIRS = common scconf pkcs15init sm \ - libopensc pkcs11 tools tests minidriver +SUBDIRS = common scconf ui pkcs15init sm \ + libopensc pkcs11 tools minidriver + +if ENABLE_TESTS +SUBDIRS += tests +endif if ENABLE_SM SUBDIRS += smm diff --git a/src/Makefile.mak b/src/Makefile.mak index 52be9224c5..8dee619666 100644 --- a/src/Makefile.mak +++ b/src/Makefile.mak @@ -1,7 +1,7 @@ TOPDIR = .. -SUBDIRS = common scconf sm pkcs15init \ - libopensc pkcs11 tools tests +SUBDIRS = common scconf ui sm pkcs15init \ + libopensc pkcs11 tools default: all @@ -15,6 +15,16 @@ SUBDIRS = $(SUBDIRS) minidriver SUBDIRS = $(SUBDIRS) smm !ENDIF -all clean:: +!IF "$(TESTS_DEF)" == "/DENABLE_TESTS" +SUBDIRS = $(SUBDIRS) tests +!ENDIF + +all:: + copy /y common\compat_getopt.h getopt.h + @for %i in ( $(SUBDIRS) ) do \ + @cmd /c "cd %i && $(MAKE) /nologo /f Makefile.mak $@" + +clean:: @for %i in ( $(SUBDIRS) ) do \ @cmd /c "cd %i && $(MAKE) /nologo /f Makefile.mak $@" + del /Q getopt.h diff --git a/src/common/Makefile.am b/src/common/Makefile.am index df8c7dc5bd..9ecbffe8fd 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -8,10 +8,32 @@ dist_noinst_DATA = \ LICENSE.compat_getopt compat_getopt.txt \ compat_getopt_main.c \ README.compat_strlcpy compat_strlcpy.3 +noinst_HEADERS = compat_strlcat.h compat_strlcpy.h compat_strnlen.h compat_getpass.h \ + compat_getopt.h simclist.h libpkcs11.h libscdl.h compat_overflow.h constant-time.h AM_CPPFLAGS = -I$(top_srcdir)/src libcompat_la_SOURCES = \ + compat_dummy.c \ + compat_strlcat.c \ + compat_strlcpy.c \ + compat_strnlen.c \ + compat_getpass.c \ + compat_getopt.c \ + compat_report_rangecheckfailure.c \ + compat___iob_func.c \ + compat_overflow.c \ + simclist.c + +compat_getopt_main_LDADD = libcompat.la + +libpkcs11_la_SOURCES = libpkcs11.c + +libscdl_la_SOURCES = libscdl.c +libscdl_la_LIBADD = $(LDL_LIBS) + +TIDY_FLAGS = $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +TIDY_FILES = \ compat_dummy.c \ compat_strlcat.h compat_strlcat.c \ compat_strlcpy.h compat_strlcpy.c \ @@ -20,10 +42,10 @@ libcompat_la_SOURCES = \ compat_getopt.h compat_getopt.c \ compat_report_rangecheckfailure.c \ compat___iob_func.c \ - simclist.c simclist.h - -compat_getopt_main_LDADD = libcompat.la - -libpkcs11_la_SOURCES = libpkcs11.c libpkcs11.h + compat_overflow.h compat_overflow.c \ + simclist.c simclist.h \ + libpkcs11.c libscdl.c \ + constant-time.h -libscdl_la_SOURCES = libscdl.c libscdl.h +check-local: + if [ -x "$(CLANGTIDY)" ]; then clang-tidy -config='' --checks='$(TIDY_CHECKS)' --warnings-as-errors='$(TIDY_CHECKS)' -header-filter=.* $(addprefix $(srcdir)/,$(TIDY_FILES)) -- $(TIDY_FLAGS); fi diff --git a/src/common/Makefile.mak b/src/common/Makefile.mak index 82273080fd..d4f00df820 100644 --- a/src/common/Makefile.mak +++ b/src/common/Makefile.mak @@ -1,6 +1,6 @@ TOPDIR = ..\.. -COMMON_OBJECTS = compat_getpass.obj compat_getopt.obj compat_strlcpy.obj compat_strlcat.obj simclist.obj compat_report_rangecheckfailure.obj compat___iob_func.obj +COMMON_OBJECTS = compat_getpass.obj compat_getopt.obj compat_strlcpy.obj compat_strlcat.obj simclist.obj compat_report_rangecheckfailure.obj compat___iob_func.obj compat_overflow.obj all: common.lib libpkcs11.lib libscdl.lib @@ -9,7 +9,7 @@ all: common.lib libpkcs11.lib libscdl.lib common.lib: $(COMMON_OBJECTS) lib $(LIBFLAGS) /out:common.lib $(COMMON_OBJECTS) -libpkcs11.lib: libpkcs11.obj libscdl.obj +libpkcs11.lib: libpkcs11.obj lib $(LIBFLAGS) /out:libpkcs11.lib libpkcs11.obj libscdl.lib: libscdl.obj diff --git a/src/common/README.compat_getopt b/src/common/README.compat_getopt index 7b4b770b89..2fdf213d36 100644 --- a/src/common/README.compat_getopt +++ b/src/common/README.compat_getopt @@ -94,14 +94,14 @@ WHY RE-INVENT THE WHEEL? ======================== I re-implemented getopt, getopt_long, and getopt_long_only because -there were noticable bugs in several versions of the GNU +there were noticeable bugs in several versions of the GNU implementations, and because the GNU versions aren't always available on some systems (*BSD, for example.) Other systems don't include any sort of standard argument parser (Win32 with Microsoft tools, for example, has no getopt.) These should do all the expected Unix- and GNU-style argument -parsing, including permution, bunching, long options with single or +parsing, including permutation, bunching, long options with single or double dashes (double dashes are required if you use my_getopt_long,) and optional arguments for both long and short options. A word with double dashes all by themselves halts argument @@ -112,11 +112,11 @@ separated by '='. As with the GNU versions, a '+' prefix to the short option specification (or the POSIXLY_CORRECT environment variable) disables -permution, a '-' prefix to the short option specification returns 1 +permutation, a '-' prefix to the short option specification returns 1 for non-options, ':' after a short option indicates a required argument, and '::' after a short option specification indicates an optional argument (which must appear in the same word.) If you'd like -to recieve ':' instead of '?' for missing option arguments, prefix the +to receive ':' instead of '?' for missing option arguments, prefix the short option specification with ':'. The original intent was to re-implement the documented behavior of diff --git a/src/common/README.compat_strlcpy b/src/common/README.compat_strlcpy index 2f7c81efc1..6169e7d5e2 100644 --- a/src/common/README.compat_strlcpy +++ b/src/common/README.compat_strlcpy @@ -1,5 +1,5 @@ strncpy() is unsafe since it does not always add a final NUL-byte. -OpenBSD developped a safer version called strlcpy(). +OpenBSD developed a safer version called strlcpy(). Use "man -l strlcpy.3" to read the manpage. diff --git a/src/common/compat___iob_func.c b/src/common/compat___iob_func.c index 86a1d39fa6..9eefb932da 100644 --- a/src/common/compat___iob_func.c +++ b/src/common/compat___iob_func.c @@ -1,4 +1,4 @@ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -7,14 +7,20 @@ #if defined(_MSC_VER) && (_MSC_VER >= 1900) // needed for OpenSSL static link // only for vs 2015 or later +// +// this is a horrible hack, the correct fix would be to recompile OpenSSL with +// VS 2015 or later. However, since in OpenSC, we don't need OpenSSL to send +// output to any of these buffers, we don't need to cope with runtime errors +// induced by this hack. See https://stackoverflow.com/a/34655235 for details. +// #pragma comment(lib, "legacy_stdio_definitions.lib") #include FILE * __cdecl __iob_func(void) { - static FILE *my_iob[3]; - my_iob[0] = stdin; - my_iob[1] = stdout; - my_iob[2] = stderr; + static FILE my_iob[3]; + my_iob[0] = *stdin; + my_iob[1] = *stdout; + my_iob[2] = *stderr; return my_iob; } #endif diff --git a/src/common/compat_getopt.c b/src/common/compat_getopt.c index e44ebca63e..5db6873a84 100644 --- a/src/common/compat_getopt.c +++ b/src/common/compat_getopt.c @@ -23,7 +23,7 @@ * DEALINGS IN THE SOFTWARE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -41,7 +41,7 @@ char *my_optarg=0; /* this is the plain old UNIX getopt, with GNU-style extensions. */ /* if you're porting some piece of UNIX software, this is all you need. */ -/* this supports GNU-style permution and optional arguments */ +/* this supports GNU-style permutation and optional arguments */ int my_getopt(int argc, char * argv[], const char *opts) { @@ -143,7 +143,7 @@ int _my_getopt_internal(int argc, char * argv[], const char *shortopts, const struct option *longopts, int *longind, int long_only) { - char mode, colon_mode = *shortopts; + char mode, colon_mode; int shortoff = 0, opt = -1; if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+'; @@ -230,7 +230,8 @@ int _my_getopt_internal(int argc, char * argv[], const char *shortopts, argv[0], longopts[found].name); } else { my_optarg = argv[my_optind] + ++charind; - charind = 0; + /* charind not read again + * charind = 0;*/ } } else if(longopts[found].has_arg == 1) { if(++my_optind >= argc) { diff --git a/src/common/compat_getopt.h b/src/common/compat_getopt.h index 8ff5401a27..b1f36337fe 100644 --- a/src/common/compat_getopt.h +++ b/src/common/compat_getopt.h @@ -30,10 +30,6 @@ #include "config.h" #endif -#if defined(HAVE_GETOPT_H) && defined(HAVE_GETOPT_LONG) && defined(HAVE_GETOPT_LONG_ONLY) -#include -#else - /* Prevent mingw32 from including an incompatible getopt implementation */ #define __GETOPT_H__ @@ -86,6 +82,4 @@ extern int _my_getopt_internal(int argc, char * argv[], const char *shortopts, } #endif -#endif /* HAVE_GETOPT_H && HAVE_GETOPT_LONG && HAVE_GETOPT_LONG_ONLY */ - #endif /* MY_GETOPT_H_INCLUDED */ diff --git a/src/common/compat_getopt_main.c b/src/common/compat_getopt_main.c index 827ba01901..d4e91d1739 100644 --- a/src/common/compat_getopt_main.c +++ b/src/common/compat_getopt_main.c @@ -94,12 +94,12 @@ handle(char *progname, if (rotate && isalpha(c)) { const char *letters = "abcdefghijklmnopqrstuvwxyz"; - char *match; + const char *match; if ((match = strchr(letters, tolower(c)))) { char rc = letters[(match - letters + rotate) % 26]; if (isupper(c)) - rc = toupper(rc); + rc = toupper((unsigned char)rc); c = rc; } } diff --git a/src/common/compat_getpass.c b/src/common/compat_getpass.c index 22a1abd14b..90e19f1ae1 100644 --- a/src/common/compat_getpass.c +++ b/src/common/compat_getpass.c @@ -1,4 +1,4 @@ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/src/common/compat_overflow.c b/src/common/compat_overflow.c new file mode 100644 index 0000000000..049c3f9533 --- /dev/null +++ b/src/common/compat_overflow.c @@ -0,0 +1,45 @@ +/* + * compat_overflow.c: Reimplementation of GCC/Clang's built-in + * functions to perform arithmetic with overflow checking + * + * Copyright (C) Frank Morgner + * + * This file is part of OpenSC. + * + * 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 + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef HAVE_BUILTIN_OVERFLOW +#include +#include +#include + +#define ADD_OVERFLOW(func, type, max) \ + bool func (type x, type y, type *sum) \ + { \ + if (NULL == sum || max - x < y) \ + return true; \ + *sum = x + y; \ + return false; \ + } + +ADD_OVERFLOW(__builtin_uadd_overflow, unsigned, UINT_MAX) +ADD_OVERFLOW(__builtin_uaddl_overflow, unsigned long, ULONG_MAX) +ADD_OVERFLOW(__builtin_uaddll_overflow, unsigned long long, ULLONG_MAX) +ADD_OVERFLOW(__builtin_zuadd_overflow, size_t, SIZE_MAX) +#endif diff --git a/src/common/compat_overflow.h b/src/common/compat_overflow.h new file mode 100644 index 0000000000..3133324521 --- /dev/null +++ b/src/common/compat_overflow.h @@ -0,0 +1,61 @@ +/* + * compat_overflow.h: Reimplementation of GCC/Clang's built-in + * functions to perform arithmetic with overflow checking + * + * Copyright (C) Frank Morgner + * + * This file is part of OpenSC. + * + * 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 + */ +#ifndef __COMPAT_OVERFLOW_H +#define __COMPAT_OVERFLOW_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef HAVE_BUILTIN_OVERFLOW +#include + +bool __builtin_uadd_overflow (unsigned x, unsigned y, unsigned *sum); +bool __builtin_uaddl_overflow (unsigned long x, unsigned long y, unsigned long *sum); +bool __builtin_uaddll_overflow(unsigned long long x, unsigned long long y, unsigned long long *sum); + +bool __builtin_zuadd_overflow (size_t x, size_t y, size_t *sum); +#else + +#define __builtin_zuadd_overflow __builtin_add_overflow + +#endif +/* TODO +bool __builtin_usub_overflow (unsigned x, unsigned y, unsigned *diff); +bool __builtin_usubl_overflow (unsigned long x, unsigned long y, unsigned long *diff); +bool __builtin_usubll_overflow(unsigned long long x, unsigned long long y, unsigned long long *diff); +bool __builtin_umul_overflow (unsigned x, unsigned y, unsigned *prod); +bool __builtin_umull_overflow (unsigned long x, unsigned long y, unsigned long *prod); +bool __builtin_umulll_overflow(unsigned long long x, unsigned long long y, unsigned long long *prod); +bool __builtin_sadd_overflow (int x, int y, int *sum); +bool __builtin_saddl_overflow (long x, long y, long *sum); +bool __builtin_saddll_overflow(long long x, long long y, long long *sum); +bool __builtin_ssub_overflow (int x, int y, int *diff); +bool __builtin_ssubl_overflow (long x, long y, long *diff); +bool __builtin_ssubll_overflow(long long x, long long y, long long *diff); +bool __builtin_smul_overflow (int x, int y, int *prod); +bool __builtin_smull_overflow (long x, long y, long *prod); +bool __builtin_smulll_overflow(long long x, long long y, long long *prod); +*/ + +#endif diff --git a/src/common/compat_report_rangecheckfailure.c b/src/common/compat_report_rangecheckfailure.c index f90e87a851..92b70fc634 100644 --- a/src/common/compat_report_rangecheckfailure.c +++ b/src/common/compat_report_rangecheckfailure.c @@ -13,9 +13,9 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/src/common/compat_strlcat.c b/src/common/compat_strlcat.c index af373f11b9..e273d469b1 100644 --- a/src/common/compat_strlcat.c +++ b/src/common/compat_strlcat.c @@ -27,11 +27,11 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif -#ifndef HAVE_STRLCAT +#if !defined(HAVE_DECL_STRLCAT) || !HAVE_DECL_STRLCAT #include #include diff --git a/src/common/compat_strlcat.h b/src/common/compat_strlcat.h index 5ac0c7cc0d..9debdfadb0 100644 --- a/src/common/compat_strlcat.h +++ b/src/common/compat_strlcat.h @@ -1,8 +1,20 @@ /** * @file - * @brief prototypes of strlcpy()/strlcat() imported from OpenBSD + * @brief prototypes of strlcat() imported from OpenBSD */ -#ifndef HAVE_STRLCAT +#ifndef __COMPAT_STRLCAT_H +#define __COMPAT_STRLCAT_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if !defined(HAVE_DECL_STRLCAT) || !HAVE_DECL_STRLCAT +#include size_t strlcat(char *dst, const char *src, size_t siz); +#else +#include +#endif + #endif diff --git a/src/common/compat_strlcpy.c b/src/common/compat_strlcpy.c index 88f3a5fba8..6993d0df05 100644 --- a/src/common/compat_strlcpy.c +++ b/src/common/compat_strlcpy.c @@ -16,11 +16,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif -#ifndef HAVE_STRLCPY /* empty file if strlcpy is available */ +#if !defined(HAVE_DECL_STRLCPY) || !HAVE_DECL_STRLCPY #include #include @@ -32,7 +32,7 @@ * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t -strlcpy(char *dst, const char *src, size_t siz) +_strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; diff --git a/src/common/compat_strlcpy.h b/src/common/compat_strlcpy.h index a293142a29..0e09e007b5 100644 --- a/src/common/compat_strlcpy.h +++ b/src/common/compat_strlcpy.h @@ -28,9 +28,22 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** * @file - * @brief prototypes of strlcpy()/strlcat() imported from OpenBSD + * @brief prototypes of strlcpy() imported from OpenBSD */ -#ifndef HAVE_STRLCPY -size_t strlcpy(char *dst, const char *src, size_t siz); +#ifndef __COMPAT_STRLCPY_H +#define __COMPAT_STRLCPY_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if !defined(HAVE_DECL_STRLCPY) || !HAVE_DECL_STRLCPY +#include +#define strlcpy _strlcpy +size_t _strlcpy(char *dst, const char *src, size_t siz); +#else +#include +#endif + #endif diff --git a/src/common/compat_strnlen.h b/src/common/compat_strnlen.h index 3c1f352d6e..7c04180b17 100644 --- a/src/common/compat_strnlen.h +++ b/src/common/compat_strnlen.h @@ -3,6 +3,16 @@ * @brief prototype of strnlen() from OpenBSD */ +#ifndef __COMPAT_STRNLEN_H +#define __COMPAT_STRNLEN_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #ifndef HAVE_STRNLEN +#include size_t strnlen(const char *str, size_t maxlen); #endif + +#endif diff --git a/src/common/constant-time.h b/src/common/constant-time.h new file mode 100644 index 0000000000..3f4446d4d8 --- /dev/null +++ b/src/common/constant-time.h @@ -0,0 +1,134 @@ +/* Original source: https://github.com/openssl/openssl/blob/9890cc42daff5e2d0cad01ac4bf78c391f599a6e/include/internal/constant_time.h */ + +#ifndef CONSTANT_TIME_H +#define CONSTANT_TIME_H + +#include +#include + +#if !defined(inline) +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define constant_inline inline +#elif defined(__GNUC__) && __GNUC__ >= 2 +#elif defined(__GNUC__) && __GNUC__ >= 2 +#elif defined(_MSC_VER) +#define constant_inline __inline +#else +#define constant_inline +#endif +#else /* use what caller wants as inline may be from config.h */ +#define constant_inline inline /* inline */ +#endif + +/*- + * The boolean methods return a bitmask of all ones (0xff...f) for true + * and 0 for false. For example, + * if (a < b) { + * c = a; + * } else { + * c = b; + * } + * can be written as + * unsigned int lt = constant_time_lt(a, b); + * c = constant_time_select(lt, a, b); + */ + +static constant_inline unsigned int +value_barrier(unsigned int a) +{ + volatile unsigned int r = a; + return r; +} + +static constant_inline size_t +value_barrier_s(size_t a) +{ + volatile size_t r = a; + return r; +} + +/* MSB */ +static constant_inline size_t +constant_time_msb_s(size_t a) +{ + return 0 - (a >> (sizeof(a) * 8 - 1)); +} + +static constant_inline unsigned int +constant_time_msb(unsigned int a) +{ + return 0 - (a >> (sizeof(a) * 8 - 1)); +} + +/* Select */ +static constant_inline unsigned int +constant_time_select(unsigned int mask, unsigned int a, unsigned int b) +{ + return (value_barrier(mask) & a) | (value_barrier(~mask) & b); +} + +static constant_inline unsigned char +constant_time_select_8(unsigned char mask, unsigned char a, unsigned char b) +{ + return (unsigned char)constant_time_select(mask, a, b); +} + +static constant_inline size_t +constant_time_select_s(size_t mask, size_t a, size_t b) +{ + return (value_barrier_s(mask) & a) | (value_barrier_s(~mask) & b); +} + +/* Zero */ +static constant_inline unsigned int +constant_time_is_zero(unsigned int a) +{ + return constant_time_msb(~a & (a - 1)); +} + +static constant_inline size_t +constant_time_is_zero_s(size_t a) +{ + return constant_time_msb_s(~a & (a - 1)); +} + +/* Comparison*/ +static constant_inline size_t +constant_time_lt_s(size_t a, size_t b) +{ + return constant_time_msb_s(a ^ ((a ^ b) | ((a - b) ^ b))); +} + +static constant_inline unsigned int +constant_time_lt(unsigned int a, unsigned int b) +{ + return constant_time_msb(a ^ ((a ^ b) | ((a - b) ^ b))); +} + +static constant_inline unsigned int +constant_time_ge(unsigned int a, unsigned int b) +{ + return ~constant_time_lt(a, b); +} + +/* Equality*/ + +static constant_inline unsigned int +constant_time_eq(unsigned int a, unsigned int b) +{ + return constant_time_is_zero(a ^ b); +} + +static constant_inline size_t +constant_time_eq_s(size_t a, size_t b) +{ + return constant_time_is_zero_s(a ^ b); +} + +static constant_inline unsigned int +constant_time_eq_i(int a, int b) +{ + return constant_time_eq((unsigned int)a, (unsigned int)b); +} + +#endif /* CONSTANT_TIME_H */ diff --git a/src/common/libpkcs11.c b/src/common/libpkcs11.c index e65fda7ecb..5ad0dd3e56 100644 --- a/src/common/libpkcs11.c +++ b/src/common/libpkcs11.c @@ -16,10 +16,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -49,6 +49,7 @@ C_LoadModule(const char *mspec, CK_FUNCTION_LIST_PTR_PTR funcs) { sc_pkcs11_module_t *mod; CK_RV rv, (*c_get_function_list)(CK_FUNCTION_LIST_PTR_PTR); + CK_RV (*c_get_interface)(CK_UTF8CHAR_PTR, CK_VERSION_PTR, CK_INTERFACE_PTR_PTR, CK_FLAGS); mod = calloc(1, sizeof(*mod)); if (mod == NULL) { return NULL; @@ -65,6 +66,24 @@ C_LoadModule(const char *mspec, CK_FUNCTION_LIST_PTR_PTR funcs) goto failed; } + c_get_interface = (CK_RV (*)(CK_UTF8CHAR_PTR, CK_VERSION_PTR, CK_INTERFACE_PTR_PTR, CK_FLAGS)) + sc_dlsym(mod->handle, "C_GetInterface"); + if (c_get_interface) { + CK_INTERFACE *interface = NULL; + + /* Get default PKCS #11 interface */ + rv = c_get_interface((CK_UTF8CHAR_PTR) "PKCS 11", NULL, &interface, 0); + if (rv == CKR_OK) { + /* this is actually 3.0 function list, but it starts + * with the same fields. Only for new functions, it + * needs to be casted to new structure */ + *funcs = interface->pFunctionList; + return (void *) mod; + } else { + fprintf(stderr, "C_GetInterface failed %lx, retry 2.x way", rv); + } + } + /* Get the list of function pointers */ c_get_function_list = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR)) sc_dlsym(mod->handle, "C_GetFunctionList"); @@ -75,10 +94,13 @@ C_LoadModule(const char *mspec, CK_FUNCTION_LIST_PTR_PTR funcs) return (void *) mod; else { fprintf(stderr, "C_GetFunctionList failed %lx", rv); - C_UnloadModule((void *) mod); - return NULL; + rv = C_UnloadModule((void *) mod); + if (rv == CKR_OK) + mod = NULL; /* already freed */ } failed: + if (mod && mod->handle) + sc_dlclose(mod->handle); free(mod); return NULL; } @@ -99,7 +121,6 @@ C_UnloadModule(void *module) if (mod->handle != NULL && sc_dlclose(mod->handle) < 0) return CKR_FUNCTION_FAILED; - memset(mod, 0, sizeof(*mod)); free(mod); return CKR_OK; } diff --git a/src/common/libpkcs11.h b/src/common/libpkcs11.h index c44767b94e..d29e8dce02 100644 --- a/src/common/libpkcs11.h +++ b/src/common/libpkcs11.h @@ -15,8 +15,12 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifndef __LIBPKCS11_H +#define __LIBPKCS11_H +#include "pkcs11/pkcs11.h" void *C_LoadModule(const char *name, CK_FUNCTION_LIST_PTR_PTR); CK_RV C_UnloadModule(void *module); +#endif diff --git a/src/common/libscdl.c b/src/common/libscdl.c index bc1d6b877a..226d56c85d 100644 --- a/src/common/libscdl.c +++ b/src/common/libscdl.c @@ -15,20 +15,24 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include #endif #include "libscdl.h" #ifdef _WIN32 +#include +#include #include + void *sc_dlopen(const char *filename) { - return (void *)LoadLibraryA(filename); + DWORD flags = PathIsRelativeA(filename) ? 0 : LOAD_WITH_ALTERED_SEARCH_PATH; + return (void *)LoadLibraryExA(filename, NULL, flags); } void *sc_dlsym(void *handle, const char *symbol) @@ -38,18 +42,48 @@ void *sc_dlsym(void *handle, const char *symbol) const char *sc_dlerror() { - return "LoadLibrary/GetProcAddress failed"; + static char msg[1024]; + DWORD err = GetLastError(); + DWORD rv = 0; + + if (err == ERROR_BAD_EXE_FORMAT) { + return "LoadLibrary/GetProcAddress failed: check module architecture matches application architecture"; + } + + rv = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + msg, sizeof(msg), NULL); + if (rv == 0) { + snprintf(msg, sizeof(msg), "LoadLibrary/GetProcAddress failed: %lx", err); + } + return msg; } int sc_dlclose(void *handle) { return FreeLibrary((HMODULE)handle); } + #else + #include +#include + void *sc_dlopen(const char *filename) { - return dlopen(filename, RTLD_LAZY); + int flags = RTLD_LAZY | RTLD_LOCAL; +#ifdef RTLD_DEEPBIND + /* By default, the pkcs11 modules and pcsclite are opened without RTLD_DEEPBIND. + * Using RTLD_DEEPBIND causes issues for dynamic analysis tools such as ASAN. + * When the OPENSC_DEEPBIND is set to "1", the flag is included in the calls + * that normally do not use RTLD_DEEPBIND. + */ + char *deep = getenv("OPENSC_DEEPBIND"); + if (deep != NULL && deep[0] == '1') { + flags |= RTLD_DEEPBIND; + } +#endif + return dlopen(filename, flags); } void *sc_dlsym(void *handle, const char *symbol) diff --git a/src/common/libscdl.h b/src/common/libscdl.h index 983683dde1..b649994620 100644 --- a/src/common/libscdl.h +++ b/src/common/libscdl.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LIBSCDL_H diff --git a/src/common/simclist.c b/src/common/simclist.c index c8e87ccaba..11a9254f5b 100644 --- a/src/common/simclist.c +++ b/src/common/simclist.c @@ -28,7 +28,9 @@ #if !defined(_WIN32) #include /* for htons() */ #include +#ifdef HAVE_SYS_TIME_H #include /* for gettimeofday() */ +#endif #include #else #include @@ -71,8 +73,10 @@ /* disable asserts */ #ifndef SIMCLIST_DEBUG +#ifndef NDEBUG #define NDEBUG #endif +#endif #include @@ -119,7 +123,7 @@ #include "simclist.h" -/* minumum number of elements for sorting with quicksort instead of insertion */ +/* minimum number of elements for sorting with quicksort instead of insertion */ #define SIMCLIST_MINQUICKSORTELS 24 @@ -199,7 +203,7 @@ static simclist_inline struct list_entry_s *list_findpos(const list_t *simclist_ * 3. avoid interference with user's RNG * * Facts pro system RNG: - * 1. may be more accurate (irrelevant for SimCList randno purposes) + * 1. may be more accurate (irrelevant for SimCList random purposes) * 2. why reinvent the wheel * * Default to local RNG for user's ease of use. @@ -419,7 +423,7 @@ static simclist_inline struct list_entry_s *list_findpos(const list_t *simclist_ /* accept 1 slot overflow for fetching head and tail sentinels */ if (posstart < -1 || posstart > (int)l->numels) return NULL; - x = (float)(posstart+1) / l->numels; + x = l->numels ? (float)(posstart+1) / l->numels : 0; if (x <= 0.25) { /* first quarter: get to posstart from head */ for (i = -1, ptr = l->head_sentinel; i < posstart; ptr = ptr->next, i++); @@ -479,6 +483,9 @@ int list_insert_at(list_t *simclist_restrict l, const void *data, unsigned int p size_t datalen = l->attrs.meter(data); lent->data = (struct list_entry_s *)malloc(datalen); if (lent->data == NULL) { + if (!(l->spareelsnum > 0)) { + free(lent); + } return -1; } memcpy(lent->data, data, datalen); @@ -489,6 +496,12 @@ int list_insert_at(list_t *simclist_restrict l, const void *data, unsigned int p /* actually append element */ prec = list_findpos(l, pos-1); if (prec == NULL) { + if (l->attrs.copy_data) { + free(lent->data); + } + if (!(l->spareelsnum > 0)) { + free(lent); + } return -1; } succ = prec->next; @@ -757,10 +770,10 @@ int list_concat(const list_t *l1, const list_t *l2, list_t *simclist_restrict de err = l2->numels - l1->numels; if ((err+1)/2 > 0) { /* correct pos RIGHT (err-1)/2 moves */ err = (err+1)/2; - for (cnt = 0; cnt < (unsigned int)err; cnt++) dest->mid = dest->mid->next; + for (cnt = 0; dest->mid && cnt < (unsigned int)err; cnt++) dest->mid = dest->mid->next; } else if (err/2 < 0) { /* correct pos LEFT (err/2)-1 moves */ err = -err/2; - for (cnt = 0; cnt < (unsigned int)err; cnt++) dest->mid = dest->mid->prev; + for (cnt = 0; dest->mid && cnt < (unsigned int)err; cnt++) dest->mid = dest->mid->prev; } assert(!(list_repOk(l1) && list_repOk(l2)) || list_repOk(dest)); diff --git a/src/common/simclist.h b/src/common/simclist.h index 49ac31fde5..1b8782bbb8 100644 --- a/src/common/simclist.h +++ b/src/common/simclist.h @@ -106,7 +106,7 @@ typedef struct { * -# receives two references to elements a and b * -# returns {<0, 0, >0} if (a > b), (a == b), (a < b) respectively * - * It is responsability of the function to handle possible NULL values. + * It is responsibility of the function to handle possible NULL values. */ typedef int (*element_comparator)(const void *a, const void *b); @@ -118,7 +118,7 @@ typedef int (*element_comparator)(const void *a, const void *b); * -# receives a reference to some indicator data * -# returns non-0 if the element matches the indicator, 0 otherwise * - * It is responsability of the function to handle possible NULL values in any + * It is responsibility of the function to handle possible NULL values in any * argument. */ typedef int (*element_seeker)(const void *el, const void *indicator); @@ -130,7 +130,7 @@ typedef int (*element_seeker)(const void *el, const void *indicator); * -# receives the reference to an element el * -# returns its size in bytes * - * It is responsability of the function to handle possible NULL values. + * It is responsibility of the function to handle possible NULL values. */ typedef size_t (*element_meter)(const void *el); @@ -141,7 +141,7 @@ typedef size_t (*element_meter)(const void *el); * -# receives the reference to an element el * -# returns a hash value for el * - * It is responsability of the function to handle possible NULL values. + * It is responsibility of the function to handle possible NULL values. */ typedef list_hash_t (*element_hash_computer)(const void *el); @@ -151,7 +151,7 @@ typedef list_hash_t (*element_hash_computer)(const void *el); * A serializer function is one that gets a reference to an element, * and returns a reference to a buffer that contains its serialization * along with the length of this buffer. - * It is responsability of the function to handle possible NULL values, + * It is responsibility of the function to handle possible NULL values, * returning a NULL buffer and a 0 buffer length. * * These functions have 3 goals: @@ -163,7 +163,7 @@ typedef list_hash_t (*element_hash_computer)(const void *el); * @param serialize_buffer reference to fill with the length of the buffer * @return reference to the buffer with the serialized data */ -typedef void *(*element_serializer)(const void *simclist_restrict el, uint32_t *simclist_restrict serializ_len); +typedef void *(*element_serializer)(const void *simclist_restrict el, uint32_t *simclist_restrict serialize_buffer); /** * a function for un-serializing an element. @@ -290,7 +290,7 @@ int list_attributes_seeker(list_t *simclist_restrict l, element_seeker seeker_fu * the list by its actual data is not free()d. With this option, every * deletion causes element data to be freed. * - * It is responsability of this function to correctly handle NULL values, if + * It is responsibility of this function to correctly handle NULL values, if * NULL elements are inserted into the list. * * @param l list to operate @@ -470,7 +470,7 @@ int list_insert_at(list_t *simclist_restrict l, const void *data, unsigned int p * expunge the first found given element from the list. * * Inspects the given list looking for the given element; if the element - * is found, it is removed. Only the first occurence is removed. + * is found, it is removed. Only the first occurrence is removed. * If a comparator function was not set, elements are compared by reference. * Otherwise, the comparator is used to match the element. * @@ -505,7 +505,7 @@ int list_delete_range(list_t *simclist_restrict l, unsigned int posstart, unsign /** * clear all the elements off of the list. * - * The element datums will not be freed. + * The element data will not be freed. * * @see list_delete_range() * @see list_size() @@ -595,7 +595,7 @@ int list_contains(const list_t *simclist_restrict l, const void *data); * user-provided list object, which must be different from both the * lists to concatenate. Attributes from the original lists are not * cloned. - * The destination list referred is threated as virgin room: if it + * The destination list referred is treated as virgin room: if it * is an existing list containing elements, memory leaks will happen. * It is OK to specify the same list twice as source, for "doubling" * it in the destination. @@ -672,7 +672,7 @@ int list_hash(const list_t *simclist_restrict l, list_hash_t *simclist_restrict #ifdef SIMCLIST_DUMPRESTORE /** - * get meta informations on a list dump on filedescriptor. + * get meta information on a list dump on filedescriptor. * * [ advanced function ] * @@ -689,7 +689,7 @@ int list_hash(const list_t *simclist_restrict l, list_hash_t *simclist_restrict int list_dump_getinfo_filedescriptor(int fd, list_dump_info_t *simclist_restrict info); /** - * get meta informations on a list dump on file. + * get meta information on a list dump on file. * * [ advanced function ] * diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 6dc37c30aa..e5411a0143 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -5,16 +5,20 @@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak lib_LTLIBRARIES = libopensc.la -noinst_HEADERS = cards.h ctbcs.h internal.h esteid.h muscle.h muscle-filesystem.h \ - internal-winscard.h p15card-helper.h pkcs15-syn.h \ - opensc.h pkcs15.h \ +noinst_HEADERS = cards.h ctbcs.h internal.h muscle.h muscle-filesystem.h \ + internal-winscard.h pkcs15-syn.h pkcs15-emulator-filter.h \ + opensc.h pkcs15.h gp.h \ cardctl.h asn1.h log.h simpletlv.h \ errors.h types.h compression.h itacns.h iso7816.h \ authentic.h iasecc.h iasecc-sdo.h sm.h card-sc-hsm.h \ pace.h cwa14890.h cwa-dnie.h card-gids.h aux-data.h \ - jpki.h sc-ossl-compat.h card-npa.h ccid-types.h reader-tr03119.h + jpki.h sc-ossl-compat.h card-npa.h card-openpgp.h \ + card-eoi.h card-dtrust.h card-srbeid.h ccid-types.h reader-tr03119.h \ + card-cac-common.h card-cardos-common.h -AM_CPPFLAGS = -DOPENSC_CONF_PATH=\"$(sysconfdir)/opensc.conf\" \ +AM_CPPFLAGS = -D'OPENSC_CONF_PATH="$(sysconfdir)/opensc.conf"' \ + -D'DEFAULT_SM_MODULE_PATH="$(DEFAULT_SM_MODULE_PATH)"' \ + -D'DEFAULT_SM_MODULE="$(DEFAULT_SM_MODULE)"' \ -I$(top_srcdir)/src AM_CFLAGS = $(OPENPACE_CFLAGS) $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_OPENCT_CFLAGS) \ $(OPTIONAL_PCSC_CFLAGS) $(OPTIONAL_ZLIB_CFLAGS) @@ -22,52 +26,52 @@ AM_OBJCFLAGS = $(AM_CFLAGS) libopensc_la_SOURCES_BASE = \ sc.c ctx.c log.c errors.c \ - asn1.c base64.c sec.c card.c iso7816.c dir.c ef-atr.c padding.c apdu.c \ - simpletlv.c \ + asn1.c base64.c sec.c card.c iso7816.c dir.c ef-atr.c \ + ef-gdo.c padding.c apdu.c simpletlv.c gp.c \ \ pkcs15.c pkcs15-cert.c pkcs15-data.c pkcs15-pin.c \ pkcs15-prkey.c pkcs15-pubkey.c pkcs15-skey.c \ - pkcs15-sec.c pkcs15-algo.c pkcs15-cache.c pkcs15-syn.c \ + pkcs15-sec.c pkcs15-algo.c pkcs15-cache.c pkcs15-syn.c pkcs15-emulator-filter.c \ \ muscle.c muscle-filesystem.c \ \ - ctbcs.c reader-ctapi.c reader-pcsc.c reader-openct.c reader-tr03119.c \ + ctbcs.c reader-ctapi.c reader-pcsc.c reader-openct.c reader-tr03119.c reader-empty.c \ \ - card-setcos.c card-miocos.c card-flex.c card-gpk.c \ - card-cardos.c card-tcos.c card-default.c \ - card-mcrd.c card-starcos.c card-openpgp.c card-jcop.c \ + card-setcos.c card-flex.c \ + card-cardos.c card-cardos-common.c card-tcos.c card-default.c \ + card-mcrd.c card-starcos.c card-openpgp.c \ card-oberthur.c card-belpic.c card-atrust-acos.c \ - card-entersafe.c card-epass2003.c card-coolkey.c card-incrypto34.c \ - card-piv.c card-cac.c card-muscle.c card-acos5.c \ - card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c \ - card-rtecp.c card-westcos.c card-myeid.c \ + card-entersafe.c card-epass2003.c card-coolkey.c \ + card-piv.c card-cac-common.c card-cac.c card-cac1.c \ + card-muscle.c card-asepcos.c card-gemsafeV1.c card-rutoken.c \ + card-rtecp.c card-myeid.c \ card-itacns.c card-authentic.c \ card-iasecc.c iasecc-sdo.c iasecc-sm.c card-sc-hsm.c \ card-dnie.c cwa14890.c cwa-dnie.c \ card-isoApplet.c card-masktech.c card-gids.c card-jpki.c \ - card-npa.c \ + card-npa.c card-esteid2018.c card-esteid2025.c card-idprime.c \ + card-edo.c card-nqApplet.c card-skeid.c card-eoi.c card-dtrust.c \ + card-lteid.c card-srbeid.c \ \ - pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ - pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \ + pkcs15-openpgp.c pkcs15-starcert.c pkcs15-cardos.c pkcs15-tcos.c \ pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \ - pkcs15-cac.c pkcs15-esinit.c pkcs15-westcos.c pkcs15-pteid.c \ + pkcs15-cac.c pkcs15-esinit.c pkcs15-pteid.c pkcs15-esteid2025.c \ pkcs15-oberthur.c pkcs15-itacns.c pkcs15-gemsafeV1.c pkcs15-sc-hsm.c \ - pkcs15-coolkey.c \ - pkcs15-dnie.c pkcs15-gids.c pkcs15-iasecc.c pkcs15-jpki.c \ - compression.c p15card-helper.c sm.c \ - aux-data.c + pkcs15-coolkey.c pkcs15-din-66291.c pkcs15-idprime.c pkcs15-nqApplet.c \ + pkcs15-dnie.c pkcs15-gids.c pkcs15-iasecc.c pkcs15-jpki.c pkcs15-esteid2018.c \ + pkcs15-starcos-esign.c pkcs15-skeid.c pkcs15-eoi.c pkcs15-dtrust.c pkcs15-lteid.c \ + pkcs15-srbeid.c compression.c sm.c aux-data.c if ENABLE_CRYPTOTOKENKIT # most platforms don't support objective C the way we needed. # Only include it if needed libopensc_la_SOURCES_BASE += reader-cryptotokenkit.m -else +endif libopensc_la_LIBTOOLFLAGS = --tag CC libopensc_static_la_LIBTOOLFLAGS = --tag CC -endif libopensc_la_SOURCES = $(libopensc_la_SOURCES_BASE) \ - libopensc.exports + opensc.exports libopensc_static_la_SOURCES = $(libopensc_la_SOURCES_BASE) if WIN32 @@ -78,15 +82,16 @@ libopensc_la_LIBADD = $(OPENPACE_LIBS) $(OPTIONAL_OPENSSL_LIBS) \ $(top_builddir)/src/pkcs15init/libpkcs15init.la \ $(top_builddir)/src/scconf/libscconf.la \ $(top_builddir)/src/common/libscdl.la \ - $(top_builddir)/src/sm/libsmeac.la \ - $(top_builddir)/src/common/libcompat.la + $(top_builddir)/src/ui/libnotify.la \ + $(top_builddir)/src/ui/libstrings.la \ + $(top_builddir)/src/sm/libsmeac.la if WIN32 -libopensc_la_LIBADD += -lws2_32 +libopensc_la_LIBADD += -lws2_32 -lshlwapi -lcomctl32 endif libopensc_static_la_LIBADD = $(libopensc_la_LIBADD) libopensc_la_LDFLAGS = $(AM_LDFLAGS) \ -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ \ - -export-symbols "$(srcdir)/libopensc.exports" \ + -export-symbols "$(srcdir)/opensc.exports" \ -no-undefined if WIN32 @@ -99,3 +104,46 @@ if ENABLE_MINIDRIVER noinst_LTLIBRARIES = libopensc_static.la endif endif + +TIDY_FLAGS = $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +TIDY_FILES = \ + sc.c ctx.c errors.c \ + asn1.c base64.c sec.c card.c iso7816.c dir.c ef-atr.c \ + ef-gdo.c padding.c apdu.c simpletlv.c gp.c \ + \ + pkcs15-cert.c pkcs15-data.c pkcs15-pin.c \ + pkcs15-prkey.c pkcs15-pubkey.c pkcs15-skey.c \ + pkcs15-sec.c pkcs15-algo.c pkcs15-cache.c pkcs15-syn.c pkcs15-emulator-filter.c \ + \ + muscle.c muscle-filesystem.c \ + \ + ctbcs.c reader-ctapi.c reader-pcsc.c reader-openct.c reader-tr03119.c reader-empty.c \ + \ + card-setcos.c card-flex.c \ + card-cardos.c card-tcos.c card-default.c \ + card-mcrd.c card-starcos.c \ + card-oberthur.c card-belpic.c card-atrust-acos.c \ + card-entersafe.c card-epass2003.c card-coolkey.c \ + card-cac-common.c card-cac.c card-cac1.c \ + card-muscle.c card-asepcos.c card-gemsafeV1.c card-rutoken.c \ + card-rtecp.c card-myeid.c \ + card-itacns.c card-authentic.c \ + card-iasecc.c iasecc-sdo.c iasecc-sm.c card-sc-hsm.c \ + cwa14890.c cwa-dnie.c \ + card-isoApplet.c card-masktech.c card-jpki.c \ + card-npa.c card-esteid2018.c card-idprime.c \ + card-edo.c card-nqApplet.c card-skeid.c card-eoi.c card-dtrust.c \ + card-lteid.c card-srbeid.c \ + \ + pkcs15-openpgp.c pkcs15-cardos.c pkcs15-tcos.c \ + pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c \ + pkcs15-cac.c pkcs15-esinit.c pkcs15-pteid.c \ + pkcs15-oberthur.c pkcs15-itacns.c pkcs15-sc-hsm.c \ + pkcs15-coolkey.c pkcs15-din-66291.c pkcs15-idprime.c pkcs15-nqApplet.c \ + pkcs15-dnie.c pkcs15-gids.c pkcs15-iasecc.c pkcs15-jpki.c pkcs15-esteid2018.c \ + pkcs15-starcos-esign.c pkcs15-skeid.c pkcs15-dtrust.c pkcs15-lteid.c \ + pkcs15-srbeid.c compression.c sm.c aux-data.c \ + #$(SOURCES) + +check-local: + if [ -x "$(CLANGTIDY)" ]; then clang-tidy -config='' --checks='$(TIDY_CHECKS)' --warnings-as-errors='$(TIDY_CHECKS)' -header-filter=.* $(addprefix $(srcdir)/,$(TIDY_FILES)) -- $(TIDY_FLAGS); fi diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak index 00c291744f..460b5332d5 100644 --- a/src/libopensc/Makefile.mak +++ b/src/libopensc/Makefile.mak @@ -4,43 +4,47 @@ TARGET = opensc.dll opensc_a.lib OBJECTS = \ sc.obj ctx.obj log.obj errors.obj \ asn1.obj base64.obj sec.obj card.obj iso7816.obj dir.obj ef-atr.obj \ - padding.obj apdu.obj simpletlv.obj \ + ef-gdo.obj padding.obj apdu.obj simpletlv.obj gp.obj \ \ pkcs15.obj pkcs15-cert.obj pkcs15-data.obj pkcs15-pin.obj \ pkcs15-prkey.obj pkcs15-pubkey.obj pkcs15-skey.obj \ - pkcs15-sec.obj pkcs15-algo.obj pkcs15-cache.obj pkcs15-syn.obj \ + pkcs15-sec.obj pkcs15-algo.obj pkcs15-cache.obj pkcs15-syn.obj pkcs15-emulator-filter.obj \ \ muscle.obj muscle-filesystem.obj \ \ ctbcs.obj reader-ctapi.obj reader-pcsc.obj reader-openct.obj reader-tr03119.obj \ \ - card-setcos.obj card-miocos.obj card-flex.obj card-gpk.obj \ - card-cardos.obj card-tcos.obj card-default.obj \ - card-mcrd.obj card-starcos.obj card-openpgp.obj card-jcop.obj \ + card-setcos.obj card-flex.obj \ + card-cardos.obj card-cardos-common.obj card-tcos.obj card-default.obj \ + card-mcrd.obj card-starcos.obj card-openpgp.obj \ card-oberthur.obj card-belpic.obj card-atrust-acos.obj \ card-entersafe.obj card-epass2003.obj card-coolkey.obj \ - card-incrypto34.obj card-cac.obj card-piv.obj card-muscle.obj \ - card-acos5.obj \ - card-asepcos.obj card-akis.obj card-gemsafeV1.obj card-rutoken.obj \ - card-rtecp.obj card-westcos.obj card-myeid.obj \ + card-cac.obj card-cac1.obj card-cac-common.obj \ + card-piv.obj card-muscle.obj \ + card-asepcos.obj card-gemsafeV1.obj card-rutoken.obj \ + card-rtecp.obj card-myeid.obj \ card-itacns.obj card-authentic.obj \ card-iasecc.obj iasecc-sdo.obj iasecc-sm.obj cwa-dnie.obj cwa14890.obj \ card-sc-hsm.obj card-dnie.obj card-isoApplet.obj pkcs15-coolkey.obj \ card-masktech.obj card-gids.obj card-jpki.obj \ - card-npa.obj \ + card-npa.obj card-esteid2018.obj card-esteid2025.obj card-idprime.obj \ + card-edo.obj card-nqApplet.obj card-skeid.obj card-eoi.obj card-dtrust.obj \ + card-lteid.obj card-srbeid.obj \ \ - pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \ - pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj pkcs15-gemsafeGPK.obj \ + pkcs15-openpgp.obj pkcs15-starcert.obj pkcs15-cardos.obj pkcs15-tcos.obj \ pkcs15-actalis.obj pkcs15-atrust-acos.obj pkcs15-tccardos.obj pkcs15-piv.obj \ - pkcs15-cac.obj pkcs15-esinit.obj pkcs15-westcos.obj pkcs15-pteid.obj \ + pkcs15-cac.obj pkcs15-esinit.obj pkcs15-pteid.obj pkcs15-din-66291.obj \ pkcs15-oberthur.obj pkcs15-itacns.obj pkcs15-gemsafeV1.obj pkcs15-sc-hsm.obj \ pkcs15-dnie.obj pkcs15-gids.obj pkcs15-iasecc.obj pkcs15-jpki.obj \ - compression.obj p15card-helper.obj sm.obj \ - aux-data.obj \ + pkcs15-esteid2018.obj pkcs15-esteid2025.obj pkcs15-idprime.obj pkcs15-nqApplet.obj \ + pkcs15-starcos-esign.obj pkcs15-skeid.obj pkcs15-eoi.obj pkcs15-dtrust.obj \ + pkcs15-lteid.obj pkcs15-srbeid.obj compression.obj sm.obj aux-data.obj \ $(TOPDIR)\win32\versioninfo.res LIBS = $(TOPDIR)\src\scconf\scconf.lib \ $(TOPDIR)\src\common\common.lib \ $(TOPDIR)\src\common\libscdl.lib \ + $(TOPDIR)\src\ui\strings.lib \ + $(TOPDIR)\src\ui\notify.lib \ $(TOPDIR)\src\sm\libsmiso.lib \ $(TOPDIR)\src\sm\libsmeac.lib \ $(TOPDIR)\src\pkcs15init\pkcs15init.lib @@ -49,12 +53,8 @@ all: $(TOPDIR)\win32\versioninfo.res $(TARGET) !INCLUDE $(TOPDIR)\win32\Make.rules.mak -opensc.dll: $(OBJECTS) $(LIBS) - echo LIBRARY $* > $*.def - echo EXPORTS >> $*.def - type lib$*.exports >> $*.def - link $(LINKFLAGS) /dll /def:$*.def /implib:$*.lib /out:opensc.dll $(OBJECTS) $(LIBS) $(OPENPACE_LIB) $(OPENSSL_LIB) $(ZLIB_LIB) gdi32.lib advapi32.lib ws2_32.lib - if EXIST opensc.dll.manifest mt -manifest opensc.dll.manifest -outputresource:opensc.dll;2 +opensc.dll: $(OBJECTS) $(LIBS) $*.def + link $(LINKFLAGS) /dll /out:$@ /def:$*.def /implib:$*.lib $(OBJECTS) $(LIBS) $(OPENPACE_LIB) $(OPENSSL_LIB) $(ZLIB_LIB) gdi32.lib Comctl32.lib Shell32.lib user32.lib advapi32.lib ws2_32.lib shlwapi.lib -opensc_a.lib: $(OBJECTS) $(LIBS) - lib $(LIBFLAGS) /out:opensc_a.lib $(OBJECTS) $(LIBS) $(OPENPACE_LIB) $(OPENSSL_LIB) $(ZLIB_LIB) user32.lib advapi32.lib ws2_32.lib +opensc_a.lib: $(OBJECTS) + lib $(LIBFLAGS) /out:opensc_a.lib $(OBJECTS) diff --git a/src/libopensc/apdu.c b/src/libopensc/apdu.c index 3a46825096..51b3aaac1f 100644 --- a/src/libopensc/apdu.c +++ b/src/libopensc/apdu.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -39,7 +39,7 @@ * @param proto the desired protocol * @return length of the encoded APDU */ -static size_t sc_apdu_get_length(const sc_apdu_t *apdu, unsigned int proto) +size_t sc_apdu_get_length(const sc_apdu_t *apdu, unsigned int proto) { size_t ret = 4; @@ -77,10 +77,10 @@ static size_t sc_apdu_get_length(const sc_apdu_t *apdu, unsigned int proto) * @param apdu APDU to be encoded as an octet string * @param proto protocol version to be used * @param out output buffer of size outlen. - * @param outlen size of hte output buffer + * @param outlen size of the output buffer * @return SC_SUCCESS on success and an error code otherwise */ -static int sc_apdu2bytes(sc_context_t *ctx, const sc_apdu_t *apdu, +int sc_apdu2bytes(sc_context_t *ctx, const sc_apdu_t *apdu, unsigned int proto, u8 *out, size_t outlen) { u8 *p = out; @@ -244,7 +244,7 @@ int sc_apdu_set_resp(sc_context_t *ctx, sc_apdu_t *apdu, const u8 *buf, * +-------------------------------+ * | * v - * card->reader->ops->tranmit + * card->reader->ops->transmit */ /** basic consistency check of the sc_apdu_t object @@ -291,10 +291,6 @@ sc_check_apdu(sc_card_t *card, const sc_apdu_t *apdu) /* data is expected */ if (apdu->resplen == 0 || apdu->resp == NULL) goto error; - /* return buffer to small */ - if ((apdu->le == 0 && apdu->resplen < SC_MAX_APDU_BUFFER_SIZE-2) - || (apdu->resplen < apdu->le)) - goto error; break; case SC_APDU_CASE_3_SHORT: /* data is sent */ @@ -314,10 +310,6 @@ sc_check_apdu(sc_card_t *card, const sc_apdu_t *apdu) /* data is expected */ if (apdu->resplen == 0 || apdu->resp == NULL) goto error; - /* return buffer to small */ - if ((apdu->le == 0 && apdu->resplen < SC_MAX_APDU_BUFFER_SIZE-2) - || (apdu->resplen < apdu->le)) - goto error; /* inconsistent datalen */ if (apdu->datalen != apdu->lc) goto error; @@ -330,13 +322,13 @@ sc_check_apdu(sc_card_t *card, const sc_apdu_t *apdu) error: sc_log(card->ctx, "Invalid Case %d %s APDU:\n" "cse=%02x cla=%02x ins=%02x p1=%02x p2=%02x lc=%lu le=%lu\n" - "resp=%p resplen=%lu data=%p datalen=%lu", + "resp=%p resplen=%lu data=%p datalen=%lu flags=0x%8.8lx", apdu->cse & SC_APDU_SHORT_MASK, (apdu->cse & SC_APDU_EXT) != 0 ? "extended" : "short", apdu->cse, apdu->cla, apdu->ins, apdu->p1, apdu->p2, (unsigned long) apdu->lc, (unsigned long) apdu->le, apdu->resp, (unsigned long) apdu->resplen, - apdu->data, (unsigned long) apdu->datalen); + apdu->data, (unsigned long) apdu->datalen, apdu->flags); return SC_ERROR_INVALID_ARGUMENTS; } @@ -350,7 +342,7 @@ sc_detect_apdu_cse(const sc_card_t *card, sc_apdu_t *apdu) if (apdu->cse == SC_APDU_CASE_2 || apdu->cse == SC_APDU_CASE_3 || apdu->cse == SC_APDU_CASE_4) { int btype = apdu->cse & SC_APDU_SHORT_MASK; - /* if either Lc or Le is bigger than the maximun for + /* if either Lc or Le is bigger than the maximum for * short APDUs and the card supports extended APDUs * use extended APDUs (unless Lc is greater than * 255 and command chaining is activated) */ @@ -409,11 +401,13 @@ sc_set_le_and_transmit(struct sc_card *card, struct sc_apdu *apdu, size_t olen) /* set the new expected length */ apdu->resplen = olen; apdu->le = nlen; - /* Belpic V1 applets have a problem: if the card sends a 6C XX (only XX bytes available), - * and we resend the command too soon (i.e. the reader is too fast), the card doesn't respond. +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* Belpic V1 applets have a problem: if the card sends a 6C XX (only XX bytes available), + * and we resend the command too soon (i.e. the reader is too fast), the card doesn't respond. * So we build in a delay. */ if (card->type == SC_CARD_TYPE_BELPIC_EID) msleep(40); +#endif /* re-transmit the APDU with new Le length */ rv = sc_single_transmit(card, apdu); @@ -443,7 +437,7 @@ sc_get_response(struct sc_card *card, struct sc_apdu *apdu, size_t olen) if (!card->ops->get_response) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "no GET RESPONSE command"); - /* call GET RESPONSE until we have read all data requested or until the card retuns 0x9000, + /* call GET RESPONSE until we have read all data requested or until the card returns 0x9000, * whatever happens first. */ /* if there are already data in response append a new data to the end of the buffer */ @@ -461,6 +455,10 @@ sc_get_response(struct sc_card *card, struct sc_apdu *apdu, size_t olen) unsigned char resp[256]; size_t resp_len = le; + /* we have all the data the caller requested even if the card has more data */ + if (buflen == 0) + break; + /* call GET RESPONSE to get more date from the card; * note: GET RESPONSE returns the left amount of data (== SW2) */ memset(resp, 0, sizeof(resp)); @@ -468,7 +466,7 @@ sc_get_response(struct sc_card *card, struct sc_apdu *apdu, size_t olen) if (rv < 0) { #ifdef ENABLE_SM if (resp_len) { - sc_log(ctx, "SM response data %s", sc_dump_hex(resp, resp_len)); + sc_log_hex(ctx, "SM response data", resp, resp_len); sc_sm_update_apdu_response(card, resp, resp_len, rv, apdu); } #endif @@ -484,10 +482,6 @@ sc_get_response(struct sc_card *card, struct sc_apdu *apdu, size_t olen) buf += le; buflen -= le; - /* we have all the data the caller requested even if the card has more data */ - if (buflen == 0) - break; - minlen -= le; if (rv != 0) le = minlen = (size_t)rv; @@ -524,21 +518,23 @@ sc_transmit(sc_card_t *card, sc_apdu_t *apdu) LOG_TEST_RET(ctx, r, "transmit APDU failed"); /* ok, the APDU was successfully transmitted. Now we have two special cases: - * 1. the card returned 0x6Cxx: in this case APDU will be re-trasmitted with Le set to SW2 + * 1. the card returned 0x6Cxx: in this case APDU will be re-transmitted with Le set to SW2 * (possible only if response buffer size is larger than new Le = SW2) */ - if (apdu->sw1 == 0x6C && (apdu->flags & SC_APDU_FLAGS_NO_RETRY_WL) == 0) + if (apdu->sw1 == 0x6C && (apdu->flags & SC_APDU_FLAGS_NO_RETRY_WL) == 0) { r = sc_set_le_and_transmit(card, apdu, olen); - LOG_TEST_RET(ctx, r, "cannot re-transmit APDU "); + LOG_TEST_RET(ctx, r, "cannot re-transmit APDU "); + } /* 2. the card returned 0x61xx: more data can be read from the card * using the GET RESPONSE command (mostly used in the T0 protocol). * Unless the SC_APDU_FLAGS_NO_GET_RESP is set we try to read as * much data as possible using GET RESPONSE. */ - if (apdu->sw1 == 0x61 && (apdu->flags & SC_APDU_FLAGS_NO_GET_RESP) == 0) + if (apdu->sw1 == 0x61 && (apdu->flags & SC_APDU_FLAGS_NO_GET_RESP) == 0) { r = sc_get_response(card, apdu, olen); - LOG_TEST_RET(ctx, r, "cannot get all data with 'GET RESPONSE'"); + LOG_TEST_RET(ctx, r, "cannot get all data with 'GET RESPONSE'"); + } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -567,6 +563,14 @@ int sc_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu) return r; } +#if ENABLE_SM + if (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT + && (apdu->flags & SC_APDU_FLAGS_CHAINING) != 0 + && (apdu->flags & SC_APDU_FLAGS_SM_CHAINING) != 0) { + sc_log(card->ctx,"Let SM do the chaining"); + r = sc_transmit(card, apdu); + } else +#endif if ((apdu->flags & SC_APDU_FLAGS_CHAINING) != 0) { /* divide et impera: transmit APDU in chunks with Lc <= max_send_size * bytes using command chaining */ @@ -591,9 +595,8 @@ int sc_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu) * secure messaging is used */ plen = max_send_size; tapdu.cla |= 0x10; + /* the intermediate APDU don't expect response data */ tapdu.le = 0; - /* the intermediate APDU don't expect data */ - tapdu.lc = 0; tapdu.resplen = 0; tapdu.resp = NULL; } else { @@ -627,9 +630,17 @@ int sc_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu) len -= plen; buf += plen; } - } else + } else { /* transmit single APDU */ r = sc_transmit(card, apdu); + } + + if (r == SC_ERROR_CARD_RESET || r == SC_ERROR_READER_REATTACHED) { + /* give card driver a chance to react on resets */ + if (card->ops->card_reader_lock_obtained) + card->ops->card_reader_lock_obtained(card, 1); + } + /* all done => release lock */ if (sc_unlock(card) != SC_SUCCESS) sc_log(card->ctx, "sc_unlock failed"); diff --git a/src/libopensc/asn1.c b/src/libopensc/asn1.c index 88d35db959..32d7ff8564 100644 --- a/src/libopensc/asn1.c +++ b/src/libopensc/asn1.c @@ -15,18 +15,20 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif -#include -#include -#include #include +#include +#include +#include #include +#include +#include #include "internal.h" #include "asn1.h" @@ -43,13 +45,13 @@ static const char *tag2str(unsigned int tag) { static const char *tags[] = { "EOC", "BOOLEAN", "INTEGER", "BIT STRING", "OCTET STRING", /* 0-4 */ - "NULL", "OBJECT", "OBJECT DESCRIPTOR", "EXTERNAL", "REAL", /* 5-9 */ - "ENUMERATED", "", "UTF8STRING", "", /* 10-13 */ - "", "", "SEQUENCE", "SET", /* 15-17 */ - "NUMERICSTRING", "PRINTABLESTRING", "T61STRING", /* 18-20 */ - "VIDEOTEXSTRING", "IA5STRING", "UTCTIME", "GENERALIZEDTIME", /* 21-24 */ - "GRAPHICSTRING", "VISIBLESTRING", "GENERALSTRING", /* 25-27 */ - "UNIVERSALSTRING", "", "BMPSTRING" /* 28-30 */ + "NULL", "OBJECT IDENTIFIER", "OBJECT DESCRIPTOR", "EXTERNAL", "REAL", /* 5-9 */ + "ENUMERATED", "Universal 11", "UTF8String", "Universal 13", /* 10-13 */ + "Universal 14", "Universal 15", "SEQUENCE", "SET", /* 15-17 */ + "NumericString", "PrintableString", "T61String", /* 18-20 */ + "VideotexString", "IA5String", "UTCTIME", "GENERALIZEDTIME", /* 21-24 */ + "GraphicString", "VisibleString", "GeneralString", /* 25-27 */ + "UniversalString", "Universal 29", "BMPString" /* 28-30 */ }; if (tag > 30) @@ -64,60 +66,72 @@ int sc_asn1_read_tag(const u8 ** buf, size_t buflen, unsigned int *cla_out, size_t left = buflen, len; unsigned int cla, tag, i; - if (left < 2) - return SC_ERROR_INVALID_ASN1_OBJECT; *buf = NULL; + + if (left == 0 || !p || buflen == 0) + return SC_ERROR_INVALID_ASN1_OBJECT; if (*p == 0xff || *p == 0) { /* end of data reached */ *taglen = 0; *tag_out = SC_ASN1_TAG_EOC; return SC_SUCCESS; } + /* parse tag byte(s) * Resulted tag is presented by integer that has not to be * confused with the 'tag number' part of ASN.1 tag. */ cla = (*p & SC_ASN1_TAG_CLASS) | (*p & SC_ASN1_TAG_CONSTRUCTED); tag = *p & SC_ASN1_TAG_PRIMITIVE; + if (left < 1) + return SC_ERROR_INVALID_ASN1_OBJECT; p++; left--; if (tag == SC_ASN1_TAG_PRIMITIVE) { /* high tag number */ size_t n = SC_ASN1_TAGNUM_SIZE - 1; /* search the last tag octet */ - while (left-- != 0 && n != 0) { + do { + if (left == 0 || n == 0) + /* either an invalid tag or it doesn't fit in + * unsigned int */ + return SC_ERROR_INVALID_ASN1_OBJECT; tag <<= 8; tag |= *p; - if ((*p++ & 0x80) == 0) - break; + p++; + left--; n--; - } - if (left == 0 || n == 0) - /* either an invalid tag or it doesn't fit in - * unsigned int */ - return SC_ERROR_INVALID_ASN1_OBJECT; + } while (tag & 0x80); } /* parse length byte(s) */ - len = *p & 0x7f; - if (*p++ & 0x80) { + if (left == 0) + return SC_ERROR_INVALID_ASN1_OBJECT; + len = *p; + p++; + left--; + if (len & 0x80) { + len &= 0x7f; unsigned int a = 0; - if (len > 4 || len > left) + if (len > sizeof a || len > left) return SC_ERROR_INVALID_ASN1_OBJECT; - left -= len; for (i = 0; i < len; i++) { a <<= 8; a |= *p; p++; + left--; } len = a; } - if (len > left) - return SC_ERROR_INVALID_ASN1_OBJECT; + *cla_out = cla; *tag_out = tag; *taglen = len; *buf = p; + + if (len > left) + return SC_ERROR_ASN1_END_OF_CONTENTS; + return SC_SUCCESS; } @@ -141,40 +155,81 @@ void sc_copy_asn1_entry(const struct sc_asn1_entry *src, dest->name = NULL; } -static void sc_asn1_print_octet_string(const u8 * buf, size_t buflen) +static void print_indent(size_t depth) { - size_t i; + for (; depth > 0; depth--) { + putchar(' '); + } +} + +static void print_hex(const u8 * buf, size_t buflen, size_t depth) +{ + size_t lines_len = buflen * 5 + 128; + char *lines = malloc(lines_len); + char *line = lines; + + if (buf == NULL || buflen == 0 || lines == NULL) { + free(lines); + return; + } + + sc_hex_dump(buf, buflen, lines, lines_len); + + while (*line != '\0') { + char *line_end = strchr(line, '\n'); + ptrdiff_t width = line_end - line; + if (!line_end || width <= 1) { + /* don't print empty lines */ + break; + } + if (buflen > 8) { + putchar('\n'); + print_indent(depth); + } else { + printf(": "); + } + printf("%.*s", (int) width, line); + line = line_end + 1; + } - for (i = 0; i < buflen; i++) - printf("%02X", buf[i]); + free(lines); } -static void sc_asn1_print_utf8string(const u8 * buf, size_t buflen) +static void print_ascii(const u8 * buf, size_t buflen) { - size_t i; + for (; 0 < buflen; buflen--, buf++) { + if (isprint(*buf)) + printf("%c", *buf); + else + putchar('.'); + } +} - for (i = 0; i < buflen; i++) - printf("%c", buf[i]); +static void sc_asn1_print_octet_string(const u8 * buf, size_t buflen, size_t depth) +{ + print_hex(buf, buflen, depth); +} + +static void sc_asn1_print_utf8string(const u8 * buf, size_t buflen) +{ + /* FIXME UTF-8 is not ASCII */ + print_ascii(buf, buflen); } static void sc_asn1_print_integer(const u8 * buf, size_t buflen) { -#ifndef _WIN32 - long long a = 0; -#else - __int64 a = 0; -#endif - size_t i; + size_t a = 0; if (buflen > sizeof(a)) { - printf("too long"); - return; - } - for (i = 0; i < buflen; i++) { - a <<= 8; - a |= buf[i]; + printf("0x%s", sc_dump_hex(buf, buflen)); + } else { + size_t i; + for (i = 0; i < buflen; i++) { + a <<= 8; + a |= buf[i]; + } + printf("%"SC_FORMAT_LEN_SIZE_T"u", a); } - printf("%lld", a); } static void sc_asn1_print_boolean(const u8 * buf, size_t buflen) @@ -188,7 +243,7 @@ static void sc_asn1_print_boolean(const u8 * buf, size_t buflen) printf("false"); } -static void sc_asn1_print_bit_string(const u8 * buf, size_t buflen) +static void sc_asn1_print_bit_string(const u8 * buf, size_t buflen, size_t depth) { #ifndef _WIN32 long long a = 0; @@ -198,66 +253,136 @@ static void sc_asn1_print_bit_string(const u8 * buf, size_t buflen) int r, i; if (buflen > sizeof(a) + 1) { - printf("too long"); - return; - } - r = sc_asn1_decode_bit_string(buf, buflen, &a, sizeof(a)); - if (r < 0) { - printf("decode error"); - return; + print_hex(buf, buflen, depth); + } else { + r = sc_asn1_decode_bit_string(buf, buflen, &a, sizeof(a), 1); + if (r < 0) { + printf("decode error, "); + /* try again without the strict mode */ + r = sc_asn1_decode_bit_string(buf, buflen, &a, sizeof(a), 0); + if (r < 0) { + printf("even for lax decoding"); + return ; + } + } + for (i = r - 1; i >= 0; i--) { + printf("%c", ((a >> i) & 1) ? '1' : '0'); + } } - for (i = r - 1; i >= 0; i--) { - printf("%c", ((a >> i) & 1) ? '1' : '0'); +} + +#ifdef ENABLE_OPENSSL +#include + +static void openssl_print_object_sn(const char *s) +{ + ASN1_OBJECT *obj = OBJ_txt2obj(s, 0); + if (obj) { + int nid = OBJ_obj2nid(obj); + if (nid != NID_undef) { + printf(", %s", OBJ_nid2sn(nid)); + } + ASN1_OBJECT_free(obj); } } +#else +static void openssl_print_object_sn(const char *s) +{ +} +#endif static void sc_asn1_print_object_id(const u8 * buf, size_t buflen) { struct sc_object_id oid; - int i = 0; - char tmp[12]; - char sbuf[(sizeof tmp)*SC_MAX_OBJECT_ID_OCTETS]; + const char *sbuf; if (sc_asn1_decode_object_id(buf, buflen, &oid)) { printf("decode error"); return; } - sbuf[0] = 0; - for (i = 0; (i < SC_MAX_OBJECT_ID_OCTETS) && (oid.value[i] != -1); i++) { + sbuf = sc_dump_oid(&oid); + printf(" %s", sbuf); + openssl_print_object_sn(sbuf); +} + +static void sc_asn1_print_utctime(const u8 * buf, size_t buflen) +{ + if (buflen < 8) { + printf("Error in decoding.\n"); + return; + } - if (i) - strcat(sbuf, "."); - sprintf(tmp, "%d", oid.value[i]); - strcat(sbuf, tmp); + print_ascii(buf, 2); /* YY */ + putchar('-'); + print_ascii(buf+2, 2); /* MM */ + putchar('-'); + print_ascii(buf+4, 2); /* DD */ + putchar(' '); + print_ascii(buf+6, 2); /* hh */ + buf += 8; + buflen -= 8; + if (buflen >= 2 && isdigit(buf[0]) && isdigit(buf[1])) { + putchar(':'); + print_ascii(buf, 2); /* mm */ + buf += 2; + buflen -= 2; + } + if (buflen >= 2 && isdigit(buf[0]) && isdigit(buf[1])) { + putchar(':'); + print_ascii(buf, 2); /* ss */ + buf += 2; + buflen -= 2; + } + if (buflen >= 4 && '.' == buf[0]) { + print_ascii(buf, 4); /* fff */ + buf += 4; + buflen -= 4; + } + + if (buflen >= 1 && 'Z' == buf[0]) { + printf(" UTC"); + } else if (buflen >= 5 && ('-' == buf[0] || '+' == buf[0])) { + putchar(' '); + print_ascii(buf, 3); /* +/-hh */ + putchar(':'); + print_ascii(buf+3, 2); /* mm */ } - printf("%s", sbuf); } static void sc_asn1_print_generalizedtime(const u8 * buf, size_t buflen) { - size_t ii; - for (ii=0; ii= 2) { - unsigned int cla = 0, tag = 0, hlen; + unsigned int cla = 0, tag = 0; + size_t hlen; const u8 *tagp = p; size_t len; r = sc_asn1_read_tag(&tagp, bytesleft, &cla, &tag, &len); - if (r != SC_SUCCESS) { + if (r != SC_SUCCESS || (tagp == NULL && tag != SC_ASN1_TAG_EOC)) { printf("Error in decoding.\n"); return; } @@ -266,71 +391,93 @@ static void print_tags_recursive(const u8 * buf0, const u8 * buf, printf("Zero tag, finishing\n"); break; } - for (i = 0; i < depth; i++) { - putchar(' '); - putchar(' '); + print_indent(depth); + /* let i be the length of the tag in bytes */ + for (i = 1; i < sizeof tag - 1; i++) { + if (!(tag >> 8*i)) + break; + } + printf("%02X", cla<<(i-1)*8 | tag); + + if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_UNIVERSAL) { + printf(" %s", tag2str(tag)); + } else { + printf(" %s %-2u", + classes[cla >> 6], + i == 1 ? tag & SC_ASN1_TAG_PRIMITIVE : tag & (((unsigned int) ~0) >> (i-1)*8)); } - printf("%02X %s: tag 0x%02X, length %3d: ", - cla | tag, classes[cla >> 6], tag & 0x1f, (int) len); + if (!((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_UNIVERSAL + && tag == SC_ASN1_TAG_NULL && len == 0)) { + printf(" (%"SC_FORMAT_LEN_SIZE_T"u byte%s)", + len, + len != 1 ? "s" : ""); + } + if (len + hlen > bytesleft) { printf(" Illegal length!\n"); return; } p += hlen + len; bytesleft -= hlen + len; - if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_UNIVERSAL) - printf("%s", tag2str(tag)); if (cla & SC_ASN1_TAG_CONSTRUCTED) { putchar('\n'); - print_tags_recursive(buf0, tagp, len, depth + 1); + print_tags_recursive(buf0, tagp, len, depth + 2*i + 1); continue; } - if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_UNIVERSAL) { - printf(" ["); - switch (tag) { + + switch (tag) { case SC_ASN1_TAG_BIT_STRING: - sc_asn1_print_bit_string(tagp, len); + printf(": "); + sc_asn1_print_bit_string(tagp, len, depth + 2*i + 1); break; case SC_ASN1_TAG_OCTET_STRING: - sc_asn1_print_octet_string(tagp, len); + sc_asn1_print_octet_string(tagp, len, depth + 2*i + 1); break; case SC_ASN1_TAG_OBJECT: + printf(": "); sc_asn1_print_object_id(tagp, len); break; case SC_ASN1_TAG_INTEGER: case SC_ASN1_TAG_ENUMERATED: + printf(": "); sc_asn1_print_integer(tagp, len); break; - case SC_ASN1_TAG_T61STRING: + case SC_ASN1_TAG_IA5STRING: case SC_ASN1_TAG_PRINTABLESTRING: + case SC_ASN1_TAG_T61STRING: case SC_ASN1_TAG_UTF8STRING: + printf(": "); sc_asn1_print_utf8string(tagp, len); break; case SC_ASN1_TAG_BOOLEAN: + printf(": "); sc_asn1_print_boolean(tagp, len); break; case SC_ASN1_GENERALIZEDTIME: + printf(": "); sc_asn1_print_generalizedtime(tagp, len); break; - } - printf("]"); + case SC_ASN1_UTCTIME: + printf(": "); + sc_asn1_print_utctime(tagp, len); + break; } - if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_APPLICATION) - printf(" [%s]", sc_dump_hex(tagp, len)); + if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_APPLICATION) { + print_hex(tagp, len, depth + 2*i + 1); + } - if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_CONTEXT) - printf(" [%s]", sc_dump_hex(tagp, len)); + if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_CONTEXT) { + print_hex(tagp, len, depth + 2*i + 1); + } putchar('\n'); } - return; } void sc_asn1_print_tags(const u8 * buf, size_t buflen) { - printf("Printing tags for buffer of length %d\n", (int) buflen); print_tags_recursive(buf, buf, buflen, 0); } @@ -342,16 +489,14 @@ const u8 *sc_asn1_find_tag(sc_context_t *ctx, const u8 * buf, *taglen_in = 0; while (left >= 2) { - unsigned int cla, tag, mask = 0xff00; + unsigned int cla = 0, tag, mask = 0xff00; buf = p; /* read a tag */ - if (sc_asn1_read_tag(&p, left, &cla, &tag, &taglen) != SC_SUCCESS) - return NULL; - if (left < (size_t)(p - buf)) { - sc_debug(ctx, SC_LOG_DEBUG_ASN1, "invalid TLV object\n"); + if (sc_asn1_read_tag(&p, left, &cla, &tag, &taglen) != SC_SUCCESS + || p == NULL) return NULL; - } + left -= (p - buf); /* we need to shift the class byte to the leftmost * byte of the tag */ @@ -368,10 +513,6 @@ const u8 *sc_asn1_find_tag(sc_context_t *ctx, const u8 * buf, return p; } /* otherwise continue reading tags */ - if (left < taglen) { - sc_debug(ctx, SC_LOG_DEBUG_ASN1, "invalid TLV object\n"); - return NULL; - } left -= taglen; p += taglen; } @@ -383,9 +524,10 @@ const u8 *sc_asn1_skip_tag(sc_context_t *ctx, const u8 ** buf, size_t *buflen, { const u8 *p = *buf; size_t len = *buflen, taglen; - unsigned int cla, tag; + unsigned int cla = 0, tag; - if (sc_asn1_read_tag((const u8 **) &p, len, &cla, &tag, &taglen) != SC_SUCCESS) + if (sc_asn1_read_tag((const u8 **) &p, len, &cla, &tag, &taglen) != SC_SUCCESS + || p == NULL) return NULL; switch (cla & 0xC0) { case SC_ASN1_TAG_UNIVERSAL: @@ -433,20 +575,36 @@ const u8 *sc_asn1_verify_tag(sc_context_t *ctx, const u8 * buf, size_t buflen, } static int decode_bit_string(const u8 * inbuf, size_t inlen, void *outbuf, - size_t outlen, int invert) + size_t outlen, int invert, const int strict) { const u8 *in = inbuf; u8 *out = (u8 *) outbuf; - int zero_bits = *in & 0x07; - size_t octets_left = inlen - 1; int i, count = 0; + int zero_bits; + size_t octets_left; + + if (inlen < 1) + return SC_ERROR_INVALID_ASN1_OBJECT; + + /* The formatting is only enforced by SHALL keyword so we should accept + * by default also non-strict values. */ + if (strict) { + /* 8.6.2.3 If the bitstring is empty, there shall be no + * subsequent octets,and the initial octet shall be zero. */ + if (inlen == 1 && *in != 0) + return SC_ERROR_INVALID_ASN1_OBJECT; + /* ITU-T Rec. X.690 8.6.2.2: The number shall be in the range zero to seven. */ + if ((*in & ~0x07) != 0) + return SC_ERROR_INVALID_ASN1_OBJECT; + } memset(outbuf, 0, outlen); + zero_bits = *in & 0x07; in++; + octets_left = inlen - 1; if (outlen < octets_left) return SC_ERROR_BUFFER_TOO_SMALL; - if (inlen < 1) - return SC_ERROR_INVALID_ASN1_OBJECT; + while (octets_left) { /* 1st octet of input: ABCDEFGH, where A is the MSB */ /* 1st octet of output: HGFEDCBA, where A is the LSB */ @@ -454,9 +612,13 @@ static int decode_bit_string(const u8 * inbuf, size_t inlen, void *outbuf, int bits_to_go; *out = 0; - if (octets_left == 1) + if (octets_left == 1 && zero_bits > 0) { bits_to_go = 8 - zero_bits; - else + /* Verify the padding is zero bits */ + if (*in & (1 << (zero_bits-1))) { + return SC_ERROR_INVALID_ASN1_OBJECT; + } + } else bits_to_go = 8; if (invert) for (i = 0; i < bits_to_go; i++) { @@ -474,15 +636,15 @@ static int decode_bit_string(const u8 * inbuf, size_t inlen, void *outbuf, } int sc_asn1_decode_bit_string(const u8 * inbuf, size_t inlen, - void *outbuf, size_t outlen) + void *outbuf, size_t outlen, const int strict) { - return decode_bit_string(inbuf, inlen, outbuf, outlen, 1); + return decode_bit_string(inbuf, inlen, outbuf, outlen, 1, strict); } int sc_asn1_decode_bit_string_ni(const u8 * inbuf, size_t inlen, - void *outbuf, size_t outlen) + void *outbuf, size_t outlen, const int strict) { - return decode_bit_string(inbuf, inlen, outbuf, outlen, 0); + return decode_bit_string(inbuf, inlen, outbuf, outlen, 0, strict); } static int encode_bit_string(const u8 * inbuf, size_t bits_left, u8 **outbuf, @@ -490,17 +652,16 @@ static int encode_bit_string(const u8 * inbuf, size_t bits_left, u8 **outbuf, { const u8 *in = inbuf; u8 *out; - size_t bytes; - int skipped = 0; + size_t bytes, skipped = 0; - bytes = (bits_left + 7)/8 + 1; + bytes = BYTES4BITS(bits_left) + 1; *outbuf = out = malloc(bytes); if (out == NULL) return SC_ERROR_OUT_OF_MEMORY; *outlen = bytes; out += 1; while (bits_left) { - int i, bits_to_go = 8; + size_t i, bits_to_go = 8; *out = 0; if (bits_left < 8) { @@ -527,7 +688,7 @@ static int encode_bit_string(const u8 * inbuf, size_t bits_left, u8 **outbuf, * Bitfields are just bit strings, stored in an unsigned int * (taking endianness into account) */ -static int decode_bit_field(const u8 * inbuf, size_t inlen, void *outbuf, size_t outlen) +static int decode_bit_field(const u8 * inbuf, size_t inlen, void *outbuf, size_t outlen, const int strict) { u8 data[sizeof(unsigned int)]; unsigned int field = 0; @@ -536,12 +697,12 @@ static int decode_bit_field(const u8 * inbuf, size_t inlen, void *outbuf, size_t if (outlen != sizeof(data)) return SC_ERROR_BUFFER_TOO_SMALL; - n = decode_bit_string(inbuf, inlen, data, sizeof(data), 1); + n = decode_bit_string(inbuf, inlen, data, sizeof(data), 1, strict); if (n < 0) return n; for (i = 0; i < n; i += 8) { - field |= (data[i/8] << i); + field |= ((unsigned int) data[i/8] << i); } memcpy(outbuf, &field, outlen); return 0; @@ -569,18 +730,43 @@ static int encode_bit_field(const u8 *inbuf, size_t inlen, return encode_bit_string(data, bits, outbuf, outlen, 1); } -int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out) +int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out, int strict) { - int a = 0; - size_t i; + int a = 0, is_negative = 0; + size_t i = 0; - if (inlen > sizeof(int)) + if (inlen == 0) { return SC_ERROR_INVALID_ASN1_OBJECT; - if (inbuf[0] & 0x80) - a = -1; - for (i = 0; i < inlen; i++) { + } + if (inlen > sizeof(int)) { + return SC_ERROR_NOT_SUPPORTED; + } + if (inbuf[0] & 0x80) { + if (strict && inlen > 1 && inbuf[0] == 0xff && (inbuf[1] & 0x80)) { + return SC_ERROR_INVALID_ASN1_OBJECT; + } + is_negative = 1; + a |= 0xff^(*inbuf++); + i = 1; + } else { + if (strict && inlen > 1 && inbuf[0] == 0x00 && (inbuf[1] & 0x80) == 0) { + return SC_ERROR_INVALID_ASN1_OBJECT; + } + } + for (; i < inlen; i++) { + if (a > (INT_MAX >> 8) || a < (INT_MIN + (1<<8))) { + return SC_ERROR_NOT_SUPPORTED; + } a <<= 8; - a |= *inbuf++; + if (is_negative) { + a |= 0xff^(*inbuf++); + } else { + a |= *inbuf++; + } + } + if (is_negative) { + /* Calculate Two's complement from previously positive number */ + a = (-1 * a) - 1; } *out = a; return 0; @@ -646,7 +832,8 @@ static int asn1_encode_integer(int in, u8 ** obj, size_t * objsize) int sc_asn1_decode_object_id(const u8 *inbuf, size_t inlen, struct sc_object_id *id) { - int a; + int large_second_octet = 0; + unsigned int a = 0; const u8 *p = inbuf; int *octet; @@ -656,27 +843,64 @@ sc_asn1_decode_object_id(const u8 *inbuf, size_t inlen, struct sc_object_id *id) sc_init_oid(id); octet = id->value; - a = *p; - *octet++ = a / 40; - *octet++ = a % 40; - inlen--; + /* The first octet can be 0, 1 or 2 and is derived from the first byte */ + a = MIN(*p / 40, 2); + *octet++ = a; + + /* The second octet fits here if the previous was 0 or 1 and second one is smaller than 40. + * for the value 2 we can go up to 47. Otherwise the first bit needs to be set + * and we continue reading further */ + if ((*p & 0x80) == 0) { + *octet++ = *p - (a * 40); + inlen--; + } else { + large_second_octet = 1; + } while (inlen) { - p++; + if (!large_second_octet) + p++; + /* This signalizes empty most significant bits, which means + * the unsigned integer encoding is not minimal */ + if (*p == 0x80) { + sc_init_oid(id); + return SC_ERROR_INVALID_ASN1_OBJECT; + } + /* Use unsigned type here so we can process the whole INT range. + * Values can not be negative */ a = *p & 0x7F; inlen--; while (inlen && *p & 0x80) { + /* Limit the OID values to int size and do not overflow */ + if (a > (UINT_MAX>>7)) { + sc_init_oid(id); + return SC_ERROR_NOT_SUPPORTED; + } p++; a <<= 7; a |= *p & 0x7F; inlen--; } + if (*p & 0x80) { + /* We dropped out from previous cycle on the end of + * data while still expecting continuation of value */ + sc_init_oid(id); + return SC_ERROR_INVALID_ASN1_OBJECT; + } + if (large_second_octet) { + a -= (2 * 40); + } + if (a > INT_MAX) { + sc_init_oid(id); + return SC_ERROR_NOT_SUPPORTED; + } *octet++ = a; if (octet - id->value >= SC_MAX_OBJECT_ID_OCTETS) { sc_init_oid(id); return SC_ERROR_INVALID_ASN1_OBJECT; } - }; + large_second_octet = 0; + } return 0; } @@ -708,10 +932,13 @@ sc_asn1_encode_object_id(u8 **buf, size_t *buflen, const struct sc_object_id *id *p = k * 40; break; case 1: - if (k > 39) + if (k > 39 && id->value[0] < 2) { return SC_ERROR_INVALID_ARGUMENTS; - *p++ += k; - break; + } + /* We can encode larger IDs to multiple bytes + * similarly as the following IDs */ + k += *p; + /* fall through */ default: shift = 28; while (shift && (k >> shift) == 0) @@ -747,11 +974,13 @@ static int sc_asn1_decode_utf8string(const u8 *inbuf, size_t inlen, return 0; } +/* + * This assumes the tag is already encoded + */ int sc_asn1_put_tag(unsigned int tag, const u8 * data, size_t datalen, u8 * out, size_t outlen, u8 **ptr) { size_t c = 0; - size_t tag_len; - size_t ii; + unsigned int tag_len, ii; u8 *p = out; u8 tag_char[4] = {0, 0, 0, 0}; @@ -791,7 +1020,7 @@ int sc_asn1_put_tag(unsigned int tag, const u8 * data, size_t datalen, u8 * out, } if (outlen == 0 || out == NULL) { /* Caller only asks for the length that would be written. */ - return tag_len + (c+1) + datalen; + return (int)(tag_len + (c + 1) + datalen); } /* We will write the tag, so check the length. */ if (outlen < tag_len + (c+1) + datalen) @@ -891,7 +1120,9 @@ static int asn1_write_element(sc_context_t *ctx, unsigned int tag, else { *p++ = datalen & 0x7F; } - memcpy(p, data, datalen); + if (datalen && data) { + memcpy(p, data, datalen); + } return SC_SUCCESS; } @@ -1016,9 +1247,12 @@ static int asn1_decode_se_info(sc_context_t *ctx, const u8 *obj, size_t objlen, size_t idx, ptrlen = objlen; int ret; + LOG_FUNC_CALLED(ctx); + ses = calloc(SC_MAX_SE_NUM, sizeof(sc_pkcs15_sec_env_info_t *)); - if (ses == NULL) - return SC_ERROR_OUT_OF_MEMORY; + if (ses == NULL) { + SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_OUT_OF_MEMORY); + } for (idx=0; idx < SC_MAX_SE_NUM && ptrlen; ) { struct sc_asn1_entry asn1_se[2]; @@ -1062,7 +1296,7 @@ static int asn1_decode_se_info(sc_context_t *ctx, const u8 *obj, size_t objlen, free(ses); } - return ret; + SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, ret); } @@ -1092,6 +1326,8 @@ static int asn1_encode_se_info(sc_context_t *ctx, if (ret != SC_SUCCESS) goto err; + if (!ptrlen) + continue; p = (unsigned char *) realloc(out, outlen + ptrlen); if (!p) { ret = SC_ERROR_OUT_OF_MEMORY; @@ -1231,7 +1467,7 @@ static int asn1_encode_p15_object(sc_context_t *ctx, const struct sc_asn1_pkcs15 sc_format_asn1_entry(asn1_c_attr + 3, (void *) &p15_obj.user_consent, NULL, 1); if (p15_obj.access_rules[0].access_mode) { - for (ii=0; p15_obj.access_rules[ii].access_mode; ii++) { + for (ii = 0; ii < SC_PKCS15_MAX_ACCESS_RULES && p15_obj.access_rules[ii].access_mode; ii++) { access_mode_len = sizeof(p15_obj.access_rules[ii].access_mode); sc_format_asn1_entry(asn1_ac_rule[ii] + 0, (void *) &p15_obj.access_rules[ii].access_mode, &access_mode_len, 1); sc_format_asn1_entry(asn1_ac_rule[ii] + 1, (void *) &p15_obj.access_rules[ii].auth_id, NULL, 1); @@ -1288,31 +1524,36 @@ static int asn1_decode_entry(sc_context_t *ctx,struct sc_asn1_entry *entry, case SC_ASN1_INTEGER: case SC_ASN1_ENUMERATED: if (parm != NULL) { - r = sc_asn1_decode_integer(obj, objlen, (int *) entry->parm); - sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*sdecoding '%s' returned %d\n", depth, depth, "", - entry->name, *((int *) entry->parm)); + r = sc_asn1_decode_integer(obj, objlen, (int *) entry->parm, 0); + if (r == SC_SUCCESS) { + sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*sdecoding '%s' returned %d\n", + depth, depth, "", entry->name, *((int *)entry->parm)); + } } break; case SC_ASN1_BIT_STRING_NI: case SC_ASN1_BIT_STRING: if (parm != NULL) { int invert = entry->type == SC_ASN1_BIT_STRING ? 1 : 0; - assert(len != NULL); + if (len == NULL) + return SC_ERROR_INTERNAL; if (objlen < 1) { r = SC_ERROR_INVALID_ASN1_OBJECT; break; } if (entry->flags & SC_ASN1_ALLOC) { u8 **buf = (u8 **) parm; - *buf = malloc(objlen-1); - if (*buf == NULL) { - r = SC_ERROR_OUT_OF_MEMORY; - break; + if (objlen > 1) { + *buf = malloc(objlen-1); + if (*buf == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + break; + } } *len = objlen-1; parm = *buf; } - r = decode_bit_string(obj, objlen, (u8 *) parm, *len, invert); + r = decode_bit_string(obj, objlen, (u8 *) parm, *len, invert, 0); if (r >= 0) { *len = r; r = 0; @@ -1321,16 +1562,17 @@ static int asn1_decode_entry(sc_context_t *ctx,struct sc_asn1_entry *entry, break; case SC_ASN1_BIT_FIELD: if (parm != NULL) - r = decode_bit_field(obj, objlen, (u8 *) parm, *len); + r = decode_bit_field(obj, objlen, (u8 *) parm, *len, 0); break; case SC_ASN1_OCTET_STRING: if (parm != NULL) { size_t c; - assert(len != NULL); + if (len == NULL) + return SC_ERROR_INTERNAL; /* Strip off padding zero */ if ((entry->flags & SC_ASN1_UNSIGNED) - && obj[0] == 0x00 && objlen > 1) { + && objlen > 1 && obj[0] == 0x00) { objlen--; obj++; } @@ -1338,10 +1580,12 @@ static int asn1_decode_entry(sc_context_t *ctx,struct sc_asn1_entry *entry, /* Allocate buffer if needed */ if (entry->flags & SC_ASN1_ALLOC) { u8 **buf = (u8 **) parm; - *buf = malloc(objlen); - if (*buf == NULL) { - r = SC_ERROR_OUT_OF_MEMORY; - break; + if (objlen > 0) { + *buf = malloc(objlen); + if (*buf == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + break; + } } c = *len = objlen; parm = *buf; @@ -1355,13 +1599,16 @@ static int asn1_decode_entry(sc_context_t *ctx,struct sc_asn1_entry *entry, case SC_ASN1_GENERALIZEDTIME: if (parm != NULL) { size_t c; - assert(len != NULL); + if (len == NULL) + return SC_ERROR_INTERNAL; if (entry->flags & SC_ASN1_ALLOC) { u8 **buf = (u8 **) parm; - *buf = malloc(objlen); - if (*buf == NULL) { - r = SC_ERROR_OUT_OF_MEMORY; - break; + if (objlen > 0) { + *buf = malloc(objlen); + if (*buf == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + break; + } } c = *len = objlen; parm = *buf; @@ -1379,7 +1626,8 @@ static int asn1_decode_entry(sc_context_t *ctx,struct sc_asn1_entry *entry, case SC_ASN1_PRINTABLESTRING: case SC_ASN1_UTF8STRING: if (parm != NULL) { - assert(len != NULL); + if (len == NULL) + return SC_ERROR_INTERNAL; if (entry->flags & SC_ASN1_ALLOC) { u8 **buf = (u8 **) parm; *buf = malloc(objlen+1); @@ -1438,6 +1686,38 @@ static int asn1_decode_entry(sc_context_t *ctx,struct sc_asn1_entry *entry, return 0; } +static void sc_free_entry(struct sc_asn1_entry *asn1) { + int idx = 0; + struct sc_asn1_entry *entry = asn1; + + if (!asn1) + return; + + for (idx = 0; asn1[idx].name != NULL; idx++) { + entry = &asn1[idx]; + switch (entry->type) { + case SC_ASN1_CHOICE: + case SC_ASN1_STRUCT: + sc_free_entry((struct sc_asn1_entry *) entry->parm); + break; + case SC_ASN1_OCTET_STRING: + case SC_ASN1_BIT_STRING_NI: + case SC_ASN1_BIT_STRING: + case SC_ASN1_GENERALIZEDTIME: + case SC_ASN1_PRINTABLESTRING: + case SC_ASN1_UTF8STRING: + if ((entry->flags & SC_ASN1_ALLOC) && (entry->flags & SC_ASN1_PRESENT)) { + u8 **buf = (u8 **)entry->parm; + free(*buf); + *buf = NULL; + } + break; + default: + break; + } + } +} + static int asn1_decode(sc_context_t *ctx, struct sc_asn1_entry *asn1, const u8 *in, size_t len, const u8 **newp, size_t *len_left, int choice, int depth) @@ -1448,7 +1728,7 @@ static int asn1_decode(sc_context_t *ctx, struct sc_asn1_entry *asn1, size_t left = len, objlen; sc_debug(ctx, SC_LOG_DEBUG_ASN1, - "%*.*scalled, left=%"SC_FORMAT_LEN_SIZE_T"u, depth %d%s\n", + "%*.*s""called, left=%"SC_FORMAT_LEN_SIZE_T"u, depth %d%s\n", depth, depth, "", left, depth, choice ? ", choice" : ""); if (!p) @@ -1480,7 +1760,12 @@ static int asn1_decode(sc_context_t *ctx, struct sc_asn1_entry *asn1, r = asn1_decode(ctx, (struct sc_asn1_entry *) entry->parm, p, left, &p, &left, 1, depth + 1); - if (r >= 0) + /* When the inner call fails it returns before writing + * back to *newp and *len_left, so the caller's p/left are + * unchanged. Swallowing the error for an optional + * CHOICE is therefore safe: the next field will be + * attempted at the same position. */ + if (r >= 0 || (entry->flags & SC_ASN1_OPTIONAL)) r = 0; goto decode_ok; } @@ -1504,6 +1789,7 @@ static int asn1_decode(sc_context_t *ctx, struct sc_asn1_entry *asn1, } sc_debug(ctx, SC_LOG_DEBUG_ASN1, "next tag: %s\n", line); } + sc_free_entry(asn1); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_ASN1_OBJECT_NOT_FOUND); } r = asn1_decode_entry(ctx, entry, obj, objlen, depth); @@ -1614,44 +1900,56 @@ static int asn1_encode_entry(sc_context_t *ctx, const struct sc_asn1_entry *entr break; case SC_ASN1_BIT_STRING_NI: case SC_ASN1_BIT_STRING: - assert(len != NULL); - if (entry->type == SC_ASN1_BIT_STRING) - r = encode_bit_string((const u8 *) parm, *len, &buf, &buflen, 1); - else - r = encode_bit_string((const u8 *) parm, *len, &buf, &buflen, 0); + if (len != NULL) { + if (entry->type == SC_ASN1_BIT_STRING) + r = encode_bit_string((const u8 *) parm, *len, &buf, &buflen, 1); + else + r = encode_bit_string((const u8 *) parm, *len, &buf, &buflen, 0); + } else { + r = SC_ERROR_INVALID_ARGUMENTS; + } break; case SC_ASN1_BIT_FIELD: - assert(len != NULL); - r = encode_bit_field((const u8 *) parm, *len, &buf, &buflen); + if (len != NULL) { + r = encode_bit_field((const u8 *) parm, *len, &buf, &buflen); + } else { + r = SC_ERROR_INVALID_ARGUMENTS; + } break; case SC_ASN1_PRINTABLESTRING: case SC_ASN1_OCTET_STRING: case SC_ASN1_UTF8STRING: - assert(len != NULL); - buf = malloc(*len + 1); - if (buf == NULL) { - r = SC_ERROR_OUT_OF_MEMORY; - break; + if (len != NULL) { + buf = malloc(*len + 1); + if (buf == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + break; + } + buflen = 0; + /* If the integer is supposed to be unsigned, insert + * a padding byte if the MSB is one */ + if ((entry->flags & SC_ASN1_UNSIGNED) + && (((u8 *) parm)[0] & 0x80)) { + buf[buflen++] = 0x00; + } + memcpy(buf + buflen, parm, *len); + buflen += *len; + } else { + r = SC_ERROR_INVALID_ARGUMENTS; } - buflen = 0; - /* If the integer is supposed to be unsigned, insert - * a padding byte if the MSB is one */ - if ((entry->flags & SC_ASN1_UNSIGNED) - && (((u8 *) parm)[0] & 0x80)) { - buf[buflen++] = 0x00; - } - memcpy(buf + buflen, parm, *len); - buflen += *len; break; case SC_ASN1_GENERALIZEDTIME: - assert(len != NULL); - buf = malloc(*len); - if (buf == NULL) { - r = SC_ERROR_OUT_OF_MEMORY; - break; + if (len != NULL) { + buf = malloc(*len); + if (buf == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + break; + } + memcpy(buf, parm, *len); + buflen = *len; + } else { + r = SC_ERROR_INVALID_ARGUMENTS; } - memcpy(buf, parm, *len); - buflen = *len; break; case SC_ASN1_OBJECT: r = sc_asn1_encode_object_id(&buf, &buflen, (struct sc_object_id *) parm); @@ -1749,6 +2047,10 @@ static int asn1_encode(sc_context_t *ctx, const struct sc_asn1_entry *asn1, u8 *obj = NULL, *buf = NULL, *tmp; size_t total = 0, objsize; + if (asn1 == NULL) { + return SC_ERROR_INVALID_ARGUMENTS; + } + for (idx = 0; asn1[idx].name != NULL; idx++) { r = asn1_encode_entry(ctx, &asn1[idx], &obj, &objsize, depth); if (r) { @@ -1804,10 +2106,12 @@ _sc_asn1_decode(sc_context_t *ctx, struct sc_asn1_entry *asn1, int sc_der_copy(sc_pkcs15_der_t *dst, const sc_pkcs15_der_t *src) { - if (!dst) + if (!dst || !src) return SC_ERROR_INVALID_ARGUMENTS; memset(dst, 0, sizeof(*dst)); if (src->len) { + if (!src->value) + return SC_ERROR_INVALID_ARGUMENTS; dst->value = malloc(src->len); if (!dst->value) return SC_ERROR_OUT_OF_MEMORY; @@ -1889,13 +2193,13 @@ sc_asn1_sig_value_rs_to_sequence(struct sc_context *ctx, unsigned char *in, size int -sc_asn1_sig_value_sequence_to_rs(struct sc_context *ctx, unsigned char *in, size_t inlen, +sc_asn1_sig_value_sequence_to_rs(struct sc_context *ctx, const unsigned char *in, size_t inlen, unsigned char *buf, size_t buflen) { struct sc_asn1_entry asn1_sig_value[C_ASN1_SIG_VALUE_SIZE]; struct sc_asn1_entry asn1_sig_value_coefficients[C_ASN1_SIG_VALUE_COEFFICIENTS_SIZE]; - unsigned char *r, *s; - size_t r_len, s_len, halflen = buflen/2; + unsigned char *r = NULL, *s = NULL; + size_t r_len = 0, s_len = 0, halflen = buflen/2; int rv; LOG_FUNC_CALLED(ctx); @@ -1910,16 +2214,18 @@ sc_asn1_sig_value_sequence_to_rs(struct sc_context *ctx, unsigned char *in, size sc_format_asn1_entry(asn1_sig_value_coefficients + 1, &s, &s_len, 0); rv = sc_asn1_decode(ctx, asn1_sig_value, in, inlen, NULL, NULL); - LOG_TEST_RET(ctx, rv, "ASN.1 decoding ECDSA-Sig-Value failed"); + LOG_TEST_GOTO_ERR(ctx, rv, "ASN.1 decoding ECDSA-Sig-Value failed"); if (halflen < r_len || halflen < s_len) { rv = SC_ERROR_BUFFER_TOO_SMALL; - goto done; + goto err; } memset(buf, 0, buflen); - memcpy(buf + (halflen - r_len), r, r_len); - memcpy(buf + (buflen - s_len), s, s_len); + if (r_len > 0) + memcpy(buf + (halflen - r_len), r, r_len); + if (s_len > 0) + memcpy(buf + (buflen - s_len), s, s_len); sc_log(ctx, "r(%"SC_FORMAT_LEN_SIZE_T"u): %s", halflen, sc_dump_hex(buf, halflen)); @@ -1927,9 +2233,59 @@ sc_asn1_sig_value_sequence_to_rs(struct sc_context *ctx, unsigned char *in, size sc_dump_hex(buf + halflen, halflen)); rv = SC_SUCCESS; -done: +err: free(r); free(s); LOG_FUNC_RETURN(ctx, rv); } + +int sc_asn1_decode_ecdsa_signature(sc_context_t *ctx, const u8 *data, size_t datalen, size_t fieldsize, u8 **out, size_t outlen) { + int i, r; + const unsigned char *pseq, *pint, *pend; + unsigned int cla, tag; + size_t seqlen, intlen; + + if (!ctx || !data || !out || !(*out)) { + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + } + if (outlen < 2 * fieldsize) { + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Output too small for EC signature"); + } + + memset(*out, 0, outlen); + + pseq = data; + r = sc_asn1_read_tag(&pseq, datalen, &cla, &tag, &seqlen); + if (pseq == NULL || r < 0 || seqlen == 0 || (cla | tag) != 0x30) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Can not find 0x30 tag"); + + pint = pseq; + pend = pseq + seqlen; + for (i = 0; i < 2; i++) { + r = sc_asn1_read_tag(&pint, (pend - pint), &cla, &tag, &intlen); + if (pint == NULL || r < 0 || intlen == 0 || (cla | tag) != 0x02) { + r = SC_ERROR_INVALID_DATA; + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_INVALID_DATA, "Can not find 0x02"); + } + + if (intlen == fieldsize + 1) { /* drop leading 00 if present */ + if (*pint != 0x00) { + r = SC_ERROR_INVALID_DATA; + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_INVALID_DATA, "Signature too long"); + } + pint++; + intlen--; + } + if (intlen > fieldsize) { + r = SC_ERROR_INVALID_DATA; + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_INVALID_DATA, "Signature too long"); + } + memcpy(*out + fieldsize * i + fieldsize - intlen , pint, intlen); + pint += intlen; /* next integer */ + } + r = (int)(2 * fieldsize); +err: + LOG_FUNC_RETURN(ctx, r); +} + diff --git a/src/libopensc/asn1.h b/src/libopensc/asn1.h index 0e5cdeb080..734c03ad38 100644 --- a/src/libopensc/asn1.h +++ b/src/libopensc/asn1.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_ASN1_H @@ -96,11 +96,11 @@ void sc_asn1_print_tags(const u8 * buf, size_t buflen); int sc_asn1_utf8string_to_ascii(const u8 * buf, size_t buflen, u8 * outbuf, size_t outlen); int sc_asn1_decode_bit_string(const u8 * inbuf, size_t inlen, - void *outbuf, size_t outlen); + void *outbuf, size_t outlen, const int strict); /* non-inverting version */ int sc_asn1_decode_bit_string_ni(const u8 * inbuf, size_t inlen, - void *outbuf, size_t outlen); -int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out); + void *outbuf, size_t outlen, const int strict); +int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out, int strict); int sc_asn1_decode_object_id(const u8 * inbuf, size_t inlen, struct sc_object_id *id); int sc_asn1_encode_object_id(u8 **buf, size_t *buflen, @@ -124,16 +124,23 @@ int sc_asn1_sig_value_rs_to_sequence(struct sc_context *ctx, unsigned char *in, size_t inlen, unsigned char **buf, size_t *buflen); int sc_asn1_sig_value_sequence_to_rs(struct sc_context *ctx, - unsigned char *in, size_t inlen, + const unsigned char *in, size_t inlen, unsigned char *buf, size_t buflen); -#define SC_ASN1_CLASS_MASK 0x30000000 +/* ECDSA signature decoding*/ +int sc_asn1_decode_ecdsa_signature(sc_context_t *ctx, const u8 *data, size_t datalen, + size_t fieldsize, u8 **out, size_t outlen); + +/* long form tags use these */ +/* Same as SC_ASN1_TAG_* shifted left by 24 bits */ +#define SC_ASN1_CLASS_MASK 0xC0000000 #define SC_ASN1_UNI 0x00000000 /* Universal */ -#define SC_ASN1_APP 0x10000000 /* Application */ -#define SC_ASN1_CTX 0x20000000 /* Context */ -#define SC_ASN1_PRV 0x30000000 /* Private */ -#define SC_ASN1_CONS 0x01000000 +#define SC_ASN1_APP 0x40000000 /* Application */ +#define SC_ASN1_CTX 0x80000000 /* Context */ +#define SC_ASN1_PRV 0xC0000000 /* Private */ +#define SC_ASN1_CONS 0x20000000 +#define SC_ASN1_CLASS_CONS 0xE0000000 /* CLASS and CONS */ #define SC_ASN1_TAG_MASK 0x00FFFFFF #define SC_ASN1_TAGNUM_SIZE 3 @@ -173,6 +180,7 @@ int sc_asn1_sig_value_sequence_to_rs(struct sc_context *ctx, /* use callback function */ #define SC_ASN1_CALLBACK 384 +/* use with short one byte tags */ #define SC_ASN1_TAG_CLASS 0xC0 #define SC_ASN1_TAG_UNIVERSAL 0x00 #define SC_ASN1_TAG_APPLICATION 0x40 @@ -181,6 +189,7 @@ int sc_asn1_sig_value_sequence_to_rs(struct sc_context *ctx, #define SC_ASN1_TAG_CONSTRUCTED 0x20 #define SC_ASN1_TAG_PRIMITIVE 0x1F +#define SC_ASN1_TAG_CLASS_CONS 0xE0 #define SC_ASN1_TAG_EOC 0 #define SC_ASN1_TAG_BOOLEAN 1 diff --git a/src/libopensc/authentic.h b/src/libopensc/authentic.h index 805ad9c8f2..e6e9cf1c1c 100644 --- a/src/libopensc/authentic.h +++ b/src/libopensc/authentic.h @@ -17,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_AUTHENTIC_V3_H diff --git a/src/libopensc/aux-data.c b/src/libopensc/aux-data.c index 84d539b2b8..35c6f07906 100644 --- a/src/libopensc/aux-data.c +++ b/src/libopensc/aux-data.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -151,7 +151,7 @@ sc_aux_data_get_md_guid(struct sc_context *ctx, struct sc_auxiliary_data *aux_da *guid = '\0'; if (!flags) - strcpy(guid, "{"); + strncpy(guid, "{", sizeof guid); strlcat(guid, (char *)cmap_record->guid, sizeof(guid)-1); if (!flags) strlcat(guid, "}", sizeof(guid)); diff --git a/src/libopensc/aux-data.h b/src/libopensc/aux-data.h index 027fe4c489..173876338b 100644 --- a/src/libopensc/aux-data.h +++ b/src/libopensc/aux-data.h @@ -1,6 +1,6 @@ /* * aux-data.h: Non PKCS#15, non ISO7816 data - * Used to pass auxiliary data from non PKCS#15, non ISO7816 appliations (like minidriver) + * Used to pass auxiliary data from non PKCS#15, non ISO7816 applications (like minidriver) * to card specific part through the standard PKCS#15 and ISO7816 frameworks * * Copyright (C) 2016 Viktor Tarasov @@ -17,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _AUX_DATA_H diff --git a/src/libopensc/base64.c b/src/libopensc/base64.c index 4fe7989765..119c02224b 100644 --- a/src/libopensc/base64.c +++ b/src/libopensc/base64.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -52,29 +52,28 @@ static const u8 bin_table[128] = { 0x31,0x32,0x33,0xFF,0xFF,0xFF,0xFF,0xFF, }; -static void to_base64(unsigned int i, u8 *out, unsigned int fillers) +static void to_base64(unsigned int i, u8 *out, size_t fillers) { - unsigned int s = 18, c; - - for (c = 0; c < 4; c++) { + unsigned int s, c; + + for (c = 0, s = 18; c < 4; c++, s -= 6) { if (fillers >= 4 - c) *out = base64_table[64]; else *out = base64_table[(i >> s) & 0x3f]; out++; - s -= 6; } } -static int from_base64(const char *in, unsigned int *out, int *skip) +static int from_base64(const char *in, unsigned int *out, size_t *skip) { unsigned int res = 0, c, s = 18; const char *in0 = in; - + for (c = 0; c < 4; c++, in++) { u8 b; int k = *in; - + if (k < 0 || k >= (int)sizeof(bin_table)) return -1; if (k == 0 && c == 0) @@ -129,7 +128,7 @@ int sc_base64_encode(const u8 *in, size_t len, u8 *out, size_t outlen, size_t li if (len) { if (outlen < 4) return SC_ERROR_BUFFER_TOO_SMALL; - to_base64(i, out, 3-len); + to_base64(i, out, 3 - len); out += 4; outlen -= 4; chars += 4; @@ -150,8 +149,9 @@ int sc_base64_encode(const u8 *in, size_t len, u8 *out, size_t outlen, size_t li int sc_base64_decode(const char *in, u8 *out, size_t outlen) { - int len = 0, r, skip; - unsigned int i; + int len = 0, r = 0; + size_t skip = 0; + unsigned int i = 0; while ((r = from_base64(in, &i, &skip)) > 0) { int finished = 0, s = 16; @@ -173,4 +173,4 @@ int sc_base64_decode(const char *in, u8 *out, size_t outlen) if (r == 0) return len; return SC_ERROR_INVALID_ARGUMENTS; -} +} \ No newline at end of file diff --git a/src/libopensc/card-acos5.c b/src/libopensc/card-acos5.c deleted file mode 100644 index 3e39258561..0000000000 --- a/src/libopensc/card-acos5.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * card-acos5.c: Support for ACS ACOS5 cards. - * - * Copyright (C) 2007 Ian A. Young - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "internal.h" -#include "cardctl.h" - -static struct sc_atr_table acos5_atrs[] = { - {"3b:be:96:00:00:41:05:20:00:00:00:00:00:00:00:00:00:90:00", NULL, NULL, - SC_CARD_TYPE_ACOS5_GENERIC, 0, NULL}, - {"3b:be:18:00:00:41:05:10:00:00:00:00:00:00:00:00:00:90:00", NULL, NULL, - SC_CARD_TYPE_ACOS5_GENERIC, 0, NULL}, - {NULL, NULL, NULL, 0, 0, NULL} -}; - -static struct sc_card_operations *iso_ops; -static struct sc_card_operations acos5_ops; -static struct sc_card_driver acos5_drv = { - "ACS ACOS5 card", - "acos5", - &acos5_ops, - NULL, 0, NULL -}; - -static int acos5_match_card(sc_card_t * card) -{ - int i; - - i = _sc_match_atr(card, acos5_atrs, &card->type); - if (i < 0) - return 0; - return 1; -} - -static int acos5_init(sc_card_t * card) -{ - card->max_recv_size = 128; - card->max_send_size = 128; - return SC_SUCCESS; -} - -static int acos5_select_file_by_path(sc_card_t * card, - const sc_path_t * in_path, - sc_file_t ** file_out) -{ - int in_len = in_path->len; - const u8 *in_pos = in_path->value; - sc_path_t path; - - memset(&path, 0, sizeof(sc_path_t)); - path.len = 2; /* one component at a time */ - path.type = SC_PATH_TYPE_FILE_ID; - - /* - * Check parameters. - */ - if (in_len % 2 != 0) - return SC_ERROR_INVALID_ARGUMENTS; - - /* - * File ID by file ID... - */ - while (in_len) { - int result; - memcpy(path.value, in_pos, 2); - result = iso_ops->select_file(card, &path, file_out); - if (result != SC_SUCCESS) - return result; - in_len -= 2; - in_pos += 2; - } - return SC_SUCCESS; -} - -static int acos5_select_file(sc_card_t * card, - const sc_path_t * in_path, sc_file_t ** file_out) -{ - switch (in_path->type) { - - case SC_PATH_TYPE_PATH: - return acos5_select_file_by_path(card, in_path, file_out); - - default: - return iso_ops->select_file(card, in_path, file_out); - } -} - -static int acos5_get_serialnr(sc_card_t * card, sc_serial_number_t * serial) -{ - int r; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - sc_apdu_t apdu; - - /* - * Check arguments. - */ - if (!serial) - return SC_ERROR_INVALID_ARGUMENTS; - - /* - * Return a cached serial number, if we have one. - */ - if (card->serialnr.len) { - memcpy(serial, &card->serialnr, sizeof(*serial)); - return SC_SUCCESS; - } - - /* - * Fetch serial number using GET CARD INFO. - */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x14, 0, 0); - apdu.cla |= 0x80; - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - apdu.le = 6; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) - return SC_ERROR_INTERNAL; - - /* - * Cache serial number. - */ - memcpy(card->serialnr.value, apdu.resp, MIN(apdu.resplen, SC_MAX_SERIALNR)); - card->serialnr.len = MIN(apdu.resplen, SC_MAX_SERIALNR); - - /* - * Copy and return serial number. - */ - memcpy(serial, &card->serialnr, sizeof(*serial)); - return SC_SUCCESS; -} - -static int acos5_card_ctl(sc_card_t * card, unsigned long cmd, void *ptr) -{ - switch (cmd) { - - case SC_CARDCTL_GET_SERIALNR: - return acos5_get_serialnr(card, (sc_serial_number_t *) ptr); - - default: - return SC_ERROR_NOT_SUPPORTED; - } -} - -static int acos5_list_files(sc_card_t * card, u8 * buf, size_t buflen) -{ - sc_apdu_t apdu; - int r; - size_t count; - u8 *bufp = buf; /* pointer into buf */ - int fno = 0; /* current file index */ - - /* - * Check parameters. - */ - if (!buf || (buflen & 1)) - return SC_ERROR_INVALID_ARGUMENTS; - - /* - * Use CARD GET INFO to fetch the number of files under the - * curently selected DF. - */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x14, 0x01, 0x00); - apdu.cla |= 0x80; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if (apdu.sw1 != 0x90) - return SC_ERROR_INTERNAL; - count = apdu.sw2; - - while (count--) { - u8 info[8]; - - /* - * Truncate the scan if no more room left in output buffer. - */ - if (buflen == 0) - break; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x14, 0x02, - fno++); - apdu.cla |= 0x80; - apdu.resp = info; - apdu.resplen = sizeof(info); - apdu.le = sizeof(info); - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) - return SC_ERROR_INTERNAL; - - *bufp++ = info[2]; - *bufp++ = info[3]; - buflen -= 2; - } - - return (bufp - buf); -} - -static struct sc_card_driver *sc_get_driver(void) -{ - struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); - - iso_ops = iso_drv->ops; - acos5_ops = *iso_ops; - - acos5_ops.match_card = acos5_match_card; - acos5_ops.init = acos5_init; - acos5_ops.select_file = acos5_select_file; - acos5_ops.card_ctl = acos5_card_ctl; - acos5_ops.list_files = acos5_list_files; - - return &acos5_drv; -} - -struct sc_card_driver *sc_get_acos5_driver(void) -{ - return sc_get_driver(); -} diff --git a/src/libopensc/card-akis.c b/src/libopensc/card-akis.c deleted file mode 100644 index c84e5513e9..0000000000 --- a/src/libopensc/card-akis.c +++ /dev/null @@ -1,567 +0,0 @@ -/* - * card-akis.c: Support for AKIS smart cards - * - * Copyright (C) 2007 TUBITAK / UEKAE - * contact: bilgi@pardus.org.tr - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include - -#include "internal.h" -#include "asn1.h" -#include "cardctl.h" - -/* generic iso 7816 operations table */ -static const struct sc_card_operations *iso_ops = NULL; - -/* our operations table with overrides */ -static struct sc_card_operations akis_ops; - -static struct sc_card_driver akis_drv = { - "TUBITAK UEKAE AKIS", - "akis", - &akis_ops, - NULL, 0, NULL -}; - -static struct sc_atr_table akis_atrs[] = { - { "3b:ba:11:00:81:31:fe:4d:55:45:4b:41:45:20:56:31:2e:30:ae", NULL, NULL, SC_CARD_TYPE_AKIS_GENERIC, 0, NULL }, - { NULL, NULL, NULL, 0, 0, NULL } -}; - -static int -akis_match_card(sc_card_t *card) -{ - int i; - - i = _sc_match_atr(card, akis_atrs, &card->type); - if (i < 0) - return 0; - return 1; -} - -static int -akis_init(sc_card_t *card) -{ - unsigned long flags; - - card->name = "AKIS"; - card->cla = 0x00; - card->max_pin_len = 16; - card->max_recv_size = 244; - card->max_send_size = 244; - - flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1; - _sc_card_add_rsa_alg(card, 2048, flags, 0); - - return 0; -} - -static int -select_file(sc_card_t *card, sc_apdu_t *apdu, const sc_path_t *path, - int mode, sc_file_t **file_out) -{ - int r; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - sc_file_t *file; - - sc_format_apdu(card, apdu, SC_APDU_CASE_4_SHORT, 0xA4, mode, 0); - apdu->resp = rbuf; - apdu->resplen = sizeof(rbuf); - apdu->datalen = path->len; - apdu->data = path->value; - apdu->lc = path->len; - apdu->le = 256; - - r = sc_transmit_apdu(card, apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu->sw1, apdu->sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - if (file_out == NULL) - return 0; - - file = sc_file_new(); - if (file == NULL) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); - - r = card->ops->process_fci(card, file, apdu->resp + 2, apdu->resp[1]); - if (r) { - sc_file_free(file); - return r; - } - - *file_out = file; - return 0; -} - -static int -akis_select_file(sc_card_t *card, const sc_path_t *path, - sc_file_t **file_out) -{ - int r; - sc_apdu_t apdu; - - if (path->type == SC_PATH_TYPE_PATH) { - /* FIXME: iso implementation seems already do that - */ - r = select_file(card, &apdu, path, path->len == 2 ? 0 : 8, file_out); - - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select DF"); - return 0; - } else if (path->type == SC_PATH_TYPE_FILE_ID) { - /* AKIS differentiates between EF and DF files - * when selecting with ID, this workaround tries both - */ - r = select_file(card, &apdu, path, 2, file_out); - if (r) - r = select_file(card, &apdu, path, 0, file_out); - - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select DF"); - return 0; - } else { - return iso_ops->select_file(card, path, file_out); - } -} - -static int -akis_list_files(sc_card_t *card, u8 *buf, size_t buflen) -{ - /* This AKIS specific command is not provided by generic ISO driver - */ - sc_apdu_t apdu; - u8 rbuf[256]; - size_t left, fids = 0; - u8 *p; - int r; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x18, 0, 0); - apdu.cla = 0x80; - apdu.le = 256; - apdu.resplen = sizeof(rbuf); - apdu.resp = rbuf; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "DIRECTORY command returned error"); - - left = apdu.resplen; - p = rbuf; - - while (left > 19) { - if (p[0] != 0x2f && p[0] != 0x3d) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Malformatted list reply %02x", p[0]); - return SC_ERROR_INTERNAL; - } - if (buflen >= 2) { - buf[fids++] = p[1]; - buf[fids++] = p[2]; - buflen -= 2; - } else { - break; - } - p += 20; - left -= 20; - } - - r = fids; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - -static int -akis_process_fci(sc_card_t *card, sc_file_t *file, - const u8 *buf, size_t buflen) -{ - int r; - size_t len; - const u8 *p; - u8 perms; - - r = iso_ops->process_fci(card, file, buf, buflen); - if (r < 0) return r; - - /* AKIS uses a different security model than ISO implementation - */ - p = sc_asn1_find_tag(card->ctx, buf, buflen, 0x90, &len); - if (p == NULL) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Security tag missing"); - return SC_ERROR_INTERNAL; - } - perms = p[0]; - /* Bit definitions: - * 0x01 Encrypted - * 0x02 Valid - * 0x04 PIN needed - * 0x08 New PIN - * 0x10 Read - * 0x20 Write - * 0x40 Wrong PIN entered once - * 0x80 Last try for PIN - */ - - if (file->type == SC_FILE_TYPE_DF) { - if (perms & 0x04) - sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, SC_AC_CHV, 0x80); - } else { - if (!(perms & 0x04)) - sc_file_add_acl_entry(file, SC_AC_OP_READ, SC_AC_CHV, 0x80); - } - - return 0; -} - -static int -akis_create_file(sc_card_t *card, sc_file_t *file) -{ - int r; - u8 type; - u8 acl; - u8 fid[4]; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - sc_apdu_t apdu; - - /* AKIS uses different create commands for EF and DF. - * Parameters are not passed as ASN.1 structs but in - * a custom format. - */ - - /* FIXME: hardcoded for now, better get it from file acl params */ - acl = 0xb0; - - fid[0] = (file->id >> 8) & 0xFF; - fid[1] = file->id & 0xFF; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x15, 0, acl); - apdu.cla = 0x80; - apdu.data = fid; - apdu.datalen = 2; - apdu.lc = 2; - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - - if (file->type == SC_FILE_TYPE_WORKING_EF) { - switch (file->ef_structure) { - case SC_FILE_EF_TRANSPARENT: - type = 0x80; - break; - case SC_FILE_EF_LINEAR_FIXED: - type = 0x41; - break; - case SC_FILE_EF_CYCLIC: - type = 0x43; - break; - case SC_FILE_EF_LINEAR_VARIABLE_TLV: - type = 0x45; - break; - default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "This EF structure is not supported yet"); - return SC_ERROR_NOT_SUPPORTED; - } - apdu.p1 = type; - if (type == 0x41 || type == 0x43) { - fid[2] = file->record_length; - apdu.datalen++; - apdu.lc++; - } - } else if (file->type == SC_FILE_TYPE_DF) { - apdu.ins = 0x10; - } else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown file type"); - return SC_ERROR_NOT_SUPPORTED; - } - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - return sc_check_sw(card, apdu.sw1, apdu.sw2); -} - -static int -akis_delete_file(sc_card_t *card, const sc_path_t *path) -{ - int r; - u8 sbuf[2]; - const u8 *buf; - size_t buflen; - int type; - sc_apdu_t apdu; - - switch (path->type) { - case SC_PATH_TYPE_FILE_ID: - sbuf[0] = path->value[0]; - sbuf[1] = path->value[1]; - buf = sbuf; - buflen = 2; - type = 0x02; - break; - case SC_PATH_TYPE_PATH: - buf = path->value; - buflen = path->len; - type = 0x08; - break; - default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type has to be FID or PATH"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); - } - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x16, type, 0x00); - apdu.cla = 0x80; - apdu.lc = buflen; - apdu.datalen = buflen; - apdu.data = buf; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - return sc_check_sw(card, apdu.sw1, apdu.sw2); -} - -static int -akis_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) -{ - if (data->cmd == SC_PIN_CMD_VERIFY) { - /* ISO7816 implementation works */ - return iso_ops->pin_cmd(card, data, tries_left); - } - - if (data->cmd == SC_PIN_CMD_CHANGE) { - /* This is AKIS specific */ - int r; - sc_apdu_t apdu; - u8 buf[64]; - int p1, p2; - - p2 = data->pin_reference; - if (p2 & 0x80) { - p1 = 2; - p2 &= 0x7f; - } else { - p1 = 1; - } - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, p1, p2); - - buf[0] = data->pin1.len; - memcpy(buf+1, data->pin1.data, data->pin1.len); - - buf[data->pin1.len+1] = data->pin2.len; - memcpy(buf+data->pin1.len+2, data->pin2.data, data->pin2.len); - - apdu.data = buf; - apdu.datalen = data->pin1.len + data->pin2.len + 2; - apdu.lc = apdu.datalen; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - return r; - } - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Other pin cmds not supported yet"); - return SC_ERROR_NOT_SUPPORTED; -} - -static int -akis_get_data(sc_card_t *card, unsigned int dataid, u8 *buf, size_t len) -{ - int r; - sc_apdu_t apdu; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, dataid); - apdu.resp = buf; - apdu.resplen = len; - apdu.le = len; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - return r; -} - -static int -akis_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) -{ - int r; - u8 system_buffer[128]; - - if (!serial) - return SC_ERROR_INVALID_ARGUMENTS; - - /* see if we have cached serial number */ - if (card->serialnr.len) goto end; - - /* read serial number */ - r = akis_get_data(card, 6, system_buffer, 0x4D); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "GET_DATA failed"); - - card->serialnr.len = 12; - memcpy(card->serialnr.value, system_buffer+55, 12); - -end: - memcpy(serial, &card->serialnr, sizeof(*serial)); - return SC_SUCCESS; -} - -static int -akis_lifecycle_get(sc_card_t *card, int *mode) -{ - int r; - u8 memory[10]; - - r = akis_get_data(card, 4, memory, 10); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "GET_DATA failed"); - - switch(memory[6]) { - case 0xA0: - *mode = SC_CARDCTRL_LIFECYCLE_ADMIN; - break; - case 0xA5: - *mode = SC_CARDCTRL_LIFECYCLE_USER; - break; - default: - *mode = SC_CARDCTRL_LIFECYCLE_OTHER; - break; - } - return SC_SUCCESS; -} - -static int -akis_lifecycle_set(sc_card_t *card, int *mode) -{ - int r; - u8 stage; - sc_apdu_t apdu; - - switch(*mode) { - case SC_CARDCTRL_LIFECYCLE_ADMIN: - stage = 0x02; - break; - case SC_CARDCTRL_LIFECYCLE_USER: - stage = 0x01; - break; - default: - return SC_ERROR_INVALID_ARGUMENTS; - } - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x09, 0x00, stage); - apdu.cla = 0x80; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - return r; -} - -static int -akis_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) -{ - switch (cmd) { - case SC_CARDCTL_GET_SERIALNR: - return akis_get_serialnr(card, (sc_serial_number_t *)ptr); - case SC_CARDCTL_LIFECYCLE_GET: - return akis_lifecycle_get(card, (int *) ptr); - case SC_CARDCTL_LIFECYCLE_SET: - return akis_lifecycle_set(card, (int *) ptr); - } - return SC_ERROR_NOT_SUPPORTED; -} - -static int -akis_set_security_env(sc_card_t *card, - const sc_security_env_t *env, - int se_num) -{ - int r; - u8 ref; - sc_apdu_t apdu; - - /* AKIS uses key references for accessing keys - */ - if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { - ref = env->key_ref[0]; - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0xC3, ref); - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - return r; - } - return SC_SUCCESS; -} - -static int -akis_logout(sc_card_t *card) -{ - int r; - sc_apdu_t apdu; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x1A, 0, 0); - apdu.cla = 0x80; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - return r; -} - -static struct sc_card_driver * -sc_get_driver(void) -{ - if (iso_ops == NULL) - iso_ops = sc_get_iso7816_driver()->ops; - - akis_ops = *iso_ops; - - akis_ops.match_card = akis_match_card; - akis_ops.init = akis_init; - /* read_binary: ISO7816 implementation works */ - /* write_binary: ISO7816 implementation works */ - /* update_binary: ISO7816 implementation works */ - /* erase_binary: Untested */ - /* read_record: Untested */ - /* write_record: Untested */ - /* append_record: Untested */ - /* update_record: Untested */ - akis_ops.select_file = akis_select_file; - /* get_response: ISO7816 implementation works */ - /* get_challenge: ISO7816 implementation works */ - akis_ops.logout = akis_logout; - /* restore_security_env: Untested */ - akis_ops.set_security_env = akis_set_security_env; - /* decipher: Untested */ - /* compute_signature: ISO7816 implementation works */ - akis_ops.create_file = akis_create_file; - akis_ops.delete_file = akis_delete_file; - akis_ops.list_files = akis_list_files; - /* check_sw: ISO7816 implementation works */ - akis_ops.card_ctl = akis_card_ctl; - akis_ops.process_fci = akis_process_fci; - /* construct_fci: Not needed */ - akis_ops.pin_cmd = akis_pin_cmd; - akis_ops.get_data = akis_get_data; - /* put_data: Not implemented */ - /* delete_record: Not implemented */ - - return &akis_drv; -} - -#if 1 -struct sc_card_driver * -sc_get_akis_driver(void) -{ - return sc_get_driver(); -} -#endif diff --git a/src/libopensc/card-asepcos.c b/src/libopensc/card-asepcos.c index 76d4396be5..32fa926829 100644 --- a/src/libopensc/card-asepcos.c +++ b/src/libopensc/card-asepcos.c @@ -13,10 +13,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -37,7 +37,7 @@ static struct sc_card_driver asepcos_drv = { NULL, 0, NULL }; -static struct sc_atr_table asepcos_atrs[] = { +static const struct sc_atr_table asepcos_atrs[] = { { "3b:d6:18:00:81:b1:80:7d:1f:03:80:51:00:61:10:30:8f", NULL, NULL, SC_CARD_TYPE_ASEPCOS_GENERIC, 0, NULL}, { "3b:d6:18:00:81:b1:fe:7d:1f:03:41:53:45:37:35:35:01", NULL, NULL, SC_CARD_TYPE_ASEPCOS_JAVA, 0, NULL}, { NULL, NULL, NULL, 0, 0, NULL } @@ -65,7 +65,7 @@ static int asepcos_select_asepcos_applet(sc_card_t *card) r = sc_select_file(card, &tpath, NULL); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to select ASEPCOS applet"); + sc_log(card->ctx, "unable to select ASEPCOS applet"); return r; } @@ -83,7 +83,7 @@ static int asepcos_init(sc_card_t *card) if (card->type == SC_CARD_TYPE_ASEPCOS_JAVA) { int r = asepcos_select_asepcos_applet(card); if (r != SC_SUCCESS) - return r; + return SC_ERROR_INVALID_CARD; } /* Set up algorithm info. */ @@ -103,7 +103,7 @@ static int asepcos_init(sc_card_t *card) return SC_SUCCESS; } -/* tables to map the asepcos access mode bytes to the OpenSC +/* tables to map the asepcos access mode bytes to the OpenSC * access mode flags */ typedef struct { @@ -135,12 +135,12 @@ static const amode_entry_t ief_amode_table[] = { { 0, 0 }, }; -static int set_sec_attr(sc_file_t *file, unsigned int am, unsigned int ac, +static int set_sec_attr(sc_file_t *file, unsigned int am, unsigned int ac, unsigned int meth) { const amode_entry_t *table; - /* CHV with reference '0' is the trasport PIN + /* CHV with reference '0' is the transport PIN * and is presented as 'AUT' key with reference '0'*/ if (meth == SC_AC_CHV && ac == 0) meth = SC_AC_AUT; @@ -167,37 +167,45 @@ static int asepcos_parse_sec_attr(sc_card_t *card, sc_file_t *file, const u8 *bu { const u8 *p = buf; - while (len != 0) { + while (len > 0) { unsigned int amode, tlen = 3; - if (len < 5 && p[0] != 0x80 && p[1] != 0x01) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid access mode encoding"); + if (len < 5 || p[0] != 0x80 || p[1] != 0x01) { + sc_log(card->ctx, "invalid access mode encoding"); return SC_ERROR_INTERNAL; } amode = p[2]; if (p[3] == 0x90 && p[4] == 0x00) { int r = set_sec_attr(file, amode, 0, SC_AC_NONE); - if (r != SC_SUCCESS) + if (r != SC_SUCCESS) return r; tlen += 2; } else if (p[3] == 0x97 && p[4] == 0x00) { int r = set_sec_attr(file, amode, 0, SC_AC_NEVER); - if (r != SC_SUCCESS) + if (r != SC_SUCCESS) return r; tlen += 2; - } else if (p[3] == 0xA0 && len >= 4U + p[4]) { + } else if (p[3] == 0xA0 && len >= 5U + p[4]) { + if (len < 6) { + sc_log(card->ctx, "invalid access mode encoding"); + return SC_ERROR_INTERNAL; + } /* TODO: support OR expressions */ int r = set_sec_attr(file, amode, p[5], SC_AC_CHV); if (r != SC_SUCCESS) return r; tlen += 2 + p[4]; /* FIXME */ - } else if (p[3] == 0xAF && len >= 4U + p[4]) { + } else if (p[3] == 0xAF && len >= 5U + p[4]) { + if (len < 6) { + sc_log(card->ctx, "invalid access mode encoding"); + return SC_ERROR_INTERNAL; + } /* TODO: support AND expressions */ int r = set_sec_attr(file, amode, p[5], SC_AC_CHV); if (r != SC_SUCCESS) return r; tlen += 2 + p[4]; /* FIXME */ } else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid security condition"); + sc_log(card->ctx, "invalid security condition"); return SC_ERROR_INTERNAL; } p += tlen; @@ -246,19 +254,19 @@ static int asepcos_get_current_df_path(sc_card_t *card, sc_path_t *path) sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x83); + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x83); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return sc_check_sw(card, apdu.sw1, apdu.sw2); return asepcos_tlvpath_to_scpath(path, apdu.resp, apdu.resplen); } -/* SELECT FILE: call the ISO SELECT FILE implementation and parse +/* SELECT FILE: call the ISO SELECT FILE implementation and parse * asepcos specific security attributes. */ static int asepcos_select_file(sc_card_t *card, const sc_path_t *in_path, @@ -267,12 +275,13 @@ static int asepcos_select_file(sc_card_t *card, const sc_path_t *in_path, int r; sc_path_t npath = *in_path; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); if (in_path->type == SC_PATH_TYPE_PATH) { /* check the current DF to avoid unnecessary re-selection of * the MF (as this might invalidate a security status) */ sc_path_t tpath; + memset(&tpath, 0, sizeof tpath); r = asepcos_get_current_df_path(card, &tpath); /* workaround: as opensc can't handle paths with file id @@ -286,12 +295,12 @@ static int asepcos_select_file(sc_card_t *card, const sc_path_t *in_path, if (tpath.len == npath.len) { /* we are already in the requested DF */ if (file == NULL) - /* no file information requested => + /* no file information requested => * nothing to do */ return SC_SUCCESS; } else { /* shorten path */ - r = sc_path_set(&npath, 0, &in_path->value[tpath.len], + r = sc_path_set(&npath, 0, &in_path->value[tpath.len], npath.len - tpath.len, 0, 0); if (r != SC_SUCCESS) return r; @@ -305,15 +314,15 @@ static int asepcos_select_file(sc_card_t *card, const sc_path_t *in_path, r = iso_ops->select_file(card, &npath, file); /* XXX: this doesn't look right */ - if (file != NULL && *file != NULL) + if (file != NULL && *file != NULL) if ((*file)->ef_structure == SC_FILE_EF_UNKNOWN) (*file)->ef_structure = SC_FILE_EF_TRANSPARENT; if (r == SC_SUCCESS && file != NULL && *file != NULL) { r = asepcos_parse_sec_attr(card, *file, (*file)->sec_attr, (*file)->sec_attr_len); - if (r != SC_SUCCESS) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "error parsing security attributes"); + if (r != SC_SUCCESS) + sc_log(card->ctx, "error parsing security attributes"); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } static int asepcos_set_security_env(sc_card_t *card, @@ -327,7 +336,7 @@ static int asepcos_akn_to_fileid(sc_card_t *card, sc_cardctl_asepcos_akn2fileid_ { int r; u8 sbuf[32], rbuf[SC_MAX_APDU_BUFFER_SIZE]; - sc_apdu_t apdu; + sc_apdu_t apdu = {0}; sbuf[0] = p->akn & 0xff; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x28, 0x02, 0x01); @@ -340,7 +349,7 @@ static int asepcos_akn_to_fileid(sc_card_t *card, sc_cardctl_asepcos_akn2fileid_ apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.resplen != 4) return SC_ERROR_INTERNAL; @@ -356,7 +365,7 @@ static int asepcos_set_sec_attributes(sc_card_t *card, const u8 *data, size_t le int is_ef) { int r, type = is_ef != 0 ? 0x02 : 0x04; - sc_apdu_t apdu; + sc_apdu_t apdu = {0}; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x8a, type, 0xab); apdu.cla |= 0x80; @@ -364,7 +373,7 @@ static int asepcos_set_sec_attributes(sc_card_t *card, const u8 *data, size_t le apdu.datalen = len; apdu.data = data; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } @@ -377,7 +386,7 @@ static int asepcos_set_security_attributes(sc_card_t *card, sc_file_t *file) u8 buf[64], *p; int r = SC_SUCCESS; - /* first check wether the security attributes in encoded form + /* first check whether the security attributes in encoded form * are already set. If present use these */ if (file->sec_attr != NULL && file->sec_attr_len != 0) return asepcos_set_sec_attributes(card, file->sec_attr, @@ -420,9 +429,9 @@ static int asepcos_set_security_attributes(sc_card_t *card, sc_file_t *file) *p++ = (st.fileid >> 8 ) & 0xff; *p++ = st.fileid & 0xff; } else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unknow auth method: '%d'", ent->method); + sc_log(card->ctx, "unknown auth method: '%d'", ent->method); return SC_ERROR_INTERNAL; - } + } } if (p != buf) @@ -436,7 +445,7 @@ static int asepcos_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_le int r; sc_apdu_t apdu; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); /* call RSA ENCRYPT DECRYPT for the decipher operation */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x14, 0x01, 0x00); @@ -447,20 +456,20 @@ static int asepcos_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_le * to tell the card the we want everything available (note: we * always have Le <= crgram_len) */ apdu.le = (outlen >= 256 && crgram_len < 256) ? 256 : outlen; - + apdu.data = crgram; apdu.lc = crgram_len; apdu.datalen = crgram_len; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); - return apdu.resplen; + return (int)apdu.resplen; } /* compute the signature. Currently the RSA ENCRYPT DECRYPT command * is used here (TODO: use the key attributes to determine method - * to use for signature generation). + * to use for signature generation). */ static int asepcos_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, u8 *out, size_t outlen) @@ -469,7 +478,7 @@ static int asepcos_compute_signature(sc_card_t *card, const u8 *data, size_t dat u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_apdu_t apdu; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); if (datalen >= 256) atype = SC_APDU_CASE_4_EXT; @@ -485,9 +494,9 @@ static int asepcos_compute_signature(sc_card_t *card, const u8 *data, size_t dat apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "error creating signature"); + sc_log(card->ctx, "error creating signature"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } @@ -495,7 +504,7 @@ static int asepcos_compute_signature(sc_card_t *card, const u8 *data, size_t dat return SC_ERROR_BUFFER_TOO_SMALL; memcpy(out, apdu.resp, apdu.resplen); - return apdu.resplen; + return (int)apdu.resplen; } /* activates the EF/DF specified in the file id. @@ -513,22 +522,22 @@ static int asepcos_activate_file(sc_card_t *card, int fileid, int is_ef) apdu.datalen = 2; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); -} +} /* CREATE FILE: creates wEF, iEF and DFs. Note: although the ISO * command is used for wEF and iEF so format of the data send to - * the card is asepcos specific. + * the card is asepcos specific. * @param card the sc_card_t object to use - * @param file sc_file_t object describing the file to create + * @param file sc_file_t object describing the file to create * @return SC_SUCCESS on success and an error code otherwise. */ static int asepcos_create_file(sc_card_t *card, sc_file_t *file) { if (file->type == SC_FILE_TYPE_DF) { int r, type; - sc_apdu_t apdu; + sc_apdu_t apdu = {0}; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE], *p = &sbuf[0]; *p++ = (file->id >> 8) & 0xff; @@ -555,9 +564,9 @@ static int asepcos_create_file(sc_card_t *card, sc_file_t *file) apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) - return sc_check_sw(card, apdu.sw1, apdu.sw2); + return sc_check_sw(card, apdu.sw1, apdu.sw2); r = sc_select_file(card, &file->path, NULL); if (r != SC_SUCCESS) @@ -565,7 +574,7 @@ static int asepcos_create_file(sc_card_t *card, sc_file_t *file) /* set security attributes */ r = asepcos_set_security_attributes(card, file); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to set security attributes"); + sc_log(card->ctx, "unable to set security attributes"); return r; } return SC_SUCCESS; @@ -604,21 +613,21 @@ static int asepcos_create_file(sc_card_t *card, sc_file_t *file) apdu.datalen = p - sbuf; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return sc_check_sw(card, apdu.sw1, apdu.sw2); /* set security attributes */ r = asepcos_set_security_attributes(card, file); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to set security attributes"); + sc_log(card->ctx, "unable to set security attributes"); return r; } return asepcos_activate_file(card, file->id, 1); } else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { /* for internal EF we 'misuse' the prop_attr field of the * sc_file_t object to store the data send to the card in - * the CREATE EF call. + * the CREATE EF call. */ int r, atype = SC_APDU_CASE_3_SHORT; sc_apdu_t apdu; @@ -632,13 +641,13 @@ static int asepcos_create_file(sc_card_t *card, sc_file_t *file) apdu.data = file->prop_attr; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return sc_check_sw(card, apdu.sw1, apdu.sw2); /* set security attributes */ r = asepcos_set_security_attributes(card, file); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to set security attributes"); + sc_log(card->ctx, "unable to set security attributes"); return r; } return asepcos_activate_file(card, file->id, 1); @@ -672,7 +681,7 @@ static int asepcos_list_files(sc_card_t *card, u8 *buf, size_t blen) return r; if (tfile->prop_attr_len != 6 || tfile->prop_attr == NULL) { sc_file_free(tfile); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to parse proprietary FCI attributes"); + sc_log(card->ctx, "unable to parse proprietary FCI attributes"); return SC_ERROR_INTERNAL; } dfFID = (tfile->prop_attr[2] << 8) | tfile->prop_attr[3]; @@ -738,32 +747,32 @@ static int asepcos_delete_file(sc_card_t *card, const sc_path_t *path) apdu.resplen = sizeof(buf); apdu.resp = buf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { /* looks like a EF */ atype = SC_APDU_CASE_3_SHORT; ftype = 0x02; - buf[0] = path->value[path->len-2]; + buf[0] = path->value[path->len-2]; buf[1] = path->value[path->len-1]; } else { - /* presumedly a DF */ + /* presumably a DF */ atype = SC_APDU_CASE_1; ftype = 0x00; } - + sc_format_apdu(card, &apdu, atype, 0xe4, ftype, 0x00); if (atype == SC_APDU_CASE_3_SHORT) { apdu.lc = 2; apdu.datalen = 2; apdu.data = buf; } - + r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } -/* returns the default transport key (note: this should be put in the +/* returns the default transport key (note: this should be put in the * pkcs15 profile file). */ static int asepcos_get_default_key(sc_card_t *card, @@ -790,11 +799,11 @@ static int asepcos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) apdu.resplen = sizeof(rbuf); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; if (apdu.resplen != 8) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unexpected response to GET DATA serial number\n"); + sc_log(card->ctx, "unexpected response to GET DATA serial number\n"); return SC_ERROR_INTERNAL; } /* cache serial number */ @@ -821,7 +830,7 @@ static int asepcos_change_key(sc_card_t *card, sc_cardctl_asepcos_change_key_t * apdu.data = p->data; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } @@ -894,7 +903,7 @@ static int asepcos_build_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, apdu->data = buf; break; case SC_PIN_CMD_UNBLOCK: - /* build the UNBLOCK KEY apdu. The PIN file is implicitly + /* build the UNBLOCK KEY apdu. The PIN file is implicitly * selected by its SFID. The new PIN is provided in the * data field of the UNBLOCK KEY command. */ *p++ = 0x81; @@ -920,22 +929,18 @@ static int asepcos_build_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, /* generic function to handle the different PIN operations, i.e verify * change and unblock. */ -static int asepcos_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *pdata, - int *tries_left) +static int asepcos_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *pdata) { sc_apdu_t apdu; int r = SC_SUCCESS; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - if (tries_left) - *tries_left = -1; - /* only PIN verification is supported at the moment */ /* check PIN length */ if (pdata->pin1.len < 4 || pdata->pin1.len > 16) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid PIN1 length"); - return SC_ERROR_INVALID_PIN_LENGTH; + sc_log(card->ctx, "invalid PIN1 length"); + return SC_ERROR_INVALID_PIN_LENGTH; } switch (pdata->cmd) { @@ -951,14 +956,14 @@ static int asepcos_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *pdata, break; r = sc_transmit_apdu(card, &apdu); if (r != SC_SUCCESS) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed"); + sc_log(card->ctx, "APDU transmit failed"); break; case SC_PIN_CMD_CHANGE: if (pdata->pin_type != SC_AC_CHV) return SC_ERROR_INVALID_ARGUMENTS; if (pdata->pin2.len < 4 || pdata->pin2.len > 16) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid PIN2 length"); - return SC_ERROR_INVALID_PIN_LENGTH; + sc_log(card->ctx, "invalid PIN2 length"); + return SC_ERROR_INVALID_PIN_LENGTH; } /* 1. step: verify the old pin */ r = asepcos_build_pin_apdu(card, &apdu, pdata, sbuf, sizeof(sbuf), SC_PIN_CMD_VERIFY, 0); @@ -966,12 +971,11 @@ static int asepcos_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *pdata, break; r = sc_transmit_apdu(card, &apdu); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed"); + sc_log(card->ctx, "APDU transmit failed"); break; } if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) { /* unable to verify the old PIN */ - r = sc_check_sw(card, apdu.sw1, apdu.sw2); break; } /* 2, step: use CHANGE KEY to update the PIN */ @@ -980,15 +984,14 @@ static int asepcos_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *pdata, break; r = sc_transmit_apdu(card, &apdu); if (r != SC_SUCCESS) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); + sc_log(card->ctx, "APDU transmit failed"); break; case SC_PIN_CMD_UNBLOCK: if (pdata->pin_type != SC_AC_CHV) return SC_ERROR_INVALID_ARGUMENTS; if (pdata->pin2.len < 4 || pdata->pin2.len > 16) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid PIN2 length"); - return SC_ERROR_INVALID_PIN_LENGTH; + sc_log(card->ctx, "invalid PIN2 length"); + return SC_ERROR_INVALID_PIN_LENGTH; } /* 1. step: verify the puk */ r = asepcos_build_pin_apdu(card, &apdu, pdata, sbuf, sizeof(sbuf), SC_PIN_CMD_VERIFY, 1); @@ -996,7 +999,7 @@ static int asepcos_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *pdata, break; r = sc_transmit_apdu(card, &apdu); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed"); + sc_log(card->ctx, "APDU transmit failed"); break; } /* 2, step: unblock and change the pin */ @@ -1005,24 +1008,56 @@ static int asepcos_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *pdata, break; r = sc_transmit_apdu(card, &apdu); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed"); + sc_log(card->ctx, "APDU transmit failed"); break; } - r = sc_check_sw(card, apdu.sw1, apdu.sw2); break; default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "error: unknow cmd type"); + sc_log(card->ctx, "error: unknown cmd type"); return SC_ERROR_INTERNAL; } /* Clear the buffer - it may contain pins */ sc_mem_clear(sbuf, sizeof(sbuf)); /* check for remaining tries if verification failed */ - if (apdu.sw1 == 0x63) { - if ((apdu.sw2 & 0xF0) == 0xC0 && tries_left != NULL) - *tries_left = apdu.sw2 & 0x0F; - return SC_ERROR_PIN_CODE_INCORRECT; + if (r == SC_SUCCESS) { + if (apdu.sw1 == 0x63) { + if ((apdu.sw2 & 0xF0) == 0xC0) + pdata->pin1.tries_left = apdu.sw2 & 0x0F; + r = SC_ERROR_PIN_CODE_INCORRECT; + return r; + } + r = sc_check_sw(card, apdu.sw1, apdu.sw2); } - return sc_check_sw(card, apdu.sw1, apdu.sw2); + + return r; +} + +static int asepcos_card_reader_lock_obtained(sc_card_t *card, int was_reset) +{ + int r = SC_SUCCESS; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (was_reset > 0 && card->type == SC_CARD_TYPE_ASEPCOS_JAVA) { + /* in case of a Java card try to select the ASEPCOS applet */ + r = asepcos_select_asepcos_applet(card); + } + + LOG_FUNC_RETURN(card->ctx, r); +} + +static int asepcos_logout(sc_card_t *card) +{ + int r = SC_ERROR_NOT_SUPPORTED; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (card->type == SC_CARD_TYPE_ASEPCOS_JAVA) { + /* in case of a Java card try to select the ASEPCOS applet */ + r = asepcos_select_asepcos_applet(card); + } + + LOG_FUNC_RETURN(card->ctx, r); } static struct sc_card_driver * sc_get_driver(void) @@ -1041,6 +1076,8 @@ static struct sc_card_driver * sc_get_driver(void) asepcos_ops.list_files = asepcos_list_files; asepcos_ops.card_ctl = asepcos_card_ctl; asepcos_ops.pin_cmd = asepcos_pin_cmd; + asepcos_ops.logout = asepcos_logout; + asepcos_ops.card_reader_lock_obtained = asepcos_card_reader_lock_obtained; return &asepcos_drv; } diff --git a/src/libopensc/card-atrust-acos.c b/src/libopensc/card-atrust-acos.c index 33496b7376..f56f4488d4 100644 --- a/src/libopensc/card-atrust-acos.c +++ b/src/libopensc/card-atrust-acos.c @@ -2,7 +2,7 @@ * atrust-acos.c: Support for A-Trust ACOS based cards * * Copyright (C) 2005 Franz Brandl based on work from - * Jörn Zukowski and + * Jörn Zukowski and * Nils Larsch , TrustCenter AG * * This library is free software; you can redistribute it and/or @@ -17,10 +17,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -67,7 +67,7 @@ static struct sc_card_driver atrust_acos_drv = { typedef struct atrust_acos_ex_data_st { int sec_ops; /* the currently selected security operation, * i.e. SC_SEC_OPERATION_AUTHENTICATE etc. */ - unsigned int fix_digestInfo; + unsigned long fix_digestInfo; } atrust_acos_ex_data; /*****************************************************************************/ @@ -75,14 +75,14 @@ typedef struct atrust_acos_ex_data_st { static int atrust_acos_match_card(struct sc_card *card) { int i, match = 0; - - for (i = 0; atrust_acos_atrs[i] != NULL; i++) + + for (i = 0; atrust_acos_atrs[i] != NULL; i++) { u8 defatr[SC_MAX_ATR_SIZE]; size_t len = sizeof(defatr); const char *atrp = atrust_acos_atrs[i]; - + if (sc_hex_to_bin(atrp, defatr, &len)) continue; /* we may only verify part of ATR since */ @@ -116,14 +116,14 @@ static int atrust_acos_init(struct sc_card *card) /* set the supported algorithm */ - flags = SC_ALGORITHM_RSA_PAD_PKCS1 + flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_HASH_SHA1 | SC_ALGORITHM_RSA_HASH_MD5 | SC_ALGORITHM_RSA_HASH_RIPEMD160 | SC_ALGORITHM_RSA_HASH_MD5_SHA1; - if (!strcmp(card->name, ACOS_EMV_A05)) + if (card->name != NULL && !strcmp(card->name, ACOS_EMV_A05)) flags |= SC_ALGORITHM_RSA_HASH_SHA256; _sc_card_add_rsa_alg(card, 1536, flags, 0x10001); @@ -152,8 +152,8 @@ static int process_fci(struct sc_context *ctx, struct sc_file *file, size_t taglen, len = buflen; const u8 *tag = NULL, *p; - - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "processing FCI bytes\n"); + + sc_log(ctx, "processing FCI bytes\n"); if (buflen < 2) return SC_ERROR_INTERNAL; @@ -170,12 +170,12 @@ static int process_fci(struct sc_context *ctx, struct sc_file *file, file->shareable = 0; file->record_length = 0; file->size = 0; - + /* get file size */ tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " bytes in file: %d\n", bytes); + sc_log(ctx, " bytes in file: %d\n", bytes); file->size = bytes; } @@ -224,8 +224,8 @@ static int process_fci(struct sc_context *ctx, struct sc_file *file, } } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " type: %s\n", type); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " EF structure: %s\n", structure); + sc_log(ctx, " type: %s\n", type); + sc_log(ctx, " EF structure: %s\n", structure); } file->magic = SC_FILE_MAGIC; @@ -249,27 +249,22 @@ static int atrust_acos_select_aid(struct sc_card *card, apdu.resplen = 0; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); /* check return value */ - if (!(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) && apdu.sw1 != 0x61 ) + if (!(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) && apdu.sw1 != 0x61) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); - - /* update cache */ - card->cache.current_path.type = SC_PATH_TYPE_DF_NAME; - card->cache.current_path.len = len; - memcpy(card->cache.current_path.value, aid, len); if (file_out) { sc_file_t *file = sc_file_new(); if (!file) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->type = SC_FILE_TYPE_DF; file->ef_structure = SC_FILE_EF_UNKNOWN; file->path.len = 0; file->size = 0; /* AID */ - for (i = 0; i < len; i++) + for (i = 0; i < len; i++) file->name[i] = aid[i]; file->namelen = len; file->id = 0x0000; @@ -300,7 +295,7 @@ static int atrust_acos_select_fid(struct sc_card *card, apdu.datalen = 2; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.p2 == 0x00 && apdu.sw1 == 0x62 && apdu.sw2 == 0x84 ) { /* no FCI => we have a DF (see comment in process_fci()) */ @@ -310,7 +305,7 @@ static int atrust_acos_select_fid(struct sc_card *card, apdu.resplen = 0; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU re-transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU re-transmit failed"); } else if (apdu.sw1 == 0x61 || (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)) { /* SELECT returned some data (possible FCI) => * try a READ BINARY to see if a EF is selected */ @@ -322,7 +317,7 @@ static int atrust_acos_select_fid(struct sc_card *card, apdu2.le = 1; apdu2.lc = 0; r = sc_transmit_apdu(card, &apdu2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu2.sw1 == 0x69 && apdu2.sw2 == 0x86) /* no current EF is selected => we have a DF */ bIsDF = 1; @@ -331,26 +326,11 @@ static int atrust_acos_select_fid(struct sc_card *card, if (apdu.sw1 != 0x61 && (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); - /* update cache */ - if (bIsDF) { - card->cache.current_path.type = SC_PATH_TYPE_PATH; - card->cache.current_path.value[0] = 0x3f; - card->cache.current_path.value[1] = 0x00; - if (id_hi == 0x3f && id_lo == 0x00) - card->cache.current_path.len = 2; - else { - card->cache.current_path.len = 4; - card->cache.current_path.value[2] = id_hi; - card->cache.current_path.value[3] = id_lo; - } - } - if (file_out) { sc_file_t *file = sc_file_new(); if (!file) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); - file->id = (id_hi << 8) + id_lo; - file->path = card->cache.current_path; + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + file->id = (id_hi << 8) + id_lo; if (bIsDF) { /* we have a DF */ @@ -362,7 +342,7 @@ static int atrust_acos_select_fid(struct sc_card *card, *file_out = file; } else { /* ok, assume we have a EF */ - r = process_fci(card->ctx, file, apdu.resp, + r = process_fci(card->ctx, file, apdu.resp, apdu.resplen); if (r != SC_SUCCESS) { sc_file_free(file); @@ -385,48 +365,22 @@ static int atrust_acos_select_file(struct sc_card *card, u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; int r; size_t i, pathlen; - char pbuf[SC_MAX_PATH_STRING_SIZE]; - - - r = sc_path_print(pbuf, sizeof(pbuf), &card->cache.current_path); - if (r != SC_SUCCESS) - pbuf[0] = '\0'; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "current path (%s, %s): %s (len: %"SC_FORMAT_LEN_SIZE_T"u)\n", - card->cache.current_path.type == SC_PATH_TYPE_DF_NAME ? - "aid" : "path", - card->cache.valid ? "valid" : "invalid", pbuf, - card->cache.current_path.len); memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; - if (in_path->type == SC_PATH_TYPE_FILE_ID) - { /* SELECT EF/DF with ID */ + if (in_path->type == SC_PATH_TYPE_FILE_ID) { + /* SELECT EF/DF with ID */ /* Select with 2byte File-ID */ if (pathlen != 2) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INVALID_ARGUMENTS); return atrust_acos_select_fid(card, path[0], path[1], file_out); - } - else if (in_path->type == SC_PATH_TYPE_DF_NAME) - { /* SELECT DF with AID */ + } else if (in_path->type == SC_PATH_TYPE_DF_NAME) { + /* SELECT DF with AID */ /* Select with 1-16byte Application-ID */ - if (card->cache.valid - && card->cache.current_path.type == SC_PATH_TYPE_DF_NAME - && card->cache.current_path.len == pathlen - && memcmp(card->cache.current_path.value, pathbuf, pathlen) == 0 ) - { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cache hit\n"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); - } - else - return atrust_acos_select_aid(card, pathbuf, pathlen, file_out); - } - else if (in_path->type == SC_PATH_TYPE_PATH) - { + return atrust_acos_select_aid(card, pathbuf, pathlen, file_out); + } else if (in_path->type == SC_PATH_TYPE_PATH) { u8 n_pathbuf[SC_MAX_PATH_SIZE]; - int bMatch = -1; /* Select with path (sequence of File-IDs) */ /* ACOS only supports one @@ -445,82 +399,17 @@ static int atrust_acos_select_file(struct sc_card *card, { n_pathbuf[0] = 0x3f; n_pathbuf[1] = 0x00; - for (i=0; i< pathlen; i++) - n_pathbuf[i+2] = pathbuf[i]; + memcpy(n_pathbuf+2, path, pathlen); path = n_pathbuf; - pathlen += 2; - } - - /* check current working directory */ - if (card->cache.valid - && card->cache.current_path.type == SC_PATH_TYPE_PATH - && card->cache.current_path.len >= 2 - && card->cache.current_path.len <= pathlen ) - { - bMatch = 0; - for (i=0; i < card->cache.current_path.len; i+=2) - if (card->cache.current_path.value[i] == path[i] - && card->cache.current_path.value[i+1] == path[i+1] ) - bMatch += 2; + pathlen += 2; } - if ( card->cache.valid && bMatch >= 0 ) - { - if ( pathlen - bMatch == 2 ) - /* we are in the rigth directory */ - return atrust_acos_select_fid(card, path[bMatch], path[bMatch+1], file_out); - else if ( pathlen - bMatch > 2 ) - { - /* two more steps to go */ - sc_path_t new_path; - - /* first step: change directory */ - r = atrust_acos_select_fid(card, path[bMatch], path[bMatch+1], NULL); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "SELECT FILE (DF-ID) failed"); - - memset(&new_path, 0, sizeof(sc_path_t)); - new_path.type = SC_PATH_TYPE_PATH; - new_path.len = pathlen - bMatch-2; - memcpy(new_path.value, &(path[bMatch+2]), new_path.len); - /* final step: select file */ - return atrust_acos_select_file(card, &new_path, file_out); - } - else /* if (bMatch - pathlen == 0) */ - { - /* done: we are already in the - * requested directory */ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cache hit\n"); - /* copy file info (if necessary) */ - if (file_out) { - sc_file_t *file = sc_file_new(); - if (!file) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); - file->id = (path[pathlen-2] << 8) + - path[pathlen-1]; - file->path = card->cache.current_path; - file->type = SC_FILE_TYPE_DF; - file->ef_structure = SC_FILE_EF_UNKNOWN; - file->size = 0; - file->namelen = 0; - file->magic = SC_FILE_MAGIC; - *file_out = file; - } - /* nothing left to do */ - return SC_SUCCESS; - } - } - else - { - /* no usable cache */ - for ( i=0; ictx, SC_LOG_DEBUG_NORMAL, r, "SELECT FILE (DF-ID) failed"); - } - return atrust_acos_select_fid(card, path[pathlen-2], path[pathlen-1], file_out); + for (i = 0; i < pathlen - 2; i += 2) { + r = atrust_acos_select_fid(card, path[i], path[i + 1], NULL); + LOG_TEST_RET(card->ctx, r, "SELECT FILE (DF-ID) failed"); } - } - else + return atrust_acos_select_fid(card, path[pathlen - 2], path[pathlen - 1], file_out); + } else SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } @@ -551,7 +440,7 @@ static int atrust_acos_set_security_env(struct sc_card *card, /* copy key reference, if present */ if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { - if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) + if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) *p++ = 0x83; else *p++ = 0x84; @@ -561,7 +450,7 @@ static int atrust_acos_set_security_env(struct sc_card *card, } pp = p; if (operation == SC_SEC_OPERATION_DECIPHER){ - if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { + if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02) { *p++ = 0x80; *p++ = 0x01; *p++ = 0x02; @@ -574,14 +463,14 @@ static int atrust_acos_set_security_env(struct sc_card *card, apdu.lc = p - sbuf; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); return SC_SUCCESS; } /* try COMPUTE SIGNATURE */ if (operation == SC_SEC_OPERATION_SIGN && ( - env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1 || + env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 || env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796)) { if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { *p++ = 0x80; @@ -592,7 +481,7 @@ static int atrust_acos_set_security_env(struct sc_card *card, /* set the method to use based on the algorithm_flags */ *p++ = 0x80; *p++ = 0x01; - if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { + if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01) { if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) *p++ = 0x12; else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_RIPEMD160) @@ -621,10 +510,10 @@ static int atrust_acos_set_security_env(struct sc_card *card, apdu.datalen = p - sbuf; apdu.lc = p - sbuf; apdu.le = 0; - /* we don't know whether to use + /* we don't know whether to use * COMPUTE SIGNATURE or INTERNAL AUTHENTICATE */ r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { ex_data->fix_digestInfo = 0; ex_data->sec_ops = SC_SEC_OPERATION_SIGN; @@ -637,19 +526,19 @@ static int atrust_acos_set_security_env(struct sc_card *card, } try_authenticate: /* try INTERNAL AUTHENTICATE */ - if (operation == SC_SEC_OPERATION_AUTHENTICATE && + if (operation == SC_SEC_OPERATION_AUTHENTICATE && env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { *p++ = 0x80; *p++ = 0x01; *p++ = 0x01; - + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xa4); apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); ex_data->fix_digestInfo = env->algorithm_flags; @@ -677,7 +566,7 @@ static int atrust_acos_compute_signature(struct sc_card *card, if (ex_data->sec_ops == SC_SEC_OPERATION_SIGN) { /* compute signature with the COMPUTE SIGNATURE command */ - + /* set the hash value */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x90, 0x81); @@ -689,9 +578,9 @@ static int atrust_acos_compute_signature(struct sc_card *card, apdu.lc = datalen; apdu.datalen = datalen; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); /* call COMPUTE SIGNATURE */ @@ -704,11 +593,11 @@ static int atrust_acos_compute_signature(struct sc_card *card, apdu.lc = 0; apdu.datalen = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); } } else if (ex_data->sec_ops == SC_SEC_OPERATION_AUTHENTICATE) { size_t tmp_len; @@ -722,7 +611,7 @@ static int atrust_acos_compute_signature(struct sc_card *card, flags = SC_ALGORITHM_RSA_HASH_NONE; tmp_len = sizeof(sbuf); r = sc_pkcs1_encode(card->ctx, flags, data, datalen, - sbuf, &tmp_len, sizeof(sbuf)); + sbuf, &tmp_len, sizeof(sbuf)*8, NULL); if (r < 0) return r; } else { @@ -736,14 +625,14 @@ static int atrust_acos_compute_signature(struct sc_card *card, apdu.resplen = sizeof(rbuf); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); } } else SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); @@ -766,8 +655,9 @@ static int atrust_acos_decipher(struct sc_card *card, u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - assert(card != NULL && crgram != NULL && out != NULL); - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + if (card == NULL || crgram == NULL || out == NULL) + return SC_ERROR_INTERNAL; + LOG_FUNC_CALLED(card->ctx); if (crgram_len > 255) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); @@ -777,7 +667,7 @@ static int atrust_acos_decipher(struct sc_card *card, sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); - + sbuf[0] = 0; /* padding indicator byte, 0x00 = No further indication */ memcpy(sbuf + 1, crgram, crgram_len); apdu.data = sbuf; @@ -785,12 +675,12 @@ static int atrust_acos_decipher(struct sc_card *card, apdu.datalen = crgram_len + 1; apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); @@ -802,17 +692,17 @@ static int atrust_acos_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "sw1 = 0x%02x, sw2 = 0x%02x\n", sw1, sw2); - - if (sw1 == 0x90) + sc_log(card->ctx, "sw1 = 0x%02x, sw2 = 0x%02x\n", sw1, sw2); + + if (sw1 == 0x90 && sw2 == 0x00) return SC_SUCCESS; if (sw1 == 0x63 && (sw2 & ~0x0fU) == 0xc0 ) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Verification failed (remaining tries: %d)\n", + sc_log(card->ctx, "Verification failed (remaining tries: %d)\n", (sw2 & 0x0f)); return SC_ERROR_PIN_CODE_INCORRECT; } - + /* iso error */ return iso_ops->check_sw(card, sw1, sw2); } @@ -841,7 +731,7 @@ static int acos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) apdu.lc = 0; apdu.datalen = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; /* cache serial number */ @@ -880,9 +770,9 @@ static int atrust_acos_logout(struct sc_card *card) apdu.data = mf_buf; apdu.datalen = 2; apdu.resplen = 0; - + r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU re-transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU re-transmit failed"); if (apdu.sw1 == 0x69 && apdu.sw2 == 0x85) /* the only possible reason for this error here is, afaik, @@ -900,7 +790,7 @@ static struct sc_card_driver * sc_get_driver(void) struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); if (iso_ops == NULL) iso_ops = iso_drv->ops; - + atrust_acos_ops = *iso_drv->ops; atrust_acos_ops.match_card = atrust_acos_match_card; atrust_acos_ops.init = atrust_acos_init; @@ -914,7 +804,7 @@ static struct sc_card_driver * sc_get_driver(void) atrust_acos_ops.decipher = atrust_acos_decipher; atrust_acos_ops.card_ctl = atrust_acos_card_ctl; atrust_acos_ops.logout = atrust_acos_logout; - + return &atrust_acos_drv; } diff --git a/src/libopensc/card-authentic.c b/src/libopensc/card-authentic.c index 4da5933534..c7982a39f0 100644 --- a/src/libopensc/card-authentic.c +++ b/src/libopensc/card-authentic.c @@ -17,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H @@ -38,14 +38,6 @@ /* #include "hash-strings.h" */ #include "authentic.h" -#include -#include -#include -#include -#include -#include -#include -#include #include #define AUTHENTIC_CARD_DEFAULT_FLAGS ( 0 \ @@ -69,18 +61,19 @@ static struct sc_card_driver authentic_drv = { NULL, 0, NULL }; +#define AUTHENTIC_N_PINS 8 /* * FIXME: use dynamic allocation for the PIN data to reduce memory usage * actually size of 'authentic_private_data' 140kb */ struct authentic_private_data { - struct sc_pin_cmd_data pins[8]; - unsigned char pins_sha1[8][SHA_DIGEST_LENGTH]; + struct sc_pin_cmd_data pins[AUTHENTIC_N_PINS]; + unsigned char pins_sha1[AUTHENTIC_N_PINS][SHA_DIGEST_LENGTH]; struct sc_cplc cplc; }; -static struct sc_atr_table authentic_known_atrs[] = { +static const struct sc_atr_table authentic_known_atrs[] = { { "3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:00:70:0A:90:00:8B", NULL, "Oberthur AuthentIC 3.2.2", SC_CARD_TYPE_OBERTHUR_AUTHENTIC_3_2, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } @@ -93,11 +86,10 @@ unsigned char aid_AuthentIC_3_2[] = { static int authentic_select_file(struct sc_card *card, const struct sc_path *path, struct sc_file **file_out); static int authentic_process_fci(struct sc_card *card, struct sc_file *file, const unsigned char *buf, size_t buflen); static int authentic_get_serialnr(struct sc_card *card, struct sc_serial_number *serial); -static int authentic_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data); -static int authentic_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, int *tries_left); +static int authentic_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data, struct sc_acl_entry *acls); +static int authentic_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd); static int authentic_select_mf(struct sc_card *card, struct sc_file **file_out); static int authentic_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr); -static void authentic_debug_select_file(struct sc_card *card, const struct sc_path *path); #ifdef ENABLE_SM static int authentic_sm_open(struct sc_card *card); @@ -110,7 +102,8 @@ authentic_update_blob(struct sc_context *ctx, unsigned tag, unsigned char *data, unsigned char **blob, size_t *blob_size) { unsigned char *pp = NULL; - int offs = 0, sz; + int offs = 0; + size_t sz; if (data_len == 0) return SC_SUCCESS; @@ -152,9 +145,9 @@ authentic_update_blob(struct sc_context *ctx, unsigned tag, unsigned char *data, static int -authentic_parse_size(unsigned char *in, size_t *out) +authentic_parse_size(unsigned char *in, size_t in_len, size_t *out) { - if (!in || !out) + if (!in || !out || in_len < 1) return SC_ERROR_INVALID_ARGUMENTS; if (*in < 0x80) { @@ -162,10 +155,14 @@ authentic_parse_size(unsigned char *in, size_t *out) return 1; } else if (*in == 0x81) { + if (in_len < 2) + return SC_ERROR_INVALID_DATA; *out = *(in + 1); return 2; } else if (*in == 0x82) { + if (in_len < 3) + return SC_ERROR_INVALID_DATA; *out = *(in + 1) * 0x100 + *(in + 2); return 3; } @@ -179,6 +176,7 @@ authentic_get_tagged_data(struct sc_context *ctx, unsigned char *in, size_t in_l unsigned in_tag, unsigned char **out, size_t *out_len) { size_t size_len, tag_len, offs, size; + int rv; unsigned tag; if (!out || !out_len) @@ -186,6 +184,8 @@ authentic_get_tagged_data(struct sc_context *ctx, unsigned char *in, size_t in_l for (offs = 0; offs < in_len; ) { if ((*(in + offs) == 0x7F) || (*(in + offs) == 0x5F)) { + if (offs + 1 >= in_len) + LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "parse error: invalid data"); tag = *(in + offs) * 0x100 + *(in + offs + 1); tag_len = 2; } @@ -194,10 +194,17 @@ authentic_get_tagged_data(struct sc_context *ctx, unsigned char *in, size_t in_l tag_len = 1; } - size_len = authentic_parse_size(in + offs + tag_len, &size); - LOG_TEST_RET(ctx, size_len, "parse error: invalid size data"); + if (offs + tag_len >= in_len) + LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "parse error: invalid data"); + + rv = authentic_parse_size(in + offs + tag_len, in_len - (offs + tag_len), &size); + LOG_TEST_RET(ctx, rv, "parse error: invalid size data"); + size_len = rv; if (tag == in_tag) { + if (in_len - (offs + tag_len + size_len) < size) + LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "parse error: invalid data"); + *out = in + offs + tag_len + size_len; *out_len = size; @@ -275,7 +282,7 @@ authentic_decode_pubkey_rsa(struct sc_context *ctx, unsigned char *blob, size_t static int authentic_parse_credential_data(struct sc_context *ctx, struct sc_pin_cmd_data *pin_cmd, - unsigned char *blob, size_t blob_len) + struct sc_acl_entry *acls, unsigned char *blob, size_t blob_len) { unsigned char *data; size_t data_len; @@ -291,38 +298,41 @@ authentic_parse_credential_data(struct sc_context *ctx, struct sc_pin_cmd_data * rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_DOCP_MECH, &data, &data_len); LOG_TEST_RET(ctx, rv, "cannot get PIN type"); - if (*data == 0) + if (data_len > 0 && *data == 0) pin_cmd->pin_type = SC_AC_CHV; - else if (*data >= 2 && *data <= 7) + else if (data_len > 0 && *data >= 2 && *data <= 7) pin_cmd->pin_type = SC_AC_AUT; else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "unsupported Credential type"); - rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_DOCP_ACLS, &data, &data_len); - LOG_TEST_RET(ctx, rv, "failed to get ACLs"); - sc_log(ctx, "data_len:%"SC_FORMAT_LEN_SIZE_T"u", data_len); - if (data_len == 10) { - for (ii=0; ii<5; ii++) { - unsigned char acl = *(data + ii*2); - unsigned char cred_id = *(data + ii*2 + 1); - unsigned sc = acl * 0x100 + cred_id; - - sc_log(ctx, "%i: SC:%X", ii, sc); - if (!sc) - continue; - - if (acl & AUTHENTIC_AC_SM_MASK) { - pin_cmd->pin1.acls[ii].method = SC_AC_SCB; - pin_cmd->pin1.acls[ii].key_ref = sc; - } - else if (acl!=0xFF && cred_id) { - sc_log(ctx, "%i: ACL(method:SC_AC_CHV,id:%i)", ii, cred_id); - pin_cmd->pin1.acls[ii].method = SC_AC_CHV; - pin_cmd->pin1.acls[ii].key_ref = cred_id; - } - else { - pin_cmd->pin1.acls[ii].method = SC_AC_NEVER; - pin_cmd->pin1.acls[ii].key_ref = 0; + /* Parse optional ACLs when requested */ + if (acls) { + rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_DOCP_ACLS, &data, &data_len); + LOG_TEST_RET(ctx, rv, "failed to get ACLs"); + sc_log(ctx, "data_len:%"SC_FORMAT_LEN_SIZE_T"u", data_len); + if (data_len == 10) { + for (ii=0; ii<5; ii++) { + unsigned char acl = *(data + ii*2); + unsigned char cred_id = *(data + ii*2 + 1); + unsigned sc = acl * 0x100 + cred_id; + + sc_log(ctx, "%i: SC:%X", ii, sc); + if (!sc) + continue; + + if (acl & AUTHENTIC_AC_SM_MASK) { + acls[ii].method = SC_AC_SCB; + acls[ii].key_ref = sc; + } + else if (acl!=0xFF && cred_id) { + sc_log(ctx, "%i: ACL(method:SC_AC_CHV,id:%i)", ii, cred_id); + acls[ii].method = SC_AC_CHV; + acls[ii].key_ref = cred_id; + } + else { + acls[ii].method = SC_AC_NEVER; + acls[ii].key_ref = 0; + } } } } @@ -375,45 +385,13 @@ authentic_get_cplc(struct sc_card *card) return SC_SUCCESS; } - -static int -authentic_select_aid(struct sc_card *card, unsigned char *aid, size_t aid_len, - unsigned char *out, size_t *out_len) -{ - struct sc_apdu apdu; - unsigned char apdu_resp[SC_MAX_APDU_BUFFER_SIZE]; - int rv; - - /* Select Card Manager (to deselect previously selected application) */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x00); - apdu.lc = aid_len; - apdu.data = aid; - apdu.datalen = aid_len; - apdu.resplen = sizeof(apdu_resp); - apdu.resp = apdu_resp; - - rv = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); - rv = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(card->ctx, rv, "Cannot select AID"); - - if (out && out_len) { - if (*out_len < apdu.resplen) - LOG_TEST_RET(card->ctx, SC_ERROR_BUFFER_TOO_SMALL, "Cannot select AID"); - memcpy(out, apdu.resp, apdu.resplen); - } - - return SC_SUCCESS; -} - - static int authentic_match_card(struct sc_card *card) { struct sc_context *ctx = card->ctx; int i; - sc_log(ctx, "try to match card with ATR %s", sc_dump_hex(card->atr.value, card->atr.len)); + sc_log_hex(ctx, "try to match card with ATR", card->atr.value, card->atr.len); i = _sc_match_atr(card, authentic_known_atrs, &card->type); if (i < 0) { sc_log(ctx, "card not matched"); @@ -436,9 +414,6 @@ authentic_init_oberthur_authentic_3_2(struct sc_card *card) flags = AUTHENTIC_CARD_DEFAULT_FLAGS; - _sc_card_add_rsa_alg(card, 1024, flags, 0x10001); - _sc_card_add_rsa_alg(card, 2048, flags, 0x10001); - card->caps = SC_CARD_CAP_RNG; card->caps |= SC_CARD_CAP_APDU_EXT; card->caps |= SC_CARD_CAP_USE_FCI_AC; @@ -449,12 +424,15 @@ authentic_init_oberthur_authentic_3_2(struct sc_card *card) card->sm_ctx.ops.free_sm_apdu = authentic_sm_free_wrapped_apdu; #endif - rv = authentic_select_aid(card, aid_AuthentIC_3_2, sizeof(aid_AuthentIC_3_2), NULL, NULL); + rv = iso7816_select_aid(card, aid_AuthentIC_3_2, sizeof(aid_AuthentIC_3_2), NULL, NULL); LOG_TEST_RET(ctx, rv, "AuthentIC application select error"); rv = authentic_select_mf(card, NULL); LOG_TEST_RET(ctx, rv, "MF selection error"); + _sc_card_add_rsa_alg(card, 1024, flags, 0x10001); + _sc_card_add_rsa_alg(card, 2048, flags, 0x10001); + LOG_FUNC_RETURN(ctx, rv); } @@ -463,7 +441,7 @@ static int authentic_init(struct sc_card *card) { struct sc_context *ctx = card->ctx; - int ii, rv = SC_ERROR_NO_CARD_SUPPORT; + int ii, rv = SC_ERROR_INVALID_CARD; LOG_FUNC_CALLED(ctx); for(ii=0;authentic_known_atrs[ii].atr;ii++) { @@ -475,23 +453,30 @@ authentic_init(struct sc_card *card) } if (!authentic_known_atrs[ii].atr) - LOG_FUNC_RETURN(ctx, SC_ERROR_NO_CARD_SUPPORT); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_CARD); card->cla = 0x00; - card->drv_data = (struct authentic_private_data *) calloc(sizeof(struct authentic_private_data), 1); + card->drv_data = (struct authentic_private_data *) calloc(1, sizeof(struct authentic_private_data)); if (!card->drv_data) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); if (card->type == SC_CARD_TYPE_OBERTHUR_AUTHENTIC_3_2) rv = authentic_init_oberthur_authentic_3_2(card); - if (!rv) + if (rv != SC_SUCCESS) rv = authentic_get_serialnr(card, NULL); + if (rv != SC_SUCCESS) + rv = SC_ERROR_INVALID_CARD; + + /* Free private data on error */ + if (rv != SC_SUCCESS) { + free(card->drv_data); + card->drv_data = NULL; + } LOG_FUNC_RETURN(ctx, rv); } - static int authentic_erase_binary(struct sc_card *card, unsigned int offs, size_t count, unsigned long flags) { @@ -503,19 +488,14 @@ authentic_erase_binary(struct sc_card *card, unsigned int offs, size_t count, un if (!count) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "'ERASE BINARY' with ZERO count not supported"); - if (card->cache.valid && card->cache.current_ef) - sc_log(ctx, "current_ef(type=%i) %s", card->cache.current_ef->path.type, - sc_print_path(&card->cache.current_ef->path)); - buf_zero = calloc(1, count); if (!buf_zero) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "cannot allocate buff 'zero'"); rv = sc_update_binary(card, offs, buf_zero, count, flags); free(buf_zero); - LOG_TEST_RET(ctx, rv, "'ERASE BINARY' failed"); - LOG_FUNC_RETURN(ctx, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, rv); } @@ -539,43 +519,16 @@ authentic_set_current_files(struct sc_card *card, struct sc_path *path, file->path = *path; rv = authentic_process_fci(card, file, resp, resplen); - LOG_TEST_RET(ctx, rv, "cannot set 'current file': FCI process error"); + if (rv != SC_SUCCESS) { + sc_file_free(file); + LOG_TEST_RET(ctx, rv, "cannot set 'current file': FCI process error"); + } break; default: LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } - if (file->type == SC_FILE_TYPE_DF) { - struct sc_path cur_df_path; - - memset(&cur_df_path, 0, sizeof(cur_df_path)); - if (card->cache.valid && card->cache.current_df) { - cur_df_path = card->cache.current_df->path; - sc_file_free(card->cache.current_df); - } - card->cache.current_df = NULL; - sc_file_dup(&card->cache.current_df, file); - - if (cur_df_path.len) { - memcpy(card->cache.current_df->path.value + cur_df_path.len, - card->cache.current_df->path.value, - card->cache.current_df->path.len); - memcpy(card->cache.current_df->path.value, cur_df_path.value, cur_df_path.len); - card->cache.current_df->path.len += cur_df_path.len; - } - - sc_file_free(card->cache.current_ef); - card->cache.current_ef = NULL; - - card->cache.valid = 1; - } - else { - sc_file_free(card->cache.current_ef); - card->cache.current_ef = NULL; - sc_file_dup(&card->cache.current_ef, file); - } - if (file_out) *file_out = file; else @@ -601,16 +554,6 @@ authentic_select_mf(struct sc_card *card, struct sc_file **file_out) sc_format_path("3F00", &mfpath); mfpath.type = SC_PATH_TYPE_PATH; - if (card->cache.valid == 1 - && card->cache.current_df - && card->cache.current_df->path.len == 2 - && !memcmp(card->cache.current_df->path.value, "\x3F\x00", 2)) { - if (file_out) - sc_file_dup(file_out, card->cache.current_df); - - LOG_FUNC_RETURN(ctx, SC_SUCCESS); - } - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0x00, 0x00); apdu.resp = rbuf; @@ -621,107 +564,12 @@ authentic_select_mf(struct sc_card *card, struct sc_file **file_out) rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "authentic_select_file() check SW failed"); - if (card->cache.valid == 1) { - sc_file_free(card->cache.current_df); - card->cache.current_df = NULL; - - sc_file_free(card->cache.current_ef); - card->cache.current_ef = NULL; - } - rv = authentic_set_current_files(card, &mfpath, apdu.resp, apdu.resplen, file_out); LOG_TEST_RET(ctx, rv, "authentic_select_file() cannot set 'current_file'"); LOG_FUNC_RETURN(ctx, rv); } - -static int -authentic_reduce_path(struct sc_card *card, struct sc_path *path) -{ - struct sc_context *ctx = card->ctx; - struct sc_path in_path, cur_path; - size_t offs; - - LOG_FUNC_CALLED(ctx); - - if (!path || path->len <= 2 || path->type == SC_PATH_TYPE_DF_NAME) - LOG_FUNC_RETURN(ctx, SC_SUCCESS); - - if (!card->cache.valid || !card->cache.current_df) - LOG_FUNC_RETURN(ctx, 0); - - in_path = *path; - cur_path = card->cache.current_df->path; - - if (!memcmp(cur_path.value, "\x3F\x00", 2) && memcmp(in_path.value, "\x3F\x00", 2)) { - memmove(in_path.value + 2, in_path.value, in_path.len); - memcpy(in_path.value, "\x3F\x00", 2); - in_path.len += 2; - } - - for (offs=0; offs < in_path.len && offs < cur_path.len; offs += 2) { - if (cur_path.value[offs] != in_path.value[offs]) - break; - if (cur_path.value[offs + 1] != in_path.value[offs + 1]) - break; - } - - memmove(in_path.value, in_path.value + offs, sizeof(in_path.value) - offs); - in_path.len -= offs; - *path = in_path; - - LOG_FUNC_RETURN(ctx, offs); -} - - -static void -authentic_debug_select_file(struct sc_card *card, const struct sc_path *path) -{ - struct sc_context *ctx = card->ctx; - struct sc_card_cache *cache = &card->cache; - - if (path) - sc_log(ctx, "try to select path(type:%i) %s", - path->type, sc_print_path(path)); - - if (!cache->valid) - return; - - if (cache->current_df) - sc_log(ctx, "current_df(type=%i) %s", - cache->current_df->path.type, sc_print_path(&cache->current_df->path)); - else - sc_log(ctx, "current_df empty"); - - if (cache->current_ef) - sc_log(ctx, "current_ef(type=%i) %s", - cache->current_ef->path.type, sc_print_path(&cache->current_ef->path)); - else - sc_log(ctx, "current_ef empty"); -} - - -static int -authentic_is_selected(struct sc_card *card, const struct sc_path *path, struct sc_file **file_out) -{ - if (!path->len) { - if (file_out && card->cache.valid && card->cache.current_df) - sc_file_dup(file_out, card->cache.current_df); - return SC_SUCCESS; - } - else if (path->len == 2 && card->cache.valid && card->cache.current_ef) { - if (!memcmp(card->cache.current_ef->path.value, path->value, 2)) { - if (file_out) - sc_file_dup(file_out, card->cache.current_ef); - return SC_SUCCESS; - } - } - - return SC_ERROR_FILE_NOT_FOUND; -} - - static int authentic_select_file(struct sc_card *card, const struct sc_path *path, struct sc_file **file_out) @@ -730,34 +578,16 @@ authentic_select_file(struct sc_card *card, const struct sc_path *path, struct sc_apdu apdu; struct sc_path lpath; unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; - int pathlen, rv; + size_t pathlen; + int rv; LOG_FUNC_CALLED(ctx); - authentic_debug_select_file(card, path); memcpy(&lpath, path, sizeof(struct sc_path)); - rv = authentic_reduce_path(card, &lpath); - LOG_TEST_RET(ctx, rv, "reduce path error"); - - if (lpath.len >= 2 && lpath.value[0] == 0x3F && lpath.value[1] == 0x00) { - rv = authentic_select_mf(card, file_out); - LOG_TEST_RET(ctx, rv, "cannot select MF"); - - memmove(&lpath.value[0], &lpath.value[2], lpath.len - 2); - lpath.len -= 2; - - if (!lpath.len) - LOG_FUNC_RETURN(ctx, SC_SUCCESS); - } - if (lpath.type == SC_PATH_TYPE_PATH && (lpath.len == 2)) lpath.type = SC_PATH_TYPE_FILE_ID; - rv = authentic_is_selected(card, &lpath, file_out); - if (!rv) - LOG_FUNC_RETURN(ctx, SC_SUCCESS); - pathlen = lpath.len; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00); @@ -807,99 +637,49 @@ authentic_select_file(struct sc_card *card, const struct sc_path *path, LOG_FUNC_RETURN(ctx, SC_SUCCESS); } - -static int -authentic_apdus_allocate(struct sc_apdu **head, struct sc_apdu **new) -{ - struct sc_apdu *allocated_apdu = NULL, *tmp_apdu = NULL; - - if (!head) - return SC_ERROR_INVALID_ARGUMENTS; - - allocated_apdu = calloc(1, sizeof(struct sc_apdu)); - if (!allocated_apdu) - return SC_ERROR_OUT_OF_MEMORY; - - if (*head == NULL) - *head = allocated_apdu; - - if (new) - *new = allocated_apdu; - - tmp_apdu = *head; - while(tmp_apdu->next) - tmp_apdu = tmp_apdu->next; - - tmp_apdu->next = allocated_apdu; - - return 0; -} - - -static void -authentic_apdus_free(struct sc_apdu *apdu) -{ - while(apdu) { - struct sc_apdu *tmp_apdu = apdu->next; - free(apdu); - apdu = tmp_apdu; - } -} - - static int authentic_read_binary(struct sc_card *card, unsigned int idx, - unsigned char *buf, size_t count, unsigned long flags) + unsigned char *buf, size_t count, unsigned long *flags) { struct sc_context *ctx = card->ctx; - struct sc_apdu *apdus = NULL, *cur_apdu = NULL; - size_t sz, rest; - int rv; + struct sc_apdu apdu; + size_t sz, rest, ret_count = 0; + int rv = SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(ctx); sc_log(ctx, "offs:%i,count:%"SC_FORMAT_LEN_SIZE_T"u,max_recv_size:%"SC_FORMAT_LEN_SIZE_T"u", idx, count, card->max_recv_size); - /* Data size more then 256 bytes can happen when card reader is - * configurated with max_send/recv_size more then 255/256 bytes - * (for ex. 'remote-access' reader) . - * In this case create chained 'read-binary' APDUs. - */ - sc_log(ctx, "reader flags 0x%lX", card->reader->flags); - if (count > 256 && !(card->reader->flags & SC_READER_HAS_WAITING_AREA)) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid size of the data to read"); - rest = count; while(rest) { - if (authentic_apdus_allocate(&apdus, &cur_apdu)) - LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "cannot allocate APDU"); - sz = rest > 256 ? 256 : rest; - sc_format_apdu(card, cur_apdu, SC_APDU_CASE_2_SHORT, 0xB0, (idx >> 8) & 0x7F, idx & 0xFF); - cur_apdu->le = sz; - cur_apdu->resplen = count; - cur_apdu->resp = buf; + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0, (idx >> 8) & 0x7F, idx & 0xFF); + apdu.le = sz; + apdu.resplen = sz; + apdu.resp = (buf + ret_count); + + rv = sc_transmit_apdu(card, &apdu); + if(!rv) + ret_count += apdu.resplen; + else + break; idx += sz; rest -= sz; } - if (!apdus) { + if (rv) { LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "authentic_read_binary() failed"); - LOG_FUNC_RETURN(ctx, count); + LOG_FUNC_RETURN(ctx, (int)count); } - rv = sc_transmit_apdu(card, apdus); - if (!rv) - rv = sc_check_sw(card, apdus->sw1, apdus->sw2); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); if (!rv) - count = apdus->resplen; - - authentic_apdus_free(apdus); + count = ret_count; LOG_TEST_RET(ctx, rv, "authentic_read_binary() failed"); - LOG_FUNC_RETURN(ctx, count); + LOG_FUNC_RETURN(ctx, (int)count); } @@ -908,49 +688,41 @@ authentic_write_binary(struct sc_card *card, unsigned int idx, const unsigned char *buf, size_t count, unsigned long flags) { struct sc_context *ctx = card->ctx; - struct sc_apdu *apdus = NULL, *cur_apdu = NULL; + struct sc_apdu apdu; size_t sz, rest; - int rv; + int rv = SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(ctx); sc_log(ctx, "offs:%i,count:%"SC_FORMAT_LEN_SIZE_T"u,max_send_size:%"SC_FORMAT_LEN_SIZE_T"u", idx, count, card->max_send_size); - /* see comments for authentic_read_binary() */ - sc_log(ctx, "reader flags 0x%lX", card->reader->flags); - if (count > 255 && !(card->reader->flags & SC_READER_HAS_WAITING_AREA)) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid size of the data to read"); - rest = count; while(rest) { - if (authentic_apdus_allocate(&apdus, &cur_apdu)) - LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "cannot allocate APDU"); - sz = rest > 255 ? 255 : rest; - sc_format_apdu(card, cur_apdu, SC_APDU_CASE_3_SHORT, 0xD0, (idx >> 8) & 0x7F, idx & 0xFF); - cur_apdu->lc = sz; - cur_apdu->datalen = sz; - cur_apdu->data = buf + count - rest; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD0, (idx >> 8) & 0x7F, idx & 0xFF); + apdu.lc = sz; + apdu.datalen = sz; + apdu.data = buf + count - rest; + + rv = sc_transmit_apdu(card, &apdu); + if(rv) + break; idx += sz; rest -= sz; } - if (!apdus) + if (rv) { LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "authentic_write_binary() failed"); - LOG_FUNC_RETURN(ctx, count); + LOG_FUNC_RETURN(ctx, (int)count); } - rv = sc_transmit_apdu(card, apdus); - if (!rv) - rv = sc_check_sw(card, apdus->sw1, apdus->sw2); - - authentic_apdus_free(apdus); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "authentic_write_binary() failed"); - LOG_FUNC_RETURN(ctx, count); + LOG_FUNC_RETURN(ctx, (int)count); } @@ -959,49 +731,41 @@ authentic_update_binary(struct sc_card *card, unsigned int idx, const unsigned char *buf, size_t count, unsigned long flags) { struct sc_context *ctx = card->ctx; - struct sc_apdu *apdus = NULL, *cur_apdu = NULL; + struct sc_apdu apdu; size_t sz, rest; - int rv; + int rv = SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(ctx); sc_log(ctx, "offs:%i,count:%"SC_FORMAT_LEN_SIZE_T"u,max_send_size:%"SC_FORMAT_LEN_SIZE_T"u", idx, count, card->max_send_size); - /* see comments for authentic_read_binary() */ - sc_log(ctx, "reader flags 0x%lX", card->reader->flags); - if (count > 255 && !(card->reader->flags & SC_READER_HAS_WAITING_AREA)) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid size of the data to read"); - rest = count; while(rest) { - if (authentic_apdus_allocate(&apdus, &cur_apdu)) - LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "cannot allocate APDU"); - sz = rest > 255 ? 255 : rest; - sc_format_apdu(card, cur_apdu, SC_APDU_CASE_3_SHORT, 0xD6, (idx >> 8) & 0x7F, idx & 0xFF); - cur_apdu->lc = sz; - cur_apdu->datalen = sz; - cur_apdu->data = buf + count - rest; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, (idx >> 8) & 0x7F, idx & 0xFF); + apdu.lc = sz; + apdu.datalen = sz; + apdu.data = buf + count - rest; + + rv = sc_transmit_apdu(card, &apdu); + if(rv) + break; idx += sz; rest -= sz; } - if (!apdus) + if (rv) { LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "authentic_update_binary() failed"); - LOG_FUNC_RETURN(ctx, count); + LOG_FUNC_RETURN(ctx, (int)count); } - rv = sc_transmit_apdu(card, apdus); - if (!rv) - rv = sc_check_sw(card, apdus->sw1, apdus->sw2); - - authentic_apdus_free(apdus); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "authentic_update_binary() failed"); - LOG_FUNC_RETURN(ctx, count); + LOG_FUNC_RETURN(ctx, (int)count); } @@ -1041,9 +805,7 @@ authentic_process_fci(struct sc_card *card, struct sc_file *file, LOG_TEST_RET(ctx, rv, "ISO parse FCI failed"); if (!file->sec_attr_len) { - sc_log(ctx, - "ACLs not found in data(%"SC_FORMAT_LEN_SIZE_T"u) %s", - buflen, sc_dump_hex(buf, buflen)); + sc_log_hex(ctx, "ACLs not found in data", buf, buflen); sc_log(ctx, "Path:%s; Type:%X; PathType:%X", sc_print_path(&file->path), file->type, file->path.type); if (file->path.type == SC_PATH_TYPE_DF_NAME || file->type == SC_FILE_TYPE_DF) { file->type = SC_FILE_TYPE_DF; @@ -1053,9 +815,8 @@ authentic_process_fci(struct sc_card *card, struct sc_file *file, } } - sc_log(ctx, "ACL data(%"SC_FORMAT_LEN_SIZE_T"u):%s", file->sec_attr_len, - sc_dump_hex(file->sec_attr, file->sec_attr_len)); - for (ii = 0; ii < file->sec_attr_len / 2; ii++) { + sc_log_hex(ctx, "ACL data", file->sec_attr, file->sec_attr_len); + for (ii = 0; ii < file->sec_attr_len / 2 && ii < sizeof ops_DF; ii++) { unsigned char op = file->type == SC_FILE_TYPE_DF ? ops_DF[ii] : ops_EF[ii]; unsigned char acl = *(file->sec_attr + ii*2); unsigned char cred_id = *(file->sec_attr + ii*2 + 1); @@ -1142,7 +903,7 @@ authentic_fcp_encode(struct sc_card *card, struct sc_file *file, unsigned char * memcpy(out, buf, offs); } - LOG_FUNC_RETURN(ctx, offs); + LOG_FUNC_RETURN(ctx, (int)offs); } @@ -1161,22 +922,13 @@ authentic_create_file(struct sc_card *card, struct sc_file *file) if (file->type != SC_FILE_TYPE_WORKING_EF) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Creation of the file with of this type is not supported"); - authentic_debug_select_file(card, &file->path); - - sbuf_len = authentic_fcp_encode(card, file, sbuf + 2, sizeof(sbuf)-2); - LOG_TEST_RET(ctx, sbuf_len, "FCP encode error"); + rv = authentic_fcp_encode(card, file, sbuf + 2, sizeof(sbuf)-2); + LOG_TEST_RET(ctx, rv, "FCP encode error"); + sbuf_len = rv; sbuf[0] = ISO7816_TAG_FCP; sbuf[1] = sbuf_len; - if (card->cache.valid && card->cache.current_df) { - const struct sc_acl_entry *entry = sc_file_get_acl_entry(card->cache.current_df, SC_AC_OP_CREATE); - - sc_log(ctx, "CREATE method/reference %X/%X", entry->method, entry->key_ref); - if (entry->method == SC_AC_SCB) - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet supported"); - } - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0, 0); apdu.data = sbuf; apdu.datalen = sbuf_len + 2; @@ -1224,17 +976,12 @@ authentic_delete_file(struct sc_card *card, const struct sc_path *path) } LOG_TEST_RET(ctx, rv, "Delete file failed"); - if (card->cache.valid) { - sc_file_free(card->cache.current_ef); - card->cache.current_ef = NULL; - } - LOG_FUNC_RETURN(ctx, rv); } static int -authentic_chv_verify_pinpad(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, int *tries_left) +authentic_chv_verify_pinpad(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd) { struct sc_context *ctx = card->ctx; unsigned char buffer[0x100]; @@ -1244,7 +991,7 @@ authentic_chv_verify_pinpad(struct sc_card *card, struct sc_pin_cmd_data *pin_cm LOG_FUNC_CALLED(ctx); sc_log(ctx, "Verify PIN(ref:%i) with pin-pad", pin_cmd->pin_reference); - rv = authentic_pin_is_verified(card, pin_cmd, tries_left); + rv = authentic_pin_is_verified(card, pin_cmd); if (!rv) LOG_FUNC_RETURN(ctx, rv); @@ -1262,36 +1009,43 @@ authentic_chv_verify_pinpad(struct sc_card *card, struct sc_pin_cmd_data *pin_cm pin_cmd->cmd = SC_PIN_CMD_VERIFY; pin_cmd->flags |= SC_PIN_CMD_USE_PINPAD; - rv = iso_ops->pin_cmd(card, pin_cmd, tries_left); + rv = iso_ops->pin_cmd(card, pin_cmd); LOG_FUNC_RETURN(ctx, rv); } static int -authentic_chv_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, - int *tries_left) +authentic_chv_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; struct sc_pin_cmd_pin *pin1 = &pin_cmd->pin1; + unsigned char pin_buff[SC_MAX_APDU_BUFFER_SIZE]; + size_t pin_len; int rv; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "CHV PIN reference %i, pin1(%p,len:%i)", pin_cmd->pin_reference, pin1->data, pin1->len); + sc_log(ctx, "CHV PIN reference %i, pin1(%p,len:%zu)", pin_cmd->pin_reference, pin1->data, pin1->len); if (pin1->data && !pin1->len) { sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0, pin_cmd->pin_reference); } else if (pin1->data && pin1->len) { - unsigned char pin_buff[SC_MAX_APDU_BUFFER_SIZE]; - size_t pin_len; - + if (pin1->len > sizeof(pin_buff)) { + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_PIN_LENGTH); + } memcpy(pin_buff, pin1->data, pin1->len); pin_len = pin1->len; if (pin1->pad_length && pin_cmd->flags & SC_PIN_CMD_NEED_PADDING) { - memset(pin_buff + pin1->len, pin1->pad_char, pin1->pad_length - pin1->len); + if (pin1->len > pin1->pad_length || pin1->pad_length > sizeof(pin_buff)) { + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_PIN_LENGTH); + } + if (pin1->len < sizeof(pin_buff)) { + memset(pin_buff + pin1->len, pin1->pad_char, + pin1->pad_length - pin1->len); + } pin_len = pin1->pad_length; } @@ -1301,7 +1055,7 @@ authentic_chv_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, apdu.lc = pin_len; } else if ((card->reader->capabilities & SC_READER_CAP_PIN_PAD) && !pin1->data && !pin1->len) { - rv = authentic_chv_verify_pinpad(card, pin_cmd, tries_left); + rv = authentic_chv_verify_pinpad(card, pin_cmd); sc_log(ctx, "authentic_chv_verify() authentic_chv_verify_pinpad returned %i", rv); LOG_FUNC_RETURN(ctx, rv); } @@ -1314,8 +1068,6 @@ authentic_chv_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, if (apdu.sw1 == 0x63 && (apdu.sw2 & 0xF0) == 0xC0) { pin1->tries_left = apdu.sw2 & 0x0F; - if (tries_left) - *tries_left = apdu.sw2 & 0x0F; } rv = sc_check_sw(card, apdu.sw1, apdu.sw2); @@ -1325,8 +1077,7 @@ authentic_chv_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, static int -authentic_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd_data, - int *tries_left) +authentic_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd_data) { struct sc_context *ctx = card->ctx; struct sc_pin_cmd_data pin_cmd; @@ -1341,7 +1092,7 @@ authentic_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd_ pin_cmd.pin1.data = (unsigned char *)""; pin_cmd.pin1.len = 0; - rv = authentic_chv_verify(card, &pin_cmd, tries_left); + rv = authentic_chv_verify(card, &pin_cmd); LOG_FUNC_RETURN(ctx, rv); } @@ -1356,12 +1107,16 @@ authentic_pin_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd) int rv; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "PIN(type:%X,reference:%X,data:%p,length:%i)", + sc_log(ctx, "PIN(type:%X,reference:%X,data:%p,length:%zu)", pin_cmd->pin_type, pin_cmd->pin_reference, pin_cmd->pin1.data, pin_cmd->pin1.len); + if (pin_cmd->pin_reference < 0 || pin_cmd->pin_reference >= AUTHENTIC_N_PINS) { + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "PIN reference out of bounds"); + } + if (pin_cmd->pin1.data && !pin_cmd->pin1.len) { pin_cmd->pin1.tries_left = -1; - rv = authentic_pin_is_verified(card, pin_cmd, &pin_cmd->pin1.tries_left); + rv = authentic_pin_is_verified(card, pin_cmd); LOG_FUNC_RETURN(ctx, rv); } @@ -1377,14 +1132,14 @@ authentic_pin_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd) memset(prv_data->pins_sha1[pin_cmd->pin_reference], 0, sizeof(prv_data->pins_sha1[0])); - rv = authentic_pin_get_policy(card, pin_cmd); + rv = authentic_pin_get_policy(card, pin_cmd, NULL); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); - if (pin_cmd->pin1.len > (int)pin_cmd->pin1.max_length) + if (pin_cmd->pin1.len > pin_cmd->pin1.max_length) LOG_TEST_RET(ctx, SC_ERROR_INVALID_PIN_LENGTH, "PIN policy check failed"); pin_cmd->pin1.tries_left = -1; - rv = authentic_chv_verify(card, pin_cmd, &pin_cmd->pin1.tries_left); + rv = authentic_chv_verify(card, pin_cmd); LOG_TEST_RET(ctx, rv, "PIN CHV verification error"); memcpy(prv_data->pins_sha1[pin_cmd->pin_reference], pin_sha1, SHA_DIGEST_LENGTH); @@ -1414,7 +1169,7 @@ authentic_pin_change_pinpad(struct sc_card *card, unsigned reference, int *tries pin_cmd.cmd = SC_PIN_CMD_CHANGE; pin_cmd.flags |= SC_PIN_CMD_USE_PINPAD | SC_PIN_CMD_NEED_PADDING; - rv = authentic_pin_get_policy(card, &pin_cmd); + rv = authentic_pin_get_policy(card, &pin_cmd, NULL); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); memset(pin1_data, pin_cmd.pin1.pad_char, sizeof(pin1_data)); @@ -1436,14 +1191,14 @@ authentic_pin_change_pinpad(struct sc_card *card, unsigned reference, int *tries pin_cmd.pin2.max_length, pin_cmd.pin2.min_length, pin_cmd.pin2.pad_length); - rv = iso_ops->pin_cmd(card, &pin_cmd, tries_left); + rv = iso_ops->pin_cmd(card, &pin_cmd); LOG_FUNC_RETURN(ctx, rv); } static int -authentic_pin_change(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +authentic_pin_change(struct sc_card *card, struct sc_pin_cmd_data *data) { struct sc_context *ctx = card->ctx; struct authentic_private_data *prv_data = (struct authentic_private_data *) card->drv_data; @@ -1452,30 +1207,41 @@ authentic_pin_change(struct sc_card *card, struct sc_pin_cmd_data *data, int *tr size_t offs; int rv; - rv = authentic_pin_get_policy(card, data); + rv = authentic_pin_get_policy(card, data, NULL); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); + if (data->pin_reference < 0 || data->pin_reference >= AUTHENTIC_N_PINS) { + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "PIN reference out of bounds"); + } + memset(prv_data->pins_sha1[data->pin_reference], 0, sizeof(prv_data->pins_sha1[0])); if (!data->pin1.data && !data->pin1.len && !data->pin2.data && !data->pin2.len) { if (!(card->reader->capabilities & SC_READER_CAP_PIN_PAD)) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "PIN pad not supported"); - rv = authentic_pin_change_pinpad(card, data->pin_reference, tries_left); + rv = authentic_pin_change_pinpad(card, data->pin_reference, &data->pin1.tries_left); sc_log(ctx, "authentic_pin_cmd(SC_PIN_CMD_CHANGE) chv_change_pinpad returned %i", rv); LOG_FUNC_RETURN(ctx, rv); } - if (card->max_send_size && (data->pin1.len + data->pin2.len > (int)card->max_send_size)) + if (card->max_send_size && (data->pin1.len + data->pin2.len > card->max_send_size)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_PIN_LENGTH, "APDU transmit failed"); memset(pin_data, data->pin1.pad_char, sizeof(pin_data)); offs = 0; if (data->pin1.data && data->pin1.len) { + if (data->pin1.len > sizeof(pin_data)) { + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "PIN length exceeds buffer"); + } memcpy(pin_data, data->pin1.data, data->pin1.len); offs += data->pin1.pad_length; } - if (data->pin2.data && data->pin2.len) + if (data->pin2.data && data->pin2.len) { + if (data->pin2.len + offs > sizeof(pin_data)) { + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "PIN2 length exceeds buffer"); + } memcpy(pin_data + offs, data->pin2.data, data->pin2.len); + } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, offs ? 0x00 : 0x01, data->pin_reference); apdu.data = pin_data; @@ -1512,7 +1278,7 @@ authentic_chv_set_pinpad(struct sc_card *card, unsigned char reference) pin_cmd.cmd = SC_PIN_CMD_UNBLOCK; pin_cmd.flags |= SC_PIN_CMD_USE_PINPAD | SC_PIN_CMD_NEED_PADDING; - rv = authentic_pin_get_policy(card, &pin_cmd); + rv = authentic_pin_get_policy(card, &pin_cmd, NULL); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); memset(pin_data, pin_cmd.pin1.pad_char, sizeof(pin_data)); @@ -1528,14 +1294,14 @@ authentic_chv_set_pinpad(struct sc_card *card, unsigned char reference) "PIN2 max/min/pad %"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u", pin_cmd.pin2.max_length, pin_cmd.pin2.min_length, pin_cmd.pin2.pad_length); - rv = iso_ops->pin_cmd(card, &pin_cmd, NULL); + rv = iso_ops->pin_cmd(card, &pin_cmd); LOG_FUNC_RETURN(ctx, rv); } static int -authentic_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data) +authentic_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data, struct sc_acl_entry *acls) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; @@ -1564,7 +1330,7 @@ authentic_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data) data->pin1.tries_left = -1; - rv = authentic_parse_credential_data(ctx, data, apdu.resp, apdu.resplen); + rv = authentic_parse_credential_data(ctx, data, acls, apdu.resp, apdu.resplen); LOG_TEST_RET(ctx, rv, "Cannot parse credential data"); data->pin1.encoding = SC_PIN_ENCODING_ASCII; @@ -1586,17 +1352,22 @@ authentic_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data) static int -authentic_pin_reset(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +authentic_pin_reset(struct sc_card *card, struct sc_pin_cmd_data *data) { struct sc_context *ctx = card->ctx; struct authentic_private_data *prv_data = (struct authentic_private_data *) card->drv_data; struct sc_pin_cmd_data pin_cmd, puk_cmd; + struct sc_acl_entry acls[SC_MAX_SDO_ACLS]; struct sc_apdu apdu; unsigned reference; int rv, ii; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "reset PIN (ref:%i,lengths %i/%i)", data->pin_reference, data->pin1.len, data->pin2.len); + sc_log(ctx, "reset PIN (ref:%i,lengths %zu/%zu)", data->pin_reference, data->pin1.len, data->pin2.len); + + if (data->pin_reference < 0 || data->pin_reference >= AUTHENTIC_N_PINS) { + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "PIN reference out of bounds"); + } memset(prv_data->pins_sha1[data->pin_reference], 0, sizeof(prv_data->pins_sha1[0])); @@ -1605,17 +1376,18 @@ authentic_pin_reset(struct sc_card *card, struct sc_pin_cmd_data *data, int *tri pin_cmd.pin_type = data->pin_type; pin_cmd.pin1.tries_left = -1; - rv = authentic_pin_get_policy(card, &pin_cmd); + memset(&acls, 0, sizeof(acls)); + rv = authentic_pin_get_policy(card, &pin_cmd, acls); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); - if (pin_cmd.pin1.acls[AUTHENTIC_ACL_NUM_PIN_RESET].method == SC_AC_CHV) { + if (acls[AUTHENTIC_ACL_NUM_PIN_RESET].method == SC_AC_CHV) { for (ii=0;ii<8;ii++) { unsigned char mask = 0x01 << ii; - if (pin_cmd.pin1.acls[AUTHENTIC_ACL_NUM_PIN_RESET].key_ref & mask) { + if (acls[AUTHENTIC_ACL_NUM_PIN_RESET].key_ref & mask) { memset(&puk_cmd, 0, sizeof(puk_cmd)); puk_cmd.pin_reference = ii + 1; - rv = authentic_pin_get_policy(card, &puk_cmd); + rv = authentic_pin_get_policy(card, &puk_cmd, NULL); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); if (puk_cmd.pin_type == SC_AC_CHV) @@ -1628,9 +1400,6 @@ authentic_pin_reset(struct sc_card *card, struct sc_pin_cmd_data *data, int *tri rv = authentic_pin_verify(card, &puk_cmd); - if (tries_left && rv == SC_ERROR_PIN_CODE_INCORRECT) - *tries_left = puk_cmd.pin1.tries_left; - LOG_TEST_RET(ctx, rv, "Cannot verify PUK"); } } @@ -1670,36 +1439,33 @@ authentic_pin_reset(struct sc_card *card, struct sc_pin_cmd_data *data, int *tri static int -authentic_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +authentic_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data) { struct sc_context *ctx = card->ctx; int rv = SC_ERROR_INTERNAL; LOG_FUNC_CALLED(ctx); sc_log(ctx, "PIN-CMD:%X,PIN(type:%X,ret:%i)", data->cmd, data->pin_type, data->pin_reference); - sc_log(ctx, "PIN1(%p,len:%i,tries-left:%i)", data->pin1.data, data->pin1.len, data->pin1.tries_left); - sc_log(ctx, "PIN2(%p,len:%i)", data->pin2.data, data->pin2.len); + sc_log(ctx, "PIN1(%p,len:%zu,tries-left:%i)", data->pin1.data, data->pin1.len, data->pin1.tries_left); + sc_log(ctx, "PIN2(%p,len:%zu)", data->pin2.data, data->pin2.len); switch (data->cmd) { case SC_PIN_CMD_VERIFY: rv = authentic_pin_verify(card, data); break; case SC_PIN_CMD_CHANGE: - rv = authentic_pin_change(card, data, tries_left); + rv = authentic_pin_change(card, data); break; case SC_PIN_CMD_UNBLOCK: - rv = authentic_pin_reset(card, data, tries_left); + rv = authentic_pin_reset(card, data); break; case SC_PIN_CMD_GET_INFO: - rv = authentic_pin_get_policy(card, data); + rv = authentic_pin_get_policy(card, data, NULL); break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported PIN command"); } - if (rv == SC_ERROR_PIN_CODE_INCORRECT && tries_left) - *tries_left = data->pin1.tries_left; - LOG_FUNC_RETURN(ctx, rv); } @@ -1731,40 +1497,27 @@ authentic_get_serialnr(struct sc_card *card, struct sc_serial_number *serial) } -/* 'GET CHALLENGE' returns always 24 bytes */ static int authentic_get_challenge(struct sc_card *card, unsigned char *rnd, size_t len) { - struct sc_context *ctx = card->ctx; - struct sc_apdu apdu; + /* 'GET CHALLENGE' returns always 24 bytes */ unsigned char rbuf[0x18]; - int rv, nn; + size_t out_len; + int r; - LOG_FUNC_CALLED(ctx); - if (!rnd && len) - return SC_ERROR_INVALID_ARGUMENTS; + LOG_FUNC_CALLED(card->ctx); - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0x00, 0x00); - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - apdu.le = sizeof(rbuf); - - while (len > 0) { - rv = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); - rv = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(ctx, rv, "PIN cmd failed"); + r = iso_ops->get_challenge(card, rbuf, sizeof rbuf); + LOG_TEST_RET(card->ctx, r, "GET CHALLENGE cmd failed"); - if (apdu.resplen != sizeof(rbuf)) - return sc_check_sw(card, apdu.sw1, apdu.sw2); - - nn = len > apdu.resplen ? apdu.resplen : len; - memcpy(rnd, apdu.resp, nn); - len -= nn; - rnd += nn; + if (len < (size_t) r) { + out_len = len; + } else { + out_len = (size_t) r; } + memcpy(rnd, rbuf, out_len); - LOG_FUNC_RETURN(ctx, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, (int)out_len); } @@ -1786,22 +1539,22 @@ authentic_manage_sdo_encode_prvkey(struct sc_card *card, struct sc_pkcs15_prkey rsa = prvkey->u.rsa; /* Encode private RSA key part */ rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_P, rsa.p.data, rsa.p.len, &blob, &blob_len); - LOG_TEST_RET(ctx, rv, "SDO RSA P encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "SDO RSA P encode error"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_Q, rsa.q.data, rsa.q.len, &blob, &blob_len); - LOG_TEST_RET(ctx, rv, "SDO RSA Q encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "SDO RSA Q encode error"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_PQ, rsa.iqmp.data, rsa.iqmp.len, &blob, &blob_len); - LOG_TEST_RET(ctx, rv, "SDO RSA PQ encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "SDO RSA PQ encode error"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_DP1, rsa.dmp1.data, rsa.dmp1.len, &blob, &blob_len); - LOG_TEST_RET(ctx, rv, "SDO RSA DP1 encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "SDO RSA DP1 encode error"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_DQ1, rsa.dmq1.data, rsa.dmq1.len, &blob, &blob_len); - LOG_TEST_RET(ctx, rv, "SDO RSA DQ1 encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "SDO RSA DQ1 encode error"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE, blob, blob_len, &blob01, &blob01_len); - LOG_TEST_RET(ctx, rv, "SDO RSA Private encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "SDO RSA Private encode error"); free (blob); blob = NULL; @@ -1812,24 +1565,27 @@ authentic_manage_sdo_encode_prvkey(struct sc_card *card, struct sc_pkcs15_prkey "modulus.len:%"SC_FORMAT_LEN_SIZE_T"u blob_len:%"SC_FORMAT_LEN_SIZE_T"u", rsa.modulus.len, blob_len); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PUBLIC_MODULUS, rsa.modulus.data, rsa.modulus.len, &blob, &blob_len); - LOG_TEST_RET(ctx, rv, "SDO RSA Modulus encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "SDO RSA Modulus encode error"); sc_log(ctx, "exponent.len:%"SC_FORMAT_LEN_SIZE_T"u blob_len:%"SC_FORMAT_LEN_SIZE_T"u", rsa.exponent.len, blob_len); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PUBLIC_EXPONENT, rsa.exponent.data, rsa.exponent.len, &blob, &blob_len); - LOG_TEST_RET(ctx, rv, "SDO RSA Exponent encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "SDO RSA Exponent encode error"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PUBLIC, blob, blob_len, &blob01, &blob01_len); - LOG_TEST_RET(ctx, rv, "SDO RSA Private encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "SDO RSA Private encode error"); free (blob); + blob = NULL; + blob_len = 0; rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA, blob01, blob01_len, out, out_len); - LOG_TEST_RET(ctx, rv, "SDO RSA encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "SDO RSA encode error"); +err: free(blob01); - + free(blob); LOG_FUNC_RETURN(ctx, rv); } @@ -1852,28 +1608,28 @@ authentic_manage_sdo_encode(struct sc_card *card, struct sc_authentic_sdo *sdo, rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_MECH, &sdo->docp.mech, sizeof(sdo->docp.mech), &data, &data_len); - LOG_TEST_RET(ctx, rv, "DOCP MECH encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "DOCP MECH encode error"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_ID, &sdo->docp.id, sizeof(sdo->docp.id), &data, &data_len); - LOG_TEST_RET(ctx, rv, "DOCP ID encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "DOCP ID encode error"); if (cmd == SC_CARDCTL_AUTHENTIC_SDO_CREATE) { rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_ACLS, sdo->docp.acl_data, sdo->docp.acl_data_len, &data, &data_len); - LOG_TEST_RET(ctx, rv, "DOCP ACLs encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "DOCP ACLs encode error"); if (sdo->docp.security_parameter) { rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_SCP, &sdo->docp.security_parameter, sizeof(sdo->docp.security_parameter), &data, &data_len); - LOG_TEST_RET(ctx, rv, "DOCP ACLs encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "DOCP ACLs encode error"); } if (sdo->docp.usage_counter[0] || sdo->docp.usage_counter[1]) { rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_USAGE_COUNTER, sdo->docp.usage_counter, sizeof(sdo->docp.usage_counter), &data, &data_len); - LOG_TEST_RET(ctx, rv, "DOCP ACLs encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "DOCP ACLs encode error"); } } else if (cmd == SC_CARDCTL_AUTHENTIC_SDO_STORE) { @@ -1883,10 +1639,10 @@ authentic_manage_sdo_encode(struct sc_card *card, struct sc_authentic_sdo *sdo, || sdo->docp.mech == AUTHENTIC_MECH_CRYPTO_RSA1792 || sdo->docp.mech == AUTHENTIC_MECH_CRYPTO_RSA2048) { rv = authentic_manage_sdo_encode_prvkey(card, sdo->data.prvkey, &data, &data_len); - LOG_TEST_RET(ctx, rv, "SDO RSA encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "SDO RSA encode error"); } else { - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Cryptographic object unsupported for encoding"); + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_NOT_SUPPORTED, "Cryptographic object unsupported for encoding"); } } else if (cmd == SC_CARDCTL_AUTHENTIC_SDO_GENERATE) { @@ -1894,21 +1650,23 @@ authentic_manage_sdo_encode(struct sc_card *card, struct sc_authentic_sdo *sdo, rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PUBLIC_EXPONENT, sdo->data.prvkey->u.rsa.exponent.data, sdo->data.prvkey->u.rsa.exponent.len, &data, &data_len); - LOG_TEST_RET(ctx, rv, "SDO RSA Exponent encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "SDO RSA Exponent encode error"); } data_tag = AUTHENTIC_TAG_RSA_GENERATE_DATA; } else if (cmd != SC_CARDCTL_AUTHENTIC_SDO_DELETE) { - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid SDO operation"); + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_INVALID_DATA, "Invalid SDO operation"); } rv = authentic_update_blob(ctx, data_tag, data, data_len, out, out_len); - LOG_TEST_RET(ctx, rv, "SDO DOCP encode error"); + LOG_TEST_GOTO_ERR(ctx, rv, "SDO DOCP encode error"); + + sc_log_hex(ctx, "encoded SDO operation data", *out, *out_len); +err: free(data); - sc_log(ctx, "encoded SDO operation data %s", sc_dump_hex(*out, *out_len)); LOG_FUNC_RETURN(ctx, rv); } @@ -1944,7 +1702,7 @@ authentic_manage_sdo_generate(struct sc_card *card, struct sc_authentic_sdo *sdo LOG_TEST_RET(ctx, rv, "authentic_sdo_create() SDO put data error"); rv = authentic_decode_pubkey_rsa(ctx, apdu.resp, apdu.resplen, &sdo->data.prvkey); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "cannot decode public key"); + LOG_TEST_RET(card->ctx, rv, "cannot decode public key"); free(data); LOG_FUNC_RETURN(ctx, rv); @@ -1978,12 +1736,14 @@ authentic_manage_sdo(struct sc_card *card, struct sc_authentic_sdo *sdo, unsigne rv = sc_transmit_apdu(card, &apdu); card->max_send_size = save_max_send; - LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + free(data); + if (rv != SC_SUCCESS) { + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + } rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "authentic_sdo_create() SDO put data error"); - free(data); LOG_FUNC_RETURN(ctx, rv); } @@ -2031,7 +1791,7 @@ authentic_set_security_env(struct sc_card *card, int rv; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "set SE#%i(op:0x%X,algo:0x%X,algo_ref:0x%X,flags:0x%X), key_ref:0x%X", + sc_log(ctx, "set SE#%i(op:0x%X,algo:0x%lX,algo_ref:0x%lX,flags:0x%lX), key_ref:0x%X", se_num, env->operation, env->algorithm, env->algorithm_ref, env->algorithm_flags, env->key_ref[0]); switch (env->operation) { case SC_SEC_OPERATION_SIGN: @@ -2093,7 +1853,7 @@ authentic_decipher(struct sc_card *card, const unsigned char *in, size_t in_len, out_len = apdu.resplen; memcpy(out, apdu.resp, out_len); - rv = out_len; + rv = (int)out_len; LOG_FUNC_RETURN(ctx, rv); } @@ -2118,6 +1878,21 @@ authentic_finish(struct sc_card *card) } +static int authentic_card_reader_lock_obtained(sc_card_t *card, int was_reset) +{ + int r = SC_SUCCESS; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (was_reset > 0 + && card->type == SC_CARD_TYPE_OBERTHUR_AUTHENTIC_3_2) { + r = iso7816_select_aid(card, aid_AuthentIC_3_2, sizeof(aid_AuthentIC_3_2), NULL, NULL); + } + + LOG_FUNC_RETURN(card->ctx, r); +} + + /* SM related */ #ifdef ENABLE_SM static int @@ -2175,7 +1950,7 @@ authentic_sm_acl_init (struct sc_card *card, struct sm_info *sm_info, int cmd, static int authentic_sm_execute (struct sc_card *card, struct sm_info *sm_info, - unsigned char *data, int data_len, unsigned char *out, size_t len) + unsigned char *data, size_t data_len, unsigned char *out, size_t len) { struct sc_context *ctx = card->ctx; struct sc_remote_data rdata; @@ -2252,7 +2027,7 @@ authentic_sm_free_wrapped_apdu(struct sc_card *card, struct sc_apdu *plain, stru if (plain) { if (plain->resplen < (*sm_apdu)->resplen) - LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Unsufficient plain APDU response size"); + LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Insufficient plain APDU response size"); memcpy(plain->resp, (*sm_apdu)->resp, (*sm_apdu)->resplen); plain->resplen = (*sm_apdu)->resplen; plain->sw1 = (*sm_apdu)->sw1; @@ -2321,6 +2096,7 @@ authentic_sm_get_wrapped_apdu(struct sc_card *card, struct sc_apdu *plain, struc apdu->resp = calloc (1, plain->resplen + 32); if (!apdu->resp) { + free((unsigned char *) apdu->data); free(apdu); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } @@ -2331,6 +2107,7 @@ authentic_sm_get_wrapped_apdu(struct sc_card *card, struct sc_apdu *plain, struc rv = card->sm_ctx.module.ops.get_apdus(ctx, &card->sm_ctx.info, NULL, 0, NULL); if (rv < 0) { free(apdu->resp); + free((unsigned char *) apdu->data); free(apdu); } LOG_TEST_RET(ctx, rv, "SM: GET_APDUS failed"); @@ -2340,6 +2117,17 @@ authentic_sm_get_wrapped_apdu(struct sc_card *card, struct sc_apdu *plain, struc } #endif +int authentic_logout(sc_card_t *card) +{ + int r = SC_ERROR_NOT_SUPPORTED; + + if (card->type == SC_CARD_TYPE_OBERTHUR_AUTHENTIC_3_2) { + r = iso7816_select_aid(card, aid_AuthentIC_3_2, sizeof(aid_AuthentIC_3_2), NULL, NULL); + } + + return r; +} + static struct sc_card_driver * sc_get_driver(void) { @@ -2370,6 +2158,7 @@ sc_get_driver(void) authentic_ops.card_ctl = authentic_card_ctl; authentic_ops.process_fci = authentic_process_fci; authentic_ops.pin_cmd = authentic_pin_cmd; + authentic_ops.card_reader_lock_obtained = authentic_card_reader_lock_obtained; return &authentic_drv; } diff --git a/src/libopensc/card-belpic.c b/src/libopensc/card-belpic.c index c223189264..e12cb5b90c 100644 --- a/src/libopensc/card-belpic.c +++ b/src/libopensc/card-belpic.c @@ -15,19 +15,19 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* About the Belpic (Belgian Personal Identity Card) card * - * The Belpic card is a Cyberflex Java card, so you normaly communicate + * The Belpic card is a Cyberflex Java card, so you normally communicate * with an applet running on the card. In order to support a pkcs15 file * structure, an applet (the Belpic applet) has been build that emulates * this. So the card's behaviour is specific for this Belpic applet, that's * why a separate driver has been made. * * The card contains the citizen's ID data (name, address, photo, ...) and - * her keys and certs. The ID data are in a seperate directory on the card and + * her keys and certs. The ID data are in a separate directory on the card and * are not handled by this software. For the cryptographic data (keys and certs) * a pkcs#15 structure has been chosen and they can be accessed and used * by the OpenSC software. @@ -35,20 +35,20 @@ * The current situation about the cryptographic data is: there is 1 PIN * that protects 2 private keys and corresponding certs. Then there is a * CA cert and the root cert. The first key (Auth Key) can be used for - * authentication, the second one (NonRep Key) for non repudation purposes + * authentication, the second one (NonRep Key) for non repudiation purposes * (so it can be used as an alternative to manual signatures). * * There are some special things to note, which all have some consequences: * (1) the SELECT FILE command doesn't return any FCI (file length, type, ...) * (2) the NonRep key needs a VERIFY PIN before a signature can be done with it - * (3) pin pad readers had to be supported by a proprietory interface (as at - * that moment no other solution was known/avaiable/ready) + * (3) pin pad readers had to be supported by a proprietary interface (as at + * that moment no other solution was known/available/ready) * The consequences are: * * For (1): we let the SELECT FILE command return that the file length is - * a fixed large number and that each file is a transparant working EF + * a fixed large number and that each file is a transparent working EF * (except the root dir 3F 00). This way however, there is a problem with the - * sc_read_binary() function that will only stop reading untill it receivces + * sc_read_binary() function that will only stop reading until it receives * a 0. Therefore, we use the 'next_idx' trick. Or, if that might fail * and so a READ BINARY past the end of file is done, length 0 is returned * instead of an error code. @@ -58,11 +58,11 @@ * causes other problems and is less user-friendly). A GUI being popped up * by the pkcs11 lib before each NonRep signature has another important * security advantage: applications that cache the PIN can't silently do - * a NonRep signature because there will allways be the GUI. + * a NonRep signature because there will always be the GUI. * * For (3), we link dynamically against a pin pad lib (DLL) that implements the - * proprietory API for a specific pin pad. For each pin pad reader (identified - * by it's PC/SC reader name), a pin pad lib correspondends. Some reader/lib + * proprietary API for a specific pin pad. For each pin pad reader (identified + * by it's PC/SC reader name), a pin pad lib corresponds. Some reader/lib * name pairs are hardcoded, and others can be added in the config file. * Note that there's also a GUI used in this case: if a signature with the * NonRep key is done: a dialog box is shown that asks the user to enter @@ -80,7 +80,7 @@ * language-selection functionality. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -90,27 +90,6 @@ #include "internal.h" #include "log.h" -#ifdef BELPIC_PIN_PAD -#ifndef HAVE_GUI -#define HAVE_GUI -#endif -#endif - -#ifdef BELPIC_PIN_PAD -#include "winscard.h" -#include "scr.h" -#endif - -#ifdef HAVE_GUI -#include "scgui.h" -#ifndef SCR_USAGE_SIGN -#define SCR_USAGE_SIGN 2 /* in scr.h */ -#endif -#ifndef SCR_USAGE_AUTH -#define SCR_USAGE_AUTH 1 -#endif -#endif - /* To be removed */ #include static long t1, t2, tot_read = 0, tot_dur = 0, dur; @@ -132,7 +111,7 @@ static long t1, t2, tot_read = 0, tot_dur = 0, dur; /* Data in the return value for the GET CARD DATA command: * All fields are one byte, except when noted otherwise. * - * See §6.9 in + * See §6.9 in * https://github.com/Fedict/eid-mw/blob/master/doc/sdk/documentation/Public_Belpic_Applet_v1%207_Ref_Manual%20-%20A01.pdf * for the full documentation on the GET CARD DATA command. */ @@ -166,36 +145,18 @@ static long t1, t2, tot_read = 0, tot_dur = 0, dur; /* Used for a trick in select file and read binary */ static size_t next_idx = (size_t)-1; -static struct sc_atr_table belpic_atrs[] = { - /* Applet V1.1 */ - { "3B:98:13:40:0A:A5:03:01:01:01:AD:13:11", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL }, - /* Applet V1.0 with new EMV-compatible ATR */ - { "3B:98:94:40:0A:A5:03:01:01:01:AD:13:10", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL }, - /* Applet beta 5 + V1.0 */ - { "3B:98:94:40:FF:A5:03:01:01:01:AD:13:10", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL }, - { NULL, NULL, NULL, 0, 0, NULL } -}; - -struct belpic_priv_data { - int lang; - int options; -#ifdef BELPIC_PIN_PAD - FARPROC scr_init; - FARPROC scr_verify_pin; - FARPROC scr_change_pin; - char szPinPadDll[64]; -#endif +static const struct sc_atr_table belpic_atrs[] = { + /* Applet V1.8 */ + {"3B:7F:96:00:00:80:31:80:65:B0:85:04:01:20:12:0F:FF:82:90:00", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL}, + /* Applet V1.1 */ + {"3B:98:13:40:0A:A5:03:01:01:01:AD:13:11", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL}, + /* Applet V1.0 with new EMV-compatible ATR */ + {"3B:98:94:40:0A:A5:03:01:01:01:AD:13:10", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL}, + /* Applet beta 5 + V1.0 */ + {"3B:98:94:40:FF:A5:03:01:01:01:AD:13:10", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL}, + {NULL, NULL, NULL, 0, 0, NULL} }; -#define DRVDATA(card) ((struct belpic_priv_data *) ((card)->drv_data)) - -/* Single Sign On */ -#ifdef HAVE_ALLOW_SSO -#define SSO_OK(drv) ((drv)->allow_sso) -#else -#define SSO_OK(drv) 0 -#endif - static struct sc_card_operations belpic_ops; static struct sc_card_driver belpic_drv = { "Belpic cards", @@ -205,425 +166,18 @@ static struct sc_card_driver belpic_drv = { }; static const struct sc_card_operations *iso_ops = NULL; -#define LNG_ENG 0 -#define LNG_DUTCH 1 -#define LNG_FRENCH 2 -#define LNG_GERMAN 3 -#define LNG_NONE 0xFFFF - -#ifdef BELPIC_PIN_PAD - -/* Option flags from the config file */ -#define PP_MSG_AUTH_PIN 0x00000001 -#define PP_MSG_WRONG_PIN 0x00000002 -#define PP_MSG_CHANGEPIN_MISMATCH 0x00000004 -#define PP_MSG_PIN_BLOCKED 0x00000008 - -/* Hardcoded pin pad reader names (PC/SC) and their pin pad lib */ -static char *pp_reader_names[] = { - "Xiring X Pass Serial", - NULL -}; -static char *pp_reader_libs[] = { - "xireid", - NULL -}; - -static BYTE aid_belpic[] = { 0xA0, 0x00, 0x00, 0x01, 0x77, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 }; -static SCR_Application scr_app_belpic = { - {aid_belpic, sizeof(aid_belpic)}, - "ID", - NULL -}; -static char *app_id_longstr[] = { - "Identity", - "Identiteit", - "Identité", - "Identität" -}; -#endif /* BELPIC_PIN_PAD */ - -#if defined(HAVE_GUI) ||defined(BELPIC_PIN_PAD) -static char *pin_usg_sig[] = { - "Signature", - "Handtekening", - "Signature", - "Signatur" -}; -static char *pin_usg_auth[] = { - "Authentication", - "Authentificatie", - "Authentification", - "Authentifizierung" -}; -#endif /* defined(HAVE_GUI) ||defined(BELPIC_PIN_PAD) */ - -#ifdef BELPIC_PIN_PAD -static char *lang_codes[4] = { - "en", - "nl", - "fr", - "de" -}; -static char *pp_msg_auth_sh[] = { - "Authentication", - "Authentificatie", - "Authentification", - "Kennzeichnung" -}; -static char *pp_msg_auth[] = { - "Enter your PIN on the reader, in order to authenticate yourself", - "Geef uw PIN in op de lezer, om u te authentificeren", - "Entrez votre PIN sur le lecteur, pour vous authentifier", - "Bitte geben Sie Ihre PIN am Kartenlesegerät ein, um sich zu authentifizieren" -}; -static char *pp_msg_sign_sh[] = { - "Signature", - "Handtekening", - "Signature", - "Signatur" -}; -static char *pp_msg_sign[] = { - "Caution: You are about to make a legally binding electronic signature with your identity card.\nPlease enter your PIN on the card reader to continue or click the Cancel button.\n\nIf you only want to log on to a web site or server, do NOT enter your PIN and click the Cancel button.", - "Let op: u gaat een wettelijk bindende electronische handtekening plaatsen met uw identiteitskaart.\nGeef uw PIN in op de kaartlezer om verder te gaan of klik op Stoppen.\n\nAls u enkel wil aanloggen op een web site of een server, geef uw PIN NIET in en klik op Stoppen.", - "Attention: vous allez apposer une signature électronique juridiquement valide avec votre carte d'identité.\nVeuillez entrer votre PIN sur le lecteur externe pour continuer ou cliquez sur Annuler.\n\nSi vous désirez seulement vous connecter à un site ou un serveur, n'entrez PAS votre PIN et cliquez sur Annuler.", - "Achtung: Mit Ihrem Personalausweis werden Sie eine rechtlich bindende elektronische Signatur setzen.\nBitte geben Sie Ihre PIN am Kartenlesgerät ein zum weitergehen oder klicken Sie auf Abbrechen.\n\nWenn Sie nur auf das Internet gehen möchten, geben Sie bitte Ihre PIN NICHT ein, sondern klicken Sie auf Abbrechen." -}; -static char *pp_msg_change_sh[] = { - "PIN change", - "PIN verandering", - "Modification du PIN ", - "PIN ändern" -}; -static char *pp_msg_change[] = { - "Change your PIN on the reader", - "Verander uw PIN op de lezer", - "Modifiez votre PIN sur le lecteur", - "Bitte ändern Sie Ihre PIN am Kartenlesegerät" -}; -static char *pp_msg_pin_mismatch[] = { - "The new PINs you entered were different.\n\nRetry or cancel?", - "De ingevoerde nieuwe PINs zijn verschillend.\n\nOpnieuw proberen of stoppen?", - "Les nouveaux PIN entrés sont différents.\n\nRéessayer ou annuler?", - "Die von Ihnen eingegebenen PINs unterscheiden sich.\n\nErneut versuchen oder abbrechen?" -}; - -#define PCSC_ERROR(ctx, desc, rv) sc_debug(ctx, SC_LOG_DEBUG_NORMAL, desc ": %lx\n", rv); - -#endif /* BELPIC_PIN_PAD */ - -/* Language support for the GUI messages */ -#ifdef HAVE_GUI - -#ifdef WIN32 -#define BTN_KEYB_SHORTCUT "&" -#else -#define BTN_KEYB_SHORTCUT "_" -#endif - -static char *app_msg[] = { - "Identity", - "Identiteit", -#ifdef _WIN32 - "Identité", -#else - "Identite", -#endif -#ifdef _WIN32 - "Identität" -#else - "Identitat", -#endif -}; -static char *btn_msg_retry[4] = { - BTN_KEYB_SHORTCUT"Try again", - BTN_KEYB_SHORTCUT"Opnieuw proberen", - BTN_KEYB_SHORTCUT"Réessayer", - BTN_KEYB_SHORTCUT"Erneut versuchen" -}; -static char *btn_msg_cancel[4] = { - BTN_KEYB_SHORTCUT"Cancel", - BTN_KEYB_SHORTCUT"Stoppen", - BTN_KEYB_SHORTCUT"Annuler", - BTN_KEYB_SHORTCUT"Abbrechen" -}; -static char *btn_msg_ok[4] = { - BTN_KEYB_SHORTCUT"OK", - BTN_KEYB_SHORTCUT"OK", - BTN_KEYB_SHORTCUT"OK", - BTN_KEYB_SHORTCUT"OK" -}; -static char *btn_msg_close[4] = { - BTN_KEYB_SHORTCUT"Close", - BTN_KEYB_SHORTCUT"Sluiten", - BTN_KEYB_SHORTCUT"Fermer", - BTN_KEYB_SHORTCUT"Schliessen" -}; -static char *enter_pin_msg_auth[] = { - "Enter your PIN, in order to authenticate yourself", - "Geef uw PIN in, om u te authentificeren", - "Entrez votre PIN, pour vous authentifier", - "Bitte geben Sie Ihre PIN ein, um sich zu authentifizieren" -}; -static char *enter_pin_msg_sign[4] = { -#ifdef _WIN32 - "Caution: You are about to make a legally binding electronic signature with your identity card.\nPlease enter your PIN to continue or click the Cancel button.\n\nWarning: if you only want to log on to a web site or server, do NOT enter your PIN and click the Cancel button.", - "Let op: u gaat een wettelijk bindende electronische handtekening plaatsen met uw identiteitskaart.\nGeef uw PIN in om verder te gaan of klik op Stoppen.\n\nWaarschuwing: als u enkel wil aanloggen op een web site of een server, geef uw PIN NIET in en klik op Stoppen.", - "Attention: vous allez apposer une signature électronique juridiquement valide avec votre carte d'identité.\nVeuillez entrer votre PIN pour continuer ou cliquez sur Annuler.\n\nPrécaution: si vous désirez seulement vous connecter à un site ou un serveur, n'entrez PAS votre PIN et cliquez sur Annuler.", - "Achtung: Mit Ihrem Personalausweis werden Sie eine rechtlich bindende elektronische Signatur setzen.\nBitte geben Sie Ihre PIN ein zum weitergehen oder klicken Sie auf Abbrechen.\n\nWarnung: Wenn Sie nur auf das Internet gehen möchten, geben Sie bitte Ihre PIN NICHT ein, sondern klicken Sie auf Abbrechen." -#else -#ifdef __APPLE__ - "CAUTION: you are about to make a legally binding electronic signature with your identity card. Please enter your PIN to continue or press the Cancel button. If you only want to log on to a web site or a server, do NOT enter your PIN and press the Cancel button.", - "LET OP: u gaat een wettelijk bindende electronische handtekening plaatsen met uw identiteitskaart. Geef uw PIN in om verder te gaan of klik op Stoppen. Als u enkel wil aanloggen op een web site of een server, geef uw PIN NIET in en klik op Stoppen.", - "ATTENTION: vous allez apposer une signature electronique\njuridiquement valide avec votre carte d'identite.Veuillez entrer votre PIN pour continuer ou cliquez sur Annuler. Si vous desirez seulement vous connecter a un site ou un serveur, n' entrez PAS votre PIN et cliquez sur Annuler.", - "ACHTUNG: Mit Ihrem Personalausweis werden Sie eine rechtlich bindende elektronische Signatur setzen. Geben Sie Ihre PIN ein zum weitergehen oder klicken Sie auf Abbrechen. Warnung: Wenn Sie nur auf das Internet gehen mochten, geben Sie bitte Ihre PIN NICHT ein, sondern klicken Sie auf Abbrechen." -#else - "Caution: you are about to make a legally binding electronic\nsignature with your identity card.\nPlease enter your PIN to continue or press the Cancel button.\n\nIf you only want to log on to a web site or a server,\ndo NOT enter your PIN and press the Cancel button.", - "Let op: u gaat een wettelijk bindende electronische handtekening\nplaatsen met uw identiteitskaart.\nGeef uw PIN in om verder te gaan of klik op Stoppen.\n\nAls u enkel wil aanloggen op een web site\nof een server, geef uw PIN NIET in en klik op Stoppen.", - "Attention: vous allez apposer une signature electronique\njuridiquement valide avec votre carte d'identite.\nVeuillez entrer votre PIN pour continuer ou cliquez sur Annuler.\n\nSi vous desirez seulement vous connecter a un site\nou un serveur, n'entrez PAS votre PIN et cliquez sur Annuler.", - "Achtung: Mit Ihrem Personalausweis werden Sie eine rechtlich\r\nbindende elektronische Signatur setzen.\r\nGeben Sie Ihre PIN ein zum weitergehen oder klicken Sie auf Abbrechen.\r\n\r\nWarnung: Wenn Sie nur auf das Internet gehen mochten, geben\r\nSie bitte Ihre PIN NICHT ein, sondern klicken Sie auf Abbrechen." -#endif -#endif -}; -static char *wrong_pin_len_msgs[4] = { - "Wrong PIN length", - "Foute PIN lengte", - "Longueur de PIN erroné", - "Falsche PIN-Länge" -}; -static char *wrong_pin_msgs[4] = { - "Wrong PIN, %d tries left\n\nRetry or cancel?", - "Foute PIN, nog %d pogingen\n\nOpnieuw proberen of stoppen?", - "PIN erroné, %d essais restants\n\nRéessayer ou annuler?", - "Falsche PIN, %d verbleibende Versuche\n\nErneut versuchen oder abbrechen?" -}; -static char *pin_blocked_msgs[4] = { - "PIN blocked", - "PIN geblokkeerd", - "PIN bloqué ", - "PIN gesperrt" -}; - -#endif /* HAVE_GUI */ - -#ifdef BELPIC_PIN_PAD - -#define SCR_INIT_ID 100 -#define SCR_VERIFY_ID 101 -#define SCR_CHANGE_ID 102 -#define SCR_CARD_HANDLE 999 - -struct tTLV { - unsigned char *base; - unsigned char *end; - unsigned char *current; - unsigned char *next; -}; - -static void TLVInit(struct tTLV *tlv, u8 * base, size_t size) -{ - tlv->base = base; - tlv->end = base + size; - tlv->current = tlv->next = base; -} - -static void TLVNext(struct tTLV *tlv, u8 tag) -{ - assert(tlv->next + 2 < tlv->end); - tlv->current = tlv->next; - *(tlv->next++) = tag; - *(tlv->next++) = 0; -} - -static void TLVAdd(struct tTLV *tlv, u8 val) -{ - assert(tlv->next + 1 < tlv->end); - *(tlv->next++) = val; - tlv->current[1]++; -} - -static void TLVAddBuffer(struct tTLV *tlv, u8 * val, size_t size) -{ - assert(tlv->next + size < tlv->end); - memcpy(tlv->next, val, size); - tlv->current[1] = size; - tlv->next = tlv->next + size; -} - -static size_t TLVLen(struct tTLV *tlv) -{ - return tlv->next - tlv->base; -} - -static LONG SCR_SCardInit(LPCTSTR szPinPadDll, LPCTSTR szReader, DWORD version, - SCR_SupportConstants * supported) -{ - LONG rv; - unsigned char sendbuf[256]; - unsigned char recvbuf[2]; - char szTemp[32]; - DWORD dwRecvLength; - struct tTLV tlv; - - memset(szTemp, 0, sizeof(szTemp)); - memset(sendbuf, 0, sizeof(sendbuf)); - memset(recvbuf, 0, sizeof(recvbuf)); - dwRecvLength = sizeof(recvbuf); - - /* Make TLV buffer */ - TLVInit(&tlv, sendbuf, sizeof(sendbuf)); - TLVNext(&tlv, 0x01); /* Function ID */ - sprintf(szTemp, "%ld", SCR_INIT_ID); - TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp)); - TLVNext(&tlv, 0x02); /* PinPad Dll */ - TLVAddBuffer(&tlv, (u8 *) szPinPadDll, strlen(szPinPadDll)); - TLVNext(&tlv, 0x03); /* Reader Name */ - TLVAddBuffer(&tlv, (u8 *) szReader, strlen(szReader)); - TLVNext(&tlv, 0x04); /* Version */ - sprintf(szTemp, "%ld", version); - TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp)); - -#ifdef HAVE_PCSC_OLD - rv = SCardControl(SCR_CARD_HANDLE, sendbuf, TLVLen(&tlv), recvbuf, &dwRecvLength); -#else - rv = SCardControl(SCR_CARD_HANDLE, 0, sendbuf, TLVLen(&tlv), - recvbuf, dwRecvLength, &dwRecvLength); -#endif - if (dwRecvLength > 0) { - *supported = recvbuf[0]; - } else { - rv = SC_ERROR_UNKNOWN_DATA_RECEIVED; - } - - return rv; -} - -static LONG SCR_SCardPIN(long lAction, LPCTSTR szPinPadDll, const SCR_Card * pCard, BYTE pinID, - const SCR_PinUsage * pUsage, const SCR_Application * pApp, - BYTE * pCardStatus) -{ - LONG rv; - unsigned char sendbuf[256]; - unsigned char recvbuf[2]; - char szTemp[32]; - DWORD dwRecvLength; - struct tTLV tlv; - - memset(szTemp, 0, sizeof(szTemp)); - memset(recvbuf, 0, sizeof(recvbuf)); - dwRecvLength = sizeof(recvbuf); - - /* Make TLV buffer */ - TLVInit(&tlv, sendbuf, sizeof(sendbuf)); - TLVNext(&tlv, 0x01); /* Function ID */ - sprintf(szTemp, "%ld", lAction); - TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp)); - TLVNext(&tlv, 0x02); /* PinPad Dll */ - TLVAddBuffer(&tlv, (u8 *) szPinPadDll, strlen(szPinPadDll)); - TLVNext(&tlv, 0x03); /* SCR_Card Handle */ - sprintf(szTemp, "%ld", pCard->hCard); - TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp)); - if (pCard->language != NULL) { - TLVNext(&tlv, 0x04); /* SCR_Card language */ - TLVAddBuffer(&tlv, (u8 *) pCard->language, strlen(pCard->language)); - } - if (pCard->id.data != NULL) { - TLVNext(&tlv, 0x05); /* SCR_Card id */ - TLVAddBuffer(&tlv, pCard->id.data, pCard->id.length); - } - TLVNext(&tlv, 0x06); /* PinID */ - TLVAdd(&tlv, pinID); - if (pUsage != NULL) { - TLVNext(&tlv, 0x07); /* SCR_PinUsage code */ - sprintf(szTemp, "%ld", pUsage->code); - TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp)); - if (pUsage->shortString != NULL) { - TLVNext(&tlv, 0x08); /* SCR_PinUsage shortstring */ - TLVAddBuffer(&tlv, (u8 *) pUsage->shortString, strlen(pUsage->shortString)); - } - if (pUsage->longString != NULL) { - TLVNext(&tlv, 0x09); /* SCR_PinUsage longstring */ - TLVAddBuffer(&tlv, (u8 *) pUsage->longString, strlen(pUsage->longString)); - } - } - if (pApp->id.data != NULL) { - TLVNext(&tlv, 0x0A); /* SCR_Application id */ - TLVAddBuffer(&tlv, (u8 *) pApp->id.data, pApp->id.length); - } - if (pApp->shortString != NULL) { - TLVNext(&tlv, 0x0B); /* SCR_Application shortstring */ - TLVAddBuffer(&tlv, (u8 *) pApp->shortString, strlen(pApp->shortString)); - } - if (pApp->longString != NULL) { - TLVNext(&tlv, 0x0C); /* SCR_Application longstring */ - TLVAddBuffer(&tlv, (u8 *) pApp->longString, strlen(pApp->longString)); - } -#ifdef HAVE_PCSC_OLD - rv = SCardControl(SCR_CARD_HANDLE, sendbuf, TLVLen(&tlv), recvbuf, &dwRecvLength); -#else - rv = SCardControl(SCR_CARD_HANDLE, 0, sendbuf, TLVLen(&tlv), - recvbuf, dwRecvLength, &dwRecvLength); -#endif - if (dwRecvLength < 2) { - rv = SC_ERROR_UNKNOWN_DATA_RECEIVED; - } else { - memcpy(pCardStatus, recvbuf, 2); - } - - return rv; -} - -static LONG SCR_SCardVerifyPIN(LPCTSTR szPinPadDll, const SCR_Card * pCard, BYTE pinID, - const SCR_PinUsage * pUsage, const SCR_Application * pApp, - BYTE * pCardStatus) -{ - return SCR_SCardPIN(SCR_VERIFY_ID, szPinPadDll, pCard, pinID, pUsage, pApp, pCardStatus); -} - -static LONG SCR_SCardChangePIN(LPCTSTR szPinPadDll, const SCR_Card * pCard, BYTE pinID, - const SCR_Application * pApp, BYTE * pCardStatus) -{ - return SCR_SCardPIN(SCR_CHANGE_ID, szPinPadDll, pCard, pinID, NULL, pApp, pCardStatus); -} - -#endif /* BELPIC_PIN_PAD */ - -#if defined(HAVE_GUI) ||defined(BELPIC_PIN_PAD) - -static int belpic_calculate_lang(sc_card_t *card) -{ - struct belpic_priv_data *priv = DRVDATA(card); - int lang = priv->lang; - return lang; -} - -#endif /* defined(HAVE_GUI) ||defined(BELPIC_PIN_PAD) */ - -static int str2lang(sc_context_t *ctx, char *lang) -{ - if (memcmp(lang, "en", 2) == 0) - return LNG_ENG; - else if (memcmp(lang, "nl", 2) == 0) - return LNG_DUTCH; - else if (memcmp(lang, "fr", 2) == 0) - return LNG_FRENCH; - else if (memcmp(lang, "de", 2) == 0) - return LNG_GERMAN; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Unknown/unsupported language code: %c%c\n", lang[0], lang[1]); - return -1; -} - static int get_carddata(sc_card_t *card, u8* carddata_loc, unsigned int carddataloc_len) { sc_apdu_t apdu; u8 carddata_cmd[] = { 0x80, 0xE4, 0x00, 0x00, 0x1C }; int r; - assert(carddataloc_len == BELPIC_CARDDATA_RESP_LEN); + if (carddataloc_len != BELPIC_CARDDATA_RESP_LEN) + return SC_ERROR_INTERNAL; r = sc_bytes2apdu(card->ctx, carddata_cmd, sizeof(carddata_cmd), &apdu); if(r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "bytes to APDU conversion failed: %d\n", r); + sc_log(card->ctx, "bytes to APDU conversion failed: %d\n", r); return r; } @@ -632,17 +186,17 @@ static int get_carddata(sc_card_t *card, u8* carddata_loc, unsigned int carddata r = sc_transmit_apdu(card, &apdu); if(r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "GetCardData command failed: %d\n", r); + sc_log(card->ctx, "GetCardData command failed: %d\n", r); return r; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if(r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "GetCardData: card returned %d\n", r); + sc_log(card->ctx, "GetCardData: card returned %d\n", r); return r; } if(apdu.resplen < carddataloc_len) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "GetCardData: card returned %"SC_FORMAT_LEN_SIZE_T"u bytes rather than expected %d\n", apdu.resplen, carddataloc_len); return SC_ERROR_WRONG_LENGTH; @@ -651,258 +205,6 @@ static int get_carddata(sc_card_t *card, u8* carddata_loc, unsigned int carddata return 0; } -#ifdef GET_LANG_FROM_CARD - -/* str is in lower case, the case of buf can be both, and buf is large enough */ -static int match_string(const char *str, const char *buf) -{ - int i = 0; - - while (str[i] != '\0') { - if (str[i] != ((buf[i] >= 'A' && buf[i] <= 'Z') ? buf[i] + 32 : buf[i])) - return 0; - i++; - } - return 1; /* match */ -} - -static int get_pref(const char *prefs, int prefs_len, const char *title, const char *key, int *len) -{ - int i = 0; - int title_len = strlen(title); - int key_len = strlen(key); - - while (prefs[i] != '\0' && i < prefs_len) - i++; - prefs_len = i; - - i = 0; - while (i < prefs_len) { - while (i < prefs_len && prefs[i] != '[') - i++; - if (i + title_len >= prefs_len) - return -1; - if (!match_string(title, prefs + i)) { - i++; - continue; - } - i += title_len; - while (i < prefs_len) { - while (i < prefs_len && (prefs[i] == '\r' || prefs[i] == '\n')) - i++; - if (i < prefs_len && prefs[i] == '[') - break; - if (i + key_len + 1 >= prefs_len) - return -2; - if (!match_string(key, prefs + i)) { - i++; - continue; - } - i += key_len; - if (prefs[i] != '=') - return -3; - *len = ++i; - while (*len < prefs_len && prefs[*len] != '\r' && prefs[*len] != '\n') - (*len)++; - *len -= i; - return i; - } - } - - return -1; -} - -static int get_language(sc_card_t *card) -{ - sc_apdu_t apdu; - u8 prefs[240], *lg_value; - u8 path[] = { 0x3F, 0x00, 0xDF, 0x01, 0x40, 0x39 }; - int r, i, len; - - /* Get the language from the card's preferences file */ - assert(card != NULL); - - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x08, 0x0C); - apdu.lc = sizeof(path); - apdu.data = path; - apdu.datalen = sizeof(path); - apdu.resplen = 0; - apdu.le = 0; - - r = sc_lock(card); - if (r < 0) - goto prefs_error; - - r = sc_transmit_apdu(card, &apdu); - if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Select_File[prefs_file] command failed: %d\n", r); - sc_unlock(card); - goto prefs_error; - } - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Select_File[prefs_file]: card returned %d\n", r); - sc_unlock(card); - goto prefs_error; - } - - r = iso_ops->read_binary(card, 0, prefs, sizeof(prefs), 0); - sc_unlock(card); - if (r <= 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Read_Binary[prefs_file] returned %d\n", r); - goto prefs_error; - } - i = get_pref(prefs, r, "[gen]", "lg", &len); - if (i <= 0 || len < 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Couldn't find language in prefs file: %d\n", i); - goto prefs_error; - } - lg_value = prefs + i; /* language code(s) found, starts here */ - i = 0; - while (1) { - while (i <= len - 2 && (lg_value[i] == ' ' || lg_value[i] == '|')) - i++; - if (i > len - 2) - goto prefs_error; - r = str2lang(card->ctx, lg_value + i); - if (r >= 0) - return r; - i += 2; - } - - prefs_error: - /* If troubles with the card's prefs file, get the language from the OS */ -#ifdef _WIN32 - switch (GetUserDefaultLangID() & 0x00FF) { - case 0x13: - return LNG_DUTCH; - case 0x0C: - return LNG_FRENCH; - case 0x07: - return LNG_GERMAN; - default: - return LNG_ENG; - } -#endif - return LNG_ENG; /* default */ -} - -#endif /* GET_LANG_FROM_CARD */ - -static scconf_block *get_belpic_conf(sc_context_t *ctx, const char *name) -{ - scconf_block *conf_block = NULL, **blocks; - int i; - - for (i = 0; ctx->conf_blocks[i] != NULL; i++) { - blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], name, NULL); - if (!blocks) - return NULL; - conf_block = blocks[0]; - free(blocks); - if (conf_block != NULL) - break; - } - - return conf_block; -} - -#ifdef BELPIC_PIN_PAD - -static void load_pin_pad_err(const char *reader_name, const char *pp_reader_lib, char *msg) -{ - char buf[300]; - void *hDlg; - - if (strlen(reader_name) + strlen(pp_reader_lib) > 200) - return; - - sprintf(buf, "Error while loading library \"%s\" for pin pad reader \"%s\": %s\n", - pp_reader_lib, reader_name, msg); - scgui_ask_message(app_msg[0], "Pin pad library error", buf, btn_msg_close[0], NULL, - reader_name); -} - -static int belpic_load_pin_pad_lib(sc_card_t *card, struct belpic_priv_data *priv_data, - const char *reader_name, const char *pp_reader_lib) -{ - LONG r; - DWORD supported; - - memset(priv_data->szPinPadDll, 0, sizeof(priv_data->szPinPadDll)); - strcpy(priv_data->szPinPadDll, pp_reader_lib); - - priv_data->scr_init = (FARPROC) SCR_SCardInit; - priv_data->scr_verify_pin = (FARPROC) SCR_SCardVerifyPIN; - priv_data->scr_change_pin = (FARPROC) SCR_SCardChangePIN; - - if (priv_data->scr_init == NULL || priv_data->scr_verify_pin == NULL) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Function not found in \"%s\" err = 0x%0x\n", - pp_reader_lib, GetLastError()); - load_pin_pad_err(reader_name, pp_reader_lib, - "unsufficient functionality found in library"); - return SC_ERROR_READER; - } - - r = priv_data->scr_init(pp_reader_lib, reader_name, 1, &supported); - if (r != SCARD_S_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SCR_Init() returned 0x%0x\n", r); - load_pin_pad_err(reader_name, pp_reader_lib, "Initialization of library failed"); - return SC_ERROR_READER; - } - if (supported) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SCR_init() returned not supported code 0x%0x\n", supported); - load_pin_pad_err(reader_name, pp_reader_lib, - "Initialization of library returned UNSUPPORTED"); - return SC_ERROR_READER; - } - return 1; -} - -static int belpic_detect_pin_pad(sc_card_t *card, struct belpic_priv_data *priv_data) -{ - int i = 0; - char *reader_name = card->reader->name, *conf_reader, *conf_lib; - scconf_block *conf_block = NULL; - char *reader_i = "reader ", *lib_i = "lib "; - int rn_len = strlen(reader_name); - - /* Hardcoded readers */ - for (i = 0; pp_reader_names[i] != NULL; i++) { - int pp_rn_len = strlen(pp_reader_names[i]); - if (rn_len >= pp_rn_len && strncmp(reader_name, pp_reader_names[i], pp_rn_len) == 0) { - return belpic_load_pin_pad_lib(card, priv_data, - reader_name, pp_reader_libs[i]); - } - } - - /* From the config file */ - conf_block = get_belpic_conf(card->ctx, "belpic_pin_pad"); - if (conf_block == NULL) - return 0; - for (i = 0; i < 10; i++) { - reader_i[6] = (char) (0x30 + i); - conf_reader = (char *) scconf_get_str(conf_block, reader_i, NULL); - if (conf_reader != NULL && rn_len >= strlen(conf_reader) && - strncmp(reader_name, conf_reader, strlen(conf_reader)) == 0) { - lib_i[3] = (char) (0x30 + i); - conf_lib = (char *) scconf_get_str(conf_block, lib_i, NULL); - if (conf_lib != NULL) - return belpic_load_pin_pad_lib(card, priv_data, - reader_name, conf_lib); - } - } - - return 0; -} -#endif /* BELPIC_PIN_PAD */ - -static int belpic_finish(sc_card_t *card) -{ - free(DRVDATA(card)); - return 0; -} - static int belpic_match_card(sc_card_t *card) { int i; @@ -915,86 +217,42 @@ static int belpic_match_card(sc_card_t *card) static int belpic_init(sc_card_t *card) { - struct belpic_priv_data *priv = NULL; - scconf_block *conf_block; - u8 applet_version; - u8 carddata[BELPIC_CARDDATA_RESP_LEN]; int key_size = 1024; - int r; + unsigned long flags = 0; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Belpic V%s", BELPIC_VERSION); -#ifdef HAVE_GUI - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " with GUI support"); -#endif -#ifdef BELPIC_PIN_PAD - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " with support for pin pad reader libs"); -#endif - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "\n"); + sc_log(card->ctx, "Belpic V%s\n", BELPIC_VERSION); if (card->type < 0) card->type = SC_CARD_TYPE_BELPIC_EID; /* Unknown card: assume it's the Belpic Card */ - priv = calloc(1, sizeof(struct belpic_priv_data)); - if (priv == NULL) - return SC_ERROR_OUT_OF_MEMORY; - card->drv_data = priv; card->cla = 0x00; if (card->type == SC_CARD_TYPE_BELPIC_EID) { - if((r = get_carddata(card, carddata, sizeof(carddata))) < 0) { - return r; + u8 carddata[BELPIC_CARDDATA_RESP_LEN]; + memset(carddata, 0, sizeof(carddata)); + + if(get_carddata(card, carddata, sizeof(carddata)) < 0) { + return SC_ERROR_INVALID_CARD; } - applet_version = carddata[BELPIC_CARDDATA_OFF_APPLETVERS]; - if(applet_version >= 0x17) { - key_size = 2048; + if (carddata[BELPIC_CARDDATA_OFF_APPLETVERS] >= 0x18) { + flags = SC_ALGORITHM_ECDSA_HASH_NONE | SC_ALGORITHM_ECDSA_RAW; + _sc_card_add_ec_alg(card, 256, flags, 0, NULL); + _sc_card_add_ec_alg(card, 384, flags, 0, NULL); + _sc_card_add_ec_alg(card, 512, flags, 0, NULL); + _sc_card_add_ec_alg(card, 521, flags, 0, NULL); + } else { + if (carddata[BELPIC_CARDDATA_OFF_APPLETVERS] >= 0x17) { + key_size = 2048; + } + _sc_card_add_rsa_alg(card, key_size, + SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE, 0); } - _sc_card_add_rsa_alg(card, key_size, - SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE, 0); } /* State that we have an RNG */ card->caps |= SC_CARD_CAP_RNG; - /* Language prefences */ - priv->lang = -1; - conf_block = get_belpic_conf(card->ctx, "belpic_general"); - if (conf_block != NULL) { - char *lang = (char *) scconf_get_str(conf_block, "force_language", NULL); - if (lang != NULL && strlen(lang) == 2) - priv->lang = str2lang(card->ctx, lang); - } -#ifdef GET_LANG_FROM_CARD - if (priv->lang == -1) - priv->lang = get_language(card); -#endif - card->max_pin_len = BELPIC_MAX_USER_PIN_LEN; -#ifdef HAVE_GUI - r = scgui_init(); - if (r != 0) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "scgui_init() returned error %d\n", i); -#endif - -#ifdef BELPIC_PIN_PAD - r = belpic_detect_pin_pad(card, priv); - if (r == 1) - card->reader->capabilities |= SC_READER_CAP_PIN_PAD; - else if (r < 0) - return r; /* error loading/initing pin pad lib */ - - conf_block = get_belpic_conf(card->ctx, "belpic_pin_pad"); - if (conf_block != NULL) { - if (scconf_get_bool(conf_block, "msg_auth_pin", 1)) - priv->options |= PP_MSG_AUTH_PIN; - if (scconf_get_bool(conf_block, "msg_wrong_pin", 1)) - priv->options |= PP_MSG_WRONG_PIN; - if (scconf_get_bool(conf_block, "msg_changepin_mismatch", 1)) - priv->options |= PP_MSG_CHANGEPIN_MISMATCH; - if (scconf_get_bool(conf_block, "msg_pin_blocked", 1)) - priv->options |= PP_MSG_PIN_BLOCKED; - } -#endif - return 0; } @@ -1003,10 +261,12 @@ static int belpic_select_file(sc_card_t *card, { sc_apdu_t apdu; u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; - int r, pathlen; + int r; + size_t pathlen; sc_file_t *file = NULL; - assert(card != NULL && in_path != NULL); + if (card == NULL || in_path == NULL) + return SC_ERROR_INTERNAL; memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; @@ -1021,7 +281,7 @@ static int belpic_select_file(sc_card_t *card, r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Select File APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "Select File APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) @@ -1048,7 +308,7 @@ static int belpic_select_file(sc_card_t *card, } static int belpic_read_binary(sc_card_t *card, - unsigned int idx, u8 * buf, size_t count, unsigned long flags) + unsigned int idx, u8 * buf, size_t count, unsigned long *flags) { int r; @@ -1074,302 +334,17 @@ static int belpic_read_binary(sc_card_t *card, return r; } -#ifdef BELPIC_PIN_PAD - -/* Test the result code of the pin pad reader + the card's status bytes */ -static int belpic_pp_test_res(sc_card_t *card, int r, const u8 * card_status, int *tries_left) +static int belpic_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) { - if (r != SCARD_S_SUCCESS) { - switch (r) { - case SCARD_E_CANCELLED: - return SC_ERROR_KEYPAD_CANCELLED; - case SCARD_W_REMOVED_CARD: - return SC_ERROR_CARD_REMOVED; - case SCR_I_PIN_CHECK_FAILED: - return SC_ERROR_KEYPAD_PIN_MISMATCH; - default: - return SC_ERROR_TRANSMIT_FAILED; - } - } - if (card_status[0] == 0xEC && card_status[1] == 0xD2) - return SC_ERROR_KEYPAD_TIMEOUT; - if (card_status[0] == 0xEC && card_status[1] == 0xD6) - return SC_ERROR_KEYPAD_CANCELLED; - if (card_status[0] == 0x63) { - if ((card_status[1] & 0xF0) == 0xC0 && tries_left != NULL) - *tries_left = card_status[1] & 0x0F; - return SC_ERROR_PIN_CODE_INCORRECT; - } - return sc_check_sw(card, card_status[0], card_status[1]); -} - -/* Send the verify pin command to the pin pad reader + optionally show message */ -static int belpic_pp_verify(sc_card_t *card, SCR_Card * scr_card, - struct belpic_priv_data *priv, int pin_ref, - int pin_usage, int *tries_left) -{ - BYTE card_status[2]; - void *hDlg; - int first_time = 1, r = SC_ERROR_PIN_CODE_INCORRECT; - int lang = belpic_calculate_lang(card); - SCR_PinUsage scr_pin_usage = { - pin_usage, - pin_usage == SCR_USAGE_SIGN ? "SIG" : "AUT", - pin_usage == SCR_USAGE_SIGN ? pin_usg_sig[lang] : pin_usg_auth[lang] - }; - char *reader_name = card->reader->name; - char *pp_msg_login_sh = - (pin_usage == SCR_USAGE_SIGN ? pp_msg_sign_sh[lang] : pp_msg_auth_sh[lang]); - char *pp_msg_login = (pin_usage == SCR_USAGE_SIGN ? pp_msg_sign[lang] : pp_msg_auth[lang]); - scgui_param_t icon = (pin_usage == SCR_USAGE_SIGN ? SCGUI_SIGN_ICON : SCGUI_NO_ICON); - int mesg_on_screen = (priv->options & PP_MSG_AUTH_PIN) || - (pin_usage != SCR_USAGE_AUTH) || SSO_OK(card->ctx); - - while (r == SC_ERROR_PIN_CODE_INCORRECT) { - if (!first_time) { - if (priv->options & PP_MSG_WRONG_PIN) { - int r1; - char msg[200]; - - sprintf(msg, wrong_pin_msgs[lang], *tries_left); - r1 = scgui_ask_message(app_msg[lang], pp_msg_login_sh, msg, - btn_msg_retry[lang], btn_msg_cancel[lang], - reader_name); - if (r1 == SCGUI_CANCEL) - return r; - else if (r1 != SCGUI_OK) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "scgui_ask_message returned %d\n", r1); - return SC_ERROR_INTERNAL; - } - } else - return r; - } - first_time = 0; - - if (mesg_on_screen) { - scgui_display_message(app_msg[lang], pp_msg_login_sh, pp_msg_login, - NULL, &hDlg, icon, reader_name); - } - r = priv->scr_verify_pin(priv->szPinPadDll, scr_card, pin_ref, - &scr_pin_usage, &scr_app_belpic, card_status); - if (mesg_on_screen) - scgui_remove_message(hDlg); - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SCR_Verify_PIN(): res = 0x%0x, status = %2X %2X\n", - r, card_status[0], card_status[1]); - r = belpic_pp_test_res(card, r, card_status, tries_left); - } - - return r; -} - -/* Send the change pin command to the pin pad reader + show message */ -static int belpic_pp_change(sc_card_t *card, SCR_Card * scr_card, - struct belpic_priv_data *priv, int pin_ref, int *tries_left) -{ - BYTE card_status[2]; - void *hDlg; - int first_time = 1, r = SC_ERROR_KEYPAD_PIN_MISMATCH, r1; - int lang = belpic_calculate_lang(card); - char *reader_name = card->reader->name; - - while (r == SC_ERROR_KEYPAD_PIN_MISMATCH || r == SC_ERROR_PIN_CODE_INCORRECT) { - if (!first_time) { - int r1 = SCGUI_OK; - if (r == SC_ERROR_KEYPAD_PIN_MISMATCH) { - if (!(priv->options & PP_MSG_CHANGEPIN_MISMATCH)) - return r; - r1 = scgui_ask_message(app_msg[lang], pp_msg_change_sh[lang], - pp_msg_pin_mismatch[lang], - btn_msg_retry[lang], btn_msg_cancel[lang], - reader_name); - } - if (r == SC_ERROR_PIN_CODE_INCORRECT) { - char msg[200]; - - if (!(priv->options & PP_MSG_WRONG_PIN)) - return r; - sprintf(msg, wrong_pin_msgs[lang], *tries_left); - r1 = scgui_ask_message(app_msg[lang], pp_msg_change_sh[lang], - msg, btn_msg_retry[lang], - btn_msg_cancel[lang], reader_name); - } - if (r1 == SCGUI_CANCEL) - return r; - else if (r1 != SCGUI_OK) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "scgui_ask_message returned %d\n", r1); - return SC_ERROR_INTERNAL; - } - } - first_time = 0; - - scgui_display_message(app_msg[lang], pp_msg_change_sh[lang], - pp_msg_change[lang], NULL, &hDlg, SCGUI_NO_ICON, reader_name); - r = priv->scr_change_pin(priv->szPinPadDll, scr_card, pin_ref, - &scr_app_belpic, card_status); - scgui_remove_message(hDlg); - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SCR_Change_PIN(): res = 0x%0x, status = %2X %2X\n", - r, card_status[0], card_status[1]); - r = belpic_pp_test_res(card, r, card_status, tries_left); - } - - return r; -} - -#endif /* BELPIC_PIN_PAD */ - -static int belpic_pin_cmd_usage(sc_card_t *card, struct sc_pin_cmd_data *data, - int *tries_left, int pin_usage) -{ -#ifdef BELPIC_PIN_PAD - sc_apdu_t apdu; - int r; - - struct belpic_priv_data *priv = DRVDATA(card); - int lang = belpic_calculate_lang(card); - if (card->reader->capabilities & SC_READER_CAP_PIN_PAD && priv->scr_init != NULL) { - LONG r; - SCR_Card scr_card = { - priv->pcsc_card, - lang_codes[lang], - {NULL, 0} - , - NULL - }; - - scr_app_belpic.longString = app_id_longstr[lang]; - - switch (data->cmd) { - case SC_PIN_CMD_VERIFY: - r = belpic_pp_verify(card, &scr_card, - priv, data->pin_reference, pin_usage, tries_left); - break; - case SC_PIN_CMD_CHANGE: - r = belpic_pp_change(card, &scr_card, - priv, data->pin_reference, tries_left); - break; - default: - r = SC_ERROR_NOT_SUPPORTED; - } - - if (r == SC_ERROR_AUTH_METHOD_BLOCKED && (priv->options & PP_MSG_PIN_BLOCKED)) - scgui_ask_message(app_msg[lang], " ", pin_blocked_msgs[lang], - btn_msg_close[lang], NULL, card->reader->name); - return r; - } -#endif /* BELPIC_PIN_PAD */ - data->pin1.encoding = data->pin2.encoding = BELPIC_PIN_ENCODING; data->pin1.pad_char = data->pin2.pad_char = BELPIC_PAD_CHAR; data->pin1.min_length = data->pin2.min_length = BELPIC_MIN_USER_PIN_LEN; data->pin1.max_length = data->pin2.max_length = BELPIC_MAX_USER_PIN_LEN; data->apdu = NULL; - return iso_ops->pin_cmd(card, data, tries_left); -} - -static int belpic_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) -{ - if (SSO_OK(card->ctx) && data->cmd == SC_PIN_CMD_VERIFY) - return 0; /* Don't log in right now, just say it's OK */ - else - return belpic_pin_cmd_usage(card, data, tries_left, 1); /* SCR_USAGE_AUTH = 1 */ + return iso_ops->pin_cmd(card, data); } -#ifdef HAVE_GUI - -/* Called by belpic_set_security_env() when a NonRep signature will be done, - * or by belpic-compute_signature the first fime an auth signature is done - * and the allow_sso is true - */ -static int belpic_askpin_verify(sc_card_t *card, int pin_usage) -{ - struct sc_pin_cmd_data data; - sc_apdu_t apdu; - u8 pin_data[BELPIC_MAX_USER_PIN_LEN + 1]; - int pin_len; - int tries_left; - int r; - struct belpic_priv_data *priv = DRVDATA(card); - int lang = belpic_calculate_lang(card); - char *enter_pin_msg = (pin_usage == SCR_USAGE_AUTH ? - enter_pin_msg_auth[lang] : enter_pin_msg_sign[lang]); - scgui_param_t icon = (pin_usage == SCR_USAGE_AUTH ? SCGUI_NO_ICON : SCGUI_SIGN_ICON); - - data.pin1.encoding = BELPIC_PIN_ENCODING; - data.pin1.pad_char = BELPIC_PAD_CHAR; - data.pin1.min_length = BELPIC_MIN_USER_PIN_LEN; - data.pin1.max_length = BELPIC_MAX_USER_PIN_LEN; - - data.cmd = SC_PIN_CMD_VERIFY; - data.flags = 0; - data.pin_type = SC_AC_CHV; - data.pin_reference = 1; - - -#ifdef BELPIC_PIN_PAD - /* In case of a pinpad reader */ - if (card->reader->capabilities & SC_READER_CAP_PIN_PAD && priv->scr_init != NULL) { - data.pin1.data = NULL; - data.pin1.len = 0; - - return belpic_pin_cmd_usage(card, &data, &tries_left, pin_usage); - } -#endif - - pin_len = BELPIC_MAX_USER_PIN_LEN + 1; - r = scgui_enterpin(app_msg[lang], enter_pin_msg, pin_data, &pin_len, - btn_msg_ok[lang], btn_msg_cancel[lang], wrong_pin_len_msgs[lang], icon); - if (r == SCGUI_CANCEL) - return SC_ERROR_KEYPAD_CANCELLED; - if (r != SCGUI_OK) - return SC_ERROR_INTERNAL; - - data.pin1.data = pin_data; - data.pin1.len = pin_len; - r = belpic_pin_cmd_usage(card, &data, &tries_left, pin_usage); - - /* card->ctx->allow_sso = true: we do PIN mgmnt ourselves */ - while (r == SC_ERROR_PIN_CODE_INCORRECT && SSO_OK(card->ctx)) { - int r1; - char msg[200]; - - sprintf(msg, wrong_pin_msgs[lang], tries_left); - r1 = scgui_ask_message(app_msg[lang], pin_usg_auth[lang], msg, - btn_msg_retry[lang], btn_msg_cancel[lang], - card->reader->name); - if (r1 == SCGUI_CANCEL) - return r; - else if (r1 != SCGUI_OK) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "scgui_ask_message returned %d\n", r1); - return SC_ERROR_INTERNAL; - } - - pin_len = BELPIC_MAX_USER_PIN_LEN + 1; - r = scgui_enterpin(app_msg[lang], enter_pin_msg, pin_data, &pin_len, - btn_msg_ok[lang], btn_msg_cancel[lang], wrong_pin_len_msgs[lang], - icon); - if (r == SCGUI_CANCEL) - return SC_ERROR_KEYPAD_CANCELLED; - if (r != SCGUI_OK) - return SC_ERROR_INTERNAL; - - data.pin1.data = pin_data; - data.pin1.len = pin_len; - r = belpic_pin_cmd_usage(card, &data, &tries_left, pin_usage); - if (tries_left == 0) - r = SC_ERROR_AUTH_METHOD_BLOCKED; - } - - if (r == SC_ERROR_AUTH_METHOD_BLOCKED && SSO_OK(card->ctx)) - scgui_ask_message(app_msg[lang], " ", pin_blocked_msgs[lang], - btn_msg_close[lang], NULL, card->reader->name); - - return r; -} -#endif /* HAVE_GUI */ - static int belpic_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { @@ -1377,10 +352,12 @@ static int belpic_set_security_env(sc_card_t *card, u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "belpic_set_security_env(), keyRef = 0x%0x, algo = 0x%0x\n", + if (card == NULL || env == NULL) + return SC_ERROR_INTERNAL; + + sc_log(card->ctx, "belpic_set_security_env(), keyRef = 0x%0x, algo = 0x%0lx\n", *env->key_ref, env->algorithm_flags); - assert(card != NULL && env != NULL); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); switch (env->operation) { case SC_SEC_OPERATION_SIGN: @@ -1388,14 +365,22 @@ static int belpic_set_security_env(sc_card_t *card, apdu.p2 = 0xB6; sbuf[0] = 0x04; /* length of the following data */ sbuf[1] = 0x80; /* tag for algorithm reference */ - if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) + if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01) sbuf[2] = 0x01; else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) sbuf[2] = 0x02; else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5) sbuf[2] = 0x04; + else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA256) + sbuf[2] = 0x01; + else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA384) + sbuf[2] = 0x02; + else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA512) + sbuf[2] = 0x04; + else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_RAW) + sbuf[2] = 0x40; else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Set Sec Env: unsupported algo 0X%0X\n", + sc_log(card->ctx, "Set Sec Env: unsupported algo 0X%0lX\n", env->algorithm_flags); return SC_ERROR_INVALID_ARGUMENTS; } @@ -1412,14 +397,14 @@ static int belpic_set_security_env(sc_card_t *card, apdu.resplen = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Set Security Env APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "Set Security Env APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card's Set Security Env command returned error"); + LOG_TEST_RET(card->ctx, r, "Card's Set Security Env command returned error"); /* If a NonRep signature will be done, ask to enter a PIN. It would be more * logical to put the code below into the compute signature function because - * a Verify Pin call must immediately preceed a Compute Signature call. + * a Verify Pin call must immediately precede a Compute Signature call. * It's not done because the Compute Signature is completely ISO7816 compliant * so we use the iso7816_compute_signature() function, and because this function * doesn't know about the key reference. @@ -1428,58 +413,13 @@ static int belpic_set_security_env(sc_card_t *card, * the next function to be executed will be the compute_signature function. */ if (*env->key_ref == BELPIC_KEY_REF_NONREP) { -#ifdef HAVE_GUI - r = belpic_askpin_verify(card, SCR_USAGE_SIGN); - if (r != 0 && r != SC_ERROR_KEYPAD_CANCELLED) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Verify PIN in SET command returned %d\n", r); - else - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Verify PIN in SET command returned %d\n", r); -#else - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No GUI for NonRep key present, signature cancelled\n"); + sc_log(card->ctx, "No GUI for NonRep key present, signature cancelled\n"); return SC_ERROR_NOT_SUPPORTED; -#endif } return r; } -static int belpic_compute_signature(sc_card_t *card, const u8 * data, - size_t data_len, u8 * out, size_t outlen) -{ - int r; - - r = iso_ops->compute_signature(card, data, data_len, out, outlen); - -#ifdef HAVE_GUI - if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED && SSO_OK(card->ctx)) { - r = belpic_askpin_verify(card, SCR_USAGE_AUTH); - if (r == 0) - r = iso_ops->compute_signature(card, data, data_len, out, outlen); - } -#endif - - return r; -} - -static int belpic_update_binary(sc_card_t *card, - unsigned int idx, const u8 *buf, size_t count, - unsigned long flags) -{ - int r; - - r = iso_ops->update_binary(card, idx, buf, count, flags); - -#ifdef HAVE_GUI - if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED && SSO_OK(card->ctx)) { - r = belpic_askpin_verify(card, SCR_USAGE_AUTH); - if (r == 0) - r = iso_ops->update_binary(card, idx, buf, count, flags); - } -#endif - - return r; -} - static struct sc_card_driver *sc_get_driver(void) { if (iso_ops == NULL) @@ -1487,15 +427,14 @@ static struct sc_card_driver *sc_get_driver(void) belpic_ops.match_card = belpic_match_card; belpic_ops.init = belpic_init; - belpic_ops.finish = belpic_finish; - belpic_ops.update_binary = belpic_update_binary; + belpic_ops.update_binary = iso_ops->update_binary; belpic_ops.select_file = belpic_select_file; belpic_ops.read_binary = belpic_read_binary; belpic_ops.pin_cmd = belpic_pin_cmd; belpic_ops.set_security_env = belpic_set_security_env; - belpic_ops.compute_signature = belpic_compute_signature; + belpic_ops.compute_signature = iso_ops->compute_signature; belpic_ops.get_challenge = iso_ops->get_challenge; belpic_ops.get_response = iso_ops->get_response; belpic_ops.check_sw = iso_ops->check_sw; diff --git a/src/libopensc/card-cac-common.c b/src/libopensc/card-cac-common.c new file mode 100644 index 0000000000..0e8ecc43e3 --- /dev/null +++ b/src/libopensc/card-cac-common.c @@ -0,0 +1,126 @@ +/* + * card-cac-common.c: Code shared among CAC1 and CAC2 drivers + * + * Copyright (C) 2018, Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "internal.h" +#include "iso7816.h" +#include "card-cac-common.h" + +/* default certificate labels for the CAC card */ +const char *cac_labels[MAX_CAC_SLOTS] = { + "CAC ID Certificate", + "CAC Email Signature Certificate", + "CAC Email Encryption Certificate", + "CAC Cert 4", + "CAC Cert 5", + "CAC Cert 6", + "CAC Cert 7", + "CAC Cert 8", + "CAC Cert 9", + "CAC Cert 10", + "CAC Cert 11", + "CAC Cert 12", + "CAC Cert 13", + "CAC Cert 14", + "CAC Cert 15", + "CAC Cert 16" +}; + +const char *get_cac_label(int index) +{ + if (index < 0 || index >= MAX_CAC_SLOTS) + return NULL; + + return cac_labels[index]; +} + +static int cac_list_compare_path(const void *a, const void *b) +{ + if (a == NULL || b == NULL) + return 1; + return memcmp( &((cac_object_t *) a)->path, + &((cac_object_t *) b)->path, sizeof(sc_path_t)); +} + +/* For SimCList autocopy, we need to know the size of the data elements */ +static size_t cac_list_meter(const void *el) { + return sizeof(cac_object_t); +} + +cac_private_data_t *cac_new_private_data(void) +{ + cac_private_data_t *priv; + + priv = calloc(1, sizeof(cac_private_data_t)); + if (priv == NULL) + return NULL; + + /* Initialize PKI Applets list */ + if (list_init(&priv->pki_list) != 0 || + list_attributes_comparator(&priv->pki_list, cac_list_compare_path) != 0 || + list_attributes_copy(&priv->pki_list, cac_list_meter, 1) != 0) { + cac_free_private_data(priv); + return NULL; + } + + /* Initialize General Applets List */ + if (list_init(&priv->general_list) != 0 || + list_attributes_comparator(&priv->general_list, cac_list_compare_path) != 0 || + list_attributes_copy(&priv->general_list, cac_list_meter, 1) != 0) { + cac_free_private_data(priv); + return NULL; + } + + return priv; +} + +void cac_free_private_data(cac_private_data_t *priv) +{ + free(priv->cac_id); + free(priv->cache_buf); + free(priv->aca_path); + list_destroy(&priv->pki_list); + list_destroy(&priv->general_list); + free(priv); + return; +} + +int cac_add_object_to_list(list_t *list, const cac_object_t *object) +{ + if (list_append(list, object) < 0) + return SC_ERROR_UNKNOWN; + return SC_SUCCESS; +} + diff --git a/src/libopensc/card-cac-common.h b/src/libopensc/card-cac-common.h new file mode 100644 index 0000000000..f3cbd678a6 --- /dev/null +++ b/src/libopensc/card-cac-common.h @@ -0,0 +1,89 @@ +/* + * card-cac-common.h: Code shared among CAC1 and CAC2 drivers + * + * Copyright (C) 2018, Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 + */ + +#ifndef HAVE_CARD_CAC_COMMON_H +#define HAVE_CARD_CAC_COMMON_H + +#define CAC_MAX_SIZE 4096 /* arbitrary, just needs to be 'large enough' */ + +typedef struct cac_cuid { + u8 gsc_rid[5]; + u8 manufacturer_id; + u8 card_type; + u8 card_id; +} cac_cuid_t; + +/* data structures to store meta data about CAC objects */ +typedef struct cac_object { + const char *name; + int fd; + sc_path_t path; +} cac_object_t; + +/* + * CAC private data per card state + */ +typedef struct cac_private_data { + int object_type; /* select set this so we know how to read the file */ + int cert_next; /* index number for the next certificate found in the list */ + u8 *cache_buf; /* cached version of the currently selected file */ + size_t cache_buf_len; /* length of the cached selected file */ + int cached; /* is the cached selected file valid */ + cac_cuid_t cuid; /* card unique ID from the CCC */ + u8 *cac_id; /* card serial number */ + size_t cac_id_len; /* card serial number len */ + list_t pki_list; /* list of pki containers */ + cac_object_t *pki_current; /* current pki object _ctl function */ + list_t general_list; /* list of general containers */ + cac_object_t *general_current; /* current object for _ctl function */ + sc_path_t *aca_path; /* ACA path to be selected before pin verification */ +} cac_private_data_t; + +#define CAC_DATA(card) ((cac_private_data_t*)card->drv_data) + +/* + * Set up the normal CAC paths + */ +#define CAC_1_RID "\xA0\x00\x00\x00\x79" +#define CAC_TO_AID(x) x, sizeof(x)-1 + + +#define MAX_CAC_SLOTS 16 /* Maximum number of slots is 16 now */ + +/* template for a CAC pki object */ +static const cac_object_t cac_cac_pki_obj = { + "CAC Certificate", 0x0, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, + { CAC_TO_AID(CAC_1_RID "\x01\x00") } } +}; + +/* template for emulated cuid */ +static const cac_cuid_t cac_cac_cuid = { + { 0xa0, 0x00, 0x00, 0x00, 0x79 }, + 2, 2, 0 +}; + +cac_private_data_t *cac_new_private_data(void); +void cac_free_private_data(cac_private_data_t *priv); +int cac_add_object_to_list(list_t *list, const cac_object_t *object); +const char *get_cac_label(int index); + +#endif /* HAVE_CARD_CAC_COMMON_H */ diff --git a/src/libopensc/card-cac.c b/src/libopensc/card-cac.c index 36f911ed91..3ae300bb0e 100644 --- a/src/libopensc/card-cac.c +++ b/src/libopensc/card-cac.c @@ -6,9 +6,10 @@ * Copyright (C) 2005,2006,2007,2008,2009,2010 Douglas E. Engert * Copyright (C) 2006, Identity Alliance, Thomas Harning * Copyright (C) 2007, EMC, Russell Larner - * Copyright (C) 2016, Red Hat, Inc. + * Copyright (C) 2016 - 2018, Red Hat, Inc. * * CAC driver author: Robert Relyea + * Further work: Jakub Jelen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,10 +23,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -42,37 +43,32 @@ #endif #ifdef ENABLE_OPENSSL - /* openssl only needed for card administration */ -#include -#include -#include -#include -#include +#include #endif /* ENABLE_OPENSSL */ #include "internal.h" #include "simpletlv.h" #include "cardctl.h" -#ifdef ENABLE_ZLIB -#include "compression.h" -#endif #include "iso7816.h" +#include "card-cac-common.h" +#include "pkcs15.h" -#define CAC_MAX_SIZE 4096 /* arbitrary, just needs to be 'large enough' */ /* * CAC hardware and APDU constants */ #define CAC_MAX_CHUNK_SIZE 240 -#define CAC_INS_GET_CERTIFICATE 0x36 /* CAC1 command to read a certificate */ -#define CAC_INS_SIGN_DECRYPT 0x42 /* A crypto operation */ +#define CAC_INS_SIGN_DECRYPT 0x42 /* A crypto operation */ +#define CAC_INS_READ_FILE 0x52 /* read a TL or V file */ +#define CAC_INS_GET_ACR 0x4c +#define CAC_INS_GET_PROPERTIES 0x56 #define CAC_P1_STEP 0x80 #define CAC_P1_FINAL 0x00 -#define CAC_INS_READ_FILE 0x52 /* read a TL or V file */ #define CAC_FILE_TAG 1 #define CAC_FILE_VALUE 2 /* TAGS in a TL file */ #define CAC_TAG_CERTIFICATE 0x70 #define CAC_TAG_CERTINFO 0x71 +#define CAC_TAG_MSCUID 0x72 #define CAC_TAG_CUID 0xF0 #define CAC_TAG_CC_VERSION_NUMBER 0xF1 #define CAC_TAG_GRAMMAR_VERION_NUMBER 0xF2 @@ -86,9 +82,28 @@ #define CAC_TAG_STATUS_TUPLES 0xFC #define CAC_TAG_NEXT_CCC 0xFD #define CAC_TAG_ERROR_CODES 0xFE -#define CAC_APP_TYPE_GENERAL 0x01 -#define CAC_APP_TYPE_SKI 0x02 -#define CAC_APP_TYPE_PKI 0x04 +#define CAC_TAG_APPLET_FAMILY 0x01 +#define CAC_TAG_NUMBER_APPLETS 0x94 +#define CAC_TAG_APPLET_ENTRY 0x93 +#define CAC_TAG_APPLET_AID 0x92 +#define CAC_TAG_APPLET_INFORMATION 0x01 +#define CAC_TAG_NUMBER_OF_OBJECTS 0x40 +#define CAC_TAG_TV_BUFFER 0x50 +#define CAC_TAG_PKI_OBJECT 0x51 +#define CAC_TAG_OBJECT_ID 0x41 +#define CAC_TAG_BUFFER_PROPERTIES 0x42 +#define CAC_TAG_PKI_PROPERTIES 0x43 + +#define CAC_APP_TYPE_GENERAL 0x01 +#define CAC_APP_TYPE_SKI 0x02 +#define CAC_APP_TYPE_PKI 0x04 + +#define CAC_ACR_ACR 0x00 +#define CAC_ACR_APPLET_OBJECT 0x10 +#define CAC_ACR_AMP 0x20 +#define CAC_ACR_SERVICE 0x21 + +#define CAC_MAX_CCC_DEPTH 16 /* hardware data structures (returned in the CCC) */ /* part of the card_url */ @@ -127,19 +142,21 @@ typedef struct cac_card_url { u8 keyCryptoAlgorithm; /* not used for VM cards */ } cac_card_url_t; -typedef struct cac_cuid { - u8 gsc_rid[5]; - u8 manufacturer_id; - u8 card_type; - u8 card_id; -} cac_cuid_t; +#define CAC_MAX_OBJECTS 16 + +typedef struct { + /* OID has two bytes */ + unsigned char oid[2]; + /* Format is NOT SimpleTLV? */ + unsigned char simpletlv; + /* Is certificate object and private key is initialized */ + unsigned char privatekey; +} cac_properties_object_t; -/* data structures to store meta data about CAC objects */ -typedef struct cac_object { - const char *name; - int fd; - sc_path_t path; -} cac_object_t; +typedef struct { + size_t num_objects; + cac_properties_object_t objects[CAC_MAX_OBJECTS]; +} cac_properties_t; /* * Flags for Current Selected Object Type @@ -152,137 +169,45 @@ typedef struct cac_object { */ #define CAC_OBJECT_TYPE_CERT 1 #define CAC_OBJECT_TYPE_TLV_FILE 4 - -/* - * CAC private data per card state - */ -typedef struct cac_private_data { - int object_type; /* select set this so we know how to read the file */ - int cert_next; /* index number for the next certificate found in the list */ - u8 *cache_buf; /* cached version of the currently selected file */ - size_t cache_buf_len; /* length of the cached selected file */ - int cached; /* is the cached selected file valid */ - cac_cuid_t cuid; /* card unique ID from the CCC */ - u8 *cac_id; /* card serial number */ - size_t cac_id_len; /* card serial number len */ - list_t pki_list; /* list of pki containers */ - cac_object_t *pki_current; /* current pki object _ctl function */ - list_t general_list; /* list of general containers */ - cac_object_t *general_current; /* current object for _ctl function */ -} cac_private_data_t; - -#define CAC_DATA(card) ((cac_private_data_t*)card->drv_data) - -int cac_list_compare_path(const void *a, const void *b) -{ - if (a == NULL || b == NULL) - return 1; - return memcmp( &((cac_object_t *) a)->path, - &((cac_object_t *) b)->path, sizeof(sc_path_t)); -} - -/* For SimCList autocopy, we need to know the size of the data elements */ -size_t cac_list_meter(const void *el) { - return sizeof(cac_object_t); -} - -static cac_private_data_t *cac_new_private_data(void) -{ - cac_private_data_t *priv; - priv = calloc(1, sizeof(cac_private_data_t)); - if (!priv) - return NULL; - list_init(&priv->pki_list); - list_attributes_comparator(&priv->pki_list, cac_list_compare_path); - list_attributes_copy(&priv->pki_list, cac_list_meter, 1); - list_init(&priv->general_list); - list_attributes_comparator(&priv->general_list, cac_list_compare_path); - list_attributes_copy(&priv->general_list, cac_list_meter, 1); - /* set other fields as appropriate */ - - return priv; -} - -static void cac_free_private_data(cac_private_data_t *priv) -{ - free(priv->cac_id); - free(priv->cache_buf); - list_destroy(&priv->pki_list); - list_destroy(&priv->general_list); - free(priv); - return; -} - -static int cac_add_object_to_list(list_t *list, const cac_object_t *object) -{ - if (list_append(list, object) < 0) - return SC_ERROR_UNKNOWN; - return SC_SUCCESS; -} +#define CAC_OBJECT_TYPE_GENERIC 5 /* * Set up the normal CAC paths */ -#define CAC_TO_AID(x) x, sizeof(x)-1 - #define CAC_2_RID "\xA0\x00\x00\x01\x16" -#define CAC_1_RID "\xA0\x00\x00\x00\x79" -#define CAC_1_CM_AID "\xA0\x00\x00\x00\x30\x00\00" -static const sc_path_t cac_CCC_Path = { +static const sc_path_t cac_ACA_Path = { "", 0, 0,0,SC_PATH_TYPE_DF_NAME, - { CAC_TO_AID(CAC_2_RID "\xDB\x00") } + { CAC_TO_AID(CAC_1_RID "\x10\x00") } }; -#define MAX_CAC_SLOTS 10 /* arbitrary, just needs to be 'large enough' */ -/* default certificate labels for the CAC card */ -static const char *cac_labels[MAX_CAC_SLOTS] = { - "CAC ID Certificate", - "CAC Email Signature Certificate", - "CAC Email Encryption Certificate", - "CAC Cert 3", - "CAC Cert 4", - "CAC Cert 5", - "CAC Cert 6", - "CAC Cert 7", - "CAC Cert 8", - "CAC Cert 9" -}; - -/* template for a cac1 pki object */ -static const cac_object_t cac_cac1_pki_obj = { - "CAC Certificate", 0x0, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, - { CAC_TO_AID(CAC_1_RID "\x01\x00") } } -}; - -/* template for cac1 cuid */ -static const cac_cuid_t cac_cac1_cuid = { - { 0xa0, 0x00, 0x00, 0x00, 0x79 }, - 2, 2, 0 +static const sc_path_t cac_CCC_Path = { + "", 0, + 0,0,SC_PATH_TYPE_DF_NAME, + { CAC_TO_AID(CAC_2_RID "\xDB\x00") } }; /* - * CAC-1 general objectes defined in 4.3.1.2 of CAC Applet Developer Guide Version 1.0. + * CAC general objects defined in 4.3.1.2 of CAC Applet Developer Guide Version 1.0. * doubles as a source for CAC-2 labels. */ -static const cac_object_t cac_1_objects[] = { +static const cac_object_t cac_objects[] = { { "Person Instance", 0x200, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, { CAC_TO_AID(CAC_1_RID "\x02\x00") }}}, { "Personnel", 0x201, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, { CAC_TO_AID(CAC_1_RID "\x02\x01") }}}, { "Benefits", 0x202, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, { CAC_TO_AID(CAC_1_RID "\x02\x02") }}}, - { "Other Benefits", 0x202, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, - { CAC_TO_AID(CAC_1_RID "\x02\x02") }}}, + { "Other Benefits", 0x203, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, + { CAC_TO_AID(CAC_1_RID "\x02\x03") }}}, { "PKI Credential", 0x2FD, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, { CAC_TO_AID(CAC_1_RID "\x02\xFD") }}}, { "PKI Certificate", 0x2FE, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, { CAC_TO_AID(CAC_1_RID "\x02\xFE") }}}, }; -static const int cac_1_object_count = sizeof(cac_1_objects)/sizeof(cac_1_objects[0]); - +static const int cac_object_count = sizeof(cac_objects)/sizeof(cac_objects[0]); /* * use the object id to find our object info on the object in our CAC-1 list @@ -291,9 +216,9 @@ static const cac_object_t *cac_find_obj_by_id(unsigned short object_id) { int i; - for (i=0; i < cac_1_object_count; i++) { - if (cac_1_objects[i].fd == object_id) { - return &cac_1_objects[i]; + for (i = 0; i < cac_object_count; i++) { + if (cac_objects[i].fd == object_id) { + return &cac_objects[i]; } } return NULL; @@ -304,7 +229,7 @@ static const cac_object_t *cac_find_obj_by_id(unsigned short object_id) */ static int cac_is_cert(cac_private_data_t * priv, const sc_path_t *in_path) { - cac_object_t test_obj; + cac_object_t test_obj = {0}; test_obj.path = *in_path; test_obj.path.index = 0; test_obj.path.count = 0; @@ -319,7 +244,7 @@ static int cac_is_cert(cac_private_data_t * priv, const sc_path_t *in_path) * an internal 4096 byte buffer is used, and a copy is returned to the * caller. that need to be freed by the caller. * - * modelled after a similiar function in card-piv.c + * modelled after a similar function in card-piv.c */ static int cac_apdu_io(sc_card_t *card, int ins, int p1, int p2, @@ -327,7 +252,7 @@ static int cac_apdu_io(sc_card_t *card, int ins, int p1, int p2, size_t * recvbuflen) { int r; - sc_apdu_t apdu; + sc_apdu_t apdu = {0}; u8 rbufinitbuf[CAC_MAX_SIZE]; u8 *rbuf; size_t rbuflen; @@ -336,7 +261,7 @@ static int cac_apdu_io(sc_card_t *card, int ins, int p1, int p2, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "%02x %02x %02x %"SC_FORMAT_LEN_SIZE_T"u : %"SC_FORMAT_LEN_SIZE_T"u %"SC_FORMAT_LEN_SIZE_T"u\n", ins, p1, p2, sendbuflen, card->max_send_size, card->max_recv_size); @@ -375,27 +300,25 @@ static int cac_apdu_io(sc_card_t *card, int ins, int p1, int p2, apdu.resplen = 0; } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "calling sc_transmit_apdu flags=%lx le=%"SC_FORMAT_LEN_SIZE_T"u, resplen=%"SC_FORMAT_LEN_SIZE_T"u, resp=%p", apdu.flags, apdu.le, apdu.resplen, apdu.resp); /* with new adpu.c and chaining, this actually reads the whole object */ r = sc_transmit_apdu(card, &apdu); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "result r=%d apdu.resplen=%"SC_FORMAT_LEN_SIZE_T"u sw1=%02x sw2=%02x", r, apdu.resplen, apdu.sw1, apdu.sw2); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Transmit failed"); + sc_log(card->ctx, "Transmit failed"); goto err; } - if (apdu.sw1 == 0x61) { - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - } + r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Card returned error "); + sc_log(card->ctx, "Card returned error "); goto err; } @@ -409,11 +332,53 @@ static int cac_apdu_io(sc_card_t *card, int ins, int p1, int p2, memcpy(*recvbuf, rbuf, apdu.resplen); } *recvbuflen = apdu.resplen; - r = *recvbuflen; + r = (int)*recvbuflen; } err: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); +} + +/* + * Get ACR of currently ACA applet identified by the acr_type + * 5.3.3.5 Get ACR APDU + */ +static int +cac_get_acr(sc_card_t *card, int acr_type, u8 **out_buf, size_t *out_len) +{ + u8 *out = NULL; + /* XXX assuming it will not be longer than 255 B */ + size_t len = 256; + int r; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* for simplicity we support only ACR without arguments now */ + if (acr_type != 0x00 && acr_type != 0x10 + && acr_type != 0x20 && acr_type != 0x21) { + return SC_ERROR_INVALID_ARGUMENTS; + } + + r = cac_apdu_io(card, CAC_INS_GET_ACR, acr_type, 0, NULL, 0, &out, &len); + if (len == 0) { + r = SC_ERROR_FILE_NOT_FOUND; + } + if (r < 0) + goto fail; + + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "got %"SC_FORMAT_LEN_SIZE_T"u bytes out=%p", len, out); + + *out_len = len; + *out_buf = out; + return SC_SUCCESS; + +fail: + if (out) + free(out); + *out_buf = NULL; + *out_len = 0; + return r; } /* @@ -424,15 +389,17 @@ static int cac_apdu_io(sc_card_t *card, int ins, int p1, int p2, static int cac_read_file(sc_card_t *card, int file_type, u8 **out_buf, size_t *out_len) { u8 params[2]; - u8 count[2]; + u8 count[2] = {0}; u8 *out = NULL; - u8 *out_ptr; + u8 *out_ptr = NULL; size_t offset = 0; size_t size = 0; size_t left = 0; - size_t len; + size_t len = 0; int r; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + params[0] = file_type; params[1] = 2; @@ -440,6 +407,9 @@ static int cac_read_file(sc_card_t *card, int file_type, u8 **out_buf, size_t *o len = sizeof(count); out_ptr = count; r = cac_apdu_io(card, CAC_INS_READ_FILE, 0, 0, ¶ms[0], sizeof(params), &out_ptr, &len); + if (len == 0) { + r = SC_ERROR_FILE_NOT_FOUND; + } if (r < 0) goto fail; @@ -457,105 +427,24 @@ static int cac_read_file(sc_card_t *card, int file_type, u8 **out_buf, size_t *o params[1] = len; r = cac_apdu_io(card, CAC_INS_READ_FILE, HIGH_BYTE_OF_SHORT(offset), LOW_BYTE_OF_SHORT(offset), ¶ms[0], sizeof(params), &out_ptr, &len); + /* if there is no data, assume there is no file */ + if (len == 0) { + r = SC_ERROR_FILE_NOT_FOUND; + } if (r < 0) { goto fail; } } *out_len = size; *out_buf = out; - return SC_SUCCESS; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); fail: if (out) free(out); *out_len = 0; - return r; + LOG_FUNC_RETURN(card->ctx, r); } -/* - * OLD cac read certificate, only use with CAC-1 card. - */ -static int cac_cac1_get_certificate(sc_card_t *card, u8 **out_buf, size_t *out_len) -{ - u8 buf[CAC_MAX_SIZE]; - u8 *out_ptr; - size_t size = 0; - size_t left = 0; - size_t len, next_len; - sc_apdu_t apdu; - int r = SC_SUCCESS; - - - /* get the size */ - size = left = *out_buf ? *out_len : sizeof(buf); - out_ptr = *out_buf ? *out_buf : buf; - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, CAC_INS_GET_CERTIFICATE, 0, 0 ); - next_len = MIN(left, 100); - for (; left > 0; left -= len, out_ptr += len) { - len = next_len; - apdu.resp = out_ptr; - apdu.le = len; - apdu.resplen = left; - - r = sc_transmit_apdu(card, &apdu); - if (r < 0) { - break; - } - /* in the old CAC-1, 0x63 means 'more data' in addition to 'pin failed' */ - if (apdu.sw1 != 0x63) { - /* we've either finished reading, or hit an error, break */ - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - left -= len; - break; - } - next_len = MIN(left,apdu.sw2); - } - if (r < 0) { - return r; - } - r = size - left; - if (*out_buf == NULL) { - *out_buf = malloc(r); - if (*out_buf == NULL) { - return SC_ERROR_OUT_OF_MEMORY; - } - memcpy(*out_buf, buf, r); - } - *out_len = r; - return r; -} - -/* Create a fake tag/length file in Simple TLV for cac1 cards based on the val_len. - */ -int cac_cac1_get_cert_tag(sc_card_t *card, size_t val_len, u8 **tlp, size_t *tl_len_p) -{ - static const u8 cac_cac1_cert_tag[] = { CAC_TAG_CERTINFO, 1, CAC_TAG_CERTIFICATE, 0xff, 0, 0 }; - u8 *tl, *tl1, *tl2; - size_t tl_len; - int rv = SC_SUCCESS; - - tl_len = sizeof(cac_cac1_cert_tag); - tl = malloc(tl_len); - if (tl == NULL) - return SC_ERROR_OUT_OF_MEMORY; - memcpy(tl, cac_cac1_cert_tag, tl_len); - - rv = sc_simpletlv_put_tag(CAC_TAG_CERTINFO, 1, tl, tl_len, &tl1); - if (rv != SC_SUCCESS) - goto failure; - - val_len -= 1; /* one byte is CERTINFO Value */ - tl_len -= (tl1 - tl); - rv = sc_simpletlv_put_tag(CAC_TAG_CERTIFICATE, val_len, tl1, tl_len, &tl2); - if (rv != SC_SUCCESS) - goto failure; - - *tlp = tl; - *tl_len_p = (tl2 - tl); - return SC_SUCCESS; -failure: - free(tl); - return rv; -} /* * Callers of this may be expecting a certificate, @@ -563,14 +452,15 @@ int cac_cac1_get_cert_tag(sc_card_t *card, size_t val_len, u8 **tlp, size_t *tl_ * as well as set that we want the cert from the object. */ static int cac_read_binary(sc_card_t *card, unsigned int idx, - unsigned char *buf, size_t count, unsigned long flags) + unsigned char *buf, size_t count, unsigned long *flags) { cac_private_data_t * priv = CAC_DATA(card); int r = 0; u8 *tl = NULL, *val = NULL; - u8 *tl_ptr, *val_ptr, *tlv_ptr, *tl_start; - u8 *cert_ptr; - size_t tl_len, val_len, tlv_len; + const u8 *tl_ptr, *val_ptr, *tl_start; + u8 *tlv_ptr; + const u8 *cert_ptr; + size_t tl_len = 0, val_len = 0, tlv_len; size_t len, tl_head_len, cert_len; u8 cert_type, tag; @@ -578,18 +468,18 @@ static int cac_read_binary(sc_card_t *card, unsigned int idx, /* if we didn't return it all last time, return the remainder */ if (priv->cached) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "returning cached value idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u", idx, count); if (idx > priv->cache_buf_len) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_END_REACHED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_END_REACHED); } len = MIN(count, priv->cache_buf_len-idx); memcpy(buf, &priv->cache_buf[idx], len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, len); + LOG_FUNC_RETURN(card->ctx, (int)len); } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "clearing cache idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u", idx, count); if (priv->cache_buf) { @@ -600,29 +490,17 @@ static int cac_read_binary(sc_card_t *card, unsigned int idx, if (priv->object_type <= 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); - - if ((card->type == SC_CARD_TYPE_CAC_I) && (priv->object_type == CAC_OBJECT_TYPE_CERT)) { - /* SPICE smart card emulator only presents CAC-1 cards with the old CAC-1 interface as - * certs. If we are a cac 1 card, use the old interface */ - r = cac_cac1_get_certificate(card, &val, &val_len); - if (r < 0) - goto done; - - r = cac_cac1_get_cert_tag(card, val_len, &tl, &tl_len); - if (r < 0) - goto done; - } else { - r = cac_read_file(card, CAC_FILE_TAG, &tl, &tl_len); - if (r < 0) { - goto done; - } + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); - r = cac_read_file(card, CAC_FILE_VALUE, &val, &val_len); - if (r < 0) - goto done; + r = cac_read_file(card, CAC_FILE_TAG, &tl, &tl_len); + if (r < 0) { + goto done; } + r = cac_read_file(card, CAC_FILE_VALUE, &val, &val_len); + if (r < 0) + goto done; + switch (priv->object_type) { case CAC_OBJECT_TYPE_TLV_FILE: tlv_len = tl_len + val_len; @@ -633,12 +511,13 @@ static int cac_read_binary(sc_card_t *card, unsigned int idx, } priv->cache_buf_len = tlv_len; - for (tl_ptr = tl, val_ptr=val, tlv_ptr = priv->cache_buf; - tl_len > 2 && val_len > 0 && tlv_len > 0; + for (tl_ptr = tl, val_ptr = val, tlv_ptr = priv->cache_buf; + tl_len >= 2 && tlv_len > 0; val_len -= len, tlv_len -= len, val_ptr += len, tlv_ptr += len) { /* get the tag and the length */ tl_start = tl_ptr; - if (sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len) != SC_SUCCESS) + r = sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len); + if (r != SC_SUCCESS && r != SC_ERROR_TLV_END_OF_CONTENTS) break; tl_head_len = (tl_ptr - tl_start); sc_simpletlv_put_tag(tag, len, tlv_ptr, tlv_len, &tlv_ptr); @@ -647,6 +526,8 @@ static int cac_read_binary(sc_card_t *card, unsigned int idx, /* don't crash on bad data */ if (val_len < len) { + sc_log(card->ctx, "Received too long value %"SC_FORMAT_LEN_SIZE_T"u, " + "while only %"SC_FORMAT_LEN_SIZE_T"u left. Truncating", len, val_len); len = val_len; } /* if we run out of return space, truncate */ @@ -655,23 +536,33 @@ static int cac_read_binary(sc_card_t *card, unsigned int idx, } memcpy(tlv_ptr, val_ptr, len); } + /* fixup the cache_buf_le to reflect for the bad data ignored above */ + priv->cache_buf_len = (tlv_ptr - priv->cache_buf); break; case CAC_OBJECT_TYPE_CERT: /* read file */ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, " obj= cert_file, val_len=%"SC_FORMAT_LEN_SIZE_T"u (0x%04"SC_FORMAT_LEN_SIZE_T"x)", val_len, val_len); cert_len = 0; cert_ptr = NULL; cert_type = 0; - tl_head_len = 2; - for (tl_ptr = tl, val_ptr=val; tl_len >= 2; - val_len -= len, val_ptr += len, tl_len -= tl_head_len) { + for (tl_ptr = tl, val_ptr = val; tl_len >= 2; + val_len -= len, val_ptr += len, tl_len -= tl_head_len) { tl_start = tl_ptr; - if (sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len) != SC_SUCCESS) + r = sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len); + if (r != SC_SUCCESS && r != SC_ERROR_TLV_END_OF_CONTENTS) break; tl_head_len = tl_ptr - tl_start; + + /* incomplete value */ + if (val_len < len) { + sc_log(card->ctx, "Read incomplete value %"SC_FORMAT_LEN_SIZE_T"u, " + "while only %"SC_FORMAT_LEN_SIZE_T"u left", len, val_len); + break; + } + if (tag == CAC_TAG_CERTIFICATE) { cert_len = len; cert_ptr = val_ptr; @@ -681,22 +572,15 @@ static int cac_read_binary(sc_card_t *card, unsigned int idx, cert_type = *val_ptr; } } - if ((val_len < len) || (tl_len < tl_head_len)) { - break; + if (tag == CAC_TAG_MSCUID) { + sc_log_hex(card->ctx, "MSCUID", val_ptr, len); } } /* if the info byte is 1, then the cert is compressed, decompress it */ - if ((cert_type & 0x3) == 1) { -#ifdef ENABLE_ZLIB - r = sc_decompress_alloc(&priv->cache_buf, &priv->cache_buf_len, - cert_ptr, cert_len, COMPRESSION_AUTO); -#else - sc_log(card->ctx, "CAC compression not supported, no zlib"); - r = SC_ERROR_NOT_SUPPORTED; -#endif - if (r) - goto done; - } else if (cert_len > 0) { + if ((cert_type & 0x3) == 1 && flags) { + *flags |= SC_FILE_FLAG_COMPRESSED_AUTO; + } + if (cert_len > 0) { priv->cache_buf = malloc(cert_len); if (priv->cache_buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; @@ -709,8 +593,14 @@ static int cac_read_binary(sc_card_t *card, unsigned int idx, goto done; } break; + case CAC_OBJECT_TYPE_GENERIC: + /* TODO + * We have some two buffers in unknown encoding that we + * need to present in PKCS#15 layer. + */ default: /* Unknown object type */ + sc_log(card->ctx, "Unknown object type: %x", priv->object_type); r = SC_ERROR_INTERNAL; goto done; } @@ -719,22 +609,13 @@ static int cac_read_binary(sc_card_t *card, unsigned int idx, priv->cached = 1; len = MIN(count, priv->cache_buf_len-idx); memcpy(buf, &priv->cache_buf[idx], len); - r = len; + r = (int)len; done: if (tl) free(tl); if (val) free(val); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - -/* CAC driver is read only */ -static int cac_write_binary(sc_card_t *card, unsigned int idx, - const u8 *buf, size_t count, unsigned long flags) -{ - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(card->ctx, r); } /* initialize getting a list and return the number of elements in the list */ @@ -775,19 +656,29 @@ static int cac_get_serial_nr_from_CUID(sc_card_t* card, sc_serial_number_t* seri { cac_private_data_t * priv = CAC_DATA(card); - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); if (card->serialnr.len) { *serial = card->serialnr; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } if (priv->cac_id_len) { serial->len = MIN(priv->cac_id_len, SC_MAX_SERIALNR); - memcpy(serial->value, priv->cac_id, priv->cac_id_len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + memcpy(serial->value, priv->cac_id, serial->len); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND); } +static int cac_get_ACA_path(sc_card_t *card, sc_path_t *path) +{ + cac_private_data_t * priv = CAC_DATA(card); + + LOG_FUNC_CALLED(card->ctx); + if (priv->aca_path) { + *path = *priv->aca_path; + } + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} static int cac_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { @@ -800,6 +691,8 @@ static int cac_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } switch(cmd) { + case SC_CARDCTL_CAC_GET_ACA_PATH: + return cac_get_ACA_path(card, (sc_path_t *) ptr); case SC_CARDCTL_GET_SERIALNR: return cac_get_serial_nr_from_CUID(card, (sc_serial_number_t *) ptr); case SC_CARDCTL_CAC_INIT_GET_GENERIC_OBJECTS: @@ -821,42 +714,23 @@ static int cac_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) static int cac_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { + /* CAC requires 8 byte response */ u8 rbuf[8]; - u8 *rbufp = NULL; - size_t rbuflen = 0; + u8 *rbufp = &rbuf[0]; + size_t out_len = sizeof rbuf; int r; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "challenge len=%"SC_FORMAT_LEN_SIZE_T"u", len); - - r = sc_lock(card); - if (r != SC_SUCCESS) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_CALLED(card->ctx); + r = cac_apdu_io(card, 0x84, 0x00, 0x00, NULL, 0, &rbufp, &out_len); + LOG_TEST_RET(card->ctx, r, "Could not get challenge"); - /* CAC requires 8 byte response */ - while (len > 0) { - size_t n; - - rbufp = &rbuf[0]; - rbuflen = sizeof(rbuf); - r = cac_apdu_io(card, 0x84, 0x00, 0x00, NULL, 0, &rbufp, &rbuflen); - if (r < 0) { - sc_unlock(card); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); - } - n = len > rbuflen ? rbuflen : len; - memcpy(rnd, rbufp, n); - len -= n; - rnd += n; + if (len < out_len) { + out_len = len; } + memcpy(rnd, rbuf, out_len); - r = sc_unlock(card); - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); - + LOG_FUNC_RETURN(card->ctx, (int) out_len); } static int cac_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) @@ -865,8 +739,8 @@ static int cac_set_security_env(sc_card_t *card, const sc_security_env_t *env, i SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "flags=%08lx op=%d alg=%d algf=%08x algr=%08x kr0=%02x, krfl=%"SC_FORMAT_LEN_SIZE_T"u\n", + sc_log(card->ctx, + "flags=%08lx op=%d alg=%lu algf=%08lx algr=%08lx kr0=%02x, krfl=%"SC_FORMAT_LEN_SIZE_T"u\n", env->flags, env->operation, env->algorithm, env->algorithm_flags, env->algorithm_ref, env->key_ref[0], env->key_ref_len); @@ -884,7 +758,7 @@ static int cac_restore_security_env(sc_card_t *card, int se_num) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -897,7 +771,7 @@ static int cac_rsa_op(sc_card_t *card, size_t rbuflen, outplen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "datalen=%"SC_FORMAT_LEN_SIZE_T"u outlen=%"SC_FORMAT_LEN_SIZE_T"u\n", datalen, outlen); @@ -910,7 +784,7 @@ static int cac_rsa_op(sc_card_t *card, * different sets of APDU's that need to be called), so this call is really a little bit of paranoia */ r = sc_lock(card); if (r != SC_SUCCESS) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); rbuf = NULL; @@ -922,8 +796,8 @@ static int cac_rsa_op(sc_card_t *card, break; } if (rbuflen != 0) { - int n = MIN(rbuflen, outplen); - memcpy(outp,rbuf, n); + size_t n = MIN(rbuflen, outplen); + memcpy(outp, rbuf, n); outp += n; outplen -= n; } @@ -941,14 +815,14 @@ static int cac_rsa_op(sc_card_t *card, goto err; } if (rbuflen != 0) { - int n = MIN(rbuflen, outplen); - memcpy(outp,rbuf, n); - outp += n; + size_t n = MIN(rbuflen, outplen); + memcpy(outp, rbuf, n); + /*outp += n; unused */ outplen -= n; } free(rbuf); rbuf = NULL; - r = outlen-outplen; + r = (int)(outlen - outplen); err: sc_unlock(card); @@ -959,7 +833,7 @@ static int cac_rsa_op(sc_card_t *card, free(rbuf); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } static int cac_compute_signature(sc_card_t *card, @@ -980,6 +854,189 @@ static int cac_decipher(sc_card_t *card, SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, cac_rsa_op(card, data, datalen, out, outlen)); } +static int cac_parse_properties_object(sc_card_t *card, u8 type, + const u8 *data, size_t data_len, cac_properties_object_t *object) +{ + size_t len; + const u8 *val, *val_end; + u8 tag; + int parsed = 0; + + if (data_len < 11) + return -1; + + /* Initialize: non-PKI applet */ + object->privatekey = 0; + + val = data; + val_end = data + data_len; + for (; val < val_end; val += len) { + /* get the tag and the length */ + if (sc_simpletlv_read_tag(&val, val_end - val, &tag, &len) != SC_SUCCESS) + break; + + switch (tag) { + case CAC_TAG_OBJECT_ID: + if (len != 2) { + sc_log(card->ctx, "TAG: Object ID: " + "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); + break; + } + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "TAG: Object ID = 0x%02x 0x%02x", val[0], val[1]); + memcpy(&object->oid, val, 2); + parsed++; + break; + + case CAC_TAG_BUFFER_PROPERTIES: + if (len != 5) { + sc_log(card->ctx, "TAG: Buffer Properties: " + "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); + break; + } + /* First byte is "Type of Tag Supported" */ + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "TAG: Buffer Properties: Type of Tag Supported = 0x%02x", + val[0]); + object->simpletlv = val[0]; + parsed++; + break; + + case CAC_TAG_PKI_PROPERTIES: + /* 4th byte is "Private Key Initialized" */ + if (len != 4) { + sc_log(card->ctx, "TAG: PKI Properties: " + "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); + break; + } + if (type != CAC_TAG_PKI_OBJECT) { + sc_log(card->ctx, "TAG: PKI Properties outside of PKI Object"); + break; + } + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "TAG: PKI Properties: Private Key Initialized = 0x%02x", + val[2]); + object->privatekey = val[2]; + parsed++; + break; + + default: + /* ignore tags we don't understand */ + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "TAG: Unknown (0x%02x)",tag ); + break; + } + } + if (parsed < 2) + return SC_ERROR_INVALID_DATA; + + return SC_SUCCESS; +} + +static int cac_get_properties(sc_card_t *card, cac_properties_t *prop) +{ + u8 *rbuf = NULL; + size_t rbuflen = 0, len; + const u8 *val, *val_end; + u8 tag; + size_t i = 0; + int r; + prop->num_objects = 0; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + r = cac_apdu_io(card, CAC_INS_GET_PROPERTIES, 0x01, 0x00, NULL, 0, + &rbuf, &rbuflen); + if (r < 0) + return r; + + val = rbuf; + val_end = val + rbuflen; + for (; val < val_end; val += len) { + /* get the tag and the length */ + if (sc_simpletlv_read_tag(&val, val_end - val, &tag, &len) != SC_SUCCESS) + break; + + switch (tag) { + case CAC_TAG_APPLET_INFORMATION: + if (len != 5) { + sc_log(card->ctx, "TAG: Applet Information: " + "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); + break; + } + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "TAG: Applet Information: Family: 0x%0x", val[0]); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + " Applet Version: 0x%02x 0x%02x 0x%02x 0x%02x", + val[1], val[2], val[3], val[4]); + break; + + case CAC_TAG_NUMBER_OF_OBJECTS: + if (len != 1) { + sc_log(card->ctx, "TAG: Num objects: " + "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); + break; + } + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "TAG: Num objects = %hhd", *val); + /* make sure we do not overrun buffer */ + prop->num_objects = MIN(val[0], CAC_MAX_OBJECTS); + break; + + case CAC_TAG_TV_BUFFER: + if (len != 17) { + sc_log(card->ctx, "TAG: TV Object: " + "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); + break; + } + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "TAG: TV Object nr. %"SC_FORMAT_LEN_SIZE_T"u", i); + if (i >= CAC_MAX_OBJECTS) { + free(rbuf); + return SC_SUCCESS; + } + + if (cac_parse_properties_object(card, tag, val, len, + &prop->objects[i]) == SC_SUCCESS) + i++; + break; + + case CAC_TAG_PKI_OBJECT: + if (len != 17) { + sc_log(card->ctx, "TAG: PKI Object: " + "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); + break; + } + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "TAG: PKI Object nr. %"SC_FORMAT_LEN_SIZE_T"u", i); + if (i >= CAC_MAX_OBJECTS) { + free(rbuf); + return SC_SUCCESS; + } + + if (cac_parse_properties_object(card, tag, val, len, + &prop->objects[i]) == SC_SUCCESS) + i++; + break; + + default: + /* ignore tags we don't understand */ + sc_log(card->ctx, "TAG: Unknown (0x%02x), len=%" + SC_FORMAT_LEN_SIZE_T"u", tag, len); + break; + } + } + free(rbuf); + /* sanity */ + if (i != prop->num_objects) + sc_log(card->ctx, "The announced number of objects (%zu) " + "did not match reality (%"SC_FORMAT_LEN_SIZE_T"u)", + prop->num_objects, i); + prop->num_objects = i; + + return SC_SUCCESS; +} + /* * CAC cards use SC_PATH_SELECT_OBJECT_ID rather than SC_PATH_SELECT_FILE_ID. In order to use more * of the PKCS #15 structure, we call the selection SC_PATH_SELECT_FILE_ID, but we set p1 to 2 instead @@ -988,17 +1045,21 @@ static int cac_decipher(sc_card_t *card, * * The rest is just copied from iso7816_select_file */ -static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out, int type) +static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { struct sc_context *ctx; struct sc_apdu apdu; unsigned char buf[SC_MAX_APDU_BUFFER_SIZE]; unsigned char pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; - int r, pathlen, pathtype; + int r, pathtype; + size_t pathlen; struct sc_file *file = NULL; - cac_private_data_t * priv = CAC_DATA(card); + cac_private_data_t *priv; + + if (card == NULL || in_path == NULL) + return SC_ERROR_INTERNAL; - assert(card != NULL && in_path != NULL); + priv = CAC_DATA(card); ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); @@ -1008,24 +1069,21 @@ static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc pathtype = in_path->type; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, - "path->aid=%x %x %x %x %x %x %x len=%"SC_FORMAT_LEN_SIZE_T"u, path->value = %x %x %x %x len=%"SC_FORMAT_LEN_SIZE_T"u path->type=%d (%x)", - in_path->aid.value[0], in_path->aid.value[1], - in_path->aid.value[2], in_path->aid.value[3], - in_path->aid.value[4], in_path->aid.value[5], - in_path->aid.value[6], in_path->aid.len, in_path->value[0], - in_path->value[1], in_path->value[2], in_path->value[3], - in_path->len, in_path->type, in_path->type); + "path=%s, path->value=%s path->type=%d (%x)", + sc_print_path(in_path), + sc_dump_hex(in_path->value, in_path->len), + in_path->type, in_path->type); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "file_out=%p index=%d count=%d\n", - file_out, in_path->index, in_path->count); + file_out, in_path->index, in_path->count); - /* Sigh, sc_key_select expects paths to keys to have specific formats. There is no override. - * we have to add some bytes to the path to make it happy. A better fix would be to give sc_key_file - * a flag that says 'no, really this path is fine'. We only need to do this for private keys */ + /* Sigh, iso7816_select_file expects paths to keys to have specific + * formats. There is no override. We have to add some bytes to the + * path to make it happy. + * We only need to do this for private keys. + */ if ((pathlen > 2) && (pathlen <= 4) && memcmp(path, "\x3F\x00", 2) == 0) { - if (pathlen > 2) { - path += 2; - pathlen -= 2; - } + path += 2; + pathlen -= 2; } @@ -1034,11 +1092,12 @@ static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc * and object type here: */ if (priv) { /* don't record anything if we haven't been initialized yet */ - priv->object_type = CAC_OBJECT_TYPE_TLV_FILE; + priv->object_type = CAC_OBJECT_TYPE_GENERIC; if (cac_is_cert(priv, in_path)) { priv->object_type = CAC_OBJECT_TYPE_CERT; } - /* forget any old cacheed values */ + + /* forget any old cached values */ if (priv->cache_buf) { free(priv->cache_buf); priv->cache_buf = NULL; @@ -1097,16 +1156,18 @@ static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc apdu.p2 = 0; /* first record, return FCI */ } else { - apdu.p2 = (type == SC_CARD_TYPE_CAC_I)? 0x00 : 0x0C; + apdu.p2 = 0x0C; } r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, r, "APDU transmit failed"); + if (file_out == NULL) { /* For some cards 'SELECT' can be only with request to return FCI/FCP. */ r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (apdu.sw1 == 0x6A && apdu.sw2 == 0x86) { apdu.p2 = 0x00; + apdu.resplen = sizeof(buf); if (sc_transmit_apdu(card, &apdu) == SC_SUCCESS) r = sc_check_sw(card, apdu.sw1, apdu.sw2); } @@ -1119,7 +1180,37 @@ static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc if (r) LOG_FUNC_RETURN(ctx, r); - /* CAC cards enver return FCI, fake one */ + /* This needs to come after the applet selection */ + if (priv && in_path->len >= 2) { + /* get applet properties to know if we can treat the + * buffer as SimpleLTV and if we have PKI applet. + * + * Do this only if we select applets for reading + * (not during driver initialization) + */ + cac_properties_t prop = {0}; + size_t i = -1; + + r = cac_get_properties(card, &prop); + if (r == SC_SUCCESS) { + for (i = 0; i < prop.num_objects; i++) { + sc_log(card->ctx, "Searching for our OID: 0x%02x 0x%02x = 0x%02x 0x%02x", + prop.objects[i].oid[0], prop.objects[i].oid[1], + in_path->value[0], in_path->value[1]); + if (memcmp(prop.objects[i].oid, + in_path->value, 2) == 0) + break; + } + } + if (i < prop.num_objects) { + if (prop.objects[i].privatekey) + priv->object_type = CAC_OBJECT_TYPE_CERT; + else if (prop.objects[i].simpletlv == 0) + priv->object_type = CAC_OBJECT_TYPE_TLV_FILE; + } + } + + /* CAC cards never return FCI, fake one */ file = sc_file_new(); if (file == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); @@ -1127,13 +1218,13 @@ static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc file->size = CAC_MAX_SIZE; /* we don't know how big, just give a large size until we can read the file */ *file_out = file; - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int cac_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { - return cac_select_file_by_type(card, in_path, file_out, card->type); + return cac_select_file_by_type(card, in_path, file_out); } static int cac_finish(sc_card_t *card) @@ -1151,19 +1242,25 @@ static int cac_finish(sc_card_t *card) /* select the Card Capabilities Container on CAC-2 */ static int cac_select_CCC(sc_card_t *card) { - return cac_select_file_by_type(card, &cac_CCC_Path, NULL, SC_CARD_TYPE_CAC_II); + return cac_select_file_by_type(card, &cac_CCC_Path, NULL); +} + +/* Select ACA in non-standard location */ +static int cac_select_ACA(sc_card_t *card) +{ + return cac_select_file_by_type(card, &cac_ACA_Path, NULL); } -static int cac_path_from_cardurl(sc_card_t *card, sc_path_t *path, cac_card_url_t *val, int len) +static int cac_path_from_cardurl(sc_card_t *card, sc_path_t *path, cac_card_url_t *val, size_t len) { if (len < 10) { return SC_ERROR_INVALID_DATA; } sc_mem_clear(path, sizeof(sc_path_t)); memcpy(path->aid.value, &val->rid, sizeof(val->rid)); - memcpy(&path->aid.value[5], &val->applicationID, sizeof(val->applicationID)); + memcpy(&path->aid.value[5], val->applicationID, sizeof(val->applicationID)); path->aid.len = sizeof(val->rid) + sizeof(val->applicationID); - memcpy(path->value, &val->objectID, sizeof(val->objectID)); + memcpy(path->value, val->objectID, sizeof(val->objectID)); path->len = sizeof(val->objectID); path->type = SC_PATH_TYPE_FILE_ID; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, @@ -1182,7 +1279,65 @@ static int cac_path_from_cardurl(sc_card_t *card, sc_path_t *path, cac_card_url_ return SC_SUCCESS; } -static int cac_parse_cardurl(sc_card_t *card, cac_private_data_t *priv, cac_card_url_t *val, int len) +static int cac_parse_aid(sc_card_t *card, cac_private_data_t *priv, const u8 *aid, int aid_len) +{ + cac_object_t new_object; + cac_properties_t prop = {0}; + size_t i; + int r; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* Search for PKI applets (7 B). Ignore generic objects for now */ + if (aid_len != 7 || (memcmp(aid, CAC_1_RID "\x01", 6) != 0 + && memcmp(aid, CAC_1_RID "\x00", 6) != 0)) + return SC_SUCCESS; + + sc_mem_clear(&new_object.path, sizeof(sc_path_t)); + memcpy(new_object.path.aid.value, aid, aid_len); + new_object.path.aid.len = aid_len; + + /* Call without OID set will just select the AID without subsequent + * OID selection, which we need to figure out just now + */ + r = cac_select_file_by_type(card, &new_object.path, NULL); + LOG_TEST_RET(card->ctx, r, "Cannot select AID"); + r = cac_get_properties(card, &prop); + LOG_TEST_RET(card->ctx, r, "Cannot get CAC properties"); + + for (i = 0; i < prop.num_objects; i++) { + /* don't fail just because we have more certs than we can support */ + if (priv->cert_next >= MAX_CAC_SLOTS) + return SC_SUCCESS; + + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "ACA: pki_object found, cert_next=%d (%s), privkey=%d", + priv->cert_next, get_cac_label(priv->cert_next), + prop.objects[i].privatekey); + + /* If the private key is not initialized, we can safely + * ignore this object here, but increase the pointer to follow + * the certificate labels + */ + if (!prop.objects[i].privatekey) { + priv->cert_next++; + continue; + } + + /* OID here has always 2B */ + memcpy(new_object.path.value, &prop.objects[i].oid, 2); + new_object.path.len = 2; + new_object.path.type = SC_PATH_TYPE_FILE_ID; + new_object.name = get_cac_label(priv->cert_next); + new_object.fd = priv->cert_next+1; + cac_add_object_to_list(&priv->pki_list, &new_object); + priv->cert_next++; + } + + return SC_SUCCESS; +} + +static int cac_parse_cardurl(sc_card_t *card, cac_private_data_t *priv, cac_card_url_t *val, size_t len) { cac_object_t new_object; const cac_object_t *obj; @@ -1201,7 +1356,7 @@ static int cac_parse_cardurl(sc_card_t *card, cac_private_data_t *priv, cac_card */ if (priv->cert_next >= MAX_CAC_SLOTS) break; /* don't fail just because we have more certs than we can support */ - new_object.name = cac_labels[priv->cert_next]; + new_object.name = get_cac_label(priv->cert_next); new_object.fd = priv->cert_next+1; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: pki_object found, cert_next=%d (%s),", priv->cert_next, new_object.name); cac_add_object_to_list(&priv->pki_list, &new_object); @@ -1221,7 +1376,7 @@ static int cac_parse_cardurl(sc_card_t *card, cac_private_data_t *priv, cac_card sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: ski_object found"); break; default: - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: unkown object_object found (type=0x%x)", val->cardApplicationType); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: unknown object_object found (type=0x%02x)", val->cardApplicationType); /* don't fail just because there is an unknown object in the CCC */ break; } @@ -1245,6 +1400,7 @@ static int cac_parse_cuid(sc_card_t *card, cac_private_data_t *priv, cac_cuid_t sc_dump_hex(&val->card_id, card_id_len), card_id_len); priv->cuid = *val; + free(priv->cac_id); priv->cac_id = malloc(card_id_len); if (priv->cac_id == NULL) { return SC_ERROR_OUT_OF_MEMORY; @@ -1253,14 +1409,14 @@ static int cac_parse_cuid(sc_card_t *card, cac_private_data_t *priv, cac_cuid_t priv->cac_id_len = card_id_len; return SC_SUCCESS; } -static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv); +static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv, int depth); -static int cac_parse_CCC(sc_card_t *card, cac_private_data_t *priv, u8 *tl, - size_t tl_len, u8 *val, size_t val_len) +static int cac_parse_CCC(sc_card_t *card, cac_private_data_t *priv, const u8 *tl, + size_t tl_len, u8 *val, size_t val_len, int depth) { size_t len = 0; - u8 *tl_end = tl + tl_len; - u8 *val_end = val + val_len; + const u8 *tl_end = tl + tl_len; + const u8 *val_end = val + val_len; sc_path_t new_path; int r; @@ -1268,8 +1424,15 @@ static int cac_parse_CCC(sc_card_t *card, cac_private_data_t *priv, u8 *tl, for (; (tl < tl_end) && (val< val_end); val += len) { /* get the tag and the length */ u8 tag; - if (sc_simpletlv_read_tag(&tl, tl_end - tl, &tag, &len) != SC_SUCCESS) + r = sc_simpletlv_read_tag(&tl, tl_end - tl, &tag, &len); + if (r != SC_SUCCESS && r != SC_ERROR_TLV_END_OF_CONTENTS) { + sc_log(card->ctx, "Failed to parse tag from buffer"); break; + } + if (val + len > val_end) { + sc_log(card->ctx, "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); + break; + } switch (tag) { case CAC_TAG_CUID: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:CUID"); @@ -1278,9 +1441,24 @@ static int cac_parse_CCC(sc_card_t *card, cac_private_data_t *priv, u8 *tl, return r; break; case CAC_TAG_CC_VERSION_NUMBER: + if (len != 1) { + sc_log(card->ctx, "TAG: CC Version: " + "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); + break; + } + /* ignore the version numbers for now */ + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "TAG: CC Version = 0x%02x", *val); + break; case CAC_TAG_GRAMMAR_VERION_NUMBER: + if (len != 1) { + sc_log(card->ctx, "TAG: Grammar Version: " + "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); + break; + } /* ignore the version numbers for now */ - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:Version"); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "TAG: Grammar Version = 0x%02x", *val); break; case CAC_TAG_CARDURL: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:CARDURL"); @@ -1292,21 +1470,34 @@ static int cac_parse_CCC(sc_card_t *card, cac_private_data_t *priv, u8 *tl, * The following are really for file systems cards. This code only cares about CAC VM cards */ case CAC_TAG_PKCS15: - /* should verify that this is '0'. If it's not zero, we should drop out of here and - * let the PKCS 15 code handle this card */ - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:PKCS5"); + if (len != 1) { + sc_log(card->ctx, "TAG: PKCS15: " + "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); + break; + } + /* TODO should verify that this is '0'. If it's not + * zero, we should drop out of here and let the PKCS 15 + * code handle this card */ + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG: PKCS15 = 0x%02x", *val); break; case CAC_TAG_DATA_MODEL: + if (len != 1) { + sc_log(card->ctx, "TAG: Registered Data Model Number: " + "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); + break; + } + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG: Registered Data Model Number (0x%02x)", *val); + break; case CAC_TAG_CARD_APDU: case CAC_TAG_CAPABILITY_TUPLES: case CAC_TAG_STATUS_TUPLES: case CAC_TAG_REDIRECTION: case CAC_TAG_ERROR_CODES: - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:FSSpecific(0x%x)", tag); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG: FSSpecific(0x%02x)", tag); break; case CAC_TAG_ACCESS_CONTROL: - /* handle access control later */ - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:ACCESS Control"); + /* TODO handle access control later */ + sc_log_hex(card->ctx, "TAG:ACCESS Control", val, len); break; case CAC_TAG_NEXT_CCC: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:NEXT CCC"); @@ -1314,29 +1505,34 @@ static int cac_parse_CCC(sc_card_t *card, cac_private_data_t *priv, u8 *tl, if (r < 0) return r; - r = cac_select_file_by_type(card, &new_path, NULL, SC_CARD_TYPE_CAC_II); + r = cac_select_file_by_type(card, &new_path, NULL); if (r < 0) return r; - r = cac_process_CCC(card, priv); + /* Increase depth to avoid infinite recursion */ + r = cac_process_CCC(card, priv, depth + 1); if (r < 0) return r; break; default: /* ignore tags we don't understand */ - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:Unknown (0x%x)",tag ); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:Unknown (0x%02x)",tag ); break; } } return SC_SUCCESS; } -static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv) +static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv, int depth) { u8 *tl = NULL, *val = NULL; - size_t tl_len, val_len; + size_t tl_len = 0, val_len = 0; int r; + if (depth > CAC_MAX_CCC_DEPTH) { + sc_log(card->ctx, "Too much recursive CCC found. Exiting"); + return SC_ERROR_INVALID_CARD; + } r = cac_read_file(card, CAC_FILE_TAG, &tl, &tl_len); if (r < 0) @@ -1346,7 +1542,7 @@ static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv) if (r < 0) goto done; - r = cac_parse_CCC(card, priv, tl, tl_len, val, val_len); + r = cac_parse_CCC(card, priv, tl, tl_len, val, val_len, depth); done: if (tl) free(tl); @@ -1355,48 +1551,123 @@ static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv) return r; } -/* select a CAC-1 pki applet by index */ +/* Service Applet Table (Table 5-21) should list all the applets on the + * card, which is a good start if we don't have CCC + */ +static int cac_parse_ACA_service(sc_card_t *card, cac_private_data_t *priv, + const u8 *val, size_t val_len) +{ + size_t len = 0; + const u8 *val_end = val + val_len; + int r; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + for (; val < val_end; val += len) { + /* get the tag and the length */ + u8 tag; + if (sc_simpletlv_read_tag(&val, val_end - val, &tag, &len) != SC_SUCCESS) + break; + + switch (tag) { + case CAC_TAG_APPLET_FAMILY: + if (len != 5) { + sc_log(card->ctx, "TAG: Applet Information: " + "bad length %"SC_FORMAT_LEN_SIZE_T"u", len); + break; + } + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "TAG: Applet Information: Family: 0x%02x", val[0]); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + " Applet Version: 0x%02x 0x%02x 0x%02x 0x%02x", + val[1], val[2], val[3], val[4]); + break; + case CAC_TAG_NUMBER_APPLETS: + if (len != 1) { + sc_log(card->ctx, "TAG: Num applets: " + "bad length %"SC_FORMAT_LEN_SIZE_T"u", len); + break; + } + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "TAG: Num applets = %hhd", *val); + break; + case CAC_TAG_APPLET_ENTRY: + /* Make sure we match the outer length */ + if (len < 3 || val[2] != len - 3) { + sc_log(card->ctx, "TAG: Applet Entry: " + "bad length (%"SC_FORMAT_LEN_SIZE_T + "u) or length of internal buffer", len); + break; + } + sc_debug_hex(card->ctx, SC_LOG_DEBUG_VERBOSE, + "TAG: Applet Entry: AID", val + 3, val[2]); + /* This is SimpleTLV prefixed with applet ID (1B) */ + r = cac_parse_aid(card, priv, val + 3, val[2]); + if (r < 0) + return r; + break; + default: + /* ignore tags we don't understand */ + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "TAG: Unknown (0x%02x)", tag); + break; + } + } + return SC_SUCCESS; +} + +/* select a CAC pki applet by index */ static int cac_select_pki_applet(sc_card_t *card, int index) { - sc_path_t applet_path = cac_cac1_pki_obj.path; + sc_path_t applet_path = cac_cac_pki_obj.path; applet_path.aid.value[applet_path.aid.len-1] = index; - return cac_select_file_by_type(card, &applet_path, NULL, SC_CARD_TYPE_CAC_I); + return cac_select_file_by_type(card, &applet_path, NULL); } /* - * Find the first existing CAC-1 applet. If none found, then this isn't a CAC-1 + * Find the first existing CAC applet. If none found, then this isn't a CAC */ static int cac_find_first_pki_applet(sc_card_t *card, int *index_out) { int r, i; - for (i=0; i < MAX_CAC_SLOTS; i++) { + for (i = 0; i < MAX_CAC_SLOTS; i++) { r = cac_select_pki_applet(card, i); if (r == SC_SUCCESS) { + /* Try to read first two bytes of the buffer to + * make sure it is not just malfunctioning card + */ + u8 params[2] = {CAC_FILE_TAG, 2}; + u8 data[2], *out_ptr = data; + size_t len = 2; + r = cac_apdu_io(card, CAC_INS_READ_FILE, 0, 0, + ¶ms[0], sizeof(params), &out_ptr, &len); + if (r != 2) + continue; + *index_out = i; - return r; + return SC_SUCCESS; } } return SC_ERROR_OBJECT_NOT_FOUND; } /* - * CAC-1 has been found, identify all the certs and general containers. - * This emulates CAC-2's CCC. + * This emulates CCC for Alt tokens, that do not come with CCC nor ACA applets */ -static int cac_populate_cac_1(sc_card_t *card, int index, cac_private_data_t *priv) +static int cac_populate_cac_alt(sc_card_t *card, int index, cac_private_data_t *priv) { int r, i; - cac_object_t pki_obj = cac_cac1_pki_obj; + cac_object_t pki_obj = cac_cac_pki_obj; u8 buf[100]; u8 *val; - size_t val_len; /* populate PKI objects */ - for (i=index; i < MAX_CAC_SLOTS; i++) { + for (i = index; i < MAX_CAC_SLOTS; i++) { r = cac_select_pki_applet(card, i); if (r == SC_SUCCESS) { - pki_obj.name = cac_labels[i]; - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CAC1: pki_object found, cert_next=%d (%s),", i, pki_obj.name); + pki_obj.name = get_cac_label(i); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "CAC: pki_object found, cert_next=%d (%s),", i, pki_obj.name); pki_obj.path.aid.value[pki_obj.path.aid.len-1] = i; pki_obj.fd = i+1; /* don't use id of zero */ cac_add_object_to_list(&priv->pki_list, &pki_obj); @@ -1404,18 +1675,20 @@ static int cac_populate_cac_1(sc_card_t *card, int index, cac_private_data_t *pr } /* populate non-PKI objects */ - for (i=0; i < cac_1_object_count; i++) { - r = cac_select_file_by_type(card, &cac_1_objects[i].path, NULL, SC_CARD_TYPE_CAC_I); + for (i=0; i < cac_object_count; i++) { + r = cac_select_file_by_type(card, &cac_objects[i].path, NULL); if (r == SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CAC1: obj_object found, cert_next=%d (%s),", i, cac_1_objects[i].name); - cac_add_object_to_list(&priv->general_list, &cac_1_objects[i]); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "CAC: obj_object found, cert_next=%d (%s),", + i, cac_objects[i].name); + cac_add_object_to_list(&priv->general_list, &cac_objects[i]); } } /* * create a cuid to simulate the cac 2 cuid. */ - priv->cuid = cac_cac1_cuid; + priv->cuid = cac_cac_cuid; /* create a serial number by hashing the first 100 bytes of the * first certificate on the card */ r = cac_select_pki_applet(card, index); @@ -1423,16 +1696,19 @@ static int cac_populate_cac_1(sc_card_t *card, int index, cac_private_data_t *pr return r; /* shouldn't happen unless the card has been removed or is malfunctioning */ } val = buf; - val_len = sizeof(buf); - r = cac_cac1_get_certificate(card, &val, &val_len); - if (r >= 0) { + r = cac_read_binary(card, 0, val, sizeof(buf), 0); + if (r > 0) { +#ifdef ENABLE_OPENSSL + size_t val_len = r; + free(priv->cac_id); priv->cac_id = malloc(20); if (priv->cac_id == NULL) { return SC_ERROR_OUT_OF_MEMORY; } -#ifdef ENABLE_OPENSSL SHA1(val, val_len, priv->cac_id); priv->cac_id_len = 20; + sc_debug_hex(card->ctx, SC_LOG_DEBUG_VERBOSE, + "cuid", priv->cac_id, priv->cac_id_len); #else sc_log(card->ctx, "OpenSSL Required"); return SC_ERROR_NOT_SUPPORTED; @@ -1441,6 +1717,34 @@ static int cac_populate_cac_1(sc_card_t *card, int index, cac_private_data_t *pr return SC_SUCCESS; } +static int cac_process_ACA(sc_card_t *card, cac_private_data_t *priv) +{ + int r; + u8 *val = NULL; + size_t val_len; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* Assuming ACA is already selected */ + r = cac_get_acr(card, CAC_ACR_SERVICE, &val, &val_len); + if (r < 0) + goto done; + + r = cac_parse_ACA_service(card, priv, val, val_len); + if (r == SC_SUCCESS) { + priv->aca_path = malloc(sizeof(sc_path_t)); + if (!priv->aca_path) { + r = SC_ERROR_OUT_OF_MEMORY; + goto done; + } + memcpy(priv->aca_path, &cac_ACA_Path, sizeof(sc_path_t)); + } +done: + if (val) + free(val); + LOG_FUNC_RETURN(card->ctx, r); +} + /* * Look for a CAC card. If it exists, initialize our data structures */ @@ -1465,7 +1769,7 @@ static int cac_find_and_initialize(sc_card_t *card, int initialize) priv = cac_new_private_data(); if (!priv) return SC_ERROR_OUT_OF_MEMORY; - r = cac_process_CCC(card, priv); + r = cac_process_CCC(card, priv, 0); if (r == SC_SUCCESS) { card->type = SC_CARD_TYPE_CAC_II; card->drv_data = priv; @@ -1473,10 +1777,10 @@ static int cac_find_and_initialize(sc_card_t *card, int initialize) } } - /* is this a CAC-1 specified in DoD "CAC Applet Developer Guide" version 1.0 September 2002 */ - r = cac_find_first_pki_applet(card, &index); + /* Even some ALT tokens can be missing CCC so we should try with ACA */ + r = cac_select_ACA(card); if (r == SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "applet found, is CAC-1"); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "ACA found, is CAC-2 without CCC"); if (!initialize) /* match card only */ return r; @@ -1485,13 +1789,34 @@ static int cac_find_and_initialize(sc_card_t *card, int initialize) if (!priv) return SC_ERROR_OUT_OF_MEMORY; } - r = cac_populate_cac_1(card, index, priv); + r = cac_process_ACA(card, priv); if (r == SC_SUCCESS) { - card->type = SC_CARD_TYPE_CAC_I; + card->type = SC_CARD_TYPE_CAC_ALT_HID; card->drv_data = priv; return r; } } + + /* is this a CAC Alt token without any accompanying structures */ + r = cac_find_first_pki_applet(card, &index); + if (r == SC_SUCCESS) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "PKI applet found, is bare CAC Alt"); + if (!initialize) /* match card only */ + return r; + + if (!priv) { + priv = cac_new_private_data(); + if (!priv) + return SC_ERROR_OUT_OF_MEMORY; + } + card->drv_data = priv; /* needed for the read_binary() */ + r = cac_populate_cac_alt(card, index, priv); + if (r == SC_SUCCESS) { + card->type = SC_CARD_TYPE_CAC_II; + return r; + } + card->drv_data = NULL; /* reset on failure */ + } if (priv) { cac_free_private_data(priv); } @@ -1504,9 +1829,6 @@ static int cac_match_card(sc_card_t *card) { int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - /* Since we send an APDU, the card's logout function may be called... - * however it may be in dirty memory */ - card->ops->logout = NULL; r = cac_find_and_initialize(card, 0); return (r == SC_SUCCESS); /* never match */ @@ -1522,30 +1844,39 @@ static int cac_init(sc_card_t *card) r = cac_find_and_initialize(card, 1); if (r < 0) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } flags = SC_ALGORITHM_RSA_RAW; - _sc_card_add_rsa_alg(card, 1024, flags, 0); /* manditory */ + _sc_card_add_rsa_alg(card, 1024, flags, 0); /* mandatory */ _sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */ _sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */ card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int cac_logout(sc_card_t *card) +{ + int index; + return cac_find_first_pki_applet(card, &index); } -static int cac_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) +static int cac_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) { /* CAC, like PIV needs Extra validation of (new) PIN during * a PIN change request, to ensure it's not outside the * FIPS 201 4.1.6.1 (numeric only) and * FIPS 140-2 * (6 character minimum) requirements. */ + sc_apdu_t apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); + int rv; if (data->cmd == SC_PIN_CMD_CHANGE) { - int i = 0; + size_t i = 0; if (data->pin2.len < 6) { return SC_ERROR_INVALID_PIN_LENGTH; } @@ -1554,9 +1885,24 @@ static int cac_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries return SC_ERROR_INVALID_DATA; } } + + /* We can change the PIN of Giesecke & Devrient CAC ALT tokens + * with a bit non-standard APDU */ + if (card->type == SC_CARD_TYPE_CAC_ALT_HID) { + int r = 0; + r = iso7816_build_pin_apdu(card, &apdu, data, sbuf, sizeof(sbuf)); + if (r < 0) + return r; + /* it requires P1 = 0x01 completely against the ISO specs */ + apdu.p1 = 0x01; + data->apdu = &apdu; + } } - return iso_drv->ops->pin_cmd(card, data, tries_left); + rv = iso_drv->ops->pin_cmd(card, data); + + data->apdu = NULL; + return rv; } static struct sc_card_operations cac_ops; @@ -1580,13 +1926,15 @@ static struct sc_card_driver * sc_get_driver(void) cac_ops.select_file = cac_select_file; /* need to record object type */ cac_ops.get_challenge = cac_get_challenge; cac_ops.read_binary = cac_read_binary; - cac_ops.write_binary = cac_write_binary; + /* CAC driver is read only */ + cac_ops.write_binary = NULL; cac_ops.set_security_env = cac_set_security_env; cac_ops.restore_security_env = cac_restore_security_env; cac_ops.compute_signature = cac_compute_signature; cac_ops.decipher = cac_decipher; cac_ops.card_ctl = cac_card_ctl; cac_ops.pin_cmd = cac_pin_cmd; + cac_ops.logout = cac_logout; return &cac_drv; } diff --git a/src/libopensc/card-cac1.c b/src/libopensc/card-cac1.c new file mode 100644 index 0000000000..4bf440bce3 --- /dev/null +++ b/src/libopensc/card-cac1.c @@ -0,0 +1,560 @@ +/* + * card-cac1.c: Support for legacy CAC-1 + * card-default.c: Support for cards with no driver + * + * Copyright (C) 2001, 2002 Juha Yrjölä + * Copyright (C) 2005,2006,2007,2008,2009,2010 Douglas E. Engert + * Copyright (C) 2006, Identity Alliance, Thomas Harning + * Copyright (C) 2007, EMC, Russell Larner + * Copyright (C) 2016 - 2018, Red Hat, Inc. + * + * CAC driver author: Robert Relyea + * Further work: Jakub Jelen + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +#ifdef ENABLE_OPENSSL +#include +#endif /* ENABLE_OPENSSL */ + +#include "internal.h" +#include "simpletlv.h" +#include "cardctl.h" +#include "iso7816.h" +#include "card-cac-common.h" +#include "pkcs15.h" + +/* + * CAC hardware and APDU constants + */ +#define CAC_INS_GET_CERTIFICATE 0x36 /* CAC1 command to read a certificate */ + +/* + * OLD cac read certificate, only use with CAC-1 card. + */ +static int cac_cac1_get_certificate(sc_card_t *card, u8 **out_buf, size_t *out_len) +{ + u8 buf[CAC_MAX_SIZE]; + u8 *out_ptr; + size_t size = 0; + size_t left = 0; + size_t len; + sc_apdu_t apdu; + int r = SC_SUCCESS; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + /* get the size */ + size = left = *out_buf ? *out_len : sizeof(buf); + out_ptr = *out_buf ? *out_buf : buf; + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, CAC_INS_GET_CERTIFICATE, 0, 0 ); + len = MIN(left, 100); + while (left > 0) { + apdu.resp = out_ptr; + apdu.le = len; + apdu.resplen = left; + r = sc_transmit_apdu(card, &apdu); + if (r < 0) { + break; + } + if (apdu.resplen == 0) { + r = SC_ERROR_INTERNAL; + break; + } + /* in the old CAC-1, 0x63 means 'more data' in addition to 'pin failed' */ + if (apdu.sw1 != 0x63 || apdu.sw2 < 1) { + /* we've either finished reading, or hit an error, break */ + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + left -= apdu.resplen; + break; + } + /* Adjust the lengths */ + left -= apdu.resplen; + out_ptr += apdu.resplen; + len = MIN(left, apdu.sw2); + } + if (r < 0) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); + } + r = (int)(size - left); + if (*out_buf == NULL) { + *out_buf = malloc(r); + if (*out_buf == NULL) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, + SC_ERROR_OUT_OF_MEMORY); + } + memcpy(*out_buf, buf, r); + } + *out_len = r; + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); +} + +/* + * Callers of this may be expecting a certificate, + * select file will have saved the object type for us + * as well as set that we want the cert from the object. + */ +static int cac_read_binary(sc_card_t *card, unsigned int idx, + unsigned char *buf, size_t count, unsigned long *flags) +{ + cac_private_data_t * priv = CAC_DATA(card); + int r = 0; + u8 *val = NULL; + u8 *cert_ptr; + size_t val_len = 0; + size_t len, cert_len; + u8 cert_type; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* if we didn't return it all last time, return the remainder */ + if (priv->cached) { + sc_log(card->ctx, + "returning cached value idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u", + idx, count); + if (idx > priv->cache_buf_len) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_END_REACHED); + } + len = MIN(count, priv->cache_buf_len-idx); + memcpy(buf, &priv->cache_buf[idx], len); + LOG_FUNC_RETURN(card->ctx, (int)len); + } + + sc_log(card->ctx, + "clearing cache idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u", + idx, count); + free(priv->cache_buf); + priv->cache_buf = NULL; + priv->cache_buf_len = 0; + + r = cac_cac1_get_certificate(card, &val, &val_len); + if (r < 0) + goto done; + if (val_len < 1) { + r = SC_ERROR_INVALID_DATA; + goto done; + } + + cert_type = val[0]; + cert_ptr = val + 1; + cert_len = val_len - 1; + + /* if the info byte is 1, then the cert is compressed, decompress it */ + if ((cert_type & 0x3) == 1) { + if (flags) + *flags |= SC_FILE_FLAG_COMPRESSED_AUTO; + } + if (cert_len > 0) { + priv->cache_buf = malloc(cert_len); + if (priv->cache_buf == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto done; + } + priv->cache_buf_len = cert_len; + memcpy(priv->cache_buf, cert_ptr, cert_len); + } + + /* OK we've read the data, now copy the required portion out to the callers buffer */ + priv->cached = 1; + len = MIN(count, priv->cache_buf_len-idx); + if (len && priv->cache_buf) + memcpy(buf, &priv->cache_buf[idx], len); + r = (int)len; +done: + if (val) + free(val); + LOG_FUNC_RETURN(card->ctx, r); +} + +/* + * CAC cards use SC_PATH_SELECT_OBJECT_ID rather than SC_PATH_SELECT_FILE_ID. In order to use more + * of the PKCS #15 structure, we call the selection SC_PATH_SELECT_FILE_ID, but we set p1 to 2 instead + * of 0. Also cac1 does not do any FCI, but it doesn't understand not selecting it. It returns invalid INS + * if it doesn't like anything about the select, so we always 'request' FCI for CAC1 + * + * The rest is just copied from iso7816_select_file + */ +static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) +{ + struct sc_context *ctx; + struct sc_apdu apdu; + unsigned char buf[SC_MAX_APDU_BUFFER_SIZE]; + unsigned char pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; + int r, pathtype; + size_t pathlen; + struct sc_file *file = NULL; + cac_private_data_t *priv; + + if (card == NULL || in_path == NULL) + return SC_ERROR_INTERNAL; + + ctx = card->ctx; + priv = CAC_DATA(card); + + SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); + + memcpy(path, in_path->value, in_path->len); + pathlen = in_path->len; + pathtype = in_path->type; + + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "path=%s, path->value=%s path->type=%d (%x)", + sc_print_path(in_path), + sc_dump_hex(in_path->value, in_path->len), + in_path->type, in_path->type); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "file_out=%p index=%d count=%d\n", + file_out, in_path->index, in_path->count); + + /* Sigh, iso7816_select_file expects paths to keys to have specific + * formats. There is no override. We have to add some bytes to the + * path to make it happy. + * We only need to do this for private keys. + */ + if ((pathlen > 2) && (pathlen <= 4) && memcmp(path, "\x3F\x00", 2) == 0) { + path += 2; + pathlen -= 2; + } + + + /* CAC has multiple different type of objects that aren't PKCS #15. When we read + * them we need convert them to something PKCS #15 would understand. Find the object + * and object type here: + */ + if (priv) { /* don't record anything if we haven't been initialized yet */ + /* forget any old cached values */ + if (priv->cache_buf) { + free(priv->cache_buf); + priv->cache_buf = NULL; + } + priv->cache_buf_len = 0; + priv->cached = 0; + } + + if (in_path->aid.len) { + if (!pathlen) { + memcpy(path, in_path->aid.value, in_path->aid.len); + pathlen = in_path->aid.len; + pathtype = SC_PATH_TYPE_DF_NAME; + } else { + /* First, select the application */ + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"select application" ); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 4, 0); + apdu.data = in_path->aid.value; + apdu.datalen = in_path->aid.len; + apdu.lc = in_path->aid.len; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) + LOG_FUNC_RETURN(ctx, r); + + } + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0); + + switch (pathtype) { + /* ideally we would had SC_PATH_TYPE_OBJECT_ID and add code to the iso7816 select. + * Unfortunately we'd also need to update the caching code as well. For now just + * use FILE_ID and change p1 here */ + case SC_PATH_TYPE_FILE_ID: + apdu.p1 = 2; + if (pathlen != 2) + return SC_ERROR_INVALID_ARGUMENTS; + break; + case SC_PATH_TYPE_DF_NAME: + apdu.p1 = 4; + break; + default: + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + } + apdu.lc = pathlen; + apdu.data = path; + apdu.datalen = pathlen; + apdu.resp = buf; + apdu.resplen = sizeof(buf); + apdu.le = sc_get_max_recv_size(card) < 256 ? sc_get_max_recv_size(card) : 256; + apdu.p2 = 0x00; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); + + if (file_out == NULL) { + /* For some cards 'SELECT' can be only with request to return FCI/FCP. */ + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (apdu.sw1 == 0x6A && apdu.sw2 == 0x86) { + apdu.p2 = 0x00; + apdu.resplen = sizeof(buf); + if (sc_transmit_apdu(card, &apdu) == SC_SUCCESS) + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + } + if (apdu.sw1 == 0x61) + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, r); + } + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) + LOG_FUNC_RETURN(ctx, r); + + /* CAC cards never return FCI, fake one */ + file = sc_file_new(); + if (file == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + file->path = *in_path; + file->size = CAC_MAX_SIZE; /* we don't know how big, just give a large size until we can read the file */ + + *file_out = file; + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + +} + +static int cac_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) +{ + return cac_select_file_by_type(card, in_path, file_out); +} + +static int cac_finish(sc_card_t *card) +{ + cac_private_data_t * priv = CAC_DATA(card); + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + if (priv) { + cac_free_private_data(priv); + } + return SC_SUCCESS; +} + + +/* select a CAC pki applet by index */ +static int cac_select_pki_applet(sc_card_t *card, int index) +{ + sc_path_t applet_path = cac_cac_pki_obj.path; + applet_path.aid.value[applet_path.aid.len-1] = index; + return cac_select_file_by_type(card, &applet_path, NULL); +} + +/* + * Find the first existing CAC applet. If none found, then this isn't a CAC + */ +static int cac_find_first_pki_applet(sc_card_t *card, int *index_out) +{ + int r, i; + + for (i = 0; i < MAX_CAC_SLOTS; i++) { + r = cac_select_pki_applet(card, i); + if (r == SC_SUCCESS) { + u8 data[2]; + sc_apdu_t apdu; + + /* Try to read first two bytes of the buffer to + * make sure it is not just malfunctioning card + */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2, + CAC_INS_GET_CERTIFICATE, 0x00, 0x00); + apdu.le = 0x02; + apdu.resplen = 2; + apdu.resp = data; + r = sc_transmit_apdu(card, &apdu); + /* SW1 = 0x63 means more data in CAC1 */ + if (r == SC_SUCCESS && apdu.sw1 != 0x63) + continue; + + *index_out = i; + return SC_SUCCESS; + } + } + return SC_ERROR_OBJECT_NOT_FOUND; +} + +static int cac_populate_cac1(sc_card_t *card, int index, cac_private_data_t *priv) +{ + int r, i; + cac_object_t pki_obj = cac_cac_pki_obj; + u8 buf[100]; + u8 *val; + size_t val_len; + + /* populate PKI objects */ + for (i = index; i < MAX_CAC_SLOTS; i++) { + r = cac_select_pki_applet(card, i); + if (r == SC_SUCCESS) { + pki_obj.name = get_cac_label(i); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "CAC: pki_object found, cert_next=%d (%s)", + i, pki_obj.name); + pki_obj.path.aid.value[pki_obj.path.aid.len-1] = i; + pki_obj.fd = i+1; /* don't use id of zero */ + cac_add_object_to_list(&priv->pki_list, &pki_obj); + } + } + + /* + * create a cuid to simulate the cac 2 cuid. + */ + priv->cuid = cac_cac_cuid; + /* create a serial number by hashing the first 100 bytes of the + * first certificate on the card */ + r = cac_select_pki_applet(card, index); + if (r < 0) { + return r; /* shouldn't happen unless the card has been removed or is malfunctioning */ + } + val = buf; + val_len = sizeof(buf); + r = cac_cac1_get_certificate(card, &val, &val_len); + if (r >= 0) { + priv->cac_id = malloc(20); + if (priv->cac_id == NULL) { + return SC_ERROR_OUT_OF_MEMORY; + } +#ifdef ENABLE_OPENSSL + SHA1(val, val_len, priv->cac_id); + priv->cac_id_len = 20; + sc_debug_hex(card->ctx, SC_LOG_DEBUG_VERBOSE, + "cuid", priv->cac_id, priv->cac_id_len); +#else + sc_log(card->ctx, "OpenSSL Required"); + return SC_ERROR_NOT_SUPPORTED; +#endif /* ENABLE_OPENSSL */ + } + return SC_SUCCESS; +} + +/* + * Look for a CAC card. If it exists, initialize our data structures + */ +static int cac_find_and_initialize(sc_card_t *card, int initialize) +{ + int r, index; + cac_private_data_t *priv = NULL; + + /* already initialized? */ + if (card->drv_data) { + return SC_SUCCESS; + } + + /* is this a CAC Alt token without any accompanying structures */ + r = cac_find_first_pki_applet(card, &index); + if (r == SC_SUCCESS) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "PKI applet found, is bare CAC-1"); + if (!initialize) /* match card only */ + return r; + + if (!priv) { + priv = cac_new_private_data(); + if (!priv) + return SC_ERROR_OUT_OF_MEMORY; + } + card->drv_data = priv; /* needed for the read_binary() */ + r = cac_populate_cac1(card, index, priv); + if (r == SC_SUCCESS) { + card->type = SC_CARD_TYPE_CAC_I; + return r; + } + card->drv_data = NULL; /* reset on failure */ + } + if (priv) { + cac_free_private_data(priv); + } + return r; +} + + +/* NOTE: returns a bool, 1 card matches, 0 it does not */ +static int cac_match_card(sc_card_t *card) +{ + int r; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + r = cac_find_and_initialize(card, 0); + return (r == SC_SUCCESS); /* never match */ +} + + +static int cac_init(sc_card_t *card) +{ + int r; + unsigned long flags; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + r = cac_find_and_initialize(card, 1); + if (r < 0) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); + } + flags = SC_ALGORITHM_RSA_RAW; + + _sc_card_add_rsa_alg(card, 1024, flags, 0); /* mandatory */ + _sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */ + _sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */ + + card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO; + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int cac_logout(sc_card_t *card) +{ + int index; + return cac_find_first_pki_applet(card, &index); +} + +static struct sc_card_operations cac_ops; + +static struct sc_card_driver cac1_drv = { + "Common Access Card (CAC 1)", + "cac1", + &cac_ops, + NULL, 0, NULL +}; + +static struct sc_card_driver * sc_get_driver(void) +{ + /* Inherit most of the things from the CAC driver */ + struct sc_card_driver *cac_drv = sc_get_cac_driver(); + + cac_ops = *cac_drv->ops; + cac_ops.match_card = cac_match_card; + cac_ops.init = cac_init; + cac_ops.finish = cac_finish; + + cac_ops.select_file = cac_select_file; /* need to record object type */ + cac_ops.read_binary = cac_read_binary; + cac_ops.logout = cac_logout; + + return &cac1_drv; +} + + +struct sc_card_driver * sc_get_cac1_driver(void) +{ + return sc_get_driver(); +} diff --git a/src/libopensc/card-cardos-common.c b/src/libopensc/card-cardos-common.c new file mode 100644 index 0000000000..d649f0cc59 --- /dev/null +++ b/src/libopensc/card-cardos-common.c @@ -0,0 +1,85 @@ +/* + * card-cardos-common.c: Common code for CardOS based cards + * + * Copyright (C) 2024 Mario Haustein + * + * 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 + */ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "card-cardos-common.h" +#include "internal.h" + +int +cardos_ec_compute_shared_value(struct sc_card *card, + const u8 *crgram, size_t crgram_len, + u8 *out, size_t outlen) +{ + int r; + struct sc_apdu apdu; + u8 *sbuf = NULL; + + if (card == NULL || crgram == NULL || out == NULL) { + return SC_ERROR_INVALID_ARGUMENTS; + } + LOG_FUNC_CALLED(card->ctx); + sc_log(card->ctx, "CardOS compute shared value: in-len %" SC_FORMAT_LEN_SIZE_T "u, out-len %" SC_FORMAT_LEN_SIZE_T "u", crgram_len, outlen); + + /* Ensure public key is provided in uncompressed format (indicator byte + * 0x04 followed by X and Y coordinate. */ + if (crgram_len % 2 == 0 || crgram[0] != 0x04) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + /* strip indicator byte */ + crgram++; + crgram_len--; + + sbuf = malloc(crgram_len + 2); + if (sbuf == NULL) + return SC_ERROR_OUT_OF_MEMORY; + + /* INS: 0x2A PERFORM SECURITY OPERATION + * P1: 0x80 Resp: Plain value + * P2: 0xA6 Cmd: Control reference template for key agreement */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x80, 0xA6); + apdu.resp = out; + apdu.resplen = outlen; + apdu.le = outlen; + + sbuf[0] = 0x9c; /* context specific ASN.1 tag */ + sbuf[1] = crgram_len; + memcpy(sbuf + 2, crgram, crgram_len); + apdu.data = sbuf; + apdu.lc = crgram_len + 2; + apdu.datalen = crgram_len + 2; + + iso7816_fixup_transceive_length(card, &apdu); + r = sc_transmit_apdu(card, &apdu); + sc_mem_clear(sbuf, crgram_len + 2); + free(sbuf); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); + else + LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); +} diff --git a/src/libopensc/card-cardos-common.h b/src/libopensc/card-cardos-common.h new file mode 100644 index 0000000000..fbccc965c0 --- /dev/null +++ b/src/libopensc/card-cardos-common.h @@ -0,0 +1,43 @@ +/* + * card-cardos-common.c: Common code for CardOS based cards + * + * Copyright (C) 2024 Mario Haustein + * + * 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 + */ + +#ifndef HAVE_CARD_CARDOS_COMMON_H +#define HAVE_CARD_CARDOS_COMMON_H + +#include "libopensc/opensc.h" + +/** + * @brief compute a shared value from the peers public ECC key + * + * Key agreement on ECC keys requires a CardOS-specific command. + * + * @param card[in] struct sc_card object on which to issue the command + * @param crgram[in] public key point coordinates of the peer party in uncompressed format + * @param crgram_len[in] size of the public key point + * @param out[out] output buffer for the shared value + * @param outlen[in] size of the output buffer + * @return number of bytes of the shared value or an error code + */ +int +cardos_ec_compute_shared_value(struct sc_card *card, + const u8 *crgram, size_t crgram_len, + u8 *out, size_t outlen); + +#endif /* HAVE_CARD_CARDOS_COMMON_H */ diff --git a/src/libopensc/card-cardos.c b/src/libopensc/card-cardos.c index 41e28835f4..0e58f43b29 100644 --- a/src/libopensc/card-cardos.c +++ b/src/libopensc/card-cardos.c @@ -18,15 +18,16 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include +#include #include "internal.h" #include "asn1.h" @@ -42,16 +43,9 @@ static struct sc_card_driver cardos_drv = { NULL, 0, NULL }; -static struct sc_atr_table cardos_atrs[] = { +static const struct sc_atr_table cardos_atrs[] = { /* 4.0 */ { "3b:e2:00:ff:c1:10:31:fe:55:c8:02:9c", NULL, NULL, SC_CARD_TYPE_CARDOS_GENERIC, 0, NULL }, - /* Italian eID card, postecert */ - { "3b:e9:00:ff:c1:10:31:fe:55:00:64:05:00:c8:02:31:80:00:47", NULL, NULL, SC_CARD_TYPE_CARDOS_CIE_V1, 0, NULL }, - /* Italian eID card, infocamere */ - { "3b:fb:98:00:ff:c1:10:31:fe:55:00:64:05:20:47:03:31:80:00:90:00:f3", NULL, NULL, SC_CARD_TYPE_CARDOS_GENERIC, 0, NULL }, - /* Another Italian InfocamereCard */ - { "3b:fc:98:00:ff:c1:10:31:fe:55:c8:03:49:6e:66:6f:63:61:6d:65:72:65:28", NULL, NULL, SC_CARD_TYPE_CARDOS_GENERIC, 0, NULL }, - { "3b:f4:98:00:ff:c1:10:31:fe:55:4d:34:63:76:b4", NULL, NULL, SC_CARD_TYPE_CARDOS_GENERIC, 0, NULL}, /* cardos m4.2 and above */ { "3b:f2:18:00:ff:c1:0a:31:fe:55:c8:06:8a", "ff:ff:0f:ff:00:ff:00:ff:ff:00:00:00:00", NULL, SC_CARD_TYPE_CARDOS_M4_2, 0, NULL }, /* CardOS 4.4 */ @@ -59,23 +53,35 @@ static struct sc_atr_table cardos_atrs[] = { /* CardOS v5.0 */ { "3b:d2:18:00:81:31:fe:58:c9:01:14", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_0, 0, NULL}, /* CardOS v5.3 */ - { "3b:d2:18:00:81:31:fe:58:c9:03:16", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_0, 0, NULL}, + { "3b:d2:18:00:81:31:fe:58:c9:02:17", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_3, 0, NULL}, + { "3b:d2:18:00:81:31:fe:58:c9:03:16", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_3, 0, NULL}, + /* CardOS v5.4 */ + { "3b:d2:18:00:81:31:fe:58:c9:04:11", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_3, 0, NULL}, { NULL, NULL, NULL, 0, 0, NULL } }; -static unsigned int algorithm_ids_in_tokeninfo[SC_MAX_SUPPORTED_ALGORITHMS]; -static unsigned int algorithm_ids_in_tokeninfo_count=0; +/* private data for cardos driver */ +typedef struct cardos_data { + /* constructed internally */ + unsigned int algorithm_ids_in_tokeninfo[SC_MAX_SUPPORTED_ALGORITHMS]; + unsigned int algorithm_ids_in_tokeninfo_count; + unsigned long flags; /* flags used by init to create sc_algorithms */ + unsigned long ec_flags; + unsigned long ext_flags; + int rsa_2048; + const sc_security_env_t * sec_env; +} cardos_data_t; static int cardos_match_card(sc_card_t *card) { - unsigned char atr[SC_MAX_ATR_SIZE]; + unsigned char atr[SC_MAX_ATR_SIZE] = {0}; int i; i = _sc_match_atr(card, cardos_atrs, &card->type); if (i < 0) return 0; - memcpy(atr, card->atr.value, sizeof(atr)); + memcpy(atr, card->atr.value, card->atr.len); /* Do not change card type for CIE! */ if (card->type == SC_CARD_TYPE_CARDOS_CIE_V1) @@ -84,10 +90,12 @@ static int cardos_match_card(sc_card_t *card) return 1; if (card->type == SC_CARD_TYPE_CARDOS_V5_0) return 1; + if (card->type == SC_CARD_TYPE_CARDOS_V5_3) + return 1; if (card->type == SC_CARD_TYPE_CARDOS_M4_2) { int rv; - sc_apdu_t apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + sc_apdu_t apdu = {0}; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE] = {0}; /* first check some additional ATR bytes */ if ((atr[4] != 0xff && atr[4] != 0x02) || (atr[6] != 0x10 && atr[6] != 0x0a) || @@ -95,34 +103,34 @@ static int cardos_match_card(sc_card_t *card) return 0; /* get the os version using GET DATA and compare it with * version in the ATR */ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "checking cardos version ..."); + sc_log(card->ctx, "checking cardos version ..."); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x82); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; apdu.lc = 0; rv = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); - if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) + LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); + if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00 || apdu.resplen < 2) return 0; if (apdu.resp[0] != atr[10] || apdu.resp[1] != atr[11]) /* version mismatch */ return 0; if (atr[11] <= 0x04) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos m4.01"); + sc_log(card->ctx, "found cardos m4.01"); card->type = SC_CARD_TYPE_CARDOS_M4_01; } else if (atr[11] == 0x08) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos v4.3b"); + sc_log(card->ctx, "found cardos v4.3b"); card->type = SC_CARD_TYPE_CARDOS_M4_3; } else if (atr[11] == 0x09) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos v4.2b"); + sc_log(card->ctx, "found cardos v4.2b"); card->type = SC_CARD_TYPE_CARDOS_M4_2B; } else if (atr[11] >= 0x0B) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos v4.2c or higher"); + sc_log(card->ctx, "found cardos v4.2c or higher"); card->type = SC_CARD_TYPE_CARDOS_M4_2C; } else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos m4.2"); + sc_log(card->ctx, "found cardos m4.2"); } } return 1; @@ -133,7 +141,7 @@ static int cardos_have_2048bit_package(sc_card_t *card) sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; - const u8 *p = rbuf, *q; + const u8 *p = rbuf, *q, *pp; size_t len, tlen = 0, ilen = 0; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x88); @@ -142,17 +150,17 @@ static int cardos_have_2048bit_package(sc_card_t *card) apdu.lc = 0; apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if ((len = apdu.resplen) == 0) /* looks like no package has been installed */ return 0; while (len != 0) { - p = sc_asn1_find_tag(card->ctx, p, len, 0xe1, &tlen); - if (p == NULL) + pp = sc_asn1_find_tag(card->ctx, p, len, 0xe1, &tlen); + if (pp == NULL) return 0; - q = sc_asn1_find_tag(card->ctx, p, tlen, 0x01, &ilen); + q = sc_asn1_find_tag(card->ctx, pp, tlen, 0x01, &ilen); if (q == NULL || ilen != 4) return 0; if (q[0] == 0x1c) @@ -164,42 +172,104 @@ static int cardos_have_2048bit_package(sc_card_t *card) return 0; } + +/* Called from cardos_init for old cards, from cardos_cardctl_parsed_token_info for new cards */ +/* TODO see if works from old cards too */ +static int cardos_add_algs(sc_card_t *card, unsigned long flags, unsigned long ec_flags, unsigned long ext_flags) +{ + + cardos_data_t * priv = (cardos_data_t *)card->drv_data; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + _sc_card_add_rsa_alg(card, 512, flags, 0); + _sc_card_add_rsa_alg(card, 768, flags, 0); + _sc_card_add_rsa_alg(card, 1024, flags, 0); + if (priv->rsa_2048 == 1) { + _sc_card_add_rsa_alg(card, 1280, flags, 0); + _sc_card_add_rsa_alg(card, 1536, flags, 0); + _sc_card_add_rsa_alg(card, 1792, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); + } + + if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { + /* Starting with CardOS 5, the card supports PIN query commands */ + card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; + _sc_card_add_rsa_alg(card, 3072, flags, 0); + _sc_card_add_rsa_alg(card, 4096, flags, 0); + } + + /* TODO need to get sizes from supported_algos too */ + if (ec_flags != 0) { + _sc_card_add_ec_alg(card, 256, ec_flags, priv->ext_flags, NULL); + _sc_card_add_ec_alg(card, 384, ec_flags, priv->ext_flags, NULL); + } + + return 0; +} + static int cardos_init(sc_card_t *card) { - unsigned long flags, rsa_2048 = 0; + cardos_data_t * priv = NULL; + unsigned long flags = 0; size_t data_field_length; sc_apdu_t apdu; u8 rbuf[2]; + int r; - card->name = "CardOS M4"; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + priv = calloc(1, sizeof(cardos_data_t)); + if (!priv) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + card->drv_data = priv; + + card->name = "Atos CardOS"; card->cla = 0x00; - /* Set up algorithm info. */ - flags = SC_ALGORITHM_RSA_RAW - | SC_ALGORITHM_RSA_HASH_NONE - | SC_ALGORITHM_ONBOARD_KEY_GEN - ; - if (card->type != SC_CARD_TYPE_CARDOS_V5_0) - flags |= SC_ALGORITHM_NEED_USAGE; + /* let user override flags and type from opensc.conf */ + /* user can override card->type too.*/ + if (card->flags) { + flags = card->flags; + } else { - _sc_card_add_rsa_alg(card, 512, flags, 0); - _sc_card_add_rsa_alg(card, 768, flags, 0); - _sc_card_add_rsa_alg(card, 1024, flags, 0); + /* Set up algorithm info. */ + flags = 0; + if (card->type == SC_CARD_TYPE_CARDOS_V5_0) { + flags |= SC_ALGORITHM_RSA_PAD_PKCS1; + } else if(card->type == SC_CARD_TYPE_CARDOS_V5_3) { + flags |= SC_ALGORITHM_RSA_RAW + | SC_ALGORITHM_RSA_HASH_NONE + | SC_ALGORITHM_ONBOARD_KEY_GEN; + } else { + flags |= SC_ALGORITHM_RSA_RAW + | SC_ALGORITHM_RSA_HASH_NONE + | SC_ALGORITHM_NEED_USAGE + | SC_ALGORITHM_ONBOARD_KEY_GEN; + } + } + + priv->flags = flags; if (card->type == SC_CARD_TYPE_CARDOS_M4_2) { - int r = cardos_have_2048bit_package(card); - if (r < 0) - return r; + r = cardos_have_2048bit_package(card); + if (r < 0) { + r = SC_ERROR_INVALID_CARD; + goto err; + } if (r == 1) - rsa_2048 = 1; + priv->rsa_2048 = 1; card->caps |= SC_CARD_CAP_APDU_EXT; - } else if (card->type == SC_CARD_TYPE_CARDOS_M4_3 + } else if (card->type == SC_CARD_TYPE_CARDOS_M4_3 || card->type == SC_CARD_TYPE_CARDOS_M4_2B || card->type == SC_CARD_TYPE_CARDOS_M4_2C || card->type == SC_CARD_TYPE_CARDOS_M4_4 - || card->type == SC_CARD_TYPE_CARDOS_V5_0) { - rsa_2048 = 1; + || card->type == SC_CARD_TYPE_CARDOS_V5_0 + || card->type == SC_CARD_TYPE_CARDOS_V5_3) { + priv->rsa_2048 = 1; card->caps |= SC_CARD_CAP_APDU_EXT; + /* TODO check this. EC only if in supported_algo */ + priv->ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES; } /* probe DATA FIELD LENGTH with GET DATA */ @@ -207,115 +277,198 @@ static int cardos_init(sc_card_t *card) apdu.le = sizeof rbuf; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, - sc_transmit_apdu(card, &apdu), - "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, - sc_check_sw(card, apdu.sw1, apdu.sw2), - "GET DATA command returned error"); - if (apdu.resplen != 2) - return SC_ERROR_WRONG_LENGTH; + + r = sc_transmit_apdu(card, &apdu); + if (r < 0) + LOG_TEST_GOTO_ERR(card->ctx, + SC_ERROR_INVALID_CARD, + "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r < 0) + LOG_TEST_GOTO_ERR(card->ctx, + SC_ERROR_INVALID_CARD, + "GET DATA command returned error"); + if (apdu.resplen != 2) { + r = SC_ERROR_INVALID_CARD; + goto err; + } data_field_length = ((rbuf[0] << 8) | rbuf[1]); - /* strip the length of possible Lc and Le bytes */ - if (card->caps & SC_CARD_CAP_APDU_EXT) - card->max_send_size = data_field_length - 6; - else - card->max_send_size = data_field_length - 3; - /* strip the length of SW bytes */ - card->max_recv_size = data_field_length - 2; + /* TODO is this really needed? strip the length of possible Lc and Le bytes */ + + /* Use Min card sizes and reader too. for V5_3 at least*/ + + if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "data_field_length:%"SC_FORMAT_LEN_SIZE_T"u " + "card->reader->max_send_size:%"SC_FORMAT_LEN_SIZE_T"u " + "card->reader->max_recv_size:%"SC_FORMAT_LEN_SIZE_T"u %s", + data_field_length, card->reader->max_send_size, card->reader->max_recv_size, + (card->caps & SC_CARD_CAP_APDU_EXT) ? "SC_CARD_CAP_APDU_EXT" : " "); + + if (card->caps & SC_CARD_CAP_APDU_EXT) { + card->max_send_size = data_field_length - 6; +#ifdef _WIN32 + /* Windows does not support PCSC PART_10 and may have forced reader to 255/256 + * https://github.com/OpenSC/OpenSC/commit/eddea6f3c2d3dafc2c09eba6695c745a61b5186f + * may have reset this. if so, will override and force extended + * Most, if not all, cardos cards do extended, but not chaining + */ + if (card->reader->max_send_size == 255 && card->reader->max_recv_size == 256) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "resetting reader to use data_field_length"); + card->reader->max_send_size = data_field_length - 6; + card->reader->max_recv_size = data_field_length - 3; + } +#endif + } else + card->max_send_size = data_field_length - 3; - if (rsa_2048 == 1) { - _sc_card_add_rsa_alg(card, 1280, flags, 0); - _sc_card_add_rsa_alg(card, 1536, flags, 0); - _sc_card_add_rsa_alg(card, 1792, flags, 0); - _sc_card_add_rsa_alg(card, 2048, flags, 0); + card->max_send_size = sc_get_max_send_size(card); /* include reader sizes and protocol */ + card->max_recv_size = data_field_length - 2; + card->max_recv_size = sc_get_max_recv_size(card); + } else { + /* old way, disregards reader capabilities */ + if (card->caps & SC_CARD_CAP_APDU_EXT) + card->max_send_size = data_field_length - 6; + else + card->max_send_size = data_field_length - 3; + /* strip the length of SW bytes */ + card->max_recv_size = data_field_length - 2; } - if (card->type == SC_CARD_TYPE_CARDOS_V5_0) { - /* Starting with CardOS 5, the card supports PIN query commands */ - card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; + /*for new cards, wait till after sc_pkcs15_bind_internal reads tokeninfo */ + if (card->type != SC_CARD_TYPE_CARDOS_V5_0 && card->type != SC_CARD_TYPE_CARDOS_V5_3) { + r = cardos_add_algs(card, flags, 0, 0); } - return 0; +err: + if (r != SC_SUCCESS) { + free(priv); + card->drv_data = NULL; + } + + return r; +} + +static int cardos_pass_algo_flags(sc_card_t *card, struct sc_cardctl_cardos_pass_algo_flags * ptr) +{ + cardos_data_t * priv = (cardos_data_t *)card->drv_data; + int r = 0; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + switch (ptr->pass) { + case 1: + ptr->card_flags = card->flags; + ptr->used_flags = priv->flags; + ptr->ec_flags = priv->ec_flags; + ptr->ext_flags = priv->ext_flags; + break; + case 2: + r = cardos_add_algs(card,ptr->new_flags, ptr->ec_flags, ptr->ext_flags); + break; + default: + sc_log(card->ctx, "ptr->pass: %ul invalid", ptr->pass); + r = SC_ERROR_INTERNAL; + } + LOG_FUNC_RETURN(card->ctx, r); } + +static int cardos_finish(sc_card_t *card) +{ + int r = 0; + + if (card == NULL ) + return 0; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* free priv data */ + if (card->drv_data) { /* priv */ + free(card->drv_data); + card->drv_data = NULL; + } + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); +} + + + static const struct sc_card_error cardos_errors[] = { /* some error inside the card */ /* i.e. nothing you can do */ -{ 0x6581, SC_ERROR_MEMORY_FAILURE, "EEPROM error; command aborted"}, +{ 0x6581, SC_ERROR_MEMORY_FAILURE, "EEPROM error; command aborted"}, { 0x6fff, SC_ERROR_CARD_CMD_FAILED, "internal assertion error"}, -{ 0x6700, SC_ERROR_WRONG_LENGTH, "LC invalid"}, -{ 0x6985, SC_ERROR_CARD_CMD_FAILED, "no random number available"}, -{ 0x6f81, SC_ERROR_CARD_CMD_FAILED, "file invalid, maybe checksum error"}, -{ 0x6f82, SC_ERROR_CARD_CMD_FAILED, "not enough memory in xram"}, -{ 0x6f84, SC_ERROR_CARD_CMD_FAILED, "general protection fault"}, +{ 0x6700, SC_ERROR_WRONG_LENGTH, "LC invalid"}, +{ 0x6985, SC_ERROR_CARD_CMD_FAILED, "no random number available"}, +{ 0x6f81, SC_ERROR_CARD_CMD_FAILED, "file invalid, maybe checksum error"}, +{ 0x6f82, SC_ERROR_CARD_CMD_FAILED, "not enough memory in xram"}, +{ 0x6f84, SC_ERROR_CARD_CMD_FAILED, "general protection fault"}, /* the card doesn't know this combination of ins+cla+p1+p2 */ /* i.e. command will never work */ -{ 0x6881, SC_ERROR_NO_CARD_SUPPORT, "logical channel not supported"}, -{ 0x6a86, SC_ERROR_INCORRECT_PARAMETERS,"p1/p2 invalid"}, -{ 0x6d00, SC_ERROR_INS_NOT_SUPPORTED, "ins invalid"}, -{ 0x6e00, SC_ERROR_CLASS_NOT_SUPPORTED, "class invalid (hi nibble)"}, +{ 0x6881, SC_ERROR_NO_CARD_SUPPORT, "logical channel not supported"}, +{ 0x6a86, SC_ERROR_INCORRECT_PARAMETERS,"p1/p2 invalid"}, +{ 0x6d00, SC_ERROR_INS_NOT_SUPPORTED, "ins invalid"}, +{ 0x6e00, SC_ERROR_CLASS_NOT_SUPPORTED, "class invalid (hi nibble)"}, /* known command, but incorrectly used */ /* i.e. command could work, but you need to change something */ -{ 0x6981, SC_ERROR_CARD_CMD_FAILED, "command cannot be used for file structure"}, -{ 0x6a80, SC_ERROR_INCORRECT_PARAMETERS,"invalid parameters in data field"}, -{ 0x6a81, SC_ERROR_NOT_SUPPORTED, "function/mode not supported"}, -{ 0x6a85, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit the tlv structure"}, -{ 0x6986, SC_ERROR_INCORRECT_PARAMETERS,"no current ef selected"}, -{ 0x6a87, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit p1/p2"}, -{ 0x6c00, SC_ERROR_WRONG_LENGTH, "le does not fit the data to be sent"}, -{ 0x6f83, SC_ERROR_CARD_CMD_FAILED, "command must not be used in transaction"}, +{ 0x6981, SC_ERROR_CARD_CMD_FAILED, "command cannot be used for file structure"}, +{ 0x6a80, SC_ERROR_INCORRECT_PARAMETERS,"invalid parameters in data field"}, +{ 0x6a81, SC_ERROR_NOT_SUPPORTED, "function/mode not supported"}, +{ 0x6a85, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit the tlv structure"}, +{ 0x6986, SC_ERROR_INCORRECT_PARAMETERS,"no current ef selected"}, +{ 0x6a87, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit p1/p2"}, +{ 0x6c00, SC_ERROR_WRONG_LENGTH, "le does not fit the data to be sent"}, +{ 0x6f83, SC_ERROR_CARD_CMD_FAILED, "command must not be used in transaction"}, /* (something) not found */ -{ 0x6987, SC_ERROR_INCORRECT_PARAMETERS,"key object for sm not found"}, -{ 0x6f86, SC_ERROR_CARD_CMD_FAILED, "key object not found"}, -{ 0x6a82, SC_ERROR_FILE_NOT_FOUND, "file not found"}, -{ 0x6a83, SC_ERROR_RECORD_NOT_FOUND, "record not found"}, -{ 0x6a88, SC_ERROR_CARD_CMD_FAILED, "object not found"}, +{ 0x6987, SC_ERROR_INCORRECT_PARAMETERS,"key object for sm not found"}, +{ 0x6f86, SC_ERROR_CARD_CMD_FAILED, "key object not found"}, +{ 0x6a82, SC_ERROR_FILE_NOT_FOUND, "file not found"}, +{ 0x6a83, SC_ERROR_RECORD_NOT_FOUND, "record not found"}, +{ 0x6a88, SC_ERROR_CARD_CMD_FAILED, "object not found"}, /* (something) invalid */ -{ 0x6884, SC_ERROR_CARD_CMD_FAILED, "chaining error"}, -{ 0x6984, SC_ERROR_CARD_CMD_FAILED, "bs object has invalid format"}, -{ 0x6988, SC_ERROR_INCORRECT_PARAMETERS,"key object used for sm has invalid format"}, +{ 0x6884, SC_ERROR_CARD_CMD_FAILED, "chaining error"}, +{ 0x6984, SC_ERROR_CARD_CMD_FAILED, "bs object has invalid format"}, +{ 0x6988, SC_ERROR_INCORRECT_PARAMETERS,"key object used for sm has invalid format"}, /* (something) deactivated */ { 0x6283, SC_ERROR_CARD_CMD_FAILED, "file is deactivated" }, -{ 0x6983, SC_ERROR_AUTH_METHOD_BLOCKED, "bs object blocked"}, +{ 0x6983, SC_ERROR_AUTH_METHOD_BLOCKED, "bs object blocked"}, /* access denied */ -{ 0x6300, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"authentication failed"}, -{ 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"required access right not granted"}, +{ 0x6300, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"authentication failed"}, +{ 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"required access right not granted"}, /* other errors */ -{ 0x6a84, SC_ERROR_CARD_CMD_FAILED, "not enough memory"}, +{ 0x6a84, SC_ERROR_CARD_CMD_FAILED, "not enough memory"}, /* command ok, execution failed */ -{ 0x6f00, SC_ERROR_CARD_CMD_FAILED, "technical error (see eToken developers guide)"}, +{ 0x6f00, SC_ERROR_CARD_CMD_FAILED, "technical error (see eToken developers guide)"}, /* no error, maybe a note */ -{ 0x9000, SC_SUCCESS, NULL}, -{ 0x9001, SC_SUCCESS, "success, but eeprom weakness detected"}, -{ 0x9850, SC_SUCCESS, "over/underflow useing in/decrease"} +{ 0x9000, SC_SUCCESS, NULL}, +{ 0x9001, SC_SUCCESS, "success, but eeprom weakness detected"}, +{ 0x9850, SC_SUCCESS, "over/underflow using in/decrease"} }; static int cardos_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) { const int err_count = sizeof(cardos_errors)/sizeof(cardos_errors[0]); int i; - + for (i = 0; i < err_count; i++) { if (cardos_errors[i].SWs == ((sw1 << 8) | sw2)) { - if ( cardos_errors[i].errorstr ) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n", + if ( cardos_errors[i].errorstr ) + sc_log(card->ctx, "%s\n", cardos_errors[i].errorstr); return cardos_errors[i].errorno; } } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2); + sc_log(card->ctx, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2); return SC_ERROR_CARD_CMD_FAILED; } @@ -323,7 +476,7 @@ static int cardos_list_files(sc_card_t *card, u8 *buf, size_t buflen) { sc_apdu_t apdu; u8 rbuf[256], offset = 0; - const u8 *p = rbuf, *q; + const u8 *p, *q, *tag; int r; size_t fids = 0, len; @@ -340,29 +493,32 @@ static int cardos_list_files(sc_card_t *card, u8 *buf, size_t buflen) apdu.resp = rbuf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "DIRECTORY command returned error"); + LOG_TEST_RET(card->ctx, r, "DIRECTORY command returned error"); if (apdu.resplen > 256) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "directory listing > 256 bytes, cutting"); + sc_log(card->ctx, "directory listing > 256 bytes, cutting"); } + p = rbuf; len = apdu.resplen; while (len != 0) { size_t tlen = 0, ilen = 0; - /* is there a file informatin block (0x6f) ? */ - p = sc_asn1_find_tag(card->ctx, p, len, 0x6f, &tlen); - if (p == NULL) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "directory tag missing"); + /* is there a file information block (0x6f) ? */ + tag = sc_asn1_find_tag(card->ctx, p, len, 0x6f, &tlen); + if (tag == NULL) { + sc_log(card->ctx, "directory tag missing"); return SC_ERROR_INTERNAL; } + len = len - tlen - (tag - p); + p = tag + tlen; if (tlen == 0) /* empty directory */ break; - q = sc_asn1_find_tag(card->ctx, p, tlen, 0x86, &ilen); + q = sc_asn1_find_tag(card->ctx, tag, tlen, 0x86, &ilen); if (q == NULL || ilen != 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "error parsing file id TLV object"); + sc_log(card->ctx, "error parsing file id TLV object"); return SC_ERROR_INTERNAL; } /* put file id in buf */ @@ -371,22 +527,19 @@ static int cardos_list_files(sc_card_t *card, u8 *buf, size_t buflen) buf[fids++] = q[1]; buflen -= 2; } else - /* not enought space left in buffer => break */ + /* not enough space left in buffer => break */ break; /* extract next offset */ - q = sc_asn1_find_tag(card->ctx, p, tlen, 0x8a, &ilen); + q = sc_asn1_find_tag(card->ctx, tag, tlen, 0x8a, &ilen); if (q != NULL && ilen == 1) { offset = (u8)ilen; - if (offset != 0) - goto get_next_part; + goto get_next_part; } - len -= tlen + 2; - p += tlen; } - r = fids; + r = (int)fids; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } static void add_acl_entry(sc_file_t *file, int op, u8 byte) @@ -458,7 +611,7 @@ static const int ef_acl[9] = { /* XXX: ADMIN should be an ACL type of its own, or mapped * to erase */ SC_AC_OP_UPDATE, /* ADMIN EF (modify meta information?) */ - -1, /* INC (-> cylic fixed files) */ + -1, /* INC (-> cyclic fixed files) */ -1 /* DEC */ }; @@ -480,12 +633,12 @@ static int cardos_select_file(sc_card_t *card, sc_file_t **file) { int r; - + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); r = iso_ops->select_file(card, in_path, file); if (r >= 0 && file) parse_sec_attr((*file), (*file)->sec_attr, (*file)->sec_attr_len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } static int cardos_acl_to_bytes(sc_card_t *card, const sc_file_t *file, @@ -504,7 +657,7 @@ static int cardos_acl_to_bytes(sc_card_t *card, const sc_file_t *file, else byte = acl_to_byte(sc_file_get_acl_entry(file, idx[i])); if (byte < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid ACL\n"); + sc_log(card->ctx, "Invalid ACL\n"); return SC_ERROR_INVALID_ARGUMENTS; } buf[i] = byte; @@ -558,8 +711,8 @@ static int cardos_set_file_attributes(sc_card_t *card, sc_file_t *file) status[0] = 0x01; if (file->type == SC_FILE_TYPE_DF) { - status[1] = file->size >> 8; - status[2] = file->size; + status[1] = (file->size >> 8) & 0xFF; + status[2] = file->size & 0xFF; } else { status[1] = status[2] = 0x00; /* not used */ } @@ -589,7 +742,7 @@ static int cardos_construct_fcp(sc_card_t *card, const sc_file_t *file, size_t inlen = *outlen, len; int r; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); if (out == NULL || inlen < 64) return SC_ERROR_INVALID_ARGUMENTS; @@ -598,6 +751,8 @@ static int cardos_construct_fcp(sc_card_t *card, const sc_file_t *file, /* we will add the length later */ p++; + memset(buf, 0, sizeof(buf)); + /* set the length */ buf[0] = (file->size >> 8) & 0xff; buf[1] = file->size & 0xff; @@ -635,7 +790,7 @@ static int cardos_construct_fcp(sc_card_t *card, const sc_file_t *file, buf[4] |= (u8) file->record_count; break; default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unknown EF type: %u", file->type); + sc_log(card->ctx, "unknown EF type: %u", file->type); return SC_ERROR_INVALID_ARGUMENTS; } if (file->ef_structure == SC_FILE_EF_CYCLIC || @@ -712,17 +867,17 @@ static int cardos_create_file(sc_card_t *card, sc_file_t *file) r = cardos_construct_fcp(card, file, sbuf, &len); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to create FCP"); + sc_log(card->ctx, "unable to create FCP"); return r; } - + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); apdu.lc = len; apdu.datalen = len; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } else @@ -744,12 +899,12 @@ cardos_restore_security_env(sc_card_t *card, int se_num) apdu.p1 = (card->type == SC_CARD_TYPE_CARDOS_CIE_V1 ? 0xF3 : 0x03); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); + LOG_TEST_RET(card->ctx, r, "Card returned error"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } /* @@ -767,16 +922,29 @@ cardos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { + cardos_data_t *priv; sc_apdu_t apdu; - u8 data[3]; + u8 data[9]; int key_id, r; - assert(card != NULL && env != NULL); + if (card == NULL || env == NULL) + return SC_ERROR_INTERNAL; + + priv = (cardos_data_t*)card->drv_data; if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) { sc_log(card->ctx, "No or invalid key reference\n"); return SC_ERROR_INVALID_ARGUMENTS; } + priv->sec_env = env; /* pass on to crypto routines */ + + /* key_ref includes card mechanism and key number + * But newer cards appear to get this some other way, + * We can use flags passed to know what OpenSC expects from the card + * and have derived what these machanisums are. + * Newer cards may change how this is done + */ + key_id = env->key_ref[0]; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); @@ -797,17 +965,55 @@ cardos_set_security_env(sc_card_t *card, return SC_ERROR_INVALID_ARGUMENTS; } - data[0] = 0x83; - data[1] = 0x01; - data[2] = key_id; - apdu.lc = apdu.datalen = 3; + if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { + /* some cards appear to have key_id be both Cryptographic mechanism reference 4 bits + * and key_ref 4 bits. But this limits card to 16 keys. + * TODO may need to be looked at at a later time + */ + /* Private key reference */ + data[0] = 0x84; + data[1] = 0x01; + data[2] = key_id & 0x0F; + /* Usage qualifier byte */ + data[3] = 0x95; + data[4] = 0x01; + data[5] = 0x40; + apdu.lc = apdu.datalen = 6; + if (key_id & 0xF0) { + /* Cryptographic mechanism reference */ + data[6] = 0x80; + data[7] = 0x01; + data[8] = key_id & 0xF0; + apdu.lc = apdu.datalen = 9; + } else if ((env->operation == SC_SEC_OPERATION_SIGN + && priv->sec_env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01) + || (env->operation == SC_SEC_OPERATION_DECIPHER + && priv->sec_env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02)) { + /* TODO this may only apply to c903 cards */ + /* TODO or only for cards without any supported_algos or EIDComplient only */ + data[6] = 0x80; + data[7] = 0x01; + data[8] = 0x10; + apdu.lc = apdu.datalen = 9; + } else if (priv->sec_env->algorithm_flags & SC_ALGORITHM_ECDSA_RAW) { + data[6] = 0x80; + data[7] = 0x01; + data[8] = 0x30; + apdu.lc = apdu.datalen = 9; + } + } else { + data[0] = 0x83; + data[1] = 0x01; + data[2] = key_id; + apdu.lc = apdu.datalen = 3; + } apdu.data = data; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); + LOG_TEST_RET(card->ctx, r, "Card returned error"); do { const struct sc_supported_algo_info* algorithm_info = env->supported_algos; @@ -822,12 +1028,12 @@ cardos_set_security_env(sc_card_t *card, sc_log(card->ctx, "is signature"); sc_log(card->ctx, "Adding ID %d at index %d", algorithm_id, algorithm_id_count); - algorithm_ids_in_tokeninfo[algorithm_id_count++] = algorithm_id; + priv->algorithm_ids_in_tokeninfo[algorithm_id_count++] = algorithm_id; } sc_log(card->ctx, "reference=%d, mechanism=%d, operations=%d, algo_ref=%d", alg.reference, alg.mechanism, alg.operations, alg.algo_ref); } - algorithm_ids_in_tokeninfo_count = algorithm_id_count; + priv -> algorithm_ids_in_tokeninfo_count = algorithm_id_count; } while (0); LOG_FUNC_RETURN(card->ctx, r); @@ -842,6 +1048,7 @@ static int do_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, u8 *out, size_t outlen) { + /* cardos_data_t* priv = (cardos_data_t*)card->drv_dataa */; int r; sc_apdu_t apdu; @@ -856,11 +1063,12 @@ do_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, apdu.data = data; apdu.lc = datalen; apdu.datalen = datalen; + iso7816_fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)apdu.resplen); else SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } @@ -869,28 +1077,35 @@ static int cardos_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, u8 *out, size_t outlen) { + cardos_data_t* priv; int r; - u8 buf[SC_MAX_APDU_BUFFER_SIZE]; - size_t buf_len = sizeof(buf), tmp_len = buf_len; sc_context_t *ctx; int do_rsa_pure_sig = 0; int do_rsa_sig = 0; + size_t i; - - assert(card != NULL && data != NULL && out != NULL); + if (card == NULL || data == NULL || out == NULL) + return SC_ERROR_INTERNAL; ctx = card->ctx; + priv = (cardos_data_t*)card->drv_data; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); - if (datalen > SC_MAX_APDU_BUFFER_SIZE) - LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - if (outlen < datalen) - LOG_FUNC_RETURN(ctx, SC_ERROR_BUFFER_TOO_SMALL); - outlen = datalen; + /* sec_env has algorithm_flags set from sc_get_encoding_flags sec_flags + * If flags are set correctly we don't need to test anything + * TODO this assumes RSA is PSS, PKCS1 or RAW and we are passing + * the correct data. Should work for ECDSA too. + * use for V5 cards and TODO should for older cards too + */ + if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { + + r = do_compute_signature(card, data, datalen, out, outlen); + LOG_FUNC_RETURN(ctx, r); + } /* There are two ways to create a signature, depending on the way, * the key was created: RSA_SIG and RSA_PURE_SIG. * We can use the following reasoning, to determine the correct operation: - * 1. We check for several caps flags (as set in card->caps), to pervent generating + * 1. We check for several caps flags (as set in card->caps), to prevent generating * invalid signatures with duplicated hash prefixes with some cards * 2. Use the information from AlgorithmInfo of the TokenInfo file. * This information is parsed in set_security_env and stored in a static variable. @@ -902,23 +1117,13 @@ cardos_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, * and www.crysys.hu/infsec/M40_Manual_E_2001_10.pdf) */ - if (card->caps & SC_CARD_CAP_ONLY_RAW_HASH_STRIPPED){ - sc_log(ctx, "Forcing RAW_HASH_STRIPPED"); - do_rsa_sig = 1; - } - else if (card->caps & SC_CARD_CAP_ONLY_RAW_HASH){ - sc_log(ctx, "Forcing RAW_HASH"); - do_rsa_sig = 1; - } - else { - /* check the the algorithmIDs from the AlgorithmInfo */ - size_t i; - for(i=0; ialgorithm_ids_in_tokeninfo_count; ++i) { + unsigned int id = priv->algorithm_ids_in_tokeninfo[i]; + if (id == 0x86 || id == 0x88) { + do_rsa_sig = 1; + } else if (id == 0x8C || id == 0x8A) { + do_rsa_pure_sig = 1; } } @@ -939,41 +1144,104 @@ cardos_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, } if(do_rsa_sig == 1){ + u8 *buf = malloc(datalen); + u8 *stripped_data = buf; + size_t stripped_datalen = datalen; + if (!buf) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + memcpy(buf, data, datalen); + data = buf; + sc_log(ctx, "trying RSA_SIG (just the DigestInfo)"); + /* remove padding: first try pkcs1 bt01 padding */ - r = sc_pkcs1_strip_01_padding(ctx, data, datalen, buf, &tmp_len); + r = sc_pkcs1_strip_01_padding(ctx, data, datalen, stripped_data, &stripped_datalen); if (r != SC_SUCCESS) { - const u8 *p = data; /* no pkcs1 bt01 padding => let's try zero padding * This can only work if the data tbs doesn't have a * leading 0 byte. */ - tmp_len = buf_len; - while (*p == 0 && tmp_len != 0) { - ++p; - --tmp_len; + while (*stripped_data == 0 && stripped_datalen != 0) { + ++stripped_data; + --stripped_datalen; } - memcpy(buf, p, tmp_len); - } - if (!(card->caps & (SC_CARD_CAP_ONLY_RAW_HASH_STRIPPED | SC_CARD_CAP_ONLY_RAW_HASH)) || card->caps & SC_CARD_CAP_ONLY_RAW_HASH ) { - sc_log(ctx, "trying to sign raw hash value with prefix"); - r = do_compute_signature(card, buf, tmp_len, out, outlen); - if (r >= SC_SUCCESS) - LOG_FUNC_RETURN(ctx, r); } - if (card->caps & SC_CARD_CAP_ONLY_RAW_HASH) { - sc_log(ctx, "Failed to sign raw hash value with prefix when forcing"); - LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + sc_log(ctx, "trying to sign raw hash value with prefix"); + r = do_compute_signature(card, stripped_data, stripped_datalen, out, outlen); + if (r >= SC_SUCCESS) { + free(buf); + LOG_FUNC_RETURN(ctx, r); } sc_log(ctx, "trying to sign stripped raw hash value (card is responsible for prefix)"); - r = sc_pkcs1_strip_digest_info_prefix(NULL,buf,tmp_len,buf,&buf_len); - if (r != SC_SUCCESS) + r = sc_pkcs1_strip_digest_info_prefix(NULL, stripped_data, stripped_datalen, stripped_data, &stripped_datalen); + if (r != SC_SUCCESS) { + free(buf); LOG_FUNC_RETURN(ctx, r); - return do_compute_signature(card, buf, buf_len, out, outlen); + } + r = do_compute_signature(card, stripped_data, stripped_datalen, out, outlen); + free(buf); + LOG_FUNC_RETURN(ctx, r); } LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); } +static int +cardos_decipher(struct sc_card *card, + const u8 * crgram, size_t crgram_len, + u8 * out, size_t outlen) +{ + cardos_data_t* priv = (cardos_data_t*)card->drv_data; + int r; + size_t card_max_send_size = card->max_send_size; + size_t reader_max_send_size = card->reader->max_send_size; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* 5.3 supports command chaining. Others may also + * card_max_send_size for 5.3 is already based on reader max_send_size */ + + if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { + + r = iso_ops->decipher(card, crgram, crgram_len, out, outlen); + /* + * 5.3 supports RAW as well as PKCS1 and PSS + * description may strip padding if card supports it + * with cards that support RAW, it always appears to + * drop first 00 that is start of padding. + */ + + if (r > 0 && priv->sec_env->algorithm_flags & SC_ALGORITHM_RSA_RAW) { + size_t rsize = r; + /* RSA RAW crgram_len == modlen */ + /* removed padding is always > 1 byte */ + /* add back missing leading zero if card dropped it */ + if (rsize == crgram_len - 1 && rsize < outlen) { + memmove(out+1, out, rsize); + out[0] =0x00; + r++; + } + } + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); + } + + if (sc_get_max_send_size(card) < crgram_len + 1) { + /* CardOS doesn't support chaining for PSO:DEC, so we just _hope_ + * that both, the reader and the card are able to send enough data. + * (data is prefixed with 1 byte padding content indicator) */ + card->max_send_size = crgram_len + 1; + card->reader->max_send_size = crgram_len + 1; + } + + r = iso_ops->decipher(card, crgram, crgram_len, out, outlen); + + /* reset whatever we've modified above */ + card->max_send_size = card_max_send_size; + card->reader->max_send_size = reader_max_send_size; + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); +} + static int cardos_lifecycle_get(sc_card_t *card, int *mode) { @@ -990,13 +1258,13 @@ cardos_lifecycle_get(sc_card_t *card, int *mode) apdu.resp = rbuf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); + LOG_TEST_RET(card->ctx, r, "Card returned error"); if (apdu.resplen < 1) { - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Lifecycle byte not in response"); + LOG_TEST_RET(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Lifecycle byte not in response"); } r = SC_SUCCESS; @@ -1011,11 +1279,11 @@ cardos_lifecycle_get(sc_card_t *card, int *mode) *mode = SC_CARDCTRL_LIFECYCLE_OTHER; break; default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown lifecycle byte %d", rbuf[0]); + sc_log(card->ctx, "Unknown lifecycle byte %d", rbuf[0]); r = SC_ERROR_INTERNAL; } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } static int @@ -1032,7 +1300,7 @@ cardos_lifecycle_set(sc_card_t *card, int *mode) target = *mode; r = cardos_lifecycle_get(card, ¤t); - + if (r != SC_SUCCESS) return r; @@ -1046,12 +1314,12 @@ cardos_lifecycle_set(sc_card_t *card, int *mode) apdu.resp = NULL; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); + LOG_TEST_RET(card->ctx, r, "Card returned error"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } static int @@ -1074,12 +1342,12 @@ cardos_put_data_oci(sc_card_t *card, apdu.datalen = args->len; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); + LOG_TEST_RET(card->ctx, r, "Card returned error"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } static int @@ -1100,10 +1368,10 @@ cardos_put_data_seci(sc_card_t *card, apdu.datalen = args->len; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); + LOG_TEST_RET(card->ctx, r, "Card returned error"); return r; } @@ -1135,9 +1403,9 @@ cardos_generate_key(sc_card_t *card, apdu.datalen = apdu.lc = sizeof(data); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "GENERATE_KEY failed"); + LOG_TEST_RET(card->ctx, r, "GENERATE_KEY failed"); return r; } @@ -1153,17 +1421,22 @@ static int cardos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) apdu.resplen = sizeof(rbuf); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; - if (apdu.resplen != 32) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unexpected response to GET DATA serial" + if ((apdu.resplen == 8) && (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3)) { + /* cache serial number */ + memcpy(card->serialnr.value, rbuf, 8); + card->serialnr.len = 8; + } else if (apdu.resplen == 32) { + /* cache serial number */ + memcpy(card->serialnr.value, &rbuf[10], 6); + card->serialnr.len = 6; + } else { + sc_log(card->ctx, "unexpected response to GET DATA serial" " number\n"); return SC_ERROR_INTERNAL; } - /* cache serial number */ - memcpy(card->serialnr.value, &rbuf[10], 6); - card->serialnr.len = 6; /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; @@ -1186,6 +1459,9 @@ cardos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) case SC_CARDCTL_CARDOS_GENERATE_KEY: return cardos_generate_key(card, (struct sc_cardctl_cardos_genkey_info *) ptr); + case SC_CARDCTL_CARDOS_PASS_ALGO_FLAGS: + return cardos_pass_algo_flags(card, + (struct sc_cardctl_cardos_pass_algo_flags *) ptr); case SC_CARDCTL_LIFECYCLE_GET: return cardos_lifecycle_get(card, (int *) ptr); case SC_CARDCTL_LIFECYCLE_SET: @@ -1202,8 +1478,7 @@ cardos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) * Unfortunately, it doesn't seem to work without this flag :-/ */ static int -cardos_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, - int *tries_left) +cardos_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data) { struct sc_context *ctx = card->ctx; int rv; @@ -1228,7 +1503,7 @@ cardos_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, if (data->pin2.max_length == 0) data->pin2.max_length = 8; - rv = iso_ops->pin_cmd(card, data, tries_left); + rv = iso_ops->pin_cmd(card, data); LOG_FUNC_RETURN(ctx, rv); } @@ -1242,7 +1517,8 @@ cardos_logout(sc_card_t *card) || card->type == SC_CARD_TYPE_CARDOS_M4_2C || card->type == SC_CARD_TYPE_CARDOS_M4_3 || card->type == SC_CARD_TYPE_CARDOS_M4_4 - || card->type == SC_CARD_TYPE_CARDOS_V5_0) { + || card->type == SC_CARD_TYPE_CARDOS_V5_0 + || card->type == SC_CARD_TYPE_CARDOS_V5_3) { sc_apdu_t apdu; int r; sc_path_t path; @@ -1256,7 +1532,7 @@ cardos_logout(sc_card_t *card) apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } else @@ -1272,11 +1548,13 @@ static struct sc_card_driver * sc_get_driver(void) cardos_ops = *iso_ops; cardos_ops.match_card = cardos_match_card; cardos_ops.init = cardos_init; + cardos_ops.finish = cardos_finish; cardos_ops.select_file = cardos_select_file; cardos_ops.create_file = cardos_create_file; cardos_ops.set_security_env = cardos_set_security_env; cardos_ops.restore_security_env = cardos_restore_security_env; cardos_ops.compute_signature = cardos_compute_signature; + cardos_ops.decipher = cardos_decipher; cardos_ops.list_files = cardos_list_files; cardos_ops.check_sw = cardos_check_sw; diff --git a/src/libopensc/card-coolkey.c b/src/libopensc/card-coolkey.c index 5d462a8f99..4864686772 100644 --- a/src/libopensc/card-coolkey.c +++ b/src/libopensc/card-coolkey.c @@ -21,16 +21,17 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include +#include #include #include @@ -42,15 +43,6 @@ #include -#ifdef ENABLE_OPENSSL - /* openssl only needed for card administration */ -#include -#include -#include -#include -#include -#endif /* ENABLE_OPENSSL */ - #include "internal.h" #include "asn1.h" #include "cardctl.h" @@ -58,9 +50,15 @@ #include "compression.h" #endif #include "iso7816.h" +#include "gp.h" #include "../pkcs11/pkcs11.h" - +#ifdef _MSC_VER +#define PACKED +#pragma pack(push,1) +#elif defined(__GNUC__) +#define PACKED __attribute__ ((__packed__)) +#endif #define COOLKEY_MAX_SIZE 4096 /* arbitrary, just needs to be 'large enough' */ @@ -71,12 +69,10 @@ /* ISO 7816 CLA values used by COOLKEY */ #define ISO7816_CLASS 0x00 -#define GLOBAL_PLATFORM_CLASS 0x80 #define COOLKEY_CLASS 0xb0 /* ISO 71816 INS values used by COOLKEY */ #define ISO7816_INS_SELECT_FILE 0xa4 -#define ISO7816_INS_GET_DATA 0xca /* COOLKEY specific INS values (public) */ #define COOLKEY_INS_GET_LIFE_CYCLE 0xf2 @@ -88,7 +84,7 @@ #define COOLKEY_INS_COMPUTE_CRYPT 0x36 #define COOLKEY_INS_COMPUTE_ECC_KEY_AGREEMENT 0x37 #define COOLKEY_INS_COMPUTE_ECC_SIGNATURE 0x38 -#define COOLKEY_INS_GET_RANDOM 0x73 +#define COOLKEY_INS_GET_RANDOM 0x72 #define COOLKEY_INS_READ_OBJECT 0x56 #define COOLKEY_INS_WRITE_OBJECT 0x54 #define COOLKEY_INS_LOGOUT 0x61 @@ -136,31 +132,7 @@ typedef struct coolkey_status { u8 logged_in_identities[2]; } coolkey_status_t; -/* returned by the iso get status apdu with the global platform cplc data parameters */ -typedef struct global_platform_cplc_data { - u8 tag[2]; - u8 length; - u8 ic_fabricator[2]; - u8 ic_type[2]; - u8 os_id[2]; - u8 os_date[2]; - u8 os_level[2]; - u8 fabrication_data[2]; - u8 ic_serial_number[4]; - u8 ic_batch[2]; - u8 module_fabricator[2]; - u8 packing_data[2]; - u8 icc_manufacturer[2]; - u8 ic_embedding_data[2]; - u8 pre_personalizer[2]; - u8 ic_pre_personalization_data[2]; - u8 ic_pre_personalization_id[4]; - u8 ic_personalizaer[2]; - u8 ic_personalization_data[2]; - u8 ic_personalization_id[4]; -} global_platform_cplc_data_t; - -/* format of the coolkey_cuid, either contructed from cplc data or read from the combined object */ +/* format of the coolkey_cuid, either constructed from cplc data or read from the combined object */ typedef struct coolkey_cuid { u8 ic_fabricator[2]; u8 ic_type[2]; @@ -214,8 +186,8 @@ typedef struct coolkey_decompressed_header { u8 object_offset[2]; u8 object_count[2]; u8 token_name_length; - u8 token_name[255]; /* arbitary size up to token_name_length */ -} coolkey_decompressed_header_t; + u8 token_name[255]; /* arbitrary size up to token_name_length */ +} PACKED coolkey_decompressed_header_t; /* * header for an object. There are 2 types of object headers, v1 and v0. @@ -263,6 +235,13 @@ typedef struct coolkey_attribute_header { /* optional attribute data, or attribute len+data, depending on the value of data_type */ } coolkey_attribute_header_t; +#ifdef _MSC_VER +#undef PACKED +#pragma pack(pop) +#elif defined(__GNUC__) +#undef PACKED +#endif + /* values for attribute_data_type */ #define COOLKEY_ATTR_TYPE_STRING 0 #define COOLKEY_ATTR_TYPE_INTEGER 1 @@ -274,30 +253,30 @@ typedef struct coolkey_attribute_header { * Definitions: * struct coolkey_fixed_attributes_values { - uint32_t cka_id:4; - uint32_t cka_class:3; - uint32_t cka_token:1; - uint32_t cka_private:1; - uint32_t cka_modifiable:1; - uint32_t cka_derive:1; - uint32_t cka_local:1; - uint32_t cka_encrypt:1; - uint32_t cka_decrypt:1; - uint32_t cka_wrap:1; - uint32_t cka_unwrap:1; - uint32_t cka_sign:1; - uint32_t cka_sign_recover:1; - uint32_t cka_verify:1; - uint32_t cka_verify_recover:1; - uint32_t cka_sensitive:1; - uint32_t cka_always_sensitive:1; - uint32_t cka_extractable:1; - uint32_t cka_never_extractable:1; - uint32_t reseved:8; + unsigned int cka_id:4; + unsigned int cka_class:3; + unsigned int cka_token:1; + unsigned int cka_private:1; + unsigned int cka_modifiable:1; + unsigned int cka_derive:1; + unsigned int cka_local:1; + unsigned int cka_encrypt:1; + unsigned int cka_decrypt:1; + unsigned int cka_wrap:1; + unsigned int cka_unwrap:1; + unsigned int cka_sign:1; + unsigned int cka_sign_recover:1; + unsigned int cka_verify:1; + unsigned int cka_verify_recover:1; + unsigned int cka_sensitive:1; + unsigned int cka_always_sensitive:1; + unsigned int cka_extractable:1; + unsigned int cka_never_extractable:1; + unsigned int reserved:8; }; * cka_class is used to determine which booleans are valid. Any attributes in the full attribute list - * takes precidence over the fixed attributes. That is if there is a CKA_ID in the full attribute list, + * takes precedence over the fixed attributes. That is if there is a CKA_ID in the full attribute list, * The cka_id in the fixed_attributes is ignored. When determining which boolean attribute is valid, the * cka_class in the fixed attributes are used, even if it is overridden by the full attribute list. * valid cka_class values and their corresponding valid bools are as follows: @@ -497,7 +476,8 @@ coolkey_v0_get_attribute_count(const u8 *obj, size_t buf_len) * If the assert is true, you can easily see that the loop * will eventually break with len == 0, even if attribute_data_len * was invalid */ - assert(len <= buf_len); + if (len > buf_len) + return SC_ERROR_INTERNAL; count++; attr += len; buf_len -= len; @@ -548,6 +528,9 @@ coolkey_v0_get_attribute_data(const u8 *attr, size_t buf_len, sc_cardctl_coolkey if (r < 0) { return r; } + if (len + sizeof(coolkey_v0_attribute_header_t) > buf_len) { + return SC_ERROR_CORRUPTED_DATA; + } if ((attr_type == CKA_CLASS) || (attr_type == CKA_CERTIFICATE_TYPE) || (attr_type == CKA_KEY_TYPE)) { if (len != 4) { @@ -557,7 +540,7 @@ coolkey_v0_get_attribute_data(const u8 *attr, size_t buf_len, sc_cardctl_coolkey } /* return the length and the data */ attr_out->attribute_length = len; - attr_out->attribute_value = attr+sizeof(coolkey_v0_attribute_header_t); + attr_out->attribute_value = attr + sizeof(coolkey_v0_attribute_header_t); return SC_SUCCESS; } @@ -658,7 +641,7 @@ coolkey_get_fixed_boolean_bit(CK_ATTRIBUTE_TYPE type) return 0; /* return no bits */ } /* This table lets us return a pointer to the CKA_ID value without allocating data or - * creating a changable static that could cause thread issues */ + * creating a changeable static that could cause thread issues */ static const u8 coolkey_static_cka_id[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, }; @@ -729,7 +712,7 @@ coolkey_v1_get_object_length(u8 *obj, size_t buf_len) len = sizeof(coolkey_combined_object_header_t); if (buf_len <= len) { - return buf_len; + return (int)buf_len; } attribute_count = bebytes2ushort(object_head->attribute_count); buf_len -= len; @@ -741,7 +724,7 @@ coolkey_v1_get_object_length(u8 *obj, size_t buf_len) current_attribute += attribute_len; buf_len -= attribute_len; } - return len; + return (int)len; } /* @@ -763,7 +746,7 @@ typedef struct coolkey_private_data { sc_cardctl_coolkey_object_t *obj; /* pointer to the current selected object */ list_t objects_list; /* list of objects on the token */ unsigned short key_id; /* key id set by select */ - int algorithm; /* saved from set_security_env */ + unsigned long algorithm; /* saved from set_security_env */ int operation; /* saved from set_security_env */ } coolkey_private_data_t; @@ -775,7 +758,7 @@ coolkey_compare_id(const void * a, const void *b) if (a == NULL || b == NULL) return 1; return ((sc_cardctl_coolkey_object_t *)a)->id - == ((sc_cardctl_coolkey_object_t *)b)->id; + != ((sc_cardctl_coolkey_object_t *)b)->id; } /* For SimCList autocopy, we need to know the size of the data elements */ @@ -783,18 +766,25 @@ size_t coolkey_list_meter(const void *el) { return sizeof(sc_cardctl_coolkey_object_t); } +static void coolkey_free_private_data(coolkey_private_data_t *priv); + static coolkey_private_data_t *coolkey_new_private_data(void) { coolkey_private_data_t *priv; + /* allocate priv and zero all the fields */ priv = calloc(1, sizeof(coolkey_private_data_t)); if (!priv) return NULL; + /* set other fields as appropriate */ priv->key_id = COOLKEY_INVALID_KEY; - list_init(&priv->objects_list); - list_attributes_comparator(&priv->objects_list, coolkey_compare_id); - list_attributes_copy(&priv->objects_list, coolkey_list_meter, 1); + if (list_init(&priv->objects_list) != 0 || + list_attributes_comparator(&priv->objects_list, coolkey_compare_id) != 0 || + list_attributes_copy(&priv->objects_list, coolkey_list_meter, 1) != 0) { + coolkey_free_private_data(priv); + return NULL; + } return priv; } @@ -814,9 +804,7 @@ static void coolkey_free_private_data(coolkey_private_data_t *priv) list_iterator_stop(l); list_destroy(&priv->objects_list); - if (priv->token_name) { - free(priv->token_name); - } + free(priv->token_name); free(priv); return; } @@ -860,21 +848,21 @@ struct coolkey_error_codes_st { }; static const struct coolkey_error_codes_st coolkey_error_codes[]= { - {SC_ERROR_UNKNOWN, "Reservered 0x9c00" }, + {SC_ERROR_UNKNOWN, "Reserved 0x9c00" }, {SC_ERROR_NOT_ENOUGH_MEMORY, "No memory left on card" }, {SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed" }, {SC_ERROR_NOT_ALLOWED, "Operation not allowed" }, - {SC_ERROR_UNKNOWN, "Reservered 0x9c04" }, + {SC_ERROR_UNKNOWN, "Reserved 0x9c04" }, {SC_ERROR_NO_CARD_SUPPORT, "Unsupported feature" }, {SC_ERROR_SECURITY_STATUS_NOT_SATISFIED, "Not authorized" }, {SC_ERROR_DATA_OBJECT_NOT_FOUND, "Object not found" }, {SC_ERROR_FILE_ALREADY_EXISTS, "Object exists" }, {SC_ERROR_NO_CARD_SUPPORT, "Incorrect Algorithm" }, - {SC_ERROR_UNKNOWN, "Reservered 0x9c0a" }, + {SC_ERROR_UNKNOWN, "Reserved 0x9c0a" }, {SC_ERROR_SM_INVALID_CHECKSUM, "Signature invalid" }, {SC_ERROR_AUTH_METHOD_BLOCKED, "Identity blocked" }, - {SC_ERROR_UNKNOWN, "Reservered 0x9c0d" }, - {SC_ERROR_UNKNOWN, "Reservered 0x9c0e" }, + {SC_ERROR_UNKNOWN, "Reserved 0x9c0d" }, + {SC_ERROR_UNKNOWN, "Reserved 0x9c0e" }, {SC_ERROR_INCORRECT_PARAMETERS, "Invalid parameter" }, {SC_ERROR_INCORRECT_PARAMETERS, "Incorrect P1" }, {SC_ERROR_INCORRECT_PARAMETERS, "Incorrect P2" }, @@ -886,10 +874,10 @@ coolkey_number_of_error_codes = sizeof(coolkey_error_codes)/sizeof(coolkey_error static int coolkey_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "sw1 = 0x%02x, sw2 = 0x%02x\n", sw1, sw2); - if (sw1 == 0x90) + if (sw1 == 0x90 && sw2 == 0x00) return SC_SUCCESS; if (sw1 == 0x9c) { @@ -914,7 +902,7 @@ static int coolkey_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) * an internal 4096 byte buffer is used, and a copy is returned to the * caller. that need to be freed by the caller. * - * modelled after a similiar function in card-piv.c. The coolkey version + * modelled after a similar function in card-piv.c. The coolkey version * adds the coolkey nonce to user authenticated operations. */ @@ -933,7 +921,7 @@ static int coolkey_apdu_io(sc_card_t *card, int cla, int ins, int p1, int p2, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "%02x %02x %02x %"SC_FORMAT_LEN_SIZE_T"u : %"SC_FORMAT_LEN_SIZE_T"u %"SC_FORMAT_LEN_SIZE_T"u\n", ins, p1, p2, sendbuflen, card->max_send_size, card->max_recv_size); @@ -998,24 +986,24 @@ static int coolkey_apdu_io(sc_card_t *card, int cla, int ins, int p1, int p2, apdu.resplen = 0; } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "calling sc_transmit_apdu flags=%lx le=%"SC_FORMAT_LEN_SIZE_T"u, resplen=%"SC_FORMAT_LEN_SIZE_T"u, resp=%p", apdu.flags, apdu.le, apdu.resplen, apdu.resp); /* with new adpu.c and chaining, this actually reads the whole object */ r = sc_transmit_apdu(card, &apdu); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "result r=%d apdu.resplen=%"SC_FORMAT_LEN_SIZE_T"u sw1=%02x sw2=%02x", r, apdu.resplen, apdu.sw1, apdu.sw2); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Transmit failed"); + sc_log(card->ctx, "Transmit failed"); goto err; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Transmit failed"); + sc_log(card->ctx, "Transmit failed"); goto err; } @@ -1029,11 +1017,11 @@ static int coolkey_apdu_io(sc_card_t *card, int cla, int ins, int p1, int p2, memcpy(*recvbuf, rbuf, apdu.resplen); } *recvbuflen = apdu.resplen; - r = *recvbuflen; + r = (int)*recvbuflen; } err: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } /* @@ -1044,30 +1032,36 @@ coolkey_get_life_cycle(sc_card_t *card, coolkey_life_cycle_t *life_cycle) { coolkey_status_t status; u8 *receive_buf; - size_t len; - int r; + size_t receive_len; + int len; - len = sizeof(*life_cycle); + receive_len = sizeof(*life_cycle); receive_buf = (u8 *)life_cycle; - r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_LIFE_CYCLE, 0, 0, - NULL, 0, &receive_buf, &len, NULL, 0); - if (r == sizeof(*life_cycle)) { + len = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_LIFE_CYCLE, 0, 0, + NULL, 0, &receive_buf, &receive_len, NULL, 0); + if (len == sizeof(*life_cycle)) { return SC_SUCCESS; } - len = 1; + receive_len = 1; receive_buf = &life_cycle->life_cycle; - r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_LIFE_CYCLE, 0, 0, - NULL, 0, &receive_buf, &len, NULL, 0); - if (r < 0) { - return r; + len = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_LIFE_CYCLE, 0, 0, + NULL, 0, &receive_buf, &receive_len, NULL, 0); + if (len < 0) { /* Error from the trasmittion */ + return len; } - len = sizeof(status); + if (len != 1) { /* The returned data is invalid */ + return SC_ERROR_INTERNAL; + } + receive_len = sizeof(status); receive_buf = (u8 *)&status; - r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_STATUS, 0, 0, - NULL, 0, &receive_buf, &len, NULL, 0); - if (r < 0) { - return r; + len = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_STATUS, 0, 0, + NULL, 0, &receive_buf, &receive_len, NULL, 0); + if (len < 0) { /* Error from the trasmittion */ + return len; + } + if (len != sizeof(status)) { /* The returned data is invalid */ + return SC_ERROR_INTERNAL; } life_cycle->protocol_version_major = status.protocol_version_major; life_cycle->protocol_version_minor = status.protocol_version_minor; @@ -1075,17 +1069,6 @@ coolkey_get_life_cycle(sc_card_t *card, coolkey_life_cycle_t *life_cycle) return SC_SUCCESS; } - -/* should be general global platform call */ -static int -coolkey_get_cplc_data(sc_card_t *card, global_platform_cplc_data_t *cplc_data) -{ - size_t len = sizeof(global_platform_cplc_data_t); - u8 *receive_buf = (u8 *)cplc_data; - return coolkey_apdu_io(card, GLOBAL_PLATFORM_CLASS, ISO7816_INS_GET_DATA, 0x9f, 0x7f, - NULL, 0, &receive_buf, &len, NULL, 0); -} - /* select the coolkey applet */ static int coolkey_select_applet(sc_card_t *card) { @@ -1121,6 +1104,8 @@ static int coolkey_read_object(sc_card_t *card, unsigned long object_id, size_t size_t len; int r; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + ulong2bebytes(¶ms.object_id[0], object_id); out_ptr = out_buf; @@ -1128,13 +1113,13 @@ static int coolkey_read_object(sc_card_t *card, unsigned long object_id, size_t do { ulong2bebytes(¶ms.offset[0], offset); params.length = MIN(left, COOLKEY_MAX_CHUNK_SIZE); - len = left+2; + len = left; r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_READ_OBJECT, 0, 0, (u8 *)¶ms, sizeof(params), &out_ptr, &len, nonce, nonce_size); if (r < 0) { goto fail; } - /* santity check to make sure we don't overflow left */ + /* sanity check to make sure we don't overflow left */ if ((left < len) || (len == 0)) { r = SC_ERROR_INTERNAL; goto fail; @@ -1144,10 +1129,10 @@ static int coolkey_read_object(sc_card_t *card, unsigned long object_id, size_t left -= len; } while (left != 0); - return out_len; + return (int)out_len; fail: - return r; + LOG_FUNC_RETURN(card->ctx, r); } /* @@ -1160,12 +1145,16 @@ static int coolkey_write_object(sc_card_t *card, unsigned long object_id, size_t operation_len; size_t left = buf_len; int r; + size_t max_operation_len; + + /* set limit for the card's maximum send size and short write */ + max_operation_len = MIN(COOLKEY_MAX_CHUNK_SIZE, (card->max_send_size - sizeof(coolkey_read_object_param_t) - nonce_size)); ulong2bebytes(¶ms.head.object_id[0], object_id); do { ulong2bebytes(¶ms.head.offset[0], offset); - operation_len = MIN(left, COOLKEY_MAX_CHUNK_SIZE); + operation_len = MIN(left, max_operation_len); params.head.length = operation_len; memcpy(params.buf, buf, operation_len); r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_WRITE_OBJECT, 0, 0, @@ -1178,7 +1167,7 @@ static int coolkey_write_object(sc_card_t *card, unsigned long object_id, left -= operation_len; } while (left != 0); - return buf_len - left; + return (int)(buf_len - left); fail: return r; @@ -1191,28 +1180,29 @@ static int coolkey_write_object(sc_card_t *card, unsigned long object_id, * translate the objects into their PKCS #15 equivalent data structures. */ static int coolkey_read_binary(sc_card_t *card, unsigned int idx, - u8 *buf, size_t count, unsigned long flags) + u8 *buf, size_t count, unsigned long *flags) { coolkey_private_data_t * priv = COOLKEY_DATA(card); - int r = 0, len; - u8 *data = NULL;; + int r = 0; + size_t len; + u8 *data = NULL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (idx > priv->obj->length) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_END_REACHED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_END_REACHED); } /* if we've already read the data, just return it */ if (priv->obj->data) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "returning cached value idx=%u count=%"SC_FORMAT_LEN_SIZE_T"u", idx, count); len = MIN(count, priv->obj->length-idx); memcpy(buf, &priv->obj->data[idx], len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, len); + LOG_FUNC_RETURN(card->ctx, (int)len); } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "clearing cache idx=%u count=%"SC_FORMAT_LEN_SIZE_T"u", idx, count); @@ -1224,7 +1214,7 @@ static int coolkey_read_binary(sc_card_t *card, unsigned int idx, r = coolkey_read_object(card, priv->obj->id, 0, data, priv->obj->length, - priv->nonce, sizeof(priv->nonce)); + priv->nonce, sizeof(priv->nonce)); if (r < 0) goto done; @@ -1236,7 +1226,7 @@ static int coolkey_read_binary(sc_card_t *card, unsigned int idx, /* OK we've read the data, now copy the required portion out to the callers buffer */ len = MIN(count, priv->obj->length-idx); memcpy(buf, &data[idx], len); - r = len; + r = (int)len; /* cache the data in the object */ priv->obj->data=data; data = NULL; @@ -1244,7 +1234,7 @@ static int coolkey_read_binary(sc_card_t *card, unsigned int idx, done: if (data) free(data); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } /* COOLKEY driver is read only. NOTE: The applet supports w/r operations, so it's perfectly @@ -1255,7 +1245,7 @@ static int coolkey_write_binary(sc_card_t *card, unsigned int idx, { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } /* initialize getting a list and return the number of elements in the list */ @@ -1347,14 +1337,17 @@ static int coolkey_get_token_info(sc_card_t *card, sc_pkcs15_tokeninfo_t * token char *manufacturer_id = NULL; char *serial_number = NULL; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); label = strdup((char *)priv->token_name); manufacturer_id = coolkey_get_manufacturer(&priv->cuid); serial_number = coolkey_cuid_to_string(&priv->cuid); if (label && manufacturer_id && serial_number) { + free(token_info->label); token_info->label = label; + free(token_info->manufacturer_id); token_info->manufacturer_id = manufacturer_id; + free(token_info->serial_number); token_info->serial_number = serial_number; return SC_SUCCESS; } @@ -1368,10 +1361,10 @@ static int coolkey_get_serial_nr_from_CUID(sc_card_t* card, sc_serial_number_t* { coolkey_private_data_t * priv = COOLKEY_DATA(card); - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); memcpy(serial->value, &priv->cuid, sizeof(priv->cuid)); serial->len = sizeof(priv->cuid); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } int @@ -1383,6 +1376,8 @@ coolkey_fill_object(sc_card_t *card, sc_cardctl_coolkey_object_t *obj) sc_cardctl_coolkey_object_t *obj_entry; coolkey_private_data_t * priv = COOLKEY_DATA(card); + LOG_FUNC_CALLED(card->ctx); + if (obj->data != NULL) { return SC_SUCCESS; } @@ -1394,7 +1389,10 @@ coolkey_fill_object(sc_card_t *card, sc_cardctl_coolkey_object_t *obj) priv->nonce, sizeof(priv->nonce)); if (r != (int)buf_len) { free(new_obj_data); - return SC_ERROR_CORRUPTED_DATA; + if (r < 0) { + LOG_FUNC_RETURN(card->ctx, r); + } + LOG_FUNC_RETURN(card->ctx, SC_ERROR_CORRUPTED_DATA); } obj_entry = coolkey_find_object_by_id(&priv->objects_list, obj->id); if (obj_entry == NULL) { @@ -1405,9 +1403,15 @@ coolkey_fill_object(sc_card_t *card, sc_cardctl_coolkey_object_t *obj) free(new_obj_data); return SC_ERROR_INTERNAL; /* shouldn't happen */ } + /* Make sure we will not go over the allocated limits in the other + * objects if they somehow got different lengths in matching objects */ + if (obj_entry->length != obj->length) { + free(new_obj_data); + return SC_ERROR_INTERNAL; /* shouldn't happen */ + } obj_entry->data = new_obj_data; obj->data = new_obj_data; - return SC_SUCCESS; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* @@ -1429,6 +1433,8 @@ coolkey_find_attribute(sc_card_t *card, sc_cardctl_coolkey_attribute_t *attribut attribute->attribute_length = 0; attribute->attribute_value = NULL; + LOG_FUNC_CALLED(card->ctx); + if (obj == NULL) { /* cast away const so we can cache the data value */ int r = coolkey_fill_object(card, (sc_cardctl_coolkey_object_t *)attribute->object); @@ -1436,10 +1442,13 @@ coolkey_find_attribute(sc_card_t *card, sc_cardctl_coolkey_attribute_t *attribut return r; } obj = attribute->object->data; + if (obj == NULL) { + return SC_ERROR_INTERNAL; + } } /* should be a static assert so we catch this at compile time */ - assert(sizeof(coolkey_object_header_t) >= sizeof(coolkey_v0_object_header_t)); + static_assert(sizeof(coolkey_object_header_t) >= sizeof(coolkey_v0_object_header_t), "internal error"); /* make sure we have enough of the object to read the record_type */ if (buf_len <= sizeof(coolkey_v0_object_header_t)) { return SC_ERROR_CORRUPTED_DATA; @@ -1451,7 +1460,6 @@ coolkey_find_attribute(sc_card_t *card, sc_cardctl_coolkey_attribute_t *attribut return SC_ERROR_CORRUPTED_DATA; } - /* * now loop through all the attributes in the list. first find the start of the list */ @@ -1466,8 +1474,8 @@ coolkey_find_attribute(sc_card_t *card, sc_cardctl_coolkey_attribute_t *attribut for (i=0; i < attribute_count; i++) { size_t record_len = coolkey_get_attribute_record_len(attr, object_record_type, buf_len); /* make sure we have the complete record */ - if (buf_len < record_len) { - return SC_ERROR_CORRUPTED_DATA; + if (buf_len < record_len || record_len < 4) { + return SC_ERROR_CORRUPTED_DATA; } /* does the attribute match the one we are looking for */ if (attr_type == coolkey_get_attribute_type(attr, object_record_type, record_len)) { @@ -1484,7 +1492,7 @@ coolkey_find_attribute(sc_card_t *card, sc_cardctl_coolkey_attribute_t *attribut return coolkey_get_attribute_data_fixed(attr_type, fixed_attributes, attribute); } - return SC_ERROR_DATA_OBJECT_NOT_FOUND; + LOG_FUNC_RETURN(card->ctx, SC_ERROR_DATA_OBJECT_NOT_FOUND); } /* @@ -1609,40 +1617,17 @@ static int coolkey_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) static int coolkey_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { - size_t rbuflen = 0; - int r; - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "challenge len=%"SC_FORMAT_LEN_SIZE_T"u", len); - - r = sc_lock(card); - if (r != SC_SUCCESS) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); - - for(; len >= COOLKEY_MAX_CHUNK_SIZE; len -= COOLKEY_MAX_CHUNK_SIZE, - rnd += COOLKEY_MAX_CHUNK_SIZE) { - rbuflen = COOLKEY_MAX_CHUNK_SIZE; - r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_RANDOM, - 0, 0, NULL, 0, &rnd, &rbuflen, NULL, 0); - if (r != COOLKEY_MAX_CHUNK_SIZE) { - len = 0; - break; - } - } - if (len) { - r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_RANDOM, - 0, 0, NULL, 0, &rnd, &len, NULL, 0); - } - sc_unlock(card); + LOG_FUNC_CALLED(card->ctx); - if (r > 0) { - r= SC_SUCCESS; - } + if (len > COOLKEY_MAX_CHUNK_SIZE) + len = COOLKEY_MAX_CHUNK_SIZE; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_TEST_RET(card->ctx, + coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_RANDOM, 0, 0, + NULL, 0, &rnd, &len, NULL, 0), + "Could not get challenge"); + LOG_FUNC_RETURN(card->ctx, (int) len); } static int coolkey_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) @@ -1652,8 +1637,8 @@ static int coolkey_set_security_env(sc_card_t *card, const sc_security_env_t *en SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "flags=%08lx op=%d alg=%d algf=%08x algr=%08x kr0=%02x, krfl=%"SC_FORMAT_LEN_SIZE_T"u\n", + sc_log(card->ctx, + "flags=%08lx op=%d alg=%lu algf=%08lx algr=%08lx kr0=%02x, krfl=%"SC_FORMAT_LEN_SIZE_T"u\n", env->flags, env->operation, env->algorithm, env->algorithm_flags, env->algorithm_ref, env->key_ref[0], env->key_ref_len); @@ -1672,7 +1657,7 @@ static int coolkey_restore_security_env(sc_card_t *card, int se_num) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } #define MAX_COMPUTE_BUF 200 @@ -1694,55 +1679,47 @@ typedef struct coolkey_compute_ecc_params { u8 buf[MAX_COMPUTE_BUF]; } coolkey_compute_ecc_params_t; -static int coolkey_rsa_op(sc_card_t *card, - const u8 * data, size_t datalen, - u8 * out, size_t max_out_len) +static int coolkey_rsa_op(sc_card_t *card, const u8 * data, size_t datalen, + u8 * out, size_t max_out_len) { int r; - const u8 *crypt_in; - u8 **crypt_out_p; - size_t crypt_in_len, *crypt_out_len_p; - coolkey_private_data_t * priv = COOLKEY_DATA(card); + u8 **crypt_out_p = NULL; + size_t crypt_out_len_p = 0; + coolkey_private_data_t *priv = COOLKEY_DATA(card); coolkey_compute_crypt_params_t params; u8 key_number; size_t params_len; + u8 buf[MAX_COMPUTE_BUF + 2]; size_t buf_len; - u8 buf[MAX_COMPUTE_BUF+2]; u8 *buf_out; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "datalen=%"SC_FORMAT_LEN_SIZE_T"u outlen=%"SC_FORMAT_LEN_SIZE_T"u\n", - datalen, max_out_len); + sc_log(card->ctx, "datalen=%"SC_FORMAT_LEN_SIZE_T"u outlen=%"SC_FORMAT_LEN_SIZE_T"u\n", + datalen, max_out_len); - crypt_in = data; - crypt_in_len = datalen; - - buf_out = &buf[0]; - crypt_out_p = &buf_out; - buf_len = sizeof(buf); - crypt_out_len_p = &buf_len; - key_number = priv->key_id; - params.init.mode = COOLKEY_CRYPT_MODE_RSA_NO_PAD; - params.init.location = COOLKEY_CRYPT_LOCATION_APDU; - params.init.direction = COOLKEY_CRYPT_DIRECTION_ENCRYPT; /* for no pad, direction is irrelevant */ + if (datalen > 0xFFFF) { + r = SC_ERROR_INVALID_ARGUMENTS; + goto done; + } if (priv->key_id > 0xff) { r = SC_ERROR_NO_DEFAULT_KEY; goto done; } + key_number = priv->key_id; - params_len = sizeof(params.init) + crypt_in_len; + memset(¶ms, 0, sizeof(params)); + params.init.mode = COOLKEY_CRYPT_MODE_RSA_NO_PAD; + params.init.direction = COOLKEY_CRYPT_DIRECTION_ENCRYPT; /* for no pad, direction is irrelevant */ /* send the data to the card if necessary */ - if (crypt_in_len > MAX_COMPUTE_BUF) { + if (datalen > MAX_COMPUTE_BUF) { + /* We need to write data to special object on the card as it does not safely fit APDU */ u8 len_buf[2]; + params.init.location = COOLKEY_CRYPT_LOCATION_DL_OBJECT; + params_len = sizeof(params.init); - crypt_in = NULL; - crypt_in_len = 0; - *crypt_out_p = NULL; - *crypt_out_len_p = 0; ushort2bebytes(len_buf, datalen); @@ -1752,26 +1729,34 @@ static int coolkey_rsa_op(sc_card_t *card, goto done; } - r = coolkey_write_object(card, COOLKEY_DL_OBJECT_ID, 2, data, datalen, priv->nonce, - sizeof(priv->nonce)); + r = coolkey_write_object(card, COOLKEY_DL_OBJECT_ID, 2, data, datalen, priv->nonce, sizeof(priv->nonce)); if (r < 0) { goto done; } + ushort2bebytes(params.init.buf_len, 0); + } else { + /* The data fits in APDU. Copy it to the params object */ + params.init.location = COOLKEY_CRYPT_LOCATION_APDU; - } - ushort2bebytes(params.init.buf_len, crypt_in_len); - if (crypt_in_len) { - memcpy(params.buf, crypt_in, crypt_in_len); - } + params_len = sizeof(params.init) + datalen; + buf_out = &buf[0]; + crypt_out_p = &buf_out; + buf_len = sizeof(buf); + crypt_out_len_p = buf_len; + + ushort2bebytes(params.init.buf_len, datalen); + memcpy(params.buf, data, datalen); + } r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_COMPUTE_CRYPT, key_number, COOLKEY_CRYPT_ONE_STEP, (u8 *)¶ms, params_len, - crypt_out_p, crypt_out_len_p, priv->nonce, sizeof(priv->nonce)); - + crypt_out_p, &crypt_out_len_p, priv->nonce, sizeof(priv->nonce)); if (r < 0) { goto done; } + buf_len = crypt_out_len_p; + if (datalen > MAX_COMPUTE_BUF) { u8 len_buf[2]; size_t out_length; @@ -1789,10 +1774,19 @@ static int coolkey_rsa_op(sc_card_t *card, priv->nonce, sizeof(priv->nonce)); } else { - size_t out_length = bebytes2ushort(buf); + size_t out_length; + if (buf_len < 2) { + r = SC_ERROR_WRONG_LENGTH; + goto done; + } + out_length = bebytes2ushort(buf); + if (out_length > (buf_len - 2)) { + r = SC_ERROR_WRONG_LENGTH; + goto done; + } out_length = MIN(out_length, max_out_len); - memcpy(out, buf+2, out_length); - r = out_length; + memcpy(out, buf + 2, out_length); + r = (int)out_length; } done: @@ -1814,10 +1808,15 @@ static int coolkey_ecc_op(sc_card_t *card, u8 key_number; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "datalen=%"SC_FORMAT_LEN_SIZE_T"u outlen=%"SC_FORMAT_LEN_SIZE_T"u\n", datalen, outlen); + if (datalen > sizeof(params.buf)) { + r = SC_ERROR_INVALID_ARGUMENTS; + goto done; + } + crypt_in = data; crypt_in_len = datalen; @@ -1903,7 +1902,7 @@ static unsigned short coolkey_get_key_id(unsigned long object_id) { } /* - * COOLKEY cards don't select objects in the applet, objects are selected by a paramter + * COOLKEY cards don't select objects in the applet, objects are selected by a parameter * to the APDU. We create paths for the object in which the path value is the object_id * and the path type is SC_PATH_SELECT_FILE_ID (so we could cache at the PKCS #15 level if * we wanted to. @@ -1914,13 +1913,16 @@ static int coolkey_select_file(sc_card_t *card, const sc_path_t *in_path, sc_fil { int r; struct sc_file *file = NULL; - coolkey_private_data_t * priv = COOLKEY_DATA(card); + coolkey_private_data_t *priv; unsigned long object_id; - assert(card != NULL && in_path != NULL); + if (card == NULL || in_path == NULL) + return SC_ERROR_INTERNAL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + priv = COOLKEY_DATA(card); + if (in_path->len != 4) { return SC_ERROR_OBJECT_NOT_FOUND; } @@ -1941,7 +1943,7 @@ static int coolkey_select_file(sc_card_t *card, const sc_path_t *in_path, sc_fil if (file_out) { file = sc_file_new(); if (file == NULL) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->path = *in_path; /* this could be like the FCI */ file->type = SC_PATH_TYPE_FILE_ID; @@ -1951,7 +1953,7 @@ static int coolkey_select_file(sc_card_t *card, const sc_path_t *in_path, sc_fil *file_out = file; } - return SC_SUCCESS; + return SC_SUCCESS; } static int coolkey_finish(sc_card_t *card) @@ -1978,6 +1980,11 @@ coolkey_add_object(coolkey_private_data_t *priv, unsigned long object_id, const new_object.id = object_id; new_object.length = object_length; + /* The object ID needs to be unique */ + if (coolkey_find_object_by_id(&priv->objects_list, object_id) != NULL) { + return SC_ERROR_INTERNAL; + } + if (object_data) { new_object.data = malloc(object_length + add_v1_record); if (new_object.data == NULL) { @@ -2048,7 +2055,8 @@ coolkey_process_combined_object(sc_card_t *card, coolkey_private_data_t *priv, u decompressed_header = (coolkey_decompressed_header_t *)decompressed_object; if (decompressed_object_len < sizeof(coolkey_decompressed_header_t)) { - return SC_ERROR_CORRUPTED_DATA; + r = SC_ERROR_CORRUPTED_DATA; + goto done; } object_offset = bebytes2ushort(decompressed_header->object_offset); object_count = bebytes2ushort(decompressed_header->object_count); @@ -2060,18 +2068,19 @@ coolkey_process_combined_object(sc_card_t *card, coolkey_private_data_t *priv, u */ /* make sure token_name doesn't overrun the buffer */ if (decompressed_header->token_name_length + - offsetof(coolkey_decompressed_header_t,token_name) > decompressed_object_len) { + offsetof(coolkey_decompressed_header_t, token_name) > decompressed_object_len) { r = SC_ERROR_CORRUPTED_DATA; goto done; } /* make sure it doesn't overlap the object space */ if (decompressed_header->token_name_length + - offsetof(coolkey_decompressed_header_t,token_name) > object_offset) { + offsetof(coolkey_decompressed_header_t, token_name) > object_offset) { r = SC_ERROR_CORRUPTED_DATA; goto done; } /* store the token name in the priv structure so the emulator can set it */ + free(priv->token_name); priv->token_name = malloc(decompressed_header->token_name_length+1); if (priv->token_name == NULL) { r = SC_ERROR_OUT_OF_MEMORY; @@ -2079,17 +2088,28 @@ coolkey_process_combined_object(sc_card_t *card, coolkey_private_data_t *priv, u } memcpy(priv->token_name, &decompressed_header->token_name[0], decompressed_header->token_name_length); - priv->token_name[decompressed_header->token_name_length] = 0; + priv->token_name[decompressed_header->token_name_length] = '\0'; priv->token_name_length = decompressed_header->token_name_length; - for (i=0; i < object_count && object_offset < decompressed_object_len; i++ ) { - u8 *current_object = &decompressed_object[object_offset]; - coolkey_combined_object_header_t *object_header = - (coolkey_combined_object_header_t *)current_object; - unsigned long object_id = bebytes2ulong(object_header->object_id); + for (i=0; i < object_count; i++) { + u8 *current_object = NULL; + coolkey_combined_object_header_t *object_header = NULL; + unsigned long object_id; int current_object_len; + /* Can we read the object header at all? */ + if ((object_offset + sizeof(coolkey_combined_object_header_t)) > decompressed_object_len) { + r = SC_ERROR_CORRUPTED_DATA; + goto done; + } + + current_object = &decompressed_object[object_offset]; + object_header = (coolkey_combined_object_header_t *)current_object; + + /* Parse object ID */ + object_id = bebytes2ulong(object_header->object_id); + /* figure out how big it is */ r = coolkey_v1_get_object_length(current_object, decompressed_object_len-object_offset); if (r < 0) { @@ -2103,6 +2123,7 @@ coolkey_process_combined_object(sc_card_t *card, coolkey_private_data_t *priv, u object_offset += current_object_len; /* record this object */ + sc_log(card->ctx, "Add new object id=%ld", object_id); r = coolkey_add_object(priv, object_id, current_object, current_object_len, 1); if (r) { goto done; @@ -2144,12 +2165,6 @@ static int coolkey_initialize(sc_card_t *card) if (card->drv_data) { return SC_SUCCESS; } - - /* Select a coolkey read the coolkey objects out */ - r = coolkey_select_applet(card); - if (r < 0) { - return r; - } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"Coolkey Applet found"); priv = coolkey_new_private_data(); @@ -2161,18 +2176,37 @@ static int coolkey_initialize(sc_card_t *card) if (r < 0) { goto cleanup; } + + /* Select a coolkey read the coolkey objects out */ + r = coolkey_select_applet(card); + if (r < 0) { + goto cleanup; + } + priv->protocol_version_major = life_cycle.protocol_version_major; priv->protocol_version_minor = life_cycle.protocol_version_minor; priv->pin_count = life_cycle.pin_count; priv->life_cycle = life_cycle.life_cycle; /* walk down the list of objects and read them off the token */ - for(r=coolkey_list_object(card, COOLKEY_LIST_RESET, &object_info); r >= 0; - r= coolkey_list_object(card, COOLKEY_LIST_NEXT, &object_info)) { - unsigned long object_id = bebytes2ulong(object_info.object_id); - unsigned short object_len = bebytes2ulong(object_info.object_length); - /* also look at the ACL... */ + r = coolkey_list_object(card, COOLKEY_LIST_RESET, &object_info); + while (r >= 0) { + unsigned long object_id; + unsigned long object_len; + + /* The card did not return what we expected: Lets try other objects */ + if ((size_t)r < (sizeof(object_info))) + break; + /* TODO also look at the ACL... */ + + object_id = bebytes2ulong(object_info.object_id); + object_len = bebytes2ulong(object_info.object_length); + /* Avoid insanely large data */ + if (object_len > MAX_FILE_SIZE) { + r = SC_ERROR_CORRUPTED_DATA; + goto cleanup; + } /* the combined object is a single object that can store the other objects. * most coolkeys provisioned by TPS has a single combined object that is @@ -2187,7 +2221,7 @@ static int coolkey_initialize(sc_card_t *card) break; } r = coolkey_read_object(card, COOLKEY_COMBINED_OBJECT_ID, 0, object, object_len, - priv->nonce, sizeof(priv->nonce)); + priv->nonce, sizeof(priv->nonce)); if (r < 0) { free(object); break; @@ -2198,39 +2232,54 @@ static int coolkey_initialize(sc_card_t *card) break; } combined_processed = 1; - continue; + } else { + sc_log(card->ctx, "Add new object id=%ld, len=%lu", object_id, object_len); + r = coolkey_add_object(priv, object_id, NULL, object_len, 0); + if (r != SC_SUCCESS) + sc_log(card->ctx, "coolkey_add_object() returned %d", r); } - r = coolkey_add_object(priv, object_id, NULL, object_len, 0); - if (r != SC_SUCCESS) - sc_log(card->ctx, "coolkey_add_object() returned %d", r); + /* Read next object: error is handled on the cycle condition and below after cycle */ + r = coolkey_list_object(card, COOLKEY_LIST_NEXT, &object_info); } if (r != SC_ERROR_FILE_END_REACHED) { + /* This means the card does not cooperate at all: bail out */ + if (r >= 0) { + r = SC_ERROR_INVALID_CARD; + } goto cleanup; } /* if we didn't pull the cuid from the combined object, then grab it now */ if (!combined_processed) { global_platform_cplc_data_t cplc_data; - r = coolkey_get_cplc_data(card, &cplc_data); + /* select the card manager, because a card with applet only will have + already selected the coolkey applet */ + + r = gp_select_card_manager(card); + if (r < 0) { + goto cleanup; + } + + r = gp_get_cplc_data(card, &cplc_data); if (r < 0) { goto cleanup; } coolkey_make_cuid_from_cplc(&priv->cuid, &cplc_data); priv->token_name = (u8 *)strdup("COOLKEY"); if (priv->token_name == NULL) { - r= SC_ERROR_OUT_OF_MEMORY; + r = SC_ERROR_OUT_OF_MEMORY; goto cleanup; } priv->token_name_length = sizeof("COOLKEY")-1; } card->drv_data = priv; - return SC_SUCCESS; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); cleanup: if (priv) { coolkey_free_private_data(priv); } - return r; + LOG_FUNC_RETURN(card->ctx, r); } @@ -2238,13 +2287,29 @@ static int coolkey_initialize(sc_card_t *card) static int coolkey_match_card(sc_card_t *card) { int r; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - /* Since we send an APDU, the card's logout function may be called... - * however it may be in dirty memory */ - card->ops->logout = NULL; r = coolkey_select_applet(card); - return (r >= SC_SUCCESS); + if (r == SC_SUCCESS) { + sc_apdu_t apdu; + + /* The GET STATUS INS with P1 = 1 returns invalid instruction (0x6D00) + * on Coolkey applet (reserved for GetMemory function), + * while incorrect P1 (0x9C10) on Muscle applets + */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, COOLKEY_INS_GET_STATUS, 0x01, 0x00); + apdu.cla = COOLKEY_CLASS; + apdu.le = 0x00; + apdu.resplen = 0; + apdu.resp = NULL; + r = sc_transmit_apdu(card, &apdu); + if (r == SC_SUCCESS && apdu.sw1 == 0x6d && apdu.sw2 == 0x00) { + return 1; + } + return 0; + } + return 0; } @@ -2259,7 +2324,7 @@ static int coolkey_init(sc_card_t *card) r = coolkey_initialize(card); if (r < 0) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } card->type = SC_CARD_TYPE_COOLKEY_GENERIC; @@ -2267,7 +2332,7 @@ static int coolkey_init(sc_card_t *card) /* set Token Major/minor version */ flags = SC_ALGORITHM_RSA_RAW; - _sc_card_add_rsa_alg(card, 1024, flags, 0); /* manditory */ + _sc_card_add_rsa_alg(card, 1024, flags, 0); /* mandatory */ _sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */ _sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */ @@ -2284,11 +2349,12 @@ static int coolkey_init(sc_card_t *card) card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } + static int -coolkey_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) +coolkey_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) { int r; coolkey_private_data_t * priv = COOLKEY_DATA(card); @@ -2311,9 +2377,6 @@ coolkey_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) */ data->pin1.tries_left = 0xf; } - if (tries_left) { - *tries_left = data->pin1.tries_left; - } r = SC_SUCCESS; break; @@ -2342,6 +2405,7 @@ coolkey_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) return r; } + static int coolkey_logout(sc_card_t *card) { @@ -2358,6 +2422,20 @@ coolkey_logout(sc_card_t *card) return SC_SUCCESS; } + +static int coolkey_card_reader_lock_obtained(sc_card_t *card, int was_reset) +{ + int r = SC_SUCCESS; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (was_reset > 0) { + r = coolkey_select_applet(card); + } + + LOG_FUNC_RETURN(card->ctx, r); +} + static struct sc_card_operations coolkey_ops; static struct sc_card_driver coolkey_drv = { @@ -2388,6 +2466,7 @@ static struct sc_card_driver * sc_get_driver(void) coolkey_ops.check_sw = coolkey_check_sw; coolkey_ops.pin_cmd = coolkey_pin_cmd; coolkey_ops.logout = coolkey_logout; + coolkey_ops.card_reader_lock_obtained = coolkey_card_reader_lock_obtained; return &coolkey_drv; } diff --git a/src/libopensc/card-default.c b/src/libopensc/card-default.c index a17c2acb56..22fcf1cb84 100644 --- a/src/libopensc/card-default.c +++ b/src/libopensc/card-default.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/src/libopensc/card-dnie.c b/src/libopensc/card-dnie.c index 6260e81c7f..b2ab102741 100644 --- a/src/libopensc/card-dnie.c +++ b/src/libopensc/card-dnie.c @@ -4,7 +4,7 @@ * Copyright (C) 2010 Juan Antonio Martinez * * This work is derived from many sources at OpenSC Project site, - * (see references) and the information made public for Spanish + * (see references) and the information made public for Spanish * Direccion General de la Policia y de la Guardia Civil * * This library is free software; you can redistribute it and/or @@ -19,7 +19,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __CARD_DNIE_C__ @@ -38,7 +38,6 @@ #include "opensc.h" #include "cardctl.h" #include "internal.h" -#include "compression.h" #include "cwa14890.h" #include "cwa-dnie.h" @@ -60,9 +59,9 @@ #define USER_CONSENT_TITLE "Confirm" extern int dnie_read_file( - sc_card_t * card, - const sc_path_t * path, - sc_file_t ** file, + sc_card_t * card, + const sc_path_t * path, + sc_file_t ** file, u8 ** buffer, size_t * length); #define DNIE_CHIP_NAME "DNIe: Spanish eID card" @@ -75,17 +74,17 @@ extern int dnie_read_file( /** * SW internal apdu response table. * - * Override APDU response error codes from iso7816.c to allow + * Override APDU response error codes from iso7816.c to allow * handling of SM specific error */ -static struct sc_card_error dnie_errors[] = { +static const struct sc_card_error dnie_errors[] = { {0x6688, SC_ERROR_SM, "Cryptographic checksum invalid"}, {0x6987, SC_ERROR_SM, "Expected SM Data Object missing"}, {0x6988, SC_ERROR_SM, "SM Data Object incorrect"}, {0, 0, NULL} }; -/* +/* * DNIe ATR info from DGP web page * Tag Value Meaning @@ -128,7 +127,7 @@ H13-H15: 0x0F 0x65 0x81 final phase: tarjeta no operativa * OpenDNIe defines two ATR's for user and finalized card state */ static struct sc_atr_table dnie_atrs[] = { - /* TODO: get ATR for uninitalized DNIe */ + /* TODO: get ATR for uninitialized DNIe */ { /** card activated; normal operation state */ "3B:7F:00:00:00:00:6A:44:4E:49:65:00:00:00:00:00:00:03:90:00", "FF:FF:00:FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:FF:FF:FF", @@ -163,6 +162,7 @@ const char *user_consent_message="Esta a punto de realizar una firma digital\nco */ char *user_consent_msgs[] = { "SETTITLE", "SETDESC", "CONFIRM", "BYE" }; +#if !defined(__APPLE__) && !defined(_WIN32) /** * Do fgets() without interruptions. * @@ -181,6 +181,7 @@ static char *nointr_fgets(char *s, int size, FILE *stream) } return s; } +#endif /** * Ask for user consent. @@ -201,7 +202,7 @@ int dnie_ask_user_consent(struct sc_card * card, const char *title, const char * CFStringRef header_ref; /* to store title */ CFStringRef message_ref; /* to store message */ #endif -#ifdef linux +#if !defined(__APPLE__) && !defined(_WIN32) pid_t pid; FILE *fin=NULL; FILE *fout=NULL; /* to handle pipes as streams */ @@ -213,7 +214,7 @@ int dnie_ask_user_consent(struct sc_card * card, const char *title, const char * int n = 0; /* to iterate on to-be-sent messages */ #endif int res = SC_ERROR_INTERNAL; /* by default error :-( */ - char *msg = NULL; /* to makr errors */ + char *msg = NULL; /* to mark errors */ if ((card == NULL) || (card->ctx == NULL)) return SC_ERROR_INVALID_ARGUMENTS; @@ -246,7 +247,7 @@ int dnie_ask_user_consent(struct sc_card * card, const char *title, const char * header_ref = CFStringCreateWithCString( NULL, title, strlen(title) ); message_ref = CFStringCreateWithCString( NULL,message, strlen(message) ); - /* Displlay user notification alert */ + /* Display user notification alert */ CFUserNotificationDisplayAlert( 0, /* no timeout */ kCFUserNotificationNoteAlertLevel, /* Alert level */ @@ -269,15 +270,7 @@ int dnie_ask_user_consent(struct sc_card * card, const char *title, const char * if( result == kCFUserNotificationAlternateResponse ) LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ALLOWED); -#elif linux - /* check that user_consent_app exists. TODO: check if executable */ - res = stat(GET_DNIE_UI_CTX(card).user_consent_app, &st_file); - if (res != 0) { - sc_log(card->ctx, "Invalid pinentry application: %s\n", - GET_DNIE_UI_CTX(card).user_consent_app); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); - } - +#else /* just a simple bidirectional pipe+fork+exec implementation */ /* In a pipe, xx[0] is for reading, xx[1] is for writing */ if (pipe(srv_send) < 0) { @@ -302,10 +295,17 @@ int dnie_ask_user_consent(struct sc_card * card, const char *title, const char * close(srv_send[1]); close(srv_recv[0]); close(srv_recv[1]); - /* call exec() with proper user_consent_app from configuration */ - /* if ok should never return */ - execlp(GET_DNIE_UI_CTX(card).user_consent_app, GET_DNIE_UI_CTX(card).user_consent_app, (char *)NULL); - sc_log(card->ctx, "execlp() error"); + /* check that user_consent_app exists. TODO: check if executable */ + res = stat(GET_DNIE_UI_CTX(card).user_consent_app, &st_file); + if (res != 0) { + sc_log(card->ctx, "Invalid pinentry application: %s\n", + GET_DNIE_UI_CTX(card).user_consent_app); + } else { + /* call exec() with proper user_consent_app from configuration */ + /* if ok should never return */ + execlp(GET_DNIE_UI_CTX(card).user_consent_app, GET_DNIE_UI_CTX(card).user_consent_app, (char *)NULL); + sc_log(card->ctx, "execlp() error"); + } abort(); default: /* parent */ /* Close the pipe ends that the child uses to read from / write to @@ -359,8 +359,6 @@ int dnie_ask_user_consent(struct sc_card * card, const char *title, const char * /* close out channel to force client receive EOF and also die */ if (fout != NULL) fclose(fout); if (fin != NULL) fclose(fin); -#else -#error "Don't know how to handle user consent in this (rare) Operating System" #endif if (msg != NULL) sc_log(card->ctx, "%s", msg); @@ -396,8 +394,8 @@ static sc_card_driver_t dnie_driver = { /** * Parse configuration file for dnie parameters. * - * DNIe card driver has two main paramaters: - * - The name of the user consent Application to be used in Linux. This application shoud be any of pinentry-xxx family + * DNIe card driver has two main parameters: + * - The name of the user consent Application to be used in Linux. This application should be any of pinentry-xxx family * - A flag to indicate if user consent is to be used in this driver. If false, the user won't be prompted for confirmation on signature operations * * @See ../../etc/opensc.conf for details @@ -410,7 +408,7 @@ static sc_card_driver_t dnie_driver = { */ #ifdef ENABLE_DNIE_UI static int dnie_get_environment( - sc_card_t * card, + sc_card_t * card, ui_context_t * ui_context) { int i; @@ -443,13 +441,13 @@ static int dnie_get_environment( /************************** cardctl defined operations *******************/ -/** +/** * Generate a public/private key pair. * - * Manual says that generate_keys() is a reserved operation; that is: - * only can be done at DGP offices. But several authors talk about + * Manual says that generate_keys() is a reserved operation; that is: + * only can be done at DGP offices. But several authors talk about * this operation is available also outside. So need to test :-) - * Notice that write operations are not supported, so we can't use + * Notice that write operations are not supported, so we can't use * created keys to generate and store new certificates into the card. * TODO: copy code from card-jcop.c::jcop_generate_keys() * @param card pointer to card info data @@ -469,7 +467,7 @@ static int dnie_generate_key(sc_card_t * card, void *data) /** * Analyze a buffer looking for provided data pattern. * - * Comodity function for dnie_get_info() that searches a byte array + * Commodity function for dnie_get_info() that searches a byte array * in provided buffer * * @param card pointer to card info data @@ -494,6 +492,10 @@ static char *findPattern(u8 *pat, u8 *buf, size_t len) data_found: /* assume length is less than 128 bytes, so is coded in 1 byte */ size = 0x000000ff & (int) *(from+6); + if (from + 7 + size > buf + len) { + /* not enough data in the source buffer -- invalid data received */ + return NULL; + } if ( size == 0 ) return NULL; /* empty data */ res = calloc( size+1, sizeof(char) ); if ( res == NULL) return NULL; /* calloc() error */ @@ -509,10 +511,10 @@ static char *findPattern(u8 *pat, u8 *buf, size_t len) * No need to enter pin nor use Secure Channel * * Notice that this is done by mean of a dirty trick: instead - * of parsing ASN1 data on EF(CDF), + * of parsing ASN1 data on EF(CDF), * we look for desired OID patterns in binary array * - * @param card pointer to card info data + * @param card pointer to card info data * @param data where to store function results (number,name,surname,idesp,version) * @return SC_SUCCESS if ok, else error code */ @@ -558,8 +560,8 @@ static int dnie_get_info(sc_card_t * card, char *data[]) sc_file_free(file); file = NULL; if (buffer) { - free(buffer); - buffer=NULL; + free(buffer); + buffer=NULL; bufferlen=0; } res = dnie_read_file(card, &path, &file, &buffer, &bufferlen); @@ -581,11 +583,11 @@ static int dnie_get_info(sc_card_t * card, char *data[]) sc_file_free(file); file = NULL; if (buffer) { - free(buffer); - buffer=NULL; + free(buffer); + buffer=NULL; bufferlen=0; } - /* + /* * Some old DNIe cards seems not to include SW version file, * so let this code fail without notice */ @@ -645,15 +647,14 @@ static int dnie_get_serialnr(sc_card_t * card, sc_serial_number_t * serial) /* if serial number is cached, use it */ if (card->serialnr.len) { memcpy(serial, &card->serialnr, sizeof(*serial)); - sc_log(card->ctx, "Serial Number (cached): '%s'", - sc_dump_hex(serial->value, serial->len)); + sc_log_hex(card->ctx, "Serial Number (cached)", serial->value, serial->len); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* not cached, retrieve it by mean of an APDU */ /* official driver read 0x11 bytes, but only uses 7. Manual says just 7 (for le) */ dnie_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xb8, 0x00, 0x00, 0x07, 0, rbuf, sizeof(rbuf), NULL, 0); - apdu.cla = 0x90; /* propietary cmd */ + apdu.cla = 0x90; /* proprietary cmd */ /* send apdu */ result = sc_transmit_apdu(card, &apdu); if (result != SC_SUCCESS) { @@ -672,8 +673,7 @@ static int dnie_get_serialnr(sc_card_t * card, sc_serial_number_t * serial) */ /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*serial)); - sc_log(card->ctx, "Serial Number (apdu): '%s'", - sc_dump_hex(serial->value, serial->len)); + sc_log_hex(card->ctx, "Serial Number (apdu)", serial->value, serial->len); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -714,6 +714,7 @@ static void init_flags(struct sc_card *card) /* RSA Support with PKCS1.5 padding */ algoflags = SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_PAD_PKCS1; _sc_card_add_rsa_alg(card, 1024, algoflags, 0); + _sc_card_add_rsa_alg(card, 1920, algoflags, 0); _sc_card_add_rsa_alg(card, 2048, algoflags, 0); } @@ -759,15 +760,27 @@ static int dnie_sm_free_wrapped_apdu(struct sc_card *card, if ((*sm_apdu) != plain) { rv = cwa_decode_response(card, provider, *sm_apdu); - if (plain) { - plain->resplen = (*sm_apdu)->resplen; + if (plain && rv == SC_SUCCESS) { + if (plain->resp) { + /* copy the response into the original resp buffer */ + if ((*sm_apdu)->resplen <= plain->resplen) { + memcpy(plain->resp, (*sm_apdu)->resp, (*sm_apdu)->resplen); + plain->resplen = (*sm_apdu)->resplen; + } else { + sc_log(card->ctx, "Invalid initial length," + " needed %"SC_FORMAT_LEN_SIZE_T"u bytes" + " but has %"SC_FORMAT_LEN_SIZE_T"u", + (*sm_apdu)->resplen, plain->resplen); + rv = SC_ERROR_BUFFER_TOO_SMALL; + } + } plain->sw1 = (*sm_apdu)->sw1; plain->sw2 = (*sm_apdu)->sw2; - if (((*sm_apdu)->data) != plain->data) - free((unsigned char *) (*sm_apdu)->data); - if ((*sm_apdu)->resp != plain->resp) - free((*sm_apdu)->resp); } + if (plain == NULL || (*sm_apdu)->data != plain->data) + free((unsigned char *) (*sm_apdu)->data); + if (plain == NULL || (*sm_apdu)->resp != plain->resp) + free((*sm_apdu)->resp); free(*sm_apdu); } *sm_apdu = NULL; @@ -779,7 +792,6 @@ static int dnie_sm_get_wrapped_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_apdu **sm_apdu) { struct sc_context *ctx = card->ctx; - struct sc_apdu *apdu = NULL; cwa_provider_t *provider = NULL; int rv = SC_SUCCESS; @@ -790,28 +802,18 @@ static int dnie_sm_get_wrapped_apdu(struct sc_card *card, provider = GET_DNIE_PRIV_DATA(card)->cwa_provider; if (((plain->cla & 0x0C) == 0) && (plain->ins != 0xC0)) { - *sm_apdu = NULL; - //construct new SM apdu from original apdu - apdu = calloc(1, sizeof(struct sc_apdu)); - if (!apdu) + *sm_apdu = calloc(1, sizeof(struct sc_apdu)); + if (!(*sm_apdu)) return SC_ERROR_OUT_OF_MEMORY; - memcpy(apdu, plain, sizeof(sc_apdu_t)); - - rv = cwa_encode_apdu(card, provider, plain, apdu); + rv = cwa_encode_apdu(card, provider, plain, *sm_apdu); if (rv != SC_SUCCESS) { - dnie_sm_free_wrapped_apdu(card, NULL, &apdu); - goto err; + dnie_sm_free_wrapped_apdu(card, plain, sm_apdu); } - - *sm_apdu = apdu; - } else + } else *sm_apdu = plain; - apdu = NULL; -err: - free(apdu); LOG_FUNC_RETURN(ctx, rv); } @@ -839,7 +841,7 @@ static int dnie_init(struct sc_card *card) /* create and initialize cwa-dnie provider*/ provider = dnie_get_cwa_provider(card); - if (!provider) + if (!provider) LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, "Error initializing cwa-dnie provider"); /** Secure messaging initialization section **/ @@ -848,25 +850,30 @@ static int dnie_init(struct sc_card *card) card->sm_ctx.ops.free_sm_apdu = dnie_sm_free_wrapped_apdu; card->sm_ctx.sm_mode = SM_MODE_NONE; - init_flags(card); - - res=cwa_create_secure_channel(card,provider,CWA_SM_OFF); - LOG_TEST_RET(card->ctx, res, "Failure creating CWA secure channel."); + res = cwa_create_secure_channel(card, provider, CWA_SM_OFF); + if (res < 0) + free(provider); + LOG_TEST_RET(card->ctx, res, "Failure resetting CWA secure channel."); /* initialize private data */ card->drv_data = calloc(1, sizeof(dnie_private_data_t)); - if (card->drv_data == NULL) + if (card->drv_data == NULL) { + free(provider); LOG_TEST_RET(card->ctx, SC_ERROR_OUT_OF_MEMORY, "Could not allocate DNIe private data."); + } #ifdef ENABLE_DNIE_UI /* read environment from configuration file */ res = dnie_get_environment(card, &(GET_DNIE_UI_CTX(card))); if (res != SC_SUCCESS) { free(card->drv_data); + free(provider); LOG_TEST_RET(card->ctx, res, "Failure reading DNIe environment."); } #endif + init_flags(card); + GET_DNIE_PRIV_DATA(card)->cwa_provider = provider; LOG_FUNC_RETURN(card->ctx, res); @@ -876,7 +883,7 @@ static int dnie_init(struct sc_card *card) * De-initialization routine. * * Called when the card object is being freed. finish() has to - * deallocate all possible private data. + * deallocate all possible private data. * * @param card Pointer to card driver data structure * @return SC_SUCCESS if ok; else error code @@ -896,86 +903,47 @@ static int dnie_finish(struct sc_card *card) /* ISO 7816-4 functions */ /** - * Convert little-endian data into unsigned long. - * - * @param pt pointer to little-endian data - * @return equivalent long - */ -static unsigned long le2ulong(u8 * pt) -{ - unsigned long res = 0L; - if (pt==NULL) return res; - res = (0xff & *(pt + 0)) + - ((0xff & *(pt + 1)) << 8) + - ((0xff & *(pt + 2)) << 16) + ((0xff & *(pt + 3)) << 24); - return res; -} - -/** - * Uncompress data if in compressed format. + * Check whether data are compressed. * - * @param card poiner to sc_card_t structure + * @param card pointer to sc_card_t structure * @param from buffer to get data from - * @param len pointer to buffer length - * @return uncompresed or original buffer; len points to new buffer length - * on error return null + * @param len buffer length + * @return 1 if data are compressed, 0 otherwise; len points to expected length of decompressed data */ -static u8 *dnie_uncompress(sc_card_t * card, u8 * from, size_t *len) + +static int dnie_is_compressed(sc_card_t * card, u8 * from, size_t len) { - u8 *upt = from; #ifdef ENABLE_ZLIB - int res = SC_SUCCESS; size_t uncompressed = 0L; size_t compressed = 0L; if (!card || !card->ctx || !from || !len) - return NULL; + return 0; LOG_FUNC_CALLED(card->ctx); - /* if data size not enought for compression header assume uncompressed */ - if (*len < 8) + /* if data size not enough for compression header assume uncompressed */ + if (len < 8) goto compress_exit; /* evaluate compressed an uncompressed sizes (little endian format) */ - uncompressed = le2ulong(from); - compressed = le2ulong(from + 4); + uncompressed = lebytes2ulong(from); + compressed = lebytes2ulong(from + 4); /* if compressed size doesn't match data length assume not compressed */ - if (compressed != (*len) - 8) + if (compressed != len - 8) goto compress_exit; /* if compressed size greater than uncompressed, assume uncompressed data */ if (uncompressed < compressed) goto compress_exit; + /* Do not try to allocate insane size if we receive bogus data */ + if (uncompressed > MAX_FILE_SIZE) + goto compress_exit; - sc_log(card->ctx, "Data seems to be compressed. calling uncompress"); - /* ok: data seems to be compressed */ - upt = calloc(uncompressed, sizeof(u8)); - if (!upt) { - sc_log(card->ctx, "alloc() for uncompressed buffer failed"); - return NULL; - } - res = sc_decompress(upt, /* try to uncompress by calling sc_xx routine */ - (size_t *) & uncompressed, - from + 8, (size_t) compressed, COMPRESSION_ZLIB); - /* TODO: check that returned uncompressed size matches expected */ - if (res != SC_SUCCESS) { - sc_log(card->ctx, "Uncompress() failed or data not compressed"); - goto compress_exit; /* assume not need uncompression */ - } - /* Done; update buffer len and return pt to uncompressed data */ - *len = uncompressed; - sc_log(card->ctx, "Compressed data:\n%s\n", - sc_dump_hex(from + 8, compressed)); - sc_log(card->ctx, - "Uncompress() done. Before:'%"SC_FORMAT_LEN_SIZE_T"u' After: '%"SC_FORMAT_LEN_SIZE_T"u'", - compressed, uncompressed); - sc_log(card->ctx, "Uncompressed data:\n%s\n", - sc_dump_hex(upt, uncompressed)); - compress_exit: - + sc_log(card->ctx, "Data seems to be compressed."); + return 1; +compress_exit: #endif - sc_log(card->ctx, "uncompress: returning with%s de-compression ", - (upt == from) ? "out" : ""); - return upt; + sc_log(card->ctx, "Data not compressed."); + return 0; } /** @@ -986,24 +954,24 @@ static u8 *dnie_uncompress(sc_card_t * card, u8 * from, size_t *len) * * DNIe card stores user certificates in compressed format. so we need * some way to detect and uncompress on-the-fly compressed files, to - * let read_binary() work transparently. - * This is the main goal of this routine: create an in-memory buffer - * for read_binary operation, filling this buffer on first read_binary() - * call, and uncompress data if compression detected. Further + * let read_binary() work transparently. + * This is the main goal of this routine: create an in-memory buffer + * for read_binary operation, filling this buffer on first read_binary() + * call, and uncompress data if compression detected. Further * read_binary() calls then make use of cached data, instead * of accessing the card * * @param card Pointer to card structure * @return SC_SUCCESS if OK; else error code */ -static int dnie_fill_cache(sc_card_t * card) +static int dnie_fill_cache(sc_card_t * card,unsigned long *flags) { u8 tmp[MAX_RESP_BUFFER_SIZE]; sc_apdu_t apdu; size_t count = 0; size_t len = 0; u8 *buffer = NULL; - u8 *pt = NULL; + u8 *p; sc_context_t *ctx = NULL; if (!card || !card->ctx) @@ -1052,19 +1020,22 @@ static int dnie_fill_cache(sc_card_t * card) } if (r == SC_ERROR_INCORRECT_PARAMETERS) goto read_done; + free(buffer); if (apdu.resp != tmp) free(apdu.resp); LOG_FUNC_RETURN(ctx, r); /* arriving here means response error */ } - /* copy received data into buffer. realloc() if not enought space */ + /* copy received data into buffer. realloc() if not enough space */ count = apdu.resplen; - buffer = realloc(buffer, len + count); - if (!buffer) { + p = realloc(buffer, len + count); + if (!p) { + free(buffer); free((void *)apdu.data); if (apdu.resp != tmp) free(apdu.resp); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } + buffer = p; memcpy(buffer + len, apdu.resp, count); if (apdu.resp != tmp) { free(apdu.resp); @@ -1076,26 +1047,33 @@ static int dnie_fill_cache(sc_card_t * card) } read_done: - /* no more data to read: check if data is compressed */ - pt = dnie_uncompress(card, buffer, &len); - free((void *)apdu.data); + free((void *)apdu.data); if (apdu.resp != tmp) free(apdu.resp); - if (pt == NULL) { - sc_log(ctx, "Uncompress proccess failed"); + + if (dnie_is_compressed(card, buffer, len)) { + if (flags) + *flags |= SC_FILE_FLAG_COMPRESSED_ZLIB; + /* Remove first 8 bytes with compression info*/ + len -= 8; + p = malloc(len); + if (!p) { + free(buffer); + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + } + memcpy(p, buffer + 8, len); free(buffer); - LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); + buffer = p; } - if (pt != buffer) - free(buffer); + /* ok: as final step, set correct cache data into dnie_priv structures */ - GET_DNIE_PRIV_DATA(card)->cache = pt; + GET_DNIE_PRIV_DATA(card)->cache = buffer; GET_DNIE_PRIV_DATA(card)->cachelen = len; sc_log(ctx, "fill_cache() done. length '%"SC_FORMAT_LEN_SIZE_T"u' bytes", len); - LOG_FUNC_RETURN(ctx,len); + LOG_FUNC_RETURN(ctx, (int)len); } /** @@ -1106,16 +1084,17 @@ static int dnie_fill_cache(sc_card_t * card) * * @param card pointer to sc_card_t structure * @param idx offset from card file to ask data for - * @param buf where to store readed data. must be non null + * @param buf where to store read data. must be non null * @param count number of bytes to read * @param flags. not used - * @return number of bytes readed, 0 on EOF, error code on error + * @return number of bytes read, 0 on EOF, error code on error */ static int dnie_read_binary(struct sc_card *card, unsigned int idx, - u8 * buf, size_t count, unsigned long flags) + u8 * buf, size_t count, unsigned long *flags) { - int res = 0; + size_t res = 0; + int rc; sc_context_t *ctx = NULL; /* preliminary checks */ if (!card || !card->ctx || !buf || (count <= 0)) @@ -1125,8 +1104,8 @@ static int dnie_read_binary(struct sc_card *card, LOG_FUNC_CALLED(ctx); if (idx == 0 || GET_DNIE_PRIV_DATA(card)->cache == NULL) { /* on first block or no cache, try to fill */ - res = dnie_fill_cache(card); - if (res < 0) { + rc = dnie_fill_cache(card, flags); + if (rc < 0) { sc_log(ctx, "Cannot fill cache. using iso_read_binary()"); return iso_ops->read_binary(card, idx, buf, count, flags); } @@ -1135,8 +1114,8 @@ static int dnie_read_binary(struct sc_card *card, return 0; /* at eof */ res = MIN(count, GET_DNIE_PRIV_DATA(card)->cachelen - idx); /* eval how many bytes to read */ memcpy(buf, GET_DNIE_PRIV_DATA(card)->cache + idx, res); /* copy data from buffer */ - sc_log(ctx, "dnie_read_binary() '%d' bytes", res); - LOG_FUNC_RETURN(ctx, res); + sc_log(ctx, "dnie_read_binary() '%zu' bytes", res); + LOG_FUNC_RETURN(ctx, (int)res); } /** @@ -1155,8 +1134,6 @@ static int dnie_compose_and_send_apdu(sc_card_t *card, const u8 *path, size_t pa int res = 0; sc_apdu_t apdu; u8 rbuf[MAX_RESP_BUFFER_SIZE]; - sc_file_t *file = NULL; - sc_context_t *ctx = NULL; if (!card || !card->ctx) @@ -1165,7 +1142,7 @@ static int dnie_compose_and_send_apdu(sc_card_t *card, const u8 *path, size_t pa LOG_FUNC_CALLED(ctx); - dnie_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, p1, 0, + dnie_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, p1, 0, sc_get_max_recv_size(card), pathlen, rbuf, sizeof(rbuf), path, pathlen); if (p1 == 3) @@ -1193,14 +1170,19 @@ static int dnie_compose_and_send_apdu(sc_card_t *card, const u8 *path, size_t pa LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } - /* finally process FCI response */ - file = sc_file_new(); - if (file == NULL) { - LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + if (file_out) { + /* finally process FCI response */ + size_t len = apdu.resp[1]; + sc_file_free(*file_out); + *file_out = sc_file_new(); + if (*file_out == NULL) { + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + } + if (apdu.resplen - 2 < len || len < 1) { + LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + } + res = card->ops->process_fci(card, *file_out, apdu.resp + 2, len); } - res = card->ops->process_fci(card, file, apdu.resp + 2, apdu.resp[1]); - sc_file_free(*file_out); - *file_out = file; LOG_FUNC_RETURN(ctx, res); } @@ -1211,12 +1193,12 @@ static int dnie_compose_and_send_apdu(sc_card_t *card, const u8 *path, size_t pa * in ISO7816-4. Stores information about the selected file to * , if not NULL. * - * SELECT file in DNIe is a bit tricky: - * - only handles some types: + * SELECT file in DNIe is a bit tricky: + * - only handles some types: * -- SC_PATH_TYPE_FILE_ID 2-byte long file ID * -- SC_PATH_TYPE_DF_NAME named DF's - * -- SC_PATH_TYPE_PARENT jump to parent DF of current EF/DF - undocummented in DNIe manual - * -- other file types are marked as unssupported + * -- SC_PATH_TYPE_PARENT jump to parent DF of current EF/DF - undocumented in DNIe manual + * -- other file types are marked as unsupported * * - Also MF must be addressed by their Name, not their ID * So some magic is needed: @@ -1238,7 +1220,7 @@ static int dnie_select_file(struct sc_card *card, int res = SC_SUCCESS; sc_context_t *ctx = NULL; unsigned char tmp_path[sizeof(DNIE_MF_NAME)]; - int reminder = 0; + size_t reminder = 0; if (!card || !card->ctx || !in_path) return SC_ERROR_INVALID_ARGUMENTS; @@ -1249,8 +1231,8 @@ static int dnie_select_file(struct sc_card *card, switch (in_path->type) { case SC_PATH_TYPE_FILE_ID: /* pathlen must be of len=2 */ - /* - * gscriptor shows that DNIe also handles + /* + * gscriptor shows that DNIe also handles * Select child DF (p1=1) and Select EF (p1=2), * but we'll use P1=0 as general solution for all cases * @@ -1259,18 +1241,18 @@ static int dnie_select_file(struct sc_card *card, */ if (in_path->len != 2) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(ctx, "select_file(ID): %s", sc_dump_hex(in_path->value, in_path->len)); + sc_log_hex(ctx, "select_file(ID)", in_path->value, in_path->len); res = dnie_compose_and_send_apdu(card, in_path->value, in_path->len, 0, file_out); break; case SC_PATH_TYPE_DF_NAME: - sc_log(ctx, "select_file(NAME): %s", sc_dump_hex(in_path->value, in_path->len)); + sc_log_hex(ctx, "select_file(NAME)", in_path->value, in_path->len); res = dnie_compose_and_send_apdu(card, in_path->value, in_path->len, 4, file_out); break; case SC_PATH_TYPE_PATH: if ((in_path->len == 0) || ((in_path->len & 1) != 0)) /* not divisible by 2 */ LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(ctx, "select_file(PATH): requested:%s ", sc_dump_hex(in_path->value, in_path->len)); + sc_log_hex(ctx, "select_file(PATH): requested", in_path->value, in_path->len); /* convert to SC_PATH_TYPE_FILE_ID */ res = sc_lock(card); /* lock to ensure path traversal */ @@ -1278,7 +1260,7 @@ static int dnie_select_file(struct sc_card *card, if (memcmp(in_path->value, "\x3F\x00", 2) == 0) { /* if MF, use the name as path */ strcpy((char *)tmp_path, DNIE_MF_NAME); - sc_log(ctx, "select_file(NAME): requested:%s ", sc_dump_hex(tmp_path, sizeof(DNIE_MF_NAME) - 1)); + sc_log_hex(ctx, "select_file(NAME): requested", tmp_path, sizeof(DNIE_MF_NAME) - 1); res = dnie_compose_and_send_apdu(card, tmp_path, sizeof(DNIE_MF_NAME) - 1, 4, file_out); if (res != SC_SUCCESS) { sc_unlock(card); @@ -1335,60 +1317,36 @@ static int dnie_select_file(struct sc_card *card, * NOTE: * Official driver redundantly sets SM before execute this command * No reason to do it, as is needed to do SM handshake... - * Also: official driver reads in blocks of 20 bytes. - * Why? Manual and iso-7816-4 states that only 8 bytes - * are required... so we will obbey Manual + * Also: official driver reads in blocks of 20 bytes. + * Why? Manual and iso-7816-4 states that only 8 bytes + * are required... so we will obey Manual * * @param card Pointer to card Structure * @param rnd Where to store challenge * @param len requested challenge length * @return SC_SUCCESS if OK; else error code */ -#define BUFFER_SIZE 8 static int dnie_get_challenge(struct sc_card *card, u8 * rnd, size_t len) { - sc_apdu_t apdu; - u8 buf[MAX_RESP_BUFFER_SIZE]; - int result = SC_SUCCESS; - if ((card == NULL) || (card->ctx == NULL)) - return SC_ERROR_INVALID_ARGUMENTS; + /* As DNIe cannot handle other data length than 0x08 and 0x14 */ + u8 rbuf[8]; + size_t out_len; + int r; + LOG_FUNC_CALLED(card->ctx); - /* just a copy of iso7816::get_challenge() but call dnie_check_sw to - * look for extra error codes */ - if ( (rnd==NULL) || (len==0) ) { - /* no valid buffer provided */ - result = SC_ERROR_INVALID_ARGUMENTS; - goto dnie_get_challenge_error; - } - dnie_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0x00, 0x00, BUFFER_SIZE, 0, - buf, MAX_RESP_BUFFER_SIZE, NULL, 0); - /* - * As DNIe cannot handle other data length than 0x08 and 0x14, - * perform consecutive reads of 8 bytes until retrieve requested length - */ - while (len > 0) { - size_t n = len > BUFFER_SIZE ? BUFFER_SIZE : len; - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0x00, 0x00); - apdu.le = BUFFER_SIZE; - apdu.resp = buf; - apdu.resplen = MAX_RESP_BUFFER_SIZE; /* include SW's */ - result = sc_transmit_apdu(card, &apdu); - if (result != SC_SUCCESS) { - LOG_TEST_RET(card->ctx, result, "APDU transmit failed"); - } - if (apdu.resplen != BUFFER_SIZE) { - result = sc_check_sw(card, apdu.sw1, apdu.sw2); - goto dnie_get_challenge_error; - } - memcpy(rnd, apdu.resp, n); - len -= n; - rnd += n; + r = iso_ops->get_challenge(card, rbuf, sizeof rbuf); + LOG_TEST_RET(card->ctx, r, "GET CHALLENGE cmd failed"); + + if (len < (size_t) r) { + out_len = len; + } else { + out_len = (size_t) r; } - result = SC_SUCCESS; - dnie_get_challenge_error: - LOG_FUNC_RETURN(card->ctx, result); + memcpy(rnd, rbuf, out_len); + + LOG_FUNC_RETURN(card->ctx, (int) out_len); } /* @@ -1406,7 +1364,6 @@ static int dnie_get_challenge(struct sc_card *card, u8 * rnd, size_t len) static int dnie_logout(struct sc_card *card) { int result = SC_SUCCESS; - sc_file_t *file = NULL; if ((card == NULL) || (card->ctx == NULL)) return SC_ERROR_INVALID_ARGUMENTS; @@ -1414,18 +1371,11 @@ static int dnie_logout(struct sc_card *card) LOG_FUNC_CALLED(card->ctx); if (card->sm_ctx.sm_mode != SM_MODE_NONE) { /* mark the channel as closed */ - result = cwa_create_secure_channel(card, + result = cwa_create_secure_channel(card, GET_DNIE_PRIV_DATA(card)->cwa_provider, CWA_SM_OFF); LOG_TEST_RET(card->ctx, result, "Cannot close the secure channel"); - /* request the Master File to provoke an SM error and close the channel */ - result = dnie_compose_and_send_apdu(card, (const u8 *) DNIE_MF_NAME, - sizeof(DNIE_MF_NAME) - 1, 4, &file); - if (result == SC_ERROR_SM) - result = SC_SUCCESS; } - if (file != NULL) - sc_file_free(file); LOG_FUNC_RETURN(card->ctx, result); } @@ -1434,8 +1384,8 @@ static int dnie_logout(struct sc_card *card) * * Initializes the security environment on card * according to , and stores the environment as on the - * card. If se_num <= 0, the environment will not be stored. - * Notice that OpenDNIe SM handling requires a buffer longer than + * card. If se_num <= 0, the environment will not be stored. + * Notice that OpenDNIe SM handling requires a buffer longer than * provided for this command; so special apdu is used in cwa code * * @param card Pointer to card driver Structure @@ -1477,7 +1427,6 @@ static int dnie_set_security_env(struct sc_card *card, case SC_ALGORITHM_RSA: result = SC_SUCCESS; break; - case SC_ALGORITHM_DSA: case SC_ALGORITHM_EC: case SC_ALGORITHM_GOSTR3410: default: @@ -1487,7 +1436,7 @@ static int dnie_set_security_env(struct sc_card *card, LOG_TEST_RET(card->ctx, result, "Unsupported algorithm"); if ((env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) == 0) { result = SC_ERROR_NOT_SUPPORTED; - /* TODO: + /* TODO: * Manual says that only RSA with SHA1 is supported, but found * some docs where states that SHA256 is also handled */ @@ -1537,16 +1486,11 @@ static int dnie_set_security_env(struct sc_card *card, apdu.p1 = 0x41; /* SET; internal operation */ apdu.p2 = 0xB6; /* Template for Digital Signature */ break; - case SC_SEC_OPERATION_AUTHENTICATE: - /* TODO: _set_security_env() study diffs on internal/external auth */ - apdu.p1 = 0x41; /* SET; internal operation */ - apdu.p2 = 0xA4; /* Template for Authenticate */ - break; default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } - /* Notice that Manual states that DNIE only allows handle of + /* Notice that Manual states that DNIE only allows handle of * current security environment, so se_num is ignored, and * store sec env apdu (00 22 F2 se_num) command will not be issued */ @@ -1572,7 +1516,7 @@ static int dnie_set_security_env(struct sc_card *card, * ADD: seems that DNIe supports a minimal cipher/decipher operation * but restricted to 1024 data chunks . Need more info and tests * - * @param card Pointer to Card Driver Structure + * @param card Pointer to Card Driver Structure * @param crgram cryptogram to be (de)ciphered * @param crgram_len cryptogram length * @param out where to store result @@ -1605,7 +1549,7 @@ static int dnie_decipher(struct sc_card *card, * (90 74 40 keyID). This code uses standard 00 2A 80 8x one) * as shown in card-atrust-acos.c and card-jcop.c */ - dnie_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, + dnie_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, /* INS: 0x2A perform security operation */ 0x80, /* P1: Response is plain value */ 0x86, /* P2: 8x: Padding indicator byte followed by cryptogram */ @@ -1629,11 +1573,11 @@ static int dnie_decipher(struct sc_card *card, /** * OpenDNIe implementation of Compute_Signature() card driver operation. * - * Generates a digital signature on the card. - * This function handles the process of hash + sign + * Generates a digital signature on the card. + * This function handles the process of hash + sign * with previously selected keys (by mean of set_security environment * - * AS iso7816 and DNIe Manual states there are 3 ways to perform + * AS iso7816 and DNIe Manual states there are 3 ways to perform * this operation: * * - (plaintext) Hash on plaintext + sign @@ -1644,7 +1588,7 @@ static int dnie_decipher(struct sc_card *card, * and applies * * @param card pointer to sc_card_t structure - * @param data data to be hased/signed + * @param data data to be hashed/signed * @param datalen length of provided data * @param out buffer to store results into * @param outlen available space in result buffer @@ -1657,11 +1601,11 @@ static int dnie_compute_signature(struct sc_card *card, u8 * out, size_t outlen) { int result = SC_SUCCESS; - int result_resplen = 0; + size_t result_resplen = 0; struct sc_apdu apdu; u8 rbuf[MAX_RESP_BUFFER_SIZE]; /* to receive sign response */ - /* some preliminar checks */ + /* some preliminary checks */ if ((card == NULL) || (card->ctx == NULL)) return SC_ERROR_INVALID_ARGUMENTS; /* OK: start working */ @@ -1671,8 +1615,6 @@ static int dnie_compute_signature(struct sc_card *card, LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); if (datalen > SC_MAX_APDU_BUFFER_SIZE) /* should be 256 */ LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); - if (outlen<256) /* enought space to store 2048 bit response */ - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); #ifdef ENABLE_DNIE_UI /* (Requested by DGP): on signature operation, ask user consent */ @@ -1680,18 +1622,18 @@ static int dnie_compute_signature(struct sc_card *card, result = dnie_ask_user_consent(card,user_consent_title,user_consent_message); LOG_TEST_RET(card->ctx, result, "User consent denied"); } -#endif +#endif /* - Seems that OpenSC already provides pkcs#1 v1.5 DigestInfo structure - with pre-calculated hash. So no need to to any Hash calculation, + Seems that OpenSC already provides pkcs#1 v1.5 DigestInfo structure + with pre-calculated hash. So no need to to any Hash calculation, So just extract 15+20 DigestInfo+Hash info from ASN.1 provided data and feed them into sign() command */ - sc_log(card->ctx, - "Compute signature len: '%"SC_FORMAT_LEN_SIZE_T"u' bytes:\n%s\n============================================================", - datalen, sc_dump_hex(data, datalen)); + sc_log_hex(card->ctx, + "Compute signature\n============================================================", + data, datalen); /*INS: 0x2A PERFORM SECURITY OPERATION * P1: 0x9E Resp: Digital Signature @@ -1711,9 +1653,11 @@ static int dnie_compute_signature(struct sc_card *card, /* ok: copy result from buffer */ result_resplen = apdu.resplen; + if (outlen < result_resplen) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); memcpy(out, apdu.resp, result_resplen); /* and return response length */ - LOG_FUNC_RETURN(card->ctx, result_resplen); + LOG_FUNC_RETURN(card->ctx, (int)result_resplen); } /* @@ -1776,7 +1720,7 @@ static int dnie_list_files(sc_card_t * card, u8 * buf, size_t buflen) sc_log(card->ctx, "List file '%02X%02X' failed", id1, id2); /* if file not found, continue; else abort */ - if (res != SC_ERROR_FILE_NOT_FOUND) + if (res != SC_ERROR_FILE_NOT_FOUND) LOG_FUNC_RETURN(card->ctx, res); continue; } @@ -1785,8 +1729,8 @@ static int dnie_list_files(sc_card_t * card, u8 * buf, size_t buflen) /* store id into buffer */ *(buf + count++) = data[0]; *(buf + count++) = data[1]; - /* TODO: - * if found file is a DF go back to parent DF + /* TODO: + * if found file is a DF go back to parent DF * to continue search */ } } @@ -1896,7 +1840,7 @@ static int dnie_card_ctl(struct sc_card *card, * the read_binary() file cache work * * Extract real file length from compressed file is done by mean of - * reading 8 first bytes for uncompressed/compressed lenght. + * reading 8 first bytes for uncompressed/compressed length. * Lengths are provided as two 4-byte little endian numbers * * Implemented just like a direct read binary apdu bypassing dnie file cache @@ -1930,8 +1874,8 @@ static int dnie_read_header(struct sc_card *card) /* check response */ if (apdu.resplen != 8) goto header_notcompressed; - uncompressed = le2ulong(apdu.resp); - compressed = le2ulong(apdu.resp + 4); + uncompressed = lebytes2ulong(apdu.resp); + compressed = lebytes2ulong(apdu.resp + 4); if (uncompressed < compressed) goto header_notcompressed; if (uncompressed > 32767) @@ -1945,8 +1889,8 @@ static int dnie_read_header(struct sc_card *card) return 0; } -/** - * Access control list bytes for propietary DNIe FCI response for DF's. +/** + * Access control list bytes for proprietary DNIe FCI response for DF's. * based in information from official DNIe Driver * Parsing code based on itacns card driver */ @@ -1956,8 +1900,8 @@ static int df_acl[] = { /* to handle DF's */ -1 /* !hey!, what about 5th byte of FCI info? */ }; -/** - * Access control list bytes for propietary DNIe FCI response for EF's. +/** + * Access control list bytes for proprietary DNIe FCI response for EF's. * based in information from official DNIe Driver * Parsing code based on itacns card driver */ @@ -1972,13 +1916,13 @@ static int ef_acl[] = { /* to handle EF's */ * * Parse SelectFile's File Control information. * - First, std iso_parse_fci is called to parse std fci tags - * - Then analyze propietary tag according DNIe Manual + * - Then analyze proprietary tag according DNIe Manual * * @param card OpenSC card structure pointer * @param file currently selected EF or DF * @param buf received FCI data * @param buflen FCI length - * @return SC_SUCCESS if OK; else error code + * @return SC_SUCCESS if OK; else error code */ static int dnie_process_fci(struct sc_card *card, struct sc_file *file, const u8 * buf, size_t buflen) @@ -1987,7 +1931,7 @@ static int dnie_process_fci(struct sc_card *card, int *op = df_acl; int n = 0; sc_context_t *ctx = NULL; - if ((card == NULL) || (card->ctx == NULL) || (file == NULL)) + if ((card == NULL) || (card->ctx == NULL) || (file == NULL) || buflen == 0) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); @@ -1995,7 +1939,7 @@ static int dnie_process_fci(struct sc_card *card, res = iso_ops->process_fci(card, file, buf, buflen); LOG_TEST_RET(ctx, res, "iso7816_process_fci() failed"); /* if tag 0x85 is received, then file->prop_attr_len should be filled - * by sc_file_set_prop_attr() code. So check and set data according manual + * by sc_file_set_prop_attr() code. So check and set data according manual * Note errata at pg 35 of Manual about DF identifier (should be 0x38) */ if (file->prop_attr_len == 0) { /* no proprietary tag (0x85) received */ res = SC_SUCCESS; @@ -2053,15 +1997,15 @@ static int dnie_process_fci(struct sc_card *card, } /* bytes 1 and 2 stores file ID */ - file->id = ( ( 0xff & (int)file->prop_attr[1] ) << 8 ) | + file->id = ( ( 0xff & (int)file->prop_attr[1] ) << 8 ) | ( 0xff & (int)file->prop_attr[2] ) ; /* bytes 3 and 4 states file length */ - file->size = ( ( 0xff & (int)file->prop_attr[3] ) << 8 ) | + file->size = ( ( 0xff & (int)file->prop_attr[3] ) << 8 ) | ( 0xff & (int)file->prop_attr[4] ) ; /* bytes 5 to 9 states security attributes */ - /* NOTE: + /* NOTE: * seems that these 5 bytes are handled according iso7816-9 sect 8. * but sadly that each card uses their own bits :-( * Moreover: Manual talks on 5 bytes, but official driver only uses 4 @@ -2079,8 +2023,8 @@ static int dnie_process_fci(struct sc_card *card, SC_AC_KEY_REF_NONE); break; case 0x10: - /* this tag is omitted in official code - case 0x20: + /* this tag is omitted in official code + case 0x20: */ case 0x30: sc_file_add_acl_entry(file, *(op + n), SC_AC_CHV, @@ -2100,8 +2044,8 @@ static int dnie_process_fci(struct sc_card *card, break; } } - /* NOTE: Following bytes are described at DNIe manual pg 36, but No - documentation about what to do with following data is provided... + /* NOTE: Following bytes are described at DNIe manual pg 36, but No + documentation about what to do with following data is provided... logs suggest that they are neither generated nor handled. UPDATE: these additional bytes are received when FileDescriptor tag @@ -2111,7 +2055,7 @@ static int dnie_process_fci(struct sc_card *card, sc_log(card->ctx, "Processing flags for Cryptographic key files"); /* byte 10 (if present) shows Control Flags for security files */ - /* bytes 11 and 12 (if present) states Control bytes for + /* bytes 11 and 12 (if present) states Control bytes for RSA crypto files */ /* TODO: write when know what to do */ } @@ -2134,10 +2078,10 @@ static int dnie_process_fci(struct sc_card *card, /** * Change PIN. * - * Not implemented yet, as current availability for DNIe user driver + * Not implemented yet, as current availability for DNIe user driver * is unknown * - * @param card Pointer to Card Driver data structrure + * @param card Pointer to Card Driver data structure * @param data Pointer to Pin data structure * @return SC_SUCCESS if ok; else error code */ @@ -2151,7 +2095,7 @@ static int dnie_pin_change(struct sc_card *card, struct sc_pin_cmd_data * data) LOG_FUNC_RETURN(card->ctx,SC_ERROR_NOT_SUPPORTED); } -/** +/** * Verify PIN. * * Initialize SM and send pin verify CHV1 command to DNIe @@ -2161,8 +2105,7 @@ static int dnie_pin_change(struct sc_card *card, struct sc_pin_cmd_data * data) * @param tries_left; on fail stores the number of tries left before car lock * @return SC_SUCCESS if ok, else error code; on pin incorrect also sets tries_left */ -static int dnie_pin_verify(struct sc_card *card, - struct sc_pin_cmd_data *data, int *tries_left) +static int dnie_pin_verify(struct sc_card *card, struct sc_pin_cmd_data *data) { int res=SC_SUCCESS; sc_apdu_t apdu; @@ -2181,7 +2124,6 @@ static int dnie_pin_verify(struct sc_card *card, res = cwa_create_secure_channel(card, GET_DNIE_PRIV_DATA(card)->cwa_provider, CWA_SM_ON); LOG_TEST_RET(card->ctx, res, "Establish SM failed"); - data->apdu = &apdu; /* prepare apdu struct */ /* compose pin data to be inserted in apdu */ if (data->flags & SC_PIN_CMD_NEED_PADDING) padding = 1; @@ -2195,18 +2137,16 @@ static int dnie_pin_verify(struct sc_card *card, dnie_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0x00, 0x00, 0, pinlen, NULL, 0, pinbuffer, pinlen); - /* and send to card throught virtual channel */ + /* and send to card through virtual channel */ res = sc_transmit_apdu(card, &apdu); if (res != SC_SUCCESS) { LOG_TEST_RET(card->ctx, res, "VERIFY APDU Transmit fail"); } /* check response and if requested setup tries_left */ - if (tries_left != NULL) { /* returning tries_left count is requested */ - if ((apdu.sw1 == 0x63) && ((apdu.sw2 & 0xF0) == 0xC0)) { - *tries_left = apdu.sw2 & 0x0F; - LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); - } + if ((apdu.sw1 == 0x63) && ((apdu.sw2 & 0xF0) == 0xC0)) { + data->pin1.tries_left = apdu.sw2 & 0x0F; + LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } res = dnie_check_sw(card, apdu.sw1, apdu.sw2); /* not a pinerr: parse result */ @@ -2214,7 +2154,9 @@ static int dnie_pin_verify(struct sc_card *card, if (card->atr.value[15] >= DNIE_30_VERSION) { sc_log(card->ctx, "DNIe 3.0 detected => re-establish secure channel"); dnie_change_cwa_provider_to_secure(card); - res = cwa_create_secure_channel(card, GET_DNIE_PRIV_DATA(card)->cwa_provider, CWA_SM_ON); + if (res == SC_SUCCESS) { + res = cwa_create_secure_channel(card, GET_DNIE_PRIV_DATA(card)->cwa_provider, CWA_SM_ON); + } } LOG_FUNC_RETURN(card->ctx, res); @@ -2232,8 +2174,7 @@ static int dnie_pin_verify(struct sc_card *card, * @param tries_left; if pin_verify() operation, on incorrect pin stores the number of tries left before car lock * @return SC_SUCCESS if ok, else error code; on pin incorrect also sets tries_left */ -static int dnie_pin_cmd(struct sc_card *card, - struct sc_pin_cmd_data *data, int *tries_left) +static int dnie_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data) { int res = SC_SUCCESS; int lc = SC_CARDCTRL_LIFECYCLE_USER; @@ -2242,9 +2183,9 @@ static int dnie_pin_cmd(struct sc_card *card, return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); - /* - * some flags and settings from documentation - * No (easy) way to handle pinpad throught SM, so disable it + /* + * some flags and settings from documentation + * No (easy) way to handle pinpad through SM, so disable it */ data->flags &= ~SC_PIN_CMD_NEED_PADDING; /* no pin padding */ data->flags &= ~SC_PIN_CMD_USE_PINPAD; /* cannot handle pinpad */ @@ -2270,7 +2211,7 @@ static int dnie_pin_cmd(struct sc_card *card, /* This DNIe driver only supports VERIFY operation */ switch (data->cmd) { case SC_PIN_CMD_VERIFY: - res = dnie_pin_verify(card,data,tries_left); + res = dnie_pin_verify(card,data); break; case SC_PIN_CMD_CHANGE: res = dnie_pin_change(card,data); @@ -2292,7 +2233,7 @@ static int dnie_pin_cmd(struct sc_card *card, /** * Internal function to initialize card driver function pointers. * - * This is done by getting a copy for iso7816 card operations, + * This is done by getting a copy for iso7816 card operations, * and replace every DNIe specific functions * * @return DNIe card driver data, or null on failure diff --git a/src/libopensc/card-dtrust.c b/src/libopensc/card-dtrust.c new file mode 100644 index 0000000000..ab905fe4b3 --- /dev/null +++ b/src/libopensc/card-dtrust.c @@ -0,0 +1,1071 @@ +/* + * card-dtrust.c: Support for (CardOS based) D-Trust Signature Cards + * + * Copyright (C) 2023 Mario Haustein + * + * 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 + * + * based on card-cardos.c + */ + +/* + * This are the support periods for the D-Trust cards. The end of life time is + * set by the expiry of the underlying card operating system and sets the + * validity limit of the issued certificates. After end of life, the code paths + * for the affected products may be removed, as the cards are then not useful + * anymore. + * + * Start of Sales End of Sales End of life + * D-Trust Card 4.1/4.4 n/a Nov 2024 Sep 2026 + * D-Trust Card 5.1/5.4 Nov 2023 n/a Oct 2028 + * D-Trust Card 6.1/6.4 Summer 2025 n/a n/a + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "libopensc/pace.h" + +#include "asn1.h" +#include "card-cardos-common.h" +#include "internal.h" +#include "sm.h" +#include "sm/sm-eac.h" + +#include "card-dtrust.h" + +static const struct sc_card_operations *iso_ops = NULL; + +static struct sc_card_operations dtrust_ops; + +// clang-format off +static struct sc_card_driver dtrust_drv = { + "D-Trust Signature Card", + "dtrust", + &dtrust_ops, + NULL, 0, NULL +}; +// clang-format on + +struct dtrust_drv_data_t { + /* track PACE state */ + unsigned int pace : 1; + unsigned int can : 1; + /* global CAN from configuration file */ + char *can_value; + /* use CAN cache */ + unsigned int can_cache : 1; + /* PKCS#15 context for CAN caching */ + struct sc_pkcs15_card *p15card; + /* save the current security environment */ + const sc_security_env_t *env; +}; + +// clang-format off +static const struct sc_atr_table dtrust_atrs[] = { + /* D-Trust Signature Card v4.1 and v4.4 - CardOS 5.4 + * + * The ATR was intentionally omitted from minidriver_registration[] within win32/customactions.cpp + * as it is identical to that of CardOS v5.4 and therefore already included. + * Any new ATR may need an entry in minidriver_registration[]. */ + { "3b:d2:18:00:81:31:fe:58:c9:04:11", NULL, NULL, SC_CARD_TYPE_DTRUST_V4_1_STD, 0, NULL }, + + + /* D-Trust Signature Card v5.1 and v5.4 - CardOS 6.0 + * + * These cards are dual interface cards. Thus they have separate ATRs. */ + + /* contact based */ + { "3b:d2:18:00:81:31:fe:58:cb:01:16", NULL, NULL, SC_CARD_TYPE_DTRUST_V5_1_STD, 0, NULL }, + + /* contactless */ + { "3b:82:80:01:cb:01:c9", NULL, NULL, SC_CARD_TYPE_DTRUST_V5_1_STD, 0, NULL }, + { "07:78:77:74:03:cb:01:09", NULL, NULL, SC_CARD_TYPE_DTRUST_V5_1_STD, 0, NULL }, + + { NULL, NULL, NULL, 0, 0, NULL } +}; +// clang-format on + +static struct sc_object_id oid_secp256r1 = { + {1, 2, 840, 10045, 3, 1, 7, -1} +}; +static struct sc_object_id oid_secp384r1 = { + {1, 3, 132, 0, 34, -1} +}; + +static int +_dtrust_match_cardos(sc_card_t *card) +{ + int r; + size_t prodlen; + u8 buf[32]; + + /* check OS version */ + r = sc_get_data(card, 0x0182, buf, 32); + LOG_TEST_RET(card->ctx, r, "OS version check failed"); + + if (card->type == SC_CARD_TYPE_DTRUST_V4_1_STD) { + if (r != 2 || buf[0] != 0xc9 || buf[1] != 0x04) + return SC_ERROR_WRONG_CARD; + } else if (card->type == SC_CARD_TYPE_DTRUST_V5_1_STD) { + if (r != 2 || buf[0] != 0xcb || buf[1] != 0x01) + return SC_ERROR_WRONG_CARD; + } + + /* check product name */ + r = sc_get_data(card, 0x0180, buf, 32); + LOG_TEST_RET(card->ctx, r, "Product name check failed"); + + prodlen = (size_t)r; + if (card->type == SC_CARD_TYPE_DTRUST_V4_1_STD) { + if (prodlen != strlen("CardOS V5.4 2019") + 1 || memcmp(buf, "CardOS V5.4 2019", prodlen)) + return SC_ERROR_WRONG_CARD; + } else if (card->type == SC_CARD_TYPE_DTRUST_V5_1_STD) { + if (prodlen != strlen("CardOS V6.0 2021") + 1 || memcmp(buf, "CardOS V6.0 2021", prodlen)) + return SC_ERROR_WRONG_CARD; + } + + return SC_SUCCESS; +} + +static int +_dtrust_match_profile(sc_card_t *card) +{ + sc_path_t cia_path; + int r; + u8 buf[SC_MAX_APDU_BUFFER_SIZE]; + size_t slen, plen; + const u8 *sp, *pp; + char *name; + + sc_format_path("5032", &cia_path); + cia_path.aid.len = sizeof(cia_path.aid.value); + r = sc_hex_to_bin("E8:28:BD:08:0F:A0:00:00:01:67:45:53:49:47:4E", (u8 *)&cia_path.aid.value, &cia_path.aid.len); + LOG_TEST_RET(card->ctx, r, "Formatting AID failed"); + + r = sc_select_file(card, &cia_path, NULL); + LOG_TEST_RET(card->ctx, r, "Selecting CIA path failed"); + + r = sc_read_binary(card, 0, buf, SC_MAX_APDU_BUFFER_SIZE, NULL); + LOG_TEST_RET(card->ctx, r, "Reading CIA information failed"); + + sp = sc_asn1_find_tag(card->ctx, buf, r, 0x30, &slen); + if (sp == NULL) + return SC_ERROR_WRONG_CARD; + + /* check vendor */ + pp = sc_asn1_find_tag(card->ctx, sp, slen, 0x0c, &plen); + if (pp == NULL) + return SC_ERROR_WRONG_CARD; + + if (plen != 16 || memcmp(pp, "D-TRUST GmbH (C)", 16)) + return SC_ERROR_WRONG_CARD; + + /* check profile */ + pp = sc_asn1_find_tag(card->ctx, sp, slen, 0x80, &plen); + if (pp == NULL) + return SC_ERROR_WRONG_CARD; + + /* + * The profile string contains (two) additional characters. They depend + * on the production process, but aren't relevant for determining the + * card profile. + */ + if (card->type == SC_CARD_TYPE_DTRUST_V4_1_STD) { + if (plen >= 27 && !memcmp(pp, "D-TRUST Card 4.1 Std. RSA 2", 27)) + card->type = SC_CARD_TYPE_DTRUST_V4_1_STD; + else if (plen >= 28 && !memcmp(pp, "D-TRUST Card 4.1 Multi ECC 2", 28)) + card->type = SC_CARD_TYPE_DTRUST_V4_1_MULTI; + else if (plen >= 27 && !memcmp(pp, "D-TRUST Card 4.1 M100 ECC 2", 27)) + card->type = SC_CARD_TYPE_DTRUST_V4_1_M100; + else if (plen >= 27 && !memcmp(pp, "D-TRUST Card 4.4 Std. RSA 2", 27)) + card->type = SC_CARD_TYPE_DTRUST_V4_4_STD; + else if (plen >= 28 && !memcmp(pp, "D-TRUST Card 4.4 Multi ECC 2", 28)) + card->type = SC_CARD_TYPE_DTRUST_V4_4_MULTI; + else + return SC_ERROR_WRONG_CARD; + } else if (card->type == SC_CARD_TYPE_DTRUST_V5_1_STD) { + if (plen >= 27 && !memcmp(pp, "D-TRUST Card 5.1 Std. RSA 2", 27)) + card->type = SC_CARD_TYPE_DTRUST_V5_1_STD; + else if (plen >= 28 && !memcmp(pp, "D-TRUST Card 5.1 Multi ECC 2", 28)) + card->type = SC_CARD_TYPE_DTRUST_V5_1_MULTI; + else if (plen >= 27 && !memcmp(pp, "D-TRUST Card 5.1 M100 ECC 2", 27)) + card->type = SC_CARD_TYPE_DTRUST_V5_1_M100; + else if (plen >= 27 && !memcmp(pp, "D-TRUST Card 5.4 Std. RSA 2", 27)) + card->type = SC_CARD_TYPE_DTRUST_V5_4_STD; + else if (plen >= 28 && !memcmp(pp, "D-TRUST Card 5.4 Multi ECC 2", 28)) + card->type = SC_CARD_TYPE_DTRUST_V5_4_MULTI; + else + return SC_ERROR_WRONG_CARD; + } + + name = malloc(plen + 1); + if (name == NULL) + return SC_ERROR_OUT_OF_MEMORY; + memcpy(name, pp, plen); + name[plen] = '\0'; + card->name = name; + + sc_log(card->ctx, "found %s", card->name); + + return SC_SUCCESS; +} + +static int dtrust_finish(sc_card_t *card); + +static int +dtrust_match_card(sc_card_t *card) +{ + if (_sc_match_atr(card, dtrust_atrs, &card->type) < 0) + return 0; + + if (_dtrust_match_cardos(card) != SC_SUCCESS) + return 0; + + if (_dtrust_match_profile(card) != SC_SUCCESS) + return 0; + + sc_log(card->ctx, "D-Trust Signature Card"); + + return 1; +} + +static int +_dtrust_get_serialnr(sc_card_t *card) +{ + int r; + + card->serialnr.len = SC_MAX_SERIALNR; + r = sc_parse_ef_gdo(card, card->serialnr.value, &card->serialnr.len, NULL, 0); + if (r < 0) { + card->serialnr.len = 0; + return r; + } + + return SC_SUCCESS; +} + +static int +dtrust_init(sc_card_t *card) +{ + struct dtrust_drv_data_t *drv_data; + const char *can_env, *can_value; + size_t i, j; + scconf_block **found_blocks, *block; + int r; + const size_t data_field_length = 437; + unsigned long flags, ext_flags; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + card->cla = 0x00; + + drv_data = calloc(1, sizeof(struct dtrust_drv_data_t)); + if (drv_data == NULL) + return SC_ERROR_OUT_OF_MEMORY; + + drv_data->pace = 0; + drv_data->can = 0; + drv_data->can_value = NULL; + drv_data->can_cache = 1; + drv_data->p15card = NULL; + + /* read environment variable */ + can_env = getenv("DTRUST_CAN"); + + /* read configuration */ + can_value = NULL; + for (i = 0; card->ctx->conf_blocks[i]; i++) { + found_blocks = scconf_find_blocks(card->ctx->conf, card->ctx->conf_blocks[i], "card_driver", "dtrust"); + if (!found_blocks) + continue; + + for (j = 0, block = found_blocks[j]; block; j++, block = found_blocks[j]) { + drv_data->can_cache = scconf_get_bool(block, "can_use_cache", drv_data->can_cache); + + /* Environment variable has precedence over configured CAN */ + if (can_env == NULL) { + can_value = scconf_get_str(block, "can", can_value); + } + } + free(found_blocks); + } + + if (can_env != NULL) { + sc_log(card->ctx, "Using CAN provided by environment variable."); + can_value = can_env; + } else if (can_value != NULL) { + sc_log(card->ctx, "Using CAN provided by configuration file."); + } + + if (can_value != NULL) { + size_t can_len; + + can_len = strlen(can_value); + drv_data->can_value = sc_mem_secure_alloc(can_len + 1); + if (drv_data->can_value == NULL) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + memcpy(drv_data->can_value, can_value, can_len + 1); + } + + card->drv_data = drv_data; + + r = _dtrust_get_serialnr(card); + if (r != SC_SUCCESS) { + dtrust_finish(card); + LOG_TEST_RET(card->ctx, r, "Error reading serial number."); + } + + card->caps |= SC_CARD_CAP_APDU_EXT | SC_CARD_CAP_ISO7816_PIN_INFO; + + card->max_send_size = data_field_length - 6; +#ifdef _WIN32 + /* see card-cardos.c */ + if (card->reader->max_send_size == 255 && card->reader->max_recv_size == 256) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "resetting reader to use data_field_length"); + card->reader->max_send_size = data_field_length - 6; + card->reader->max_recv_size = data_field_length - 3; + } +#endif + + card->max_send_size = sc_get_max_send_size(card); /* see card-cardos.c */ + card->max_recv_size = data_field_length - 2; + card->max_recv_size = sc_get_max_recv_size(card); + + flags = 0; + + switch (card->type) { + case SC_CARD_TYPE_DTRUST_V4_1_STD: + case SC_CARD_TYPE_DTRUST_V4_4_STD: + case SC_CARD_TYPE_DTRUST_V5_1_STD: + case SC_CARD_TYPE_DTRUST_V5_4_STD: + flags |= SC_ALGORITHM_RSA_PAD_PKCS1; + flags |= SC_ALGORITHM_RSA_PAD_PSS; + flags |= SC_ALGORITHM_RSA_PAD_OAEP; + flags |= SC_ALGORITHM_RSA_HASH_SHA256; + flags |= SC_ALGORITHM_RSA_HASH_SHA384; + flags |= SC_ALGORITHM_RSA_HASH_SHA512; + flags |= SC_ALGORITHM_MGF1_SHA256; + flags |= SC_ALGORITHM_MGF1_SHA384; + flags |= SC_ALGORITHM_MGF1_SHA512; + + _sc_card_add_rsa_alg(card, 3072, flags, 0); + break; + + case SC_CARD_TYPE_DTRUST_V4_1_MULTI: + case SC_CARD_TYPE_DTRUST_V4_1_M100: + case SC_CARD_TYPE_DTRUST_V4_4_MULTI: + flags |= SC_ALGORITHM_ECDSA_RAW; + flags |= SC_ALGORITHM_ECDH_CDH_RAW; + ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE; + + _sc_card_add_ec_alg(card, 256, flags, ext_flags, &oid_secp256r1); + break; + + case SC_CARD_TYPE_DTRUST_V5_1_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_M100: + case SC_CARD_TYPE_DTRUST_V5_4_MULTI: + flags |= SC_ALGORITHM_ECDSA_RAW; + flags |= SC_ALGORITHM_ECDH_CDH_RAW; + ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE; + + _sc_card_add_ec_alg(card, 384, flags, ext_flags, &oid_secp384r1); + break; + + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_CARD); + } + + switch (card->type) { + case SC_CARD_TYPE_DTRUST_V5_1_STD: + case SC_CARD_TYPE_DTRUST_V5_4_STD: + case SC_CARD_TYPE_DTRUST_V5_1_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_M100: + case SC_CARD_TYPE_DTRUST_V5_4_MULTI: + r = sc_pkcs15_bind(card, NULL, &drv_data->p15card); + if (r != SC_SUCCESS) { + dtrust_finish(card); + LOG_TEST_RET(card->ctx, r, "Binding PKCS#15 context failed"); + } + break; + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +dtrust_finish(sc_card_t *card) +{ + struct dtrust_drv_data_t *drv_data; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + drv_data = card->drv_data; + + if (drv_data->p15card != NULL) { + sc_pkcs15_unbind(drv_data->p15card); + } + + if (drv_data->can_value != NULL) { + sc_mem_secure_free(drv_data->can_value, strlen(drv_data->can_value) + 1); + } + + free((char *)card->name); + free(card->drv_data); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +dtrust_select_app(struct sc_card *card, int ref) +{ + sc_path_t path; + int r; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + switch (card->type) { + case SC_CARD_TYPE_DTRUST_V5_1_STD: + case SC_CARD_TYPE_DTRUST_V5_4_STD: + case SC_CARD_TYPE_DTRUST_V5_1_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_M100: + case SC_CARD_TYPE_DTRUST_V5_4_MULTI: + switch (ref) { + case DTRUST5_PIN_ID_QES: + sc_format_path("3F000101", &path); + break; + + case DTRUST5_PIN_ID_AUT: + sc_format_path("3F000102", &path); + break; + + default: + sc_format_path("3F00", &path); + break; + } + + r = sc_select_file(card, &path, NULL); + LOG_TEST_RET(card->ctx, r, "Selecting master file failed"); + break; + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +dtrust_perform_pace(struct sc_card *card, + int ref, + const unsigned char *pin, + size_t pinlen, + int *tries_left) +{ + struct dtrust_drv_data_t *drv_data; + sc_path_t can_path; + u8 can_buffer[16]; + u8 *can_ptr = can_buffer; + size_t can_len; + int r; + struct establish_pace_channel_input pace_input; + struct establish_pace_channel_output pace_output; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + drv_data = card->drv_data; + + /* Dummy file path for the CAN cache */ + sc_format_path("CA4E", &can_path); + + /* Read CAN cache always. We need to know if the cache contains a CAN + * value even if we use a CAN source with higher precedence. The CAN + * cache must only be written if it was empty. Writing to a non-empty + * cache file will append the data to be written instead of overwriting + * the file. */ + can_len = sizeof(can_buffer) - 1; + r = sc_pkcs15_read_cached_file(drv_data->p15card, &can_path, &can_ptr, &can_len); + if (r == SC_SUCCESS && can_len > 0) { + can_buffer[can_len] = '\0'; + } else { + can_len = 0; + } + + /* The PKCS#11 layer cannot provide a CAN. Instead we consider the + * following sources for CAN input. + * 1. A CAN provided by the caller + * 2. A CAN provided in the environment variable DTRUST_CAN + * 3. A CAN provided in the configuration file + * 4. A cached CAN when the cache feature is enabled + * 5. If the reader supports the PACE protocol, we let it query for a + * CAN on the pin pad. + * 6. Querying the user interactively if possible */ + if (ref == PACE_PIN_ID_CAN) { + /* Use CAN from environment variable or configuration file */ + if (pin == NULL) { + pin = (const unsigned char *)drv_data->can_value; + if (pin != NULL) { + sc_log(card->ctx, "Using static CAN (environment variable/configuration file)."); + pinlen = strlen(drv_data->can_value); + } + } + + /* Use the CAN cache if no CAN is provided. */ + if (drv_data->can_cache && pin == NULL) { + if (can_len > 0) { + sc_log(card->ctx, "Using cached CAN."); + pin = can_buffer; + pinlen = can_len; + } else { + sc_log(card->ctx, "No cached CAN available."); + } + } + + /* Query the user interactively if no cached CAN is available. */ + if (pin == NULL) { + if (card->reader->capabilities & SC_READER_CAP_PACE_GENERIC) { + /* If no CAN is provided and the reader is + * PACE-capable, we leave pin == NULL to request the + * ready for querying the CAN on its pin pad. */ + sc_log(card->ctx, "Letting the reader prompt for the CAN on its pin pad."); + } else { + /* TODO: Request user input */ + sc_log(card->ctx, "Unable to query for the CAN. Aborting."); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + } + } + + /* Establish secure channel via PACE */ + memset(&pace_input, 0, sizeof pace_input); + memset(&pace_output, 0, sizeof pace_output); + + pace_input.pin_id = ref; + pace_input.pin = pin; + pace_input.pin_length = pinlen; + + /* Select the right application for authentication. */ + r = dtrust_select_app(card, ref); + LOG_TEST_RET(card->ctx, r, "Selecting application failed"); + + r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02); + + /* We need to track whether we established a PACE channel. Checking + * against card->sm_ctx.sm_mode != SM_MODE_TRANSMIT is not sufficient + * as PACE-capable card readers handle secure messaging transparently. */ + if (r == SC_SUCCESS) { + drv_data->pace = 1; + } + + /* We further need to track whether we authenticated against CAN as + * only this PINs allows us to verify the QES or AUT-PIN. */ + if (ref == PACE_PIN_ID_CAN) { + drv_data->can = r == SC_SUCCESS; + } + + free(pace_output.ef_cardaccess); + free(pace_output.recent_car); + free(pace_output.previous_car); + free(pace_output.id_icc); + free(pace_output.id_pcd); + + if (tries_left != NULL) { + if (r != SC_SUCCESS && + pace_output.mse_set_at_sw1 == 0x63 && + (pace_output.mse_set_at_sw2 & 0xc0) == 0xc0) { + *tries_left = pace_output.mse_set_at_sw2 & 0x0f; + } else { + *tries_left = -1; + } + } + + /* Write CAN to the cache, if it is correct and the cache was initially empty. */ + if (ref == PACE_PIN_ID_CAN && pin != NULL && drv_data->can_cache && + r == SC_SUCCESS && can_len == 0) { + sc_pkcs15_cache_file(drv_data->p15card, &can_path, pin, pinlen); + } + + return r; +} + +static int +dtrust_pin_cmd_get_info(struct sc_card *card, + struct sc_pin_cmd_data *data) +{ + struct dtrust_drv_data_t *drv_data; + int r; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + drv_data = card->drv_data; + + switch (data->pin_reference) { + case PACE_PIN_ID_CAN: + /* unlimited number of retries */ + data->pin1.max_tries = -1; + data->pin1.tries_left = -1; + r = SC_SUCCESS; + break; + + case PACE_PIN_ID_PUK: + case DTRUST5_PIN_ID_PIN_T: + case DTRUST5_PIN_ID_PIN_T_AUT: + /* Select the right application for authentication. */ + r = dtrust_select_app(card, data->pin_reference); + LOG_TEST_RET(card->ctx, r, "Selecting application failed"); + + /* FIXME: Doesn't work. Returns SW1=69 SW2=85 (Conditions of use not satisfied) instead. */ + data->pin1.max_tries = 3; + r = eac_pace_get_tries_left(card, data->pin_reference, &data->pin1.tries_left); + break; + + default: + /* Check if CAN authentication is necessary */ + if (!drv_data->can) { + /* Establish a secure channel with CAN to query PIN information. */ + r = dtrust_perform_pace(card, PACE_PIN_ID_CAN, NULL, 0, NULL); + LOG_TEST_RET(card->ctx, r, "CAN authentication failed"); + + /* Select the right application again. */ + r = dtrust_select_app(card, data->pin_reference); + LOG_TEST_RET(card->ctx, r, "Selecting application failed"); + } + + /* Now query PIN information */ + r = iso_ops->pin_cmd(card, data); + break; + } + + LOG_FUNC_RETURN(card->ctx, r); +} + +static int +dtrust_pin_cmd_verify(struct sc_card *card, + struct sc_pin_cmd_data *data) +{ + struct dtrust_drv_data_t *drv_data; + int r; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + drv_data = card->drv_data; + + switch (data->pin_reference) { + /* When the retry counter reaches 1 PACE-PINs become suspended. Before + * verifying a suspended PIN, the CAN has to verified. We go without + * verifying the CAN here, as this only matters for the PUK and the + * transport PIN. Neither PIN ist required during normal operation. The + * user has to resume a suspended PIN using dtrust-tool which manages + * CAN authentication. */ + case PACE_PIN_ID_CAN: + case PACE_PIN_ID_PUK: + case DTRUST5_PIN_ID_PIN_T: + case DTRUST5_PIN_ID_PIN_T_AUT: + /* Establish secure channel via PACE */ + r = dtrust_perform_pace(card, data->pin_reference, data->pin1.data, data->pin1.len, &data->pin1.tries_left); + break; + + default: + /* Check if CAN authentication is necessary */ + if (!drv_data->can) { + /* Establish a secure channel with CAN to to verify the PINs. */ + r = dtrust_perform_pace(card, PACE_PIN_ID_CAN, NULL, 0, NULL); + LOG_TEST_RET(card->ctx, r, "CAN authentication failed"); + + /* Select the right application again. */ + r = dtrust_select_app(card, data->pin_reference); + LOG_TEST_RET(card->ctx, r, "Selecting application failed"); + } + + /* Now verify the PIN */ + r = iso_ops->pin_cmd(card, data); + + break; + } + + LOG_FUNC_RETURN(card->ctx, r); +} + +static int +dtrust_pin_cmd(struct sc_card *card, + struct sc_pin_cmd_data *data) +{ + struct dtrust_drv_data_t *drv_data; + int r; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + drv_data = card->drv_data; + + if (!data) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + /* Upper layers may try to verify the PIN twice, first with PIN type + * SC_AC_CHV and then with PIN type SC_AC_CONTEXT_SPECIFIC. For the + * second attempt we first check by SC_PIN_CMD_GET_INFO whether a + * second PIN authentication is still necessary. If not, we simply + * return without a second verification attempt. Otherwise we perform + * the verification as requested. This only matters for pin pad readers + * to prevent the user from prompting the PIN twice. */ + if (data->cmd == SC_PIN_CMD_VERIFY && data->pin_type == SC_AC_CONTEXT_SPECIFIC) { + struct sc_pin_cmd_data data2; + + sc_log(card->ctx, "Checking if verification of PIN 0x%02x is necessary.", data->pin_reference); + + memset(&data2, 0, sizeof(struct sc_pin_cmd_data)); + data2.pin_reference = data->pin_reference; + data2.pin1 = data->pin1; + + /* Check verification state */ + data2.cmd = SC_PIN_CMD_GET_INFO; + data2.pin_type = data->pin_type; + r = dtrust_pin_cmd(card, &data2); + + if (data2.pin1.logged_in & SC_PIN_STATE_LOGGED_IN) { + /* Return if we are already authenticated */ + sc_log(card->ctx, "PIN 0x%02x already verified. Skipping authentication.", data->pin_reference); + + data->pin1 = data2.pin1; + LOG_FUNC_RETURN(card->ctx, r); + } + + sc_log(card->ctx, "Additional verification of PIN 0x%02x is necessary.", data->pin_reference); + } + + /* No special handling for D-Trust Card 4.1/4.4 */ + if (card->type >= SC_CARD_TYPE_DTRUST_V4_1_STD && card->type <= SC_CARD_TYPE_DTRUST_V4_4_MULTI) { + r = iso_ops->pin_cmd(card, data); + LOG_FUNC_RETURN(card->ctx, r); + } + + switch (data->cmd) { + case SC_PIN_CMD_GET_INFO: + r = dtrust_pin_cmd_get_info(card, data); + break; + + case SC_PIN_CMD_VERIFY: + r = dtrust_pin_cmd_verify(card, data); + break; + + case SC_PIN_CMD_CHANGE: + /* The card requires a secure channel to change the PIN. + * Although we could return the error code of the card, we + * prevent to send the APDU in case no secure channel was + * established. This prevents us from exposing our new PIN + * inadvertently in plaintext over the contactless interface in + * case of a software error in the upper layers. */ + if (!drv_data->pace) { + sc_log(card->ctx, "Secure channel required for PIN change"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED); + } + + if (data->pin1.len != 0 || !(data->flags & SC_PIN_CMD_IMPLICIT_CHANGE)) { + sc_log(card->ctx, "Card supports implicit PIN change only"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + + if (data->pin2.len == 0 && !(data->flags & SC_PIN_CMD_USE_PINPAD)) { + sc_log(card->ctx, "No value provided for the new PIN"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + r = iso_ops->pin_cmd(card, data); + break; + + case SC_PIN_CMD_UNBLOCK: + /* The supports only to reset the retry counter to its default + * value, but not to set verify or set a PIN. */ + if (data->pin1.len != 0 || data->pin2.len != 0 || + data->flags & SC_PIN_CMD_USE_PINPAD) { + sc_log(card->ctx, "Card supports retry counter reset only"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + r = iso_ops->pin_cmd(card, data); + break; + + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + + LOG_FUNC_RETURN(card->ctx, r); +} + +static int +dtrust_set_security_env(sc_card_t *card, + const sc_security_env_t *env, + int se_num) +{ + struct dtrust_drv_data_t *drv_data; + + if (card == NULL || env == NULL) + return SC_ERROR_INVALID_ARGUMENTS; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + drv_data = card->drv_data; + drv_data->env = env; + + if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) { + sc_log(card->ctx, "No or invalid key reference"); + return SC_ERROR_INVALID_ARGUMENTS; + } + + /* + * The card does not support to set a security environment. Instead a + * predefined template has to be loaded via MSE RESTORE which depends + * on the algorithm used. + */ + + switch (env->operation) { + case SC_SEC_OPERATION_DECIPHER: + if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02) { + se_num = 0x31; + } else if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_OAEP) { + switch (env->algorithm_flags & SC_ALGORITHM_MGF1_HASHES) { + case SC_ALGORITHM_MGF1_SHA256: + se_num = 0x32; + break; + case SC_ALGORITHM_MGF1_SHA384: + se_num = 0x33; + break; + case SC_ALGORITHM_MGF1_SHA512: + se_num = 0x34; + break; + + default: + return SC_ERROR_NOT_SUPPORTED; + } + } else { + return SC_ERROR_NOT_SUPPORTED; + } + break; + + case SC_SEC_OPERATION_SIGN: + if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01) { + switch (env->algorithm_flags & SC_ALGORITHM_RSA_HASHES) { + case SC_ALGORITHM_RSA_HASH_SHA256: + se_num = 0x25; + break; + case SC_ALGORITHM_RSA_HASH_SHA384: + se_num = 0x26; + break; + case SC_ALGORITHM_RSA_HASH_SHA512: + se_num = 0x27; + break; + + default: + return SC_ERROR_NOT_SUPPORTED; + } + } else if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PSS) { + /* + * According to the specification the message digest has + * to match the hash function used for the PSS scheme. + * We don't enforce this constraint here as the output + * is valid in all cases as long as the message digest + * is calculated in software and not on the card. + */ + + switch (env->algorithm_flags & SC_ALGORITHM_MGF1_HASHES) { + case SC_ALGORITHM_MGF1_SHA256: + se_num = 0x19; + break; + case SC_ALGORITHM_MGF1_SHA384: + se_num = 0x1A; + break; + case SC_ALGORITHM_MGF1_SHA512: + se_num = 0x1B; + break; + + default: + return SC_ERROR_NOT_SUPPORTED; + } + } else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_RAW) { + switch (card->type) { + case SC_CARD_TYPE_DTRUST_V4_1_MULTI: + case SC_CARD_TYPE_DTRUST_V4_1_M100: + case SC_CARD_TYPE_DTRUST_V4_4_MULTI: + /* ECDSA on SHA-256 hashes. Other hashes will work though. */ + se_num = 0x21; + break; + + case SC_CARD_TYPE_DTRUST_V5_1_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_M100: + case SC_CARD_TYPE_DTRUST_V5_4_MULTI: + /* ECDSA on SHA-384 hashes. Other hashes will work though. */ + se_num = 0x22; + break; + + default: + return SC_ERROR_NOT_SUPPORTED; + } + } else { + return SC_ERROR_NOT_SUPPORTED; + } + break; + + case SC_SEC_OPERATION_DERIVE: + if (env->algorithm_flags & SC_ALGORITHM_ECDH_CDH_RAW) { + se_num = 0x39; + } else { + return SC_ERROR_NOT_SUPPORTED; + } + break; + + default: + return SC_ERROR_NOT_SUPPORTED; + } + + return iso_ops->restore_security_env(card, se_num); +} + +static int +dtrust_compute_signature(struct sc_card *card, const u8 *data, + size_t data_len, u8 *out, size_t outlen) +{ + struct dtrust_drv_data_t *drv_data; + unsigned long flags; + size_t buflen = 0, tmplen; + u8 *buf = NULL; + int r; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + drv_data = card->drv_data; + flags = drv_data->env->algorithm_flags; + + /* + * PKCS#1 padded signatures require some special handling. When using + * the PKCS#1 scheme, first a digest info OID is prepended to the + * message digest. Afterward this resulting octet string is padded to + * the length of the key modulus. The card performs padding, but + * requires the digest info to be prepended in software. + */ + + /* Only PKCS#1 signature scheme requires special handling */ + if (!(flags & SC_ALGORITHM_RSA_PAD_PKCS1)) + return iso_ops->compute_signature(card, data, data_len, out, outlen); + + /* + * We have to clear the padding flag, because padding is done in + * hardware. We are keeping the hash algorithm flags, to ensure the + * digest info is prepended before padding. + */ + flags &= ~SC_ALGORITHM_RSA_PAD_PKCS1; + flags |= SC_ALGORITHM_RSA_PAD_NONE; + + /* 32 Bytes should be enough to prepend the digest info */ + buflen = data_len + 32; + buf = sc_mem_secure_alloc(buflen); + if (buf == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + + tmplen = buflen; + + /* Prepend digest info */ + r = sc_pkcs1_encode(card->ctx, flags, data, data_len, buf, &tmplen, 0, NULL); + LOG_TEST_GOTO_ERR(card->ctx, r, "Prepending digest info failed"); + + /* Do padding in hardware and compute signature */ + r = iso_ops->compute_signature(card, buf, tmplen, out, outlen); + +err: + sc_mem_secure_clear_free(buf, buflen); + + return r; +} + +static int +dtrust_decipher(struct sc_card *card, const u8 *data, + size_t data_len, u8 *out, size_t outlen) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + switch (card->type) { + /* No special handling necessary for RSA cards. */ + case SC_CARD_TYPE_DTRUST_V4_1_STD: + case SC_CARD_TYPE_DTRUST_V4_4_STD: + case SC_CARD_TYPE_DTRUST_V5_1_STD: + case SC_CARD_TYPE_DTRUST_V5_4_STD: + LOG_FUNC_RETURN(card->ctx, iso_ops->decipher(card, data, data_len, out, outlen)); + + /* Elliptic Curve cards cannot use PSO:DECIPHER command and need to + * perform key agreement by a CardOS specific command. */ + case SC_CARD_TYPE_DTRUST_V4_1_MULTI: + case SC_CARD_TYPE_DTRUST_V4_1_M100: + case SC_CARD_TYPE_DTRUST_V4_4_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_M100: + case SC_CARD_TYPE_DTRUST_V5_4_MULTI: + LOG_FUNC_RETURN(card->ctx, cardos_ec_compute_shared_value(card, data, data_len, out, outlen)); + + default: + return SC_ERROR_NOT_SUPPORTED; + } +} + +static int +dtrust_logout(sc_card_t *card) +{ + struct dtrust_drv_data_t *drv_data; + int r; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + drv_data = card->drv_data; + + sc_sm_stop(card); + drv_data->pace = 0; + drv_data->can = 0; + + /* If PACE is done between reader and card, SM is transparent to us as + * it ends at the reader. With CLA=0x0C we provoke a SM error to + * disable SM on the reader. */ + if (card->reader->capabilities & SC_READER_CAP_PACE_GENERIC) { + struct sc_apdu apdu; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xA4, 0x00, 0x00); + apdu.cla = 0x0C; + + r = sc_transmit_apdu(card, &apdu); + if (r != SC_SUCCESS) + sc_log(card->ctx, "Warning: Could not logout."); + } + + r = sc_select_file(card, sc_get_mf_path(), NULL); + + LOG_FUNC_RETURN(card->ctx, r); +} + +struct sc_card_driver * +sc_get_dtrust_driver(void) +{ + if (iso_ops == NULL) + iso_ops = sc_get_iso7816_driver()->ops; + + dtrust_ops = *iso_ops; + dtrust_ops.match_card = dtrust_match_card; + dtrust_ops.init = dtrust_init; + dtrust_ops.finish = dtrust_finish; + dtrust_ops.pin_cmd = dtrust_pin_cmd; + dtrust_ops.set_security_env = dtrust_set_security_env; + dtrust_ops.compute_signature = dtrust_compute_signature; + dtrust_ops.decipher = dtrust_decipher; + dtrust_ops.logout = dtrust_logout; + + return &dtrust_drv; +} diff --git a/src/libopensc/card-dtrust.h b/src/libopensc/card-dtrust.h new file mode 100644 index 0000000000..f435883834 --- /dev/null +++ b/src/libopensc/card-dtrust.h @@ -0,0 +1,33 @@ +/* + * card-dtrust.h: Support for (CardOS based) D-Trust Signature Cards + * + * Copyright (C) 2024 Mario Haustein + * + * 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 + */ + +#ifndef _CARD_DTRUST_H +#define _CARD_DTRUST_H + +#define DTRUST4_PIN_ID_PIN_CH 0x03 +#define DTRUST4_PIN_ID_PUK_CH 0x04 +#define DTRUST4_PIN_ID_PIN_T 0x0b +#define DTRUST4_PIN_ID_QES 0x87 +#define DTRUST5_PIN_ID_PIN_T 0x0b +#define DTRUST5_PIN_ID_PIN_T_AUT 0x0c +#define DTRUST5_PIN_ID_QES 0x87 +#define DTRUST5_PIN_ID_AUT 0x91 + +#endif diff --git a/src/libopensc/card-edo.c b/src/libopensc/card-edo.c new file mode 100644 index 0000000000..eb18215c5f --- /dev/null +++ b/src/libopensc/card-edo.c @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2020 Piotr Majkrzak + * + * This file is part of OpenSC. + * + * 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 + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) + +#include "libopensc/internal.h" +#include "libopensc/opensc.h" +#include "libopensc/pace.h" +#include "libopensc/sm.h" +#include "libopensc/asn1.h" +#include "sm/sm-eac.h" +#include +#include + + +static struct sc_card_operations edo_ops; + + +static struct sc_card_driver edo_drv = { + "Polish eID card (e-dowód, eDO)", + "edo", + &edo_ops, + NULL, 0, NULL +}; + + +static const struct sc_atr_table edo_atrs[] = { + { "3b:84:80:01:47:43:50:43:12", NULL, NULL, SC_CARD_TYPE_EDO, 0, NULL }, + { NULL, NULL, NULL, 0, 0, NULL } +}; + + +static struct { + int len; + struct sc_object_id oid; +} edo_curves[] = { + // secp384r1 + {384, {{1, 3, 132, 0, 34, -1}}} +}; + + +static int edo_match_card(sc_card_t* card) { + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + if (_sc_match_atr(card, edo_atrs, &card->type) >= 0) { + sc_log(card->ctx, "ATR recognized as Polish eID card."); + LOG_FUNC_RETURN(card->ctx, 1); + } + LOG_FUNC_RETURN(card->ctx, 0); +} + + +static int edo_get_can(sc_card_t* card, struct establish_pace_channel_input* pace_input) { + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + const char* can; + + can = getenv("EDO_CAN"); + + if (!can || can[0] != '\0') { + for (size_t i = 0; card->ctx->conf_blocks[i]; ++i) { + scconf_block** blocks = scconf_find_blocks(card->ctx->conf, card->ctx->conf_blocks[i], "card_driver", "edo"); + if (!blocks) + continue; + for (size_t j = 0; blocks[j]; ++j) + if ((can = scconf_get_str(blocks[j], "can", NULL))) + break; + free(blocks); + } + } + + if (!can || 6 != strlen(can)) { + sc_log(card->ctx, "Missing or invalid CAN. 6 digits required."); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN); + } + + pace_input->pin_id = PACE_PIN_ID_CAN; + pace_input->pin = (const unsigned char*)can; + pace_input->pin_length = 6; + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +static int edo_unlock(sc_card_t* card) { + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + struct establish_pace_channel_input pace_input; + struct establish_pace_channel_output pace_output; + + memset(&pace_input, 0, sizeof pace_input); + memset(&pace_output, 0, sizeof pace_output); + + if (SC_SUCCESS != edo_get_can(card, &pace_input)) { + sc_log(card->ctx, "Error reading CAN."); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN); + } + + if (SC_SUCCESS != perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02)) { + sc_log(card->ctx, "Error verifying CAN."); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN); + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +struct edo_buff { + u8 val[SC_MAX_APDU_RESP_SIZE]; + size_t len; +}; + + +static int edo_select_mf(struct sc_card* card, struct edo_buff* buff) { + LOG_FUNC_CALLED(card->ctx); + struct sc_apdu apdu; + sc_format_apdu_ex(&apdu, 00, 0xA4, 0x00, 0x00, NULL, 0, buff->val, sizeof buff->val); + LOG_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "SW check failed"); + buff->len = apdu.resplen; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +static int edo_select_df(struct sc_card* card, const u8 path[2], struct edo_buff* buff) { + LOG_FUNC_CALLED(card->ctx); + struct sc_apdu apdu; + sc_format_apdu_ex(&apdu, 00, 0xA4, 0x01, 0x04, path, 2, buff->val, sizeof buff->val); + LOG_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "SW check failed"); + buff->len = apdu.resplen; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +static int edo_select_ef(struct sc_card* card, const u8 path[2], struct edo_buff* buff) { + LOG_FUNC_CALLED(card->ctx); + struct sc_apdu apdu; + sc_format_apdu_ex(&apdu, 00, 0xA4, 0x02, 0x04, path, 2, buff->val, sizeof buff->val); + LOG_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "SW check failed"); + buff->len = apdu.resplen; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +static int edo_select_name(struct sc_card* card, const u8* name, size_t namelen, struct edo_buff* buff) { + LOG_FUNC_CALLED(card->ctx); + LOG_FUNC_RETURN(card->ctx, iso7816_select_aid(card, name, namelen, buff->val, &buff->len)); +} + + +static int edo_select_path(struct sc_card* card, const u8* path, size_t pathlen, struct edo_buff* buff) { + LOG_FUNC_CALLED(card->ctx); + while (pathlen >= 2) { + if (path[0] == 0x3F && path[1] == 0x00) + LOG_TEST_RET(card->ctx, edo_select_mf(card, buff), "MF select failed"); + else if (path[0] == 0xDF) + LOG_TEST_RET(card->ctx, edo_select_df(card, path, buff), "DF select failed"); + else if (pathlen == 2) + LOG_TEST_RET(card->ctx, edo_select_ef(card, path, buff), "EF select failed"); + else + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + path += 2; + pathlen -= 2; + } + if (pathlen) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +/*! Selects file specified by given path. + * + * Card does not support selecting file at once, that's why it have to be done in following way: + * 1. Select AID if provided, + * 2. Select MF if provided, + * 3. Select DF until provided, + * 4. Select EF if provided. + */ +static int edo_select_file(struct sc_card* card, const struct sc_path* in_path, struct sc_file** file_out) { + LOG_FUNC_CALLED(card->ctx); + struct edo_buff buff; + + switch (in_path->type) { + case SC_PATH_TYPE_PATH: + case SC_PATH_TYPE_FILE_ID: + if (in_path->aid.len) + LOG_TEST_RET(card->ctx, edo_select_name(card, in_path->aid.value, in_path->aid.len, &buff), "Select AID failed"); + if (in_path->len) + LOG_TEST_RET(card->ctx, edo_select_path(card, in_path->value, in_path->len, &buff), "Select path failed"); + break; + case SC_PATH_TYPE_DF_NAME: + LOG_TEST_RET(card->ctx, edo_select_name(card, in_path->value, in_path->len, &buff), "Select AID failed"); + break; + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + + if (file_out) { + if (buff.len < 2) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + if (!(*file_out = sc_file_new())) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + (*file_out)->path = *in_path; + LOG_TEST_RET(card->ctx, card->ops->process_fci(card, *file_out, buff.val, buff.len), "Process FCI failed"); + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +/*! Computes ECDSA signature. + * + * If ECDSA was used, the ASN.1 sequence of integers R,S returned by the + * card needs to be converted to the raw concatenation of R,S for PKCS#11. + */ +static int edo_compute_signature(struct sc_card* card, const u8* data, size_t datalen, u8* out, size_t outlen) { + LOG_FUNC_CALLED(card->ctx); + u8 sig[SC_MAX_APDU_RESP_SIZE]; + LOG_TEST_RET(card->ctx, sc_get_iso7816_driver()->ops->compute_signature(card, data, datalen, sig, sizeof sig), "Internal signature failed"); + LOG_TEST_RET(card->ctx, sc_asn1_sig_value_sequence_to_rs(card->ctx, sig, sizeof sig, out, outlen), "ASN.1 conversion failed"); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +/*! Sets security environment + * + * Card expects key file to be selected first, followed by the + * set security env packet with: 0x80, 0x01, 0xcc, 0x84, 0x01, 0x80|x, + * where x is the key reference byte. + */ +static int edo_set_security_env(struct sc_card* card, const struct sc_security_env* env, int se_num) { + LOG_FUNC_CALLED(card->ctx); + struct sc_apdu apdu; + + if (env->algorithm == SC_ALGORITHM_EC && env->operation == SC_SEC_OPERATION_SIGN && env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { + u8 payload[] = {0x80, 0x01, 0xcc, 0x84, 0x01, 0x80 | env->key_ref[0]}; + sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xB6, payload, sizeof payload, NULL, 0); + } else + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + + LOG_TEST_RET(card->ctx, sc_select_file(card, &env->file_ref, NULL), "SELECT file failed"); + LOG_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "SW check failed"); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +/*! Initializes card driver. + * + * Card is known to support only short APDU-s. + * Preinitialized keys are on secp384r1 curve. + * PACE channel have to be established. + */ +static int edo_init(sc_card_t* card) { + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + memset(&card->sm_ctx, 0, sizeof card->sm_ctx); + + card->max_send_size = SC_MAX_APDU_RESP_SIZE; + card->max_recv_size = SC_MAX_APDU_RESP_SIZE; + + for (size_t i = 0; i < sizeof edo_curves / sizeof * edo_curves; ++i) { + LOG_TEST_RET(card->ctx, _sc_card_add_ec_alg( + card, edo_curves[i].len, + SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDSA_HASH_NONE, + 0, &edo_curves[i].oid + ), "Add EC alg failed"); + } + + LOG_TEST_RET(card->ctx, sc_enum_apps(card), "Enumerate apps failed"); + + LOG_TEST_RET(card->ctx, edo_unlock(card), "Unlock card failed"); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +static int edo_logout(sc_card_t* card) { + sc_sm_stop(card); + return edo_unlock(card); +} + + +struct sc_card_driver* sc_get_edo_driver(void) { + edo_ops = *sc_get_iso7816_driver()->ops; + edo_ops.match_card = edo_match_card; + edo_ops.init = edo_init; + edo_ops.select_file = edo_select_file; + edo_ops.set_security_env = edo_set_security_env; + edo_ops.compute_signature = edo_compute_signature; + edo_ops.logout = edo_logout; + + return &edo_drv; +} + +#else + +#include "libopensc/opensc.h" + +struct sc_card_driver* sc_get_edo_driver(void) { + return NULL; +} + +#endif diff --git a/src/libopensc/card-entersafe.c b/src/libopensc/card-entersafe.c index 9db0664ec7..00ea9c8a9e 100644 --- a/src/libopensc/card-entersafe.c +++ b/src/libopensc/card-entersafe.c @@ -11,12 +11,12 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* Initially written by Weitao Sun (weitao@ftsafe.com) 2008 */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef ENABLE_OPENSSL /* empty file without openssl */ @@ -30,16 +30,16 @@ #include "asn1.h" #include "cardctl.h" -static struct sc_atr_table entersafe_atrs[] = { - { - "3b:0f:00:65:46:53:05:19:05:71:df:00:00:00:00:00:00", - "ff:ff:ff:ff:ff:ff:ff:00:ff:ff:ff:00:00:00:00:00:00", - "ePass3000", SC_CARD_TYPE_ENTERSAFE_3K, 0, NULL }, - { - "3b:9f:95:81:31:fe:9f:00:65:46:53:05:30:06:71:df:00:00:00:80:6a:82:5e", - "FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:00:FF:FF:FF:FF:FF:FF:00:00:00:00", - "FTCOS/PK-01C", SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C, 0, NULL }, - { +static const struct sc_atr_table entersafe_atrs[] = { + { + "3b:0f:00:65:46:53:05:19:05:71:df:00:00:00:00:00:00", + "ff:ff:ff:ff:ff:ff:ff:00:ff:ff:ff:00:00:00:00:00:00", + "ePass3000", SC_CARD_TYPE_ENTERSAFE_3K, 0, NULL }, + { + "3b:9f:95:81:31:fe:9f:00:65:46:53:05:30:06:71:df:00:00:00:80:6a:82:5e", + "FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:00:FF:FF:FF:FF:FF:FF:00:00:00:00", + "FTCOS/PK-01C", SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C, 0, NULL }, + { "3b:fc:18:00:00:81:31:80:45:90:67:46:4a:00:64:18:14:00:00:00:00:02", "ff:00:00:00:00:00:00:00:00:ff:ff:ff:ff:00:00:00:00:ff:ff:ff:ff:00", "EJAVA/PK-01C", SC_CARD_TYPE_ENTERSAFE_EJAVA_PK_01C, 0, NULL }, @@ -86,45 +86,44 @@ static struct sc_card_driver entersafe_drv = { static u8 trans_code_3k[] = { - 0x01,0x02,0x03,0x04, - 0x05,0x06,0x07,0x08, + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, }; static u8 trans_code_ftcos_pk_01c[] = { - 0x92,0x34,0x2E,0xEF, - 0x23,0x40,0x4F,0xD1, + 0x92, 0x34, 0x2E, 0xEF, + 0x23, 0x40, 0x4F, 0xD1, }; static u8 init_key[] = { - 1, 2, 3, 4, - 5, 6, 7, 8, - 9, 10, 11, 12, - 13, 14, 15, 16, + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16, }; static u8 key_maintain[] = { - 0x12, 0x34, 0x56, 0x78, - 0x21, 0x43, 0x65, 0x87, - 0x11, 0x22, 0xaa, 0xbb, - 0x33, 0x44, 0xcd, 0xef + 0x12, 0x34, 0x56, 0x78, + 0x21, 0x43, 0x65, 0x87, + 0x11, 0x22, 0xaa, 0xbb, + 0x33, 0x44, 0xcd, 0xef }; -static void entersafe_reverse_buffer(u8* buff,size_t size) +static void entersafe_reverse_buffer(u8* buff, size_t size) { - u8 t; - u8 * end=buff+size-1; - - while(bufftype); if (i < 0) - return 0; + return 0; return 1; } @@ -154,263 +153,277 @@ static int entersafe_init(sc_card_t *card) card->cla = 0x00; card->drv_data = NULL; - flags =SC_ALGORITHM_ONBOARD_KEY_GEN - | SC_ALGORITHM_RSA_RAW - | SC_ALGORITHM_RSA_HASH_NONE; + flags = SC_ALGORITHM_ONBOARD_KEY_GEN | SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASH_NONE; _sc_card_add_rsa_alg(card, 512, flags, 0); _sc_card_add_rsa_alg(card, 768, flags, 0); - _sc_card_add_rsa_alg(card,1024, flags, 0); - _sc_card_add_rsa_alg(card,2048, flags, 0); + _sc_card_add_rsa_alg(card, 1024, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); - card->caps = SC_CARD_CAP_RNG; + card->caps = SC_CARD_CAP_RNG; /* we need read_binary&friends with max 224 bytes per read */ card->max_send_size = 224; card->max_recv_size = 224; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } -static int entersafe_gen_random(sc_card_t *card,u8 *buff,size_t size) +static int entersafe_gen_random(sc_card_t *card, u8 *buff, size_t size) { - int r=SC_SUCCESS; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]={0}; - sc_apdu_t apdu; + int r = SC_SUCCESS; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE] = {0}; + sc_apdu_t apdu; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - - sc_format_apdu(card,&apdu,SC_APDU_CASE_2_SHORT,0x84,0x00,0x00); - apdu.resp=rbuf; - apdu.le=size; - apdu.resplen=sizeof(rbuf); - r=sc_transmit_apdu(card,&apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "entersafe gen random failed"); + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0x00, 0x00); + apdu.resp = rbuf; + apdu.le = size; + apdu.resplen = sizeof(rbuf); + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "entersafe gen random failed"); - if(apdu.resplen!=size) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL,SC_ERROR_INTERNAL); - memcpy(buff,rbuf,size); + if (apdu.resplen != size) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + memcpy(buff, rbuf, size); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL,r); + LOG_FUNC_RETURN(card->ctx, r); } static int entersafe_cipher_apdu(sc_card_t *card, sc_apdu_t *apdu, u8 *key, size_t keylen, u8 *buff, size_t buffsize) { - EVP_CIPHER_CTX * ctx = NULL; - u8 iv[8]={0}; - int len; + EVP_CIPHER_CTX *ctx = NULL; + EVP_CIPHER *alg = NULL; + + u8 iv[8] = {0}; + int len; + + if (card == NULL || apdu == NULL || key == NULL || buff == NULL) + return SC_ERROR_INTERNAL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert(card); - assert(apdu); - assert(key); - assert(buff); - - /* padding as 0x80 0x00 0x00...... */ - memset(buff,0,buffsize); - buff[0]=apdu->lc; - memcpy(buff+1,apdu->data,apdu->lc); - buff[apdu->lc+1]=0x80; - - ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); - EVP_CIPHER_CTX_set_padding(ctx,0); - - if(keylen == 8) - EVP_EncryptInit_ex(ctx, EVP_des_ecb(), NULL, key, iv); - else if (keylen == 16) - EVP_EncryptInit_ex(ctx, EVP_des_ede(), NULL, key, iv); - else - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); - - len = apdu->lc; - if(!EVP_EncryptUpdate(ctx, buff, &len, buff, buffsize)){ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "entersafe encryption error."); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); - } - apdu->lc = len; - - EVP_CIPHER_CTX_free(ctx); - - if(apdu->lc!=buffsize) - { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "entersafe build cipher apdu failed."); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INTERNAL); - } - - apdu->data=buff; - apdu->datalen=apdu->lc; - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); + /* padding as 0x80 0x00 0x00...... */ + memset(buff, 0, buffsize); + buff[0] = apdu->lc; + memcpy(buff + 1, apdu->data, apdu->lc); + buff[apdu->lc + 1] = 0x80; + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) { + sc_log_openssl(card->ctx); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + EVP_CIPHER_CTX_set_padding(ctx, 0); + + if (keylen == 8) { + alg = sc_evp_cipher(card->ctx, "DES-ECB"); + } else if (keylen == 16) { + alg = sc_evp_cipher(card->ctx, "DES-EDE"); + } else { + EVP_CIPHER_CTX_free(ctx); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + + if (EVP_EncryptInit_ex(ctx, alg, NULL, key, iv) != 1) { + sc_log_openssl(card->ctx); + sc_evp_cipher_free(alg); + EVP_CIPHER_CTX_free(ctx); + sc_log(card->ctx, "entersafe encryption error."); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + + len = (int)apdu->lc; + if (!EVP_EncryptUpdate(ctx, buff, &len, buff, (int)buffsize)) { + sc_log_openssl(card->ctx); + sc_evp_cipher_free(alg); + EVP_CIPHER_CTX_free(ctx); + sc_log(card->ctx, "entersafe encryption error."); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + apdu->lc = len; + + sc_evp_cipher_free(alg); + EVP_CIPHER_CTX_free(ctx); + + if (apdu->lc != buffsize) { + sc_log(card->ctx, "entersafe build cipher apdu failed."); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INTERNAL); + } + + apdu->data = buff; + apdu->datalen = apdu->lc; + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int entersafe_mac_apdu(sc_card_t *card, sc_apdu_t *apdu, u8 * key,size_t keylen, u8 * buff,size_t buffsize) { - int r; - u8 iv[8]; - u8 *tmp=0,*tmp_rounded=NULL; - size_t tmpsize=0,tmpsize_rounded=0; - int outl=0; - EVP_CIPHER_CTX * ctx = NULL; + int r; + u8 iv[8]; + u8 *tmp = NULL, *tmp_rounded = NULL; + size_t tmpsize = 0, tmpsize_rounded = 0; + int outl = 0; + EVP_CIPHER_CTX *ctx = NULL; + EVP_CIPHER *alg = NULL; + + if (card == NULL || apdu == NULL || key == NULL || buff == NULL) + return SC_ERROR_INTERNAL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert(card); - assert(apdu); - assert(key); - assert(buff); - - if(apdu->cse != SC_APDU_CASE_3_SHORT) - return SC_ERROR_INTERNAL; - if(keylen!=8 && keylen!=16) - return SC_ERROR_INTERNAL; - - r=entersafe_gen_random(card,iv,sizeof(iv)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,r,"entersafe gen random failed"); - - /* encode the APDU in the buffer */ - if ((r=sc_apdu_get_octets(card->ctx, apdu, &tmp, &tmpsize,SC_PROTO_RAW)) != SC_SUCCESS) - goto out; - - /* round to 8 */ - tmpsize_rounded=(tmpsize/8+1)*8; - - tmp_rounded = malloc(tmpsize_rounded); - if (tmp_rounded == NULL) - { - r = SC_ERROR_OUT_OF_MEMORY; - goto out; - } - - /*build content and padded buffer by 0x80 0x00 0x00..... */ - memset(tmp_rounded,0,tmpsize_rounded); - memcpy(tmp_rounded,tmp,tmpsize); - tmp_rounded[4]+=4; - tmp_rounded[tmpsize]=0x80; - - /* block_size-1 blocks*/ - ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL) { - r = SC_ERROR_OUT_OF_MEMORY; + if (apdu->cse != SC_APDU_CASE_3_SHORT) + return SC_ERROR_INTERNAL; + if (keylen != 8 && keylen != 16) + return SC_ERROR_INTERNAL; + + r = entersafe_gen_random(card, iv, sizeof(iv)); + LOG_TEST_RET(card->ctx, r, "entersafe gen random failed"); + + /* encode the APDU in the buffer */ + if ((r = sc_apdu_get_octets(card->ctx, apdu, &tmp, &tmpsize, SC_PROTO_RAW)) != SC_SUCCESS) + goto out; + + /* round to 8 */ + tmpsize_rounded = (tmpsize / 8 + 1) * 8; + + tmp_rounded = malloc(tmpsize_rounded); + if (tmp_rounded == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; goto out; - } - EVP_CIPHER_CTX_set_padding(ctx,0); - EVP_EncryptInit_ex(ctx, EVP_des_cbc(), NULL, key, iv); - - if(tmpsize_rounded>8){ - if(!EVP_EncryptUpdate(ctx,tmp_rounded,&outl,tmp_rounded,tmpsize_rounded-8)){ - r = SC_ERROR_INTERNAL; - goto out; - } - } - /* last block */ - if(keylen==8) - { - if(!EVP_EncryptUpdate(ctx,tmp_rounded+outl,&outl,tmp_rounded+outl,8)){ - r = SC_ERROR_INTERNAL; - goto out; - } - } - else - { - EVP_EncryptInit_ex(ctx, EVP_des_ede_cbc(), NULL, key,tmp_rounded+outl-8); - if(!EVP_EncryptUpdate(ctx,tmp_rounded+outl,&outl,tmp_rounded+outl,8)){ - r = SC_ERROR_INTERNAL; - goto out; - } - } - - memcpy(buff,apdu->data,apdu->lc); - /* use first 4 bytes of last block as mac value*/ - memcpy(buff+apdu->lc,tmp_rounded+tmpsize_rounded-8,4); - apdu->data=buff; - apdu->lc+=4; - apdu->datalen=apdu->lc; + } + + /* build content and padded buffer by 0x80 0x00 0x00..... */ + memset(tmp_rounded, 0, tmpsize_rounded); + memcpy(tmp_rounded, tmp, tmpsize); + tmp_rounded[4] += 4; + tmp_rounded[tmpsize] = 0x80; + + /* block_size-1 blocks*/ + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + sc_log_openssl(card->ctx); + goto out; + } + EVP_CIPHER_CTX_set_padding(ctx, 0); + alg = sc_evp_cipher(card->ctx, "DES-CBC"); + if (!alg || + EVP_EncryptInit_ex(ctx, alg, NULL, key, iv) != 1) { + r = SC_ERROR_INTERNAL; + sc_log_openssl(card->ctx); + goto out; + } + + if (tmpsize_rounded > 8) { + if (!EVP_EncryptUpdate(ctx, tmp_rounded, &outl, tmp_rounded, (int)tmpsize_rounded - 8)) { + r = SC_ERROR_INTERNAL; + sc_log_openssl(card->ctx); + goto out; + } + } + /* last block */ + if (keylen == 8) { + if (!EVP_EncryptUpdate(ctx, tmp_rounded + outl, &outl, tmp_rounded + outl, 8)) { + r = SC_ERROR_INTERNAL; + sc_log_openssl(card->ctx); + goto out; + } + } else { + if (EVP_EncryptInit_ex(ctx, EVP_des_ede_cbc(), NULL, key, tmp_rounded + outl - 8) != 1 || + !EVP_EncryptUpdate(ctx, tmp_rounded + outl, &outl, tmp_rounded + outl, 8)) { + r = SC_ERROR_INTERNAL; + sc_log_openssl(card->ctx); + goto out; + } + } + + if (apdu->lc + 4 > buffsize) { + r = SC_ERROR_INVALID_DATA; + goto out; + } + + memcpy(buff, apdu->data, apdu->lc); + /* use first 4 bytes of last block as mac value */ + memcpy(buff + apdu->lc, tmp_rounded + tmpsize_rounded - 8, 4); + apdu->data = buff; + apdu->lc += 4; + apdu->datalen = apdu->lc; out: - if(tmp) - free(tmp); - if(tmp_rounded) - free(tmp_rounded); - if (ctx) - EVP_CIPHER_CTX_free(ctx); + free(tmp); + free(tmp_rounded); + sc_evp_cipher_free(alg); + EVP_CIPHER_CTX_free(ctx); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int entersafe_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu, u8 * key, size_t keylen, - int cipher,int mac) + int cipher, int mac) { - u8 *cipher_data=0,*mac_data=0; - size_t cipher_data_size,mac_data_size; - int blocks; - int r=SC_SUCCESS; - u8 *sbuf=NULL; - size_t ssize=0; + u8 *cipher_data = NULL, *mac_data = NULL; + size_t cipher_data_size, mac_data_size, blocks; + int r = SC_SUCCESS; + u8 *sbuf = NULL; + size_t ssize = 0; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + if (card == NULL || apdu == NULL) + return SC_ERROR_INTERNAL; - assert(card); - assert(apdu); + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - if((cipher||mac) && (!key||(keylen!=8 && keylen!=16))) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + if ((cipher || mac) && (!key || (keylen != 8 && keylen != 16))) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); r = sc_apdu_get_octets(card->ctx, apdu, &sbuf, &ssize, SC_PROTO_RAW); if (r == SC_SUCCESS) - sc_apdu_log(card->ctx, SC_LOG_DEBUG_VERBOSE, sbuf, ssize, 1); - if(sbuf) + sc_apdu_log(card->ctx, sbuf, ssize, 1); + if (sbuf) free(sbuf); - if(cipher) - { - blocks=(apdu->lc+2)/8+1; - cipher_data_size=blocks*8; - cipher_data=malloc(cipher_data_size); - if(!cipher_data) - { - r = SC_ERROR_OUT_OF_MEMORY; - goto out; - } - - if((r = entersafe_cipher_apdu(card,apdu,key,keylen,cipher_data,cipher_data_size))<0) - goto out; - } - if(mac) - { - mac_data_size=apdu->lc+4; - mac_data=malloc(mac_data_size); - if(!mac_data) - { - r = SC_ERROR_OUT_OF_MEMORY; - goto out; - } - r = entersafe_mac_apdu(card,apdu,key,keylen,mac_data,mac_data_size); - if(r < 0) - goto out; - } - - r = sc_transmit_apdu(card,apdu); + if (cipher) { + blocks = (apdu->lc + 2) / 8 + 1; + cipher_data_size = blocks * 8; + cipher_data = malloc(cipher_data_size); + if (!cipher_data) { + r = SC_ERROR_OUT_OF_MEMORY; + goto out; + } + + if ((r = entersafe_cipher_apdu(card, apdu, key, keylen, cipher_data, cipher_data_size)) < 0) + goto out; + } + if (mac) { + mac_data_size = apdu->lc + 4; + mac_data = malloc(mac_data_size); + if (!mac_data) { + r = SC_ERROR_OUT_OF_MEMORY; + goto out; + } + r = entersafe_mac_apdu(card, apdu, key, keylen, mac_data, mac_data_size); + if (r < 0) + goto out; + } + + r = sc_transmit_apdu(card, apdu); out: - if(cipher_data) - free(cipher_data); - if(mac_data) - free(mac_data); + free(cipher_data); + free(mac_data); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int entersafe_read_binary(sc_card_t *card, unsigned int idx, u8 *buf, size_t count, - unsigned long flags) + unsigned long *flags) { sc_apdu_t apdu; u8 recvbuf[SC_MAX_APDU_BUFFER_SIZE]; @@ -418,22 +431,22 @@ static int entersafe_read_binary(sc_card_t *card, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert(count <= card->max_recv_size); - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0, - (idx >> 8) & 0xFF, idx & 0xFF); + if (count > card->max_recv_size) + return SC_ERROR_INTERNAL; + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0, (idx >> 8) & 0xFF, idx & 0xFF); - apdu.cla=idx > 0x7fff ? 0x80:0x00; + apdu.cla = idx > 0x7fff ? 0x80 : 0x00; apdu.le = count; apdu.resplen = count; apdu.resp = recvbuf; - r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + r = entersafe_transmit_apdu(card, &apdu, 0, 0, 0, 0); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.resplen == 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); memcpy(buf, recvbuf, apdu.resplen); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)apdu.resplen); } static int entersafe_update_binary(sc_card_t *card, @@ -445,46 +458,43 @@ static int entersafe_update_binary(sc_card_t *card, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert(count <= card->max_send_size); + if (count > card->max_send_size) + return SC_ERROR_INTERNAL; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, - (idx >> 8) & 0xFF, idx & 0xFF); - apdu.cla=idx > 0x7fff ? 0x80:0x00; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, (idx >> 8) & 0xFF, idx & 0xFF); + apdu.cla = idx > 0x7fff ? 0x80 : 0x00; apdu.lc = count; apdu.datalen = count; apdu.data = buf; - r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), + r = entersafe_transmit_apdu(card, &apdu, 0, 0, 0, 0); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "Card returned error"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, count); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)count); } - static int entersafe_process_fci(struct sc_card *card, struct sc_file *file, - const u8 *buf, size_t buflen) + const u8 *buf, size_t buflen) { - int r; - - assert(file); - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - - r = iso_ops->process_fci(card,file,buf,buflen); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Process fci failed"); - - if(file->namelen) - { - file->type = SC_FILE_TYPE_DF; - file->ef_structure = SC_FILE_EF_UNKNOWN; - } - else - { - file->type = SC_FILE_TYPE_WORKING_EF; - file->ef_structure = SC_FILE_EF_TRANSPARENT; - } - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); + int r; + + if (file == NULL) + return SC_ERROR_INTERNAL; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + r = iso_ops->process_fci(card, file, buf, buflen); + LOG_TEST_RET(card->ctx, r, "Process fci failed"); + + if (file->namelen) { + file->type = SC_FILE_TYPE_DF; + file->ef_structure = SC_FILE_EF_UNKNOWN; + } else { + file->type = SC_FILE_TYPE_WORKING_EF; + file->ef_structure = SC_FILE_EF_TRANSPARENT; + } + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int entersafe_select_fid(sc_card_t *card, @@ -497,30 +507,16 @@ static int entersafe_select_fid(sc_card_t *card, memset(&path, 0, sizeof(sc_path_t)); - path.type=SC_PATH_TYPE_FILE_ID; - path.value[0]=id_hi; - path.value[1]=id_lo; - path.len=2; + path.type = SC_PATH_TYPE_FILE_ID; + path.value[0] = id_hi; + path.value[1] = id_lo; + path.len = 2; - r = iso_ops->select_file(card,&path,&file); + r = iso_ops->select_file(card, &path, &file); if (r < 0) sc_file_free(file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - - /* update cache */ - if (file->type == SC_FILE_TYPE_DF) { - card->cache.current_path.type = SC_PATH_TYPE_PATH; - card->cache.current_path.value[0] = 0x3f; - card->cache.current_path.value[1] = 0x00; - if (id_hi == 0x3f && id_lo == 0x00){ - card->cache.current_path.len = 2; - } else { - card->cache.current_path.len = 4; - card->cache.current_path.value[2] = id_hi; - card->cache.current_path.value[3] = id_lo; - } - } - + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (file_out) *file_out = file; else @@ -533,42 +529,24 @@ static int entersafe_select_aid(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { - int r = 0; + int r; - if (card->cache.valid - && card->cache.current_path.type == SC_PATH_TYPE_DF_NAME - && card->cache.current_path.len == in_path->len - && memcmp(card->cache.current_path.value, in_path->value, in_path->len)==0 ) - { - if(file_out) - { - *file_out = sc_file_new(); - if(!file_out) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); - } - } - else - { - r = iso_ops->select_file(card,in_path,file_out); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + r = iso_ops->select_file(card, in_path, file_out); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - /* update cache */ - card->cache.current_path.type = SC_PATH_TYPE_DF_NAME; - card->cache.current_path.len = in_path->len; - memcpy(card->cache.current_path.value,in_path->value,in_path->len); - } if (file_out) { - sc_file_t *file = *file_out; - assert(file); - - file->type = SC_FILE_TYPE_DF; - file->ef_structure = SC_FILE_EF_UNKNOWN; - file->path.len = 0; - file->size = 0; - /* AID */ - memcpy(file->name,in_path->value,in_path->len); - file->namelen = in_path->len; - file->id = 0x0000; + sc_file_t *file = *file_out; + if (file == NULL) + return SC_ERROR_INTERNAL; + + file->type = SC_FILE_TYPE_DF; + file->ef_structure = SC_FILE_EF_UNKNOWN; + file->path.len = 0; + file->size = 0; + /* AID */ + memcpy(file->name, in_path->value, in_path->len); + file->namelen = in_path->len; + file->id = 0x0000; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } @@ -577,143 +555,58 @@ static int entersafe_select_path(sc_card_t *card, const u8 pathbuf[16], const size_t len, sc_file_t **file_out) { - u8 n_pathbuf[SC_MAX_PATH_SIZE]; - const u8 *path=pathbuf; - size_t pathlen=len; - int bMatch = -1; - unsigned int i; - int r; - - if (pathlen%2 != 0 || pathlen > 6 || pathlen <= 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); - - /* if pathlen == 6 then the first FID must be MF (== 3F00) */ - if (pathlen == 6 && ( path[0] != 0x3f || path[1] != 0x00 )) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); - - /* unify path (the first FID should be MF) */ - if (path[0] != 0x3f || path[1] != 0x00) - { - n_pathbuf[0] = 0x3f; - n_pathbuf[1] = 0x00; - for (i=0; i< pathlen; i++) - n_pathbuf[i+2] = pathbuf[i]; - path = n_pathbuf; - pathlen += 2; - } - - /* check current working directory */ - if (card->cache.valid - && card->cache.current_path.type == SC_PATH_TYPE_PATH - && card->cache.current_path.len >= 2 - && card->cache.current_path.len <= pathlen ) - { - bMatch = 0; - for (i=0; i < card->cache.current_path.len; i+=2) - if (card->cache.current_path.value[i] == path[i] - && card->cache.current_path.value[i+1] == path[i+1] ) - bMatch += 2; - } - - if ( card->cache.valid && bMatch > 2 ) - { - if ( pathlen - bMatch == 2 ) - { - /* we are in the rigth directory */ - return entersafe_select_fid(card, path[bMatch], path[bMatch+1], file_out); - } - else if ( pathlen - bMatch > 2 ) - { - /* two more steps to go */ - sc_path_t new_path; - - /* first step: change directory */ - r = entersafe_select_fid(card, path[bMatch], path[bMatch+1], NULL); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "SELECT FILE (DF-ID) failed"); - - memset(&new_path, 0, sizeof(sc_path_t)); - - new_path.type = SC_PATH_TYPE_PATH; - new_path.len = pathlen - bMatch-2; - memcpy(new_path.value, &(path[bMatch+2]), new_path.len); - /* final step: select file */ - return entersafe_select_file(card, &new_path, file_out); - } - else /* if (bMatch - pathlen == 0) */ - { - /* done: we are already in the - * requested directory */ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "cache hit\n"); - /* copy file info (if necessary) */ - if (file_out) { - sc_file_t *file = sc_file_new(); - if (!file) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); - file->id = (path[pathlen-2] << 8) + - path[pathlen-1]; - file->path = card->cache.current_path; - file->type = SC_FILE_TYPE_DF; - file->ef_structure = SC_FILE_EF_UNKNOWN; - file->size = 0; - file->namelen = 0; - file->magic = SC_FILE_MAGIC; - *file_out = file; - } - /* nothing left to do */ - return SC_SUCCESS; - } - } - else - { - /* no usable cache */ - for ( i=0; ictx, SC_LOG_DEBUG_NORMAL, r, "SELECT FILE (DF-ID) failed"); - } - return entersafe_select_fid(card, path[pathlen-2], path[pathlen-1], file_out); - } + u8 n_pathbuf[SC_MAX_PATH_SIZE]; + const u8 *path = pathbuf; + size_t pathlen = len; + unsigned int i; + int r; + + if (pathlen % 2 != 0 || pathlen > 6 || pathlen <= 0) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + + /* if pathlen == 6 then the first FID must be MF (== 3F00) */ + if (pathlen == 6 && (path[0] != 0x3f || path[1] != 0x00)) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + + /* unify path (the first FID should be MF) */ + if (path[0] != 0x3f || path[1] != 0x00) { + n_pathbuf[0] = 0x3f; + n_pathbuf[1] = 0x00; + memcpy(n_pathbuf + 2, path, pathlen); + path = n_pathbuf; + pathlen += 2; + } + + for (i = 0; i < pathlen - 2; i += 2) { + r = entersafe_select_fid(card, path[i], path[i + 1], NULL); + LOG_TEST_RET(card->ctx, r, "SELECT FILE (DF-ID) failed"); + } + return entersafe_select_fid(card, path[pathlen - 2], path[pathlen - 1], file_out); } static int entersafe_select_file(sc_card_t *card, - const sc_path_t *in_path, - sc_file_t **file_out) + const sc_path_t *in_path, + sc_file_t **file_out) { - int r; - char pbuf[SC_MAX_PATH_STRING_SIZE]; - assert(card); - assert(in_path); - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - - - r = sc_path_print(pbuf, sizeof(pbuf), &card->cache.current_path); - if (r != SC_SUCCESS) - pbuf[0] = '\0'; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "current path (%s, %s): %s (len: %"SC_FORMAT_LEN_SIZE_T"u)\n", - card->cache.current_path.type == SC_PATH_TYPE_DF_NAME ? - "aid" : "path", - card->cache.valid ? "valid" : "invalid", pbuf, - card->cache.current_path.len); - - switch(in_path->type) - { - case SC_PATH_TYPE_FILE_ID: - if (in_path->len != 2) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INVALID_ARGUMENTS); - return entersafe_select_fid(card,in_path->value[0],in_path->value[1], file_out); - case SC_PATH_TYPE_DF_NAME: - return entersafe_select_aid(card,in_path,file_out); - case SC_PATH_TYPE_PATH: - return entersafe_select_path(card,in_path->value,in_path->len,file_out); - default: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); - } + if (card == NULL || in_path == NULL) + return SC_ERROR_INTERNAL; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + switch (in_path->type) { + case SC_PATH_TYPE_FILE_ID: + if (in_path->len != 2) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + return entersafe_select_fid(card,in_path->value[0], in_path->value[1], file_out); + case SC_PATH_TYPE_DF_NAME: + return entersafe_select_aid(card, in_path, file_out); + case SC_PATH_TYPE_PATH: + return entersafe_select_path(card, in_path->value, in_path->len, file_out); + default: + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + } } -static int entersafe_create_mf(sc_card_t *card, sc_entersafe_create_data * data) +static int entersafe_create_mf(sc_card_t *card, sc_entersafe_create_data *data) { int r; sc_apdu_t apdu; @@ -722,38 +615,35 @@ static int entersafe_create_mf(sc_card_t *card, sc_entersafe_create_data * data) memcpy(data->data.df.init_key, init_key, sizeof(init_key)); - sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xE0,0x00,0x00); - apdu.cla=0x84; - apdu.data=(u8*)&data->data.df; - apdu.datalen=apdu.lc=sizeof(data->data.df); - - switch(card->type) - { - case SC_CARD_TYPE_ENTERSAFE_3K: - { - r = entersafe_transmit_apdu(card, &apdu,trans_code_3k,sizeof(trans_code_3k),0,1); - }break; - case SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C: - case SC_CARD_TYPE_ENTERSAFE_EJAVA_PK_01C: - case SC_CARD_TYPE_ENTERSAFE_EJAVA_PK_01C_T0: - case SC_CARD_TYPE_ENTERSAFE_EJAVA_H10CR_PK_01C_T1: - case SC_CARD_TYPE_ENTERSAFE_EJAVA_D11CR_PK_01C_T1: - case SC_CARD_TYPE_ENTERSAFE_EJAVA_C21C_PK_01C_T1: - case SC_CARD_TYPE_ENTERSAFE_EJAVA_A22CR_PK_01C_T1: - case SC_CARD_TYPE_ENTERSAFE_EJAVA_A40CR_PK_01C_T1: - { - r = entersafe_transmit_apdu(card, &apdu,trans_code_ftcos_pk_01c,sizeof(trans_code_ftcos_pk_01c),0,1); - }break; - default: - { - r = SC_ERROR_INTERNAL; - }break; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); + apdu.cla = 0x84; + apdu.data = (u8 *)&data->data.df; + apdu.datalen = apdu.lc = sizeof(data->data.df); + + switch(card->type) { + case SC_CARD_TYPE_ENTERSAFE_3K: + r = entersafe_transmit_apdu(card, &apdu, trans_code_3k, sizeof(trans_code_3k), 0, 1); + break; + case SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C: + case SC_CARD_TYPE_ENTERSAFE_EJAVA_PK_01C: + case SC_CARD_TYPE_ENTERSAFE_EJAVA_PK_01C_T0: + case SC_CARD_TYPE_ENTERSAFE_EJAVA_H10CR_PK_01C_T1: + case SC_CARD_TYPE_ENTERSAFE_EJAVA_D11CR_PK_01C_T1: + case SC_CARD_TYPE_ENTERSAFE_EJAVA_C21C_PK_01C_T1: + case SC_CARD_TYPE_ENTERSAFE_EJAVA_A22CR_PK_01C_T1: + case SC_CARD_TYPE_ENTERSAFE_EJAVA_A40CR_PK_01C_T1: + r = entersafe_transmit_apdu(card, &apdu, trans_code_ftcos_pk_01c, sizeof(trans_code_ftcos_pk_01c), 0, 1); + break; + default: + r = SC_ERROR_INTERNAL; + break; } - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } -static int entersafe_create_df(sc_card_t *card, sc_entersafe_create_data * data) + +static int entersafe_create_df(sc_card_t *card, sc_entersafe_create_data *data) { int r; sc_apdu_t apdu; @@ -762,17 +652,17 @@ static int entersafe_create_df(sc_card_t *card, sc_entersafe_create_data * data) memcpy(data->data.df.init_key, init_key, sizeof(init_key)); - sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xE0,0x01,0x00); - apdu.cla=0x84; - apdu.data=(u8*)&data->data.df; - apdu.lc=apdu.datalen=sizeof(data->data.df); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x01, 0x00); + apdu.cla = 0x84; + apdu.data = (u8 *)&data->data.df; + apdu.lc = apdu.datalen = sizeof(data->data.df); r = entersafe_transmit_apdu(card, &apdu,init_key,sizeof(init_key),0,1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } -static int entersafe_create_ef(sc_card_t *card, sc_entersafe_create_data * data) +static int entersafe_create_ef(sc_card_t *card, sc_entersafe_create_data *data) { int r; sc_apdu_t apdu; @@ -784,8 +674,8 @@ static int entersafe_create_ef(sc_card_t *card, sc_entersafe_create_data * data) apdu.data = (u8*)&data->data.ef; apdu.lc = apdu.datalen = sizeof(data->data.ef); - r = entersafe_transmit_apdu(card, &apdu,init_key,sizeof(init_key),0,1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + r = entersafe_transmit_apdu(card, &apdu, init_key, sizeof(init_key), 0, 1); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } @@ -793,47 +683,40 @@ static u8 process_acl_entry(sc_file_t *in, unsigned int method, unsigned int in_ { u8 def = (u8)in_def; const sc_acl_entry_t *entry = sc_file_get_acl_entry(in, method); - if (!entry) - { + if (!entry) { return def; - } - else if (entry->method & SC_AC_CHV) - { + } else if (entry->method & SC_AC_CHV) { unsigned int key_ref = entry->key_ref; if (key_ref == SC_AC_KEY_REF_NONE) return def; else return ENTERSAFE_AC_ALWAYS&0x04; - } - else if (entry->method & SC_AC_NEVER) - { + } else if (entry->method & SC_AC_NEVER) { return ENTERSAFE_AC_NEVER; } - else - { - return def; - } + return def; } static int entersafe_create_file(sc_card_t *card, sc_file_t *file) -{ - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - - if (file->type == SC_FILE_TYPE_WORKING_EF) { - sc_entersafe_create_data data; - memset(&data,0,sizeof(data)); - - data.data.ef.file_id[0] = (file->id>>8)&0xFF; - data.data.ef.file_id[1] = file->id&0xFF; - data.data.ef.size[0] = (file->size>>8)&0xFF; - data.data.ef.size[1] = file->size&0xFF; - memset(data.data.ef.ac,ENTERSAFE_AC_ALWAYS,sizeof(data.data.ef.ac)); - data.data.ef.ac[0] = process_acl_entry(file,SC_AC_OP_READ,ENTERSAFE_AC_ALWAYS); - data.data.ef.ac[1] = process_acl_entry(file,SC_AC_OP_UPDATE,ENTERSAFE_AC_ALWAYS); - - return entersafe_create_ef(card, &data); - } else - return SC_ERROR_INVALID_ARGUMENTS; +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (file->type == SC_FILE_TYPE_WORKING_EF) { + sc_entersafe_create_data data; + memset(&data, 0, sizeof(data)); + + data.data.ef.file_id[0] = (file->id >> 8) & 0xFF; + data.data.ef.file_id[1] = file->id & 0xFF; + data.data.ef.size[0] = (file->size >> 8) & 0xFF; + data.data.ef.size[1] = file->size & 0xFF; + memset(data.data.ef.ac, ENTERSAFE_AC_ALWAYS, sizeof(data.data.ef.ac)); + data.data.ef.ac[0] = process_acl_entry(file, SC_AC_OP_READ, ENTERSAFE_AC_ALWAYS); + data.data.ef.ac[1] = process_acl_entry(file, SC_AC_OP_UPDATE, ENTERSAFE_AC_ALWAYS); + + return entersafe_create_ef(card, &data); + } else { + return SC_ERROR_INVALID_ARGUMENTS; + } } static int entersafe_internal_set_security_env(sc_card_t *card, @@ -842,130 +725,127 @@ static int entersafe_internal_set_security_env(sc_card_t *card, { sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 *p=sbuf; + u8 *p = sbuf; int r; + if (card == NULL || env == NULL) + return SC_ERROR_INTERNAL; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert(card != NULL && env != NULL); - switch (env->operation) { - case SC_SEC_OPERATION_DECIPHER: - case SC_SEC_OPERATION_SIGN: - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); - apdu.p1 = 0x41; - apdu.p2 = 0xB8; - *p++ = 0x80; - *p++ = 0x01; - *p++ = 0x80; - *p++ = 0x83; - *p++ = 0x02; - *p++ = env->key_ref[0]; - *p++ = 0x22; - if(*size>1024/8) - { - if(*size == 2048/8) - { - *p++ = 0x89; - *p++ = 0x40; - memcpy(p,*data,0x40); - p+=0x40; - *data+=0x40; - *size-=0x40; - } - else - { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); - } - } - break; - default: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + case SC_SEC_OPERATION_DECIPHER: + case SC_SEC_OPERATION_SIGN: + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); + apdu.p1 = 0x41; + apdu.p2 = 0xB8; + *p++ = 0x80; + *p++ = 0x01; + *p++ = 0x80; + *p++ = 0x83; + *p++ = 0x02; + *p++ = env->key_ref[0]; + *p++ = 0x22; + if (*size > 1024 / 8) { + if (*size == 2048 / 8) { + *p++ = 0x89; + *p++ = 0x40; + memcpy(p, *data, 0x40); + p += 0x40; + *data += 0x40; + *size -= 0x40; + } else { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + } + } + break; + default: + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } - + apdu.le = 0; apdu.lc = apdu.datalen = p - sbuf; apdu.data = sbuf; apdu.resplen = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } /** - * We don't really set the security envirment,but cache it.It will be set when + * We don't really set the security environment,but cache it.It will be set when * security operation is performed later.Because we may transport partial of - * the sign/decipher data within the security envirment apdu. + * the sign/decipher data within the security environment apdu. */ static int entersafe_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { - assert(card); - assert(env); + if (card == NULL || env == NULL) + return SC_ERROR_INTERNAL; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - if(card->drv_data){ - free(card->drv_data); - card->drv_data=0; - } + if (card->drv_data) { + free(card->drv_data); + card->drv_data = 0; + } - card->drv_data = calloc(1,sizeof(*env)); - if(!card->drv_data) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_OUT_OF_MEMORY); + card->drv_data = calloc(1, sizeof(*env)); + if (!card->drv_data) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_OUT_OF_MEMORY); - memcpy(card->drv_data,env,sizeof(*env)); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); + memcpy(card->drv_data, env, sizeof(*env)); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int entersafe_restore_security_env(sc_card_t *card, int se_num) { - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - return SC_SUCCESS; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + return SC_SUCCESS; } static int entersafe_compute_with_prkey(sc_card_t *card, - const u8 * data, size_t datalen, - u8 * out, size_t outlen) + const u8 *data, size_t datalen, + u8 *out, size_t outlen) { int r; sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8* p=sbuf; + u8 *p = sbuf; size_t size = datalen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - if(!data) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INVALID_ARGUMENTS); + if (!data || datalen > SC_MAX_APDU_BUFFER_SIZE) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); memcpy(p,data,size); - if(!card->drv_data) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INTERNAL); + if (!card->drv_data) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INTERNAL); + + r = entersafe_internal_set_security_env(card, card->drv_data, &p, &size); + LOG_TEST_RET(card->ctx, r, "internal set security env failed"); - r = entersafe_internal_set_security_env(card,card->drv_data,&p,&size); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "internal set security env failed"); - - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x86,0x80); - apdu.data=p; + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x86, 0x80); + apdu.data = p; apdu.lc = size; apdu.datalen = size; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; - r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + r = entersafe_transmit_apdu(card, &apdu, 0, 0, 0, 0); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } @@ -974,16 +854,16 @@ static int entersafe_compute_signature(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - return entersafe_compute_with_prkey(card,data,datalen,out,outlen); + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + return entersafe_compute_with_prkey(card, data, datalen, out, outlen); } static int entersafe_decipher(sc_card_t *card, - const u8 * crgram, size_t crgram_len, - u8 * out, size_t outlen) + const u8 *crgram, size_t crgram_len, + u8 *out, size_t outlen) { - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - return entersafe_compute_with_prkey(card,crgram,crgram_len,out,outlen); + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + return entersafe_compute_with_prkey(card, crgram, crgram_len, out, outlen); } static void entersafe_init_pin_info(struct sc_pin_cmd_pin *pin, unsigned int num) @@ -996,52 +876,56 @@ static void entersafe_init_pin_info(struct sc_pin_cmd_pin *pin, unsigned int num pin->pad_char = 0x00; } -static int entersafe_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, - int *tries_left) +static int entersafe_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) { - int r; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - entersafe_init_pin_info(&data->pin1,0); - entersafe_init_pin_info(&data->pin2,1); - data->flags |= SC_PIN_CMD_NEED_PADDING; - - if(data->cmd!=SC_PIN_CMD_UNBLOCK) - { - r = iso_ops->pin_cmd(card,data,tries_left); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Verify rv:%i", r); - } - else - { - {/*verify*/ - sc_apdu_t apdu; - u8 sbuf[0x10]={0}; - - memcpy(sbuf,data->pin1.data,data->pin1.len); - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT,0x20,0x00,data->pin_reference+1); - apdu.lc = apdu.datalen = sizeof(sbuf); - apdu.data = sbuf; - - r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - } - - {/*change*/ - sc_apdu_t apdu; - u8 sbuf[0x12]={0}; - - sbuf[0] = 0x33; - sbuf[1] = 0x00; - memcpy(sbuf+2,data->pin2.data,data->pin2.len); - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT,0xF4,0x0B,data->pin_reference); - apdu.cla = 0x84; - apdu.lc = apdu.datalen = sizeof(sbuf); - apdu.data = sbuf; - - r = entersafe_transmit_apdu(card, &apdu,key_maintain,sizeof(key_maintain),1,1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - } - } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); + int r; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + entersafe_init_pin_info(&data->pin1, 0); + entersafe_init_pin_info(&data->pin2, 1); + data->flags |= SC_PIN_CMD_NEED_PADDING; + + if (data->cmd != SC_PIN_CMD_UNBLOCK) { + r = iso_ops->pin_cmd(card, data); + sc_log(card->ctx, "Verify rv:%i", r); + } else { + { /*verify*/ + sc_apdu_t apdu; + u8 sbuf[0x10] = {0}; + + if (data->pin1.len > sizeof(sbuf)) { + return SC_ERROR_INVALID_DATA; + } + + memcpy(sbuf, data->pin1.data, data->pin1.len); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0x00, data->pin_reference + 1); + apdu.lc = apdu.datalen = sizeof(sbuf); + apdu.data = sbuf; + + r = entersafe_transmit_apdu(card, &apdu, 0, 0, 0, 0); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + } + + { /*change*/ + sc_apdu_t apdu; + u8 sbuf[0x12] = {0}; + + if (data->pin2.len + 2 > sizeof(sbuf)) { + return SC_ERROR_INVALID_DATA; + } + + sbuf[0] = 0x33; + sbuf[1] = 0x00; + memcpy(sbuf + 2, data->pin2.data, data->pin2.len); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xF4, 0x0B, data->pin_reference); + apdu.cla = 0x84; + apdu.lc = apdu.datalen = sizeof(sbuf); + apdu.data = sbuf; + + r = entersafe_transmit_apdu(card, &apdu, key_maintain, sizeof(key_maintain), 1, 1); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + } + } + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int entersafe_erase_card(sc_card_t *card) @@ -1055,123 +939,135 @@ static int entersafe_erase_card(sc_card_t *card) sbuf[0] = 0x3f; sbuf[1] = 0x00; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x00, 0x00); - apdu.lc = 2; + apdu.lc = 2; apdu.datalen = 2; apdu.data = sbuf; - - r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - /* invalidate cache */ - card->cache.valid = 0; + + r = entersafe_transmit_apdu(card, &apdu, 0, 0, 0, 0); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xEE, 0x00, 0x00); - apdu.cla=0x84; - apdu.lc=2; - apdu.datalen=2; - apdu.data=sbuf; + apdu.cla = 0x84; + apdu.lc = 2; + apdu.datalen = 2; + apdu.data = sbuf; - switch(card->type) - { - case SC_CARD_TYPE_ENTERSAFE_3K: - { - r = entersafe_transmit_apdu(card, &apdu,trans_code_3k,sizeof(trans_code_3k),0,1); - }break; - case SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C: - case SC_CARD_TYPE_ENTERSAFE_EJAVA_PK_01C: - case SC_CARD_TYPE_ENTERSAFE_EJAVA_PK_01C_T0: - case SC_CARD_TYPE_ENTERSAFE_EJAVA_H10CR_PK_01C_T1: - case SC_CARD_TYPE_ENTERSAFE_EJAVA_D11CR_PK_01C_T1: - case SC_CARD_TYPE_ENTERSAFE_EJAVA_C21C_PK_01C_T1: - case SC_CARD_TYPE_ENTERSAFE_EJAVA_A22CR_PK_01C_T1: - case SC_CARD_TYPE_ENTERSAFE_EJAVA_A40CR_PK_01C_T1: - { - r = entersafe_transmit_apdu(card, &apdu,trans_code_ftcos_pk_01c,sizeof(trans_code_ftcos_pk_01c),0,1); - }break; - default: - { - r = SC_ERROR_INTERNAL; - }break; + switch(card->type) { + case SC_CARD_TYPE_ENTERSAFE_3K: + r = entersafe_transmit_apdu(card, &apdu, trans_code_3k, sizeof(trans_code_3k), 0, 1); + break; + case SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C: + case SC_CARD_TYPE_ENTERSAFE_EJAVA_PK_01C: + case SC_CARD_TYPE_ENTERSAFE_EJAVA_PK_01C_T0: + case SC_CARD_TYPE_ENTERSAFE_EJAVA_H10CR_PK_01C_T1: + case SC_CARD_TYPE_ENTERSAFE_EJAVA_D11CR_PK_01C_T1: + case SC_CARD_TYPE_ENTERSAFE_EJAVA_C21C_PK_01C_T1: + case SC_CARD_TYPE_ENTERSAFE_EJAVA_A22CR_PK_01C_T1: + case SC_CARD_TYPE_ENTERSAFE_EJAVA_A40CR_PK_01C_T1: + r = entersafe_transmit_apdu(card, &apdu, trans_code_ftcos_pk_01c, sizeof(trans_code_ftcos_pk_01c), 0, 1); + break; + default: + r = SC_ERROR_INTERNAL; + break; } - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } -static void entersafe_encode_bignum(u8 tag,sc_pkcs15_bignum_t bignum,u8** ptr) +static int +entersafe_encode_bignum(u8 tag, sc_pkcs15_bignum_t bignum, u8 **ptr, size_t *ptrlen) { - u8 *p=*ptr; - - *p++=tag; - if(bignum.len<128) - { - *p++=(u8)bignum.len; - } - else - { - u8 bytes=1; - size_t len=bignum.len; - while(len) - { - len=len>>8; - ++bytes; - } - bytes&=0x0F; - *p++=0x80|bytes; - while(bytes) - { - *p++=bignum.len>>((bytes-1)*8); - --bytes; - } - } - memcpy(p,bignum.data,bignum.len); - entersafe_reverse_buffer(p,bignum.len); - p+=bignum.len; - *ptr = p; + u8 *p = *ptr; + + /* lower bound for 1B length encoding */ + if (*ptrlen < bignum.len + 2) { + return SC_ERROR_INVALID_DATA; + } + + *p++ = tag; + *ptrlen -= 1; + if (bignum.len < 128) { + *p++ = (u8)bignum.len; + *ptrlen -= 1; + } else { + u8 bytes = 1; + size_t len = bignum.len; + while (len) { + len = len >> 8; + ++bytes; + } + bytes &= 0x0F; + *p++ = 0x80 | bytes; + *ptrlen -= 1; + while (bytes) { + *p++ = bignum.len >> ((bytes - 1) * 8); + *ptrlen -= 1; + --bytes; + } + } + /* Tag and length bytes already counted and checked */ + if (*ptrlen < bignum.len) { + return SC_ERROR_INVALID_DATA; + } + + memcpy(p, bignum.data, bignum.len); + entersafe_reverse_buffer(p, bignum.len); + p += bignum.len; + *ptr = p; + *ptrlen -= bignum.len; + + return SC_SUCCESS; } -static int entersafe_write_small_rsa_key(sc_card_t *card,u8 key_id,struct sc_pkcs15_prkey_rsa *rsa) +static int entersafe_write_small_rsa_key(sc_card_t *card, u8 key_id, struct sc_pkcs15_prkey_rsa *rsa) { - sc_apdu_t apdu; - u8 sbuff[SC_MAX_APDU_BUFFER_SIZE]; - int r; - u8 *p=sbuff; + sc_apdu_t apdu; + u8 sbuff[SC_MAX_APDU_BUFFER_SIZE]; + int r; + u8 *p = sbuff; + size_t buflen = sizeof(sbuff); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - {/* write prkey */ - *p++=0x00; /* EC */ - *p++=0x00; /* ver */ - entersafe_encode_bignum('E',rsa->exponent,&p); - entersafe_encode_bignum('D',rsa->d,&p); - - sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF4,0x22,key_id); - apdu.cla=0x84; - apdu.data=sbuff; - apdu.lc=apdu.datalen=p-sbuff; - - r=entersafe_transmit_apdu(card,&apdu,key_maintain,sizeof(key_maintain),1,1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write prkey failed"); - } - - p=sbuff; - {/* write pukey */ - *p++=0x00; /* EC */ - *p++=0x00; /* ver */ - entersafe_encode_bignum('E',rsa->exponent,&p); - entersafe_encode_bignum('N',rsa->modulus,&p); - - sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF4,0x2A,key_id); - apdu.cla=0x84; - apdu.data=sbuff; - apdu.lc=apdu.datalen=p-sbuff; - - r=entersafe_transmit_apdu(card,&apdu,key_maintain,sizeof(key_maintain),1,1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write pukey failed"); - } - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); + { /* write prkey */ + *p++ = 0x00; /* EC */ + *p++ = 0x00; /* ver */ + r = entersafe_encode_bignum('E', rsa->exponent, &p, &buflen); + LOG_TEST_RET(card->ctx, r, "Failed to encode bignum. Buffer too small?"); + r = entersafe_encode_bignum('D', rsa->d, &p, &buflen); + LOG_TEST_RET(card->ctx, r, "Failed to encode bignum. Buffer too small?"); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xF4, 0x22, key_id); + apdu.cla = 0x84; + apdu.data = sbuff; + apdu.lc = apdu.datalen = p - sbuff; + + r = entersafe_transmit_apdu(card, &apdu, key_maintain, sizeof(key_maintain), 1, 1); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "Write prkey failed"); + } + + p = sbuff; + { /* write pukey */ + *p++ = 0x00; /* EC */ + *p++ = 0x00; /* ver */ + r = entersafe_encode_bignum('E', rsa->exponent, &p, &buflen); + LOG_TEST_RET(card->ctx, r, "Failed to encode bignum. Buffer too small?"); + r = entersafe_encode_bignum('N', rsa->modulus, &p, &buflen); + LOG_TEST_RET(card->ctx, r, "Failed to encode bignum. Buffer too small?"); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xF4, 0x2A, key_id); + apdu.cla = 0x84; + apdu.data = sbuff; + apdu.lc = apdu.datalen = p - sbuff; + + r = entersafe_transmit_apdu(card, &apdu, key_maintain, sizeof(key_maintain), 1, 1); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "Write pukey failed"); + } + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int entersafe_write_rsa_key_factor(sc_card_t *card, @@ -1184,178 +1080,174 @@ static int entersafe_write_rsa_key_factor(sc_card_t *card, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - {/* MSE */ - u8 sbuff[4]; - sbuff[0]=0x84; - sbuff[1]=0x02; - sbuff[2]=key_id; - sbuff[3]=usage; - - sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0x22,0x01,0xB8); - apdu.data=sbuff; - apdu.lc=apdu.datalen=4; - - r=entersafe_transmit_apdu(card,&apdu,0,0,0,0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write prkey factor failed(MSE)"); + { /* MSE */ + u8 sbuff[4]; + sbuff[0] = 0x84; + sbuff[1] = 0x02; + sbuff[2] = key_id; + sbuff[3] = usage; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x01, 0xB8); + apdu.data = sbuff; + apdu.lc = apdu.datalen = 4; + + r = entersafe_transmit_apdu(card, &apdu, 0, 0, 0, 0); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "Write prkey factor failed(MSE)"); } - {/* Write 'x'; */ + { /* Write 'x'; */ u8 sbuff[SC_MAX_APDU_BUFFER_SIZE]; - sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0x46,factor,0x00); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, factor, 0x00); - memcpy(sbuff,data.data,data.len); - entersafe_reverse_buffer(sbuff,data.len); + memcpy(sbuff, data.data, data.len); + entersafe_reverse_buffer(sbuff, data.len); /* * PK01C and PK13C smart card only support 1024 or 2048bit key . * Size of exponent1 exponent2 coefficient of RSA private key keep the same as size of prime1 * So check factor is padded with zero or not */ - switch(factor){ - case 0x3: - case 0x4: - case 0x5: - { - if( data.len > 32 && data.len < 64 ) - { - for(r = data.len ; r < 64 ; r ++) - sbuff[r] = 0; - data.len = 64; - } - else if( data.len > 64 && data.len < 128 ) - { - for(r = data.len ; r < 128 ; r ++) - sbuff[r] = 0; - data.len = 128; - } - } - break; - default: - break; - } - - apdu.data=sbuff; - apdu.lc=apdu.datalen=data.len; - - r = entersafe_transmit_apdu(card,&apdu,0,0,0,0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write prkey factor failed"); + switch(factor) { + case 0x3: + case 0x4: + case 0x5: { + size_t i; + if (data.len > 32 && data.len < 64) { + for (i = data.len; i < 64; i++) + sbuff[i] = 0; + data.len = 64; + } else if( data.len > 64 && data.len < 128 ) { + for (i = data.len; i < 128; i++) + sbuff[i] = 0; + data.len = 128; + } + } + break; + default: + break; + } + + apdu.data = sbuff; + apdu.lc = apdu.datalen = data.len; + + r = entersafe_transmit_apdu(card, &apdu, 0, 0, 0, 0); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "Write prkey factor failed"); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int entersafe_write_large_rsa_key(sc_card_t *card,u8 key_id,struct sc_pkcs15_prkey_rsa *rsa) { - int r; + int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - {/* write prkey */ - r = entersafe_write_rsa_key_factor(card,key_id,0x22,0x01,rsa->p); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "write p failed"); - r = entersafe_write_rsa_key_factor(card,key_id,0x22,0x02,rsa->q); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "write q failed"); - r = entersafe_write_rsa_key_factor(card,key_id,0x22,0x03,rsa->dmp1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "write dmp1 failed"); - r = entersafe_write_rsa_key_factor(card,key_id,0x22,0x04,rsa->dmq1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "write dmq1 failed"); - r = entersafe_write_rsa_key_factor(card,key_id,0x22,0x05,rsa->iqmp); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "write iqmp failed"); - } - - {/* write pukey */ - u8 sbuff[SC_MAX_APDU_BUFFER_SIZE]; - sc_apdu_t apdu; - - /* first 64(0x40) bytes of N */ - sbuff[0]=0x83; - sbuff[1]=0x02; - sbuff[2]=key_id; - sbuff[3]=0x2A; - sbuff[4]=0x89; - sbuff[5]=0x40; - memcpy(sbuff+6,rsa->modulus.data,0x40); - - sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0x22,0x01,0xB8); - apdu.data=sbuff; - apdu.lc=apdu.datalen=0x46; - - r=entersafe_transmit_apdu(card,&apdu,0,0,0,0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write pukey N(1) failed"); - - /* left 192(0xC0) bytes of N */ - sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0x46,0x0B,0x00); - apdu.data=rsa->modulus.data+0x40; - apdu.lc=apdu.datalen=0xC0; - - r=entersafe_transmit_apdu(card,&apdu,0,0,0,0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write pukey N(2) failed"); - - /* E */ - r = entersafe_write_rsa_key_factor(card,key_id,0x2A,0x0D,rsa->exponent); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "write exponent failed"); - } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); + { /* write prkey */ + r = entersafe_write_rsa_key_factor(card, key_id, 0x22, 0x01, rsa->p); + LOG_TEST_RET(card->ctx, r, "write p failed"); + r = entersafe_write_rsa_key_factor(card, key_id, 0x22, 0x02, rsa->q); + LOG_TEST_RET(card->ctx, r, "write q failed"); + r = entersafe_write_rsa_key_factor(card, key_id, 0x22, 0x03, rsa->dmp1); + LOG_TEST_RET(card->ctx, r, "write dmp1 failed"); + r = entersafe_write_rsa_key_factor(card, key_id, 0x22, 0x04, rsa->dmq1); + LOG_TEST_RET(card->ctx, r, "write dmq1 failed"); + r = entersafe_write_rsa_key_factor(card, key_id, 0x22, 0x05, rsa->iqmp); + LOG_TEST_RET(card->ctx, r, "write iqmp failed"); + } + + { /* write pukey */ + u8 sbuff[SC_MAX_APDU_BUFFER_SIZE]; + sc_apdu_t apdu; + + /* first 64(0x40) bytes of N */ + sbuff[0] = 0x83; + sbuff[1] = 0x02; + sbuff[2] = key_id; + sbuff[3] = 0x2A; + sbuff[4] = 0x89; + sbuff[5] = 0x40; + memcpy(sbuff + 6, rsa->modulus.data, 0x40); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x01, 0xB8); + apdu.data = sbuff; + apdu.lc = apdu.datalen = 0x46; + + r = entersafe_transmit_apdu(card, &apdu, 0, 0, 0, 0); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "Write pukey N(1) failed"); + + /* left 192(0xC0) bytes of N */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x0B, 0x00); + apdu.data = rsa->modulus.data + 0x40; + apdu.lc = apdu.datalen = 0xC0; + + r = entersafe_transmit_apdu(card, &apdu, 0, 0, 0, 0); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "Write pukey N(2) failed"); + + /* E */ + r = entersafe_write_rsa_key_factor(card, key_id, 0x2A, 0x0D, rsa->exponent); + LOG_TEST_RET(card->ctx, r, "write exponent failed"); + } + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int entersafe_write_symmetric_key(sc_card_t *card, - u8 key_id,u8 usage, - u8 EC,u8 ver, - u8 *data,size_t len) + u8 key_id, u8 usage, + u8 EC, u8 ver, + u8 *data, size_t len) { - sc_apdu_t apdu; - u8 sbuff[SC_MAX_APDU_BUFFER_SIZE]={0}; - int r; + sc_apdu_t apdu; + u8 sbuff[SC_MAX_APDU_BUFFER_SIZE] = {0}; + int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - if(len>240) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INCORRECT_PARAMETERS); + if (len > 240) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INCORRECT_PARAMETERS); - sbuff[0]=EC; - sbuff[1]=ver; - memcpy(&sbuff[2],data,len); + sbuff[0] = EC; + sbuff[1] = ver; + memcpy(&sbuff[2], data, len); - sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF4,usage,key_id); - apdu.cla=0x84; - apdu.data=sbuff; - apdu.lc=apdu.datalen=len+2; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xF4, usage, key_id); + apdu.cla = 0x84; + apdu.data = sbuff; + apdu.lc = apdu.datalen = len + 2; - r=entersafe_transmit_apdu(card,&apdu,key_maintain,sizeof(key_maintain),1,1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2),"Write prkey failed"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); + r = entersafe_transmit_apdu(card, &apdu, key_maintain, sizeof(key_maintain), 1, 1); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "Write prkey failed"); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int entersafe_write_key(sc_card_t *card, sc_entersafe_wkey_data *data) { - struct sc_pkcs15_prkey_rsa* rsa=data->key_data.rsa; + struct sc_pkcs15_prkey_rsa *rsa = data->key_data.rsa; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - switch(data->usage) - { - case 0x22: - if(rsa->modulus.len < 256) - return entersafe_write_small_rsa_key(card,data->key_id,rsa); - else - return entersafe_write_large_rsa_key(card,data->key_id,rsa); - break; - case 0x2A: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); - break; - default: - return entersafe_write_symmetric_key(card,data->key_id,data->usage, - data->key_data.symmetric.EC, - data->key_data.symmetric.ver, - data->key_data.symmetric.key_val, - data->key_data.symmetric.key_len); - break; - } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); + switch(data->usage) { + case 0x22: + if(rsa->modulus.len < 256) + return entersafe_write_small_rsa_key(card,data->key_id, rsa); + else + return entersafe_write_large_rsa_key(card,data->key_id, rsa); + break; + case 0x2A: + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); + break; + default: + return entersafe_write_symmetric_key(card,data->key_id,data->usage, + data->key_data.symmetric.EC, + data->key_data.symmetric.ver, + data->key_data.symmetric.key_val, + data->key_data.symmetric.key_len); + break; + } + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int entersafe_gen_key(sc_card_t *card, sc_entersafe_gen_key_data *data) @@ -1363,76 +1255,92 @@ static int entersafe_gen_key(sc_card_t *card, sc_entersafe_gen_key_data *data) int r; size_t len = data->key_length >> 3; sc_apdu_t apdu; - u8 rbuf[300]; - u8 sbuf[4],*p; + u8 rbuf[300] = {0}; + u8 sbuf[4], *p; + size_t plen = 0; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* MSE */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x01, 0xB8); - apdu.lc=0x04; - sbuf[0]=0x83; - sbuf[1]=0x02; - sbuf[2]=data->key_id; - sbuf[3]=0x2A; + apdu.lc = 0x04; + sbuf[0] = 0x83; + sbuf[1] = 0x02; + sbuf[2] = data->key_id; + sbuf[3] = 0x2A; apdu.data = sbuf; - apdu.datalen=4; - apdu.lc=4; - apdu.le=0; + apdu.datalen = 4; + apdu.lc = 4; + apdu.le = 0; - r=entersafe_transmit_apdu(card, &apdu, 0,0,0,0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card,apdu.sw1,apdu.sw2),"EnterSafe set MSE failed"); + r = entersafe_transmit_apdu(card, &apdu, 0, 0, 0, 0); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "EnterSafe set MSE failed"); /* generate key */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, 0x00); - apdu.le = 0; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, 0x00); + apdu.le = 0; sbuf[0] = (u8)(data->key_length >> 8); sbuf[1] = (u8)(data->key_length); - apdu.data = sbuf; - apdu.lc = 2; + apdu.data = sbuf; + apdu.lc = 2; apdu.datalen = 2; - r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card,apdu.sw1,apdu.sw2),"EnterSafe generate keypair failed"); + r = entersafe_transmit_apdu(card, &apdu, 0, 0, 0, 0); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "EnterSafe generate key pair failed"); /* read public key via READ PUBLIC KEY */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xE6, 0x2A, data->key_id); + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xE6, 0x2A, data->key_id); apdu.cla = 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; - r = entersafe_transmit_apdu(card, &apdu,0,0,0,0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card,apdu.sw1,apdu.sw2),"EnterSafe get pukey failed"); - - data->modulus = malloc(len); - if (!data->modulus) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_OUT_OF_MEMORY); - - p=rbuf; - assert(*p=='E'); - p+=2+p[1]; + r = entersafe_transmit_apdu(card, &apdu, 0, 0, 0, 0); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "EnterSafe get pukey failed"); + + p = rbuf; + plen = apdu.resplen; + if (*p != 'E') { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_DATA); + } + if ((size_t)(p - rbuf) + 2 + p[1] >= plen) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_DATA); + } + p += 2 + p[1]; /* N */ - assert(*p=='N'); + if (*p != 'N') { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_DATA); + } + if ((size_t)(p - rbuf) + 2 >= plen) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_DATA); + } ++p; - if(*p++>0x80) - { - u8 len_bytes=(*(p-1))&0x0f; - size_t module_len=0; - while(len_bytes!=0) - { - module_len=module_len<<8; - module_len+=*p++; - --len_bytes; - } + if (*p++ > 0x80) { + u8 len_bytes = (*(p - 1)) & 0x0f; + size_t module_len = 0; + if ((size_t)(p - rbuf) + len_bytes >= plen) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_DATA); + } + while (len_bytes != 0) { + module_len = module_len << 8; + module_len += *p++; + --len_bytes; + } + } + + if ((p - rbuf) + len >= plen) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_DATA); } - entersafe_reverse_buffer(p,len); - memcpy(data->modulus,p,len); + data->modulus = malloc(len); + if (!data->modulus) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_OUT_OF_MEMORY); + entersafe_reverse_buffer(p, len); + memcpy(data->modulus, p, len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int entersafe_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) @@ -1442,30 +1350,33 @@ static int entersafe_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert(serial); + if (serial == NULL) + return SC_ERROR_INTERNAL; - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT,0xEA,0x00,0x00); - apdu.cla=0x80; - apdu.resp=rbuf; - apdu.resplen=sizeof(rbuf); - apdu.le=0x08; + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xEA, 0x00, 0x00); + apdu.cla = 0x80; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 0x08; - r=entersafe_transmit_apdu(card, &apdu,0,0,0,0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card,apdu.sw1,apdu.sw2),"EnterSafe get SN failed"); + r = entersafe_transmit_apdu(card, &apdu, 0, 0, 0, 0); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "EnterSafe get SN failed"); + if (apdu.resplen != 8) + LOG_TEST_RET(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Invalid length of SN"); - card->serialnr.len=serial->len=8; - memcpy(card->serialnr.value,rbuf,8); - memcpy(serial->value,rbuf,8); + card->serialnr.len = serial->len = 8; + memcpy(card->serialnr.value, rbuf, 8); + memcpy(serial->value, rbuf, 8); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } -static int entersafe_preinstall_rsa_2048(sc_card_t *card,u8 key_id) +static int entersafe_preinstall_rsa_2048(sc_card_t *card, u8 key_id) { u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_apdu_t apdu; - int ret=0; + int ret = 0; static u8 const rsa_key_e[] = { 'E', 0x04, 0x01, 0x00, 0x01, 0x00 @@ -1473,7 +1384,7 @@ static int entersafe_preinstall_rsa_2048(sc_card_t *card,u8 key_id) SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - /* create rsa item in IKF */ + /* create rsa item in IKF */ sbuf[0] = 0x04; /* key len extern */ sbuf[1] = 0x0a; /* key len */ sbuf[2] = 0x22; /* USAGE */ @@ -1489,15 +1400,15 @@ static int entersafe_preinstall_rsa_2048(sc_card_t *card,u8 key_id) sbuf[9 + sizeof(rsa_key_e) + 2] = 0x04; sbuf[9 + sizeof(rsa_key_e) + 3] = 0x00; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT,0xF0,0x00,key_id); - apdu.cla=0x84; - apdu.data=sbuf; - apdu.lc=apdu.datalen=9 + sizeof(rsa_key_e) + 4; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xF0, 0x00, key_id); + apdu.cla = 0x84; + apdu.data = sbuf; + apdu.lc = apdu.datalen = 9 + sizeof(rsa_key_e) + 4; - ret = entersafe_transmit_apdu(card,&apdu,init_key,sizeof(init_key),0,1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Preinstall rsa failed"); + ret = entersafe_transmit_apdu(card, &apdu, init_key, sizeof(init_key), 0, 1); + LOG_TEST_RET(card->ctx, ret, "Preinstall rsa failed"); - /* create rsa item in PKF */ + /* create rsa item in PKF */ sbuf[0] = 0x01; /* key len extern */ sbuf[1] = 0x0A; /* key len */ sbuf[2] = 0x2A; /* USAGE */ @@ -1507,108 +1418,104 @@ static int entersafe_preinstall_rsa_2048(sc_card_t *card,u8 key_id) sbuf[6] = 0x40; /* ALGO */ sbuf[7] = 0x00; /* EC */ sbuf[8] = 0x00; /* VER */ - memcpy(&sbuf[9], rsa_key_e, sizeof(rsa_key_e)); + memcpy(&sbuf[9], rsa_key_e, sizeof(rsa_key_e)); sbuf[9 + sizeof(rsa_key_e) + 0] = 'N'; sbuf[9 + sizeof(rsa_key_e) + 1] = 0x82; sbuf[9 + sizeof(rsa_key_e) + 2] = 0x01; sbuf[9 + sizeof(rsa_key_e) + 3] = 0x00; - sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF0,0x00,key_id); - apdu.cla=0x84; - apdu.data=sbuf; - apdu.lc=apdu.datalen=9 + sizeof(rsa_key_e) + 4; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xF0, 0x00, key_id); + apdu.cla = 0x84; + apdu.data = sbuf; + apdu.lc = apdu.datalen = 9 + sizeof(rsa_key_e) + 4; - ret=entersafe_transmit_apdu(card,&apdu,init_key,sizeof(init_key),0,1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Preinstall rsa failed"); + ret = entersafe_transmit_apdu(card, &apdu, init_key, sizeof(init_key), 0, 1); + LOG_TEST_RET(card->ctx, ret, "Preinstall rsa failed"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } -static int entersafe_preinstall_keys(sc_card_t *card,int (*install_rsa)(sc_card_t *,u8)) +static int entersafe_preinstall_keys(sc_card_t *card, int (*install_rsa)(sc_card_t *, u8)) { - int r; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - sc_apdu_t apdu; + int r; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + sc_apdu_t apdu; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - {/* RSA */ - u8 rsa_index; - for(rsa_index=ENTERSAFE_MIN_KEY_ID; - rsa_index<=ENTERSAFE_MAX_KEY_ID; - ++rsa_index) - { - r=install_rsa(card,rsa_index); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Preinstall rsa key failed"); - } - } - - {/* key maintain */ - /* create key maintain*/ - sbuf[0] = 0; /* key len extern */ - sbuf[1] = sizeof(key_maintain); /* key len */ - sbuf[2] = 0x03; /* USAGE */ - sbuf[3] = ENTERSAFE_AC_ALWAYS; /* use AC */ - sbuf[4] = ENTERSAFE_AC_ALWAYS; /* CHANGE AC */ - sbuf[5] = ENTERSAFE_AC_NEVER; /* UPDATE AC */ - sbuf[6] = 0x01; /* ALGO */ - sbuf[7] = 0x00; /* EC */ - sbuf[8] = 0x00; /* VER */ - memcpy(&sbuf[9], key_maintain, sizeof(key_maintain)); - - sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF0,0x00,0x00); - apdu.cla=0x84; - apdu.data=sbuf; - apdu.lc=apdu.datalen=0x19; - - r = entersafe_transmit_apdu(card,&apdu,init_key,sizeof(init_key),0,1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Preinstall key maintain failed"); - } - - {/* user PIN */ - memset(sbuf,0,sizeof(sbuf)); - sbuf[0] = 0; /* key len extern */ - sbuf[1] = 16; /* key len */ - sbuf[2] = 0x0B; /* USAGE */ - sbuf[3] = ENTERSAFE_AC_ALWAYS; /* use AC */ - sbuf[4] = 0X04; /* CHANGE AC */ - sbuf[5] = 0x38; /* UPDATE AC */ - sbuf[6] = 0x01; /* ALGO */ - sbuf[7] = 0xFF; /* EC */ - sbuf[8] = 0x00; /* VER */ - - sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF0,0x00,ENTERSAFE_USER_PIN_ID); - apdu.cla=0x84; - apdu.data=sbuf; - apdu.lc=apdu.datalen=0x19; - - r = entersafe_transmit_apdu(card,&apdu,init_key,sizeof(init_key),0,1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Preinstall user PIN failed"); - } - - {/* user PUK */ - memset(sbuf,0,sizeof(sbuf)); - sbuf[0] = 0; /* key len extern */ - sbuf[1] = 16; /* key len */ - sbuf[2] = 0x0B; /* USAGE */ - sbuf[3] = ENTERSAFE_AC_ALWAYS; /* use AC */ - sbuf[4] = 0X08; /* CHANGE AC */ - sbuf[5] = 0xC0; /* UPDATE AC */ - sbuf[6] = 0x01; /* ALGO */ - sbuf[7] = 0xFF; /* EC */ - sbuf[8] = 0x00; /* VER */ - - sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xF0,0x00,ENTERSAFE_USER_PIN_ID+1); - apdu.cla=0x84; - apdu.data=sbuf; - apdu.lc=apdu.datalen=0x19; - - r = entersafe_transmit_apdu(card,&apdu,init_key,sizeof(init_key),0,1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Preinstall user PUK failed"); - } - - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); + { /* RSA */ + u8 rsa_index; + for (rsa_index = ENTERSAFE_MIN_KEY_ID; rsa_index <= ENTERSAFE_MAX_KEY_ID; ++rsa_index) { + r = install_rsa(card, rsa_index); + LOG_TEST_RET(card->ctx, r, "Preinstall rsa key failed"); + } + } + + { /* key maintain */ + /* create key maintain*/ + sbuf[0] = 0; /* key len extern */ + sbuf[1] = sizeof(key_maintain); /* key len */ + sbuf[2] = 0x03; /* USAGE */ + sbuf[3] = ENTERSAFE_AC_ALWAYS; /* use AC */ + sbuf[4] = ENTERSAFE_AC_ALWAYS; /* CHANGE AC */ + sbuf[5] = ENTERSAFE_AC_NEVER; /* UPDATE AC */ + sbuf[6] = 0x01; /* ALGO */ + sbuf[7] = 0x00; /* EC */ + sbuf[8] = 0x00; /* VER */ + memcpy(&sbuf[9], key_maintain, sizeof(key_maintain)); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xF0, 0x00, 0x00); + apdu.cla = 0x84; + apdu.data = sbuf; + apdu.lc = apdu.datalen = 0x19; + + r = entersafe_transmit_apdu(card, &apdu, init_key, sizeof(init_key), 0, 1); + LOG_TEST_RET(card->ctx, r, "Preinstall key maintain failed"); + } + + { /* user PIN */ + memset(sbuf, 0, sizeof(sbuf)); + sbuf[0] = 0; /* key len extern */ + sbuf[1] = 16; /* key len */ + sbuf[2] = 0x0B; /* USAGE */ + sbuf[3] = ENTERSAFE_AC_ALWAYS; /* use AC */ + sbuf[4] = 0X04; /* CHANGE AC */ + sbuf[5] = 0x38; /* UPDATE AC */ + sbuf[6] = 0x01; /* ALGO */ + sbuf[7] = 0xFF; /* EC */ + sbuf[8] = 0x00; /* VER */ + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xF0, 0x00, ENTERSAFE_USER_PIN_ID); + apdu.cla = 0x84; + apdu.data = sbuf; + apdu.lc = apdu.datalen = 0x19; + + r = entersafe_transmit_apdu(card, &apdu, init_key, sizeof(init_key), 0, 1); + LOG_TEST_RET(card->ctx, r, "Preinstall user PIN failed"); + } + + { /* user PUK */ + memset(sbuf, 0, sizeof(sbuf)); + sbuf[0] = 0; /* key len extern */ + sbuf[1] = 16; /* key len */ + sbuf[2] = 0x0B; /* USAGE */ + sbuf[3] = ENTERSAFE_AC_ALWAYS; /* use AC */ + sbuf[4] = 0X08; /* CHANGE AC */ + sbuf[5] = 0xC0; /* UPDATE AC */ + sbuf[6] = 0x01; /* ALGO */ + sbuf[7] = 0xFF; /* EC */ + sbuf[8] = 0x00; /* VER */ + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xF0, 0x00, ENTERSAFE_USER_PIN_ID + 1); + apdu.cla = 0x84; + apdu.data = sbuf; + apdu.lc = apdu.datalen = 0x19; + + r = entersafe_transmit_apdu(card, &apdu, init_key, sizeof(init_key), 0, 1); + LOG_TEST_RET(card->ctx, r, "Preinstall user PUK failed"); + } + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int entersafe_card_ctl_2048(sc_card_t *card, unsigned long cmd, void *ptr) @@ -1617,29 +1524,28 @@ static int entersafe_card_ctl_2048(sc_card_t *card, unsigned long cmd, void *ptr SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - switch (cmd) - { - case SC_CARDCTL_ENTERSAFE_CREATE_FILE: - if (tmp->type == SC_ENTERSAFE_MF_DATA) - return entersafe_create_mf(card, tmp); - else if (tmp->type == SC_ENTERSAFE_DF_DATA) - return entersafe_create_df(card, tmp); - else if (tmp->type == SC_ENTERSAFE_EF_DATA) - return entersafe_create_ef(card, tmp); - else - return SC_ERROR_INTERNAL; - case SC_CARDCTL_ENTERSAFE_WRITE_KEY: - return entersafe_write_key(card, (sc_entersafe_wkey_data *)ptr); - case SC_CARDCTL_ENTERSAFE_GENERATE_KEY: - return entersafe_gen_key(card, (sc_entersafe_gen_key_data *)ptr); - case SC_CARDCTL_ERASE_CARD: - return entersafe_erase_card(card); - case SC_CARDCTL_GET_SERIALNR: - return entersafe_get_serialnr(card, (sc_serial_number_t *)ptr); - case SC_CARDCTL_ENTERSAFE_PREINSTALL_KEYS: - return entersafe_preinstall_keys(card,entersafe_preinstall_rsa_2048); - default: - return SC_ERROR_NOT_SUPPORTED; + switch (cmd) { + case SC_CARDCTL_ENTERSAFE_CREATE_FILE: + if (tmp->type == SC_ENTERSAFE_MF_DATA) + return entersafe_create_mf(card, tmp); + else if (tmp->type == SC_ENTERSAFE_DF_DATA) + return entersafe_create_df(card, tmp); + else if (tmp->type == SC_ENTERSAFE_EF_DATA) + return entersafe_create_ef(card, tmp); + else + return SC_ERROR_INTERNAL; + case SC_CARDCTL_ENTERSAFE_WRITE_KEY: + return entersafe_write_key(card, (sc_entersafe_wkey_data *)ptr); + case SC_CARDCTL_ENTERSAFE_GENERATE_KEY: + return entersafe_gen_key(card, (sc_entersafe_gen_key_data *)ptr); + case SC_CARDCTL_ERASE_CARD: + return entersafe_erase_card(card); + case SC_CARDCTL_GET_SERIALNR: + return entersafe_get_serialnr(card, (sc_serial_number_t *)ptr); + case SC_CARDCTL_ENTERSAFE_PREINSTALL_KEYS: + return entersafe_preinstall_keys(card, entersafe_preinstall_rsa_2048); + default: + return SC_ERROR_NOT_SUPPORTED; } } @@ -1649,22 +1555,22 @@ static struct sc_card_driver * sc_get_driver(void) if (iso_ops == NULL) iso_ops = iso_drv->ops; - + entersafe_ops = *iso_drv->ops; entersafe_ops.match_card = entersafe_match_card; - entersafe_ops.init = entersafe_init; + entersafe_ops.init = entersafe_init; entersafe_ops.read_binary = entersafe_read_binary; entersafe_ops.write_binary = NULL; entersafe_ops.update_binary = entersafe_update_binary; entersafe_ops.select_file = entersafe_select_file; entersafe_ops.restore_security_env = entersafe_restore_security_env; - entersafe_ops.set_security_env = entersafe_set_security_env; + entersafe_ops.set_security_env = entersafe_set_security_env; entersafe_ops.decipher = entersafe_decipher; entersafe_ops.compute_signature = entersafe_compute_signature; entersafe_ops.create_file = entersafe_create_file; entersafe_ops.delete_file = NULL; entersafe_ops.pin_cmd = entersafe_pin_cmd; - entersafe_ops.card_ctl = entersafe_card_ctl_2048; + entersafe_ops.card_ctl = entersafe_card_ctl_2048; entersafe_ops.process_fci = entersafe_process_fci; return &entersafe_drv; } diff --git a/src/libopensc/card-eoi.c b/src/libopensc/card-eoi.c new file mode 100644 index 0000000000..b10da71994 --- /dev/null +++ b/src/libopensc/card-eoi.c @@ -0,0 +1,588 @@ +/* + * Support for the eOI card + * + * Copyright (C) 2022 Luka Logar + * + * 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 + */ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "opensc.h" + +#if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) + +#include +#include +#include "internal.h" +#include "sm/sm-eac.h" +#include "common/compat_strlcpy.h" +#include "card-eoi.h" + +static struct sc_card_operations eoi_ops; + +static struct { + int len; + struct sc_object_id oid; +} eoi_curves[] = { + /* secp384r1 */ + {384, {{1, 3, 132, 0, 34, -1}}} +}; + +static char *eoi_model = "ChipDocLite"; + +/* The description of the driver. */ +static struct sc_card_driver eoi_drv = +{ + "eOI (Slovenian eID card)", + "eOI", + &eoi_ops, + NULL, 0, NULL +}; + +static const struct sc_atr_table eoi_atrs[] = { + /* Contact interface */ + { "3b:d5:18:ff:81:91:fe:1f:c3:80:73:c8:21:10:0a", NULL, NULL, SC_CARD_TYPE_EOI, 0, NULL }, + /* Contactless interface */ + { "3b:85:80:01:80:73:c8:21:10:0e", NULL, NULL, SC_CARD_TYPE_EOI_CONTACTLESS, 0, NULL }, + { NULL, NULL, NULL, 0, 0, NULL } +}; + +/* + * CAN is stored encrypted in a file that (looks like) is pointed to by 'Card CAN' PIN object. + * eoi_decrypt_can() decrypts CAN from it's encrypted form + */ + +static void rol(u8 *to, const u8 *from) +{ + int i; + u8 b = from[0] & 0x80; + for (i = 15; i >= 0; i--) { + u8 bo = b; + b = from[i] & 0x80; + to[i] = (from[i] << 1) | (bo ? 1 : 0); + if ((i == 15) && bo) + to[i] = (to[i] ^ 0x87) | 1; + } +} + +static int aes256_ecb_encrypt(const u8 *key, const u8 input[AES_BLOCK_SIZE], u8 output[AES_BLOCK_SIZE]) +{ + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + int r = 0, pos, len = pos = AES_BLOCK_SIZE; + if (!ctx) + goto err; + if (!EVP_EncryptInit(ctx, EVP_aes_256_ecb(), key, NULL)) + goto err; + /* Disable padding, otherwise EVP_EncryptFinal() will fail */ + if (!EVP_CIPHER_CTX_set_padding(ctx, 0)) + goto err; + if (!EVP_EncryptUpdate(ctx, output, &pos, input, len)) + goto err; + len -= pos; + if (!EVP_EncryptFinal(ctx, output + pos, &len)) + goto err; + r = 1; +err: + if (ctx) + EVP_CIPHER_CTX_free(ctx); + return r; +} + +static int aes256_ecb_decrypt(const u8 *key, const u8 input[AES_BLOCK_SIZE], u8 output[AES_BLOCK_SIZE]) +{ + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + int r = 0, pos, len = pos = AES_BLOCK_SIZE; + if (!ctx) + goto err; + if (!EVP_DecryptInit(ctx, EVP_aes_256_ecb(), key, NULL)) + goto err; + /* Disable padding, otherwise it will fail to decrypt non-padded inputs */ + if (!EVP_CIPHER_CTX_set_padding(ctx, 0)) + goto err; + if (!EVP_DecryptUpdate(ctx, output, &pos, input, len)) + goto err; + len -= pos; + if (!EVP_DecryptFinal(ctx, output + pos, &len)) + goto err; + r = 1; +err: + if (ctx) + EVP_CIPHER_CTX_free(ctx); + return r; +} + +/* + * CAN decrypt magic... + */ +static int get_can_key(const u8 *key, const u8 round, const u8 *input, u8 *output) +{ + size_t i; + u8 tmp[3][AES_BLOCK_SIZE]; + memset(tmp[0], 0, AES_BLOCK_SIZE); + if (!aes256_ecb_encrypt(key, tmp[0], tmp[0])) + return 0; + rol(tmp[1], tmp[0]); + rol(tmp[0], tmp[1]); + memset(tmp[1], 0, AES_BLOCK_SIZE); + tmp[1][11] = 4; + tmp[1][13] = 1; + tmp[1][15] = round; + if (!aes256_ecb_encrypt(key, tmp[1], tmp[2])) + return 0; + memset(tmp[1], 0, AES_BLOCK_SIZE); + memcpy(tmp[1], &input[AES_BLOCK_SIZE], 8); + tmp[1][8] = 0x80; + for (i = 0; i < AES_BLOCK_SIZE; i++) + tmp[0][i] = tmp[0][i] ^ tmp[1][i] ^ tmp[2][i]; + if (!aes256_ecb_encrypt(key, tmp[0], output)) + return 0; + return 1; +} + +#define AES256_KEY_LEN 32 + +static int eoi_decrypt_can(struct sc_pkcs15_u8 *enc_can, char *can) { + /* Magic key that is used to decrypt CAN */ + const u8 magic_key[AES256_KEY_LEN] = {0xC8, 0x12, 0x0F, 0xD8, 0x21, 0x20, 0x1F, 0x77, 0xF1, 0x83, 0x9D, 0xD8, 0x86, 0xB0, 0x5C, 0xF2, 0x4F, 0x7E, 0x52, 0x66, 0xE5, 0x87, 0x89, 0x2B, 0xF4, 0xC5, 0xE5, 0x4C, 0x54, 0xA1, 0x55, 0x30}; + u8 can_key[AES256_KEY_LEN] = { 0 }; + + if (!can || !enc_can || !enc_can->value || enc_can->len != 24) + return SC_ERROR_INVALID_ARGUMENTS; + + if (!get_can_key(magic_key, 0x01, enc_can->value, &can_key[0])) + return SC_ERROR_INTERNAL; + if (!get_can_key(magic_key, 0x02, enc_can->value, &can_key[AES_BLOCK_SIZE])) + return SC_ERROR_INTERNAL; + + if (!aes256_ecb_decrypt(can_key, enc_can->value, (u8 *)can)) + return SC_ERROR_INTERNAL; + can[AES_BLOCK_SIZE - 1] = 0; + + return SC_SUCCESS; +} + +static int eoi_sm_open(struct sc_card *card) +{ + int r; + struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; + struct establish_pace_channel_input pace_input; + struct establish_pace_channel_output pace_output; + + if (!privdata) + return SC_ERROR_INTERNAL; + + if (!privdata->can[0]) { + /* If no CAN is specified in conf, try to decrypt it from enc_can file */ + r = eoi_decrypt_can(&privdata->enc_can, privdata->can); + sc_log_openssl(card->ctx); + LOG_TEST_RET(card->ctx, r, "Cannot decrypt CAN"); + } + /* CAN should be 6 chars long */ + if (strlen(privdata->can) != 6) + return SC_ERROR_DECRYPT_FAILED; + + memset(&pace_input, 0, sizeof pace_input); + memset(&pace_output, 0, sizeof pace_output); + + pace_input.pin_id = PACE_PIN_ID_CAN; + pace_input.pin = (u8 *)privdata->can; + pace_input.pin_length = strlen(privdata->can); + + /* EF.CardAccess can only be read from MF */ + r = sc_select_file(card, sc_get_mf_path(), NULL); + LOG_TEST_RET(card->ctx, r, "sc_select_file failed"); + + r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02); + LOG_TEST_RET(card->ctx, r, "Error verifying CAN"); + + return SC_SUCCESS; +} + +static int eoi_get_data(sc_card_t *card, u8 data_id, u8 *buf, size_t len) +{ + int r; + sc_apdu_t apdu; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, data_id); + apdu.resp = buf; + apdu.resplen = len; + apdu.le = len; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + return r; +} + +#define ATR_MATCH 1 + +static int eoi_match_card(sc_card_t* card) { + LOG_FUNC_CALLED(card->ctx); + if (_sc_match_atr(card, eoi_atrs, &card->type) >= 0) { + sc_log(card->ctx, "ATR recognized as Slovenian eID card"); + LOG_FUNC_RETURN(card->ctx, ATR_MATCH); + } + LOG_FUNC_RETURN(card->ctx, !ATR_MATCH); +} + +static int eoi_init(sc_card_t* card) { + struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; + u8 version[6]; + size_t i, j; + scconf_block **found_blocks, *block; + int r; + char *can; + + LOG_FUNC_CALLED(card->ctx); + + if (eoi_get_data(card, 0x16, version, sizeof(version)) != SC_SUCCESS) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_CARD); + + if (privdata) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + privdata = sc_mem_secure_alloc(sizeof(struct eoi_privdata)); + if (!privdata) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + /* sc_mem_secure_alloc()-ed memory may not be zeroized */ + memset(privdata, 0, sizeof(struct eoi_privdata)); + card->drv_data = privdata; + + sprintf(privdata->version, "%X%02X.%02X%02X", version[0], version[1], version[2], version[3]); + sc_log(card->ctx, "App version: %s", privdata->version); + + memset(&card->sm_ctx, 0, sizeof card->sm_ctx); + card->sm_ctx.ops.open = eoi_sm_open; + + card->max_send_size = SC_MAX_APDU_DATA_SIZE; + card->max_recv_size = SC_MAX_APDU_RESP_SIZE; + + for (i = 0; i < sizeof eoi_curves / sizeof * eoi_curves; ++i) { + r = _sc_card_add_ec_alg(card, eoi_curves[i].len, SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDSA_HASH_NONE, 0, &eoi_curves[i].oid); + LOG_TEST_GOTO_ERR(card->ctx, r, "Add EC alg failed"); + } + + can = getenv("EOI_CAN"); + if (can) + strlcpy(privdata->can, can, sizeof(privdata->can)); + for (i = 0; card->ctx->conf_blocks[i]; i++) { + found_blocks = scconf_find_blocks(card->ctx->conf, card->ctx->conf_blocks[i], + "card_driver", "eoi"); + if (!found_blocks) + continue; + + for (j = 0, block = found_blocks[j]; block; j++, block = found_blocks[j]) { + if (!privdata->can[0]) { + const char *can = scconf_get_str(block, "can", NULL); + if (can) + strlcpy(privdata->can, can, sizeof(privdata->can)); + } + } + free(found_blocks); + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + +err: + if (privdata) { + sc_mem_clear(privdata, sizeof(struct eoi_privdata)); + sc_mem_secure_free(privdata, sizeof(struct eoi_privdata)); + } + card->drv_data = NULL; + + LOG_FUNC_RETURN(card->ctx, r); +} + +static int eoi_finish(sc_card_t* card) +{ + struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; + + LOG_FUNC_CALLED(card->ctx); + + if (privdata) { + sc_mem_clear(privdata, sizeof(struct eoi_privdata)); + sc_mem_secure_free(privdata, sizeof(struct eoi_privdata)); + } + + card->drv_data = NULL; + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int eoi_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) +{ + struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; + int i; + + LOG_FUNC_CALLED(card->ctx); + + if (!privdata) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + + for (i = 0; i < MAX_OBJECTS && privdata->pin_paths[i]; i++) { + if (privdata->pin_paths[i] && sc_compare_path(privdata->pin_paths[i], in_path)) { + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + } + } + + LOG_FUNC_RETURN(card->ctx, sc_get_iso7816_driver()->ops->select_file(card, in_path, file_out)); +} + +static int eoi_logout(struct sc_card *card) +{ + struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; + struct sc_apdu apdu; + u8 buf[256]; + int r = SC_SUCCESS; + + LOG_FUNC_CALLED(card->ctx); + + if (!privdata) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + + iso_sm_close(card); + card->sm_ctx.sm_mode = SM_MODE_NONE; + + if (card->reader->flags & SC_READER_ENABLE_ESCAPE) { + /* + * Get the UID of the ISO 14443 A card. (see PCSC Part 3) + * The "official" PKCS#11 does it and we do the same. + */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x00, 0x00); + apdu.cla = 0xFF; + apdu.resp = buf; + apdu.resplen = 256; + apdu.lc = 0; + apdu.le = 256; + + r = sc_transmit_apdu(card, &apdu); + } + + LOG_FUNC_RETURN(card->ctx, r); +} + +static int eoi_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data) +{ + int r; + + LOG_FUNC_CALLED(card->ctx); + + if (data->cmd == SC_PIN_CMD_VERIFY && card->sm_ctx.sm_mode == SM_MODE_NONE) { + /* Establish SM before any PIN VERIFY command */ + r = eoi_sm_open(card); + if (r != SC_SUCCESS) + LOG_FUNC_RETURN(card->ctx, r); + } + + if (data->cmd == SC_PIN_CMD_UNBLOCK) { + int pin_reference = data->pin_reference; + size_t pin2_len = data->pin2.len; + /* Verify PUK, establish SM if necessary */ + data->cmd = SC_PIN_CMD_VERIFY; + data->pin_reference = data->puk_reference; + r = eoi_pin_cmd(card, data); + if (r != SC_SUCCESS) + LOG_FUNC_RETURN(card->ctx, r); + /* RESET RETRY COUNTER */ + data->cmd = SC_PIN_CMD_UNBLOCK; + data->pin_reference = 0x80|pin_reference; + data->pin1.len = 0; + data->pin2.len = 0; + r = sc_get_iso7816_driver()->ops->pin_cmd(card, data); + if (r != SC_SUCCESS) + LOG_FUNC_RETURN(card->ctx, r); + /* Continue as CHANGE PIN */ + data->cmd = SC_PIN_CMD_CHANGE; + data->pin2.len = pin2_len; + } + + /* CHANGE PIN command does not send the old PIN as it should already be verified */ + if (data->cmd == SC_PIN_CMD_CHANGE) + data->pin1.len = 0; + + LOG_FUNC_RETURN(card->ctx, sc_get_iso7816_driver()->ops->pin_cmd(card, data)); +} + +static int +eoi_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) +{ + struct sc_card_driver *iso_driver = NULL; + int r = SC_ERROR_NOT_SUPPORTED; + + LOG_FUNC_CALLED(card->ctx); + switch (cmd) { + case SC_CARDCTL_GET_MODEL: + if (!ptr) { + r = SC_ERROR_INVALID_ARGUMENTS; + } else { + *(char **)ptr = eoi_model; + r = SC_SUCCESS; + } + break; + default: + iso_driver = sc_get_iso7816_driver(); + if (iso_driver->ops->card_ctl != NULL) { + r = sc_get_iso7816_driver()->ops->card_ctl(card, cmd, ptr); + } + } + LOG_FUNC_RETURN(card->ctx, r); +} + +#define ALREADY_PROCESSED 0x80000000 + +static int eoi_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num) +{ + struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; + struct sc_apdu apdu; + u8 sbuf[4]; + int i, r, locked = 0; + + LOG_FUNC_CALLED(card->ctx); + + if (!privdata) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + + if (!card || !env) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + /* We don't know yet which hash is used. So just store the security_env data and return */ + if (!(env->algorithm_flags & ALREADY_PROCESSED)) { + privdata->key_len = BYTES4BITS(env->algorithm_ref); + memcpy(&privdata->sec_env, env, sizeof(struct sc_security_env)); + privdata->se_num = se_num; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + } + + if (env->operation != SC_SEC_OPERATION_SIGN) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT)) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + if (env->key_ref_len != 1) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + if (env->algorithm != SC_ALGORITHM_EC) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x81, 0xB6); + sbuf[0] = 0x91; + sbuf[1] = 0x02; + if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA1) + sbuf[2] = 0x11; + else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA256) + sbuf[2] = 0x21; + else if (env->algorithm_flags & (SC_ALGORITHM_ECDSA_RAW|SC_ALGORITHM_ECDSA_HASH_NONE)) + sbuf[2] = 0x22; + else + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + for (i = 0; i < MAX_OBJECTS && privdata->prkey_mappings[i][1]; i++) { + if (privdata->prkey_mappings[i][0] == env->key_ref[0]) + break; + } + if (i == MAX_OBJECTS) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + sbuf[3] = privdata->prkey_mappings[i][1]; + apdu.lc = 4; + apdu.datalen = 4; + apdu.data = sbuf; + if (se_num > 0) { + r = sc_lock(card); + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); + locked = 1; + } + if (apdu.datalen) { + r = sc_transmit_apdu(card, &apdu); + if (r) { + sc_log(card->ctx, "%s: APDU transmit failed", sc_strerror(r)); + goto err; + } + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) { + sc_log(card->ctx, "%s: Card returned error", sc_strerror(r)); + goto err; + } + } + if (se_num <= 0) { + r = SC_SUCCESS; + goto err; + } + sc_unlock(card); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); +err: + if (locked) + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, r); +} + +static int eoi_compute_signature(struct sc_card *card, const u8 * data, size_t data_len, u8 *out, size_t outlen) +{ + struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; + int r; + + LOG_FUNC_CALLED(card->ctx); + + if (!privdata) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + + /* + * Guess the correct mode. If the size is less than the full-key-len, it must be a hash then + */ + if (privdata->key_len != data_len) { + switch (data_len) { + case SHA_DIGEST_LENGTH: + privdata->sec_env.algorithm_flags = SC_ALGORITHM_ECDSA_HASH_SHA1; + break; + case SHA256_DIGEST_LENGTH: + privdata->sec_env.algorithm_flags = SC_ALGORITHM_ECDSA_HASH_SHA256; + break; + } + } + /* Now we know which hash is used */ + privdata->sec_env.algorithm_flags |= ALREADY_PROCESSED; + + /* Perform the true set_security_env */ + r = eoi_set_security_env(card, &privdata->sec_env, privdata->se_num); + LOG_TEST_RET(card->ctx, r, "set_security_env failed"); + + LOG_FUNC_RETURN(card->ctx, sc_get_iso7816_driver()->ops->compute_signature(card, data, data_len, out, outlen)); +} + +struct sc_card_driver *sc_get_eoi_driver(void) +{ + eoi_ops = *sc_get_iso7816_driver()->ops; + + eoi_ops.match_card = eoi_match_card; + eoi_ops.init = eoi_init; + eoi_ops.finish = eoi_finish; + eoi_ops.select_file = eoi_select_file; + eoi_ops.logout = eoi_logout; + eoi_ops.pin_cmd = eoi_pin_cmd; + eoi_ops.card_ctl = eoi_card_ctl; + eoi_ops.set_security_env = eoi_set_security_env; + eoi_ops.compute_signature = eoi_compute_signature; + + return &eoi_drv; +} + +#else + +struct sc_card_driver* sc_get_eoi_driver(void) { + return NULL; +} + +#endif diff --git a/src/libopensc/card-eoi.h b/src/libopensc/card-eoi.h new file mode 100644 index 0000000000..16abdf0d91 --- /dev/null +++ b/src/libopensc/card-eoi.h @@ -0,0 +1,41 @@ +/* + * Support for the eOI card + * + * Copyright (C) 2022 Luka Logar + * + * 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 + */ + +#include +#include "pkcs15.h" + +#define MAX_OBJECTS 8 + +struct eoi_privdata { + /* App version */ + char version[10]; + /* Serial + encrypted CAN */ + struct sc_pkcs15_u8 enc_can; + /* CAN from the conf file */ + char can[AES_BLOCK_SIZE]; + /* Cached data for signing operation */ + size_t key_len; + struct sc_security_env sec_env; + int se_num; + /* PINs that shouldn't report an error when selected */ + struct sc_path *pin_paths[MAX_OBJECTS]; + /* PrKey reference to eOI mappings */ + int prkey_mappings[MAX_OBJECTS][2]; +}; diff --git a/src/libopensc/card-epass2003.c b/src/libopensc/card-epass2003.c index 84ab645692..52f9c30772 100644 --- a/src/libopensc/card-epass2003.c +++ b/src/libopensc/card-epass2003.c @@ -16,10 +16,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef ENABLE_SM /* empty file without SM enabled */ @@ -39,17 +39,30 @@ #include #include -#include +#include +#include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" -static struct sc_atr_table epass2003_atrs[] = { +/* + * See https://github.com/OpenSC/OpenSC/issues/2572 + * 2012 ATR: note version 01:00:11 + * 3b:9f:95:81:31:fe:9f:00:66:46:53:05:01:00:11:71:df:00:00:03:90:00:80 + * 2022 ATRs: note version 23:00:25 + * OpenSC-initialized ATR: + * 3b 9f:95:81:31:fe:9f:00:66:46:53:05:23:00:25:71:df:00:00:03:90:00:96 + * Feitian-initalized ATR: + * 3b:9f:95:81:31:fe:9f:00:66:46:53:05:23:00:25:71:df:00:00:00:00:00:05 + */ + +static const struct sc_atr_table epass2003_atrs[] = { /* This is a FIPS certified card using SCP01 security messaging. */ - {"3B:9F:95:81:31:FE:9F:00:66:46:53:05:10:00:11:71:df:00:00:00:6a:82:5e", - "FF:FF:FF:FF:FF:00:FF:FF:FF:FF:FF:FF:00:00:00:ff:00:ff:ff:00:00:00:00", + /* will match all the above */ + {"3B:9F:95:81:31:FE:9F:00:66:46:53:05:00:00:00:71:df:00:00:00:00:00:00", + "FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:FF:FF:FF:FF:00:00:00:00", "FTCOS/ePass2003", SC_CARD_TYPE_ENTERSAFE_FTCOS_EPASS2003, 0, NULL }, {NULL, NULL, NULL, 0, 0, NULL} }; @@ -88,9 +101,7 @@ static unsigned char g_init_key_mac[16] = { 0x0D, 0x0E, 0x0F, 0x10 }; -static unsigned char g_random[8] = { - 0xBF, 0xC3, 0x29, 0x11, 0xC7, 0x18, 0xC3, 0x40 -}; +static unsigned char g_random[8]; typedef struct epass2003_exdata_st { unsigned char sm; /* SM_PLAIN or SM_SCP01 */ @@ -98,6 +109,9 @@ typedef struct epass2003_exdata_st { unsigned char sk_enc[16]; /* encrypt session key */ unsigned char sk_mac[16]; /* mac session key */ unsigned char icv_mac[16]; /* instruction counter vector(for sm) */ + unsigned char bFipsCertification; /* fips mode Alg */ + unsigned char currAlg; /* current Alg */ + unsigned int ecAlgFlags; /* Ec Alg mechanism type*/ } epass2003_exdata; #define REVERSE_ORDER4(x) ( \ @@ -114,17 +128,17 @@ static const struct sc_card_error epass2003_errors[] = { { 0x6283, SC_ERROR_CARD_CMD_FAILED, "Selected file invalidated" }, { 0x6284, SC_ERROR_CARD_CMD_FAILED, "FCI not formatted according to ISO 7816-4" }, - { 0x6300, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63C1, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed. One tries left"}, - { 0x63C2, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed. Two tries left"}, - { 0x63C3, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63C4, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63C5, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63C6, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63C7, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63C8, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63C9, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63CA, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x6300, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63C1, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed. One tries left"}, + { 0x63C2, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed. Two tries left"}, + { 0x63C3, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63C4, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63C5, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63C6, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63C7, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63C8, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63C9, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63CA, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, { 0x6381, SC_ERROR_CARD_CMD_FAILED, "Warning: file filled up by last write" }, @@ -167,9 +181,64 @@ static const struct sc_card_error epass2003_errors[] = { { 0x9000,SC_SUCCESS, NULL } }; +typedef struct sec_attr_to_acl_entries { + unsigned int file_type; /* file->type */ + unsigned int file_ef_structure; /* file->ef_structure */ + int index; /* index in epass2003 iversion of sec_attr */ + /* use the follow for sc_file_add_entry */ + int op; /* SC_AC_OP_* */ +} sec_attr_to_acl_entries_t; + +// clang-format off +/* Known combinations of file type and methods. More can be added as needed */ +static const sec_attr_to_acl_entries_t sec_attr_to_acl_entry[] = { + {SC_FILE_TYPE_DF, 0, 0, SC_AC_OP_LIST_FILES}, + {SC_FILE_TYPE_DF, 0, 1, SC_AC_OP_CREATE}, + {SC_FILE_TYPE_DF, 0, 3, SC_AC_OP_DELETE}, + + {SC_FILE_TYPE_WORKING_EF, SC_FILE_EF_TRANSPARENT, 0, SC_AC_OP_READ}, + {SC_FILE_TYPE_WORKING_EF, SC_FILE_EF_TRANSPARENT, 1, SC_AC_OP_UPDATE}, + {SC_FILE_TYPE_WORKING_EF, SC_FILE_EF_TRANSPARENT, 3, SC_AC_OP_DELETE}, + + {SC_FILE_TYPE_WORKING_EF, SC_FILE_EF_TRANSPARENT, 0, SC_AC_OP_READ}, + {SC_FILE_TYPE_WORKING_EF, SC_FILE_EF_TRANSPARENT, 1, SC_AC_OP_UPDATE}, + {SC_FILE_TYPE_WORKING_EF, SC_FILE_EF_TRANSPARENT, 3, SC_AC_OP_DELETE}, + + {SC_FILE_TYPE_WORKING_EF, SC_FILE_EF_LINEAR_FIXED, 0, SC_AC_OP_READ}, + {SC_FILE_TYPE_WORKING_EF, SC_FILE_EF_LINEAR_FIXED, 1, SC_AC_OP_UPDATE}, + {SC_FILE_TYPE_WORKING_EF, SC_FILE_EF_LINEAR_FIXED, 2, SC_AC_OP_WRITE}, + {SC_FILE_TYPE_WORKING_EF, SC_FILE_EF_LINEAR_FIXED, 3, SC_AC_OP_DELETE}, + + {SC_FILE_TYPE_WORKING_EF, SC_FILE_EF_LINEAR_VARIABLE, 0, SC_AC_OP_READ}, + {SC_FILE_TYPE_WORKING_EF, SC_FILE_EF_LINEAR_VARIABLE, 1, SC_AC_OP_UPDATE}, + {SC_FILE_TYPE_WORKING_EF, SC_FILE_EF_LINEAR_VARIABLE, 2, SC_AC_OP_WRITE}, + {SC_FILE_TYPE_WORKING_EF, SC_FILE_EF_LINEAR_VARIABLE, 3, SC_AC_OP_DELETE}, + + {SC_FILE_TYPE_BSO, 0, 0, SC_AC_OP_UPDATE}, + {SC_FILE_TYPE_BSO, 0, 3, SC_AC_OP_DELETE}, + + {SC_FILE_TYPE_INTERNAL_EF, SC_CARDCTL_OBERTHUR_KEY_RSA_CRT, 1, SC_AC_OP_UPDATE}, + {SC_FILE_TYPE_INTERNAL_EF, SC_CARDCTL_OBERTHUR_KEY_EC_CRT, 1, SC_AC_OP_UPDATE}, + {SC_FILE_TYPE_INTERNAL_EF, SC_CARDCTL_OBERTHUR_KEY_RSA_CRT, 2, SC_AC_OP_CRYPTO}, + {SC_FILE_TYPE_INTERNAL_EF, SC_CARDCTL_OBERTHUR_KEY_EC_CRT, 2, SC_AC_OP_CRYPTO}, + {SC_FILE_TYPE_INTERNAL_EF, SC_CARDCTL_OBERTHUR_KEY_RSA_CRT, 3, SC_AC_OP_DELETE}, + {SC_FILE_TYPE_INTERNAL_EF, SC_CARDCTL_OBERTHUR_KEY_EC_CRT, 3, SC_AC_OP_DELETE}, + + {SC_FILE_TYPE_INTERNAL_EF, SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC, 0, SC_AC_OP_READ}, + {SC_FILE_TYPE_INTERNAL_EF, SC_CARDCTL_OBERTHUR_KEY_EC_PUBLIC, 0, SC_AC_OP_READ}, + {SC_FILE_TYPE_INTERNAL_EF, SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC, 1, SC_AC_OP_UPDATE}, + {SC_FILE_TYPE_INTERNAL_EF, SC_CARDCTL_OBERTHUR_KEY_EC_PUBLIC, 1, SC_AC_OP_UPDATE}, + {SC_FILE_TYPE_INTERNAL_EF, SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC, 2, SC_AC_OP_CRYPTO}, + {SC_FILE_TYPE_INTERNAL_EF, SC_CARDCTL_OBERTHUR_KEY_EC_PUBLIC, 2, SC_AC_OP_CRYPTO}, + {SC_FILE_TYPE_INTERNAL_EF, SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC, 3, SC_AC_OP_DELETE}, + {SC_FILE_TYPE_INTERNAL_EF, SC_CARDCTL_OBERTHUR_KEY_EC_PUBLIC, 3, SC_AC_OP_DELETE}, +}; +// clang-format on + static int epass2003_transmit_apdu(struct sc_card *card, struct sc_apdu *apdu); static int epass2003_select_file(struct sc_card *card, const sc_path_t * in_path, sc_file_t ** file_out); int epass2003_refresh(struct sc_card *card); +static int hash_data(struct sc_card *card, const unsigned char *data, size_t datalen, unsigned char *hash, unsigned int mechanismType); static int epass2003_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) @@ -183,8 +252,7 @@ epass2003_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) return SC_ERROR_WRONG_LENGTH; } - - for (i = 0; i < err_count; i++) { + for (i = 0; i < err_count; i++) { if (epass2003_errors[i].SWs == ((sw1 << 8) | sw2)) { sc_log(card->ctx, "%s", epass2003_errors[i].errorstr); return epass2003_errors[i].errorno; @@ -198,10 +266,12 @@ epass2003_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) static int sc_transmit_apdu_t(sc_card_t *card, sc_apdu_t *apdu) { + size_t resplen = apdu->resplen; int r = sc_transmit_apdu(card, apdu); - if ( ((0x69 == apdu->sw1) && (0x85 == apdu->sw2)) || ((0x69 == apdu->sw1) && (0x88 == apdu->sw2))) - { + if ((0x69 == apdu->sw1 && 0x85 == apdu->sw2) || (0x69 == apdu->sw1 && 0x88 == apdu->sw2)) { epass2003_refresh(card); + /* renew old resplen */ + apdu->resplen = resplen; r = sc_transmit_apdu(card, apdu); } return r; @@ -215,16 +285,17 @@ openssl_enc(const EVP_CIPHER * cipher, const unsigned char *key, const unsigned EVP_CIPHER_CTX * ctx = NULL; int outl = 0; int outl_tmp = 0; - unsigned char iv_tmp[EVP_MAX_IV_LENGTH] = { 0 }; + unsigned char iv_tmp[EVP_MAX_IV_LENGTH] = {0}; memcpy(iv_tmp, iv, EVP_MAX_IV_LENGTH); ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) goto out; - EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv_tmp); - EVP_CIPHER_CTX_set_padding(ctx, 0); - if (!EVP_EncryptUpdate(ctx, output, &outl, input, length)) + if (!EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv_tmp) || !EVP_CIPHER_CTX_set_padding(ctx, 0)) + goto out; + + if (!EVP_EncryptUpdate(ctx, output, &outl, input, (int)length)) goto out; if (!EVP_EncryptFinal_ex(ctx, output + outl, &outl_tmp)) @@ -232,8 +303,7 @@ openssl_enc(const EVP_CIPHER * cipher, const unsigned char *key, const unsigned r = SC_SUCCESS; out: - if (ctx) - EVP_CIPHER_CTX_free(ctx); + EVP_CIPHER_CTX_free(ctx); return r; } @@ -245,16 +315,18 @@ openssl_dec(const EVP_CIPHER * cipher, const unsigned char *key, const unsigned EVP_CIPHER_CTX * ctx = NULL; int outl = 0; int outl_tmp = 0; - unsigned char iv_tmp[EVP_MAX_IV_LENGTH] = { 0 }; + unsigned char iv_tmp[EVP_MAX_IV_LENGTH] = {0}; memcpy(iv_tmp, iv, EVP_MAX_IV_LENGTH); ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) goto out; - EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv_tmp); - EVP_CIPHER_CTX_set_padding(ctx, 0); - if (!EVP_DecryptUpdate(ctx, output, &outl, input, length)) + if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv_tmp) || + !EVP_CIPHER_CTX_set_padding(ctx, 0)) + goto out; + + if (!EVP_DecryptUpdate(ctx, output, &outl, input, (int)length)) goto out; if (!EVP_DecryptFinal_ex(ctx, output + outl, &outl_tmp)) @@ -262,104 +334,281 @@ openssl_dec(const EVP_CIPHER * cipher, const unsigned char *key, const unsigned r = SC_SUCCESS; out: - if (ctx) - EVP_CIPHER_CTX_free(ctx); + EVP_CIPHER_CTX_free(ctx); return r; } +static int +aes128_encrypt_cmac_ft(struct sc_card *card, const unsigned char *key, int keysize, + const unsigned char *input, size_t length, unsigned char *output,unsigned char *iv) +{ + unsigned char data1[32] = {0}; + unsigned char data2[32] = {0}; + unsigned char k1Bin[32] = {0}; + unsigned char k2Bin[32] = {0}; + + unsigned char check = 0; + BIGNUM *enc1,*lenc1; + BIGNUM *enc2,*lenc2; + + // k1 + int offset = 0; + int r = SC_ERROR_INTERNAL; + unsigned char out[32] = {0}; + unsigned char iv0[EVP_MAX_IV_LENGTH] = {0}; + EVP_CIPHER *alg = sc_evp_cipher(card->ctx, "AES-128-ECB"); + r = openssl_enc(alg, key, iv0, data1, 16, out); + if (r != SC_SUCCESS) { + sc_log_openssl(card->ctx); + sc_evp_cipher_free(alg); + return r; + } + + check = out[0]; + enc1 = BN_new(); + lenc1 = BN_new(); + BN_bin2bn(out,16,enc1); + BN_lshift1(lenc1,enc1); + BN_bn2bin(lenc1,k1Bin); + if (check & 0x80) { + offset = 1; + k1Bin[15+offset] ^= 0x87; + } + BN_free(enc1); + BN_free(lenc1); + + // k2 + enc2 = BN_new(); + lenc2 = BN_new(); + check = k1Bin[offset]; + BN_bin2bn(&k1Bin[offset],16,enc2); + + offset = 0; + BN_lshift1(lenc2,enc2); + BN_bn2bin(lenc2,k2Bin); + if (check & 0x80) { + offset = 1; + k2Bin[15+offset] ^= 0x87; + } + BN_free(enc2); + BN_free(lenc2); + // padding + if (length < 16) { + memcpy(&data2[0],input,length); + data2[length] = 0x80; + } + + // k2 xor padded data + for (int i = 0; i < 16; i++) { + data2[i] = data2[i] ^ k2Bin[offset + i]; + } + r = openssl_enc(alg, key, iv, data2, 16, output); + sc_evp_cipher_free(alg); + if (r != SC_SUCCESS) + sc_log_openssl(card->ctx); + return r; +} static int -aes128_encrypt_ecb(const unsigned char *key, int keysize, +aes128_encrypt_cmac(struct sc_card *card, const unsigned char *key, int keysize, + const unsigned char *input, size_t length, unsigned char *output) +{ + size_t mactlen = 0; + int r = SC_ERROR_INTERNAL; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + CMAC_CTX *ctx = CMAC_CTX_new(); + if (ctx == NULL) { + return SC_ERROR_INTERNAL; + } + + if (!CMAC_Init(ctx, key, keysize / 8, EVP_aes_128_cbc(), NULL)) { + goto err; + } + if (!CMAC_Update(ctx, input, length)) { + goto err; + } + if (!CMAC_Final(ctx, output, &mactlen)) { + goto err; + } + r = SC_SUCCESS; +err: + CMAC_CTX_free(ctx); +#else + EVP_MAC *mac = EVP_MAC_fetch(card->ctx->ossl3ctx->libctx, "cmac", NULL); + if (mac == NULL) { + return r; + } + + OSSL_PARAM params[2] = {0}; + params[0] = OSSL_PARAM_construct_utf8_string("cipher","aes-128-cbc", 0); + params[1] = OSSL_PARAM_construct_end(); + + EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(mac); + if (ctx == NULL) { + EVP_MAC_CTX_free(ctx); + sc_log_openssl(card->ctx); + return r; + } + if (!EVP_MAC_init(ctx, (const unsigned char *)key, keysize / 8, params)) { + sc_log_openssl(card->ctx); + goto err; + } + if (!EVP_MAC_update(ctx, input, length)) { + sc_log_openssl(card->ctx); + goto err; + } + if (!EVP_MAC_final(ctx, output, &mactlen, 16)) { + sc_log_openssl(card->ctx); + goto err; + } + r = SC_SUCCESS; +err: + EVP_MAC_CTX_free(ctx); + EVP_MAC_free(mac); +#endif + return r; +} + +static int +aes128_encrypt_ecb(struct sc_card *card, const unsigned char *key, int keysize, const unsigned char *input, size_t length, unsigned char *output) { - unsigned char iv[EVP_MAX_IV_LENGTH] = { 0 }; - return openssl_enc(EVP_aes_128_ecb(), key, iv, input, length, output); + unsigned char iv[EVP_MAX_IV_LENGTH] = {0}; + EVP_CIPHER *alg = sc_evp_cipher(card->ctx, "AES-128-ECB"); + int r; + r = openssl_enc(alg, key, iv, input, length, output); + sc_evp_cipher_free(alg); + if (r != SC_SUCCESS) + sc_log_openssl(card->ctx); + return r; } static int -aes128_encrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[16], +aes128_encrypt_cbc(struct sc_card *card, const unsigned char *key, int keysize, unsigned char iv[16], const unsigned char *input, size_t length, unsigned char *output) { - return openssl_enc(EVP_aes_128_cbc(), key, iv, input, length, output); + EVP_CIPHER *alg = sc_evp_cipher(card->ctx, "AES-128-CBC"); + int r; + r = openssl_enc(alg, key, iv, input, length, output); + sc_evp_cipher_free(alg); + if (r != SC_SUCCESS) + sc_log_openssl(card->ctx); + return r; } static int -aes128_decrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[16], +aes128_decrypt_cbc(struct sc_card *card, const unsigned char *key, int keysize, unsigned char iv[16], const unsigned char *input, size_t length, unsigned char *output) { - return openssl_dec(EVP_aes_128_cbc(), key, iv, input, length, output); + EVP_CIPHER *alg = sc_evp_cipher(card->ctx, "AES-128-CBC"); + int r; + r = openssl_dec(alg, key, iv, input, length, output); + sc_evp_cipher_free(alg); + if (r != SC_SUCCESS) + sc_log_openssl(card->ctx); + return r; } static int -des3_encrypt_ecb(const unsigned char *key, int keysize, +des3_encrypt_ecb(struct sc_card *card, const unsigned char *key, int keysize, const unsigned char *input, int length, unsigned char *output) { - unsigned char iv[EVP_MAX_IV_LENGTH] = { 0 }; - unsigned char bKey[24] = { 0 }; + unsigned char iv[EVP_MAX_IV_LENGTH] = {0}; + unsigned char bKey[24] = {0}; + EVP_CIPHER *alg = sc_evp_cipher(card->ctx, "DES-EDE3"); + int r; if (keysize == 16) { memcpy(&bKey[0], key, 16); memcpy(&bKey[16], key, 8); - } - else { + } else { memcpy(&bKey[0], key, 24); } - return openssl_enc(EVP_des_ede3(), bKey, iv, input, length, output); + r = openssl_enc(alg, bKey, iv, input, length, output); + sc_evp_cipher_free(alg); + if (r != SC_SUCCESS) + sc_log_openssl(card->ctx); + return r; } static int -des3_encrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[EVP_MAX_IV_LENGTH], +des3_encrypt_cbc(struct sc_card *card, const unsigned char *key, int keysize, unsigned char iv[EVP_MAX_IV_LENGTH], const unsigned char *input, size_t length, unsigned char *output) { - unsigned char bKey[24] = { 0 }; + unsigned char bKey[24] = {0}; + EVP_CIPHER *alg = sc_evp_cipher(card->ctx, "DES-EDE3-CBC"); + int r; if (keysize == 16) { memcpy(&bKey[0], key, 16); memcpy(&bKey[16], key, 8); - } - else { + } else { memcpy(&bKey[0], key, 24); } - return openssl_enc(EVP_des_ede3_cbc(), bKey, iv, input, length, output); + r = openssl_enc(EVP_des_ede3_cbc(), bKey, iv, input, length, output); + sc_evp_cipher_free(alg); + if (r != SC_SUCCESS) + sc_log_openssl(card->ctx); + return r; } static int -des3_decrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[EVP_MAX_IV_LENGTH], +des3_decrypt_cbc(struct sc_card *card, const unsigned char *key, int keysize, unsigned char iv[EVP_MAX_IV_LENGTH], const unsigned char *input, size_t length, unsigned char *output) { - unsigned char bKey[24] = { 0 }; + unsigned char bKey[24] = {0}; + EVP_CIPHER *alg = sc_evp_cipher(card->ctx, "DES-EDE3-CBC"); + int r; + if (keysize == 16) { memcpy(&bKey[0], key, 16); memcpy(&bKey[16], key, 8); - } - else { + } else { memcpy(&bKey[0], key, 24); } - return openssl_dec(EVP_des_ede3_cbc(), bKey, iv, input, length, output); + r = openssl_dec(alg, bKey, iv, input, length, output); + sc_evp_cipher_free(alg); + if (r != SC_SUCCESS) + sc_log_openssl(card->ctx); + return r; } static int -des_encrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[EVP_MAX_IV_LENGTH], +des_encrypt_cbc(struct sc_card *card, const unsigned char *key, int keysize, unsigned char iv[EVP_MAX_IV_LENGTH], const unsigned char *input, size_t length, unsigned char *output) { - return openssl_enc(EVP_des_cbc(), key, iv, input, length, output); + EVP_CIPHER *alg = sc_evp_cipher(card->ctx, "DES-CBC"); + int r; + + r = openssl_enc(alg, key, iv, input, length, output); + sc_evp_cipher_free(alg); + if (r != SC_SUCCESS) + sc_log_openssl(card->ctx); + return r; } static int -des_decrypt_cbc(const unsigned char *key, int keysize, unsigned char iv[EVP_MAX_IV_LENGTH], +des_decrypt_cbc(struct sc_card *card, const unsigned char *key, int keysize, unsigned char iv[EVP_MAX_IV_LENGTH], const unsigned char *input, size_t length, unsigned char *output) { - return openssl_dec(EVP_des_cbc(), key, iv, input, length, output); + EVP_CIPHER *alg = sc_evp_cipher(card->ctx, "DES-CBC"); + int r; + + r = openssl_dec(alg, key, iv, input, length, output); + sc_evp_cipher_free(alg); + if (r != SC_SUCCESS) + sc_log_openssl(card->ctx); + return r; } @@ -373,13 +622,13 @@ openssl_dig(const EVP_MD * digest, const unsigned char *input, size_t length, ctx = EVP_MD_CTX_create(); if (ctx == NULL) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; + r = SC_ERROR_OUT_OF_MEMORY; + goto err; } - + EVP_MD_CTX_init(ctx); - EVP_DigestInit_ex(ctx, digest, NULL); - if (!EVP_DigestUpdate(ctx, input, length)) { + if (!EVP_DigestInit_ex(ctx, digest, NULL) || + !EVP_DigestUpdate(ctx, input, length)) { r = SC_ERROR_INTERNAL; goto err; } @@ -398,9 +647,29 @@ openssl_dig(const EVP_MD * digest, const unsigned char *input, size_t length, static int -sha1_digest(const unsigned char *input, size_t length, unsigned char *output) +sha1_digest(struct sc_card *card, const unsigned char *input, size_t length, unsigned char *output) +{ + EVP_MD *md = sc_evp_md(card->ctx, "SHA1"); + int r; + + r = openssl_dig(md, input, length, output); + sc_evp_md_free(md); + if (r != SC_SUCCESS) + sc_log_openssl(card->ctx); + return r; +} + +static int +sha256_digest(struct sc_card *card, const unsigned char *input, size_t length, unsigned char *output) { - return openssl_dig(EVP_sha1(), input, length, output); + EVP_MD *md = sc_evp_md(card->ctx, "SHA256"); + int r; + + r = openssl_dig(md, input, length, output); + sc_evp_md_free(md); + if (r != SC_SUCCESS) + sc_log_openssl(card->ctx); + return r; } @@ -410,25 +679,34 @@ gen_init_key(struct sc_card *card, unsigned char *key_enc, unsigned char *key_ma { int r; struct sc_apdu apdu; - unsigned char data[256] = { 0 }; + unsigned char data[256] = {0}; unsigned char tmp_sm; + unsigned char isFips; unsigned long blocksize = 0; - unsigned char cryptogram[256] = { 0 }; /* host cryptogram */ - unsigned char iv[16] = { 0 }; + unsigned char cryptogram[256] = {0}; /* host cryptogram */ + unsigned char iv[16] = {0}; epass2003_exdata *exdata = NULL; - - if (!card->drv_data) + + if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; - + exdata = (epass2003_exdata *)card->drv_data; + isFips = exdata->bFipsCertification; LOG_FUNC_CALLED(card->ctx); + if (1 != RAND_bytes(g_random, sizeof(g_random))) + return SC_ERROR_INTERNAL; + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x50, 0x00, 0x00); apdu.cla = 0x80; apdu.lc = apdu.datalen = sizeof(g_random); apdu.data = g_random; /* host random */ - apdu.le = apdu.resplen = 28; + if (isFips) + apdu.le = apdu.resplen = 29; + else + apdu.le = apdu.resplen = 28; + apdu.resp = result; /* card random is result[12~19] */ tmp_sm = exdata->sm; @@ -441,37 +719,73 @@ gen_init_key(struct sc_card *card, unsigned char *key_enc, unsigned char *key_ma LOG_TEST_RET(card->ctx, r, "gen_init_key failed"); /* Step 1 - Generate Derivation data */ - memcpy(data, &result[16], 4); - memcpy(&data[4], g_random, 4); - memcpy(&data[8], &result[12], 4); - memcpy(&data[12], &g_random[4], 4); + if (isFips) { + memset(data, 0x00, 15); + data[11] = 0x04; + data[14] = 0x80; + data[15] = 0x01; + memcpy(&data[16], g_random, 8); + memcpy(&data[24], &result[12 + 1], 8); + } else { + memcpy(data, &result[16], 4); + memcpy(&data[4], g_random, 4); + memcpy(&data[8], &result[12], 4); + memcpy(&data[12], &g_random[4], 4); + } /* Step 2,3 - Create S-ENC/S-MAC Session Key */ if (KEY_TYPE_AES == key_type) { - aes128_encrypt_ecb(key_enc, 16, data, 16, exdata->sk_enc); - aes128_encrypt_ecb(key_mac, 16, data, 16, exdata->sk_mac); - } - else { - des3_encrypt_ecb(key_enc, 16, data, 16, exdata->sk_enc); - des3_encrypt_ecb(key_mac, 16, data, 16, exdata->sk_mac); + if (isFips) { + r = aes128_encrypt_cmac(card, key_enc, 128, data, 32, exdata->sk_enc); + LOG_TEST_RET(card->ctx, r, "aes128_encrypt_cmac enc failed"); + memset(&data[11], 0x06, 1); + r = aes128_encrypt_cmac(card, key_mac, 128, data, 32, exdata->sk_mac); + LOG_TEST_RET(card->ctx, r, "aes128_encrypt_cmac mac failed"); + } else { + r = aes128_encrypt_ecb(card, key_enc, 16, data, 16, exdata->sk_enc); + LOG_TEST_RET(card->ctx, r, "aes128_encrypt_ecb enc failed"); + r = aes128_encrypt_ecb(card, key_mac, 16, data, 16, exdata->sk_mac); + LOG_TEST_RET(card->ctx, r, "aes128_encrypt_ecb mac failed"); + } + } else { + r = des3_encrypt_ecb(card, key_enc, 16, data, 16, exdata->sk_enc); + LOG_TEST_RET(card->ctx, r, "des3_encrypt_ecb failed"); + r = des3_encrypt_ecb(card, key_mac, 16, data, 16, exdata->sk_mac); + LOG_TEST_RET(card->ctx, r, "des3_encrypt_ecb failed"); } - memcpy(data, g_random, 8); - memcpy(&data[8], &result[12], 8); - data[16] = 0x80; - blocksize = (key_type == KEY_TYPE_AES ? 16 : 8); - memset(&data[17], 0x00, blocksize - 1); + if (isFips) { + data[11] = 0x00; + data[14] = 0x40; + } else { + memcpy(data, g_random, 8); + memcpy(&data[8], &result[12], 8); + data[16] = 0x80; + blocksize = (key_type == KEY_TYPE_AES ? 16 : 8); + memset(&data[17], 0x00, blocksize - 1); + } /* calculate host cryptogram */ - if (KEY_TYPE_AES == key_type) - aes128_encrypt_cbc(exdata->sk_enc, 16, iv, data, 16 + blocksize, cryptogram); - else - des3_encrypt_cbc(exdata->sk_enc, 16, iv, data, 16 + blocksize, cryptogram); + if (KEY_TYPE_AES == key_type) { + if (isFips) { + r = aes128_encrypt_cmac(card, exdata->sk_enc, 128, data, 32, cryptogram); + } else { + r = aes128_encrypt_cbc(card, exdata->sk_enc, 16, iv, data, 16 + blocksize, cryptogram); + } + } else { + r = des3_encrypt_cbc(card, exdata->sk_enc, 16, iv, data, 16 + blocksize, cryptogram); + } - /* verify card cryptogram */ - if (0 != memcmp(&cryptogram[16], &result[20], 8)) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); + LOG_TEST_RET(card->ctx, r, "calculate host cryptogram failed"); + /* verify card cryptogram */ + if (isFips) { + if (0 != memcmp(&cryptogram[0], &result[20+1], 8)) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); + } else { + if (0 != memcmp(&cryptogram[16], &result[20], 8)) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); + } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -482,55 +796,84 @@ verify_init_key(struct sc_card *card, unsigned char *ran_key, unsigned char key_ int r; struct sc_apdu apdu; unsigned long blocksize = (key_type == KEY_TYPE_AES ? 16 : 8); - unsigned char data[256] = { 0 }; - unsigned char cryptogram[256] = { 0 }; /* host cryptogram */ - unsigned char iv[16] = { 0 }; - unsigned char mac[256] = { 0 }; + unsigned char data[256] = {0}; + unsigned char cryptogram[256] = {0}; /* host cryptogram */ + unsigned char iv[16] = {0}; + unsigned char mac[256] = {0}; unsigned long i; unsigned char tmp_sm; + unsigned char isFips; epass2003_exdata *exdata = NULL; - if (!card->drv_data) + if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; + isFips = exdata->bFipsCertification; LOG_FUNC_CALLED(card->ctx); - memcpy(data, ran_key, 8); - memcpy(&data[8], g_random, 8); - data[16] = 0x80; - memset(&data[17], 0x00, blocksize - 1); - memset(iv, 0, 16); + if (isFips) { + memset(data,0x00,15); + data[11] = 0x01; + data[14] = 0x40; + data[15] = 0x01; + memcpy(&data[16], g_random, 8); + memcpy(&data[24], ran_key, 8); + } else { + memcpy(data, ran_key, 8); + memcpy(&data[8], g_random, 8); + data[16] = 0x80; + memset(&data[17], 0x00, blocksize - 1); + memset(iv, 0, 16); + } /* calculate host cryptogram */ if (KEY_TYPE_AES == key_type) { - aes128_encrypt_cbc(exdata->sk_enc, 16, iv, data, 16 + blocksize, - cryptogram); + if (isFips) { + r = aes128_encrypt_cmac(card, exdata->sk_enc, 128, data, 32, cryptogram); + } else { + r = aes128_encrypt_cbc(card, exdata->sk_enc, 16, iv, data, 16 + blocksize,cryptogram); + } } else { - des3_encrypt_cbc(exdata->sk_enc, 16, iv, data, 16 + blocksize, - cryptogram); + r = des3_encrypt_cbc(card, exdata->sk_enc, 16, iv, data, 16 + blocksize,cryptogram); } + LOG_TEST_RET(card->ctx, r, "calculate host cryptogram failed"); + memset(data, 0, sizeof(data)); memcpy(data, "\x84\x82\x03\x00\x10", 5); - memcpy(&data[5], &cryptogram[16], 8); - memcpy(&data[13], "\x80\x00\x00", 3); + if (isFips) { + memcpy(&data[5], &cryptogram[0], 8); + } else { + memcpy(&data[5], &cryptogram[16], 8); + memcpy(&data[13], "\x80\x00\x00", 3); + } /* calculate mac icv */ memset(iv, 0x00, 16); if (KEY_TYPE_AES == key_type) { - aes128_encrypt_cbc(exdata->sk_mac, 16, iv, data, 16, mac); + if (isFips) { + r = aes128_encrypt_cmac(card, exdata->sk_mac, 128, data, 13, mac); + } else { + r = aes128_encrypt_cbc(card, exdata->sk_mac, 16, iv, data, 16, mac); + } i = 0; } else { - des3_encrypt_cbc(exdata->sk_mac, 16, iv, data, 16, mac); + r = des3_encrypt_cbc(card, exdata->sk_mac, 16, iv, data, 16, mac); i = 8; } + + LOG_TEST_RET(card->ctx, r, "calculate mac icv failed"); /* save mac icv */ memset(exdata->icv_mac, 0x00, 16); memcpy(exdata->icv_mac, &mac[i], 8); /* verify host cryptogram */ - memcpy(data, &cryptogram[16], 8); + if (isFips) { + memcpy(data, &cryptogram[0], 8); + } else { + memcpy(data, &cryptogram[16], 8); + } memcpy(&data[8], &mac[i], 8); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x82, 0x03, 0x00); apdu.cla = 0x84; @@ -540,11 +883,9 @@ verify_init_key(struct sc_card *card, unsigned char *ran_key, unsigned char key_ exdata->sm = SM_PLAIN; r = epass2003_transmit_apdu(card, &apdu); exdata->sm = tmp_sm; - LOG_TEST_RET(card->ctx, r, - "APDU verify_init_key failed"); + LOG_TEST_RET(card->ctx, r, "APDU verify_init_key failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(card->ctx, r, - "verify_init_key failed"); + LOG_TEST_RET(card->ctx, r, "verify_init_key failed"); return r; } @@ -555,11 +896,11 @@ mutual_auth(struct sc_card *card, unsigned char *key_enc, { struct sc_context *ctx = card->ctx; int r; - unsigned char result[256] = { 0 }; - unsigned char ran_key[8] = { 0 }; + unsigned char result[256] = {0}; + unsigned char ran_key[8] = {0}; epass2003_exdata *exdata = NULL; - if (!card->drv_data) + if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; @@ -567,7 +908,11 @@ mutual_auth(struct sc_card *card, unsigned char *key_enc, r = gen_init_key(card, key_enc, key_mac, result, exdata->smtype); LOG_TEST_RET(ctx, r, "gen_init_key failed"); - memcpy(ran_key, &result[12], 8); + if (exdata->bFipsCertification) { + memcpy(ran_key, &result[12+1], 8); + } else { + memcpy(ran_key, &result[12], 8); + } r = verify_init_key(card, ran_key, exdata->smtype); LOG_TEST_RET(ctx, r, "verify_init_key failed"); @@ -582,7 +927,7 @@ epass2003_refresh(struct sc_card *card) int r = SC_SUCCESS; epass2003_exdata *exdata = NULL; - if (!card->drv_data) + if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; @@ -604,17 +949,23 @@ construct_data_tlv(struct sc_card *card, struct sc_apdu *apdu, unsigned char *ap unsigned char *data_tlv, size_t * data_tlv_len, const unsigned char key_type) { size_t block_size = (KEY_TYPE_AES == key_type ? 16 : 8); - unsigned char pad[4096] = { 0 }; + unsigned char pad[4096] = {0}; size_t pad_len; size_t tlv_more; /* increased tlv length */ - unsigned char iv[16] = { 0 }; + unsigned char iv[16] = {0}; epass2003_exdata *exdata = NULL; + int r = 0; - if (!card->drv_data) + if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; + /* we encrypt the pad buffer and then write it to the apdu_buf at offset block_size + tlv_more */ + if (apdu->lc >= sizeof(pad) - block_size - 5) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, + "ePass2003 secure messaging APDU data too large"); + /* padding */ apdu_buf[block_size] = 0x87; memcpy(pad, apdu->data, apdu->lc); @@ -632,8 +983,7 @@ construct_data_tlv(struct sc_card *card, struct sc_apdu *apdu, unsigned char *ap apdu_buf[block_size + 3] = (unsigned char)((pad_len + 1) % 0x100); apdu_buf[block_size + 4] = 0x01; tlv_more = 5; - } - else { + } else { apdu_buf[block_size + 1] = (unsigned char)pad_len + 1; apdu_buf[block_size + 2] = 0x01; tlv_more = 3; @@ -641,10 +991,13 @@ construct_data_tlv(struct sc_card *card, struct sc_apdu *apdu, unsigned char *ap memcpy(data_tlv, &apdu_buf[block_size], tlv_more); /* encrypt Data */ - if (KEY_TYPE_AES == key_type) - aes128_encrypt_cbc(exdata->sk_enc, 16, iv, pad, pad_len, apdu_buf + block_size + tlv_more); - else - des3_encrypt_cbc(exdata->sk_enc, 16, iv, pad, pad_len, apdu_buf + block_size + tlv_more); + if (KEY_TYPE_AES == key_type) { + r = aes128_encrypt_cbc(card, exdata->sk_enc, 16, iv, pad, pad_len, apdu_buf + block_size + tlv_more); + LOG_TEST_RET(card->ctx, r, "aes128_encrypt_cbc failed"); + } else { + r = des3_encrypt_cbc(card, exdata->sk_enc, 16, iv, pad, pad_len, apdu_buf + block_size + tlv_more); + LOG_TEST_RET(card->ctx, r, "des3_encrypt_cbc failed"); + } memcpy(data_tlv + tlv_more, apdu_buf + block_size + tlv_more, pad_len); *data_tlv_len = tlv_more + pad_len; @@ -667,8 +1020,7 @@ construct_le_tlv(struct sc_apdu *apdu, unsigned char *apdu_buf, size_t data_tlv_ *(apdu_buf + block_size + data_tlv_len + 3) = (unsigned char)(apdu->le % 0x100); memcpy(le_tlv, apdu_buf + block_size + data_tlv_len, 4); *le_tlv_len = 4; - } - else { + } else { *(apdu_buf + block_size + data_tlv_len + 1) = 1; *(apdu_buf + block_size + data_tlv_len + 2) = (unsigned char)apdu->le; memcpy(le_tlv, apdu_buf + block_size + data_tlv_len, 3); @@ -684,30 +1036,33 @@ construct_mac_tlv(struct sc_card *card, unsigned char *apdu_buf, size_t data_tlv unsigned char *mac_tlv, size_t * mac_tlv_len, const unsigned char key_type) { size_t block_size = (KEY_TYPE_AES == key_type ? 16 : 8); - unsigned char mac[4096] = { 0 }; + unsigned char mac[4096] = {0}; size_t mac_len; - unsigned char icv[16] = { 0 }; + unsigned char icv[16] = {0}; + int r ; int i = (KEY_TYPE_AES == key_type ? 15 : 7); epass2003_exdata *exdata = NULL; - if (!card->drv_data) + if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; + if (data_tlv_len + le_tlv_len + block_size + 1 > sizeof(mac)) { + return SC_ERROR_BUFFER_TOO_SMALL; + } + if (0 == data_tlv_len && 0 == le_tlv_len) { mac_len = block_size; - } - else { + } else { /* padding */ *(apdu_buf + block_size + data_tlv_len + le_tlv_len) = 0x80; - if ((data_tlv_len + le_tlv_len + 1) % block_size) + if ((data_tlv_len + le_tlv_len + 1) % block_size) { mac_len = (((data_tlv_len + le_tlv_len + 1) / block_size) + 1) * block_size + block_size; - - else + } else { mac_len = data_tlv_len + le_tlv_len + 1 + block_size; - + } memset((apdu_buf + block_size + data_tlv_len + le_tlv_len + 1), 0, (mac_len - (data_tlv_len + le_tlv_len + 1))); } @@ -716,8 +1071,7 @@ construct_mac_tlv(struct sc_card *card, unsigned char *apdu_buf, size_t data_tlv for (; i >= 0; i--) { if (exdata->icv_mac[i] == 0xff) { exdata->icv_mac[i] = 0; - } - else { + } else { exdata->icv_mac[i]++; break; } @@ -727,22 +1081,113 @@ construct_mac_tlv(struct sc_card *card, unsigned char *apdu_buf, size_t data_tlv memset(icv, 0, sizeof(icv)); memcpy(icv, exdata->icv_mac, 16); if (KEY_TYPE_AES == key_type) { - aes128_encrypt_cbc(exdata->sk_mac, 16, icv, apdu_buf, mac_len, mac); - memcpy(mac_tlv + 2, &mac[mac_len - 16], 8); - } - else { - unsigned char iv[8] = { 0 }; - unsigned char tmp[8] = { 0 }; - des_encrypt_cbc(exdata->sk_mac, 8, icv, apdu_buf, mac_len, mac); - des_decrypt_cbc(&exdata->sk_mac[8], 8, iv, &mac[mac_len - 8], 8, tmp); - memset(iv, 0x00, 8); - des_encrypt_cbc(exdata->sk_mac, 8, iv, tmp, 8, mac_tlv + 2); + if (exdata->bFipsCertification) { + for (int i = 0; i < 16; i++) { + apdu_buf[i] = apdu_buf[i] ^ icv[i]; + } + r = aes128_encrypt_cmac(card, exdata->sk_mac, 128, apdu_buf, data_tlv_len + le_tlv_len + block_size, mac); + LOG_TEST_RET(card->ctx, r, "aes128_encrypt_cmac failed"); + memcpy(mac_tlv + 2, &mac[0 /*ulmacLen-16*/], 8); + for (int j = 0; j < 4; j++) { + apdu_buf[j] = apdu_buf[j] ^ icv[j]; + } + } else { + r = aes128_encrypt_cbc(card, exdata->sk_mac, 16, icv, apdu_buf, mac_len, mac); + LOG_TEST_RET(card->ctx, r, "aes128_encrypt_cbc failed"); + memcpy(mac_tlv + 2, &mac[mac_len - 16], 8); + } + } else { + unsigned char iv[EVP_MAX_IV_LENGTH] = {0}; + unsigned char tmp[8] = {0}; + r = des_encrypt_cbc(card, exdata->sk_mac, 8, icv, apdu_buf, mac_len, mac); + LOG_TEST_RET(card->ctx, r, "des_encrypt_cbc 1 failed"); + r = des_decrypt_cbc(card, &exdata->sk_mac[8], 8, iv, &mac[mac_len - 8], 8, tmp); + LOG_TEST_RET(card->ctx, r, "des_decrypt_cbc failed"); + memset(iv, 0x00, sizeof iv); + r = des_encrypt_cbc(card, exdata->sk_mac, 8, iv, tmp, 8, mac_tlv + 2); + LOG_TEST_RET(card->ctx, r, "des_encrypt_cbc 2 failed"); } *mac_tlv_len = 2 + 8; return 0; } +/* MAC(TLV case 1) */ +static int +construct_mac_tlv_case1(struct sc_card *card, unsigned char *apdu_buf, size_t data_tlv_len, size_t le_tlv_len, + unsigned char *mac_tlv, size_t * mac_tlv_len, const unsigned char key_type) +{ + int r; + size_t block_size = 4; + unsigned char mac[4096] = {0}; + size_t mac_len; + int i = (KEY_TYPE_AES == key_type ? 15 : 7); + unsigned char icv[16] = {0}; + + epass2003_exdata *exdata = NULL; + + if (!card->drv_data) + return SC_ERROR_INVALID_ARGUMENTS; + + exdata = (epass2003_exdata *)card->drv_data; + + if (data_tlv_len + le_tlv_len + block_size + 1 > sizeof(mac)) { + return SC_ERROR_BUFFER_TOO_SMALL; + } + + if (0 == data_tlv_len && 0 == le_tlv_len) { + mac_len = block_size; + } else { + /* padding */ + *(apdu_buf + block_size + data_tlv_len + le_tlv_len) = 0x80; + if ((data_tlv_len + le_tlv_len + 1) % block_size) { + mac_len = (((data_tlv_len + le_tlv_len + 1) / block_size) + 1) * block_size + block_size; + } else { + mac_len = data_tlv_len + le_tlv_len + 1 + block_size; + } + } + /* increase icv */ + for (; i >= 0; i--) { + if (exdata->icv_mac[i] == 0xff) { + exdata->icv_mac[i] = 0; + } else { + exdata->icv_mac[i]++; + break; + } + } + + /* calculate MAC */ + memset(icv, 0, sizeof(icv)); + memcpy(icv, exdata->icv_mac, 16); + if (KEY_TYPE_AES == key_type) { + if (exdata->bFipsCertification) { + r = aes128_encrypt_cmac_ft(card, exdata->sk_mac, 128, apdu_buf, data_tlv_len + le_tlv_len + block_size, mac, &icv[0]); + LOG_TEST_RET(card->ctx, r, "aes128_encrypt_cmac_ft failed"); + memcpy(mac_tlv + 2, &mac[0 /*ulmacLen-16*/], 8); + } else { + if (mac_len < 16) + LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, "incorrect mac length"); + r = aes128_encrypt_cbc(card, exdata->sk_mac, 16, icv, apdu_buf, mac_len, mac); + LOG_TEST_RET(card->ctx, r, "aes128_encrypt_cbc failed"); + memcpy(mac_tlv + 2, &mac[mac_len - 16], 8); + } + } else { + unsigned char iv[EVP_MAX_IV_LENGTH] = {0}; + unsigned char tmp[8] = {0}; + if (mac_len < 8) + LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, "incorrect mac length"); + r = des_encrypt_cbc(card, exdata->sk_mac, 8, icv, apdu_buf, mac_len, mac); + LOG_TEST_RET(card->ctx, r, "des_encrypt_cbc failed"); + r = des_decrypt_cbc(card, &exdata->sk_mac[8], 8, iv, &mac[mac_len - 8], 8, tmp); + LOG_TEST_RET(card->ctx, r, "des_decrypt_cbc failed"); + memset(iv, 0x00, sizeof iv); + r = des_encrypt_cbc(card, exdata->sk_mac, 8, iv, tmp, 8, mac_tlv + 2); + LOG_TEST_RET(card->ctx, r, "des_encrypt_cbc failed"); + } + + *mac_tlv_len = 2 + 8; + return 0; +} /* According to GlobalPlatform Card Specification's SCP01 * encode APDU from @@ -756,20 +1201,21 @@ encode_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_apdu *sm, unsigned char *apdu_buf, size_t * apdu_buf_len) { size_t block_size = 0; - unsigned char dataTLV[4096] = { 0 }; + unsigned char dataTLV[4096] = {0}; size_t data_tlv_len = 0; - unsigned char le_tlv[256] = { 0 }; + unsigned char le_tlv[256] = {0}; size_t le_tlv_len = 0; size_t mac_tlv_len = 10; size_t tmp_lc = 0; size_t tmp_le = 0; - unsigned char mac_tlv[256] = { 0 }; + size_t expected_total_len; + unsigned char mac_tlv[256] = {0}; epass2003_exdata *exdata = NULL; mac_tlv[0] = 0x8E; mac_tlv[1] = 8; /* size_t plain_le = 0; */ - if (!card->drv_data) + if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata*)card->drv_data; block_size = (KEY_TYPE_DES == exdata->smtype ? 16 : 8); @@ -781,7 +1227,11 @@ encode_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_apdu *sm, apdu_buf[3] = (unsigned char)plain->p2; /* plain_le = plain->le; */ /* padding */ - apdu_buf[4] = 0x80; + if (exdata->bFipsCertification && plain->lc == 0 && apdu_buf[1] == 0x82 && apdu_buf[2] == 0x01) { + apdu_buf[4] = 0x00; + } else { + apdu_buf[4] = 0x80; + } memset(&apdu_buf[5], 0x00, block_size - 5); /* Data -> Data' */ @@ -794,8 +1244,13 @@ encode_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_apdu *sm, &le_tlv_len, exdata->smtype)) return -1; - if (0 != construct_mac_tlv(card, apdu_buf, data_tlv_len, le_tlv_len, mac_tlv, &mac_tlv_len, exdata->smtype)) - return -1; + if (exdata->bFipsCertification && plain->lc == 0 && apdu_buf[1] == 0x82 && apdu_buf[2] == 0x01) { + if (0 != construct_mac_tlv_case1(card, apdu_buf, data_tlv_len, le_tlv_len, mac_tlv, &mac_tlv_len, exdata->smtype)) + return -1; + } else { + if (0 != construct_mac_tlv(card, apdu_buf, data_tlv_len, le_tlv_len, mac_tlv, &mac_tlv_len, exdata->smtype)) + return -1; + } memset(apdu_buf + 4, 0, *apdu_buf_len - 4); sm->lc = sm->datalen = data_tlv_len + le_tlv_len + mac_tlv_len; @@ -805,12 +1260,18 @@ encode_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_apdu *sm, apdu_buf[5] = (unsigned char)(((sm->lc) / 0x100) % 0x100); apdu_buf[6] = (unsigned char)((sm->lc) % 0x100); tmp_lc = 3; - } - else { + } else { apdu_buf[4] = (unsigned char)sm->lc; tmp_lc = 1; } + /* 2 is for Le extension in the worst case */ + expected_total_len = 4 + tmp_lc + data_tlv_len + le_tlv_len + mac_tlv_len + 2; + + if (expected_total_len > *apdu_buf_len) { + return SC_ERROR_BUFFER_TOO_SMALL; + } + memcpy(apdu_buf + 4 + tmp_lc, dataTLV, data_tlv_len); memcpy(apdu_buf + 4 + tmp_lc + data_tlv_len, le_tlv, le_tlv_len); memcpy(apdu_buf + 4 + tmp_lc + data_tlv_len + le_tlv_len, mac_tlv, mac_tlv_len); @@ -822,8 +1283,7 @@ encode_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_apdu *sm, *(apdu_buf + 4 + tmp_lc + sm->lc) = (unsigned char)(plain->le / 0x100); *(apdu_buf + 4 + tmp_lc + sm->lc + 1) = (unsigned char)(plain->le % 0x100); tmp_le = 2; - } - else if (3 == le_tlv_len) { + } else if (3 == le_tlv_len) { *(apdu_buf + 4 + tmp_lc + sm->lc) = (unsigned char)plain->le; tmp_le = 1; } @@ -837,13 +1297,13 @@ encode_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_apdu *sm, static int epass2003_sm_wrap_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_apdu *sm) { - unsigned char buf[4096] = { 0 }; /* APDU buffer */ + unsigned char buf[4096] = {0}; /* APDU buffer */ size_t buf_len = sizeof(buf); epass2003_exdata *exdata = NULL; - - if (!card->drv_data) + + if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; - + exdata = (epass2003_exdata *)card->drv_data; LOG_FUNC_CALLED(card->ctx); @@ -881,6 +1341,110 @@ epass2003_sm_wrap_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_ap return SC_SUCCESS; } +static int +epass2003_check_response_mac_and_sw(struct sc_card *card, struct sc_apdu *sm) +{ + unsigned char iv[16]; + unsigned char *data = NULL, *mac = NULL; + size_t blocksize, mac_len; + int ret = -1; + size_t taglen; + const u8 *tag; + epass2003_exdata *exdata; + unsigned char *in = sm->resp; + unsigned char *alt_in; + size_t inlen = sm->resplen; + size_t len_correction; + + /* card/ctx/drv_data is already checked by caller */ + exdata = (epass2003_exdata *)card->drv_data; + + /* The SM must contain at least TLV encoded SW and MAC fields. */ + if (inlen < 14 ) + return ret; + + /* compare BER-TLV encoded SW (TAG 0x99) and raw SW */ + alt_in = in; + tag = sc_asn1_find_tag(card->ctx, alt_in, inlen, 0x99, &taglen); + if (tag == NULL || taglen != 2) { + /* + * It seems that the EPASS2003 firmware has some problem with BER-TLV encoding. + * Instead of (correct) TLV 87 81 81 [01 .. ..] incorrect TLV 87 81 [01 .. ..] + * is returned. There seems to be some proprietary fix for the faulty encoding + * in the decrypt_response() function, similar fix here: + */ + if (0x01 == in[2] && 0x82 != in[1]) { + sc_log(card->ctx, "Workaround, wrong BER-TLV ?"); + len_correction = in[1] + 2; + if (inlen < len_correction) + return ret; + inlen -= len_correction; + alt_in += len_correction; + tag = sc_asn1_find_tag(card->ctx, alt_in, inlen, 0x99, &taglen); + if (tag == NULL || taglen != 2) + return ret; + } else { + return ret; + } + } + if (sm->sw1 != tag[0] || sm->sw2 != tag[1]) + return ret; + + /* no documentation/real hardware to test, the response is accepted without MAC check */ + if (exdata->bFipsCertification) { + sc_log(card->ctx, "Warning, MAC is not checked"); + return 0; + } + tag = sc_asn1_find_tag(card->ctx, alt_in, inlen, 0x8e, &taglen); + if (tag == NULL || taglen != 8) + return ret; + + if (KEY_TYPE_AES == exdata->smtype) + blocksize = 16; + else + blocksize = 8; + + mac_len = tag - in - 2; + if (NULL == (data = calloc(1, mac_len + blocksize))) + goto end; + if (NULL == (mac = malloc(mac_len + blocksize))) + goto end; + + /* copy response to buffer and append padding */ + memcpy(data, in, mac_len); + data[mac_len++] = 0x80; + + if (mac_len % blocksize) + mac_len += (blocksize - (mac_len % blocksize)); + + /* calculate MAC */ + memcpy(iv, exdata->icv_mac, blocksize); + + if (KEY_TYPE_AES == exdata->smtype) { + if (aes128_encrypt_cbc(card, exdata->sk_mac, 16, iv, data, mac_len, mac)) + goto end; + } else { + uint8_t tmp[8]; + uint8_t iv0[EVP_MAX_IV_LENGTH]; + if (des_encrypt_cbc(card, exdata->sk_mac, 8, iv, data, mac_len, mac)) + goto end; + memset(iv0, 0, EVP_MAX_IV_LENGTH); + if (des_decrypt_cbc(card, &exdata->sk_mac[8], 8, iv0, &mac[mac_len - 8], 8, tmp)) + goto end; + memset(iv0, 0, EVP_MAX_IV_LENGTH); + if (des_encrypt_cbc(card, exdata->sk_mac, 8, iv0, tmp, 8, &mac[mac_len - 8])) + goto end; + } + /* compare MAC */ + if (!memcmp(tag, mac + mac_len - blocksize, 8)) + ret = 0; +end: + if (data) + free(data); + if (mac) + free(mac); + return ret; +} /* According to GlobalPlatform Card Specification's SCP01 * decrypt APDU response from @@ -894,56 +1458,48 @@ epass2003_sm_wrap_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_ap * SW12(TLV)=0x99|0x02|SW1+SW2 * MAC(TLV)=0x8e|0x08|MAC */ static int -decrypt_response(struct sc_card *card, unsigned char *in, unsigned char *out, size_t * out_len) +decrypt_response(struct sc_card *card, unsigned char *in, size_t inlen, unsigned char *out, size_t * out_len) { - size_t in_len; - size_t i; - unsigned char iv[16] = { 0 }; - unsigned char plaintext[4096] = { 0 }; + unsigned int cla = 0, tag = 0; + const unsigned char *p = in; + size_t cipher_len; + unsigned char iv[16] = {0}; + unsigned char plaintext[4096] = {0}; epass2003_exdata *exdata = NULL; - if (!card->drv_data) + if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; - /* no cipher */ - if (in[0] == 0x99) + if (SC_SUCCESS != sc_asn1_read_tag(&p, inlen, &cla, &tag, &cipher_len) || p == NULL) { + return -1; + } + if ((cla | tag) == 0x99) return 0; + if ((cla | tag) != 0x87) + return -1; - /* parse cipher length */ - if (0x01 == in[2] && 0x82 != in[1]) { - in_len = in[1]; - i = 3; - } - else if (0x01 == in[3] && 0x81 == in[1]) { - in_len = in[2]; - i = 4; - } - else if (0x01 == in[4] && 0x82 == in[1]) { - in_len = in[2] * 0x100; - in_len += in[3]; - i = 5; - } - else { + if (cipher_len < 2 || cipher_len > sizeof plaintext) return -1; - } + /* Skip the 0x01 separator between tag and ciphertext */ + p++; /* decrypt */ if (KEY_TYPE_AES == exdata->smtype) - aes128_decrypt_cbc(exdata->sk_enc, 16, iv, &in[i], in_len - 1, plaintext); + aes128_decrypt_cbc(card, exdata->sk_enc, 16, iv, p, cipher_len - 1, plaintext); else - des3_decrypt_cbc(exdata->sk_enc, 16, iv, &in[i], in_len - 1, plaintext); + des3_decrypt_cbc(card, exdata->sk_enc, 16, iv, p, cipher_len - 1, plaintext); /* unpadding */ - while (0x80 != plaintext[in_len - 2] && (in_len - 2 > 0)) - in_len--; + while (0x80 != plaintext[cipher_len - 2] && (cipher_len > 2)) + cipher_len--; - if (2 == in_len) + if (2 == cipher_len || *out_len < cipher_len - 2) return -1; - memcpy(out, plaintext, in_len - 2); - *out_len = in_len - 2; + memcpy(out, plaintext, cipher_len - 2); + *out_len = cipher_len - 2; return 0; } @@ -955,20 +1511,27 @@ epass2003_sm_unwrap_apdu(struct sc_card *card, struct sc_apdu *sm, struct sc_apd size_t len = 0; epass2003_exdata *exdata = NULL; - if (!card->drv_data) + if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; LOG_FUNC_CALLED(card->ctx); + /* verify MAC, and check if SW1,2 match SW1,2 encapsulated in SM */ + if (exdata->sm) { + if (epass2003_check_response_mac_and_sw(card, sm)) { + sc_log(card->ctx, "MAC or SW incorrect"); + return SC_ERROR_CARD_CMD_FAILED; + } + } r = sc_check_sw(card, sm->sw1, sm->sw2); if (r == SC_SUCCESS) { if (exdata->sm) { - if (0 != decrypt_response(card, sm->resp, plain->resp, &len)) + len = plain->resplen; + if (0 != decrypt_response(card, sm->resp, sm->resplen, plain->resp, &len)) return SC_ERROR_CARD_CMD_FAILED; - } - else { + } else { memcpy(plain->resp, sm->resp, sm->resplen); len = sm->resplen; } @@ -1050,7 +1613,7 @@ epass2003_sm_get_wrapped_apdu(struct sc_card *card, apdu->resplen = SC_MAX_EXT_APDU_BUFFER_SIZE; rv = epass2003_sm_wrap_apdu(card, plain, apdu); - if (rv) { + if (rv) { rv = epass2003_sm_free_wrapped_apdu(card, NULL, &apdu); if (rv < 0) goto err; @@ -1088,11 +1651,11 @@ get_data(struct sc_card *card, unsigned char type, unsigned char *data, size_t d { int r; struct sc_apdu apdu; - unsigned char resp[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; + unsigned char resp[SC_MAX_APDU_BUFFER_SIZE] = {0}; size_t resplen = SC_MAX_APDU_BUFFER_SIZE; epass2003_exdata *exdata = NULL; - if (!card->drv_data) + if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; @@ -1109,8 +1672,7 @@ get_data(struct sc_card *card, unsigned char type, unsigned char *data, size_t d exdata->sm = SM_PLAIN; r = sc_transmit_apdu(card, &apdu); exdata->sm = tmp_sm; - } - else { + } else { r = sc_transmit_apdu_t(card, &apdu); } LOG_TEST_RET(card->ctx, r, "APDU get_data failed"); @@ -1140,9 +1702,11 @@ static int epass2003_init(struct sc_card *card) { unsigned int flags; - unsigned char data[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; + unsigned int ext_flags; + unsigned char data[SC_MAX_APDU_BUFFER_SIZE] = {0}; size_t datalen = SC_MAX_APDU_BUFFER_SIZE; epass2003_exdata *exdata = NULL; + void *old_drv_data = card->drv_data; LOG_FUNC_CALLED(card->ctx); @@ -1157,8 +1721,17 @@ epass2003_init(struct sc_card *card) exdata->sm = SM_SCP01; /* decide FIPS/Non-FIPS mode */ - if (SC_SUCCESS != get_data(card, 0x86, data, datalen)) - return SC_ERROR_CARD_CMD_FAILED; + if (SC_SUCCESS != get_data(card, 0x86, data, datalen)) { + free(exdata); + card->drv_data = old_drv_data; + return SC_ERROR_INVALID_CARD; + } + + if (memcmp(&data[32], "\x87\x01\x01", 3) == 0 && memcmp(&data[0], "\x80\x01\x01", 3) == 0) { + exdata->bFipsCertification = 0x01; + } else { + exdata->bFipsCertification = 0x00; + } if (0x01 == data[2]) exdata->smtype = KEY_TYPE_AES; @@ -1166,12 +1739,11 @@ epass2003_init(struct sc_card *card) exdata->smtype = KEY_TYPE_DES; if (0x84 == data[14]) { - if (0x00 == data[16]) { + if (0x00 == data[16]) { exdata->sm = SM_PLAIN; } } - /* mutual authentication */ card->max_recv_size = 0xD8; card->max_send_size = 0xE8; @@ -1192,6 +1764,11 @@ epass2003_init(struct sc_card *card) _sc_card_add_rsa_alg(card, 1024, flags, 0); _sc_card_add_rsa_alg(card, 2048, flags, 0); + //set EC Alg Flags + flags = SC_ALGORITHM_ONBOARD_KEY_GEN|SC_ALGORITHM_ECDSA_HASH_SHA1|SC_ALGORITHM_ECDSA_HASH_SHA256|SC_ALGORITHM_ECDSA_HASH_NONE|SC_ALGORITHM_ECDSA_RAW; + ext_flags = 0; + _sc_card_add_ec_alg(card, 256, flags, ext_flags, NULL); + card->caps = SC_CARD_CAP_RNG | SC_CARD_CAP_APDU_EXT; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); @@ -1212,8 +1789,13 @@ epass2003_finish(sc_card_t *card) static int epass2003_hook_path(struct sc_path *path, int inc) { - u8 fid_h = path->value[path->len - 2]; - u8 fid_l = path->value[path->len - 1]; + u8 fid_h = 0; + u8 fid_l = 0; + + if (!path || path->len < 2) + return -1; + fid_h = path->value[path->len - 2]; + fid_l = path->value[path->len - 1]; switch (fid_h) { case 0x29: @@ -1235,17 +1817,24 @@ epass2003_hook_path(struct sc_path *path, int inc) } -static void +static int epass2003_hook_file(struct sc_file *file, int inc) { int fidl = file->id & 0xff; int fidh = file->id & 0xff00; - if (epass2003_hook_path(&file->path, inc)) { + int rv = 0; + + rv = epass2003_hook_path(&file->path, inc); + + if (rv > 0) { if (inc) file->id = fidh + fidl * FID_STEP; else file->id = fidh + fidl / FID_STEP; } + if (rv < 0) + return rv; + return SC_SUCCESS; } @@ -1253,12 +1842,15 @@ static int epass2003_select_fid_(struct sc_card *card, sc_path_t * in_path, sc_file_t ** file_out) { struct sc_apdu apdu; - u8 buf[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; + u8 buf[SC_MAX_APDU_BUFFER_SIZE] = {0}; u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; - int r, pathlen; + int r; + size_t pathlen; sc_file_t *file = NULL; - epass2003_hook_path(in_path, 1); + r = epass2003_hook_path(in_path, 1); + LOG_TEST_RET(card->ctx, r, "Can not hook path"); + memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; @@ -1282,13 +1874,12 @@ epass2003_select_fid_(struct sc_card *card, sc_path_t * in_path, sc_file_t ** fi apdu.resp = buf; apdu.resplen = sizeof(buf); apdu.le = 0; - } - else { + } else { apdu.cse = (apdu.lc == 0) ? SC_APDU_CASE_1 : SC_APDU_CASE_3_SHORT; } if (path[0] == 0x29) { /* TODO:0x29 accords with FID prefix in profile */ - /* Not allowed to select prvate key file, so fake fci. */ + /* Not allowed to select private key file, so fake fci. */ /* 62 16 82 02 11 00 83 02 29 00 85 02 08 00 86 08 FF 90 90 90 FF FF FF FF */ apdu.resplen = 0x18; memcpy(apdu.resp, @@ -1346,7 +1937,7 @@ epass2003_select_fid(struct sc_card *card, unsigned int id_hi, unsigned int id_l sc_file_t ** file_out) { int r; - sc_file_t *file = 0; + sc_file_t *file = NULL; sc_path_t path; memset(&path, 0, sizeof(path)); @@ -1358,23 +1949,11 @@ epass2003_select_fid(struct sc_card *card, unsigned int id_hi, unsigned int id_l r = epass2003_select_fid_(card, &path, &file); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - /* update cache */ - if (file && file->type == SC_FILE_TYPE_DF) { - card->cache.current_path.type = SC_PATH_TYPE_PATH; - card->cache.current_path.value[0] = 0x3f; - card->cache.current_path.value[1] = 0x00; - if (id_hi == 0x3f && id_lo == 0x00) { - card->cache.current_path.len = 2; - } - else { - card->cache.current_path.len = 4; - card->cache.current_path.value[2] = id_hi; - card->cache.current_path.value[3] = id_lo; - } - } - - if (file_out) + if (file_out) { *file_out = file; + } else { + sc_file_free(file); + } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -1385,25 +1964,8 @@ epass2003_select_aid(struct sc_card *card, const sc_path_t * in_path, sc_file_t { int r = 0; - if (card->cache.valid - && card->cache.current_path.type == SC_PATH_TYPE_DF_NAME - && card->cache.current_path.len == in_path->len - && memcmp(card->cache.current_path.value, in_path->value, in_path->len) == 0) { - if (file_out) { - *file_out = sc_file_new(); - if (!file_out) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); - } - } - else { - r = iso_ops->select_file(card, in_path, file_out); - LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - - /* update cache */ - card->cache.current_path.type = SC_PATH_TYPE_DF_NAME; - card->cache.current_path.len = in_path->len; - memcpy(card->cache.current_path.value, in_path->value, in_path->len); - } + r = iso_ops->select_file(card, in_path, file_out); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (file_out) { sc_file_t *file = *file_out; @@ -1429,7 +1991,6 @@ epass2003_select_path(struct sc_card *card, const u8 pathbuf[16], const size_t l u8 n_pathbuf[SC_MAX_PATH_SIZE]; const u8 *path = pathbuf; size_t pathlen = len; - int bMatch = -1; unsigned int i; int r; @@ -1444,77 +2005,17 @@ epass2003_select_path(struct sc_card *card, const u8 pathbuf[16], const size_t l if (path[0] != 0x3f || path[1] != 0x00) { n_pathbuf[0] = 0x3f; n_pathbuf[1] = 0x00; - - for (i = 0; i < pathlen; i++) - n_pathbuf[i + 2] = pathbuf[i]; + memcpy(n_pathbuf + 2, path, pathlen); path = n_pathbuf; pathlen += 2; } - /* check current working directory */ - if (card->cache.valid - && card->cache.current_path.type == SC_PATH_TYPE_PATH - && card->cache.current_path.len >= 2 - && card->cache.current_path.len <= pathlen) { - bMatch = 0; - for (i = 0; i < card->cache.current_path.len; i += 2) - if (card->cache.current_path.value[i] == path[i] - && card->cache.current_path.value[i + 1] == path[i + 1]) - bMatch += 2; + for (i = 0; i < pathlen - 2; i += 2) { + r = epass2003_select_fid(card, path[i], path[i + 1], NULL); + LOG_TEST_RET(card->ctx, r, "SELECT FILE (DF-ID) failed"); } - if (card->cache.valid && bMatch > 2) { - if (pathlen - bMatch == 2) { - /* we are in the rigth directory */ - return epass2003_select_fid(card, path[bMatch], path[bMatch + 1], file_out); - } - else if (pathlen - bMatch > 2) { - /* two more steps to go */ - sc_path_t new_path; - - /* first step: change directory */ - r = epass2003_select_fid(card, path[bMatch], path[bMatch + 1], NULL); - LOG_TEST_RET(card->ctx, r, "SELECT FILE (DF-ID) failed"); - - new_path.type = SC_PATH_TYPE_PATH; - new_path.len = pathlen - bMatch - 2; - memcpy(new_path.value, &(path[bMatch + 2]), new_path.len); - - /* final step: select file */ - return epass2003_select_file(card, &new_path, file_out); - } - else { /* if (bMatch - pathlen == 0) */ - - /* done: we are already in the - * requested directory */ - sc_log(card->ctx, "cache hit\n"); - /* copy file info (if necessary) */ - if (file_out) { - sc_file_t *file = sc_file_new(); - if (!file) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); - file->id = (path[pathlen - 2] << 8) + path[pathlen - 1]; - file->path = card->cache.current_path; - file->type = SC_FILE_TYPE_DF; - file->ef_structure = SC_FILE_EF_UNKNOWN; - file->size = 0; - file->namelen = 0; - file->magic = SC_FILE_MAGIC; - *file_out = file; - } - /* nothing left to do */ - return SC_SUCCESS; - } - } - else { - /* no usable cache */ - for (i = 0; i < pathlen - 2; i += 2) { - r = epass2003_select_fid(card, path[i], path[i + 1], NULL); - LOG_TEST_RET(card->ctx, r, "SELECT FILE (DF-ID) failed"); - } - - return epass2003_select_fid(card, path[pathlen - 2], path[pathlen - 1], file_out); - } + return epass2003_select_fid(card, path[pathlen - 2], path[pathlen - 1], file_out); } @@ -1522,22 +2023,8 @@ static int epass2003_select_file(struct sc_card *card, const sc_path_t * in_path, sc_file_t ** file_out) { - int r; - char pbuf[SC_MAX_PATH_STRING_SIZE]; - LOG_FUNC_CALLED(card->ctx); - r = sc_path_print(pbuf, sizeof(pbuf), &card->cache.current_path); - if (r != SC_SUCCESS) - pbuf[0] = '\0'; - - sc_log(card->ctx, - "current path (%s, %s): %s (len: %"SC_FORMAT_LEN_SIZE_T"u)\n", - card->cache.current_path.type == SC_PATH_TYPE_DF_NAME ? - "aid" : "path", - card->cache.valid ? "valid" : "invalid", pbuf, - card->cache.current_path.len); - switch (in_path->type) { case SC_PATH_TYPE_FILE_ID: if (in_path->len != 2) @@ -1552,27 +2039,22 @@ epass2003_select_file(struct sc_card *card, const sc_path_t * in_path, } } - static int epass2003_set_security_env(struct sc_card *card, const sc_security_env_t * env, int se_num) { struct sc_apdu apdu; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE] = {0}; u8 *p; unsigned short fid = 0; int r, locked = 0; + epass2003_exdata *exdata = NULL; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0); - switch (env->operation) { - case SC_SEC_OPERATION_DECIPHER: - apdu.p2 = 0xB8; - break; - case SC_SEC_OPERATION_SIGN: - apdu.p2 = 0xB8; - break; - default: + if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; - } + + exdata = (epass2003_exdata *)card->drv_data; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0); p = sbuf; *p++ = 0x80; /* algorithm reference */ @@ -1586,10 +2068,32 @@ epass2003_set_security_env(struct sc_card *card, const sc_security_env_t * env, fid += (unsigned short)(0x20 * (env->key_ref[0] & 0xff)); *p++ = fid >> 8; *p++ = fid & 0xff; - r = p - sbuf; + r = (int)(p - sbuf); apdu.lc = r; apdu.datalen = r; apdu.data = sbuf; + + if (env->algorithm == SC_ALGORITHM_EC) { + apdu.p2 = 0xB6; + exdata->currAlg = SC_ALGORITHM_EC; + if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA1) { + sbuf[2] = 0x91; + exdata->ecAlgFlags = SC_ALGORITHM_ECDSA_HASH_SHA1; + } else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA256) { + sbuf[2] = 0x92; + exdata->ecAlgFlags = SC_ALGORITHM_ECDSA_HASH_SHA256; + } else { + sbuf[2] = 0x92; + exdata->ecAlgFlags = SC_ALGORITHM_ECDSA_HASH_NONE; + } + } else if (env->algorithm == SC_ALGORITHM_RSA) { + exdata->currAlg = SC_ALGORITHM_RSA; + apdu.p2 = 0xB8; + sc_log(card->ctx, "setenv RSA Algorithm alg_flags = %0lx\n", env->algorithm_flags); + } else { + sc_log(card->ctx, "%0lx Alg Not Supported!", env->algorithm); + } + if (se_num > 0) { r = sc_lock(card); LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); @@ -1638,18 +2142,73 @@ static int epass2003_decipher(struct sc_card *card, const u8 * data, size_t data { int r; struct sc_apdu apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE] = {0}; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE] = {0}; + epass2003_exdata *exdata = NULL; - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_EXT, 0x2A, 0x80, 0x86); - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - apdu.le = 256; + LOG_FUNC_CALLED(card->ctx); - memcpy(sbuf, data, datalen); - apdu.data = sbuf; - apdu.lc = datalen; - apdu.datalen = datalen; + if (!card->drv_data) + return SC_ERROR_INVALID_ARGUMENTS; + + exdata = (epass2003_exdata *)card->drv_data; + + if (exdata->currAlg == SC_ALGORITHM_EC) { + if (exdata->ecAlgFlags & SC_ALGORITHM_ECDSA_HASH_SHA1) { + r = hash_data(card, data, datalen, sbuf, SC_ALGORITHM_ECDSA_HASH_SHA1); + LOG_TEST_RET(card->ctx, r, "hash_data failed"); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0x2A, 0x9E, 0x9A); + apdu.data = sbuf; + apdu.lc = 0x14; + apdu.datalen = 0x14; + } else if (exdata->ecAlgFlags & SC_ALGORITHM_ECDSA_HASH_SHA256) { + r = hash_data(card, data, datalen, sbuf, SC_ALGORITHM_ECDSA_HASH_SHA256); + LOG_TEST_RET(card->ctx, r, "hash_data failed"); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0x2A, 0x9E, 0x9A); + apdu.data = sbuf; + apdu.lc = 0x20; + apdu.datalen = 0x20; + } else if (exdata->ecAlgFlags & SC_ALGORITHM_ECDSA_HASH_NONE) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_3,0x2A, 0x9E, 0x9A); + apdu.data = data; + apdu.lc = datalen; + apdu.datalen = datalen; + } else { + return SC_ERROR_NOT_SUPPORTED; + } + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 0; + + r = sc_transmit_apdu_t(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; + memcpy(out, apdu.resp, len); + LOG_FUNC_RETURN(card->ctx, (int)len); + } + LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); + } else if (exdata->currAlg == SC_ALGORITHM_RSA) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_EXT, 0x2A, 0x80, 0x86); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 0; + + memcpy(sbuf, data, datalen); + apdu.data = sbuf; + apdu.lc = datalen; + apdu.datalen = datalen; + } else { + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_EXT, 0x2A, 0x80, 0x86); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 256; + + memcpy(sbuf, data, datalen); + apdu.data = sbuf; + apdu.lc = datalen; + apdu.datalen = datalen; + } r = sc_transmit_apdu_t(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); @@ -1657,7 +2216,7 @@ static int epass2003_decipher(struct sc_card *card, const u8 * data, size_t data if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); - LOG_FUNC_RETURN(card->ctx, len); + LOG_FUNC_RETURN(card->ctx, (int)len); } LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); @@ -1682,6 +2241,50 @@ acl_to_ac_byte(struct sc_card *card, const struct sc_acl_entry *e) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); } +/* Use epass2003 sec_attr to add acl entries */ +int +sec_attr_to_entry(struct sc_card *card, sc_file_t *file, int index) +{ + int i; + int found = 0; + + unsigned int method; + unsigned long keyref; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + switch (file->sec_attr[index]) { + case (EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE): + method = SC_AC_NONE; + keyref = SC_AC_KEY_REF_NONE; + break; + case (EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_USER): + method = SC_AC_CHV; + keyref = 1; + break; + default: + sc_log(card->ctx, "Unknown value 0x%2.2x in file->sec_attr[%d]", file->sec_attr[index], index); + method = SC_AC_NEVER; + keyref = SC_AC_KEY_REF_NONE; + break; + } + + for (i = 0; i < (int)(sizeof(sec_attr_to_acl_entry) / sizeof(sec_attr_to_acl_entries_t)); i++) { + const sec_attr_to_acl_entries_t *e = &sec_attr_to_acl_entry[i]; + + if (index == e->index && file->type == e->file_type + && file->ef_structure == e->file_ef_structure) { + /* may add multiple entries */ + sc_file_add_acl_entry(file, e->op, method, keyref); + found++; + } + } + if (found != 1) { + sc_log(card->ctx,"found %d entries ", found); + } + + return 0; +} static int epass2003_process_fci(struct sc_card *card, sc_file_t * file, const u8 * buf, size_t buflen) @@ -1703,7 +2306,7 @@ epass2003_process_fci(struct sc_card *card, sc_file_t * file, const u8 * buf, si if (taglen == 2) file->size = (file->size << 8) + tag[1]; sc_log(ctx, " bytes in file: %"SC_FORMAT_LEN_SIZE_T"u", - file->size); + file->size); } if (tag == NULL) { @@ -1725,8 +2328,7 @@ epass2003_process_fci(struct sc_card *card, sc_file_t * file, const u8 * buf, si if (byte == 0x38) { type = "DF"; file->type = SC_FILE_TYPE_DF; - } - else if (0x01 <= byte && byte <= 0x07) { + } else if (0x01 <= byte && byte <= 0x07) { type = "working EF"; file->type = SC_FILE_TYPE_WORKING_EF; switch (byte) { @@ -1739,21 +2341,14 @@ epass2003_process_fci(struct sc_card *card, sc_file_t * file, const u8 * buf, si case 0x04: file->ef_structure = SC_FILE_EF_LINEAR_FIXED; break; - case 0x03: - case 0x05: - case 0x06: - case 0x07: - break; default: break; } - } - else if (0x10 == byte) { + } else if (0x10 == byte) { type = "BSO"; file->type = SC_FILE_TYPE_BSO; - } - else if (0x11 <= byte) { + } else if (0x11 <= byte) { type = "internal EF"; file->type = SC_FILE_TYPE_INTERNAL_EF; switch (byte) { @@ -1764,11 +2359,9 @@ epass2003_process_fci(struct sc_card *card, sc_file_t * file, const u8 * buf, si default: break; } - } - else { + } else { type = "unknown"; file->type = SC_FILE_TYPE_INTERNAL_EF; - } sc_log(ctx, "type %s, EF structure %d", type, byte); } @@ -1776,12 +2369,10 @@ epass2003_process_fci(struct sc_card *card, sc_file_t * file, const u8 * buf, si tag = sc_asn1_find_tag(ctx, p, len, 0x84, &taglen); if (tag != NULL && taglen > 0 && taglen <= 16) { - char tbuf[128]; memcpy(file->name, tag, taglen); file->namelen = taglen; - sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, file->name, file->namelen, tbuf, sizeof(tbuf)); - sc_log(ctx, "File name: %s", tbuf); + sc_log_hex(ctx, "File name", file->name, file->namelen); if (!file->type) file->type = SC_FILE_TYPE_DF; } @@ -1797,8 +2388,13 @@ epass2003_process_fci(struct sc_card *card, sc_file_t * file, const u8 * buf, si sc_file_set_prop_attr(file, tag, taglen); tag = sc_asn1_find_tag(ctx, p, len, 0x86, &taglen); - if (tag != NULL && taglen) + if (tag != NULL && taglen) { + unsigned int i; sc_file_set_sec_attr(file, tag, taglen); + for (i = 0; i< taglen; i++) + if (tag[i] != 0xff) /* skip unused entries */ + sec_attr_to_entry(card, file, i); + } tag = sc_asn1_find_tag(ctx, p, len, 0x8A, &taglen); if (tag != NULL && taglen == 1) { @@ -1847,35 +2443,32 @@ epass2003_construct_fci(struct sc_card *card, const sc_file_t * file, if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { buf[1] = 0x00; sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p); - } - else if (file->ef_structure == SC_FILE_EF_LINEAR_FIXED - || file->ef_structure == SC_FILE_EF_LINEAR_VARIABLE) { + } else if (file->ef_structure == SC_FILE_EF_LINEAR_FIXED || + file->ef_structure == SC_FILE_EF_LINEAR_VARIABLE) { buf[1] = 0x00; buf[2] = 0x00; buf[3] = 0x40; /* record length */ buf[4] = 0x00; /* record count */ sc_asn1_put_tag(0x82, buf, 5, p, *outlen - (p - out), &p); - } - else { + } else { return SC_ERROR_NOT_SUPPORTED; } } else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { - if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_CRT) { + if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_CRT || + file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_EC_CRT) { buf[0] = 0x11; buf[1] = 0x00; - } - else if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + } else if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC || + file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_EC_PUBLIC) { buf[0] = 0x12; buf[1] = 0x00; - } - else { + } else { return SC_ERROR_NOT_SUPPORTED; } sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p); - } - else if (file->type == SC_FILE_TYPE_BSO) { + } else if (file->type == SC_FILE_TYPE_BSO) { buf[0] = 0x10; buf[1] = 0x00; sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p); @@ -1887,8 +2480,7 @@ epass2003_construct_fci(struct sc_card *card, const sc_file_t * file, if (file->type == SC_FILE_TYPE_DF) { if (file->namelen != 0) { sc_asn1_put_tag(0x84, file->name, file->namelen, p, *outlen - (p - out), &p); - } - else { + } else { return SC_ERROR_INVALID_ARGUMENTS; } } @@ -1896,14 +2488,14 @@ epass2003_construct_fci(struct sc_card *card, const sc_file_t * file, unsigned char data[2] = {0x00, 0x7F}; /* 127 files at most */ sc_asn1_put_tag(0x85, data, sizeof(data), p, *outlen - (p - out), &p); - } - else if (file->type == SC_FILE_TYPE_BSO) { + } else if (file->type == SC_FILE_TYPE_BSO) { buf[0] = file->size & 0xff; sc_asn1_put_tag(0x85, buf, 1, p, *outlen - (p - out), &p); - } - else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { + } else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_CRT || - file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC || + file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_EC_CRT || + file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_EC_PUBLIC) { buf[0] = (file->size >> 8) & 0xFF; buf[1] = file->size & 0xFF; sc_asn1_put_tag(0x85, buf, 2, p, *outlen - (p - out), &p); @@ -1912,50 +2504,44 @@ epass2003_construct_fci(struct sc_card *card, const sc_file_t * file, if (file->sec_attr_len) { memcpy(buf, file->sec_attr, file->sec_attr_len); sc_asn1_put_tag(0x86, buf, file->sec_attr_len, p, *outlen - (p - out), &p); - } - else { + + } else { sc_log(card->ctx, "SC_FILE_ACL"); if (file->type == SC_FILE_TYPE_DF) { ops[0] = SC_AC_OP_LIST_FILES; ops[1] = SC_AC_OP_CREATE; ops[3] = SC_AC_OP_DELETE; - } - else if (file->type == SC_FILE_TYPE_WORKING_EF) { + } else if (file->type == SC_FILE_TYPE_WORKING_EF) { if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { ops[0] = SC_AC_OP_READ; ops[1] = SC_AC_OP_UPDATE; ops[3] = SC_AC_OP_DELETE; - } - else if (file->ef_structure == SC_FILE_EF_LINEAR_FIXED - || file->ef_structure == SC_FILE_EF_LINEAR_VARIABLE) { + } else if (file->ef_structure == SC_FILE_EF_LINEAR_FIXED || + file->ef_structure == SC_FILE_EF_LINEAR_VARIABLE) { ops[0] = SC_AC_OP_READ; ops[1] = SC_AC_OP_UPDATE; ops[2] = SC_AC_OP_WRITE; ops[3] = SC_AC_OP_DELETE; - } - else { + } else { return SC_ERROR_NOT_SUPPORTED; } - } - else if (file->type == SC_FILE_TYPE_BSO) { + } else if (file->type == SC_FILE_TYPE_BSO) { ops[0] = SC_AC_OP_UPDATE; ops[3] = SC_AC_OP_DELETE; - } - else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { - if (file->ef_structure == - SC_CARDCTL_OBERTHUR_KEY_RSA_CRT) { + } else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { + if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_CRT || + file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_EC_CRT) { ops[1] = SC_AC_OP_UPDATE; ops[2] = SC_AC_OP_CRYPTO; ops[3] = SC_AC_OP_DELETE; - } - else if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + } else if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC || + file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_EC_PUBLIC) { ops[0] = SC_AC_OP_READ; ops[1] = SC_AC_OP_UPDATE; ops[2] = SC_AC_OP_CRYPTO; ops[3] = SC_AC_OP_DELETE; } - } - else { + } else { return SC_ERROR_NOT_SUPPORTED; } @@ -1973,13 +2559,19 @@ epass2003_construct_fci(struct sc_card *card, const sc_file_t * file, buf[ii] = rv; } sc_asn1_put_tag(0x86, buf, sizeof(ops), p, *outlen - (p - out), &p); - + if (file->size == 256) { + out[4]= 0x13; + } } /* VT ??? */ - if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC || + file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_EC_PUBLIC) { unsigned char data[2] = {0x00, 0x66}; sc_asn1_put_tag(0x87, data, sizeof(data), p, *outlen - (p - out), &p); + if (file->size == 256) { + out[4] = 0x14; + } } out[1] = p - out - 2; @@ -1994,7 +2586,7 @@ epass2003_create_file(struct sc_card *card, sc_file_t * file) { int r; size_t len; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE] = {0}; struct sc_apdu apdu; len = SC_MAX_APDU_BUFFER_SIZE; @@ -2032,18 +2624,16 @@ epass2003_delete_file(struct sc_card *card, const sc_path_t * path) LOG_FUNC_CALLED(card->ctx); r = sc_select_file(card, path, NULL); - epass2003_hook_path((struct sc_path *)path, 1); - if (r == SC_SUCCESS) { - sbuf[0] = path->value[path->len - 2]; - sbuf[1] = path->value[path->len - 1]; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); - apdu.lc = 2; - apdu.datalen = 2; - apdu.data = sbuf; - } - else { - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); - } + LOG_TEST_RET(card->ctx, r, "Can not select file"); + r = epass2003_hook_path((struct sc_path *)path, 1); + LOG_TEST_RET(card->ctx, r, "Can not hook path"); + + sbuf[0] = path->value[path->len - 2]; + sbuf[1] = path->value[path->len - 1]; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); + apdu.lc = 2; + apdu.datalen = 2; + apdu.data = sbuf; r = sc_transmit_apdu_t(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); @@ -2057,7 +2647,7 @@ static int epass2003_list_files(struct sc_card *card, unsigned char *buf, size_t buflen) { struct sc_apdu apdu; - unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE] = { 0 }; + unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE] = {0}; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); @@ -2079,7 +2669,7 @@ epass2003_list_files(struct sc_card *card, unsigned char *buf, size_t buflen) buflen = buflen < apdu.resplen ? buflen : apdu.resplen; memcpy(buf, rbuf, buflen); - LOG_FUNC_RETURN(card->ctx, buflen); + LOG_FUNC_RETURN(card->ctx, (int)buflen); } @@ -2089,7 +2679,7 @@ internal_write_rsa_key_factor(struct sc_card *card, unsigned short fid, u8 facto { int r; struct sc_apdu apdu; - u8 sbuff[SC_MAX_EXT_APDU_BUFFER_SIZE] = { 0 }; + u8 sbuff[SC_MAX_EXT_APDU_BUFFER_SIZE] = {0}; LOG_FUNC_CALLED(card->ctx); @@ -2129,19 +2719,31 @@ internal_write_rsa_key(struct sc_card *card, unsigned short fid, struct sc_pkcs1 static int -hash_data(unsigned char *data, size_t datalen, unsigned char *hash) +hash_data(struct sc_card *card, const unsigned char *data, size_t datalen, unsigned char *hash, unsigned int mechanismType) { - unsigned char data_hash[24] = { 0 }; - size_t len = 0; if ((NULL == data) || (NULL == hash)) return SC_ERROR_INVALID_ARGUMENTS; - sha1_digest(data, datalen, data_hash); - - len = REVERSE_ORDER4(datalen); - memcpy(&data_hash[20], &len, 4); - memcpy(hash, data_hash, 24); + if (mechanismType & SC_ALGORITHM_ECDSA_HASH_SHA1) { + unsigned char data_hash[24] = {0}; + size_t len = 0; + + sha1_digest(card, data, datalen, data_hash); + len = REVERSE_ORDER4(datalen); + memcpy(&data_hash[20], &len, 4); + memcpy(hash, data_hash, 24); + } else if (mechanismType & SC_ALGORITHM_ECDSA_HASH_SHA256) { + unsigned char data_hash[36] = {0}; + size_t len = 0; + + sha256_digest(card, data, datalen, data_hash); + len = REVERSE_ORDER4(datalen); + memcpy(&data_hash[32], &len, 4); + memcpy(hash, data_hash, 36); + } else { + return SC_ERROR_NOT_SUPPORTED; + } return SC_SUCCESS; } @@ -2155,7 +2757,7 @@ install_secret_key(struct sc_card *card, unsigned char ktype, unsigned char kid, int r; struct sc_apdu apdu; unsigned char isapp = 0x00; /* appendable */ - unsigned char tmp_data[256] = { 0 }; + unsigned char tmp_data[256] = {0}; tmp_data[0] = ktype; tmp_data[1] = kid; @@ -2212,9 +2814,9 @@ static int internal_install_pin(struct sc_card *card, sc_epass2003_wkey_data * pin) { int r; - unsigned char hash[HASH_LEN] = { 0 }; + unsigned char hash[HASH_LEN] = {0}; - r = hash_data(pin->key_data.es_secret.key_val, pin->key_data.es_secret.key_len, hash); + r = hash_data(card, pin->key_data.es_secret.key_val, pin->key_data.es_secret.key_len, hash, SC_ALGORITHM_ECDSA_HASH_SHA1); LOG_TEST_RET(card->ctx, r, "hash data failed"); r = install_secret_key(card, 0x04, pin->key_data.es_secret.kid, @@ -2245,8 +2847,7 @@ epass2003_write_key(struct sc_card *card, sc_epass2003_wkey_data * data) return internal_install_pin(card, data); else LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); - } - else { + } else { LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } @@ -2260,12 +2861,16 @@ epass2003_gen_key(struct sc_card *card, sc_epass2003_gen_key_data * data) int r; size_t len = data->key_length; struct sc_apdu apdu; - u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE] = { 0 }; - u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE] = { 0 }; + u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE] = {0}; + u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE] = {0}; LOG_FUNC_CALLED(card->ctx); - sbuf[0] = 0x01; + if (len == 256) { + sbuf[0] = 0x02; + } else { + sbuf[0] = 0x01; + } sbuf[1] = (u8) ((len >> 8) & 0xff); sbuf[2] = (u8) (len & 0xff); sbuf[3] = (u8) ((data->prkey_id >> 8) & 0xFF); @@ -2281,10 +2886,14 @@ epass2003_gen_key(struct sc_card *card, sc_epass2003_gen_key_data * data) r = sc_transmit_apdu_t(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(card->ctx, r, "generate keypair failed"); + LOG_TEST_RET(card->ctx, r, "generate key pair failed"); /* read public key */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xb4, 0x02, 0x00); + if (len == 256) { + apdu.p1 = 0x00; + } + apdu.cla = 0x80; apdu.lc = apdu.datalen = 2; apdu.data = &sbuf[5]; @@ -2300,12 +2909,46 @@ epass2003_gen_key(struct sc_card *card, sc_epass2003_gen_key_data * data) if (len < apdu.resplen) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); - data->modulus = (u8 *) malloc(len); - if (!data->modulus) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + if (256 == len) { /* ECC 256 bit */ + size_t xCoordinateLen = rbuf[1]; + size_t yCoordinateLen; + unsigned char *tmp; + + if (2 + xCoordinateLen + 1 > apdu.resplen) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); + } + yCoordinateLen = rbuf[2 + xCoordinateLen + 1]; + if (2 + xCoordinateLen + 2 + yCoordinateLen > apdu.resplen) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); + } + data->modulus_len = xCoordinateLen + yCoordinateLen; + tmp = (u8 *)malloc(data->modulus_len); + if (!tmp) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } - memcpy(data->modulus, rbuf, len); + if (0x58 == rbuf[0]) { + memcpy(tmp, &rbuf[2], xCoordinateLen); + } else { + free(tmp); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_VALID); + } + if (0x59 == rbuf[2 + xCoordinateLen]) { + memcpy(tmp + xCoordinateLen, &rbuf[2+xCoordinateLen+2], yCoordinateLen); + } else { + free(tmp); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_VALID); + } + data->modulus = tmp; + } else { + data->modulus = (u8 *) malloc(len); + if (!data->modulus) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } else { + memcpy(data->modulus, rbuf, len); + } + } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -2313,12 +2956,50 @@ epass2003_gen_key(struct sc_card *card, sc_epass2003_gen_key_data * data) static int epass2003_erase_card(struct sc_card *card) { + static const unsigned char install_magic_pin[26] = { + /* compare install_secret_key */ + 0x06, 0x01, 0x10, 0x16, 0x16, 0x16, 0x00, 0x0f, 0xff, 0x66, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + }; + static const unsigned char magic_pin[16] = { + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + }; + static const unsigned char mf_path[2] = { 0x3f, 0x00 }; + sc_apdu_t apdu; int r; LOG_FUNC_CALLED(card->ctx); - card->cache.valid = 0; - r = sc_delete_file(card, sc_get_mf_path()); + /* install magic pin */ + sc_format_apdu(card, &apdu, 0x03, 0xe3, 0x00, 0x00); + apdu.cla = 0x80; + apdu.data = install_magic_pin; + apdu.datalen = apdu.lc = sizeof(install_magic_pin); + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU install magic pin failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "install magic pin failed"); + + /* verify magic pin */ + sc_format_apdu(card, &apdu, 0x03, 0x20, 0x00, 0x01); + apdu.cla = 0; + apdu.data = magic_pin; + apdu.datalen = apdu.lc = sizeof(magic_pin); + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU verify magic pin failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "verify magic pin failed"); + + /* delete MF */ + sc_format_apdu(card, &apdu, 0x03, 0xe4, 0x00, 0x00); + apdu.cla = 0; + apdu.data = mf_path; + apdu.datalen = apdu.lc = sizeof(mf_path); + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU delete MF failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "delete MF failed"); LOG_FUNC_RETURN(card->ctx, r); @@ -2349,6 +3030,7 @@ epass2003_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) { LOG_FUNC_CALLED(card->ctx); + sc_log(card->ctx, "cmd is %0lx", cmd); switch (cmd) { case SC_CARDCTL_ENTERSAFE_WRITE_KEY: return epass2003_write_key(card, (sc_epass2003_wkey_data *) ptr); @@ -2379,7 +3061,7 @@ internal_sanitize_pin_info(struct sc_pin_cmd_pin *pin, unsigned int num) static int get_external_key_maxtries(struct sc_card *card, unsigned char *maxtries) { - unsigned char maxcounter[2] = { 0 }; + unsigned char maxcounter[2] = {0}; static const sc_path_t file_path = { {0x3f, 0x00, 0x50, 0x15, 0x9f, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, 0, @@ -2405,7 +3087,7 @@ get_external_key_retries(struct sc_card *card, unsigned char kid, unsigned char { int r; struct sc_apdu apdu; - unsigned char random[16] = { 0 }; + unsigned char random[16] = {0}; r = sc_get_challenge(card, random, 8); LOG_TEST_RET(card->ctx, r, "get challenge get_external_key_retries failed"); @@ -2420,8 +3102,7 @@ get_external_key_retries(struct sc_card *card, unsigned char kid, unsigned char if (retries && ((0x63 == (apdu.sw1 & 0xff)) && (0xC0 == (apdu.sw2 & 0xf0)))) { *retries = (apdu.sw2 & 0x0f); r = SC_SUCCESS; - } - else { + } else { LOG_TEST_RET(card->ctx, r, "get_external_key_retries failed"); r = SC_ERROR_CARD_CMD_FAILED; } @@ -2429,34 +3110,26 @@ get_external_key_retries(struct sc_card *card, unsigned char kid, unsigned char return r; } - static int -epass2003_get_challenge(sc_card_t *card, u8 *rnd, size_t count) +static int +epass2003_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { - sc_apdu_t apdu; u8 rbuf[16]; - size_t n; - int ret = SC_SUCCESS; /* if count == 0 */ + size_t out_len; + int r; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0x00, 0x00); - apdu.le = sizeof(rbuf); - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); + LOG_FUNC_CALLED(card->ctx); - while (count > 0) - { - ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); - ret = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Get challenge failed"); - if (apdu.resplen != sizeof(rbuf)) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN); - n = count < sizeof(rbuf) ? count : sizeof(rbuf); - memcpy(rnd, rbuf, n); - count -= n; - rnd += n; + r = iso_ops->get_challenge(card, rbuf, sizeof rbuf); + LOG_TEST_RET(card->ctx, r, "GET CHALLENGE cmd failed"); + + if (len < (size_t) r) { + out_len = len; + } else { + out_len = (size_t) r; } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); + memcpy(rnd, rbuf, out_len); + + LOG_FUNC_RETURN(card->ctx, (int) out_len); } @@ -2466,18 +3139,20 @@ external_key_auth(struct sc_card *card, unsigned char kid, { int r; struct sc_apdu apdu; - unsigned char random[16] = { 0 }; - unsigned char tmp_data[16] = { 0 }; - unsigned char hash[HASH_LEN] = { 0 }; - unsigned char iv[16] = { 0 }; + unsigned char random[16] = {0}; + unsigned char tmp_data[16] = {0}; + unsigned char hash[HASH_LEN] = {0}; + unsigned char iv[16] = {0}; r = sc_get_challenge(card, random, 8); LOG_TEST_RET(card->ctx, r, "get challenge external_key_auth failed"); - r = hash_data(data, datalen, hash); + r = hash_data(card, data, datalen, hash, SC_ALGORITHM_ECDSA_HASH_SHA1); LOG_TEST_RET(card->ctx, r, "hash data failed"); - des3_encrypt_cbc(hash, HASH_LEN, iv, random, 8, tmp_data); + r = des3_encrypt_cbc(card, hash, HASH_LEN, iv, random, 8, tmp_data); + LOG_TEST_RET(card->ctx, r, "encryption failed"); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x82, 0x01, 0x80 | kid); apdu.lc = apdu.datalen = 8; apdu.data = tmp_data; @@ -2493,15 +3168,15 @@ external_key_auth(struct sc_card *card, unsigned char kid, static int update_secret_key(struct sc_card *card, unsigned char ktype, unsigned char kid, - unsigned char *data, unsigned long datalen) + const unsigned char *data, unsigned long datalen) { int r; struct sc_apdu apdu; - unsigned char hash[HASH_LEN] = { 0 }; - unsigned char tmp_data[256] = { 0 }; + unsigned char hash[HASH_LEN] = {0}; + unsigned char tmp_data[256] = {0}; unsigned char maxtries = 0; - r = hash_data(data, datalen, hash); + r = hash_data(card, data, datalen, hash, SC_ALGORITHM_ECDSA_HASH_SHA1); LOG_TEST_RET(card->ctx, r, "hash data failed"); r = get_external_key_maxtries(card, &maxtries); @@ -2524,7 +3199,7 @@ update_secret_key(struct sc_card *card, unsigned char ktype, unsigned char kid, /* use external auth secret as pin */ static int -epass2003_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +epass2003_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data) { int r; u8 kid; @@ -2538,49 +3213,70 @@ epass2003_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries internal_sanitize_pin_info(&data->pin2, 1); data->flags |= SC_PIN_CMD_NEED_PADDING; kid = data->pin_reference; + + if (NULL == (unsigned char *)data->pin1.data || 0 == data->pin1.len) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); + /* get pin retries */ if (data->cmd == SC_PIN_CMD_GET_INFO) { r = get_external_key_retries(card, 0x80 | kid, &retries); if (r == SC_SUCCESS) { data->pin1.tries_left = retries; - if (tries_left) - *tries_left = retries; r = get_external_key_maxtries(card, &maxtries); LOG_TEST_RET(card->ctx, r, "get max counter failed"); data->pin1.max_tries = maxtries; } - - return r; - } - /* verify */ - if (data->cmd == SC_PIN_CMD_UNBLOCK) { + LOG_TEST_RET(card->ctx, r, "verify pin failed"); + } else if (data->cmd == SC_PIN_CMD_UNBLOCK) { /* verify */ r = external_key_auth(card, (kid + 1), (unsigned char *)data->pin1.data, data->pin1.len); LOG_TEST_RET(card->ctx, r, "verify pin failed"); - } - else { + } else if (data->cmd == SC_PIN_CMD_CHANGE || data->cmd == SC_PIN_CMD_UNBLOCK) { /* change */ + r = external_key_auth(card, kid, (unsigned char *)data->pin1.data, + data->pin1.len); + LOG_TEST_RET(card->ctx, r, "verify pin failed"); + + r = update_secret_key(card, 0x04, kid, data->pin2.data, + (unsigned long)data->pin2.len); + LOG_TEST_RET(card->ctx, r, "change pin failed"); + } else { r = external_key_auth(card, kid, (unsigned char *)data->pin1.data, data->pin1.len); - get_external_key_retries(card, 0x80 | kid, &retries); + LOG_TEST_RET(card->ctx, r, "verify pin failed"); + + r = get_external_key_retries(card, 0x80 | kid, &retries); if (retries < pin_low) sc_log(card->ctx, "Verification failed (remaining tries: %d)", retries); + LOG_TEST_RET(card->ctx, r, "verify pin failed"); } - LOG_TEST_RET(card->ctx, r, "verify pin failed"); - - if (data->cmd == SC_PIN_CMD_CHANGE || data->cmd == SC_PIN_CMD_UNBLOCK) { - /* change */ - r = update_secret_key(card, 0x04, kid, (unsigned char *)data->pin2.data, - (unsigned long)data->pin2.len); - LOG_TEST_RET(card->ctx, r, "verify pin failed"); + if (r == SC_SUCCESS) { + data->pin1.logged_in = SC_PIN_STATE_LOGGED_IN; } return r; } +static int +epass2003_logout(struct sc_card *card) +{ + epass2003_exdata *exdata = NULL; + + if (!card->drv_data) + return SC_ERROR_INVALID_ARGUMENTS; + + exdata = (epass2003_exdata *)card->drv_data; + if (exdata->sm) { + sc_sm_stop(card); + return epass2003_refresh(card); + } + + return SC_ERROR_NOT_SUPPORTED; +} + static struct sc_card_driver *sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); @@ -2610,6 +3306,7 @@ static struct sc_card_driver *sc_get_driver(void) epass2003_ops.pin_cmd = epass2003_pin_cmd; epass2003_ops.check_sw = epass2003_check_sw; epass2003_ops.get_challenge = epass2003_get_challenge; + epass2003_ops.logout = epass2003_logout; return &epass2003_drv; } diff --git a/src/libopensc/card-esteid2018.c b/src/libopensc/card-esteid2018.c new file mode 100644 index 0000000000..e7b204ae26 --- /dev/null +++ b/src/libopensc/card-esteid2018.c @@ -0,0 +1,322 @@ +/* + * Driver for EstEID card issued from December 2018. + * + * Copyright (C) 2019, Martin Paljak + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "asn1.h" +#include "gp.h" +#include "internal.h" + +/* Helping defines */ +#define SIGNATURE_PAYLOAD_SIZE 0x30 +#define SIGN_REF 0x10 +#define PIN1_REF 0x01 +#define PIN2_REF 0x85 +#define PUK_REF 0x02 + +/* + * EstEID: https://www.id.ee/wp-content/uploads/2025/10/id1developerguide2025.pdf + */ +static const struct sc_atr_table esteid_atrs[] = { + {"3b:db:96:00:80:b1:fe:45:1f:83:00:12:23:3f:53:65:49:44:0f:90:00:f1", NULL, "EstEID 2018", SC_CARD_TYPE_ESTEID_2018, 0, NULL}, + {"3b:db:96:00:80:b1:fe:45:1f:83:00:12:42:8f:53:65:49:44:0f:90:00:20", NULL, "Latvian eID 2018", SC_CARD_TYPE_LATEID_2018, 0, NULL}, + {"3b:dc:96:00:80:b1:fe:45:1f:83:00:12:23:3f:54:65:49:44:32:0f:90:00:c3", NULL, "EstEID 2018 v2", SC_CARD_TYPE_ESTEID_2018_V2_2025, 0, NULL}, + {"3b:dc:96:00:80:b1:fe:45:1f:83:00:12:42:8f:54:65:49:44:32:0f:90:00:12", NULL, "Latvian eID 2018 v2", SC_CARD_TYPE_LATEID_2018_V2_2025, 0, NULL}, + {NULL, NULL, NULL, 0, 0, NULL} +}; + +static const struct sc_aid IASECC_AID = { + {0xA0, 0x00, 0x00, 0x00, 0x77, 0x01, 0x08, 0x00, 0x07, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x01, 0x00}, + 16 +}; + +static const struct sc_path MF = { + {0x3f, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + 2, 0, 0, SC_PATH_TYPE_PATH, {{0}, 0} +}; + +static const struct sc_path adf2 = { + {0x3f, 0x00, 0xAD, 0xF2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + 4, 0, 0, SC_PATH_TYPE_PATH, {{0}, 0} +}; + +static const struct sc_card_operations *iso_ops = NULL; +static struct sc_card_operations esteid_ops; + +static struct sc_card_driver esteid2018_driver = {"EstEID 2018", "esteid2018", &esteid_ops, NULL, 0, NULL}; + +struct esteid_priv_data { + sc_security_env_t sec_env; /* current security environment */ +}; + +#define DRVDATA(card) ((struct esteid_priv_data *)((card)->drv_data)) + +#define SC_TRANSMIT_TEST_RET(card, apdu, text) \ + do { \ + LOG_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed"); \ + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), text); \ + } while (0) + +static int esteid_match_card(sc_card_t *card) { + int i = _sc_match_atr(card, esteid_atrs, &card->type); + + if (i >= 0 && gp_select_aid(card, &IASECC_AID) == SC_SUCCESS) { + card->name = esteid_atrs[i].name; + return 1; + } + return 0; +} + +static int esteid_select_file(struct sc_card *card, const struct sc_path *in_path, struct sc_file **file_out) { + const u8 *path = in_path->value; + u8 resp[SC_MAX_APDU_RESP_SIZE]; + size_t resplen = sizeof(resp); + int r; + struct sc_file *file = NULL; + struct sc_apdu apdu; + + LOG_FUNC_CALLED(card->ctx); + + // Only support full paths + if (in_path->type != SC_PATH_TYPE_PATH) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + if (in_path->len % 2 != 0) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + for (size_t pathlen = in_path->len; pathlen >= 2; pathlen -= 2, path += 2) { + if (pathlen == 2 && memcmp(path, "\x3F\x00", 2) == 0) { + sc_format_apdu_ex(&apdu, card->cla, 0xA4, 0x00, 0x0C, path, 0, NULL, 0); + SC_TRANSMIT_TEST_RET(card, apdu, "MF select failed"); + } else if (pathlen == 2 && path[0] == 0xAD) { + sc_format_apdu_ex(&apdu, card->cla, 0xA4, 0x01, 0x0C, path, 2, NULL, 0); + SC_TRANSMIT_TEST_RET(card, apdu, "DF select failed"); + } else { + sc_format_apdu_ex(&apdu, card->cla, 0xA4, 0x09, 0x04, path, pathlen, resp, resplen); + SC_TRANSMIT_TEST_RET(card, apdu, "EF select failed"); + + if (file_out != NULL) { + file = sc_file_new(); + if (file == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + r = iso_ops->process_fci(card, file, resp, apdu.resplen); + if (r != SC_SUCCESS) { + sc_file_free(file); + } else { + *file_out = file; + } + LOG_TEST_RET(card->ctx, r, "Process fci failed"); + } + break; + } + } + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int esteid_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { + struct esteid_priv_data *priv; + sc_security_env_t new_env = {0}; + + LOG_FUNC_CALLED(card->ctx); + + if (env == NULL || env->key_ref_len != 1) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + + sc_log(card->ctx, "algo: %lu operation: %d keyref: %d", env->algorithm, env->operation, env->key_ref[0]); + + if (env->algorithm != SC_ALGORITHM_EC) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + new_env = *env; + switch (env->operation) { + case SC_SEC_OPERATION_SIGN: + if (env->key_ref[0] & SIGN_REF) { + new_env.algorithm_ref = 0x54; + } else { + new_env.operation = SC_SEC_OPERATION_AUTHENTICATE; + new_env.algorithm_ref = 0x04; + } + break; + case SC_SEC_OPERATION_DERIVE: + new_env.algorithm_ref = 0x0B; + break; + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + new_env.flags |= SC_SEC_ENV_ALG_REF_PRESENT; + new_env.flags &= ~SC_SEC_ENV_FILE_REF_PRESENT; + LOG_TEST_RET(card->ctx, iso_ops->set_security_env(card, &new_env, se_num), "Failed to set security environment"); + + priv = DRVDATA(card); + priv->sec_env = *env; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int esteid_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, u8 *out, size_t outlen) { + struct esteid_priv_data *priv = DRVDATA(card); + struct sc_security_env *env = NULL; + struct sc_apdu apdu; + u8 sbuf[SIGNATURE_PAYLOAD_SIZE] = {0}; + size_t le = MIN(SC_MAX_APDU_RESP_SIZE, MIN(SIGNATURE_PAYLOAD_SIZE * 2, outlen)); + + LOG_FUNC_CALLED(card->ctx); + if (data == NULL || out == NULL || datalen > SIGNATURE_PAYLOAD_SIZE) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + env = &priv->sec_env; + // left-pad if necessary + memcpy(&sbuf[SIGNATURE_PAYLOAD_SIZE - datalen], data, MIN(datalen, SIGNATURE_PAYLOAD_SIZE)); + datalen = SIGNATURE_PAYLOAD_SIZE; + + if ((env->key_ref[0] & SIGN_REF) > 0) { + sc_format_apdu_ex(&apdu, 0x00, 0x2A, 0x9E, 0x9A, sbuf, datalen, out, le); + } else { + sc_format_apdu_ex(&apdu, 0x00, 0x88, 0, 0, sbuf, datalen, out, le); + } + + SC_TRANSMIT_TEST_RET(card, apdu, "PSO CDS/INTERNAL AUTHENTICATE failed"); + + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); +} + +static int esteid_get_pin_remaining_tries(sc_card_t *card, int pin_reference) { + const u8 get_pin_info[] = {0x4D, 0x08, 0x70, 0x06, 0xBF, 0x81, pin_reference & 0x0F, 0x02, 0xA0, 0x80}; // mask out local/global + struct sc_apdu apdu; + u8 apdu_resp[SC_MAX_APDU_RESP_SIZE]; + LOG_FUNC_CALLED(card->ctx); + + // We don't get the file information here, so we need to be ugly + if (pin_reference == PIN1_REF || pin_reference == PUK_REF) { + LOG_TEST_RET(card->ctx, esteid_select_file(card, &MF, NULL), "Cannot select MF"); + } else if (pin_reference == PIN2_REF) { + LOG_TEST_RET(card->ctx, esteid_select_file(card, &adf2, NULL), "Cannot select QSCD AID"); + } else { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + + sc_format_apdu_ex(&apdu, 0x00, 0xCB, 0x3F, 0xFF, get_pin_info, sizeof(get_pin_info), apdu_resp, sizeof(apdu_resp)); + SC_TRANSMIT_TEST_RET(card, apdu, "GET DATA(pin info) failed"); + if (apdu.resplen < 32) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + + // XXX: sc_asn1_find_tag with the following payload (to get to tag 0x9B): + // https://lapo.it/asn1js/#cB6_gQEaoBiaAQObAQOhEIwG8wAAc0MAnAbzAABzQwA + return (int)apdu_resp[13]; +} + +static int esteid_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) { + int r; + struct sc_pin_cmd_data tmp; + LOG_FUNC_CALLED(card->ctx); + sc_log(card->ctx, "PIN CMD is %d", data->cmd); + if (data->cmd == SC_PIN_CMD_GET_INFO) { + sc_log(card->ctx, "SC_PIN_CMD_GET_INFO for %d", data->pin_reference); + r = esteid_get_pin_remaining_tries(card, data->pin_reference); + LOG_TEST_RET(card->ctx, r, "GET DATA(pin info) failed"); + + data->pin1.tries_left = r; + data->pin1.max_tries = -1; // "no support, which means the one set in PKCS#15 emulation sticks + data->pin1.logged_in = SC_PIN_STATE_UNKNOWN; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + } else if (data->cmd == SC_PIN_CMD_UNBLOCK) { + // Verify PUK, then issue UNBLOCK + // VERIFY + tmp = *data; + tmp.cmd = SC_PIN_CMD_VERIFY; + tmp.pin_reference = PUK_REF; + tmp.pin2.len = 0; + r = iso_ops->pin_cmd(card, &tmp); + LOG_TEST_RET(card->ctx, r, "VERIFY during unblock failed"); + + if (data->pin_reference == PIN2_REF) { + LOG_TEST_RET(card->ctx, esteid_select_file(card, &adf2, NULL), "Cannot select QSCD AID"); + } + // UNBLOCK + tmp = *data; + tmp.cmd = SC_PIN_CMD_UNBLOCK; + tmp.pin1.len = 0; + r = iso_ops->pin_cmd(card, &tmp); + sc_mem_clear(&tmp, sizeof(tmp)); + LOG_FUNC_RETURN(card->ctx, r); + } + + LOG_FUNC_RETURN(card->ctx, iso_ops->pin_cmd(card, data)); +} + +static int esteid_init(sc_card_t *card) { + unsigned long flags, ext_flags; + struct esteid_priv_data *priv; + + priv = calloc(1, sizeof *priv); + if (!priv) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + card->drv_data = priv; + card->max_recv_size = 233; // XXX: empirical, not documented + // Workaround for the 2018 v2 card, with reader Alcor Micro AU9540 + if (card->type == SC_CARD_TYPE_ESTEID_2018_V2_2025) { + card->max_recv_size = 0xC0; + } + + flags = SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDH_CDH_RAW | SC_ALGORITHM_ECDSA_HASH_NONE; + ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES; + + _sc_card_add_ec_alg(card, 384, flags, ext_flags, NULL); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int esteid_finish(sc_card_t *card) { + if (card != NULL) + free(DRVDATA(card)); + return 0; +} + +static int esteid_logout(sc_card_t *card) { + return gp_select_aid(card, &IASECC_AID); +} + +struct sc_card_driver *sc_get_esteid2018_driver(void) { + struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); + + if (iso_ops == NULL) + iso_ops = iso_drv->ops; + + esteid_ops = *iso_drv->ops; + esteid_ops.match_card = esteid_match_card; + esteid_ops.init = esteid_init; + esteid_ops.finish = esteid_finish; + + esteid_ops.select_file = esteid_select_file; + + esteid_ops.set_security_env = esteid_set_security_env; + esteid_ops.compute_signature = esteid_compute_signature; + esteid_ops.pin_cmd = esteid_pin_cmd; + esteid_ops.logout = esteid_logout; + + return &esteid2018_driver; +} diff --git a/src/libopensc/card-esteid2025.c b/src/libopensc/card-esteid2025.c new file mode 100644 index 0000000000..4ebcdc93db --- /dev/null +++ b/src/libopensc/card-esteid2025.c @@ -0,0 +1,238 @@ +/* + * Driver for EstEID card issued from December 2025. + * + * Copyright (C) 2025, Raul Metsma + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "asn1.h" +#include "gp.h" +#include "internal.h" + +#define SIGNATURE_PAYLOAD_SIZE 0x30 + +/* + * EstEID: https://www.id.ee/wp-content/uploads/2025/11/ged24035_015_tdc_est_eid_developer_guide.pdf + * FinEID: + * * App 4.0: https://dvv.fi/documents/16079645/17324992/S1v40+(1).pdf/56a167fe-9f26-1fda-7d76-cfbbb29d184e/S1v40+(1).pdf + * * Imp 4.0: https://dvv.fi/documents/16079645/17324992/S4-1v40.pdf/55bddc08-6893-b4b4-73fa-24dced600198/S4-1v40.pdf + * * Imp 4.1: https://dvv.fi/documents/16079645/17324992/S4-1v41.pdf/63afc997-ed16-a00b-bdca-e9971b164137/S4-1v41.pdf + */ +static const struct sc_atr_table esteid_atrs[] = { + {"3b:ff:96:00:00:80:31:fe:43:80:31:b8:53:65:49:44:64:b0:85:05:10:12:23:3f:1d", NULL, "EstEID 2025", SC_CARD_TYPE_ESTEID_2025, 0, NULL}, + {"3b:7f:96:00:00:80:31:b8:65:b0:85:05:00:11:12:24:60:82:90:00", NULL, "FinEID 2022", SC_CARD_TYPE_FINEID_2022, 0, NULL}, + {"3b:7f:96:00:00:80:31:b8:65:b0:85:05:10:24:12:24:60:82:90:00", NULL, "FinEID 2025", SC_CARD_TYPE_FINEID_2025, 0, NULL}, + {NULL, NULL, NULL, 0, 0, NULL} +}; + +static const struct sc_aid THALES_AID = { + {0xA0, 0x00, 0x00, 0x00, 0x63, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35}, + 12 +}; + +static const struct sc_card_operations *iso_ops = NULL; +static struct sc_card_operations esteid_ops; + +static struct sc_card_driver esteid2025_driver = {"EstEID 2025", "esteid2025", &esteid_ops, NULL, 0, NULL}; + +#define SC_TRANSMIT_TEST_RET(card, apdu, text) \ + do { \ + LOG_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed"); \ + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), text); \ + } while (0) + +static int +esteid_match_card(sc_card_t *card) +{ + int i = _sc_match_atr(card, esteid_atrs, &card->type); + + if (i >= 0 && gp_select_aid(card, &THALES_AID) == SC_SUCCESS) { + card->name = esteid_atrs[i].name; + return 1; + } + return 0; +} + +static int +esteid_select_file(struct sc_card *card, const struct sc_path *in_path, struct sc_file **file_out) +{ + u8 resp[SC_MAX_APDU_RESP_SIZE]; + size_t resplen = sizeof(resp); + int r; + struct sc_file *file = NULL; + struct sc_apdu apdu; + + LOG_FUNC_CALLED(card->ctx); + + // Only support full paths + if (in_path->type != SC_PATH_TYPE_PATH) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + sc_format_apdu_ex(&apdu, card->cla, 0xA4, 0x08, 0x04, in_path->value, in_path->len, resp, resplen); + SC_TRANSMIT_TEST_RET(card, apdu, "SELECT failed"); + if (file_out != NULL) { + file = sc_file_new(); + if (file == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + r = iso_ops->process_fci(card, file, resp, resplen); + if (r != SC_SUCCESS) { + sc_file_free(file); + } + LOG_TEST_RET(card->ctx, r, "Process fci failed"); + *file_out = file; + } + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +esteid_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) +{ + struct sc_apdu apdu; + u8 cse_crt_sig[] = {0x80, 0x01, 0x54, 0x84, 0x01, 0x00}; + u8 cse_crt_der[] = {0x84, 0x01, 0x00}; + + LOG_FUNC_CALLED(card->ctx); + + if (env == NULL || env->key_ref_len != 1) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + + sc_log(card->ctx, "algo: %lu operation: %d keyref: %d", env->algorithm, env->operation, env->key_ref[0]); + + if (env->algorithm == SC_ALGORITHM_EC && env->operation == SC_SEC_OPERATION_SIGN) { + cse_crt_sig[5] = env->key_ref[0]; + sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xB6, cse_crt_sig, sizeof(cse_crt_sig), NULL, 0); + } else if (env->algorithm == SC_ALGORITHM_EC && env->operation == SC_SEC_OPERATION_DERIVE) { + cse_crt_der[2] = env->key_ref[0]; + sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xB8, cse_crt_der, sizeof(cse_crt_der), NULL, 0); + } else { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + SC_TRANSMIT_TEST_RET(card, apdu, "SET SECURITY ENV failed"); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +esteid_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, u8 *out, size_t outlen) +{ + struct sc_apdu apdu; + u8 sbuf[SIGNATURE_PAYLOAD_SIZE + 2] = {0x90, SIGNATURE_PAYLOAD_SIZE}; + size_t le = MIN(SC_MAX_APDU_RESP_SIZE, MIN(SIGNATURE_PAYLOAD_SIZE * 2, outlen)); + + LOG_FUNC_CALLED(card->ctx); + if (data == NULL || out == NULL || datalen > SIGNATURE_PAYLOAD_SIZE) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + // left-pad if necessary + memcpy(&sbuf[SIGNATURE_PAYLOAD_SIZE + 2 - datalen], data, MIN(datalen, SIGNATURE_PAYLOAD_SIZE)); + datalen = SIGNATURE_PAYLOAD_SIZE + 2; + + sc_format_apdu_ex(&apdu, 0x00, 0x2A, 0x90, 0xA0, sbuf, datalen, NULL, 0); + SC_TRANSMIT_TEST_RET(card, apdu, "PSO Set Hash failed"); + + sc_format_apdu_ex(&apdu, 0x00, 0x2A, 0x9E, 0x9A, NULL, 0, out, le); + SC_TRANSMIT_TEST_RET(card, apdu, "PSO Compute Digital Signature failed"); + + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); +} + +static int +esteid_get_pin_info(sc_card_t *card, struct sc_pin_cmd_data *data) +{ + const u8 get_pin_info[] = {0xA0, 0x03, 0x83, 0x01, data->pin_reference}; + struct sc_apdu apdu; + u8 apdu_resp[SC_MAX_APDU_RESP_SIZE]; + size_t taglen; + LOG_FUNC_CALLED(card->ctx); + + sc_format_apdu_ex(&apdu, 0x00, 0xCB, 0x00, 0xFF, get_pin_info, sizeof(get_pin_info), apdu_resp, sizeof(apdu_resp)); + SC_TRANSMIT_TEST_RET(card, apdu, "GET DATA(pin info) failed"); + if (apdu.resplen < 3 || apdu.resp[0] != 0xA0) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + + const u8 *tag = sc_asn1_find_tag(card->ctx, apdu_resp + 2, apdu.resplen - 2, 0xDF21, &taglen); + if (tag == NULL || taglen == 0) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + data->pin1.tries_left = tag[0]; + data->pin1.max_tries = -1; // "no support, which means the one set in PKCS#15 emulation sticks + data->pin1.logged_in = SC_PIN_STATE_UNKNOWN; + tag += taglen; + tag = sc_asn1_find_tag(card->ctx, tag, apdu.resplen - (tag - apdu_resp), 0xDF2F, &taglen); + if (tag != NULL && taglen == 1 && tag[0] == 0x00) { + data->pin1.logged_in |= SC_PIN_STATE_NEEDS_CHANGE; + } + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +esteid_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) +{ + LOG_FUNC_CALLED(card->ctx); + sc_log(card->ctx, "PIN CMD is %d", data->cmd); + if (data->cmd == SC_PIN_CMD_GET_INFO) { + sc_log(card->ctx, "SC_PIN_CMD_GET_INFO for %d", data->pin_reference); + LOG_FUNC_RETURN(card->ctx, esteid_get_pin_info(card, data)); + } + LOG_FUNC_RETURN(card->ctx, iso_ops->pin_cmd(card, data)); +} + +static int +esteid_init(sc_card_t *card) +{ + unsigned long flags, ext_flags; + + flags = SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDH_CDH_RAW | SC_ALGORITHM_ECDSA_HASH_NONE; + ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES; + + _sc_card_add_ec_alg(card, 384, flags, ext_flags, NULL); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +esteid_logout(sc_card_t *card) +{ + return gp_select_aid(card, &THALES_AID); +} + +struct sc_card_driver * +sc_get_esteid2025_driver(void) +{ + struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); + + if (iso_ops == NULL) + iso_ops = iso_drv->ops; + + esteid_ops = *iso_drv->ops; + esteid_ops.match_card = esteid_match_card; + esteid_ops.init = esteid_init; + + esteid_ops.select_file = esteid_select_file; + + esteid_ops.set_security_env = esteid_set_security_env; + esteid_ops.compute_signature = esteid_compute_signature; + esteid_ops.pin_cmd = esteid_pin_cmd; + esteid_ops.logout = esteid_logout; + + return &esteid2025_driver; +} diff --git a/src/libopensc/card-flex.c b/src/libopensc/card-flex.c index 1dd2769409..47fb7f33f7 100644 --- a/src/libopensc/card-flex.c +++ b/src/libopensc/card-flex.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -31,7 +31,7 @@ #define FLAG_KEYGEN 0x80000000 #define IS_CYBERFLEX(card) (card->type == SC_CARD_TYPE_FLEX_CYBER) -static struct sc_atr_table flex_atrs[] = { +static const struct sc_atr_table flex_atrs[] = { /* Cryptoflex */ /* 8k win2000 */ { "3b:95:15:40:20:68:01:02:00:00", NULL, "Cryptoflex 8K", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL }, @@ -64,7 +64,7 @@ static struct sc_atr_table flex_atrs[] = { /* 32K e-gate v4 */ { "3B:95:18:40:FF:62:04:01:01:05", NULL, "Cryptoflex 32K e-gate v4", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL }, - /* new cryptoflex 32k card - atr looks very similiar to old 8k card */ + /* new cryptoflex 32k card - atr looks very similar to old 8k card */ { "3b:95:15:40:ff:68:01:02:45:47", NULL, "Cryptoflex 32K", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL }, { "3B:E2:00:00:40:20:49:06", NULL, "Cryptoflex", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL }, @@ -187,7 +187,7 @@ static int flex_init(sc_card_t *card) /* FIXME: Card type detection */ if (1) { unsigned long flags; - + flags = SC_ALGORITHM_RSA_RAW; flags |= SC_ALGORITHM_RSA_HASH_NONE; if (card->flags & FLAG_KEYGEN) @@ -272,7 +272,7 @@ cryptoflex_process_file_attrs(sc_card_t *card, sc_file_t *file, const u8 *p = buf + 2; u8 b1, b2; int is_mf = 0; - + if (buflen < 14) return -1; b1 = *p++; @@ -304,7 +304,7 @@ cryptoflex_process_file_attrs(sc_card_t *card, sc_file_t *file, file->type = SC_FILE_TYPE_DF; break; default: - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "invalid file type: 0x%02X\n", *p); + sc_log(ctx, "invalid file type: 0x%02X\n", *p); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } p += 2; @@ -368,7 +368,7 @@ cyberflex_process_file_attrs(sc_card_t *card, sc_file_t *file, file->type = SC_FILE_TYPE_WORKING_EF; break; default: - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "invalid file type: 0x%02X\n", *p); + sc_log(ctx, "invalid file type: 0x%02X\n", *p); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } @@ -415,7 +415,7 @@ cyberflex_process_file_attrs(sc_card_t *card, sc_file_t *file, case 0x04: break; default: - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "invalid file type: 0x%02X\n", *p); + sc_log(ctx, "invalid file type: 0x%02X\n", *p); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } switch (file->ef_structure) { @@ -433,80 +433,6 @@ cyberflex_process_file_attrs(sc_card_t *card, sc_file_t *file, return 0; } -static int check_path(sc_card_t *card, const u8 **pathptr, size_t *pathlen, - int need_info) -{ - const u8 *curptr = card->cache.current_path.value; - const u8 *ptr = *pathptr; - size_t curlen = card->cache.current_path.len; - size_t len = *pathlen; - - if (curlen < 2) - return 0; - if (len < 2) - return 0; - if (memcmp(ptr, "\x3F\x00", 2) != 0) { - /* Skip the MF id */ - curptr += 2; - curlen -= 2; - } - if (len == curlen && memcmp(ptr, curptr, len) == 0) { - if (need_info) - return 0; - *pathptr = ptr + len; - *pathlen = 0; - return 1; - } - if (curlen < len && memcmp(ptr, curptr, curlen) == 0) { - *pathptr = ptr + curlen; - *pathlen = len - curlen; - return 1; - } - /* FIXME: Build additional logic */ - return 0; -} - -static void cache_path(sc_card_t *card, const sc_path_t *path, - int result) -{ - sc_path_t *curpath = &card->cache.current_path; - - if (result < 0) { - curpath->len = 0; - return; - } - - switch (path->type) { - case SC_PATH_TYPE_FILE_ID: - if (path->value[0] == 0x3F && path->value[1] == 0x00) - sc_format_path("3F00", curpath); - else { - if (curpath->len + 2 > SC_MAX_PATH_SIZE) { - curpath->len = 0; - return; - } - memcpy(curpath->value + curpath->len, path->value, 2); - curpath->len += 2; - } - break; - case SC_PATH_TYPE_PATH: - curpath->len = 0; - if (path->value[0] != 0x3F || path->value[1] != 0) - sc_format_path("3F00", curpath); - if (curpath->len + path->len > SC_MAX_PATH_SIZE) { - curpath->len = 0; - return; - } - memcpy(curpath->value + curpath->len, path->value, path->len); - curpath->len += path->len; - break; - case SC_PATH_TYPE_DF_NAME: - /* All bets are off */ - curpath->len = 0; - break; - } -} - static int select_file_id(sc_card_t *card, const u8 *buf, size_t buflen, u8 p1, sc_file_t **file_out) { @@ -515,8 +441,8 @@ static int select_file_id(sc_card_t *card, const u8 *buf, size_t buflen, u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_file_t *file; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "called, p1=%u\n", p1); - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "path", buf, buflen); + sc_log(card->ctx, "called, p1=%u\n", p1); + sc_log_hex(card->ctx, "path", buf, buflen); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, p1, 0); apdu.resp = rbuf; @@ -532,9 +458,9 @@ static int select_file_id(sc_card_t *card, const u8 *buf, size_t buflen, apdu.le = 0; } r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); + LOG_TEST_RET(card->ctx, r, "Card returned error"); if (file_out == NULL) return 0; @@ -542,12 +468,12 @@ static int select_file_id(sc_card_t *card, const u8 *buf, size_t buflen, if (apdu.resplen < 14) return SC_ERROR_UNKNOWN_DATA_RECEIVED; if (apdu.resp[0] == 0x6F) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unsupported: card returned FCI\n"); + sc_log(card->ctx, "unsupported: card returned FCI\n"); return SC_ERROR_UNKNOWN_DATA_RECEIVED; /* FIXME */ } file = sc_file_new(); if (file == NULL) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); /* We abuse process_fci here even though it's not the real FCI. */ r = card->ops->process_fci(card, file, apdu.resp, apdu.resplen); @@ -566,39 +492,30 @@ static int flex_select_file(sc_card_t *card, const sc_path_t *path, int r; const u8 *pathptr = path->value; size_t pathlen = path->len; - int locked = 0, magic_done; + int locked = 0; u8 p1 = 0; - char pbuf[SC_MAX_PATH_STRING_SIZE]; - - - r = sc_path_print(pbuf, sizeof(pbuf), &card->cache.current_path); - if (r != SC_SUCCESS) - pbuf[0] = '\0'; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "called, cached path=%s\n", pbuf); switch (path->type) { case SC_PATH_TYPE_PATH: if ((pathlen & 1) != 0) /* not divisible by 2 */ return SC_ERROR_INVALID_ARGUMENTS; - magic_done = check_path(card, &pathptr, &pathlen, file_out != NULL); if (pathlen == 0) return 0; if (pathlen != 2 || memcmp(pathptr, "\x3F\x00", 2) != 0) { locked = 1; r = sc_lock(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); - if (!magic_done && memcmp(pathptr, "\x3F\x00", 2) != 0) { + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); + if (memcmp(pathptr, "\x3F\x00", 2) != 0) { r = select_file_id(card, (const u8 *) "\x3F\x00", 2, 0, NULL); if (r) sc_unlock(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select Master File (MF)"); + LOG_TEST_RET(card->ctx, r, "Unable to select Master File (MF)"); } while (pathlen > 2) { r = select_file_id(card, pathptr, 2, 0, NULL); if (r) sc_unlock(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select DF"); + LOG_TEST_RET(card->ctx, r, "Unable to select DF"); pathptr += 2; pathlen -= 2; } @@ -615,7 +532,6 @@ static int flex_select_file(sc_card_t *card, const sc_path_t *path, r = select_file_id(card, pathptr, pathlen, p1, file_out); if (locked) sc_unlock(card); - cache_path(card, path, r); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } @@ -625,7 +541,7 @@ static int cryptoflex_list_files(sc_card_t *card, u8 *buf, size_t buflen) u8 rbuf[4]; int r; size_t count = 0; - + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA8, 0, 0); apdu.cla = 0xF0; apdu.le = 4; @@ -641,7 +557,7 @@ static int cryptoflex_list_files(sc_card_t *card, u8 *buf, size_t buflen) if (r) return r; if (apdu.resplen != 4) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "expected 4 bytes, got %"SC_FORMAT_LEN_SIZE_T"u.\n", apdu.resplen); return SC_ERROR_UNKNOWN_DATA_RECEIVED; @@ -651,7 +567,7 @@ static int cryptoflex_list_files(sc_card_t *card, u8 *buf, size_t buflen) count += 2; buflen -= 2; } - return count; + return (int)count; } /* @@ -661,9 +577,9 @@ static int cyberflex_list_files(sc_card_t *card, u8 *buf, size_t buflen) { sc_apdu_t apdu; u8 rbuf[6]; - int r; - size_t count = 0, p2 = 0; - + int r, p2 = 0; + size_t count = 0; + while (buflen > 2) { sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA8, 0, ++p2); apdu.le = 6; @@ -678,7 +594,7 @@ static int cyberflex_list_files(sc_card_t *card, u8 *buf, size_t buflen) if (r) return r; if (apdu.resplen != 6) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "expected 6 bytes, got %"SC_FORMAT_LEN_SIZE_T"u.\n", apdu.resplen); return SC_ERROR_UNKNOWN_DATA_RECEIVED; @@ -688,7 +604,7 @@ static int cyberflex_list_files(sc_card_t *card, u8 *buf, size_t buflen) count += 2; buflen -= 2; } - return count; + return (int)count; } static int flex_delete_file(sc_card_t *card, const sc_path_t *path) @@ -698,8 +614,8 @@ static int flex_delete_file(sc_card_t *card, const sc_path_t *path) SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type has to be SC_PATH_TYPE_FILE_ID\n"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + sc_log(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); if (!IS_CYBERFLEX(card)) @@ -707,9 +623,9 @@ static int flex_delete_file(sc_card_t *card, const sc_path_t *path) apdu.data = path->value; apdu.lc = 2; apdu.datalen = 2; - + r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } @@ -783,7 +699,7 @@ cryptoflex_construct_file_attrs(sc_card_t *card, const sc_file_t *file, p[6] = 0x06; break; default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid EF structure\n"); + sc_log(card->ctx, "Invalid EF structure\n"); return -1; } p[7] = 0xFF; /* allow Decrease and Increase */ @@ -809,13 +725,13 @@ cryptoflex_construct_file_attrs(sc_card_t *card, const sc_file_t *file, continue; entry = sc_file_get_acl_entry(file, ops[i]); r = acl_to_ac_nibble(entry); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Invalid ACL value"); + LOG_TEST_RET(card->ctx, r, "Invalid ACL value"); /* Do some magic to get the nibbles right */ p[8 + i/2] |= (r & 0x0F) << (((i+1) % 2) * 4); r = acl_to_keynum_nibble(entry); p[13 + i/2] |= (r & 0x0F) << (((i+1) % 2) * 4); } - p[11] = (file->status & SC_FILE_STATUS_INVALIDATED) ? 0x00 : 0x01; + p[11] = (file->status == SC_FILE_STATUS_INVALIDATED) ? 0x00 : 0x01; if (file->type != SC_FILE_TYPE_DF && (file->ef_structure == SC_FILE_EF_LINEAR_FIXED || file->ef_structure == SC_FILE_EF_CYCLIC)) @@ -849,7 +765,7 @@ cyberflex_construct_file_attrs(sc_card_t *card, const sc_file_t *file, break; } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Creating %02x:%02x, size %"SC_FORMAT_LEN_SIZE_T"u %02"SC_FORMAT_LEN_SIZE_T"x:%02"SC_FORMAT_LEN_SIZE_T"x\n", file->id >> 8, file->id & 0xFF, @@ -878,12 +794,12 @@ cyberflex_construct_file_attrs(sc_card_t *card, const sc_file_t *file, p[4] = 0x1D; break; default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid EF structure\n"); + sc_log(card->ctx, "Invalid EF structure\n"); return -1; } p[5] = 0x01; /* status?? */ p[6] = p[7] = 0; - + *buflen = 16; p[8] = p[9] = p[11] = 0xFF; @@ -897,17 +813,17 @@ static int flex_create_file(sc_card_t *card, sc_file_t *file) size_t sendlen; int r, rec_nr; sc_apdu_t apdu; - + /* Build the file attrs. These are not the real FCI bytes * in the standard sense, but its a convenient way of * abstracting the Cryptoflex/Cyberflex differences */ r = card->ops->construct_fci(card, file, sbuf, &sendlen); if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File structure encoding failed.\n"); + sc_log(card->ctx, "File structure encoding failed.\n"); return SC_ERROR_INVALID_ARGUMENTS; } if (file->type != SC_FILE_TYPE_DF && file->ef_structure != SC_FILE_EF_TRANSPARENT) - rec_nr = file->record_count; + rec_nr = (int)file->record_count; else rec_nr = 0; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, rec_nr); @@ -918,55 +834,47 @@ static int flex_create_file(sc_card_t *card, sc_file_t *file) apdu.lc = sendlen; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - if (card->cache.valid) { - u8 file_id[2]; - - file_id[0] = file->id >> 8; - file_id[1] = file->id & 0xFF; - if (card->cache.current_path.len != 0) - sc_append_path_id(&card->cache.current_path, file_id, 2); - } - return 0; + LOG_TEST_RET(card->ctx, r, "Card returned error"); + return SC_SUCCESS; } static int flex_set_security_env(sc_card_t *card, const sc_security_env_t *env, - int se_num) + int se_num) { struct flex_private_data *prv = (struct flex_private_data *) card->drv_data; if (env->operation != SC_SEC_OPERATION_SIGN && env->operation != SC_SEC_OPERATION_DECIPHER) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid crypto operation supplied.\n"); + sc_log(card->ctx, "Invalid crypto operation supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } if (env->algorithm != SC_ALGORITHM_RSA) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid crypto algorithm supplied.\n"); + sc_log(card->ctx, "Invalid crypto algorithm supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } if ((env->algorithm_flags & SC_ALGORITHM_RSA_PADS) || (env->algorithm_flags & SC_ALGORITHM_RSA_HASHES)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Card supports only raw RSA.\n"); + sc_log(card->ctx, "Card supports only raw RSA.\n"); return SC_ERROR_NOT_SUPPORTED; } if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { if (env->key_ref_len != 1 || (env->key_ref[0] != 0 && env->key_ref[0] != 1)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid key reference supplied.\n"); + sc_log(card->ctx, "Invalid key reference supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } prv->rsa_key_ref = env->key_ref[0]; } if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Algorithm reference not supported.\n"); + sc_log(card->ctx, "Algorithm reference not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) if (memcmp(env->file_ref.value, "\x00\x12", 2) != 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File reference is not 0012.\n"); + sc_log(card->ctx, "File reference is not 0012.\n"); return SC_ERROR_NOT_SUPPORTED; } return 0; @@ -986,15 +894,15 @@ cryptoflex_compute_signature(sc_card_t *card, const u8 *data, u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; size_t i, i2; - + if (data_len != 64 && data_len != 96 && data_len != 128 && data_len != 256) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Illegal input length: %"SC_FORMAT_LEN_SIZE_T"u\n", data_len); return SC_ERROR_INVALID_ARGUMENTS; } if (outlen < data_len) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Output buffer too small.\n"); + sc_log(card->ctx, "Output buffer too small.\n"); return SC_ERROR_BUFFER_TOO_SMALL; } sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x00, prv->rsa_key_ref); @@ -1010,9 +918,9 @@ cryptoflex_compute_signature(sc_card_t *card, const u8 *data, for (i2 = 0; i2 < 10; i2++) sbuf[i2]=data[data_len-1-i2]; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); + LOG_TEST_RET(card->ctx, r, "Card returned error"); data_len -= 10; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x00, prv->rsa_key_ref); apdu.cla=0x0; @@ -1027,12 +935,12 @@ cryptoflex_compute_signature(sc_card_t *card, const u8 *data, apdu.le = apdu.resplen > 256 ? 256 : apdu.resplen; apdu.resp = sbuf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); + LOG_TEST_RET(card->ctx, r, "Card returned error"); for (i = 0; i < apdu.resplen; i++) out[i] = sbuf[apdu.resplen-1-i]; - return apdu.resplen; + return (int)apdu.resplen; } static int @@ -1043,13 +951,13 @@ cyberflex_compute_signature(sc_card_t *card, const u8 *data, sc_apdu_t apdu; u8 alg_id, key_id; int r; - + switch (data_len) { case 64: alg_id = 0xC4; break; case 96: alg_id = 0xC6; break; case 128: alg_id = 0xC8; break; default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Illegal input length: %"SC_FORMAT_LEN_SIZE_T"u\n", data_len); return SC_ERROR_INVALID_ARGUMENTS; @@ -1057,7 +965,7 @@ cyberflex_compute_signature(sc_card_t *card, const u8 *data, key_id = prv->rsa_key_ref + 1; /* Why? */ if (outlen < data_len) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Output buffer too small.\n"); + sc_log(card->ctx, "Output buffer too small.\n"); return SC_ERROR_BUFFER_TOO_SMALL; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x88, alg_id, key_id); @@ -1068,10 +976,10 @@ cyberflex_compute_signature(sc_card_t *card, const u8 *data, apdu.resplen = outlen; apdu.resp = out; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - return apdu.resplen; + LOG_TEST_RET(card->ctx, r, "Card returned error"); + return (int)apdu.resplen; } static int flex_decipher(sc_card_t *card, @@ -1116,14 +1024,14 @@ static int flex_generate_key(sc_card_t *card, struct sc_cardctl_cryptoflex_genke sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; int r, p1, p2; - + switch (data->key_bits) { case 512: p2 = 0x40; break; case 768: p2 = 0x60; break; case 1024: p2 = 0x80; break; case 2048: p2 = 0x00; break; default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Illegal key length: %d\n", data->key_bits); + sc_log(card->ctx, "Illegal key length: %zu\n", data->key_bits); return SC_ERROR_INVALID_ARGUMENTS; } @@ -1143,11 +1051,11 @@ static int flex_generate_key(sc_card_t *card, struct sc_cardctl_cryptoflex_genke sbuf[3] = (data->exponent >> 24) & 0xFF; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); + LOG_TEST_RET(card->ctx, r, "Card returned error"); - data->pubkey_len = apdu.resplen; + data->pubkey_len = (unsigned)apdu.resplen; return 0; } @@ -1175,14 +1083,14 @@ static int flex_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) len = tfile->size; sc_file_free(tfile); if (len != 8) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unexpected file length of EF_ICCSN (%lu)\n", + sc_log(card->ctx, "unexpected file length of EF_ICCSN (%lu)\n", (unsigned long) len); return SC_ERROR_INTERNAL; } r = sc_read_binary(card, 0, buf, len, 0); if (r < 0) return r; - card->serialnr.len = len; + card->serialnr.len = len; memcpy(card->serialnr.value, buf, len); memcpy(serial, &card->serialnr, sizeof(*serial)); @@ -1252,8 +1160,7 @@ static void flex_init_pin_info(struct sc_pin_cmd_pin *pin, unsigned int num) pin->offset = 5 + num * 8; } -static int flex_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, - int *tries_left) +static int flex_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) { sc_apdu_t apdu; int r; @@ -1279,11 +1186,11 @@ static int flex_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, /* According to the Cryptoflex documentation, the card * does not return the number of attempts left using - * the 63C0xx convention, hence we don't pass the - * tries_left pointer. */ - r = iso_ops->pin_cmd(card, data, NULL); + * the 63C0xx convention */ + r = iso_ops->pin_cmd(card, data); if (old_cla != -1) card->cla = old_cla; + data->apdu = NULL; return r; } @@ -1296,12 +1203,12 @@ static int flex_logout(sc_card_t *card) apdu.cla = 0xF0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); + LOG_TEST_RET(card->ctx, r, "Card returned error"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } diff --git a/src/libopensc/card-gemsafeV1.c b/src/libopensc/card-gemsafeV1.c index ff05629768..e9705db02a 100644 --- a/src/libopensc/card-gemsafeV1.c +++ b/src/libopensc/card-gemsafeV1.c @@ -11,13 +11,13 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* Initially written by David Mattes (david.mattes@boeing.com) */ /* Portuguese eID card support by Joao Poupino (joao.poupino@ist.utl.pt) */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -32,6 +32,7 @@ #define GEMSAFEV3_ALG_REF_FREEFORM 0x02 #define GEMSAFEV3_ALG_REF_SHA1 0x12 #define GEMSAFEV3_ALG_REF_SHA256 0x42 +#define MAX_RESP_BUFFER_SIZE 2048 static struct sc_card_operations gemsafe_ops; static struct sc_card_operations *iso_ops = NULL; @@ -44,7 +45,7 @@ static struct sc_card_driver gemsafe_drv = { }; /* Known ATRs */ -static struct sc_atr_table gemsafe_atrs[] = { +static const struct sc_atr_table gemsafe_atrs[] = { /* standard version */ {"3B:7B:94:00:00:80:65:B0:83:01:01:74:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL}, {"3B:6B:00:00:80:65:B0:83:01:01:74:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL}, @@ -61,6 +62,8 @@ static struct sc_atr_table gemsafe_atrs[] = { {"3B:7D:95:00:00:80:31:80:65:B0:83:11:C0:A9:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_PTEID, 0, NULL}, {"3B:7D:95:00:00:80:31:80:65:B0:83:11:00:C8:83:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_PTEID, 0, NULL}, {"3B:7D:95:00:00:80:31:80:65:B0:83:11:00:C8:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_PTEID, 0, NULL}, + {"3B:FF:96:00:00:81:31:80:43:80:31:80:65:B0:85:03:00:EF:12:0F:FF:82:90:00:67", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_PTEID, 0, NULL}, + {"3B:FF:96:00:00:81:31:FE:43:80:31:80:65:B0:85:04:01:20:12:0F:FF:82:90:00:D0", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_PTEID, 0, NULL}, /* Swedish eID card */ {"3B:7D:96:00:00:80:31:80:65:B0:83:11:00:C8:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_SEEID, 0, NULL}, /* European Patent Office epoline card*/ @@ -105,44 +108,18 @@ static int get_conf_aid(sc_card_t *card, u8 *aid, size_t *len) } if (!conf_block) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "no card specific options configured, trying default AID\n"); + sc_log(ctx, "no card specific options configured, trying default AID\n"); return SC_ERROR_INTERNAL; } str_aid = scconf_get_str(conf_block, "aid", NULL); if (!str_aid) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "no aid configured, trying default AID\n"); + sc_log(ctx, "no aid configured, trying default AID\n"); return SC_ERROR_INTERNAL; } return sc_hex_to_bin(str_aid, aid, len); } -static int gp_select_applet(sc_card_t *card, const u8 *aid, size_t aid_len) -{ - int r; - u8 buf[SC_MAX_APDU_BUFFER_SIZE]; - struct sc_context *ctx = card->ctx; - struct sc_apdu apdu; - - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); - - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xa4, 0x04, 0x00); - apdu.lc = aid_len; - apdu.data = aid; - apdu.datalen = aid_len; - apdu.resp = buf; - apdu.le = 256; - apdu.resplen = sizeof(buf); - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); - - return SC_SUCCESS; -} - static int gemsafe_match_card(sc_card_t *card) { int i; @@ -180,59 +157,58 @@ static int gemsafe_init(struct sc_card *card) memcpy(exdata->aid, gemsafe_pteid_aid, sizeof(gemsafe_pteid_aid)); exdata->aid_len = sizeof(gemsafe_pteid_aid); } else if (card->type == SC_CARD_TYPE_GEMSAFEV1_SEEID) { - memcpy(exdata->aid, gemsafe_seeid_aid, sizeof(gemsafe_seeid_aid)); - exdata->aid_len = sizeof(gemsafe_seeid_aid); - } + memcpy(exdata->aid, gemsafe_seeid_aid, sizeof(gemsafe_seeid_aid)); + exdata->aid_len = sizeof(gemsafe_seeid_aid); + } /* increase lock_count here to prevent sc_unlock to select * applet twice in gp_select_applet */ card->lock_count++; /* SELECT applet */ - r = gp_select_applet(card, exdata->aid, exdata->aid_len); + r = iso7816_select_aid(card, exdata->aid, exdata->aid_len, NULL, NULL); if (r < 0) { free(exdata); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "applet selection failed\n"); - return SC_ERROR_INTERNAL; + sc_log(card->ctx, "applet selection failed\n"); + return SC_ERROR_INVALID_CARD; } card->lock_count--; /* set the supported algorithm */ - r = gemsafe_match_card(card); - if (r > 0) { - unsigned long flags; - - flags = SC_ALGORITHM_RSA_PAD_PKCS1; - flags |= SC_ALGORITHM_RSA_PAD_ISO9796; - flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; - flags |= SC_ALGORITHM_RSA_HASH_NONE; - - /* GemSAFE V3 cards support SHA256 */ - if (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID || - card->type == SC_CARD_TYPE_GEMSAFEV1_SEEID) - flags |= SC_ALGORITHM_RSA_HASH_SHA256; + unsigned long flags; + + flags = SC_ALGORITHM_RSA_PAD_PKCS1; + flags |= SC_ALGORITHM_RSA_PAD_ISO9796; + flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; + flags |= SC_ALGORITHM_RSA_HASH_NONE; + + /* GemSAFE V3 cards support SHA256 */ + if (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID || + card->type == SC_CARD_TYPE_GEMSAFEV1_SEEID) + flags |= SC_ALGORITHM_RSA_HASH_SHA256; + + _sc_card_add_rsa_alg(card, 512, flags, 0); + _sc_card_add_rsa_alg(card, 768, flags, 0); + _sc_card_add_rsa_alg(card, 1024, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); + _sc_card_add_rsa_alg(card, 3072, flags, 0); + _sc_card_add_rsa_alg(card, 4096, flags, 0); + + /* fake algorithm to persuade register_mechanisms() + * to register these hashes */ + if (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID || + card->type == SC_CARD_TYPE_GEMSAFEV1_SEEID) { + flags = SC_ALGORITHM_RSA_HASH_SHA1; + flags |= SC_ALGORITHM_RSA_HASH_MD5; + flags |= SC_ALGORITHM_RSA_HASH_MD5_SHA1; + flags |= SC_ALGORITHM_RSA_HASH_RIPEMD160; _sc_card_add_rsa_alg(card, 512, flags, 0); - _sc_card_add_rsa_alg(card, 768, flags, 0); - _sc_card_add_rsa_alg(card, 1024, flags, 0); - _sc_card_add_rsa_alg(card, 2048, flags, 0); - - /* fake algorithm to persuade register_mechanisms() - * to register these hashes */ - if (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID || - card->type == SC_CARD_TYPE_GEMSAFEV1_SEEID) { - flags = SC_ALGORITHM_RSA_HASH_SHA1; - flags |= SC_ALGORITHM_RSA_HASH_MD5; - flags |= SC_ALGORITHM_RSA_HASH_MD5_SHA1; - flags |= SC_ALGORITHM_RSA_HASH_RIPEMD160; - - _sc_card_add_rsa_alg(card, 512, flags, 0); - } } card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; card->drv_data = exdata; - return 0; + return SC_SUCCESS; } static int gemsafe_finish(sc_card_t *card) @@ -292,7 +268,7 @@ static int gemsafe_setacl(sc_card_t *card, sc_file_t *file, const u8 *data, cond = *p++; else cond = 0xff; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "DF security byte CREATE DF: %02x\n", cond); r = gemsafe_sc2acl(file, SC_AC_OP_CREATE, cond); if (r < 0) @@ -301,7 +277,7 @@ static int gemsafe_setacl(sc_card_t *card, sc_file_t *file, const u8 *data, cond = *p; else cond = 0xff; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "DF security byte CREATE EF: %02x\n", cond); /* XXX: opensc doesn't currently separate access conditions for * CREATE EF and CREATE DF, this should be changed */ @@ -315,7 +291,7 @@ static int gemsafe_setacl(sc_card_t *card, sc_file_t *file, const u8 *data, cond = *p++; else cond = 0xff; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "EF security byte UPDATE/ERASE BINARY: %02x\n", cond); r = gemsafe_sc2acl(file, SC_AC_OP_UPDATE, cond); if (r < 0) @@ -330,7 +306,7 @@ static int gemsafe_setacl(sc_card_t *card, sc_file_t *file, const u8 *data, cond = *p; else cond = 0xff; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "EF security byte READ BINARY: %02x\n", cond); r = gemsafe_sc2acl(file, SC_AC_OP_READ, cond); if (r < 0) @@ -354,7 +330,7 @@ static int gemsafe_process_fci(struct sc_card *card, struct sc_file *file, r = iso_ops->process_fci(card, file, buf, len); if (r < 0) return r; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "processing GemSAFE V1 specific FCI information\n"); @@ -368,17 +344,17 @@ static int gemsafe_process_fci(struct sc_card *card, struct sc_file *file, file->type = SC_FILE_TYPE_WORKING_EF; } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file type: %s\n", type); + sc_log(ctx, "file type: %s\n", type); tag = sc_asn1_find_tag(ctx, p, len, 0x8C, &tlen); - if (tag) { + if (tag && tlen > 1) { r = gemsafe_setacl(card, file, tag, strcmp(type, "DF") ? 0 : 1); if (r < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "unable to set ACL\n"); + sc_log(ctx, "unable to set ACL\n"); return SC_ERROR_INTERNAL; } } else - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "error: AM and SC bytes missing\n"); + sc_log(ctx, "error: AM and SC bytes missing\n"); return SC_SUCCESS; } @@ -390,7 +366,7 @@ static u8 gemsafe_flags2algref(struct sc_card *card, const struct sc_security_en if (env->operation == SC_SEC_OPERATION_SIGN) { if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA256) ret = GEMSAFEV3_ALG_REF_SHA256; - else if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) + else if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01) ret = (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID || card->type == SC_CARD_TYPE_GEMSAFEV1_SEEID) ? GEMSAFEV3_ALG_REF_FREEFORM : @@ -398,7 +374,7 @@ static u8 gemsafe_flags2algref(struct sc_card *card, const struct sc_security_en else if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796) ret = 0x11; } else if (env->operation == SC_SEC_OPERATION_DECIPHER) { - if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) + if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02) ret = (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID || card->type == SC_CARD_TYPE_GEMSAFEV1_SEEID) ? GEMSAFEV3_ALG_REF_FREEFORM : @@ -418,7 +394,7 @@ static int gemsafe_restore_security_env(struct sc_card *card, int se_num) sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0x73, (u8) se_num); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } @@ -443,7 +419,7 @@ static int gemsafe_set_security_env(struct sc_card *card, } } if (!(se_env.flags & SC_SEC_ENV_ALG_REF_PRESENT)) - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "unknown algorithm flags '%x'\n", se_env.algorithm_flags); + sc_log(ctx, "unknown algorithm flags '%lx'\n", se_env.algorithm_flags); se_env.flags &= ~SC_SEC_ENV_FILE_REF_PRESENT; return iso_ops->set_security_env(card, &se_env, se_num); @@ -452,17 +428,18 @@ static int gemsafe_set_security_env(struct sc_card *card, static int gemsafe_compute_signature(struct sc_card *card, const u8 * data, size_t data_len, u8 * out, size_t outlen) { - int r, len; + int r; + size_t len; struct sc_apdu apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 rbuf[MAX_RESP_BUFFER_SIZE]; + u8 sbuf[MAX_RESP_BUFFER_SIZE]; sc_context_t *ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); /* the card can sign 36 bytes of free form data */ if (data_len > 36) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "error: input data too long: %"SC_FORMAT_LEN_SIZE_T"u bytes\n", data_len); return SC_ERROR_INVALID_ARGUMENTS; @@ -489,7 +466,7 @@ static int gemsafe_compute_signature(struct sc_card *card, const u8 * data, apdu.datalen = data_len + 2; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { if(card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID || card->type == SC_CARD_TYPE_GEMSAFEV1_SEEID) { @@ -503,14 +480,14 @@ static int gemsafe_compute_signature(struct sc_card *card, const u8 * data, apdu.cla = 0x00; } r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } @@ -520,7 +497,7 @@ static int gemsafe_decipher(struct sc_card *card, const u8 * crgram, { int r; struct sc_apdu apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 rbuf[MAX_RESP_BUFFER_SIZE]; sc_context_t *ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); @@ -537,12 +514,12 @@ static int gemsafe_decipher(struct sc_card *card, const u8 * crgram, apdu.lc = crgram_len; apdu.datalen = crgram_len; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { - int len = apdu.resplen > outlen ? outlen : apdu.resplen; + size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } @@ -566,6 +543,27 @@ static int gemsafe_get_challenge(sc_card_t *card, u8 *rnd, size_t len) return r; } +static int gemsafe_card_reader_lock_obtained(sc_card_t *card, int was_reset) +{ + int r = SC_SUCCESS; + gemsafe_exdata *exdata = (gemsafe_exdata *)card->drv_data; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (was_reset > 0 && exdata) { + r = iso7816_select_aid(card, exdata->aid, exdata->aid_len, NULL, NULL); + } + + LOG_FUNC_RETURN(card->ctx, r); +} + +static int gemsafe_logout(sc_card_t *card) +{ + gemsafe_exdata *exdata = (gemsafe_exdata *)card->drv_data; + + return iso7816_select_aid(card, exdata->aid, exdata->aid_len, NULL, NULL); +} + static struct sc_card_driver *sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); @@ -585,6 +583,8 @@ static struct sc_card_driver *sc_get_driver(void) gemsafe_ops.get_challenge = gemsafe_get_challenge; gemsafe_ops.process_fci = gemsafe_process_fci; gemsafe_ops.pin_cmd = iso_ops->pin_cmd; + gemsafe_ops.card_reader_lock_obtained = gemsafe_card_reader_lock_obtained; + gemsafe_ops.logout = gemsafe_logout; return &gemsafe_drv; } diff --git a/src/libopensc/card-gids.c b/src/libopensc/card-gids.c index 923cf3bd9a..cca53728e4 100644 --- a/src/libopensc/card-gids.c +++ b/src/libopensc/card-gids.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* @@ -51,6 +51,8 @@ Some features are undocumented like the format used to store certificates. They // used for changing the default label if used twice #include "../pkcs15init/pkcs15-init.h" #include "card-gids.h" +#include "../common/compat_strlcat.h" +#include "../common/compat_strlcpy.h" #define GIDS_STATE_NONE 0 #define GIDS_STATE_READ_DATA_PRESENT 1 @@ -117,7 +119,7 @@ static struct sc_card_driver gids_drv = { struct gids_aid { int enumtag; - size_t len_short; /* min lenght without version */ + size_t len_short; /* min length without version */ size_t len_long; /* With version and other stuff */ u8 *value; }; @@ -136,11 +138,10 @@ static struct gids_aid gids_aids[] = { // stolen from cardmod.h for the cardcf file typedef struct _CARD_CACHE_FILE_FORMAT { - unsigned char bVersion; // Cache version - unsigned char bPinsFreshness; // Card PIN - unsigned short wContainersFreshness; - unsigned short wFilesFreshness; - + unsigned char bVersion; // Cache version + unsigned char bPinsFreshness; // Card PIN + unsigned short wContainersFreshness; + unsigned short wFilesFreshness; } CARD_CACHE_FILE_FORMAT, *PCARD_CACHE_FILE_FORMAT; struct gids_private_data { @@ -163,18 +164,19 @@ static int gids_get_identifiers(sc_card_t* card, u8* masterfile, size_t masterfi gids_mf_record_t *records = (gids_mf_record_t *) (masterfile+1); size_t recordcount = ((masterfilesize-1) / sizeof(gids_mf_record_t)); size_t i; - assert(masterfilesize >= 1); + if (masterfilesize < 1) + return SC_ERROR_INTERNAL; for (i = 0; i < recordcount; i++) { - if (strcmp(directory, records[i].directory) == 0 && strcmp(filename, records[i].filename) == 0) { + if (strncmp(directory, records[i].directory, sizeof(records[i].directory)) == 0 && strncmp(filename, records[i].filename, sizeof(records[i].filename)) == 0) { *fileIdentifier = records[i].fileIdentifier; *dataObjectIdentifier = records[i].dataObjectIdentifier; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Identifiers of %s %s is fileIdentifier=%x, dataObjectIdentifier=%x\n", directory, filename, *fileIdentifier, *dataObjectIdentifier); return 0; } } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "file %s %s not found\n", directory, filename); + sc_log(card->ctx, "file %s %s not found\n", directory, filename); return SC_ERROR_FILE_NOT_FOUND; } @@ -186,7 +188,8 @@ static int gids_find_available_DO(sc_card_t *card, u8* masterfile, size_t master size_t recordcount = (masterfilesize / sizeof(gids_mf_record_t)); size_t i; - assert(masterfilesize >= 1); + if (masterfilesize < 1) + return SC_ERROR_INTERNAL; *fileIdentifier = CERT_FI; @@ -211,9 +214,10 @@ static int gids_get_DO(sc_card_t* card, int fileIdentifier, int dataObjectIdenti size_t datasize = 0; const u8* p; u8 buffer[MAX_GIDS_FILE_SIZE]; + size_t buffer_len = sizeof(buffer); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Got args: fileIdentifier=%x, dataObjectIdentifier=%x, response=%p, responselen=%"SC_FORMAT_LEN_SIZE_T"u\n", fileIdentifier, dataObjectIdentifier, response, responselen ? *responselen : 0); @@ -224,22 +228,25 @@ static int gids_get_DO(sc_card_t* card, int fileIdentifier, int dataObjectIdenti apdu.data = data; apdu.datalen = 04; apdu.resp = buffer; - apdu.resplen = sizeof(buffer); + apdu.resplen = buffer_len; apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids get data failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + LOG_TEST_RET(card->ctx, r, "gids get data failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + buffer_len = apdu.resplen; - p = sc_asn1_find_tag(card->ctx, buffer, sizeof(buffer), dataObjectIdentifier, &datasize); + p = sc_asn1_find_tag(card->ctx, buffer, buffer_len, dataObjectIdentifier, &datasize); if (!p) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND); } - if (datasize > *responselen) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_BUFFER_TOO_SMALL); + if (response && responselen) { + if (datasize > *responselen) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); + } + memcpy(response, p, datasize); + *responselen = datasize; } - memcpy(response, p, datasize); - *responselen = datasize; return SC_SUCCESS; } @@ -250,7 +257,7 @@ static int gids_put_DO(sc_card_t* card, int fileIdentifier, int dataObjectIdenti u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE]; u8* p = buffer; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Got args: fileIdentifier=%x, dataObjectIdentifier=%x, data=%p, datalen=%"SC_FORMAT_LEN_SIZE_T"u\n", fileIdentifier, dataObjectIdentifier, data, datalen); @@ -265,8 +272,8 @@ static int gids_put_DO(sc_card_t* card, int fileIdentifier, int dataObjectIdenti apdu.flags |= SC_APDU_FLAGS_CHAINING; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids put data failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + LOG_TEST_RET(card->ctx, r, "gids put data failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); return SC_SUCCESS; } @@ -278,7 +285,7 @@ static int gids_select_aid(sc_card_t* card, u8* aid, size_t aidlen, u8* response int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Got args: aid=%p, aidlen=%"SC_FORMAT_LEN_SIZE_T"u, response=%p, responselen=%"SC_FORMAT_LEN_SIZE_T"u\n", aid, aidlen, response, responselen ? *responselen : 0); @@ -289,13 +296,13 @@ static int gids_select_aid(sc_card_t* card, u8* aid, size_t aidlen, u8* response apdu.datalen = aidlen; apdu.resp = response; apdu.resplen = responselen ? *responselen : 0; - apdu.le = response == NULL ? 0 : 256; /* could be 21 for fci */ + apdu.le = response == NULL ? 0 : 256; /* could be 21 for fci */ r = sc_transmit_apdu(card, &apdu); if (responselen) *responselen = apdu.resplen; - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids select failed"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); + LOG_TEST_RET(card->ctx, r, "gids select failed"); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } // DIRECT FILE MANIPULATION @@ -309,9 +316,9 @@ static int gids_read_gidsfile_without_cache(sc_card_t* card, u8* masterfile, siz SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); r = gids_get_identifiers(card, masterfile, masterfilesize, directory, filename, &fileIdentifier, &dataObjectIdentifier); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the identifier for the gids file"); + LOG_TEST_RET(card->ctx, r, "unable to get the identifier for the gids file"); r = gids_get_DO(card, fileIdentifier, dataObjectIdentifier, response, responselen); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the data from the file"); + LOG_TEST_RET(card->ctx, r, "unable to get the data from the file"); return r; } @@ -323,13 +330,13 @@ static int gids_write_gidsfile_without_cache(sc_card_t* card, u8* masterfile, si SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (datalen > MAX_GIDS_FILE_SIZE) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); } r = gids_get_identifiers(card, masterfile, masterfilesize, directory, filename, &fileIdentifier, &dataObjectIdentifier); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the identifier for the gids file"); + LOG_TEST_RET(card->ctx, r, "unable to get the identifier for the gids file"); r = gids_put_DO(card, fileIdentifier, dataObjectIdentifier, data, datalen); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the data from the file"); + LOG_TEST_RET(card->ctx, r, "unable to get the data from the file"); return r; } @@ -342,11 +349,11 @@ static int gids_read_masterfile(sc_card_t* card) { r = gids_get_DO(card, MF_FI, MF_DO, data->masterfile, &data->masterfilesize); if (r<0) { data->masterfilesize = sizeof(data->masterfile); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_CARD); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } if (data->masterfilesize < 1 || data->masterfile[0] != 1) { data->masterfilesize = sizeof(data->masterfile); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_CARD); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } return r; } @@ -359,7 +366,7 @@ static int gids_update_cardcf(sc_card_t* card, int file, int container) { int r; size_t cardcfsize = sizeof(cardcf); r = gids_read_gidsfile_without_cache(card, data->masterfile, data->masterfilesize, "", "cardcf", cardcf, &cardcfsize); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the cardcf"); + LOG_TEST_RET(card->ctx, r, "unable to get the cardcf"); if (file) { short filefreshness = cardcf[4] + cardcf[5] * 0x100; @@ -374,7 +381,7 @@ static int gids_update_cardcf(sc_card_t* card, int file, int container) { cardcf[3] = (containerfreshness>>8) & 0xFF; } r = gids_write_gidsfile_without_cache(card, data->masterfile, data->masterfilesize, "", "cardcf", cardcf, 6); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cardcf file"); + LOG_TEST_RET(card->ctx, r, "unable to update the cardcf file"); return r; } @@ -385,15 +392,15 @@ static int gids_read_gidsfile(sc_card_t* card, char *directory, char *filename, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (privatedata->masterfilesize == sizeof(privatedata->masterfile)) { r = gids_read_masterfile(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the masterfile"); + LOG_TEST_RET(card->ctx, r, "unable to get the masterfile"); } r = gids_read_gidsfile_without_cache(card, privatedata->masterfile, privatedata->masterfilesize, directory, filename, response, responselen); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to read the file"); + LOG_TEST_RET(card->ctx, r, "unable to read the file"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); } -// check for the existance of a file +// check for the existence of a file static int gids_does_file_exists(sc_card_t *card, char* directory, char* filename) { struct gids_private_data* privatedata = (struct gids_private_data*) card->drv_data; int fileIdentifier, dataObjectIdentifier; @@ -407,11 +414,11 @@ static int gids_write_gidsfile(sc_card_t* card, char *directory, char *filename, int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); r = gids_update_cardcf(card, 1, 0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cache file"); + LOG_TEST_RET(card->ctx, r, "unable to update the cache file"); r = gids_write_gidsfile_without_cache(card, privatedata->masterfile, privatedata->masterfilesize, directory, filename, data, datalen); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the file"); + LOG_TEST_RET(card->ctx, r, "unable to write the file"); if (strcmp(directory, "mscp") == 0 && strcmp(filename, "cmapfile") == 0) { // update the cmapfile cache privatedata->cmapfilesize = datalen; @@ -431,7 +438,7 @@ static int gids_read_cmapfile(sc_card_t* card) { if (r<0) { data->cmapfilesize = sizeof(data->cmapfile); } - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the cmapfile"); + LOG_TEST_RET(card->ctx, r, "unable to get the cmapfile"); return r; } @@ -443,11 +450,11 @@ static int gids_create_file(sc_card_t *card, char* directory, char* filename) { struct gids_private_data* privatedata = (struct gids_private_data*) card->drv_data; int fileIdentifier, dataObjectIdentifier; size_t records; - int offset; + size_t offset; gids_mf_record_t* record; r = gids_find_available_DO(card, privatedata->masterfile, privatedata->masterfilesize, &fileIdentifier, &dataObjectIdentifier); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to find an empty DO"); + LOG_TEST_RET(card->ctx, r, "unable to find an empty DO"); memcpy(masterfilebuffer, privatedata->masterfile, privatedata->masterfilesize); masterfilebuffersize = privatedata->masterfilesize + sizeof(gids_mf_record_t); @@ -455,22 +462,22 @@ static int gids_create_file(sc_card_t *card, char* directory, char* filename) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_ENOUGH_MEMORY); } - records = ((privatedata->masterfilesize -1) / sizeof(gids_mf_record_t)); + records = ((privatedata->masterfilesize - 1) / sizeof(gids_mf_record_t)); offset = 1 + sizeof(gids_mf_record_t) * records; memcpy(masterfilebuffer + offset + sizeof(gids_mf_record_t), masterfilebuffer + offset, privatedata->masterfilesize - offset); memset(masterfilebuffer + offset, 0, sizeof(gids_mf_record_t)); record = (gids_mf_record_t*) (masterfilebuffer + offset); strncpy(record->directory, directory, 8); - strncpy(record->filename, filename, 8); + strlcpy(record->filename, filename, sizeof(record->filename)); record->fileIdentifier = fileIdentifier; record->dataObjectIdentifier = dataObjectIdentifier; r = gids_update_cardcf(card, 1, 0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cardcf"); + LOG_TEST_RET(card->ctx, r, "unable to update the cardcf"); r = gids_put_DO(card, MF_FI, MF_DO, masterfilebuffer, masterfilebuffersize); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the masterfile"); + LOG_TEST_RET(card->ctx, r, "unable to update the masterfile"); memcpy(privatedata->masterfile, masterfilebuffer, masterfilebuffersize); privatedata->masterfilesize = masterfilebuffersize; @@ -528,30 +535,32 @@ static int gids_get_pin_status(sc_card_t *card, int pinreference, int *tries_lef dataObjectIdentifier = GIDS_PUK_STATUS_OBJECT_IDENTIFIER; break; default: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OBJECT_NOT_FOUND); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_FOUND); } r = gids_get_DO(card, GIDS_APPLET_EFID, dataObjectIdentifier, buffer, &buffersize); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the masterfile"); + LOG_TEST_RET(card->ctx, r, "unable to update the masterfile"); + + buffersize = buffersize > sizeof(buffer) ? sizeof(buffer) : buffersize; - p = sc_asn1_find_tag(card->ctx, buffer, sizeof(buffer), GIDS_TRY_COUNTER_OLD_TAG, &datasize); + p = sc_asn1_find_tag(card->ctx, buffer, buffersize, GIDS_TRY_COUNTER_OLD_TAG, &datasize); if (p && datasize == 1) { if (tries_left) *tries_left = p[0]; } - p = sc_asn1_find_tag(card->ctx, buffer, sizeof(buffer), GIDS_TRY_COUNTER_TAG, &datasize); + p = sc_asn1_find_tag(card->ctx, buffer, buffersize, GIDS_TRY_COUNTER_TAG, &datasize); if (p && datasize == 1) { if (tries_left) *tries_left = p[0]; } - p = sc_asn1_find_tag(card->ctx, buffer, sizeof(buffer), GIDS_TRY_LIMIT_TAG, &datasize); + p = sc_asn1_find_tag(card->ctx, buffer, buffersize , GIDS_TRY_LIMIT_TAG, &datasize); if (p && datasize == 1) { - if (tries_left) + if (max_tries) *max_tries = p[0]; } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Pin information for PIN 0x%x: triesleft=%d trieslimit=%d\n", pinreference, (tries_left?*tries_left:-1), (max_tries?*max_tries:-1)); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int gids_match_card(sc_card_t * card) @@ -576,7 +585,7 @@ static int gids_match_card(sc_card_t * card) if (tag != NULL) { aid = sc_asn1_find_tag(card->ctx, tag, taglen, GIDS_APPLICATION_AID_TAG, &aidlen); if (aid != NULL ) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"found AID"); + sc_log(card->ctx, "found AID"); for (i = 0; gids_aids[i].len_long != 0; i++) { if ( aidlen > gids_aids[i].len_long && memcmp(aid, gids_aids[i].value, gids_aids[i].len_long) == 0) { @@ -603,11 +612,11 @@ static int gids_get_serialnr(sc_card_t * card, sc_serial_number_t * serial) buffersize = sizeof(buffer); r = gids_read_gidsfile(card, "", "cardid", buffer, &buffersize); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to read cardid"); + LOG_TEST_RET(card->ctx, r, "unable to read cardid"); if (SC_MAX_SERIALNR < buffersize) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } /* cache serial number */ @@ -618,7 +627,7 @@ static int gids_get_serialnr(sc_card_t * card, sc_serial_number_t * serial) if (serial) memcpy(serial, &card->serialnr, sizeof(*serial)); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } // initialize the driver @@ -631,7 +640,7 @@ static int gids_init(sc_card_t * card) // cache some data in memory data = (struct gids_private_data*) calloc(1, sizeof(struct gids_private_data)); if (!data) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_MEMORY_FAILURE); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } memset(data, 0, sizeof(struct gids_private_data)); card->drv_data = data; @@ -640,10 +649,14 @@ static int gids_init(sc_card_t * card) data->masterfilesize = sizeof(data->masterfile); /* supported RSA keys and how padding is done */ - flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_ONBOARD_KEY_GEN | SC_ALGORITHM_RSA_RAW; + flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_ONBOARD_KEY_GEN; + /* fix me: add other algorithms when the gids specification will tell how to extract the algo id from the FCP */ _sc_card_add_rsa_alg(card, 1024, flags, 0); _sc_card_add_rsa_alg(card, 2048, flags, 0); + _sc_card_add_rsa_alg(card, 3072, flags, 0); + _sc_card_add_rsa_alg(card, 4096, flags, 0); + return SC_SUCCESS; } @@ -667,7 +680,7 @@ static int gids_get_crypto_identifier_from_key_ref(sc_card_t *card, const unsign int recordsnum = (int) (data->cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); int index = keyref - GIDS_FIRST_KEY_IDENTIFIER; if (index >= recordsnum) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } *cryptoidentifier = 0x00; /* initialize to zero */ if (records[index].wKeyExchangeKeySizeBits == 1024 || records[index].wSigKeySizeBits == 1024) { @@ -706,7 +719,7 @@ static int gids_get_crypto_identifier_from_key_ref(sc_card_t *card, const unsign *cryptoidentifier = GIDS_ECC_521_IDENTIFIER; return SC_SUCCESS; } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } // same here @@ -740,9 +753,11 @@ static int gids_set_security_env(sc_card_t *card, u8 *p; int r, locked = 0; - assert(card != NULL && env != NULL); + if (card == NULL || env == NULL) + return SC_ERROR_INTERNAL; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); + memset(sbuf, 0, sizeof(sbuf)); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_MANAGE_SECURITY_ENVIRONMENT, P1_DECIPHERMENT_INTERNAL_AUTHENTICATE_KEY_AGREEMENT, 0); switch (env->operation) { @@ -770,14 +785,15 @@ static int gids_set_security_env(sc_card_t *card, } } if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT)) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } - if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) + if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) *p++ = 0x83; else *p++ = 0x84; *p++ = (u8) env->key_ref_len; - assert(sizeof(sbuf) - (p - sbuf) >= env->key_ref_len); + if (sizeof(sbuf) - (p - sbuf) < env->key_ref_len) + return SC_ERROR_INTERNAL; memcpy(p, env->key_ref, env->key_ref_len); p += env->key_ref_len; @@ -816,21 +832,65 @@ static int gids_set_security_env(sc_card_t *card, return r; } + +static int +gids_decipher(struct sc_card *card, + const u8 * crgram, size_t crgram_len, + u8 * out, size_t outlen) +{ + int r; + struct sc_apdu apdu; + + if (card == NULL || crgram == NULL || out == NULL) { + return SC_ERROR_INVALID_ARGUMENTS; + } + LOG_FUNC_CALLED(card->ctx); + sc_log(card->ctx, + "Gids decipher: in-len %"SC_FORMAT_LEN_SIZE_T"u, out-len %"SC_FORMAT_LEN_SIZE_T"u", + crgram_len, outlen); + + /* INS: 0x2A PERFORM SECURITY OPERATION + * P1: 0x80 Resp: Plain value + * P2: 0x86 Cmd: Padding indicator byte followed by cryptogram + * Implementation by Microsoft indicates that Padding indicator + * must not be sent. It may only be needed if Secure Messaging + * is used. This driver does not support SM. + */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x80, 0x86); + apdu.resp = out; + apdu.resplen = outlen; + apdu.le = outlen; + + apdu.data = crgram; /* Skip padding indication not needed unless SM */ + apdu.lc = crgram_len; + apdu.datalen = crgram_len; + + iso7816_fixup_transceive_length(card, &apdu); + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); + + LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); +} + // deauthenticate all pins static int gids_logout(sc_card_t *card) { struct sc_apdu apdu; int r; - assert(card && card->ctx); + if (card == NULL || card->ctx == NULL) + return SC_ERROR_INTERNAL; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); // use the special PIN to deauthenticate sc_format_apdu(card, &apdu, SC_APDU_CASE_1, INS_VERIFY, 0x00, P2_PIN_DEAUTHENTICATE); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } // read a public key @@ -852,7 +912,7 @@ static int gids_read_public_key (struct sc_card *card , unsigned int algorithm, size_t buffersize = sizeof(buffer); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Got args: key_reference=%x, response=%p, responselen=%"SC_FORMAT_LEN_SIZE_T"u\n", key_reference, response, responselen ? *responselen : 0); @@ -866,13 +926,13 @@ static int gids_read_public_key (struct sc_card *card , unsigned int algorithm, apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read public key failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + LOG_TEST_RET(card->ctx, r, "gids read public key failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); buffersize = apdu.resplen; keytemplate = sc_asn1_find_tag(card->ctx, buffer, buffersize, GIDS_PUBKEY_TAG, &tlen); if (keytemplate == NULL) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid public key data: missing tag"); + sc_log(card->ctx, "invalid public key data: missing tag"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } @@ -896,12 +956,12 @@ static int gids_read_public_key (struct sc_card *card , unsigned int algorithm, r = sc_pkcs15_encode_pubkey_rsa(card->ctx, &rsa_key, response, responselen); LOG_TEST_RET(card->ctx, r, "failed to read public key: cannot encode RSA public key"); } else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "it is not a known public key"); + sc_log(card->ctx, "it is not a known public key"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } if (response && responselen) - sc_log(card->ctx, "encoded public key: %s", sc_dump_hex(*response, *responselen)); + sc_log_hex(card->ctx, "encoded public key", *response, *responselen); return SC_SUCCESS; } @@ -913,7 +973,7 @@ static int gids_select_file(sc_card_t *card, const struct sc_path *in_path, struct sc_context *ctx = card->ctx; struct gids_private_data *data = (struct gids_private_data *) card->drv_data; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); data->state = GIDS_STATE_NONE; data->currentDO = 0; @@ -924,14 +984,16 @@ static int gids_select_file(sc_card_t *card, const struct sc_path *in_path, data->currentEFID = in_path->value[1] + (in_path->value[0]<<8); data->currentDO = in_path->value[3] + (in_path->value[2]<<8); - file = sc_file_new(); - if (file == NULL) - LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); - file->path = *in_path; - file->type = SC_FILE_TYPE_WORKING_EF; - file->ef_structure = SC_FILE_EF_TRANSPARENT; - file->size = SC_MAX_EXT_APDU_BUFFER_SIZE; - *file_out = file; + if (file_out) { + file = sc_file_new(); + if (file == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + file->path = *in_path; + file->type = SC_FILE_TYPE_WORKING_EF; + file->ef_structure = SC_FILE_EF_TRANSPARENT; + file->size = SC_MAX_EXT_APDU_BUFFER_SIZE; + *file_out = file; + } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } else if (in_path->len == 4 && in_path->value[0] == 0x3F && in_path->value[1] == 0xFF && in_path->type == SC_PATH_TYPE_PATH) { // GIDS does not allow a select with a path containing a DF @@ -950,14 +1012,13 @@ static int gids_select_file(sc_card_t *card, const struct sc_path *in_path, static int gids_get_pin_policy(struct sc_card *card, struct sc_pin_cmd_data *data) { int r; - if (data->pin_type != SC_AC_CHV) { + if (data->pin_type != SC_AC_CHV) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } r = gids_get_pin_status(card, data->pin_reference, &(data->pin1.tries_left), &(data->pin1.max_tries)); LOG_TEST_RET(card->ctx, r, "gids_get_pin_status failed"); data->pin1.max_length = 16; data->pin1.min_length = 4; - data->pin1.stored_length = 0; data->pin1.encoding = SC_PIN_ENCODING_ASCII; data->pin1.offset = 5; data->pin1.logged_in = SC_PIN_STATE_UNKNOWN; @@ -965,23 +1026,23 @@ static int gids_get_pin_policy(struct sc_card *card, struct sc_pin_cmd_data *dat } static int -gids_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { +gids_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data) { if (data->cmd == SC_PIN_CMD_GET_INFO) { return gids_get_pin_policy(card, data); } else { - return iso_ops->pin_cmd(card, data, tries_left); + return iso_ops->pin_cmd(card, data); } } // used to read existing certificates static int gids_read_binary(sc_card_t *card, unsigned int offset, - unsigned char *buf, size_t count, unsigned long flags) { + unsigned char *buf, size_t count, unsigned long *flags) { struct gids_private_data *data = (struct gids_private_data *) card->drv_data; struct sc_context *ctx = card->ctx; int r; int size; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); if (! data->currentDO || ! data->currentEFID) { LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); @@ -993,25 +1054,17 @@ static int gids_read_binary(sc_card_t *card, unsigned int offset, r = gids_get_DO(card, data->currentEFID, data->currentDO, buffer, &(buffersize)); if (r <0) return r; if (buffersize < 4) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); } if (buffer[0] == 1 && buffer[1] == 0) { - size_t expectedsize = buffer[2] + buffer[3] * 0x100; - data->buffersize = sizeof(data->buffer); - r = sc_decompress(data->buffer, &(data->buffersize), buffer+4, buffersize-4, COMPRESSION_ZLIB); - if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Zlib error: %d", r); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); - } - if (data->buffersize != expectedsize) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "expected size: %"SC_FORMAT_LEN_SIZE_T"u real size: %"SC_FORMAT_LEN_SIZE_T"u", - expectedsize, data->buffersize); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA); - } + if (flags) + *flags |= SC_FILE_FLAG_COMPRESSED_ZLIB; + /* compressed data are starting on position buffer + 4 */ + data->buffersize = buffersize - 4; + memcpy(data->buffer, buffer + 4, buffersize - 4); } else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unknown compression method %d", buffer[0] + (buffer[1] <<8)); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA); + sc_log(card->ctx, "unknown compression method %d", buffer[0] + (buffer[1] << 8)); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); } data->state = GIDS_STATE_READ_DATA_PRESENT; } @@ -1052,7 +1105,7 @@ gids_get_container_detail(sc_card_t* card, sc_cardctl_gids_get_container_t* cont memset(container, 0, sizeof(sc_cardctl_gids_get_container_t)); container->containernum = num; - if (!records[num].bFlags & CONTAINER_MAP_VALID_CONTAINER) { + if (!(records[num].bFlags & CONTAINER_MAP_VALID_CONTAINER)) { return SC_SUCCESS; } // ignore problematic containers @@ -1094,9 +1147,9 @@ gids_select_key_reference(sc_card_t *card, sc_pkcs15_prkey_info_t* key_info) { // refresh the cached data in case some thing has been modified r = gids_read_masterfile(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read masterfile failed"); + LOG_TEST_RET(card->ctx, r, "gids read masterfile failed"); r = gids_read_cmapfile(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read cmapfile failed"); + LOG_TEST_RET(card->ctx, r, "gids read cmapfile failed"); recordsnum = (data->cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); @@ -1112,24 +1165,24 @@ gids_select_key_reference(sc_card_t *card, sc_pkcs15_prkey_info_t* key_info) { } // use a new key number if (recordsnum > GIDS_MAX_CONTAINER) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_ENOUGH_MEMORY); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); } key_info->key_reference = (int) (GIDS_FIRST_KEY_IDENTIFIER + recordsnum); } else { // key was specified. Search if the key can be used size_t i = key_info->key_reference - GIDS_FIRST_KEY_IDENTIFIER; if (i > GIDS_MAX_CONTAINER) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid key ref %d", key_info->key_reference); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + sc_log(card->ctx, "invalid key ref %d", key_info->key_reference); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } if (i > recordsnum) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "container num is not allowed %"SC_FORMAT_LEN_SIZE_T"u %"SC_FORMAT_LEN_SIZE_T"u", i, recordsnum); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } } - snprintf(ch_tmp, sizeof(ch_tmp), "3FFFB0%02X", key_info->key_reference); + snprintf(ch_tmp, sizeof(ch_tmp), "3FFFB0%02X", (u8) (0xFF & key_info->key_reference)); sc_format_path(ch_tmp, &(key_info->path)); return SC_SUCCESS; } @@ -1193,7 +1246,7 @@ static int gids_perform_create_keyfile(sc_card_t *card, u8 keytype, u8 kid, u8 a apdu.datalen = sizeof(sign); apdu.data = sign; } else { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } r = sc_transmit_apdu(card, &apdu); @@ -1205,12 +1258,12 @@ static int gids_perform_create_keyfile(sc_card_t *card, u8 keytype, u8 kid, u8 a sc_format_apdu(card, &apdu, SC_APDU_CASE_1, INS_ACTIVATE_FILE, 0x00, 0x00); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "ACTIVATE_FILE returned error"); + LOG_TEST_RET(card->ctx, r, "ACTIVATE_FILE returned error"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } // perform the creation of the keyfile and its registration in the cmapfile and keymap file @@ -1235,10 +1288,11 @@ static int gids_create_keyfile(sc_card_t *card, sc_pkcs15_object_t *object) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); // sanity check - assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY); + if ((object->type & SC_PKCS15_TYPE_CLASS_MASK) != SC_PKCS15_TYPE_PRKEY) + return SC_ERROR_INTERNAL; if (!algid) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } // masterfile & cmapfile have been refreshed in gids_perform_create_keyfile @@ -1247,7 +1301,7 @@ static int gids_create_keyfile(sc_card_t *card, sc_pkcs15_object_t *object) { // sanity check if (containernum > recordnum || containernum > GIDS_MAX_CONTAINER) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); // refresh the key map file keymapbuffersize = sizeof(keymapbuffer); @@ -1255,19 +1309,18 @@ static int gids_create_keyfile(sc_card_t *card, sc_pkcs15_object_t *object) { if (r<0) { // the keymap DO should be present if the cmapfile is not empty if (recordnum > 0) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } // else can be empty if not record - keymaprecordnum = 0; keymapbuffersize = 0; } else { keymaprecordnum = (keymapbuffersize - 1) / sizeof(struct gids_keymap_record); if (keymaprecordnum != recordnum) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL , "Error: Unable to create the key file because the keymap and cmapfile are inconsistent"); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL , + sc_log(card->ctx , "Error: Unable to create the key file because the keymap and cmapfile are inconsistent"); + sc_log(card->ctx , "keymaprecordnum = %"SC_FORMAT_LEN_SIZE_T"u recordnum = %"SC_FORMAT_LEN_SIZE_T"u", keymaprecordnum, recordnum); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } } @@ -1304,7 +1357,7 @@ static int gids_create_keyfile(sc_card_t *card, sc_pkcs15_object_t *object) { records->wSigKeySizeBits = (unsigned short) key_info->modulus_length; keymaprecord->keytype = GIDS_KEY_TYPE_AT_SIGNATURE; } else { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } //the GIDS card must have unique container names @@ -1312,8 +1365,8 @@ static int gids_create_keyfile(sc_card_t *card, sc_pkcs15_object_t *object) { if (strcmp(DEFAULT_PRIVATE_KEY_LABEL, object->label) == 0 && strlen(DEFAULT_PRIVATE_KEY_LABEL) + 3 < MAX_CONTAINER_NAME_LEN) { char addition[4] = " 00"; addition[1] += containernum % 10; - addition[2] += (containernum < 0xFF) / 10; - strcat(object->label, addition); + addition[2] += (containernum & 0xFF) / 10; + strlcat(object->label, addition, SC_PKCS15_MAX_LABEL_SIZE); } // convert char to wchar @@ -1332,17 +1385,17 @@ static int gids_create_keyfile(sc_card_t *card, sc_pkcs15_object_t *object) { keymaprecord->unknownWithFFFF = (unsigned short) (-1); keymaprecord->keyref = 0xB000 + kid; r = gids_perform_create_keyfile(card, keytype, kid, algid); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to create the key file"); + LOG_TEST_RET(card->ctx, r, "unable to create the key file"); r = gids_update_cardcf(card, 0, 1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cardcf file regarding container"); + LOG_TEST_RET(card->ctx, r, "unable to update the cardcf file regarding container"); r = gids_put_DO(card, KEYMAP_FI, KEYMAP_DO, keymapbuffer, keymapbuffersize); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the keymap file"); + LOG_TEST_RET(card->ctx, r, "unable to write the keymap file"); r = gids_write_gidsfile(card, "mscp", "cmapfile", cmapbuffer, cmapbuffersize); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the cmap file after the container creation"); + LOG_TEST_RET(card->ctx, r, "unable to write the cmap file after the container creation"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } // generate a key on an existing container @@ -1361,10 +1414,11 @@ static int gids_generate_key(sc_card_t *card, sc_pkcs15_object_t *object, struct size_t buffersize = 0; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY); + if ((object->type & SC_PKCS15_TYPE_CLASS_MASK) != SC_PKCS15_TYPE_PRKEY) + return SC_ERROR_INTERNAL; if ((key_info->key_reference > GIDS_FIRST_KEY_IDENTIFIER + GIDS_MAX_CONTAINER) || (kid < GIDS_FIRST_KEY_IDENTIFIER)) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_GENERATE_ASYMECTRIC_KEY_PAIR, 0x00, 0x00); @@ -1373,17 +1427,17 @@ static int gids_generate_key(sc_card_t *card, sc_pkcs15_object_t *object, struct apdu.data = generatekey; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "generate key returned error"); + LOG_TEST_RET(card->ctx, r, "generate key returned error"); r = gids_read_public_key(card, 0, NULL, kid, 0, &buffer, &buffersize); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "read public key returned error"); + LOG_TEST_RET(card->ctx, r, "read public key returned error"); r = sc_pkcs15_decode_pubkey(card->ctx, pubkey, buffer, buffersize); if (buffer) free(buffer); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } // import the key in an existing container @@ -1429,14 +1483,15 @@ static int gids_import_key(sc_card_t *card, sc_pkcs15_object_t *object, sc_pkcs1 { NULL, 0, 0, 0, NULL, NULL } }; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY); + if ((object->type & SC_PKCS15_TYPE_CLASS_MASK) != SC_PKCS15_TYPE_PRKEY) + return SC_ERROR_INTERNAL; if (object->type != SC_PKCS15_TYPE_PRKEY_RSA || key->algorithm != SC_ALGORITHM_RSA) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "GIDS supports RSA keys only (but may support ECC one day)."); + sc_log(card->ctx, "GIDS supports RSA keys only (but may support ECC one day)."); return SC_ERROR_NOT_SUPPORTED; } if (!key->u.rsa.dmp1.len || !key->u.rsa.dmq1.len || !key->u.rsa.iqmp.len) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "GIDS needs dmp1 & dmq1 & iqmp"); + sc_log(card->ctx, "GIDS needs dmp1 & dmq1 & iqmp"); return SC_ERROR_NOT_SUPPORTED; } sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 0, &version, NULL, 1); @@ -1459,10 +1514,10 @@ static int gids_import_key(sc_card_t *card, sc_pkcs15_object_t *object, sc_pkcs1 sc_format_asn1_entry(asn1_key_usage_template + 1, asn1_key_value_template, NULL, 1); r = sc_asn1_encode(card->ctx, asn1_key_usage_template, &buffer, &buflen); - SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to encode the private key"); + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE, r, "unable to encode the private key"); r = gids_put_DO(card, GIDS_APPLET_EFID, GIDS_PUT_KEY_DO, buffer, buflen); - SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to put the private key - key greater than 2048 bits ?"); + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE, r, "unable to put the private key - key greater than 2048 bits ?"); r = SC_SUCCESS; err: sc_mem_clear(buffer, buflen); @@ -1474,14 +1529,14 @@ static int gids_delete_key_file(sc_card_t *card, int containernum) { int r; char ch_tmp[10]; sc_path_t cpath; - snprintf(ch_tmp, sizeof(ch_tmp), "B0%02X",containernum + GIDS_FIRST_KEY_IDENTIFIER); + snprintf(ch_tmp, sizeof(ch_tmp), "3FFFB0%02X", (u8) (0xFF & (containernum + GIDS_FIRST_KEY_IDENTIFIER))); sc_format_path(ch_tmp, &cpath); - r = iso_ops->select_file(card, &cpath, NULL); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to select the key file"); + r = gids_select_file(card, &cpath, NULL); + LOG_TEST_RET(card->ctx, r, "unable to select the key file"); // delete current selected file memset(&cpath, 0, sizeof(cpath)); r = iso_ops->delete_file(card, &cpath); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to delete the key file"); + LOG_TEST_RET(card->ctx, r, "unable to delete the key file"); return r; } @@ -1493,7 +1548,7 @@ static int gids_encode_certificate(sc_card_t *card, u8* source, size_t sourcesiz return SC_ERROR_BUFFER_TOO_SMALL; } if (sourcesize > 0xFFFF) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } // format is: // 2 bytes for compression version @@ -1505,7 +1560,7 @@ static int gids_encode_certificate(sc_card_t *card, u8* source, size_t sourcesiz destination[3] = (sourcesize & 0xFF00) >> 8; outlen = *destinationsize - 4; r = sc_compress(destination + 4, &outlen, source, sourcesize, COMPRESSION_ZLIB); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to compress the certificate"); + LOG_TEST_RET(card->ctx, r, "unable to compress the certificate"); *destinationsize = outlen + 4; return SC_SUCCESS; } @@ -1520,18 +1575,20 @@ static int gids_save_certificate(sc_card_t *card, sc_pkcs15_object_t *certobject struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *) privkeyobject->data; unsigned char containernum; char filename[9]; - assert((certobject->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_CERT); - assert((privkeyobject->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY); + if ((certobject->type & SC_PKCS15_TYPE_CLASS_MASK) != SC_PKCS15_TYPE_CERT) + return SC_ERROR_INTERNAL; + if ((privkeyobject->type & SC_PKCS15_TYPE_CLASS_MASK) != SC_PKCS15_TYPE_PRKEY) + return SC_ERROR_INTERNAL; // refresh the cached data in case some thing has been modified r = gids_read_masterfile(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read masterfile failed"); + LOG_TEST_RET(card->ctx, r, "gids read masterfile failed"); r= gids_read_cmapfile(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read cmapfile failed"); + LOG_TEST_RET(card->ctx, r, "gids read cmapfile failed"); // compress the certificate according to the minidriver specification r = gids_encode_certificate(card, cert_info->value.value, cert_info->value.len, certbuffer, &certbuffersize); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to encode the certificate"); + LOG_TEST_RET(card->ctx, r, "unable to encode the certificate"); // save it to a minidriver file containernum = prkey_info->key_reference - GIDS_FIRST_KEY_IDENTIFIER; @@ -1544,14 +1601,14 @@ static int gids_save_certificate(sc_card_t *card, sc_pkcs15_object_t *certobject r = gids_does_file_exists(card, "mscp", filename); if (r == SC_ERROR_FILE_NOT_FOUND) { r = gids_create_file(card, "mscp", filename); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to create the certificate file"); + LOG_TEST_RET(card->ctx, r, "gids unable to create the certificate file"); } r = gids_write_gidsfile(card, "mscp", filename, certbuffer, certbuffersize); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to write the certificate data"); + LOG_TEST_RET(card->ctx, r, "gids unable to write the certificate data"); // return the path to the DO r = gids_build_certificate_path(card, containernum, !(prkey_info->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT), path); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to build the certificate path"); + LOG_TEST_RET(card->ctx, r, "gids unable to build the certificate path"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -1576,18 +1633,18 @@ static int gids_delete_container_num(sc_card_t *card, size_t containernum) { // sanity check if (containernum >= recordnum || recordnum > GIDS_MAX_CONTAINER) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); // refresh the key map file keymapbuffersize = sizeof(keymapbuffer); r = gids_get_DO(card, KEYMAP_FI, KEYMAP_DO, keymapbuffer, &keymapbuffersize); if (r<0) { // the keymap DO should be present if the cmapfile is not empty - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } keymaprecordnum = (keymapbuffersize - 1) / sizeof(struct gids_keymap_record); if (keymaprecordnum != recordnum) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } // update the key map file @@ -1603,16 +1660,16 @@ static int gids_delete_container_num(sc_card_t *card, size_t containernum) { // remove the key, update the key map & cmap file and signal the change r = gids_delete_key_file(card, (int) containernum); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to delete the key file"); + LOG_TEST_RET(card->ctx, r, "unable to delete the key file"); r = gids_update_cardcf(card, 0, 1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cardcf file regarding container"); + LOG_TEST_RET(card->ctx, r, "unable to update the cardcf file regarding container"); r = gids_put_DO(card, KEYMAP_FI, KEYMAP_DO, keymapbuffer, keymapbuffersize); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the keymap file"); + LOG_TEST_RET(card->ctx, r, "unable to write the keymap file"); r = gids_write_gidsfile(card, "mscp", "cmapfile", cmapbuffer, cmapbuffersize); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the cmap file after the container creation"); + LOG_TEST_RET(card->ctx, r, "unable to write the cmap file after the container creation"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } // delete a certificate associated to a container @@ -1623,21 +1680,21 @@ static int gids_delete_cert(sc_card_t *card, sc_pkcs15_object_t* object) { unsigned short fileIdentifier, DO; u8 masterfilebuffer[MAX_GIDS_FILE_SIZE]; size_t masterfilebuffersize = 0; - gids_mf_record_t *records = (gids_mf_record_t *) masterfilebuffer; + gids_mf_record_t *records = (gids_mf_record_t *) (masterfilebuffer+1); size_t recordcount, recordnum = (size_t) -1; size_t i; - - assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_CERT); + if ((object->type & SC_PKCS15_TYPE_CLASS_MASK) != SC_PKCS15_TYPE_CERT) + return SC_ERROR_INTERNAL; // refresh the cached data in case some thing has been modified r = gids_read_masterfile(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read masterfile failed"); + LOG_TEST_RET(card->ctx, r, "gids read masterfile failed"); r= gids_read_cmapfile(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read cmapfile failed"); + LOG_TEST_RET(card->ctx, r, "gids read cmapfile failed"); // remove the file reference from the masterfile if (cert_info->path.len != 4) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } fileIdentifier = cert_info->path.value[0] * 0x100 + cert_info->path.value[1]; DO = cert_info->path.value[2] * 0x100 + cert_info->path.value[3]; @@ -1645,7 +1702,7 @@ static int gids_delete_cert(sc_card_t *card, sc_pkcs15_object_t* object) { memcpy(masterfilebuffer, privatedata->masterfile, privatedata->masterfilesize); masterfilebuffersize = privatedata->masterfilesize; - recordcount = (masterfilebuffersize / sizeof(gids_mf_record_t)); + recordcount = ((masterfilebuffersize-1) / sizeof(gids_mf_record_t)); for (i = 0; i < recordcount; i++) { if (records[i].fileIdentifier == fileIdentifier && records[i].dataObjectIdentifier == DO) { recordnum = i; @@ -1653,23 +1710,23 @@ static int gids_delete_cert(sc_card_t *card, sc_pkcs15_object_t* object) { } } if (recordnum == (size_t) -1) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND); } - for (i = (recordnum+1) * sizeof(gids_mf_record_t); i < masterfilebuffersize; i++) { + for (i = 1 + (recordnum+1) * sizeof(gids_mf_record_t); i < masterfilebuffersize; i++) { masterfilebuffer[i - sizeof(gids_mf_record_t)] = masterfilebuffer[i]; } masterfilebuffersize -= sizeof(gids_mf_record_t); // remove the DO, update the masterfile, and signal the change r = gids_update_cardcf(card, 1, 0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cache file"); + LOG_TEST_RET(card->ctx, r, "unable to update the cache file"); r = gids_put_DO(card, fileIdentifier, DO, NULL, 0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to delete the certificate DO"); + LOG_TEST_RET(card->ctx, r, "gids unable to delete the certificate DO"); r = gids_put_DO(card, MF_FI, MF_DO, masterfilebuffer, masterfilebuffersize); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to update the masterfile"); + LOG_TEST_RET(card->ctx, r, "gids unable to update the masterfile"); memcpy(privatedata->masterfile, masterfilebuffer, masterfilebuffersize); privatedata->masterfilesize = masterfilebuffersize; @@ -1682,16 +1739,17 @@ static int gids_delete_key(sc_card_t *card, sc_pkcs15_object_t* object) { size_t containernum; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; - assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY); + if ((object->type & SC_PKCS15_TYPE_CLASS_MASK) != SC_PKCS15_TYPE_PRKEY) + return SC_ERROR_INTERNAL; // refresh the cached data in case some thing has been modified r = gids_read_masterfile(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read masterfile failed"); + LOG_TEST_RET(card->ctx, r, "gids read masterfile failed"); r = gids_read_cmapfile(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read cmapfile failed"); + LOG_TEST_RET(card->ctx, r, "gids read cmapfile failed"); containernum = key_info->key_reference - GIDS_FIRST_KEY_IDENTIFIER; r = gids_delete_container_num(card, containernum); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to delete the container"); + LOG_TEST_RET(card->ctx, r, "gids unable to delete the container"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -1705,16 +1763,16 @@ static int gids_initialize_create_file(sc_card_t *card, u8* command, size_t comm apdu.datalen = commandsize; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU1 transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + LOG_TEST_RET(card->ctx, r, "APDU1 transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); // activate file sc_format_apdu(card, &apdu, SC_APDU_CASE_1, INS_ACTIVATE_FILE, 0x00, 0x00); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU2 transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_TEST_RET(card->ctx, r, "APDU2 transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + LOG_FUNC_RETURN(card->ctx, r); } // used by gids_initialize to set the admin key @@ -1733,7 +1791,7 @@ static int gids_set_administrator_key(sc_card_t *card, u8* key) { memcpy(adminKeyData+7, key, 24); r = gids_put_DO(card, GIDS_APPLET_EFID, GIDS_PUT_KEY_DO, adminKeyData, sizeof(adminKeyData)); sc_mem_clear(adminKeyData, sizeof(adminKeyData)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to set the admin key"); + LOG_TEST_RET(card->ctx, r, "gids unable to set the admin key"); return SC_SUCCESS; } @@ -1766,7 +1824,7 @@ static int gids_initialize(sc_card_t *card, sc_cardctl_gids_init_param_t* param) u8 EveryoneReadAdminWriteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x12,0x8C,0x03,0x03,0x20,0x00}; u8 UserReadWriteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x13,0x8C,0x03,0x03,0x30,0x30}; u8 AdminReadWriteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x14,0x8C,0x03,0x03,0x20,0x20}; - // File type=18=key file ; type = symetric key + // File type=18=key file ; type = symmetric key u8 AdminKey[] = {0x62,0x1A,0x82,0x01,0x18,0x83,0x02,0xB0,0x80,0x8C,0x04,0x87,0x00,0x20,0xFF,0xA5, 0x0B,0xA4,0x09,0x80,0x01,0x02,0x83,0x01,0x80,0x95,0x01,0xC0}; // file used to store other file references. Format undocumented. @@ -1788,7 +1846,7 @@ static int gids_initialize(sc_card_t *card, sc_cardctl_gids_init_param_t* param) // avoid multiple initialization r = gids_check_that_card_is_new(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "card seems to have been already initialized"); + LOG_TEST_RET(card->ctx, r, "card seems to have been already initialized"); memset(&pindata, 0, sizeof(pindata)); // create PIN & PUK @@ -1798,52 +1856,53 @@ static int gids_initialize(sc_card_t *card, sc_cardctl_gids_init_param_t* param) pindata.pin2.data = param->user_pin; pindata.pin_reference = 0x80; - r = sc_pin_cmd(card, &pindata, NULL); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids set pin"); + r = sc_pin_cmd(card, &pindata); + LOG_TEST_RET(card->ctx, r, "gids set pin"); // create file r = gids_initialize_create_file(card, UserCreateDeleteDirAc, sizeof(UserCreateDeleteDirAc)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file UserCreateDeleteDirAc"); + LOG_TEST_RET(card->ctx, r, "gids to create the file UserCreateDeleteDirAc"); r = gids_initialize_create_file(card, EveryoneReadUserWriteAc, sizeof(EveryoneReadUserWriteAc)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file EveryoneReadUserWriteAc"); + LOG_TEST_RET(card->ctx, r, "gids to create the file EveryoneReadUserWriteAc"); r = gids_initialize_create_file(card, UserWriteExecuteAc, sizeof(UserWriteExecuteAc)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file UserWriteExecuteAc"); + LOG_TEST_RET(card->ctx, r, "gids to create the file UserWriteExecuteAc"); r = gids_initialize_create_file(card, EveryoneReadAdminWriteAc, sizeof(EveryoneReadAdminWriteAc)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file EveryoneReadAdminWriteAc"); + LOG_TEST_RET(card->ctx, r, "gids to create the file EveryoneReadAdminWriteAc"); r = gids_initialize_create_file(card, UserReadWriteAc, sizeof(UserReadWriteAc)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file UserReadWriteAc"); + LOG_TEST_RET(card->ctx, r, "gids to create the file UserReadWriteAc"); r = gids_initialize_create_file(card, AdminReadWriteAc, sizeof(AdminReadWriteAc)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file AdminReadWriteAc"); + LOG_TEST_RET(card->ctx, r, "gids to create the file AdminReadWriteAc"); //admin key r = gids_initialize_create_file(card, AdminKey, sizeof(AdminKey)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file AdminKey"); + LOG_TEST_RET(card->ctx, r, "gids to create the file AdminKey"); r = gids_set_administrator_key(card, param->init_code); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to set the admin key"); + LOG_TEST_RET(card->ctx, r, "gids unable to set the admin key"); // create the filesystem r = gids_put_DO(card, MF_FI, MF_DO, masterfile, sizeof(masterfile)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the masterfile"); + LOG_TEST_RET(card->ctx, r, "gids unable to save the masterfile"); r = gids_put_DO(card, CARDAPPS_FI, CARDAPPS_DO, cardapps, sizeof(cardapps)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the cardapps"); + LOG_TEST_RET(card->ctx, r, "gids unable to save the cardapps"); r = gids_put_DO(card, CARDCF_FI, CARDCF_DO, cardcf, sizeof(cardcf)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the cardcf"); + LOG_TEST_RET(card->ctx, r, "gids unable to save the cardcf"); r = gids_put_DO(card, CMAP_FI, CMAP_DO, NULL, 0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the cmapfile"); + LOG_TEST_RET(card->ctx, r, "gids unable to save the cmapfile"); #ifdef ENABLE_OPENSSL for (i = sizeof(param->cardid) -1; i >= 0; i--) { if (param->cardid[i]) break; } if (i < 0) { // set a random cardid if not set - r = RAND_bytes(param->cardid, sizeof(param->cardid)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to set a random serial number"); - + if (RAND_bytes(param->cardid, sizeof(param->cardid)) != 1) { + sc_log_openssl(card->ctx); + LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, "unable to set a random serial number"); + } } #endif r = gids_put_DO(card, CARDID_FI, CARDID_DO, param->cardid, sizeof(param->cardid)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the cardid"); + LOG_TEST_RET(card->ctx, r, "gids unable to save the cardid"); //select applet sc_format_apdu(card, &apdu, SC_APDU_CASE_3, INS_SELECT, 0x00, 0x0C); @@ -1852,14 +1911,14 @@ static int gids_initialize(sc_card_t *card, sc_cardctl_gids_init_param_t* param) apdu.datalen = 2; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); // activate file sc_format_apdu(card, &apdu, SC_APDU_CASE_1, INS_ACTIVATE_FILE, 0x00, 0x00); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + LOG_FUNC_RETURN(card->ctx, r); } // execute an admin authentication based on a secret key @@ -1870,7 +1929,7 @@ static int gids_initialize(sc_card_t *card, sc_cardctl_gids_init_param_t* param) // this data comes from the reverse of the GIDS minidriver. static int gids_authenticate_admin(sc_card_t *card, u8* key) { #ifndef ENABLE_OPENSSL - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); #else EVP_CIPHER_CTX *ctx = NULL; int r; @@ -1879,22 +1938,17 @@ static int gids_authenticate_admin(sc_card_t *card, u8* key) { u8 apduSetRandomResponse[256]; u8* randomR2 = apduSetRandomResponse+4; u8 apduSendReponse[40 + 4] = {0x7C,0x2A,0x82,0x28}; - // according to the specification, the z size (z1||z2) should be 14 bytes - // but because the buffer must be a multiple of the 3DES block size (8 bytes), 7 isn't working u8 z1[8]; u8 buffer[16+16+8]; u8* buffer2 = apduSendReponse + 4; int buffer2size = 40; u8 apduSendResponseResponse[256]; + u8 buffer3[16+16+8]; + int buffer3size = 40; sc_apdu_t apdu; - const EVP_CIPHER *cipher; + EVP_CIPHER *cipher; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - // this is CBC instead of ECB - cipher = EVP_des_ede3_cbc(); - if (!cipher) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); - } // select the admin key sc_format_apdu(card, &apdu, SC_APDU_CASE_3, INS_MANAGE_SECURITY_ENVIRONMENT, 0xC1, 0xA4); @@ -1902,15 +1956,16 @@ static int gids_authenticate_admin(sc_card_t *card, u8* key) { apdu.data = (const unsigned char *) "\x83\x01\x80"; apdu.datalen = 3; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); // generate a challenge - r = RAND_bytes(randomR1, 16); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to set computer random"); + if (RAND_bytes(randomR1, 16) != 1) { + sc_log_openssl(card->ctx); + LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, "unable to set computer random"); + } // send it to the card - memcpy(apduSetRandom+4, randomR1, 16); sc_format_apdu(card, &apdu, SC_APDU_CASE_4, INS_GENERAL_AUTHENTICATE, 0x00, 0x00); apdu.lc = sizeof(apduSetRandom); apdu.data = apduSetRandom; @@ -1919,12 +1974,17 @@ static int gids_authenticate_admin(sc_card_t *card, u8* key) { apdu.resplen = sizeof(apduSetRandomResponse); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); // compute the half size of the mutual authentication secret - r = RAND_bytes(z1, sizeof(z1)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to set computer random"); + if (RAND_bytes(z1, 7) != 1) { + sc_log_openssl(card->ctx); + LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, "unable to set computer random"); + } + + // set the padding + z1[7] = 0x80; // Encrypt R2||R1||Z1 memcpy(buffer, randomR2, 16); @@ -1933,25 +1993,34 @@ static int gids_authenticate_admin(sc_card_t *card, u8* key) { // init crypto ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + sc_log_openssl(card->ctx); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } - + cipher = sc_evp_cipher(card->ctx, "DES-EDE3-CBC"); if (!EVP_EncryptInit(ctx, cipher, key, NULL)) { + sc_log_openssl(card->ctx); EVP_CIPHER_CTX_free(ctx); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + sc_evp_cipher_free(cipher); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } EVP_CIPHER_CTX_set_padding(ctx,0); if (!EVP_EncryptUpdate(ctx, buffer2, &buffer2size, buffer, sizeof(buffer))) { + sc_log_openssl(card->ctx); EVP_CIPHER_CTX_free(ctx); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + sc_evp_cipher_free(cipher); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } - if(!EVP_EncryptFinal(ctx, buffer2+buffer2size, &buffer2size)) { + if (!EVP_EncryptFinal(ctx, buffer2 + buffer2size, &buffer2size)) { + sc_log_openssl(card->ctx); EVP_CIPHER_CTX_free(ctx); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + sc_evp_cipher_free(cipher); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } EVP_CIPHER_CTX_free(ctx); ctx = NULL; + sc_evp_cipher_free(cipher); + cipher = NULL; // send it to the card sc_format_apdu(card, &apdu, SC_APDU_CASE_4, INS_GENERAL_AUTHENTICATE, 0x00, 0x00); apdu.lc = sizeof(apduSendReponse); @@ -1961,16 +2030,75 @@ static int gids_authenticate_admin(sc_card_t *card, u8* key) { apdu.resplen = sizeof(apduSendResponseResponse); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + if (apdu.resplen != 44) + { + sc_log(card->ctx, "Expecting a response len of 44 - found %d", (int)apdu.resplen); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + // init crypto + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + cipher = sc_evp_cipher(card->ctx, "DES-EDE3-CBC"); + if (!EVP_DecryptInit(ctx, cipher, key, NULL)) { + sc_log_openssl(card->ctx); + sc_evp_cipher_free(cipher); + EVP_CIPHER_CTX_free(ctx); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + EVP_CIPHER_CTX_set_padding(ctx,0); + if (!EVP_DecryptUpdate(ctx, buffer3, &buffer3size, apdu.resp + 4, (int)apdu.resplen - 4)) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "unable to decrypt data"); + sc_evp_cipher_free(cipher); + EVP_CIPHER_CTX_free(ctx); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); + } + if (!EVP_DecryptFinal(ctx, buffer3 + buffer3size, &buffer3size)) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "unable to decrypt final data"); + sc_evp_cipher_free(cipher); + EVP_CIPHER_CTX_free(ctx); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); + } + sc_log(card->ctx, "data has been decrypted using the key"); + if (memcmp(buffer3, randomR1, 16) != 0) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "R1 doesn't match"); + sc_evp_cipher_free(cipher); + EVP_CIPHER_CTX_free(ctx); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); + } + if (memcmp(buffer3 + 16, randomR2, 16) != 0) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "R2 doesn't match"); + sc_evp_cipher_free(cipher); + EVP_CIPHER_CTX_free(ctx); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); + } + if (buffer[39] != 0x80) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "Padding not found"); + sc_evp_cipher_free(cipher); + EVP_CIPHER_CTX_free(ctx); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); + } + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + sc_evp_cipher_free(cipher); + cipher = NULL; + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); #endif } static int gids_card_ctl(sc_card_t * card, unsigned long cmd, void *ptr) { - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); switch (cmd) { case SC_CARDCTL_GET_SERIALNR: return gids_get_serialnr(card, (sc_serial_number_t *) ptr); @@ -2004,13 +2132,27 @@ static int gids_card_ctl(sc_card_t * card, unsigned long cmd, void *ptr) } } +static int gids_card_reader_lock_obtained(sc_card_t *card, int was_reset) +{ + int r = SC_SUCCESS; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (was_reset > 0) { + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + size_t resplen = sizeof(rbuf); + r = gids_select_aid(card, gids_aid.value, gids_aid.len, rbuf, &resplen); + } + + LOG_FUNC_RETURN(card->ctx, r); +} + static struct sc_card_driver *sc_get_driver(void) { if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; - gids_ops.match_card = gids_match_card; gids_ops.init = gids_init; gids_ops.finish = gids_finish; @@ -2029,7 +2171,7 @@ static struct sc_card_driver *sc_get_driver(void) gids_ops.logout = gids_logout; gids_ops.restore_security_env = NULL; gids_ops.set_security_env = gids_set_security_env; - gids_ops.decipher = iso_ops->decipher; + gids_ops.decipher = gids_decipher; gids_ops.compute_signature = iso_ops->compute_signature; gids_ops.change_reference_data = NULL; // see pin_cmd gids_ops.reset_retry_counter = NULL; // see pin_cmd @@ -2045,6 +2187,8 @@ static struct sc_card_driver *sc_get_driver(void) gids_ops.put_data = NULL; gids_ops.delete_record = NULL; gids_ops.read_public_key = gids_read_public_key; + gids_ops.card_reader_lock_obtained = gids_card_reader_lock_obtained; + return &gids_drv; } diff --git a/src/libopensc/card-gids.h b/src/libopensc/card-gids.h index 03ab85a2ce..f9ea51ba6c 100644 --- a/src/libopensc/card-gids.h +++ b/src/libopensc/card-gids.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CARD_GIDS_H_ diff --git a/src/libopensc/card-gpk.c b/src/libopensc/card-gpk.c deleted file mode 100644 index 86bfebd8b7..0000000000 --- a/src/libopensc/card-gpk.c +++ /dev/null @@ -1,1836 +0,0 @@ -/* - * card-gpk: Driver for GPK 4000 cards - * - * Copyright (C) 2002 Olaf Kirch - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif -#ifdef ENABLE_OPENSSL /* empty file without openssl */ - -#include -#include -#include -#include - -#include "internal.h" -#include "cardctl.h" -#include "pkcs15.h" - -#define GPK_SEL_MF 0x00 -#define GPK_SEL_DF 0x01 -#define GPK_SEL_EF 0x02 -#define GPK_SEL_AID 0x04 -#define GPK_FID_MF 0x3F00 - -#define GPK_FTYPE_SC 0x21 - -#define GPK_SIGN_RSA_MD5 0x11 -#define GPK_SIGN_RSA_SHA 0x12 -#define GPK_SIGN_RSA_SSL 0x18 -#define GPK_VERIFY_RSA_MD5 0x21 -#define GPK_VERIFY_RSA_SHA 0x22 -#define GPK_AUTH_RSA_MD5 0x31 -#define GPK_AUTH_RSA_SHA 0x32 -#define GPK_AUTH_RSA_SSL 0x38 -#define GPK_UNWRAP_RSA 0x77 - -#define GPK_MAX_PINS 8 -#define GPK_HASH_CHUNK 62 - -/* - * GPK4000 private data - */ -struct gpk_private_data { - /* The GPK usually do file offsets in multiples of - * 4 bytes. This can be customized however. We - * should really query for this during gpk_init */ - unsigned int offset_shift; - unsigned int offset_mask; - unsigned int locked : 1, - sample_card : 1; - - /* access control bits of file most recently selected */ - unsigned short int ac[3]; - - /* is non-zero if we should use secure messaging */ - unsigned key_set : 1; - unsigned int key_reference; - u8 key[16]; - - /* crypto related data from set_security_env */ - unsigned int sec_algorithm; - unsigned int sec_hash_len; - unsigned int sec_mod_len; - unsigned int sec_padding; -}; -#define DRVDATA(card) ((struct gpk_private_data *) ((card)->drv_data)) - -static int gpk_get_info(sc_card_t *, int, int, u8 *, size_t); - -/* - * ATRs of GPK4000 cards courtesy of libscez - */ -static struct sc_atr_table gpk_atrs[] = { - { "3B:27:00:80:65:A2:04:01:01:37", NULL, "GPK 4K", SC_CARD_TYPE_GPK_GPK4000_s, 0, NULL }, - { "3B:27:00:80:65:A2:05:01:01:37", NULL, "GPK 4K", SC_CARD_TYPE_GPK_GPK4000_sp, 0, NULL }, - { "3B:27:00:80:65:A2:0C:01:01:37", NULL, "GPK 4K", SC_CARD_TYPE_GPK_GPK4000_su256, 0, NULL }, - { "3B:A7:00:40:14:80:65:A2:14:01:01:37", NULL, "GPK 4K", SC_CARD_TYPE_GPK_GPK4000_sdo, 0, NULL }, - { "3B:A7:00:40:18:80:65:A2:08:01:01:52", NULL, "GPK 8K", SC_CARD_TYPE_GPK_GPK8000_8K, 0, NULL }, - { "3B:A7:00:40:18:80:65:A2:09:01:01:52", NULL, "GPK 8K", SC_CARD_TYPE_GPK_GPK8000_16K, 0, NULL }, - { "3B:A7:00:40:18:80:65:A2:09:01:02:52", NULL, "GPK 16K", SC_CARD_TYPE_GPK_GPK16000, 0, NULL }, - { "3B:A7:00:40:18:80:65:A2:09:01:03:52", NULL, "GPK 16K", SC_CARD_TYPE_GPK_GPK16000, 0, NULL }, - { NULL, NULL, NULL, 0, 0, NULL } -}; - -/* - * Driver and card ops structures - */ -static struct sc_card_operations gpk_ops, *iso_ops; -static struct sc_card_driver gpk_drv = { - "Gemplus GPK", - "gpk", - &gpk_ops, - NULL, 0, NULL -}; - -/* - * return 1 if this driver can handle the card - */ -static int -gpk_match_card(sc_card_t *card) -{ - int i; - - i = _sc_match_atr(card, gpk_atrs, &card->type); - if (i < 0) { - const u8 *hist_bytes = card->reader->atr_info.hist_bytes; - - /* Gemplus GPK docs say we can use just the - * FMN and PRN fields of the historical bytes - * to recognize a GPK card - * See Table 43, pp. 188 - * We'll use the first 2 bytes as well - */ - - if ((card->reader->atr_info.hist_bytes_len >= 7) - && (hist_bytes[0] == 0x80) - && (hist_bytes[1] == 0x65) - && (hist_bytes[2] == 0xa2)) { /* FMN */ - if (hist_bytes[3] == 0x08) { /* PRN? */ - card->type = SC_CARD_TYPE_GPK_GPK8000; - return 1; - } - if (hist_bytes[3] == 0x09) { /* PRN? */ - card->type = SC_CARD_TYPE_GPK_GPK16000; - return 1; - } - } - return 0; - } - return 1; -} - -/* - * Initialize the card struct - */ -static int -gpk_init(sc_card_t *card) -{ - struct gpk_private_data *priv; - unsigned long exponent, flags, kg; - unsigned char info[13]; - - card->drv_data = priv = calloc(1, sizeof(*priv)); - if (card->drv_data == NULL) - return SC_ERROR_OUT_OF_MEMORY; - - /* read/write/update binary expect offset to be the - * number of 32 bit words. - * offset_shift is the shift value. - * offset_mask is the corresponding mask. */ - priv->offset_shift = 2; - priv->offset_mask = 3; - card->cla = 0x00; - - /* Set up algorithm info. GPK 16000 will do any RSA - * exponent, earlier ones are restricted to 0x10001 */ - flags = SC_ALGORITHM_RSA_HASH_MD5 | SC_ALGORITHM_RSA_HASH_SHA1 - | SC_ALGORITHM_RSA_HASH_MD5_SHA1; - flags |= SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_PAD_ANSI - | SC_ALGORITHM_RSA_PAD_ISO9796; - exponent = (card->type < SC_CARD_TYPE_GPK_GPK16000) ? 0x10001 : 0; - kg = (card->type >= SC_CARD_TYPE_GPK_GPK8000) ? SC_ALGORITHM_ONBOARD_KEY_GEN : 0; - _sc_card_add_rsa_alg(card, 512, flags|kg, exponent); - _sc_card_add_rsa_alg(card, 768, flags, exponent); - _sc_card_add_rsa_alg(card, 1024, flags|kg, exponent); - - /* Inspect the LOCK byte */ - if (gpk_get_info(card, 0x02, 0xA4, info, sizeof(info)) >= 0) { - if (info[12] & 0x40) { - priv->offset_shift = 0; - priv->offset_mask = 0; - } - if (info[12] & 0x10) { - /* DSA supported - add algo information. - * It's highly unlikely we'll ever see this. - */ - } - if (info[12] & 0x08) { - priv->locked = 1; - } - /* Sample cards use a transport key of "TEST KEYTEST KEY" */ - if (!memcmp(info+5, "\x00\xff\x00", 3)) { - priv->sample_card = 1; - } - } - - /* State that we have an RNG */ - card->caps |= SC_CARD_CAP_RNG; - - /* Make sure max send/receive size is 4 byte aligned and <256. */ - card->max_recv_size = 252; - - return 0; -} - -/* - * Card is being closed; discard any private data etc - */ -static int -gpk_finish(sc_card_t *card) -{ - if (card->drv_data) - free(card->drv_data); - card->drv_data = NULL; - return 0; -} - -/* - * Select a DF/EF - */ -static int -match_path(sc_card_t *card, unsigned short int **pathptr, size_t *pathlen, - int need_info) -{ - unsigned short int *curptr, *ptr; - size_t curlen, len; - size_t i; - - curptr = (unsigned short int *) card->cache.current_path.value; - curlen = card->cache.current_path.len; - ptr = *pathptr; - len = *pathlen; - - if (curlen < 1 || len < 1) - return 0; - - /* Make sure path starts with MF. - * Note the cached path should always begin with MF. */ - if (ptr[0] != GPK_FID_MF || curptr[0] != GPK_FID_MF) - return 0; - - for (i = 1; i < len && i < curlen; i++) { - if (ptr[i] != curptr[i]) - break; - } - - if (len < curlen) { - /* Caller asked us to select the DF, but the - * current file is some EF within the DF we're - * interested in. Say ACK */ - if (len == 2) - goto okay; - /* Anything else won't work */ - return 0; - } - - /* In the case of an exact match: - * If the caller needs info on the file to be selected, - * make sure we at least select the file itself. - * If the DF matches the current DF, just return the - * FID */ - if (i == len && need_info) { - if (i > 1) { - *pathptr = ptr + len - 1; - *pathlen = len - 1; - return 1; - } - /* bummer */ - return 0; - } - -okay: - *pathptr = ptr + i; - *pathlen = len - i; - return 1; -} - -static void -ac_to_acl(unsigned int ac, sc_file_t *file, unsigned int op) -{ - unsigned int npins, pin; - - npins = (ac >> 14) & 3; - if (npins == 3) { - sc_file_add_acl_entry(file, op, SC_AC_NEVER, - SC_AC_KEY_REF_NONE); - return; - } - - sc_file_add_acl_entry(file, op, SC_AC_NONE, SC_AC_KEY_REF_NONE); - pin = ac & 0xFF; - if (npins >= 1) - sc_file_add_acl_entry(file, op, SC_AC_CHV, (pin >> 4) & 0xF); - if (npins == 2) - sc_file_add_acl_entry(file, op, SC_AC_CHV, pin & 0xF); - - /* Check whether secure messaging key is specified */ - if (ac & 0x3F00) - sc_file_add_acl_entry(file, op, SC_AC_PRO, (ac & 0x3F00) >> 8); -} - -/* - * Convert ACLs requested by the application to access condition - * bits supported by the GPK. Since these do not map 1:1 there's - * some fuzz involved. - */ -static void -acl_to_ac(sc_file_t *file, unsigned int op, u8 *ac) -{ - const sc_acl_entry_t *acl; - unsigned int npins = 0; - - ac[0] = ac[1] = 0; - - if ((acl = sc_file_get_acl_entry(file, op)) == NULL) - return; - - assert(acl->method != SC_AC_UNKNOWN); - switch (acl->method) { - case SC_AC_NEVER: - ac[0] = 0xC0; - return; - case SC_AC_NONE: - return; - } - - while (acl) { - if (acl->method == SC_AC_CHV) { - /* Support up to 2 PINS only */ - if (++npins >= 2) - continue; - ac[1] >>= 4; - ac[1] |= acl->key_ref << 4; - ac[0] += 0x40; - } - if (acl->method == SC_AC_PRO) { - ac[0] |= acl->key_ref & 0x1f; - } - acl = acl->next; - } -} - -static int -gpk_parse_fci(sc_card_t *card, - const u8 *buf, size_t buflen, - sc_file_t *file) -{ - const u8 *end, *next; - unsigned int tag, len; - - end = buf + buflen; - for (; buf + 2 < end; buf = next) { - next = buf + 2 + buf[1]; - if (next > end) - break; - tag = *buf++; - len = *buf++; - if (tag == 0x84) { - /* unknown purpose - usually the name, but - * the content looks weird, such as - * 84 0D A0 00 00 00 18 0F 00 00 01 63 00 01 04 - */ - } else - if (tag == 0xC1 && len >= 2) { - /* Seems to be the file id, followed by something - * C1 04 02 00 00 00 */ - file->id = (buf[0] << 8) | buf[1]; - } else - if (tag == 0xC2) { - /* unknown purpose - * C2 01 01 - */ - } - } - - return 0; -} - -static int -gpk_parse_fileinfo(sc_card_t *card, - const u8 *buf, size_t buflen, - sc_file_t *file) -{ - const u8 *sp, *end, *next; - int i, rc; - - memset(file, 0, sizeof(*file)); - for (i = 0; i < SC_MAX_AC_OPS; i++) - sc_file_add_acl_entry(file, i, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE); - - end = buf + buflen; - for (sp = buf; sp + 2 < end; sp = next) { - next = sp + 2 + sp[1]; - if (next > end) - break; - if (sp[0] == 0x84) { - /* ignore if name is longer than what it should be */ - if (sp[1] > sizeof(file->name)) - continue; - memset(file->name, 0, sizeof(file->name)); - memcpy(file->name, sp+2, sp[1]); - } else - if (sp[0] == 0x85) { - unsigned int ac[3], n; - - file->id = (sp[4] << 8) | sp[5]; - file->size = (sp[8] << 8) | sp[9]; - file->record_length = sp[7]; - - /* Map ACLs. Note the third AC byte is - * valid of EFs only */ - for (n = 0; n < 3; n++) - ac[n] = (sp[10+2*n] << 8) | sp[11+2*n]; - - /* Examine file type */ - switch (sp[6] & 7) { - case 0x01: case 0x02: case 0x03: case 0x04: - case 0x05: case 0x06: case 0x07: - file->type = SC_FILE_TYPE_WORKING_EF; - file->ef_structure = sp[6] & 7; - ac_to_acl(ac[0], file, SC_AC_OP_UPDATE); - ac_to_acl(ac[1], file, SC_AC_OP_WRITE); - ac_to_acl(ac[2], file, SC_AC_OP_READ); - break; - case 0x00: /* 0x38 is DF */ - file->type = SC_FILE_TYPE_DF; - /* Icky: the GPK uses different ACLs - * for creating data files and - * 'sensitive' i.e. key files */ - ac_to_acl(ac[0], file, SC_AC_OP_LOCK); - ac_to_acl(ac[1], file, SC_AC_OP_CREATE); - sc_file_add_acl_entry(file, SC_AC_OP_SELECT, - SC_AC_NONE, SC_AC_KEY_REF_NONE); - sc_file_add_acl_entry(file, SC_AC_OP_DELETE, - SC_AC_NEVER, SC_AC_KEY_REF_NONE); - sc_file_add_acl_entry(file, SC_AC_OP_REHABILITATE, - SC_AC_NEVER, SC_AC_KEY_REF_NONE); - sc_file_add_acl_entry(file, SC_AC_OP_INVALIDATE, - SC_AC_NEVER, SC_AC_KEY_REF_NONE); - sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, - SC_AC_NEVER, SC_AC_KEY_REF_NONE); - break; - } - } else - if (sp[0] == 0x6f) { - /* oops - this is a directory with an IADF. - * This happens with the personalized GemSafe cards - * for instance. */ - file->type = SC_FILE_TYPE_DF; - rc = gpk_parse_fci(card, sp + 2, sp[1], file); - if (rc < 0) - return rc; - } - } - - if (file->record_length) - file->record_count = file->size / file->record_length; - file->magic = SC_FILE_MAGIC; - - return 0; -} - -static int -gpk_select(sc_card_t *card, int kind, - const u8 *buf, size_t buflen, - sc_file_t **file) -{ - struct gpk_private_data *priv = DRVDATA(card); - sc_apdu_t apdu; - u8 resbuf[256]; - int r; - - /* If we're about to select a DF, invalidate secure messaging keys */ - if (kind == GPK_SEL_MF || kind == GPK_SEL_DF) { - memset(priv->key, 0, sizeof(priv->key)); - priv->key_set = 0; - } - - /* do the apdu thing */ - memset(&apdu, 0, sizeof(apdu)); - apdu.cla = 0x00; - apdu.cse = SC_APDU_CASE_3_SHORT; - apdu.ins = 0xA4; - apdu.p1 = kind; - apdu.p2 = 0; - apdu.data = buf; - apdu.datalen = buflen; - apdu.lc = apdu.datalen; - - if (file) { - apdu.cse = SC_APDU_CASE_4_SHORT; - apdu.resp = resbuf; - apdu.resplen = sizeof(resbuf); - apdu.le = sizeof(resbuf); - } - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - /* Nothing we can say about it... invalidate - * path cache */ - if (kind == GPK_SEL_AID) { - card->cache.current_path.len = 0; - } - - if (file == NULL) - return 0; - *file = sc_file_new(); - - r = gpk_parse_fileinfo(card, apdu.resp, apdu.resplen, *file); - if (r < 0) { - sc_file_free(*file); - *file = NULL; - } - return r; -} - -static int -gpk_select_id(sc_card_t *card, int kind, unsigned int fid, - sc_file_t **file) -{ - sc_path_t *cp = &card->cache.current_path; - u8 fbuf[2]; - int r; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "gpk_select_id(0x%04X, kind=%u)\n", fid, kind); - - fbuf[0] = fid >> 8; - fbuf[1] = fid & 0xff; - - r = gpk_select(card, kind, fbuf, 2, file); - - /* Fix up the path cache. - * NB we never cache the ID of an EF, just the DF path */ - if (r == 0) { - unsigned short int *path; - - switch (kind) { - case GPK_SEL_MF: - cp->len = 0; - /* fallthru */ - case GPK_SEL_DF: - assert(cp->len + 1 <= SC_MAX_PATH_SIZE / 2); - path = (unsigned short int *) cp->value; - path[cp->len++] = fid; - } - } else { - cp->len = 0; - } - return r; -} - -static int -gpk_select_file(sc_card_t *card, const sc_path_t *path, - sc_file_t **file) -{ - unsigned short int pathtmp[SC_MAX_PATH_SIZE/2]; - unsigned short int *pathptr; - size_t pathlen, n; - int locked = 0, r = 0, use_relative = 0, retry = 1; - u8 leaf_type; - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - - /* Handle the AID case first */ - if (path->type == SC_PATH_TYPE_DF_NAME) { - if (path->len > 16) - return SC_ERROR_INVALID_ARGUMENTS; - r = gpk_select(card, GPK_SEL_AID, - path->value, path->len, file); - goto done; - } - - /* Now we know we're dealing with 16bit FIDs, either as - * an absolute path name (SC_PATH_TYPE_PATH) or a relative - * FID (SC_PATH_TYPE_FILE_ID) - * - * The API should really tell us whether this is a DF or EF - * we're selecting. All we can do is read tea leaves... - */ - leaf_type = GPK_SEL_EF; - -try_again: - if ((path->len & 1) || path->len > sizeof(pathtmp)) - return SC_ERROR_INVALID_ARGUMENTS; - pathptr = pathtmp; - for (n = 0; n < path->len; n += 2) - pathptr[n>>1] = (path->value[n] << 8)|path->value[n+1]; - pathlen = path->len >> 1; - - /* See whether we can skip an initial portion of the - * (absolute) path */ - if (path->type == SC_PATH_TYPE_PATH) { - /* Do not retry selecting if this cannot be a DF */ - if ((pathptr[0] == GPK_FID_MF && pathlen > 2) - || (pathptr[0] != GPK_FID_MF && pathlen > 1)) - retry = 0; - use_relative = match_path(card, &pathptr, &pathlen, file != 0); - if (pathlen == 0) - goto done; - } else { - /* SC_PATH_TYPE_FILEID */ - if (pathlen > 1) - return SC_ERROR_INVALID_ARGUMENTS; - use_relative = 1; - } - - if (pathlen == 1 && pathptr[0] == GPK_FID_MF) { - /* Select just the MF */ - leaf_type = GPK_SEL_MF; - } else { - if (!locked++) { - r = sc_lock(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); - } - - /* Do we need to select the MF first? */ - if (!use_relative) { - r = gpk_select_id(card, GPK_SEL_MF, GPK_FID_MF, NULL); - if (r) - sc_unlock(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select MF"); - - /* Consume the MF FID if it's there */ - if (pathptr[0] == GPK_FID_MF) { - pathptr++; - pathlen--; - } - if (pathlen == 0) - goto done; - } - - /* Next comes a DF, if at all. - * This loop can deal with nesting levels > 1 even - * though the GPK4000 doesn't support it. */ - while (pathlen > 1) { - r = gpk_select_id(card, GPK_SEL_DF, pathptr[0], NULL); - if (r) - sc_unlock(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select DF"); - pathptr++; - pathlen--; - } - } - - /* Remaining component will be a DF or EF. How do we find out? - * All we can do is try */ - r = gpk_select_id(card, leaf_type, pathptr[0], file); - if (r) { - /* Did we guess EF, and were wrong? If so, invalidate - * path cache and try again; this time aiming for a DF */ - if (leaf_type == GPK_SEL_EF && retry) { - card->cache.current_path.len = 0; - leaf_type = GPK_SEL_DF; - goto try_again; - } - } - -done: - if (locked) - sc_unlock(card); - return r; -} - -/* - * GPK versions of {read,write,update}_binary functions. - * Required because by default the GPKs do word offsets - */ -static int -gpk_read_binary(sc_card_t *card, unsigned int offset, - u8 *buf, size_t count, unsigned long flags) -{ - struct gpk_private_data *priv = DRVDATA(card); - - if (offset & priv->offset_mask) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid file offset (not a multiple of %d)", - priv->offset_mask + 1); - return SC_ERROR_INVALID_ARGUMENTS; - } - return iso_ops->read_binary(card, offset >> priv->offset_shift, - buf, count, flags); -} - -static int -gpk_write_binary(sc_card_t *card, unsigned int offset, - const u8 *buf, size_t count, unsigned long flags) -{ - struct gpk_private_data *priv = DRVDATA(card); - - if (offset & priv->offset_mask) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid file offset (not a multiple of %d)", - priv->offset_mask + 1); - return SC_ERROR_INVALID_ARGUMENTS; - } - return iso_ops->write_binary(card, offset >> priv->offset_shift, - buf, count, flags); -} - -static int -gpk_update_binary(sc_card_t *card, unsigned int offset, - const u8 *buf, size_t count, unsigned long flags) -{ - struct gpk_private_data *priv = DRVDATA(card); - - if (offset & priv->offset_mask) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid file offset (not a multiple of %d)", - priv->offset_mask + 1); - return SC_ERROR_INVALID_ARGUMENTS; - } - return iso_ops->update_binary(card, offset >> priv->offset_shift, - buf, count, flags); -} - -/* - * Secure messaging - */ -static int -gpk_compute_crycks(sc_card_t *card, sc_apdu_t *apdu, - u8 *crycks1) -{ - struct gpk_private_data *priv = DRVDATA(card); - u8 in[8], out[8], block[64]; - unsigned int len = 0, i; - int r = SC_SUCCESS, outl; - EVP_CIPHER_CTX *ctx = NULL; - - ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL) - return SC_ERROR_INTERNAL; - - - /* Fill block with 0x00 and then with the data. */ - memset(block, 0x00, sizeof(block)); - block[len++] = apdu->cla; - block[len++] = apdu->ins; - block[len++] = apdu->p1; - block[len++] = apdu->p2; - block[len++] = apdu->lc + 3; - if ((i = apdu->datalen) + len > sizeof(block)) - i = sizeof(block) - len; - memcpy(block+len, apdu->data, i); - len += i; - - /* Set IV */ - memset(in, 0x00, 8); - - EVP_EncryptInit_ex(ctx, EVP_des_ede_cbc(), NULL, priv->key, in); - for (i = 0; i < len; i += 8) { - if (!EVP_EncryptUpdate(ctx, out, &outl, &block[i], 8)) { - r = SC_ERROR_INTERNAL; - break; - } - } - EVP_CIPHER_CTX_free(ctx); - - memcpy((u8 *) (apdu->data + apdu->datalen), out + 5, 3); - apdu->datalen += 3; - apdu->lc += 3; - apdu->le += 3; - if (crycks1) - memcpy(crycks1, out, 3); - sc_mem_clear(in, sizeof(in)); - sc_mem_clear(out, sizeof(out)); - sc_mem_clear(block, sizeof(block)); - return r; -} - -/* - * Verify secure messaging response - */ -static int -gpk_verify_crycks(sc_card_t *card, sc_apdu_t *apdu, u8 *crycks) -{ - if (apdu->resplen < 3 - || memcmp(apdu->resp + apdu->resplen - 3, crycks, 3)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "Invalid secure messaging reply\n"); - return SC_ERROR_UNKNOWN_DATA_RECEIVED; - } - apdu->resplen -= 3; - return 0; -} - -/* - * Create a file or directory. - * This is a bit tricky because we abuse the ef_structure - * field to transport file types that are non-standard - * (the GPK4000 has lots of bizarre file types). - */ -static int -gpk_create_file(sc_card_t *card, sc_file_t *file) -{ - struct gpk_private_data *priv = DRVDATA(card); - sc_apdu_t apdu; - u8 data[28+3], crycks[3], resp[3]; - size_t datalen, namelen; - int r; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "gpk_create_file(0x%04X)\n", file->id); - - /* Prepare APDU */ - memset(&apdu, 0, sizeof(apdu)); - apdu.cla = 0x80; /* assume no secure messaging */ - apdu.cse = SC_APDU_CASE_3_SHORT; - apdu.ins = 0xE0; - apdu.p2 = 0x00; - - /* clear data */ - memset(data, 0, sizeof(data)); - datalen = 12; - - /* FID */ - data[0] = file->id >> 8; - data[1] = file->id & 0xFF; - - /* encode ACLs */ - if (file->type == SC_FILE_TYPE_DF) { - /* The GPK4000 has separate AC bits for - * creating sensitive files and creating - * data files. Since OpenSC has just the notion - * of "file" we use the same ACL for both AC words - */ - apdu.p1 = 0x01; /* create DF */ - data[2] = 0x38; - acl_to_ac(file, SC_AC_OP_CREATE, data + 6); - acl_to_ac(file, SC_AC_OP_CREATE, data + 8); - if ((namelen = file->namelen) != 0) { - if (namelen > 16) - return SC_ERROR_INVALID_ARGUMENTS; - memcpy(data+datalen, file->name, namelen); - data[5] = namelen; - datalen += namelen; - } - } else { - apdu.p1 = 0x02; /* create EF */ - data[2] = file->ef_structure; - data[3] = file->record_length; - data[4] = file->size >> 8; - data[5] = file->size & 0xff; - acl_to_ac(file, SC_AC_OP_UPDATE, data + 6); - acl_to_ac(file, SC_AC_OP_WRITE, data + 8); - acl_to_ac(file, SC_AC_OP_READ, data + 10); - } - - apdu.data = data; - apdu.datalen = datalen; - apdu.lc = datalen; - - if (priv->key_set) { - apdu.cla = 0x84; - apdu.cse = SC_APDU_CASE_4_SHORT; - r = gpk_compute_crycks(card, &apdu, crycks); - if (r) - return r; - apdu.resp = resp; - apdu.resplen = sizeof(resp); /* XXX? */ - } - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - /* verify secure messaging response */ - if (priv->key_set) - r = gpk_verify_crycks(card, &apdu, crycks); - - return r; -} - -/* - * Set the secure messaging key following a Select FileKey - */ -static int -gpk_set_filekey(const u8 *key, const u8 *challenge, - const u8 *r_rn, u8 *kats) -{ - int r = SC_SUCCESS, outl; - EVP_CIPHER_CTX * ctx = NULL; - u8 out[16]; - - memcpy(out, key+8, 8); - memcpy(out+8, key, 8); - - ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL) - return SC_ERROR_INTERNAL; - - EVP_EncryptInit_ex(ctx, EVP_des_ede(), NULL, key, NULL); - if (!EVP_EncryptUpdate(ctx, kats, &outl, r_rn+4, 8)) - r = SC_ERROR_INTERNAL; - - if (!EVP_CIPHER_CTX_cleanup(ctx)) - r = SC_ERROR_INTERNAL; - if (r == SC_SUCCESS) { - EVP_CIPHER_CTX_init(ctx); - EVP_EncryptInit_ex(ctx, EVP_des_ede(), NULL, out, NULL); - if (!EVP_EncryptUpdate(ctx, kats+8, &outl, r_rn+4, 8)) - r = SC_ERROR_INTERNAL; - if (!EVP_CIPHER_CTX_cleanup(ctx)) - r = SC_ERROR_INTERNAL; - } - memset(out, 0, sizeof(out)); - - /* Verify Cryptogram presented by the card terminal - * XXX: what is the appropriate error code to return - * here? INVALID_ARGS doesn't seem quite right - */ - if (r == SC_SUCCESS) { - EVP_CIPHER_CTX_init(ctx); - EVP_EncryptInit_ex(ctx, EVP_des_ede(), NULL, kats, NULL); - if (!EVP_EncryptUpdate(ctx, out, &outl, challenge, 8)) - r = SC_ERROR_INTERNAL; - if (memcmp(r_rn, out+4, 4) != 0) - r = SC_ERROR_INVALID_ARGUMENTS; - } - - if (ctx) - EVP_CIPHER_CTX_free(ctx); - - sc_mem_clear(out, sizeof(out)); - return r; -} - -/* - * Verify a key presented by the user for secure messaging - */ -static int -gpk_select_key(sc_card_t *card, int key_sfi, const u8 *buf, size_t buflen) -{ - struct gpk_private_data *priv = DRVDATA(card); - sc_apdu_t apdu; - u8 rnd[8], resp[258]; - int r; - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - - if (buflen != 16) - return SC_ERROR_INVALID_ARGUMENTS; - - /* now do the SelFk */ - RAND_bytes(rnd, sizeof(rnd)); - memset(&apdu, 0, sizeof(apdu)); - apdu.cla = 0x80; - apdu.cse = SC_APDU_CASE_4_SHORT; - apdu.ins = 0x28; - apdu.p1 = 0; - apdu.p2 = key_sfi; - apdu.data = rnd; - apdu.datalen = sizeof(rnd); - apdu.lc = apdu.datalen; - apdu.resp = resp; - apdu.resplen = sizeof(resp); - apdu.le = 12; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - if (apdu.resplen != 12) { - r = SC_ERROR_UNKNOWN_DATA_RECEIVED; - } else - if ((r = gpk_set_filekey(buf, rnd, resp, priv->key)) == 0) { - priv->key_set = 1; - priv->key_reference = key_sfi; - } - - sc_mem_clear(resp, sizeof(resp)); - return r; -} - -/* - * Select a security environment (Set Crypto Context in GPK docs). - * When we get here, the PK file has already been selected. - * - * Issue: the GPK distinguishes between "signing" and - * "card internal authentication". I don't know whether this - * makes any difference in practice... - * - * Issue: it seems that sc_compute_signature() does not hash - * the data for the caller. So what is the purpose of HASH_SHA - * and other flags? - */ -static int -gpk_set_security_env(sc_card_t *card, - const sc_security_env_t *env, - int se_num) -{ - struct gpk_private_data *priv = DRVDATA(card); - sc_apdu_t apdu; - unsigned int context, algorithm; - unsigned int file_id; - u8 sysrec[7]; - int r; - - /* According to several sources from GemPlus, they don't - * have off the shelf cards that do DSA. So I won't bother - * with implementing this stuff here. */ - algorithm = SC_ALGORITHM_RSA; - if (env->flags & SC_SEC_ENV_ALG_PRESENT) - algorithm = env->algorithm; - if (algorithm != SC_ALGORITHM_RSA) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Algorithm not supported.\n"); - return SC_ERROR_NOT_SUPPORTED; - } - priv->sec_algorithm = algorithm; - - /* If there's a key reference, it must be 0 */ - if ((env->flags & SC_SEC_ENV_KEY_REF_PRESENT) - && (env->key_ref_len != 1 || env->key_ref[0] != 0)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown key referenced.\n"); - return SC_ERROR_NOT_SUPPORTED; - } - - /* Right now, the OpenSC flags do not support any padding - * other than PKCS#1. */ - if (env->flags & SC_ALGORITHM_RSA_PAD_PKCS1) - priv->sec_padding = 0; - else if (env->flags & SC_ALGORITHM_RSA_PAD_ANSI) - priv->sec_padding = 1; - else if (env->flags & SC_ALGORITHM_RSA_PAD_ISO9796) - priv->sec_padding = 2; - else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Padding algorithm not supported.\n"); - return SC_ERROR_NOT_SUPPORTED; - } - - switch (env->operation) { - case SC_SEC_OPERATION_SIGN: - /* Again, the following may not make any difference - * because we don't do any hashing on-card. But - * what the hell, we have all those nice macros, - * so why not use them :) - */ - if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) { - context = GPK_SIGN_RSA_SHA; - priv->sec_hash_len = 20; - } else - if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5_SHA1) { - context = GPK_SIGN_RSA_SSL; - priv->sec_hash_len = 36; - } else - if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5) { - context = GPK_SIGN_RSA_MD5; - priv->sec_hash_len = 16; - } else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported signature algorithm"); - return SC_ERROR_NOT_SUPPORTED; - } - break; - case SC_SEC_OPERATION_DECIPHER: - context = GPK_UNWRAP_RSA; - break; - default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Crypto operation not supported.\n"); - return SC_ERROR_NOT_SUPPORTED; - } - - /* Get the file ID */ - if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) { - if (env->file_ref.len != 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File reference: invalid length.\n"); - return SC_ERROR_INVALID_ARGUMENTS; - } - file_id = (env->file_ref.value[0] << 8) - | env->file_ref.value[1]; - } else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File reference missing.\n"); - return SC_ERROR_INVALID_ARGUMENTS; - } - - /* Select the PK file. The caller has already selected - * the DF. */ - r = gpk_select_id(card, GPK_SEL_EF, file_id, NULL); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to select PK file"); - - /* Read the sys record of the PK file to find out the key length */ - r = sc_read_record(card, 1, sysrec, sizeof(sysrec), - SC_RECORD_BY_REC_NR); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to read PK sysrec"); - if (r != 7 || sysrec[0] != 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "First record of file is not the sysrec"); - return SC_ERROR_OBJECT_NOT_VALID; - } - if (sysrec[5] != 0x00) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Public key is not an RSA key"); - return SC_ERROR_OBJECT_NOT_VALID; - } - switch (sysrec[1]) { - case 0x00: priv->sec_mod_len = 512 / 8; break; - case 0x10: priv->sec_mod_len = 768 / 8; break; - case 0x11: priv->sec_mod_len = 1024 / 8; break; - default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported modulus length"); - return SC_ERROR_OBJECT_NOT_VALID; - } - - /* Now do SelectCryptoContext */ - memset(&apdu, 0, sizeof(apdu)); - apdu.cse = SC_APDU_CASE_1; - apdu.cla = 0x80; - apdu.ins = 0xA6; - apdu.p1 = file_id & 0x1f; - apdu.p2 = context; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - return r; -} - -/* - * Restore security environment - * Not sure what this is supposed to do. - */ -static int -gpk_restore_security_env(sc_card_t *card, int se_num) -{ - return 0; -} - -/* - * Revert buffer (needed for all GPK crypto operations because - * they want LSB byte order internally - */ -static int -reverse(u8 *out, size_t outlen, const u8 *in, size_t inlen) -{ - if (inlen > outlen) - return SC_ERROR_BUFFER_TOO_SMALL; - outlen = inlen; - while (inlen--) - *out++ = in[inlen]; - return outlen; -} - -/* - * Use the card's on-board hashing functions to hash some data - */ -#ifdef dontuse -static int -gpk_hash(sc_card_t *card, const u8 *data, size_t datalen) -{ - sc_apdu_t apdu; - unsigned int count, chain, len; - int r; - - chain = 0x01; - for (count = 0; count < datalen; count += len) { - unsigned char buffer[GPK_HASH_CHUNK+2]; - - if ((len = datalen - count) > GPK_HASH_CHUNK) - len = GPK_HASH_CHUNK; - else - chain |= 0x10; - buffer[0] = 0x55; - buffer[1] = len; - memcpy(buffer+2, data + count, len); - - memset(&apdu, 0, sizeof(apdu)); - apdu.cse = SC_APDU_CASE_3_SHORT; - apdu.cla = 0x80; - apdu.ins = 0xDA; - apdu.p1 = chain; - apdu.p2 = len; - apdu.lc = len + 2; - apdu.data= buffer; - apdu.datalen = len + 2; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - chain = 0; - } - - return 0; -} -#endif - -/* - * Send the hashed data to the card. - */ -static int -gpk_init_hashed(sc_card_t *card, const u8 *digest, unsigned int len) -{ - sc_apdu_t apdu; - u8 tsegid[64]; - int r; - - r = reverse(tsegid, sizeof(tsegid), digest, len); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to reverse buffer"); - - memset(&apdu, 0, sizeof(apdu)); - apdu.cse = SC_APDU_CASE_3_SHORT; - apdu.cla = 0x80; - apdu.ins = 0xEA; - apdu.lc = len; - apdu.data= tsegid; - apdu.datalen = len; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - return r; -} - -/* - * Compute a signature. - * Note we hash everything manually and send it to the card. - */ -static int -gpk_compute_signature(sc_card_t *card, const u8 *data, - size_t data_len, u8 * out, size_t outlen) -{ - struct gpk_private_data *priv = DRVDATA(card); - sc_apdu_t apdu; - u8 cardsig[1024/8]; - int r; - - if (data_len > priv->sec_mod_len) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "Data length (%"SC_FORMAT_LEN_SIZE_T"u) does not match key modulus %u.\n", - data_len, priv->sec_mod_len); - return SC_ERROR_INTERNAL; - } - if (sizeof(cardsig) < priv->sec_mod_len) - return SC_ERROR_BUFFER_TOO_SMALL; - - r = gpk_init_hashed(card, data, data_len); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to send hash to card"); - - /* Now sign the hash. - * The GPK has Internal Authenticate and PK_Sign. I am not - * sure what the difference between the two is. */ - memset(&apdu, 0, sizeof(apdu)); - apdu.cse = SC_APDU_CASE_2_SHORT; - apdu.cla = 0x80; - apdu.ins = 0x86; - apdu.p2 = priv->sec_padding; - apdu.resp= cardsig; - apdu.resplen = sizeof(cardsig); - apdu.le = priv->sec_mod_len; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - /* The GPK returns the signature as little endian numbers. - * Need to revert these */ - r = reverse(out, outlen, cardsig, apdu.resplen); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to reverse signature"); - - return r; -} - -/* - * Decrypt some RSA encrypted piece of data. - * Due to legal restrictions, the GPK will not let you see the - * full cleartext block, just the last N bytes. - * The GPK documentation refers to N as the MaxSessionKey size, - * probably because this feature limits the maximum size of an - * SSL session key you will be able to decrypt using this card. - */ -static int -gpk_decipher(sc_card_t *card, const u8 *in, size_t inlen, - u8 *out, size_t outlen) -{ - struct gpk_private_data *priv = DRVDATA(card); - sc_apdu_t apdu; - u8 buffer[256]; - int r; - - if (inlen != priv->sec_mod_len) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "Data length (%"SC_FORMAT_LEN_SIZE_T"u) does not match key modulus %u.\n", - inlen, priv->sec_mod_len); - return SC_ERROR_INVALID_ARGUMENTS; - } - - /* First revert the cryptogram */ - r = reverse(buffer, sizeof(buffer), in, inlen); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Cryptogram too large"); - in = buffer; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x1C, 0x00, 0x00); - apdu.cla |= 0x80; - apdu.lc = inlen; - apdu.data = in; - apdu.datalen = inlen; - apdu.le = 256; /* give me all you got :) */ - apdu.resp = buffer; - apdu.resplen = sizeof(buffer); - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - /* Reverse the data we got back */ - r = reverse(out, outlen, buffer, apdu.resplen); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to reverse buffer"); - - return r; -} - -/* - * Erase card - */ -static int -gpk_erase_card(sc_card_t *card) -{ - struct gpk_private_data *priv = DRVDATA(card); - sc_apdu_t apdu; - u8 offset; - int r; - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - switch (card->type) { - case SC_CARD_TYPE_GPK_GPK4000_su256: - case SC_CARD_TYPE_GPK_GPK4000_sdo: - offset = 0x6B; /* courtesy gemplus hotline */ - break; - - case SC_CARD_TYPE_GPK_GPK4000_s: - offset = 7; - break; - - case SC_CARD_TYPE_GPK_GPK8000: - case SC_CARD_TYPE_GPK_GPK8000_8K: - case SC_CARD_TYPE_GPK_GPK8000_16K: - case SC_CARD_TYPE_GPK_GPK16000: - offset = 0; - break; - - default: - return SC_ERROR_NOT_SUPPORTED; - } - - memset(&apdu, 0, sizeof(apdu)); - apdu.cse = SC_APDU_CASE_1; - apdu.cla = 0xDB; - apdu.ins = 0xDE; - apdu.p2 = offset; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - priv->key_set = 0; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); -} - -/* - * Lock a file Access Condition. - * - * File must be selected, and we assume that any authentication - * that needs to be presented in order to allow this operation - * have been presented (ACs from the DF; AC1 for sensitive files, - * AC2 for normal files). - */ -static int -gpk_lock(sc_card_t *card, struct sc_cardctl_gpk_lock *args) -{ - struct gpk_private_data *priv = DRVDATA(card); - sc_file_t *file = args->file; - sc_apdu_t apdu; - u8 data[8], crycks[3], resp[3]; - int r; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "gpk_lock(0x%04X, %u)\n", file->id, args->operation); - - memset(data, 0, sizeof(data)); - data[0] = file->id >> 8; - data[1] = file->id; - switch (args->operation) { - case SC_AC_OP_UPDATE: - data[2] = 0x40; break; - case SC_AC_OP_WRITE: - data[3] = 0x40; break; - case SC_AC_OP_READ: - data[4] = 0x40; break; - default: - return SC_ERROR_INVALID_ARGUMENTS; - } - - memset(&apdu, 0, sizeof(apdu)); - apdu.cse = SC_APDU_CASE_3_SHORT; - apdu.cla = 0x80; - apdu.ins = 0x16; - apdu.p1 = (file->type == SC_FILE_TYPE_DF)? 1 : 2; - apdu.p2 = 0; - apdu.lc = 5; - apdu.datalen = 5; - apdu.data = data; - - if (priv->key_set) { - apdu.cla = 0x84; - apdu.cse = SC_APDU_CASE_4_SHORT; - r = gpk_compute_crycks(card, &apdu, crycks); - if (r) - return r; - apdu.resp = resp; - apdu.resplen = sizeof(resp); /* XXX? */ - } - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - if (priv->key_set) - r = gpk_verify_crycks(card, &apdu, crycks); - - return r; -} - -/* - * Initialize the private portion of a public key file - */ -static int -gpk_pkfile_init(sc_card_t *card, struct sc_cardctl_gpk_pkinit *args) -{ - sc_apdu_t apdu; - int r; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "gpk_pkfile_init(%u)\n", args->privlen); - - memset(&apdu, 0, sizeof(apdu)); - apdu.cse = SC_APDU_CASE_1; - apdu.cla = 0x80; - apdu.ins = 0x12; - apdu.p1 = args->file->id & 0x1F; - apdu.p2 = args->privlen / 4; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - return r; -} - -/* - * Initialize the private portion of a public key file - */ -static int -gpk_generate_key(sc_card_t *card, struct sc_cardctl_gpk_genkey *args) -{ - sc_apdu_t apdu; - int r; - u8 buffer[256]; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "gpk_generate_key(%u)\n", args->privlen); - if (args->privlen != 512 && args->privlen != 1024) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "Key generation not supported for key length %d", - args->privlen); - return SC_ERROR_NOT_SUPPORTED; - } - - memset(&apdu, 0, sizeof(apdu)); - apdu.cse = SC_APDU_CASE_2_SHORT; - apdu.cla = 0x80; - apdu.ins = 0xD2; - apdu.p1 = 0x80 | (args->fid & 0x1F); - apdu.p2 = (args->privlen == 1024) ? 0x11 : 0; - apdu.le = args->privlen / 8 + 2; - apdu.resp = buffer; - apdu.resplen = 256; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - /* Return the public key, inverted. - * The first two bytes must be stripped off. */ - if (args->pubkey_len && apdu.resplen > 2) { - r = reverse(args->pubkey, args->pubkey_len, - buffer + 2, apdu.resplen - 2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to reverse buffer"); - args->pubkey_len = r; - } - - return r; -} - -/* - * Store a privat key component - */ -static int -gpk_pkfile_load(sc_card_t *card, struct sc_cardctl_gpk_pkload *args) -{ - struct gpk_private_data *priv = DRVDATA(card); - sc_apdu_t apdu; - unsigned int n; - u8 temp[256]; - int r = SC_SUCCESS, outl; - EVP_CIPHER_CTX * ctx; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "gpk_pkfile_load(fid=%04x, len=%d, datalen=%d)\n", - args->file->id, args->len, args->datalen); - - ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL) - return SC_ERROR_INTERNAL; - - if (0) { - char buf[2048]; - - sc_hex_dump(card->ctx, SC_LOG_DEBUG_NORMAL, - args->data, args->datalen, - buf, sizeof(buf)); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Sending %d bytes (cleartext):\n%s", - args->datalen, buf); - } - - memset(&apdu, 0, sizeof(apdu)); - apdu.cse = SC_APDU_CASE_3_SHORT; - apdu.cla = 0x80; - apdu.ins = 0x18; - apdu.p1 = args->file->id & 0x1F; - apdu.p2 = args->len; - apdu.lc = args->datalen; - - /* encrypt the private key material */ - assert(args->datalen <= sizeof(temp)); - if (!priv->key_set) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No secure messaging key set!\n"); - return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; - } - - EVP_EncryptInit_ex(ctx, EVP_des_ede(), NULL, priv->key, NULL); - for (n = 0; n < args->datalen; n += 8) { - if (!EVP_EncryptUpdate(ctx, temp+n, &outl, args->data + n, 8)) { - r = SC_ERROR_INTERNAL; - break; - } - } - if (ctx) - EVP_CIPHER_CTX_free(ctx); - if (r != SC_SUCCESS) - return SC_ERROR_INTERNAL; - - apdu.data = temp; - apdu.datalen = args->datalen; - - /* Forget the key. The card seems to forget it, too :) */ - priv->key_set = 0; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - -/* - * This function lets pkcs15init query for the transport key - */ -static int -gpk_get_default_key(sc_card_t *card, struct sc_cardctl_default_key *data) -{ - if (data->method == SC_AC_PRO && data->key_ref == 1) { - if (data->len < 16) - return SC_ERROR_BUFFER_TOO_SMALL; - memcpy(data->key_data, "TEST KEYTEST KEY", 16); - data->len = 16; - return 0; - } - return SC_ERROR_NO_DEFAULT_KEY; -} - -/* - * GetInfo call - */ -static int gpk_get_info(sc_card_t *card, int p1, int p2, u8 *buf, - size_t buflen) -{ - sc_apdu_t apdu; - int r, retry = 0; - - /* We may have to retry the get info command. It - * returns 6B00 if a previous command returned a 61xx response, - * but the host failed to collect the results. - * - * Note the additional sc_lock/sc_unlock pair, which - * is required to prevent sc_transmit_apdu from - * calling logout(), which in turn does a SELECT MF - * without collecting the response :) - */ - r = sc_lock(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); - - do { - memset(&apdu, 0, sizeof(apdu)); - apdu.cse = SC_APDU_CASE_2_SHORT; - apdu.cla = 0x80; - apdu.ins = 0xC0; - apdu.p1 = p1; - apdu.p2 = p2; - apdu.le = buflen; - apdu.resp = buf; - apdu.resplen = buflen; - - if ((r = sc_transmit_apdu(card, &apdu)) < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed: %s", - sc_strerror(r)); - sc_unlock(card); - return r; - } - } while (apdu.sw1 == 0x6B && apdu.sw2 == 0x00 && retry++ < 1); - sc_unlock(card); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - return r; -} - -static int gpk_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) -{ - int r; - u8 rbuf[10]; - sc_apdu_t apdu; - - if (card->type != SC_CARD_TYPE_GPK_GPK16000) - return SC_ERROR_NOT_SUPPORTED; - - if (!serial) - return SC_ERROR_INVALID_ARGUMENTS; - /* see if we have cached serial number */ - if (card->serialnr.len) { - memcpy(serial, &card->serialnr, sizeof(*serial)); - return SC_SUCCESS; - } - /* get serial number via Get CSN */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xb8, 0x00, 0x00); - apdu.cla |= 0x80; - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - apdu.le = 8; - apdu.lc = 0; - apdu.datalen = 0; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) - return SC_ERROR_INTERNAL; - /* cache serial number */ - memcpy(card->serialnr.value, apdu.resp, apdu.resplen); - card->serialnr.len = apdu.resplen; - /* copy and return serial number */ - memcpy(serial, &card->serialnr, sizeof(*serial)); - return SC_SUCCESS; -} - -/* - * Dispatch card_ctl calls - */ -static int -gpk_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) -{ - switch (cmd) { - case SC_CARDCTL_ERASE_CARD: - return gpk_erase_card(card); - case SC_CARDCTL_GET_DEFAULT_KEY: - return gpk_get_default_key(card, - (struct sc_cardctl_default_key *) ptr); - case SC_CARDCTL_GPK_VARIANT: - *(int *) ptr = card->type; - return 0; - case SC_CARDCTL_GPK_LOCK: - return gpk_lock(card, (struct sc_cardctl_gpk_lock *) ptr); - case SC_CARDCTL_GPK_PKINIT: - return gpk_pkfile_init(card, - (struct sc_cardctl_gpk_pkinit *) ptr); - case SC_CARDCTL_GPK_PKLOAD: - return gpk_pkfile_load(card, - (struct sc_cardctl_gpk_pkload *) ptr); - case SC_CARDCTL_GPK_IS_LOCKED: - *(int *) ptr = DRVDATA(card)->locked; - return 0; - case SC_CARDCTL_GPK_GENERATE_KEY: - return gpk_generate_key(card, - (struct sc_cardctl_gpk_genkey *) ptr); - case SC_CARDCTL_GET_SERIALNR: - return gpk_get_serialnr(card, (sc_serial_number_t *) ptr); - } - - - return SC_ERROR_NOT_SUPPORTED; -} - -static int -gpk_build_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, struct sc_pin_cmd_data *data) -{ - static u8 sbuf[8]; - int r; - - if (data->pin_type != SC_AC_CHV) - return SC_ERROR_INVALID_ARGUMENTS; - - /* XXX deal with secure messaging here */ - memset(apdu, 0, sizeof(*apdu)); - apdu->cse = SC_APDU_CASE_3_SHORT; - - data->flags |= SC_PIN_CMD_NEED_PADDING; - - switch (data->cmd) { - case SC_PIN_CMD_VERIFY: - /* Copy PIN to buffer and pad */ - data->pin1.encoding = SC_PIN_ENCODING_ASCII; - data->pin1.pad_length = 8; - data->pin1.pad_char = 0x00; - data->pin1.offset = 5; - r = sc_build_pin(sbuf, 8, &data->pin1, 1); - if (r < 0) - return r; - - apdu->cla = 0x00; - apdu->ins = 0x20; - apdu->p1 = 0x00; - break; - case SC_PIN_CMD_CHANGE: - case SC_PIN_CMD_UNBLOCK: - /* Copy PINs to buffer, BCD-encoded, and pad */ - data->pin1.encoding = SC_PIN_ENCODING_BCD; - data->pin1.pad_length = 8; - data->pin1.pad_char = 0x00; - data->pin1.offset = 5; - data->pin2.encoding = SC_PIN_ENCODING_BCD; - data->pin2.pad_length = 8; - data->pin2.pad_char = 0x00; - data->pin2.offset = 5 + 4; - if ((r = sc_build_pin(sbuf, 4, &data->pin1, 1)) < 0 - || (r = sc_build_pin(sbuf + 4, 4, &data->pin2, 1)) < 0) - return r; - - apdu->cla = 0x80; - apdu->ins = 0x24; - apdu->p1 = (data->cmd == SC_PIN_CMD_CHANGE)? 0x00 : 0x01; - break; - default: - return SC_ERROR_NOT_SUPPORTED; - } - - apdu->p2 = data->pin_reference & 7; - apdu->lc = 8; - apdu->datalen = 8; - apdu->data = sbuf; - - return 0; -} - -static int -gpk_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) -{ - sc_apdu_t apdu; - int r; - - /* Special case - External Authenticate */ - if (data->cmd == SC_PIN_CMD_VERIFY - && data->pin_type == SC_AC_PRO) - return gpk_select_key(card, - data->pin_reference, - data->pin1.data, - data->pin1.len); - - r = gpk_build_pin_apdu(card, &apdu, data); - if (r < 0) - return r; - - data->apdu = &apdu; - - return iso_ops->pin_cmd(card, data, tries_left); -} - -/* - * Initialize the driver struct - */ -static struct sc_card_driver * -sc_get_driver(void) -{ - struct sc_card_driver *iso_drv; - - iso_drv = sc_get_iso7816_driver(); - iso_ops = iso_drv->ops; - gpk_ops = *iso_ops; - - gpk_ops.match_card = gpk_match_card; - gpk_ops.init = gpk_init; - gpk_ops.finish = gpk_finish; - gpk_ops.select_file = gpk_select_file; - gpk_ops.read_binary = gpk_read_binary; - gpk_ops.write_binary = gpk_write_binary; - gpk_ops.update_binary = gpk_update_binary; - gpk_ops.create_file = gpk_create_file; - /* gpk_ops.check_sw = gpk_check_sw; */ - gpk_ops.card_ctl = gpk_card_ctl; - gpk_ops.set_security_env= gpk_set_security_env; - gpk_ops.restore_security_env= gpk_restore_security_env; - gpk_ops.compute_signature= gpk_compute_signature; - gpk_ops.decipher = gpk_decipher; - gpk_ops.pin_cmd = gpk_pin_cmd; - - return &gpk_drv; -} - -struct sc_card_driver * -sc_get_gpk_driver(void) -{ - return sc_get_driver(); -} -#endif /* ENABLE_OPENSSL */ diff --git a/src/libopensc/card-iasecc.c b/src/libopensc/card-iasecc.c index 9b97c18fa2..f508f1eb87 100644 --- a/src/libopensc/card-iasecc.c +++ b/src/libopensc/card-iasecc.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H @@ -42,9 +42,11 @@ #include "asn1.h" #include "cardctl.h" #include "opensc.h" +#include "sc-ossl-compat.h" /* #include "sm.h" */ #include "pkcs15.h" /* #include "hash-strings.h" */ +#include "gp.h" #include "iasecc.h" @@ -56,6 +58,12 @@ | SC_ALGORITHM_RSA_HASH_SHA1 \ | SC_ALGORITHM_RSA_HASH_SHA256) +#define IASECC_CARD_DEFAULT_CAPS ( 0 \ + | SC_CARD_CAP_RNG \ + | SC_CARD_CAP_APDU_EXT \ + | SC_CARD_CAP_USE_FCI_AC \ + | SC_CARD_CAP_ISO7816_PIN_INFO) + /* generic iso 7816 operations table */ static const struct sc_card_operations *iso_ops = NULL; @@ -69,11 +77,12 @@ static struct sc_card_driver iasecc_drv = { NULL, 0, NULL }; -static struct sc_atr_table iasecc_known_atrs[] = { +static const struct sc_atr_table iasecc_known_atrs[] = { { "3B:7F:96:00:00:00:31:B8:64:40:70:14:10:73:94:01:80:82:90:00", "FF:FF:FF:FF:FF:FF:FF:FE:FF:FF:00:00:FF:FF:FF:FF:FF:FF:FF:FF", "IAS/ECC Gemalto", SC_CARD_TYPE_IASECC_GEMALTO, 0, NULL }, - { "3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:08:00:07:90:00:FE", NULL, + { "3B:DD:00:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:08:00:07:90:00:00", + "FF:FF:00:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:00", "IAS/ECC v1.0.1 Oberthur", SC_CARD_TYPE_IASECC_OBERTHUR, 0, NULL }, { "3B:7D:13:00:00:4D:44:57:2D:49:41:53:2D:43:41:52:44:32", NULL, "IAS/ECC v1.0.1 Sagem MDW-IAS-CARD2", SC_CARD_TYPE_IASECC_SAGEM, 0, NULL }, @@ -87,15 +96,21 @@ static struct sc_atr_table iasecc_known_atrs[] = { "IAS/ECC v1.0.1 Amos", SC_CARD_TYPE_IASECC_AMOS, 0, NULL }, { "3B:DC:18:FF:81:91:FE:1F:C3:80:73:C8:21:13:66:01:0B:03:52:00:05:38", NULL, "IAS/ECC v1.0.1 Amos", SC_CARD_TYPE_IASECC_AMOS, 0, NULL }, + { + .atr = "3B:AC:00:40:2A:00:12:25:00:64:80:00:03:10:00:90:00", + .atrmask = "FF:00:00:00:00:FF:FF:FF:FF:FF:FF:00:00:00:FF:FF:FF", + .name = "IAS/ECC CPx", + .type = SC_CARD_TYPE_IASECC_CPX, + }, + { + .atr = "2B:8F:80:01:00:31:B8:64:04:B0:EC:C1:73:94:01:80:82:90:00:0E", + .atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:00:00:FF:C0:FF:FF:FF:FF:FF:FF:FF:FF", + .name = "IAS/ECC CPxCL", + .type = SC_CARD_TYPE_IASECC_CPXCL, + }, { NULL, NULL, NULL, 0, 0, NULL } }; -static struct sc_aid GlobalPlatform_CardManager_AID = { - { 0xA0,0x00,0x00,0x00,0x03,0x00,0x00}, 7 -}; -static struct sc_aid GlobalPlatform_ISD_Default_RID = { - { 0xA0,0x00,0x00,0x01,0x51,0x00,0x00}, 7 -}; static struct sc_aid OberthurIASECC_AID = { {0xA0,0x00,0x00,0x00,0x77,0x01,0x08,0x00,0x07,0x00,0x00,0xFE,0x00,0x00,0x01,0x00}, 16 }; @@ -114,12 +129,28 @@ struct iasecc_pin_status { struct iasecc_pin_status *checked_pins = NULL; +/* Any absent field is set to -1, except scbs, which is always present */ +struct iasecc_pin_policy { + int min_length; + int max_length; + int stored_length; + int tries_maximum; + int tries_remaining; + unsigned char scbs[IASECC_MAX_SCBS]; +}; + static int iasecc_select_file(struct sc_card *card, const struct sc_path *path, struct sc_file **file_out); static int iasecc_process_fci(struct sc_card *card, struct sc_file *file, const unsigned char *buf, size_t buflen); static int iasecc_get_serialnr(struct sc_card *card, struct sc_serial_number *serial); static int iasecc_sdo_get_data(struct sc_card *card, struct iasecc_sdo *sdo); -static int iasecc_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data); -static int iasecc_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, int *tries_left); +static int iasecc_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data, struct iasecc_pin_policy *pin); +static int iasecc_pin_get_status(struct sc_card *card, struct sc_pin_cmd_data *data); +static int iasecc_pin_get_info(struct sc_card *card, struct sc_pin_cmd_data *data); +static int iasecc_check_update_pin(struct sc_pin_cmd_data *data, struct sc_pin_cmd_pin *pin); +static void iasecc_set_pin_padding(struct sc_pin_cmd_data *data, struct sc_pin_cmd_pin *pin, + size_t pad_len); +static int iasecc_pin_merge_policy(struct sc_card *card, struct sc_pin_cmd_data *data, + struct sc_pin_cmd_pin *pin, struct iasecc_pin_policy *policy); static int iasecc_get_free_reference(struct sc_card *card, struct iasecc_ctl_get_free_reference *ctl_data); static int iasecc_sdo_put_data(struct sc_card *card, struct iasecc_sdo_update *update); @@ -128,6 +159,18 @@ static int _iasecc_sm_read_binary(struct sc_card *card, unsigned int offs, unsig static int _iasecc_sm_update_binary(struct sc_card *card, unsigned int offs, const unsigned char *buff, size_t count); #endif +void +sc_invalidate_cache(struct sc_card *card) +{ + if (card) { + struct iasecc_private_data *prv = (struct iasecc_private_data *)card->drv_data; + sc_file_free(prv->cache.current_ef); + sc_file_free(prv->cache.current_df); + memset(&prv->cache, 0, sizeof(prv->cache)); + prv->cache.valid = 0; + } +} + static int iasecc_chv_cache_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd) { @@ -157,7 +200,7 @@ iasecc_chv_cache_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd) else memset(pin_status->sha1, 0, SHA_DIGEST_LENGTH); - sc_log(ctx, "iasecc_chv_cache_verified() sha1(PIN): %s", sc_dump_hex(pin_status->sha1, SHA_DIGEST_LENGTH)); + sc_log_hex(ctx, "iasecc_chv_cache_verified() sha1(PIN)", pin_status->sha1, SHA_DIGEST_LENGTH); if (!current) { if (!checked_pins) { @@ -219,7 +262,7 @@ iasecc_chv_cache_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_c SHA1(pin_cmd->pin1.data, pin_cmd->pin1.len, data_sha1); else memset(data_sha1, 0, SHA_DIGEST_LENGTH); - sc_log(ctx, "data_sha1: %s", sc_dump_hex(data_sha1, SHA_DIGEST_LENGTH)); + sc_log_hex(ctx, "data_sha1: %s", data_sha1, SHA_DIGEST_LENGTH); for(current = checked_pins; current; current = current->next) if (current->reference == pin_cmd->pin_reference) @@ -238,6 +281,7 @@ iasecc_chv_cache_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_c static int iasecc_select_mf(struct sc_card *card, struct sc_file **file_out) { + struct iasecc_private_data *prv = (struct iasecc_private_data *)card->drv_data; struct sc_context *ctx = card->ctx; struct sc_file *mf_file = NULL; struct sc_path path; @@ -253,17 +297,21 @@ iasecc_select_mf(struct sc_card *card, struct sc_file **file_out) struct sc_apdu apdu; unsigned char apdu_resp[SC_MAX_APDU_BUFFER_SIZE]; - /* ISO 'select' command failes when not FCP data returned */ + /* ISO 'select' command fails when not FCP data returned */ sc_format_path("3F00", &path); path.type = SC_PATH_TYPE_FILE_ID; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x00, 0x00); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x00, 0x0C); apdu.lc = path.len; apdu.data = path.value; apdu.datalen = path.len; apdu.resplen = sizeof(apdu_resp); apdu.resp = apdu_resp; + /* TODO: this might be obsolete now that 0x0c (no data) is default for p2 */ + if (card->type == SC_CARD_TYPE_IASECC_MI2) + apdu.p2 = 0x04; + rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); @@ -280,7 +328,7 @@ iasecc_select_mf(struct sc_card *card, struct sc_file **file_out) /* Ignore the FCP of the MF, because: * - some cards do not return it; - * - there is not need of it -- create/delete of the files in MF is not invisaged. + * - there is not need of it -- create/delete of the files in MF is not envisaged. */ mf_file = sc_file_new(); if (mf_file == NULL) @@ -288,16 +336,9 @@ iasecc_select_mf(struct sc_card *card, struct sc_file **file_out) mf_file->type = SC_FILE_TYPE_DF; mf_file->path = path; - if (card->cache.valid) - sc_file_free(card->cache.current_df); - card->cache.current_df = NULL; - - if (card->cache.valid) - sc_file_free(card->cache.current_ef); - card->cache.current_ef = NULL; - - sc_file_dup(&card->cache.current_df, mf_file); - card->cache.valid = 1; + sc_invalidate_cache(card); + sc_file_dup(&prv->cache.current_df, mf_file); + prv->cache.valid = 1; if (file_out && *file_out == NULL) *file_out = mf_file; @@ -307,42 +348,12 @@ iasecc_select_mf(struct sc_card *card, struct sc_file **file_out) LOG_FUNC_RETURN(ctx, rv); } - -static int -iasecc_select_aid(struct sc_card *card, struct sc_aid *aid, unsigned char *out, size_t *out_len) -{ - struct sc_apdu apdu; - unsigned char apdu_resp[SC_MAX_APDU_BUFFER_SIZE]; - int rv; - - /* Select application (deselect previously selected application) */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x00); - apdu.lc = aid->len; - apdu.data = aid->value; - apdu.datalen = aid->len; - apdu.resplen = sizeof(apdu_resp); - apdu.resp = apdu_resp; - - rv = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); - rv = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(card->ctx, rv, "Cannot select AID"); - - if (*out_len < apdu.resplen) - LOG_TEST_RET(card->ctx, SC_ERROR_BUFFER_TOO_SMALL, "Cannot select AID"); - memcpy(out, apdu.resp, apdu.resplen); - - return SC_SUCCESS; -} - - static int iasecc_match_card(struct sc_card *card) { struct sc_context *ctx = card->ctx; int i; - sc_log(ctx, "iasecc_match_card(%s) called", sc_dump_hex(card->atr.value, card->atr.len)); i = _sc_match_atr(card, iasecc_known_atrs, &card->type); if (i < 0) { sc_log(ctx, "card not matched"); @@ -384,6 +395,11 @@ static int iasecc_parse_ef_atr(struct sc_card *card) sizes->recv = card->ef_atr->issuer_data[10] * 0x100 + card->ef_atr->issuer_data[11]; sizes->recv_sc = card->ef_atr->issuer_data[14] * 0x100 + card->ef_atr->issuer_data[15]; + sc_log(ctx, + "EF.ATR: IO Buffer Size send/sc %"SC_FORMAT_LEN_SIZE_T"d/%"SC_FORMAT_LEN_SIZE_T"d " + "recv/sc %"SC_FORMAT_LEN_SIZE_T"d/%"SC_FORMAT_LEN_SIZE_T"d", + sizes->send, sizes->send_sc, sizes->recv, sizes->recv_sc); + card->max_send_size = sizes->send; card->max_recv_size = sizes->recv; @@ -414,16 +430,13 @@ iasecc_init_gemalto(struct sc_card *card) flags = IASECC_CARD_DEFAULT_FLAGS; - _sc_card_add_rsa_alg(card, 1024, flags, 0x10001); - _sc_card_add_rsa_alg(card, 2048, flags, 0x10001); - - card->caps = SC_CARD_CAP_RNG; - card->caps |= SC_CARD_CAP_APDU_EXT; - card->caps |= SC_CARD_CAP_USE_FCI_AC; + card->caps = IASECC_CARD_DEFAULT_CAPS; sc_format_path("3F00", &path); - rv = sc_select_file(card, &path, NULL); - /* Result ignored*/ + if (SC_SUCCESS != sc_select_file(card, &path, NULL)) { + /* Result ignored*/ + sc_log(card->ctx, "Warning, MF select failed"); + } rv = iasecc_parse_ef_atr(card); sc_log(ctx, "rv %i", rv); @@ -439,6 +452,9 @@ iasecc_init_gemalto(struct sc_card *card) sc_log(ctx, "rv %i", rv); LOG_TEST_RET(ctx, rv, "Cannot read/parse EF.ATR"); + _sc_card_add_rsa_alg(card, 1024, flags, 0x10001); + _sc_card_add_rsa_alg(card, 2048, flags, 0x10001); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -454,7 +470,7 @@ iasecc_oberthur_match(struct sc_card *card) if (*hist != 0x80 || ((*(hist+1)&0xF0) != 0xF0)) LOG_FUNC_RETURN(ctx, SC_ERROR_OBJECT_NOT_FOUND); - sc_log(ctx, "AID in historical_bytes '%s'", sc_dump_hex(hist + 2, *(hist+1) & 0x0F)); + sc_log_hex(ctx, "AID in historical_bytes", hist + 2, *(hist+1) & 0x0F); if (memcmp(hist + 2, OberthurIASECC_AID.value, *(hist+1) & 0x0F)) LOG_FUNC_RETURN(ctx, SC_ERROR_RECORD_NOT_FOUND); @@ -475,8 +491,6 @@ static int iasecc_init_oberthur(struct sc_card *card) { struct sc_context *ctx = card->ctx; - unsigned char resp[0x100]; - size_t resp_len; unsigned int flags; int rv = 0; @@ -487,16 +501,13 @@ iasecc_init_oberthur(struct sc_card *card) _sc_card_add_rsa_alg(card, 1024, flags, 0x10001); _sc_card_add_rsa_alg(card, 2048, flags, 0x10001); - card->caps = SC_CARD_CAP_RNG; - card->caps |= SC_CARD_CAP_APDU_EXT; - card->caps |= SC_CARD_CAP_USE_FCI_AC; + card->caps = IASECC_CARD_DEFAULT_CAPS; iasecc_parse_ef_atr(card); - resp_len = sizeof(resp); - if (iasecc_select_aid(card, &GlobalPlatform_CardManager_AID, resp, &resp_len)) { - resp_len = sizeof(resp); - iasecc_select_aid(card, &GlobalPlatform_ISD_Default_RID, resp, &resp_len); + /* if we fail to select CM, */ + if (gp_select_card_manager(card)) { + gp_select_isd_rid(card); } rv = iasecc_oberthur_match(card); @@ -514,38 +525,32 @@ iasecc_init_oberthur(struct sc_card *card) static int -iasecc_init_sagem(struct sc_card *card) +iasecc_mi_match(struct sc_card *card) { struct sc_context *ctx = card->ctx; - unsigned int flags; + unsigned char resp[0x100]; + size_t resp_len = sizeof(resp); int rv = 0; LOG_FUNC_CALLED(ctx); - flags = IASECC_CARD_DEFAULT_FLAGS; - - _sc_card_add_rsa_alg(card, 1024, flags, 0x10001); - _sc_card_add_rsa_alg(card, 2048, flags, 0x10001); + rv = iso7816_select_aid(card, MIIASECC_AID.value, MIIASECC_AID.len, resp, &resp_len); + LOG_TEST_RET(ctx, rv, "IASECC: failed to select MI IAS/ECC applet"); - card->caps = SC_CARD_CAP_RNG; - card->caps |= SC_CARD_CAP_APDU_EXT; - card->caps |= SC_CARD_CAP_USE_FCI_AC; - - rv = iasecc_parse_ef_atr(card); - if (rv == SC_ERROR_FILE_NOT_FOUND) { - rv = iasecc_select_mf(card, NULL); - LOG_TEST_RET(ctx, rv, "MF selection error"); + if (!card->ef_atr) + card->ef_atr = calloc(1, sizeof(struct sc_ef_atr)); + if (!card->ef_atr) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); - rv = iasecc_parse_ef_atr(card); - } - LOG_TEST_RET(ctx, rv, "IASECC: ATR parse failed"); + memcpy(card->ef_atr->aid.value, MIIASECC_AID.value, MIIASECC_AID.len); + card->ef_atr->aid.len = MIIASECC_AID.len; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int -iasecc_init_amos(struct sc_card *card) +iasecc_init_amos_or_sagem(struct sc_card *card) { struct sc_context *ctx = card->ctx; unsigned int flags; @@ -558,9 +563,15 @@ iasecc_init_amos(struct sc_card *card) _sc_card_add_rsa_alg(card, 1024, flags, 0x10001); _sc_card_add_rsa_alg(card, 2048, flags, 0x10001); - card->caps = SC_CARD_CAP_RNG; - card->caps |= SC_CARD_CAP_APDU_EXT; - card->caps |= SC_CARD_CAP_USE_FCI_AC; + card->caps = IASECC_CARD_DEFAULT_CAPS; + + if (card->type == SC_CARD_TYPE_IASECC_MI) { + rv = iasecc_mi_match(card); + if (rv) + card->type = SC_CARD_TYPE_IASECC_MI2; + else + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + } rv = iasecc_parse_ef_atr(card); if (rv == SC_ERROR_FILE_NOT_FOUND) { @@ -569,65 +580,56 @@ iasecc_init_amos(struct sc_card *card) rv = iasecc_parse_ef_atr(card); } - LOG_TEST_RET(ctx, rv, "IASECC: ATR parse failed"); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); } -static int -iasecc_mi_match(struct sc_card *card) +inline static int +iasecc_is_cpx(const struct sc_card *card) { - struct sc_context *ctx = card->ctx; - - LOG_FUNC_CALLED(ctx); - - if (!card->ef_atr) - card->ef_atr = calloc(1, sizeof(struct sc_ef_atr)); - if (!card->ef_atr) - LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); - - memcpy(card->ef_atr->aid.value, MIIASECC_AID.value, MIIASECC_AID.len); - card->ef_atr->aid.len = MIIASECC_AID.len; + switch(card->type) { + case SC_CARD_TYPE_IASECC_CPX: + case SC_CARD_TYPE_IASECC_CPXCL: + return 1; + default: + return 0; + } - LOG_FUNC_RETURN(ctx, SC_SUCCESS); + return 0; } static int -iasecc_init_mi(struct sc_card *card) +iasecc_init_cpx(struct sc_card *card) { struct sc_context *ctx = card->ctx; unsigned int flags; - unsigned char resp[0x100]; - size_t resp_len; int rv = 0; LOG_FUNC_CALLED(ctx); - flags = IASECC_CARD_DEFAULT_FLAGS; - - _sc_card_add_rsa_alg(card, 1024, flags, 0x10001); - _sc_card_add_rsa_alg(card, 2048, flags, 0x10001); + card->caps = IASECC_CARD_DEFAULT_CAPS; - card->caps = SC_CARD_CAP_RNG; - card->caps |= SC_CARD_CAP_APDU_EXT; - card->caps |= SC_CARD_CAP_USE_FCI_AC; + flags = IASECC_CARD_DEFAULT_FLAGS; - resp_len = sizeof(resp); - rv = iasecc_select_aid(card, &MIIASECC_AID, resp, &resp_len); - LOG_TEST_RET(ctx, rv, "Could not select MI's AID"); + _sc_card_add_rsa_alg(card, 512, flags, 0); + _sc_card_add_rsa_alg(card, 1024, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); - rv = iasecc_mi_match(card); - LOG_TEST_RET(ctx, rv, "Could not match MI's AID"); + rv = iasecc_parse_ef_atr(card); + LOG_TEST_RET(ctx, rv, "Parse EF.ATR"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } + static int iasecc_init(struct sc_card *card) { struct sc_context *ctx = card->ctx; struct iasecc_private_data *private_data = NULL; int rv = SC_ERROR_NO_CARD_SUPPORT; + void *old_drv_data = card->drv_data; LOG_FUNC_CALLED(ctx); private_data = (struct iasecc_private_data *) calloc(1, sizeof(struct iasecc_private_data)); @@ -642,13 +644,16 @@ iasecc_init(struct sc_card *card) else if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR) rv = iasecc_init_oberthur(card); else if (card->type == SC_CARD_TYPE_IASECC_SAGEM) - rv = iasecc_init_sagem(card); + rv = iasecc_init_amos_or_sagem(card); else if (card->type == SC_CARD_TYPE_IASECC_AMOS) - rv = iasecc_init_amos(card); + rv = iasecc_init_amos_or_sagem(card); else if (card->type == SC_CARD_TYPE_IASECC_MI) - rv = iasecc_init_mi(card); - else - LOG_FUNC_RETURN(ctx, SC_ERROR_NO_CARD_SUPPORT); + rv = iasecc_init_amos_or_sagem(card); + else if (iasecc_is_cpx(card)) + rv = iasecc_init_cpx(card); + else { + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_INVALID_CARD, ""); + } if (!rv) { @@ -662,10 +667,10 @@ iasecc_init(struct sc_card *card) rv = iasecc_select_file(card, &path, NULL); sc_log(ctx, "Select ECC ROOT with the AID from EF.ATR: rv %i", rv); - LOG_TEST_RET(ctx, rv, "Select EF.ATR AID failed"); + LOG_TEST_GOTO_ERR(ctx, rv, "Select EF.ATR AID failed"); } - rv = iasecc_get_serialnr(card, NULL); + iasecc_get_serialnr(card, NULL); } #ifdef ENABLE_SM @@ -673,15 +678,26 @@ iasecc_init(struct sc_card *card) card->sm_ctx.ops.update_binary = _iasecc_sm_update_binary; #endif - if (!rv) + if (!rv && card->ef_atr && card->ef_atr->aid.len) { sc_log(ctx, "EF.ATR(aid:'%s')", sc_dump_hex(card->ef_atr->aid.value, card->ef_atr->aid.len)); + } + +err: + if (rv < 0) { + sc_invalidate_cache(card); + free(private_data); + card->drv_data = old_drv_data; + } else { + free(old_drv_data); + } + LOG_FUNC_RETURN(ctx, rv); } static int iasecc_read_binary(struct sc_card *card, unsigned int offs, - unsigned char *buf, size_t count, unsigned long flags) + unsigned char *buf, size_t count, unsigned long *flags) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; @@ -710,14 +726,14 @@ iasecc_read_binary(struct sc_card *card, unsigned int offs, apdu.resplen); if (apdu.resplen == IASECC_READ_BINARY_LENGTH_MAX && apdu.resplen < count) { - rv = iasecc_read_binary(card, offs + apdu.resplen, buf + apdu.resplen, count - apdu.resplen, flags); + rv = iasecc_read_binary(card, (int)(offs + apdu.resplen), buf + apdu.resplen, count - apdu.resplen, flags); if (rv != SC_ERROR_WRONG_LENGTH) { LOG_TEST_RET(ctx, rv, "iasecc_read_binary() read tail failed"); apdu.resplen += rv; } } - LOG_FUNC_RETURN(ctx, apdu.resplen); + LOG_FUNC_RETURN(ctx, (int)apdu.resplen); } @@ -742,7 +758,7 @@ iasecc_erase_binary(struct sc_card *card, unsigned int offs, size_t count, unsig rv = sc_update_binary(card, offs, tmp, count, flags); free(tmp); - LOG_TEST_RET(ctx, rv, "iasecc_erase_binary() update binary error"); + LOG_FUNC_RETURN(ctx, rv); } @@ -752,6 +768,7 @@ static int _iasecc_sm_read_binary(struct sc_card *card, unsigned int offs, unsigned char *buff, size_t count) { + struct iasecc_private_data *prv = (struct iasecc_private_data *)card->drv_data; struct sc_context *ctx = card->ctx; const struct sc_acl_entry *entry = NULL; int rv; @@ -766,16 +783,14 @@ _iasecc_sm_read_binary(struct sc_card *card, unsigned int offs, if (count == 0) return 0; - sc_print_cache(card); - - if (card->cache.valid && card->cache.current_ef) { - entry = sc_file_get_acl_entry(card->cache.current_ef, SC_AC_OP_READ); + if (prv->cache.valid && prv->cache.current_ef) { + entry = sc_file_get_acl_entry(prv->cache.current_ef, SC_AC_OP_READ); if (!entry) LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "iasecc_sm_read() 'READ' ACL not present"); sc_log(ctx, "READ method/reference %X/%X", entry->method, entry->key_ref); if ((entry->method == SC_AC_SCB) && (entry->key_ref & IASECC_SCB_METHOD_SM)) { - unsigned char se_num = (entry->method == SC_AC_SCB) ? (entry->key_ref & IASECC_SCB_METHOD_MASK_REF) : 0; + unsigned char se_num = entry->key_ref & IASECC_SCB_METHOD_MASK_REF; rv = iasecc_sm_read_binary(card, se_num, offs, buff, count); LOG_FUNC_RETURN(ctx, rv); @@ -790,6 +805,7 @@ static int _iasecc_sm_update_binary(struct sc_card *card, unsigned int offs, const unsigned char *buff, size_t count) { + struct iasecc_private_data *prv = (struct iasecc_private_data *)card->drv_data; struct sc_context *ctx = card->ctx; const struct sc_acl_entry *entry = NULL; int rv; @@ -799,18 +815,17 @@ _iasecc_sm_update_binary(struct sc_card *card, unsigned int offs, LOG_FUNC_CALLED(ctx); sc_log(ctx, - "iasecc_sm_read_binary() card:%p offs:%i count:%"SC_FORMAT_LEN_SIZE_T"u ", - card, offs, count); - sc_print_cache(card); + "iasecc_sm_read_binary() card:%p offs:%i count:%" SC_FORMAT_LEN_SIZE_T "u ", + card, offs, count); - if (card->cache.valid && card->cache.current_ef) { - entry = sc_file_get_acl_entry(card->cache.current_ef, SC_AC_OP_UPDATE); + if (prv->cache.valid && prv->cache.current_ef) { + entry = sc_file_get_acl_entry(prv->cache.current_ef, SC_AC_OP_UPDATE); if (!entry) LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "iasecc_sm_update() 'UPDATE' ACL not present"); sc_log(ctx, "UPDATE method/reference %X/%X", entry->method, entry->key_ref); if (entry->method == SC_AC_SCB && (entry->key_ref & IASECC_SCB_METHOD_SM)) { - unsigned char se_num = entry->method == SC_AC_SCB ? entry->key_ref & IASECC_SCB_METHOD_MASK_REF : 0; + unsigned char se_num = entry->key_ref & IASECC_SCB_METHOD_MASK_REF; rv = iasecc_sm_update_binary(card, se_num, offs, buff, count); LOG_FUNC_RETURN(ctx, rv); @@ -847,21 +862,22 @@ iasecc_emulate_fcp(struct sc_context *ctx, struct sc_apdu *apdu) memcpy(dummy_df_fcp + 16, apdu->data, apdu->datalen); dummy_df_fcp[15] = apdu->datalen; dummy_df_fcp[1] = apdu->datalen + 14; - memcpy(apdu->resp, dummy_df_fcp, apdu->datalen + 16); + apdu->resplen = apdu->datalen + 16; + memcpy(apdu->resp, dummy_df_fcp, apdu->resplen); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* TODO: redesign using of cache - * TODO: do not keep inermediate results in 'file_out' argument */ + * TODO: do not keep intermediate results in 'file_out' argument */ static int iasecc_select_file(struct sc_card *card, const struct sc_path *path, struct sc_file **file_out) { + struct iasecc_private_data *prv = (struct iasecc_private_data *)card->drv_data; struct sc_context *ctx = card->ctx; struct sc_path lpath; - int cache_valid = card->cache.valid, df_from_cache = 0; int rv, ii; LOG_FUNC_CALLED(ctx); @@ -874,17 +890,18 @@ iasecc_select_file(struct sc_card *card, const struct sc_path *path, card, path->len, path->type, path->aid.len); sc_log(ctx, "iasecc_select_file() path:%s", sc_print_path(path)); - sc_print_cache(card); - if (lpath.len >= 2 && lpath.value[0] == 0x3F && lpath.value[1] == 0x00) { + if ((!iasecc_is_cpx(card)) && + (card->type != SC_CARD_TYPE_IASECC_GEMALTO) && + (path->type != SC_PATH_TYPE_DF_NAME + && lpath.len >= 2 + && lpath.value[0] == 0x3F && lpath.value[1] == 0x00)) { sc_log(ctx, "EF.ATR(aid:'%s')", card->ef_atr ? sc_dump_hex(card->ef_atr->aid.value, card->ef_atr->aid.len) : ""); rv = iasecc_select_mf(card, file_out); LOG_TEST_RET(ctx, rv, "MF selection error"); - if (lpath.len >= 2 && lpath.value[0] == 0x3F && lpath.value[1] == 0x00) { - memmove(&lpath.value[0], &lpath.value[2], lpath.len - 2); - lpath.len -= 2; - } + memmove(&lpath.value[0], &lpath.value[2], lpath.len - 2); + lpath.len -= 2; } if (lpath.aid.len) { @@ -900,18 +917,15 @@ iasecc_select_file(struct sc_card *card, const struct sc_path *path, ppath.len = lpath.aid.len; ppath.type = SC_PATH_TYPE_DF_NAME; - if (card->cache.valid && card->cache.current_df - && card->cache.current_df->path.len == lpath.aid.len - && !memcmp(card->cache.current_df->path.value, lpath.aid.value, lpath.aid.len)) - df_from_cache = 1; - rv = iasecc_select_file(card, &ppath, &file); - LOG_TEST_RET(ctx, rv, "select AID path failed"); + LOG_TEST_GOTO_ERR(ctx, rv, "select AID path failed"); - if (file_out) + if (file_out) { + sc_file_free(*file_out); *file_out = file; - else - sc_file_free(file); + } else { + sc_file_free(file); + } if (lpath.type == SC_PATH_TYPE_DF_NAME) lpath.type = SC_PATH_TYPE_FROM_CURRENT; @@ -920,21 +934,11 @@ iasecc_select_file(struct sc_card *card, const struct sc_path *path, if (lpath.type == SC_PATH_TYPE_PATH) lpath.type = SC_PATH_TYPE_FROM_CURRENT; - if (!lpath.len) - LOG_FUNC_RETURN(ctx, SC_SUCCESS); - - sc_print_cache(card); - - if (card->cache.valid && card->cache.current_df && lpath.type == SC_PATH_TYPE_DF_NAME - && card->cache.current_df->path.len == lpath.len - && !memcmp(card->cache.current_df->path.value, lpath.value, lpath.len)) { - sc_log(ctx, "returns current DF path %s", sc_print_path(&card->cache.current_df->path)); - if (file_out) { + if (!lpath.len) { + if (file_out) { sc_file_free(*file_out); - sc_file_dup(file_out, card->cache.current_df); + *file_out = NULL; } - - sc_print_cache(card); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -942,7 +946,7 @@ iasecc_select_file(struct sc_card *card, const struct sc_path *path, struct sc_apdu apdu; struct sc_file *file = NULL; unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; - int pathlen = lpath.len; + size_t pathlen = lpath.len; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00); @@ -950,28 +954,37 @@ iasecc_select_file(struct sc_card *card, const struct sc_path *path, && card->type != SC_CARD_TYPE_IASECC_OBERTHUR && card->type != SC_CARD_TYPE_IASECC_SAGEM && card->type != SC_CARD_TYPE_IASECC_AMOS - && card->type != SC_CARD_TYPE_IASECC_MI) - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported card"); + && card->type != SC_CARD_TYPE_IASECC_MI + && card->type != SC_CARD_TYPE_IASECC_MI2 + && !iasecc_is_cpx(card)) { + rv = SC_ERROR_NOT_SUPPORTED; + LOG_TEST_GOTO_ERR(ctx, rv, "Unsupported card"); + } if (lpath.type == SC_PATH_TYPE_FILE_ID) { apdu.p1 = 0x02; - if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR) { + if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR) apdu.p1 = 0x01; + if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR || + card->type == SC_CARD_TYPE_IASECC_AMOS || + card->type == SC_CARD_TYPE_IASECC_MI || + card->type == SC_CARD_TYPE_IASECC_MI2 || + card->type == SC_CARD_TYPE_IASECC_GEMALTO || + iasecc_is_cpx(card) + ) { apdu.p2 = 0x04; } - if (card->type == SC_CARD_TYPE_IASECC_AMOS) - apdu.p2 = 0x04; - if (card->type == SC_CARD_TYPE_IASECC_MI) - apdu.p2 = 0x04; } else if (lpath.type == SC_PATH_TYPE_FROM_CURRENT) { apdu.p1 = 0x09; - if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR) - apdu.p2 = 0x04; - if (card->type == SC_CARD_TYPE_IASECC_AMOS) - apdu.p2 = 0x04; - if (card->type == SC_CARD_TYPE_IASECC_MI) + if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR || + card->type == SC_CARD_TYPE_IASECC_AMOS || + card->type == SC_CARD_TYPE_IASECC_MI || + card->type == SC_CARD_TYPE_IASECC_MI2 || + card->type == SC_CARD_TYPE_IASECC_GEMALTO || + iasecc_is_cpx(card)) { apdu.p2 = 0x04; + } } else if (lpath.type == SC_PATH_TYPE_PARENT) { apdu.p1 = 0x03; @@ -980,12 +993,18 @@ iasecc_select_file(struct sc_card *card, const struct sc_path *path, } else if (lpath.type == SC_PATH_TYPE_DF_NAME) { apdu.p1 = 0x04; - if (card->type == SC_CARD_TYPE_IASECC_AMOS) + if (card->type == SC_CARD_TYPE_IASECC_AMOS || + card->type == SC_CARD_TYPE_IASECC_MI2 || + card->type == SC_CARD_TYPE_IASECC_OBERTHUR || + card->type == SC_CARD_TYPE_IASECC_GEMALTO || + iasecc_is_cpx(card)) { apdu.p2 = 0x04; + } } else { sc_log(ctx, "Invalid PATH type: 0x%X", lpath.type); - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "iasecc_select_file() invalid PATH type"); + rv = SC_ERROR_NOT_SUPPORTED; + LOG_TEST_GOTO_ERR(ctx, rv, "iasecc_select_file() invalid PATH type"); } for (ii=0; ii<2; ii++) { @@ -998,10 +1017,11 @@ iasecc_select_file(struct sc_card *card, const struct sc_path *path, apdu.le = 256; rv = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + LOG_TEST_GOTO_ERR(ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); if (rv == SC_ERROR_INCORRECT_PARAMETERS && lpath.type == SC_PATH_TYPE_DF_NAME && apdu.p2 == 0x00) { + sc_log(ctx, "Warning: SC_ERROR_INCORRECT_PARAMETERS for SC_PATH_TYPE_DF_NAME, try again with P2=0x0C"); apdu.p2 = 0x0C; continue; } @@ -1010,29 +1030,13 @@ iasecc_select_file(struct sc_card *card, const struct sc_path *path, /* 'SELECT AID' do not returned FCP. Try to emulate. */ apdu.resplen = sizeof(rbuf); rv = iasecc_emulate_fcp(ctx, &apdu); - LOG_TEST_RET(ctx, rv, "Failed to emulate DF FCP"); + LOG_TEST_GOTO_ERR(ctx, rv, "Failed to emulate DF FCP"); } break; } - /* - * Using of the cached DF and EF can cause problems in the multi-thread environment. - * FIXME: introduce config. option that invalidates this cache outside the locked card session, - * (or invent something else) - */ - if (rv == SC_ERROR_FILE_NOT_FOUND && cache_valid && df_from_cache) { - card->cache.valid = 0; - sc_log(ctx, "iasecc_select_file() file not found, retry without cached DF"); - if (file_out) { - sc_file_free(*file_out); - *file_out = NULL; - } - rv = iasecc_select_file(card, path, file_out); - LOG_FUNC_RETURN(ctx, rv); - } - - LOG_TEST_RET(ctx, rv, "iasecc_select_file() check SW failed"); + LOG_TEST_GOTO_ERR(ctx, rv, "iasecc_select_file() check SW failed"); sc_log(ctx, "iasecc_select_file() apdu.resp %"SC_FORMAT_LEN_SIZE_T"u", @@ -1044,39 +1048,43 @@ iasecc_select_file(struct sc_card *card, const struct sc_path *path, case 0x62: case 0x6F: file = sc_file_new(); - if (file == NULL) + if (file == NULL) { + if (file_out) { + sc_file_free(*file_out); + *file_out = NULL; + } LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + } file->path = lpath; rv = iasecc_process_fci(card, file, apdu.resp, apdu.resplen); - if (rv) + if (rv) { + sc_file_free(file); + if (file_out) { + sc_file_free(*file_out); + *file_out = NULL; + } LOG_FUNC_RETURN(ctx, rv); + } break; default: + if (file_out) { + sc_file_free(*file_out); + *file_out = NULL; + } LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } sc_log(ctx, "FileType %i", file->type); if (file->type == SC_FILE_TYPE_DF) { - if (card->cache.valid) - sc_file_free(card->cache.current_df); - card->cache.current_df = NULL; - - - if (card->cache.valid) - sc_file_free(card->cache.current_ef); - card->cache.current_ef = NULL; - - sc_file_dup(&card->cache.current_df, file); - card->cache.valid = 1; + sc_invalidate_cache(card); + sc_file_dup(&prv->cache.current_df, file); + prv->cache.valid = 1; } else { - if (card->cache.valid) - sc_file_free(card->cache.current_ef); - - card->cache.current_ef = NULL; - - sc_file_dup(&card->cache.current_ef, file); + sc_file_free(prv->cache.current_ef); + sc_file_dup(&prv->cache.current_ef, file); + prv->cache.valid = 1; } if (file_out) { @@ -1088,18 +1096,18 @@ iasecc_select_file(struct sc_card *card, const struct sc_path *path, } } else if (lpath.type == SC_PATH_TYPE_DF_NAME) { - sc_file_free(card->cache.current_df); - card->cache.current_df = NULL; - - sc_file_free(card->cache.current_ef); - card->cache.current_ef = NULL; - - card->cache.valid = 1; + sc_invalidate_cache(card); + prv->cache.valid = 1; } } while(0); - sc_print_cache(card); LOG_FUNC_RETURN(ctx, SC_SUCCESS); +err: + if (file_out) { + sc_file_free(*file_out); + *file_out = NULL; + } + return rv; } @@ -1108,8 +1116,8 @@ iasecc_process_fci(struct sc_card *card, struct sc_file *file, const unsigned char *buf, size_t buflen) { struct sc_context *ctx = card->ctx; - size_t taglen; - int rv, ii, offs; + size_t taglen, offs, ii; + int rv; const unsigned char *acls = NULL, *tag = NULL; unsigned char mask; unsigned char ops_DF[7] = { @@ -1165,10 +1173,15 @@ iasecc_process_fci(struct sc_card *card, struct sc_file *file, for (ii = 0; ii < 7; ii++, mask /= 2) { unsigned char op = file->type == SC_FILE_TYPE_DF ? ops_DF[ii] : ops_EF[ii]; + /* avoid any access to acls[offs] beyond the taglen */ + if (offs >= taglen) { + sc_log(ctx, "Warning: Invalid offset reached during ACL parsing"); + break; + } if (!(mask & acls[0])) continue; - sc_log(ctx, "ACLs mask 0x%X, offs %i, op 0x%X, acls[offs] 0x%X", mask, offs, op, acls[offs]); + sc_log(ctx, "ACLs mask 0x%X, offs %"SC_FORMAT_LEN_SIZE_T"u, op 0x%X, acls[offs] 0x%X", mask, offs, op, acls[offs]); if (op == 0xFF) { ; } @@ -1205,7 +1218,7 @@ iasecc_fcp_encode(struct sc_card *card, struct sc_file *file, unsigned char *out SC_AC_OP_DELETE, 0xFF, SC_AC_OP_ACTIVATE, SC_AC_OP_DEACTIVATE, 0xFF, SC_AC_OP_UPDATE, SC_AC_OP_READ }; unsigned char smbs[8]; - size_t ii, offs = 0, amb, mask, nn_smb; + size_t ii, amb, offs = 0, mask, nn_smb; LOG_FUNC_CALLED(ctx); @@ -1313,13 +1326,14 @@ iasecc_fcp_encode(struct sc_card *card, struct sc_file *file, unsigned char *out memcpy(out, buf, offs); } - LOG_FUNC_RETURN(ctx, offs); + LOG_FUNC_RETURN(ctx, (int)offs); } static int iasecc_create_file(struct sc_card *card, struct sc_file *file) { + struct iasecc_private_data *prv = (struct iasecc_private_data *)card->drv_data; struct sc_context *ctx = card->ctx; struct sc_apdu apdu; const struct sc_acl_entry *entry = NULL; @@ -1328,19 +1342,19 @@ iasecc_create_file(struct sc_card *card, struct sc_file *file) int rv; LOG_FUNC_CALLED(ctx); - sc_print_cache(card); if (file->type != SC_FILE_TYPE_WORKING_EF) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Creation of the file with of this type is not supported"); - sbuf_len = iasecc_fcp_encode(card, file, sbuf + 2, sizeof(sbuf)-2); - LOG_TEST_RET(ctx, sbuf_len, "FCP encode error"); + rv = iasecc_fcp_encode(card, file, sbuf + 2, sizeof(sbuf)-2); + LOG_TEST_RET(ctx, rv, "FCP encode error"); + sbuf_len = rv; sbuf[0] = IASECC_FCP_TAG; sbuf[1] = sbuf_len; - if (card->cache.valid && card->cache.current_df) { - entry = sc_file_get_acl_entry(card->cache.current_df, SC_AC_OP_CREATE); + if (prv->cache.valid && prv->cache.current_df) { + entry = sc_file_get_acl_entry(prv->cache.current_df, SC_AC_OP_CREATE); if (!entry) LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "iasecc_create_file() 'CREATE' ACL not present"); @@ -1372,6 +1386,29 @@ iasecc_create_file(struct sc_card *card, struct sc_file *file) LOG_FUNC_RETURN(ctx, rv); } +static int +iasecc_get_challenge(struct sc_card *card, u8 * rnd, size_t len) +{ + /* As IAS/ECC cannot handle other data length than 0x08 */ + u8 rbuf[8]; + size_t out_len; + int r; + + LOG_FUNC_CALLED(card->ctx); + + r = iso_ops->get_challenge(card, rbuf, sizeof rbuf); + LOG_TEST_RET(card->ctx, r, "GET CHALLENGE cmd failed"); + + if (len < (size_t) r) { + out_len = len; + } else { + out_len = (size_t) r; + } + memcpy(rnd, rbuf, out_len); + + LOG_FUNC_RETURN(card->ctx, (int) out_len); +} + static int iasecc_logout(struct sc_card *card) @@ -1405,6 +1442,8 @@ iasecc_finish(struct sc_card *card) LOG_FUNC_CALLED(ctx); + sc_invalidate_cache(card); + while (se_info) { sc_file_free(se_info->df); next = se_info->next; @@ -1429,7 +1468,6 @@ iasecc_delete_file(struct sc_card *card, const struct sc_path *path) int rv; LOG_FUNC_CALLED(ctx); - sc_print_cache(card); rv = iasecc_select_file(card, path, &file); if (rv == SC_ERROR_FILE_NOT_FOUND) @@ -1437,15 +1475,20 @@ iasecc_delete_file(struct sc_card *card, const struct sc_path *path) LOG_TEST_RET(ctx, rv, "Cannot select file to delete"); entry = sc_file_get_acl_entry(file, SC_AC_OP_DELETE); - if (!entry) + if (!entry) { + sc_file_free(file); LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "Cannot delete file: no 'DELETE' acl"); + } sc_log(ctx, "DELETE method/reference %X/%X", entry->method, entry->key_ref); if (entry->method == SC_AC_SCB && (entry->key_ref & IASECC_SCB_METHOD_SM)) { - unsigned char se_num = (entry->method == SC_AC_SCB) ? (entry->key_ref & IASECC_SCB_METHOD_MASK_REF) : 0; + unsigned char se_num = entry->key_ref & IASECC_SCB_METHOD_MASK_REF; rv = iasecc_sm_delete_file(card, se_num, file->id); + sc_file_free(file); } else { + struct iasecc_private_data *prv = (struct iasecc_private_data *)card->drv_data; + sc_file_free(file); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, 0x00, 0x00); rv = sc_transmit_apdu(card, &apdu); @@ -1453,12 +1496,12 @@ iasecc_delete_file(struct sc_card *card, const struct sc_path *path) rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "Delete file failed"); - if (card->cache.valid) - sc_file_free(card->cache.current_ef); - card->cache.current_ef = NULL; + if (prv->cache.valid) { + sc_file_free(prv->cache.current_ef); + } + prv->cache.current_ef = NULL; } - sc_file_free(file); LOG_FUNC_RETURN(ctx, rv); } @@ -1504,7 +1547,7 @@ iasecc_get_algorithm(struct sc_context *ctx, const struct sc_security_env *env, static int iasecc_se_cache_info(struct sc_card *card, struct iasecc_se_info *se) { - struct iasecc_private_data *prv = (struct iasecc_private_data *) card->drv_data; + struct iasecc_private_data *prv = (struct iasecc_private_data *)card->drv_data; struct sc_context *ctx = card->ctx; struct iasecc_se_info *se_info = NULL, *si = NULL; int rv; @@ -1516,8 +1559,8 @@ iasecc_se_cache_info(struct sc_card *card, struct iasecc_se_info *se) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "SE info allocation error"); memcpy(se_info, se, sizeof(struct iasecc_se_info)); - if (card->cache.valid && card->cache.current_df) { - sc_file_dup(&se_info->df, card->cache.current_df); + if (prv->cache.valid && prv->cache.current_df) { + sc_file_dup(&se_info->df, prv->cache.current_df); if (se_info->df == NULL) { free(se_info); LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current DF file"); @@ -1547,7 +1590,7 @@ iasecc_se_cache_info(struct sc_card *card, struct iasecc_se_info *se) static int iasecc_se_get_info_from_cache(struct sc_card *card, struct iasecc_se_info *se) { - struct iasecc_private_data *prv = (struct iasecc_private_data *) card->drv_data; + struct iasecc_private_data *prv = (struct iasecc_private_data *)card->drv_data; struct sc_context *ctx = card->ctx; struct iasecc_se_info *si = NULL; int rv; @@ -1557,12 +1600,12 @@ iasecc_se_get_info_from_cache(struct sc_card *card, struct iasecc_se_info *se) for(si = prv->se_info; si; si = si->next) { if (si->reference != se->reference) continue; - if (!(card->cache.valid && card->cache.current_df) && si->df) + if (!(prv->cache.valid && prv->cache.current_df) && si->df) continue; - if (card->cache.valid && card->cache.current_df && !si->df) + if (prv->cache.valid && prv->cache.current_df && !si->df) continue; - if (card->cache.valid && card->cache.current_df && si->df) - if (memcmp(&card->cache.current_df->path, &si->df->path, sizeof(struct sc_path))) + if (prv->cache.valid && prv->cache.current_df && si->df) + if (memcmp(&prv->cache.current_df->path, &si->df->path, sizeof(struct sc_path))) continue; break; } @@ -1601,6 +1644,11 @@ iasecc_se_get_info(struct sc_card *card, struct iasecc_se_info *se) LOG_FUNC_CALLED(ctx); + if (iasecc_is_cpx(card)) { + rv = iasecc_select_mf(card, NULL); + LOG_TEST_RET(ctx, rv, "MF invalid"); + } + if (se->reference > IASECC_SE_REF_MAX) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); @@ -1638,10 +1686,11 @@ iasecc_set_security_env(struct sc_card *card, { struct sc_context *ctx = card->ctx; struct iasecc_sdo sdo; - struct iasecc_private_data *prv = (struct iasecc_private_data *) card->drv_data; + struct iasecc_private_data *prv = (struct iasecc_private_data *)card->drv_data; unsigned algo_ref; struct sc_apdu apdu; - unsigned sign_meth, sign_ref, auth_meth, auth_ref, aflags; + unsigned sign_meth, sign_ref, auth_meth, auth_ref; + unsigned long aflags; unsigned char cse_crt_at[] = { 0x84, 0x01, 0xFF, 0x80, 0x01, IASECC_ALGORITHM_RSA_PKCS @@ -1661,7 +1710,7 @@ iasecc_set_security_env(struct sc_card *card, /* TODO: take algorithm references from 5032, not from header file. */ LOG_FUNC_CALLED(ctx); - sc_log(ctx, "iasecc_set_security_env(card:%p) operation 0x%X; senv.algorithm 0x%X, senv.algorithm_ref 0x%X", + sc_log(ctx, "iasecc_set_security_env(card:%p) operation 0x%X; senv.algorithm 0x%lX, senv.algorithm_ref 0x%lX", card, env->operation, env->algorithm, env->algorithm_ref); memset(&sdo, 0, sizeof(sdo)); @@ -1670,6 +1719,8 @@ iasecc_set_security_env(struct sc_card *card, rv = iasecc_sdo_get_data(card, &sdo); LOG_TEST_RET(ctx, rv, "Cannot get RSA PRIVATE SDO data"); + if (sdo.docp.size.size < 2) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); /* To made by iasecc_sdo_convert_to_file() */ prv->key_size = *(sdo.docp.size.value + 0) * 0x100 + *(sdo.docp.size.value + 1); sc_log(ctx, "prv->key_size 0x%"SC_FORMAT_LEN_SIZE_T"X", prv->key_size); @@ -1682,7 +1733,7 @@ iasecc_set_security_env(struct sc_card *card, aflags = env->algorithm_flags; - if (!(aflags & SC_ALGORITHM_RSA_PAD_PKCS1)) + if (!(aflags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01)) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Only supported signature with PKCS1 padding"); if (operation == SC_SEC_OPERATION_SIGN) { @@ -1707,14 +1758,14 @@ iasecc_set_security_env(struct sc_card *card, prv->op_ref = auth_ref; } - sc_log(ctx, "senv.algorithm 0x%X, senv.algorithm_ref 0x%X", env->algorithm, env->algorithm_ref); + sc_log(ctx, "senv.algorithm 0x%lX, senv.algorithm_ref 0x%lX", env->algorithm, env->algorithm_ref); sc_log(ctx, - "se_num %i, operation 0x%X, algorithm 0x%X, algorithm_ref 0x%X, flags 0x%X; key size %"SC_FORMAT_LEN_SIZE_T"u", + "se_num %i, operation 0x%X, algorithm 0x%lX, algorithm_ref 0x%lX, flags 0x%lX; key size %"SC_FORMAT_LEN_SIZE_T"u", se_num, operation, env->algorithm, env->algorithm_ref, env->algorithm_flags, prv->key_size); switch (operation) { case SC_SEC_OPERATION_SIGN: - if (!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)) + if (!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Need RSA_PKCS1 specified"); if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA256) { @@ -1782,7 +1833,7 @@ iasecc_set_security_env(struct sc_card *card, LOG_TEST_RET(ctx, rv, "Cannot convert SC_AC_OP_PSO_DECRYPT acl"); algo_ref = iasecc_get_algorithm(ctx, env, SC_PKCS15_ALGO_OP_DECIPHER, CKM_RSA_PKCS); if (!algo_ref) - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Application do not supports DECHIPHER:RSA_PKCS"); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Application do not supports DECIPHER:RSA_PKCS"); cse_crt_ct[2] = env->key_ref[0] | IASECC_OBJECT_REF_LOCAL; cse_crt_ct[5] = algo_ref; /* IASECC_ALGORITHM_RSA_PKCS_DECRYPT | IASECC_ALGORITHM_SHA1 */ @@ -1809,97 +1860,22 @@ iasecc_set_security_env(struct sc_card *card, static int -iasecc_chv_verify_pinpad(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, int *tries_left) +iasecc_chv_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, unsigned char *scbs) { struct sc_context *ctx = card->ctx; - unsigned char buffer[0x100]; + unsigned char scb = scbs[IASECC_ACLS_CHV_VERIFY]; int rv; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "CHV PINPAD PIN reference %i", pin_cmd->pin_reference); - - rv = iasecc_pin_is_verified(card, pin_cmd, tries_left); - if (!rv) - LOG_FUNC_RETURN(ctx, rv); - - if (!card->reader || !card->reader->ops || !card->reader->ops->perform_verify) { - sc_log(ctx, "Reader not ready for PIN PAD"); - LOG_FUNC_RETURN(ctx, SC_ERROR_READER); - } - - /* When PIN stored length available - * P10 verify data contains full template of 'VERIFY PIN' APDU. - * Without PIN stored length - * pin-pad has to set the Lc and fill PIN data itself. - * Not all pin-pads support this case - */ - pin_cmd->pin1.len = pin_cmd->pin1.stored_length; - pin_cmd->pin1.length_offset = 5; - - memset(buffer, 0xFF, sizeof(buffer)); - pin_cmd->pin1.data = buffer; - - pin_cmd->cmd = SC_PIN_CMD_VERIFY; - pin_cmd->flags |= SC_PIN_CMD_USE_PINPAD; - - /* - if (card->reader && card->reader->ops && card->reader->ops->load_message) { - rv = card->reader->ops->load_message(card->reader, card->slot, 0, "Here we are!"); - sc_log(ctx, "Load message returned %i", rv); - } - */ - - rv = iso_ops->pin_cmd(card, pin_cmd, tries_left); - sc_log(ctx, "rv %i", rv); - - LOG_FUNC_RETURN(ctx, rv); -} - - -static int -iasecc_chv_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, - int *tries_left) -{ - struct sc_context *ctx = card->ctx; - struct sc_acl_entry acl = pin_cmd->pin1.acls[IASECC_ACLS_CHV_VERIFY]; - struct sc_apdu apdu; - int rv; - - LOG_FUNC_CALLED(ctx); - sc_log(ctx, "Verify CHV PIN(ref:%i,len:%i,acl:%X:%X)", pin_cmd->pin_reference, pin_cmd->pin1.len, - acl.method, acl.key_ref); - - if (acl.method & IASECC_SCB_METHOD_SM) { - rv = iasecc_sm_pin_verify(card, acl.key_ref, pin_cmd, tries_left); - LOG_FUNC_RETURN(ctx, rv); - } + sc_log(ctx, "Verify CHV PIN(ref:%i,len:%zu,scb:%X)", pin_cmd->pin_reference, pin_cmd->pin1.len, + scb); - if (pin_cmd->pin1.data && !pin_cmd->pin1.len) { - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0, pin_cmd->pin_reference); - } - else if (pin_cmd->pin1.data && pin_cmd->pin1.len) { - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, pin_cmd->pin_reference); - apdu.data = pin_cmd->pin1.data; - apdu.datalen = pin_cmd->pin1.len; - apdu.lc = pin_cmd->pin1.len; - } - else if ((card->reader->capabilities & SC_READER_CAP_PIN_PAD) && !pin_cmd->pin1.data && !pin_cmd->pin1.len) { - rv = iasecc_chv_verify_pinpad(card, pin_cmd, tries_left); - sc_log(ctx, "Result of verifying CHV with PIN pad %i", rv); + if (scb & IASECC_SCB_METHOD_SM) { + rv = iasecc_sm_pin_verify(card, scb & IASECC_SCB_METHOD_MASK_REF, pin_cmd); LOG_FUNC_RETURN(ctx, rv); } - else { - LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); - } - - rv = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(ctx, rv, "APDU transmit failed"); - - if (tries_left && apdu.sw1 == 0x63 && (apdu.sw2 & 0xF0) == 0xC0) - *tries_left = apdu.sw2 & 0x0F; - - rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + rv = iso_ops->pin_cmd(card, pin_cmd); LOG_FUNC_RETURN(ctx, rv); } @@ -1942,241 +1918,166 @@ iasecc_se_at_to_chv_reference(struct sc_card *card, unsigned reference, static int -iasecc_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd_data, - int *tries_left) +iasecc_pin_get_status(struct sc_card *card, struct sc_pin_cmd_data *data) { struct sc_context *ctx = card->ctx; - struct sc_pin_cmd_data pin_cmd; - struct sc_acl_entry acl = pin_cmd_data->pin1.acls[IASECC_ACLS_CHV_VERIFY]; - int rv = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; + struct sc_pin_cmd_data info; + int rv; LOG_FUNC_CALLED(ctx); - if (pin_cmd_data->pin_type != SC_AC_CHV) - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "PIN type is not supported for the verification"); + if (data->pin_type != SC_AC_CHV) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "PIN type is not supported for status"); - sc_log(ctx, "Verify ACL(method:%X;ref:%X)", acl.method, acl.key_ref); - if (acl.method != IASECC_SCB_ALWAYS) - LOG_FUNC_RETURN(ctx, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED); + memset(&info, 0, sizeof(info)); + info.cmd = SC_PIN_CMD_GET_INFO; + info.pin_type = data->pin_type; + info.pin_reference = data->pin_reference; - pin_cmd = *pin_cmd_data; - pin_cmd.pin1.data = (unsigned char *)""; - pin_cmd.pin1.len = 0; + rv = iso_ops->pin_cmd(card, &info); + LOG_TEST_RET(ctx, rv, "Failed to get PIN info"); - rv = iasecc_chv_verify(card, &pin_cmd, tries_left); + data->pin1.max_tries = info.pin1.max_tries; + data->pin1.tries_left = info.pin1.tries_left; + data->pin1.logged_in = info.pin1.logged_in; LOG_FUNC_RETURN(ctx, rv); } static int -iasecc_pin_verify(struct sc_card *card, unsigned type, unsigned reference, - const unsigned char *data, size_t data_len, int *tries_left) +iasecc_pin_verify(struct sc_card *card, struct sc_pin_cmd_data *data) { struct sc_context *ctx = card->ctx; + unsigned type = data->pin_type; + unsigned reference = data->pin_reference; struct sc_pin_cmd_data pin_cmd; - unsigned chv_ref = reference; + struct iasecc_pin_policy policy; + int tries_before_verify = -1; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, - "Verify PIN(type:%X,ref:%i,data(len:%"SC_FORMAT_LEN_SIZE_T"u,%p)", - type, reference, data_len, data); + "Verify PIN(type:%X,ref:%i,data(len:%zu,%p)", + type, reference, data->pin1.len, data->pin1.data); if (type == SC_AC_AUT) { - rv = iasecc_sm_external_authentication(card, reference, tries_left); + rv = iasecc_sm_external_authentication(card, reference, &data->pin1.tries_left); LOG_FUNC_RETURN(ctx, rv); } - else if (type == SC_AC_SCB) { + + if (type == SC_AC_SCB) { if (reference & IASECC_SCB_METHOD_USER_AUTH) { type = SC_AC_SEN; reference = reference & IASECC_SCB_METHOD_MASK_REF; } - else { - sc_log(ctx, "Do not try to verify non CHV PINs"); - LOG_FUNC_RETURN(ctx, SC_SUCCESS); - } } if (type == SC_AC_SEN) { - rv = iasecc_se_at_to_chv_reference(card, reference, &chv_ref); + type = SC_AC_CHV; + rv = iasecc_se_at_to_chv_reference(card, reference, &reference); LOG_TEST_RET(ctx, rv, "SE AT to CHV reference error"); } - memset(&pin_cmd, 0, sizeof(pin_cmd)); + if (type != SC_AC_CHV) { + sc_log(ctx, "Do not try to verify non CHV PINs"); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + pin_cmd = *data; pin_cmd.pin_type = SC_AC_CHV; - pin_cmd.pin_reference = chv_ref; + pin_cmd.pin_reference = reference; pin_cmd.cmd = SC_PIN_CMD_VERIFY; - rv = iasecc_pin_get_policy(card, &pin_cmd); - LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); - - pin_cmd.pin1.data = data; - pin_cmd.pin1.len = data_len; - - rv = iasecc_pin_is_verified(card, &pin_cmd, tries_left); - if (data && !data_len) + rv = iasecc_pin_get_status(card, &pin_cmd); + if (data->pin1.data && !data->pin1.len) LOG_FUNC_RETURN(ctx, rv); if (!rv) { - if (iasecc_chv_cache_is_verified(card, &pin_cmd)) - LOG_FUNC_RETURN(ctx, SC_SUCCESS); + if (pin_cmd.pin1.logged_in & SC_PIN_STATE_LOGGED_IN) + if (iasecc_chv_cache_is_verified(card, &pin_cmd)) + LOG_FUNC_RETURN(ctx, SC_SUCCESS); } - else if (rv != SC_ERROR_PIN_CODE_INCORRECT && rv != SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { + else if (rv != SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { LOG_FUNC_RETURN(ctx, rv); } iasecc_chv_cache_clean(card, &pin_cmd); - rv = iasecc_chv_verify(card, &pin_cmd, tries_left); - LOG_TEST_RET(ctx, rv, "PIN CHV verification error"); - - rv = iasecc_chv_cache_verified(card, &pin_cmd); - - LOG_FUNC_RETURN(ctx, rv); -} - - -static int -iasecc_chv_change_pinpad(struct sc_card *card, unsigned reference, int *tries_left) -{ - struct sc_context *ctx = card->ctx; - struct sc_pin_cmd_data pin_cmd; - unsigned char pin1_data[0x100], pin2_data[0x100]; - int rv; - - LOG_FUNC_CALLED(ctx); - sc_log(ctx, "CHV PINPAD PIN reference %i", reference); - - memset(pin1_data, 0xFF, sizeof(pin1_data)); - memset(pin2_data, 0xFF, sizeof(pin2_data)); + rv = iasecc_pin_merge_policy(card, &pin_cmd, &pin_cmd.pin1, &policy); + LOG_TEST_RET(ctx, rv, "Failed to update PIN1 info"); - if (!card->reader || !card->reader->ops || !card->reader->ops->perform_verify) { - sc_log(ctx, "Reader not ready for PIN PAD"); - LOG_FUNC_RETURN(ctx, SC_ERROR_READER); + /* PIN-pads work best with fixed-size lengths. Use PIN padding when length is available. */ + if (pin_cmd.flags & SC_PIN_CMD_USE_PINPAD) { + tries_before_verify = pin_cmd.pin1.tries_left; + if (policy.stored_length > 0) + iasecc_set_pin_padding(&pin_cmd, &pin_cmd.pin1, policy.stored_length); } - memset(&pin_cmd, 0, sizeof(pin_cmd)); - pin_cmd.pin_type = SC_AC_CHV; - pin_cmd.pin_reference = reference; - pin_cmd.cmd = SC_PIN_CMD_CHANGE; - pin_cmd.flags |= SC_PIN_CMD_USE_PINPAD; - - rv = iasecc_pin_get_policy(card, &pin_cmd); - LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); + rv = iasecc_chv_verify(card, &pin_cmd, policy.scbs); - /* Some pin-pads do not support mode with Lc=0. - * Give them a chance to work with some cards. + /* + * Detect and log PIN-pads which don't handle variable-length PIN - special case where they + * forward the CHV verify command with Lc = 0 to the card, without updating Lc. An IAS-ECC + * card responds to this command by returning the number of attempts left, without + * decreasing the counter. */ - if ((pin_cmd.pin1.min_length == pin_cmd.pin1.stored_length) && (pin_cmd.pin1.max_length == pin_cmd.pin1.min_length)) - pin_cmd.pin1.len = pin_cmd.pin1.stored_length; - else - pin_cmd.pin1.len = 0; - - pin_cmd.pin1.length_offset = 5; - pin_cmd.pin1.data = pin1_data; - - memcpy(&pin_cmd.pin2, &pin_cmd.pin1, sizeof(pin_cmd.pin1)); - pin_cmd.pin2.data = pin2_data; - - sc_log(ctx, - "PIN1 max/min/stored: %"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u", - pin_cmd.pin1.max_length, pin_cmd.pin1.min_length, - pin_cmd.pin1.stored_length); - sc_log(ctx, - "PIN2 max/min/stored: %"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u", - pin_cmd.pin2.max_length, pin_cmd.pin2.min_length, - pin_cmd.pin2.stored_length); - rv = iso_ops->pin_cmd(card, &pin_cmd, tries_left); - - LOG_FUNC_RETURN(ctx, rv); -} - - -#if 0 -static int -iasecc_chv_set_pinpad(struct sc_card *card, unsigned char reference) -{ - struct sc_context *ctx = card->ctx; - struct sc_pin_cmd_data pin_cmd; - unsigned char pin_data[0x100]; - int rv; - - LOG_FUNC_CALLED(ctx); - sc_log(ctx, "Set CHV PINPAD PIN reference %i", reference); - - memset(pin_data, 0xFF, sizeof(pin_data)); - - if (!card->reader || !card->reader->ops || !card->reader->ops->perform_verify) { - sc_log(ctx, "Reader not ready for PIN PAD"); - LOG_FUNC_RETURN(ctx, SC_ERROR_READER); + if ((pin_cmd.flags & SC_PIN_CMD_USE_PINPAD) && !(pin_cmd.flags & SC_PIN_CMD_NEED_PADDING)) { + if (rv == SC_ERROR_PIN_CODE_INCORRECT && pin_cmd.pin1.tries_left == tries_before_verify) { + SC_TEST_RET(ctx, SC_LOG_DEBUG_VERBOSE, rv, + "PIN-pad reader does not support variable-length PIN"); + } } - memset(&pin_cmd, 0, sizeof(pin_cmd)); - pin_cmd.pin_type = SC_AC_CHV; - pin_cmd.pin_reference = reference; - pin_cmd.cmd = SC_PIN_CMD_UNBLOCK; - pin_cmd.flags |= SC_PIN_CMD_USE_PINPAD; - - rv = iasecc_pin_get_policy(card, &pin_cmd); - LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); - - if ((pin_cmd.pin1.min_length == pin_cmd.pin1.stored_length) && (pin_cmd.pin1.max_length == pin_cmd.pin1.min_length)) - pin_cmd.pin1.len = pin_cmd.pin1.stored_length; - else - pin_cmd.pin1.len = 0; - - pin_cmd.pin1.length_offset = 5; - pin_cmd.pin1.data = pin_data; - - memcpy(&pin_cmd.pin2, &pin_cmd.pin1, sizeof(pin_cmd.pin1)); - memset(&pin_cmd.pin1, 0, sizeof(pin_cmd.pin1)); - pin_cmd.flags |= SC_PIN_CMD_IMPLICIT_CHANGE; + LOG_TEST_RET(ctx, rv, "PIN CHV verification error"); - sc_log(ctx, "PIN1(max:%i,min:%i)", pin_cmd.pin1.max_length, pin_cmd.pin1.min_length); - sc_log(ctx, "PIN2(max:%i,min:%i)", pin_cmd.pin2.max_length, pin_cmd.pin2.min_length); + rv = iasecc_chv_cache_verified(card, &pin_cmd); - rv = iso_ops->pin_cmd(card, &pin_cmd, NULL); LOG_FUNC_RETURN(ctx, rv); } -#endif static int -iasecc_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data) +iasecc_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data, struct iasecc_pin_policy *pin) { + struct iasecc_private_data *prv = (struct iasecc_private_data *)card->drv_data; struct sc_context *ctx = card->ctx; struct sc_file *save_current_df = NULL, *save_current_ef = NULL; struct iasecc_sdo sdo; struct sc_path path; - unsigned ii; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_pin_get_policy(card:%p)", card); if (data->pin_type != SC_AC_CHV) { - sc_log(ctx, "To unblock PIN it's CHV reference should be presented"); + sc_log(ctx, "PIN policy only available for CHV type"); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } - if (card->cache.valid && card->cache.current_df) { - sc_file_dup(&save_current_df, card->cache.current_df); - if (save_current_df == NULL) - LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current DF file"); + if (prv->cache.valid && prv->cache.current_df) { + sc_file_dup(&save_current_df, prv->cache.current_df); + if (save_current_df == NULL) { + rv = SC_ERROR_OUT_OF_MEMORY; + sc_log(ctx, "Cannot duplicate current DF file"); + goto err; + } } - if (card->cache.valid && card->cache.current_ef) { - sc_file_dup(&save_current_ef, card->cache.current_ef); - if (save_current_ef == NULL) - LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current EF file"); + if (prv->cache.valid && prv->cache.current_ef) { + sc_file_dup(&save_current_ef, prv->cache.current_ef); + if (save_current_ef == NULL) { + rv = SC_ERROR_OUT_OF_MEMORY; + sc_log(ctx, "Cannot duplicate current EF file"); + goto err; + } } - if (!(data->pin_reference & IASECC_OBJECT_REF_LOCAL) && card->cache.valid && card->cache.current_df) { + if (!(data->pin_reference & IASECC_OBJECT_REF_LOCAL) && prv->cache.valid && prv->cache.current_df) { sc_format_path("3F00", &path); path.type = SC_PATH_TYPE_FILE_ID; rv = iasecc_select_file(card, &path, NULL); - LOG_TEST_RET(ctx, rv, "Unable to select MF"); + LOG_TEST_GOTO_ERR(ctx, rv, "Unable to select MF"); } memset(&sdo, 0, sizeof(sdo)); @@ -2187,104 +2088,173 @@ iasecc_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data) sc_log(ctx, "iasecc_pin_get_policy() reference %i", sdo.sdo_ref); rv = iasecc_sdo_get_data(card, &sdo); - LOG_TEST_RET(ctx, rv, "Cannot get SDO PIN data"); + LOG_TEST_GOTO_ERR(ctx, rv, "Cannot get SDO PIN data"); - if (sdo.docp.acls_contact.size == 0) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Extremely strange ... there is no ACLs"); + if (sdo.docp.acls_contact.size == 0) { + rv = SC_ERROR_INVALID_DATA; + sc_log(ctx, "Extremely strange ... there is no ACLs"); + goto err; + } sc_log(ctx, "iasecc_pin_get_policy() sdo.docp.size.size %"SC_FORMAT_LEN_SIZE_T"u", sdo.docp.size.size); - for (ii=0; iipin1.acls[ii]; - int crt_num = 0; - memset(&se, 0, sizeof(se)); - memset(&acl->crts, 0, sizeof(acl->crts)); + memcpy(pin->scbs, sdo.docp.scbs, sizeof(pin->scbs)); - sc_log(ctx, "iasecc_pin_get_policy() set info acls: SCB 0x%X", scb); - /* acl->raw_value = scb; */ - acl->method = scb & IASECC_SCB_METHOD_MASK; - acl->key_ref = scb & IASECC_SCB_METHOD_MASK_REF; - - if (scb==0 || scb==0xFF) - continue; - - if (se.reference != (int)acl->key_ref) { - memset(&se, 0, sizeof(se)); - - se.reference = acl->key_ref; - - rv = iasecc_se_get_info(card, &se); - LOG_TEST_RET(ctx, rv, "SDO get data error"); - } - - if (scb & IASECC_SCB_METHOD_USER_AUTH) { - rv = iasecc_se_get_crt_by_usage(card, &se, - IASECC_CRT_TAG_AT, IASECC_UQB_AT_USER_PASSWORD, &acl->crts[crt_num]); - LOG_TEST_RET(ctx, rv, "no authentication template for 'USER PASSWORD'"); - sc_log(ctx, "iasecc_pin_get_policy() scb:0x%X; sdo_ref:[%i,%i,...]", - scb, acl->crts[crt_num].refs[0], acl->crts[crt_num].refs[1]); - crt_num++; - } - - if (scb & (IASECC_SCB_METHOD_SM | IASECC_SCB_METHOD_EXT_AUTH)) { - sc_log(ctx, "'SM' and 'EXTERNAL AUTHENTICATION' protection methods are not supported: SCB:0x%X", scb); - /* Set to 'NEVER' if all conditions are needed or - * there is no user authentication method allowed */ - if (!crt_num || (scb & IASECC_SCB_METHOD_NEED_ALL)) - acl->method = SC_AC_NEVER; - continue; - } - - sc_file_free(se.df); - } - - if (sdo.data.chv.size_max.value) - data->pin1.max_length = *sdo.data.chv.size_max.value; - if (sdo.data.chv.size_min.value) - data->pin1.min_length = *sdo.data.chv.size_min.value; - if (sdo.docp.tries_maximum.value) - data->pin1.max_tries = *sdo.docp.tries_maximum.value; - if (sdo.docp.tries_remaining.value) - data->pin1.tries_left = *sdo.docp.tries_remaining.value; - if (sdo.docp.size.value) { - for (ii=0; iipin1.stored_length = ((data->pin1.stored_length) << 8) + *(sdo.docp.size.value + ii); + pin->min_length = (sdo.data.chv.size_min.value ? *sdo.data.chv.size_min.value : -1); + pin->max_length = (sdo.data.chv.size_max.value ? *sdo.data.chv.size_max.value : -1); + pin->tries_maximum = (sdo.docp.tries_maximum.value ? *sdo.docp.tries_maximum.value : -1); + pin->tries_remaining = (sdo.docp.tries_remaining.value ? *sdo.docp.tries_remaining.value : -1); + if (sdo.docp.size.value && sdo.docp.size.size <= sizeof(int)) { + unsigned int n = 0; + unsigned int i; + for (i=0; istored_length = n; + } else { + pin->stored_length = -1; } - data->pin1.encoding = SC_PIN_ENCODING_ASCII; - data->pin1.offset = 5; - data->pin1.logged_in = SC_PIN_STATE_UNKNOWN; - - sc_log(ctx, - "PIN policy: size max/min %"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u, tries max/left %i/%i", - data->pin1.max_length, data->pin1.min_length, - data->pin1.max_tries, data->pin1.tries_left); + sc_log(ctx, "PIN policy: size max/min %i/%i, tries max/left %i/%i", + pin->max_length, pin->min_length, pin->tries_maximum, pin->tries_remaining); iasecc_sdo_free_fields(card, &sdo); if (save_current_df) { sc_log(ctx, "iasecc_pin_get_policy() restore current DF"); rv = iasecc_select_file(card, &save_current_df->path, NULL); - LOG_TEST_RET(ctx, rv, "Cannot return to saved DF"); - sc_file_free(save_current_df); + LOG_TEST_GOTO_ERR(ctx, rv, "Cannot return to saved DF"); } if (save_current_ef) { sc_log(ctx, "iasecc_pin_get_policy() restore current EF"); rv = iasecc_select_file(card, &save_current_ef->path, NULL); - LOG_TEST_RET(ctx, rv, "Cannot return to saved EF"); - sc_file_free(save_current_ef); + LOG_TEST_GOTO_ERR(ctx, rv, "Cannot return to saved EF"); + } + +err: + sc_file_free(save_current_df); + sc_file_free(save_current_ef); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_pin_get_info(struct sc_card *card, struct sc_pin_cmd_data *data) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_pin_policy policy; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "iasecc_pin_get_info(card:%p)", card); + + /* + * Get PIN status first and thereafter update with info from PIN policy, when available. + * The first one is typically used for the PIN verification status and number of remaining + * tries, and the second one for the maximum tries. If a field is present in both, the + * policy takes precedence. + */ + rv = iasecc_pin_get_status(card, data); + LOG_TEST_RET(ctx, rv, "Failed to get PIN status"); + + rv = iasecc_pin_get_policy(card, data, &policy); + LOG_TEST_RET(ctx, rv, "Failed to get PIN policy"); + + /* + * We only care about the tries_xxx fields in the PIN policy, since the other ones are not + * commonly expected or used in a SC_PIN_CMD_GET_INFO response. Note that max_tries is + * always taken from the policy, since it is never expected to be available in status (it + * is set to -1 when not available in policy). + */ + data->pin1.max_tries = policy.tries_maximum; + if (policy.tries_remaining >= 0) + data->pin1.tries_left = policy.tries_remaining; + + LOG_FUNC_RETURN(ctx, rv); +} + + +/* + * Check PIN and update flags. We reject empty PINs (where data is non-NULL but length is 0) due + * to their non-obvious meaning in verification/change/unblock. We also need to update the + * SC_PIN_CMD_USE_PINPAD flag depending on the PIN being available or not (where data is NULL means + * that PIN is not available). Unfortunately we can not rely on the flag provided by the caller due + * to its ambiguous use. The approach here is to assume pin-pad input when the PIN data is NULL, + * otherwise not. + */ +static int iasecc_check_update_pin(struct sc_pin_cmd_data *data, struct sc_pin_cmd_pin *pin) +{ + if ((!pin->data && pin->len) || (pin->data && !pin->len)) + return SC_ERROR_INVALID_ARGUMENTS; + + if (pin->data) + data->flags &= ~SC_PIN_CMD_USE_PINPAD; + else + data->flags |= SC_PIN_CMD_USE_PINPAD; + + return SC_SUCCESS; +} + + +/* Enable PIN padding with 0xff as the padding character, unless already enabled */ +static void iasecc_set_pin_padding(struct sc_pin_cmd_data *data, struct sc_pin_cmd_pin *pin, + size_t pad_len) +{ + if (data->flags & SC_PIN_CMD_NEED_PADDING) + return; + + pin->pad_length = pad_len; + pin->pad_char = 0xff; + data->flags |= SC_PIN_CMD_NEED_PADDING; +} + + +/* + * Retrieve the PIN policy and combine it with the existing fields in an intelligent way. This is + * needed since we may be called with existing settings, typically from the PKCS #15 layer. We use + * the IAS-ECC card-level PIN settings as complementary. + */ +static int +iasecc_pin_merge_policy(struct sc_card *card, struct sc_pin_cmd_data *data, + struct sc_pin_cmd_pin *pin, struct iasecc_pin_policy *policy) +{ + struct sc_context *ctx = card->ctx; + size_t pad_len = 0; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "iasecc_pin_merge_policy(card:%p)", card); + + rv = iasecc_check_update_pin(data, pin); + LOG_TEST_RET(ctx, rv, "Invalid PIN"); + + rv = iasecc_pin_get_policy(card, data, policy); + LOG_TEST_RET(ctx, rv, "Failed to get PIN policy"); + + /* Some cards obviously use the min/max length fields to signal PIN padding */ + if (policy->min_length > 0 && policy->min_length == policy->max_length) { + pad_len = policy->min_length; + policy->min_length = 0; } + /* Take the most limited values of min/max lengths */ + if (policy->min_length > 0 && (size_t) policy->min_length > pin->min_length) + pin->min_length = policy->min_length; + if (policy->max_length > 0 && (!pin->max_length || (size_t) policy->max_length < pin->max_length)) + pin->max_length = policy->max_length; + + /* Set PIN padding if needed and not already set by the caller */ + if (pad_len) + iasecc_set_pin_padding(data, pin, pad_len); + LOG_FUNC_RETURN(ctx, rv); } static int -iasecc_keyset_change(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +iasecc_keyset_change(struct sc_card *card, struct sc_pin_cmd_data *data) { struct sc_context *ctx = card->ctx; struct iasecc_sdo_update update; @@ -2293,7 +2263,7 @@ iasecc_keyset_change(struct sc_card *card, struct sc_pin_cmd_data *data, int *tr int rv; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "Change keyset(ref:%i,lengths:%i)", data->pin_reference, data->pin2.len); + sc_log(ctx, "Change keyset(ref:%i,lengths:%zu)", data->pin_reference, data->pin2.len); if (!data->pin2.data || data->pin2.len < 32) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Needs at least 32 bytes for a new keyset value"); @@ -2307,16 +2277,17 @@ iasecc_keyset_change(struct sc_card *card, struct sc_pin_cmd_data *data, int *tr if (sdo.docp.acls_contact.size == 0) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Bewildered ... there are no ACLs"); scb = sdo.docp.scbs[IASECC_ACLS_KEYSET_PUT_DATA]; + + memset(&update, 0, sizeof(update)); + update.magic = SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA; + update.sdo_class = sdo.sdo_class; + update.sdo_ref = sdo.sdo_ref; iasecc_sdo_free_fields(card, &sdo); sc_log(ctx, "SCB:0x%X", scb); if (!(scb & IASECC_SCB_METHOD_SM)) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Other then protected by SM, the keyset change is not supported"); - memset(&update, 0, sizeof(update)); - update.magic = SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA; - update.sdo_class = sdo.sdo_class; - update.sdo_ref = sdo.sdo_ref; update.fields[0].parent_tag = IASECC_SDO_KEYSET_TAG; update.fields[0].tag = IASECC_SDO_KEYSET_TAG_MAC; @@ -2335,107 +2306,121 @@ iasecc_keyset_change(struct sc_card *card, struct sc_pin_cmd_data *data, int *tr } +/* + * The PIN change function can handle different PIN-pad input combinations for the old and new + * PINs: + * OLD PIN: NEW PIN: DESCRIPTION: + * Available Available No input. + * Available Absent Only new PIN is input. + * Absent Available Both PINs are input (due to limitations in IAS-ECC) + * Absent Absent Both PINs are input. + */ static int -iasecc_pin_change(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +iasecc_pin_change(struct sc_card *card, struct sc_pin_cmd_data *data) { struct sc_context *ctx = card->ctx; - struct sc_apdu apdu; - unsigned reference = data->pin_reference; - unsigned char pin_data[0x100]; + struct sc_pin_cmd_data pin_cmd; + struct iasecc_pin_policy policy; int rv; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "Change PIN(ref:%i,type:0x%X,lengths:%i/%i)", reference, data->pin_type, data->pin1.len, data->pin2.len); + sc_log(ctx, "Change PIN(ref:%i,type:0x%X,lengths:%zu/%zu)", + data->pin_reference, data->pin_type, data->pin1.len, data->pin2.len); - if ((card->reader->capabilities & SC_READER_CAP_PIN_PAD)) { - if (!data->pin1.data && !data->pin1.len && !data->pin2.data && !data->pin2.len) { - rv = iasecc_chv_change_pinpad(card, reference, tries_left); - sc_log(ctx, "iasecc_pin_cmd(SC_PIN_CMD_CHANGE) chv_change_pinpad returned %i", rv); - LOG_FUNC_RETURN(ctx, rv); - } + if (data->pin_type != SC_AC_CHV) { + sc_log(ctx, "Can not change non-CHV PINs"); + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } - if (!data->pin1.data && data->pin1.len) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid PIN1 arguments"); - - if (!data->pin2.data && data->pin2.len) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid PIN2 arguments"); - - rv = iasecc_pin_verify(card, data->pin_type, reference, data->pin1.data, data->pin1.len, tries_left); - sc_log(ctx, "iasecc_pin_cmd(SC_PIN_CMD_CHANGE) pin_verify returned %i", rv); - LOG_TEST_RET(ctx, rv, "PIN verification error"); + /* + * Verify the original PIN. This would normally not be needed since it is implicitly done + * by the card when executing a PIN change command. But we must go through our verification + * function in order to handle secure messaging setup, if enabled for the PIN. The + * verification is skipped for PIN-pads (which do not work with SM anyway), to avoid the + * user having to enter the PIN twice. + */ + pin_cmd = *data; + pin_cmd.cmd = SC_PIN_CMD_VERIFY; - if ((unsigned)(data->pin1.len + data->pin2.len) > sizeof(pin_data)) - LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Buffer too small for the 'Change PIN' data"); + rv = iasecc_pin_merge_policy(card, &pin_cmd, &pin_cmd.pin1, &policy); + LOG_TEST_RET(ctx, rv, "Failed to update PIN1 info"); - if (data->pin1.data) - memcpy(pin_data, data->pin1.data, data->pin1.len); - if (data->pin2.data) - memcpy(pin_data + data->pin1.len, data->pin2.data, data->pin2.len); + if (!(pin_cmd.flags & SC_PIN_CMD_USE_PINPAD)) { + rv = iasecc_chv_verify(card, &pin_cmd, policy.scbs); + LOG_TEST_RET(ctx, rv, "PIN CHV verification error"); + } - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0, reference); - apdu.data = pin_data; - apdu.datalen = data->pin1.len + data->pin2.len; - apdu.lc = apdu.datalen; + /* + * To keep things simple, assume that we can use the same PIN parameters for the new PIN as + * for the old one, ignoring the ones specified by the caller, with the exception of the + * PIN prompt and the PIN data itself. Note that the old PIN is re-verified since the + * IAS-ECC specification has no implicit verification for the PIN change command. This also + * forces us to always use PIN-pad for the second PIN if the first one was input on a + * PIN-pad. + */ + pin_cmd.cmd = SC_PIN_CMD_CHANGE; + pin_cmd.pin2 = pin_cmd.pin1; + pin_cmd.pin2.prompt = data->pin2.prompt; + if (pin_cmd.flags & SC_PIN_CMD_USE_PINPAD) { + pin_cmd.pin2.data = NULL; + pin_cmd.pin2.len = 0; + } else { + pin_cmd.pin2.data = data->pin2.data; + pin_cmd.pin2.len = data->pin2.len; + } - rv = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(ctx, rv, "APDU transmit failed"); - rv = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(ctx, rv, "PIN cmd failed"); + rv = iasecc_check_update_pin(&pin_cmd, &pin_cmd.pin2); + LOG_TEST_RET(ctx, rv, "Invalid PIN2"); + rv = iso_ops->pin_cmd(card, &pin_cmd); LOG_FUNC_RETURN(ctx, rv); } +/* + * The PIN unblock function can handle different PIN-pad input combinations for the PUK and the new + * PIN: + * PUK: NEW PIN: DESCRIPTION: + * Available Available No input. + * Available Absent Only new PIN is input. + * Absent Available Only PUK is input. + * Absent Absent Both PUK and new PIN are input. + */ static int -iasecc_pin_reset(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +iasecc_pin_reset(struct sc_card *card, struct sc_pin_cmd_data *data) { struct sc_context *ctx = card->ctx; - struct sc_file *save_current = NULL; - struct iasecc_sdo sdo; - struct sc_apdu apdu; - unsigned reference, scb; + unsigned char scb; + struct sc_pin_cmd_data pin_cmd; + struct iasecc_pin_policy policy; int rv; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "Reset PIN(ref:%i,lengths:%i/%i)", data->pin_reference, data->pin1.len, data->pin2.len); + sc_log(ctx, "Reset PIN(ref:%i,lengths:%zu/%zu)", data->pin_reference, data->pin1.len, data->pin2.len); if (data->pin_type != SC_AC_CHV) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unblock procedure can be used only with the PINs of type CHV"); - reference = data->pin_reference; - - if (!(data->pin_reference & IASECC_OBJECT_REF_LOCAL) && card->cache.valid && card->cache.current_df) { - struct sc_path path; - - sc_file_dup(&save_current, card->cache.current_df); - if (save_current == NULL) - LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current DF file"); - - sc_format_path("3F00", &path); - path.type = SC_PATH_TYPE_FILE_ID; - rv = iasecc_select_file(card, &path, NULL); - LOG_TEST_RET(ctx, rv, "Unable to select MF"); - } - - memset(&sdo, 0, sizeof(sdo)); - sdo.sdo_class = IASECC_SDO_CLASS_CHV; - sdo.sdo_ref = reference & ~IASECC_OBJECT_REF_LOCAL; - - rv = iasecc_sdo_get_data(card, &sdo); - LOG_TEST_RET(ctx, rv, "Cannot get PIN data"); - - if (sdo.docp.acls_contact.size == 0) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Extremely strange ... there are no ACLs"); + rv = iasecc_pin_get_policy(card, data, &policy); + LOG_TEST_RET(ctx, rv, "Failed to get PIN policy"); - scb = sdo.docp.scbs[IASECC_ACLS_CHV_RESET]; + scb = policy.scbs[IASECC_ACLS_CHV_RESET]; do { unsigned need_all = scb & IASECC_SCB_METHOD_NEED_ALL ? 1 : 0; unsigned char se_num = scb & IASECC_SCB_METHOD_MASK_REF; if (scb & IASECC_SCB_METHOD_USER_AUTH) { - sc_log(ctx, "Verify PIN in SE %X", se_num); - rv = iasecc_pin_verify(card, SC_AC_SEN, se_num, data->pin1.data, data->pin1.len, tries_left); + pin_cmd = *data; + if (pin_cmd.puk_reference) { + sc_log(ctx, "Verify PIN with CHV %X", pin_cmd.puk_reference); + pin_cmd.pin_type = SC_AC_CHV; + pin_cmd.pin_reference = pin_cmd.puk_reference; + } else { + sc_log(ctx, "Verify PIN in SE %X", se_num); + pin_cmd.pin_type = SC_AC_SEN; + pin_cmd.pin_reference = se_num; + } + rv = iasecc_pin_verify(card, &pin_cmd); LOG_TEST_RET(ctx, rv, "iasecc_pin_reset() verify PUK error"); if (!need_all) @@ -2448,79 +2433,52 @@ iasecc_pin_reset(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_ } if (scb & IASECC_SCB_METHOD_EXT_AUTH) { - rv = iasecc_sm_external_authentication(card, reference, tries_left); + rv = iasecc_sm_external_authentication(card, data->pin_reference, &data->pin1.tries_left); LOG_TEST_RET(ctx, rv, "iasecc_pin_reset() external authentication error"); } } while(0); - iasecc_sdo_free_fields(card, &sdo); - - if (data->pin2.len) { - sc_log(ctx, "Reset PIN %X and set new value", reference); - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2C, 0x02, reference); - apdu.data = data->pin2.data; - apdu.datalen = data->pin2.len; - apdu.lc = apdu.datalen; - - rv = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(ctx, rv, "APDU transmit failed"); - rv = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(ctx, rv, "PIN cmd failed"); - } - else if (data->pin2.data) { - sc_log(ctx, "Reset PIN %X and set new value", reference); - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2C, 3, reference); + /* Use iso 7816 layer for unblock, with implicit pin for PIN1 and the new PIN for PIN2 */ + pin_cmd = *data; + pin_cmd.cmd = SC_PIN_CMD_UNBLOCK; + pin_cmd.flags |= SC_PIN_CMD_IMPLICIT_CHANGE; + pin_cmd.pin1.len = 0; - rv = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(ctx, rv, "APDU transmit failed"); - rv = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(ctx, rv, "PIN cmd failed"); - } - else { - sc_log(ctx, "Reset PIN %X and set new value with PIN-PAD", reference); -#if 0 - rv = iasecc_chv_set_pinpad(card, reference); - LOG_TEST_RET(ctx, rv, "Reset PIN failed"); -#else - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Reset retry counter with PIN PAD not supported "); -#endif - } + rv = iasecc_pin_merge_policy(card, &pin_cmd, &pin_cmd.pin2, &policy); + LOG_TEST_RET(ctx, rv, "Failed to update PIN2 info"); - if (save_current) { - rv = iasecc_select_file(card, &save_current->path, NULL); - LOG_TEST_RET(ctx, rv, "Cannot return to saved PATH"); - } + rv = iso_ops->pin_cmd(card, &pin_cmd); LOG_FUNC_RETURN(ctx, rv); } static int -iasecc_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +iasecc_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data) { struct sc_context *ctx = card->ctx; int rv; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "iasecc_pin_cmd() cmd 0x%X, PIN type 0x%X, PIN reference %i, PIN-1 %p:%i, PIN-2 %p:%i", + sc_log(ctx, "iasecc_pin_cmd() cmd 0x%X, PIN type 0x%X, PIN reference %i, PIN-1 %p:%zu, PIN-2 %p:%zu", data->cmd, data->pin_type, data->pin_reference, data->pin1.data, data->pin1.len, data->pin2.data, data->pin2.len); switch (data->cmd) { case SC_PIN_CMD_VERIFY: - rv = iasecc_pin_verify(card, data->pin_type, data->pin_reference, data->pin1.data, data->pin1.len, tries_left); + rv = iasecc_pin_verify(card, data); break; case SC_PIN_CMD_CHANGE: if (data->pin_type == SC_AC_AUT) - rv = iasecc_keyset_change(card, data, tries_left); + rv = iasecc_keyset_change(card, data); else - rv = iasecc_pin_change(card, data, tries_left); + rv = iasecc_pin_change(card, data); break; case SC_PIN_CMD_UNBLOCK: - rv = iasecc_pin_reset(card, data, tries_left); + rv = iasecc_pin_reset(card, data); break; case SC_PIN_CMD_GET_INFO: - rv = iasecc_pin_get_policy(card, data); + rv = iasecc_pin_get_info(card, data); break; default: sc_log(ctx, "Other pin commands not supported yet: 0x%X", data->cmd); @@ -2538,7 +2496,7 @@ iasecc_get_serialnr(struct sc_card *card, struct sc_serial_number *serial) struct sc_iin *iin = &card->serialnr.iin; struct sc_apdu apdu; unsigned char rbuf[0xC0]; - size_t ii, offs; + size_t ii, offs, len; int rv; LOG_FUNC_CALLED(ctx); @@ -2557,8 +2515,9 @@ iasecc_get_serialnr(struct sc_card *card, struct sc_serial_number *serial) rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "Get 'serial number' data failed"); - if (rbuf[0] != ISO7812_PAN_SN_TAG) + if (apdu.resplen < 2 || rbuf[0] != ISO7812_PAN_SN_TAG || rbuf[1] > (apdu.resplen-2)) LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "serial number parse error"); + len = rbuf[1]; iin->mii = (rbuf[2] >> 4) & 0x0F; @@ -2574,17 +2533,18 @@ iasecc_get_serialnr(struct sc_card *card, struct sc_serial_number *serial) iin->issuer_id += (rbuf[ii/2] >> (ii & 0x01 ? 0 : 4)) & 0x0F; } - offs = rbuf[1] > 8 ? rbuf[1] - 8 : 0; + /* Copy the serial number from the last 8 bytes (at most) */ + offs = len > 8 ? len - 8 : 0; if (card->type == SC_CARD_TYPE_IASECC_SAGEM) { /* 5A 0A 92 50 00 20 10 10 25 00 01 3F */ /* 00 02 01 01 02 50 00 13 */ - for (ii=0; ii < rbuf[1] - offs; ii++) + for (ii=0; ii < len - offs; ii++) *(card->serialnr.value + ii) = ((rbuf[ii + offs + 1] & 0x0F) << 4) + ((rbuf[ii + offs + 2] & 0xF0) >> 4) ; card->serialnr.len = ii; } else { - for (ii=0; ii < rbuf[1] - offs; ii++) + for (ii=0; ii < len - offs; ii++) *(card->serialnr.value + ii) = rbuf[ii + offs + 2]; card->serialnr.len = ii; } @@ -2846,6 +2806,34 @@ iasecc_sdo_get_tagged_data(struct sc_card *card, int sdo_tag, struct iasecc_sdo LOG_FUNC_CALLED(ctx); + sc_log(ctx, "sdo_tag=0x%x sdo_ref=0x%x sdo_class=0x%x", sdo_tag, + sdo->sdo_ref, sdo->sdo_class); + + /* XXX: for the CPx, the SDO are available from some specific path */ + if (iasecc_is_cpx(card)) { + struct sc_path path; + char *path_str = NULL; + switch(sdo_tag) { + case IASECC_SDO_PRVKEY_TAG: + /* APDU 00 CB 3F FF 0B 4D 09 70 07 BF 90 02 03 7F 48 80 */ + path_str = "3F00:0001"; + break; + case IASECC_SDO_CHV_TAG: + /* APDU 00 CB 3F FF 0B 4D 09 70 07 BF 81 01 03 7F 41 80 */ + path_str = "3F00"; + break; + default: + path_str = NULL; + break; + } + if (path_str) { + sc_log(ctx, "Warning: Enforce the path=%s", path_str); + sc_format_path(path_str, &path); + rv = iasecc_select_file(card, &path, NULL); + LOG_TEST_RET(ctx, rv, "path error"); + } + } + sbuf[offs--] = 0x80; sbuf[offs--] = sdo_tag & 0xFF; if ((sdo_tag >> 8) & 0xFF) @@ -3093,7 +3081,7 @@ iasecc_decipher(struct sc_card *card, out_len = apdu.resplen; memcpy(out, apdu.resp, out_len); - rv = out_len; + rv = (int)out_len; LOG_FUNC_RETURN(ctx, rv); } @@ -3103,10 +3091,14 @@ static int iasecc_qsign_data_sha1(struct sc_context *ctx, const unsigned char *in, size_t in_len, struct iasecc_qsign_data *out) { - SHA_CTX sha; - SHA_LONG pre_hash_Nl, *hh[5] = { - &sha.h0, &sha.h1, &sha.h2, &sha.h3, &sha.h4 - }; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + + int r = SC_ERROR_INTERNAL; + EVP_MD_CTX *mdctx = NULL; + EVP_MD *md = NULL; + SHA_CTX *md_data = NULL; + unsigned int md_out_len; + SHA_LONG pre_hash_Nl, *hh[5] = {NULL, NULL, NULL, NULL, NULL}; int jj, ii; int hh_size = sizeof(SHA_LONG), hh_num = SHA_DIGEST_LENGTH / sizeof(SHA_LONG); @@ -3120,8 +3112,33 @@ iasecc_qsign_data_sha1(struct sc_context *ctx, const unsigned char *in, size_t i in_len); memset(out, 0, sizeof(struct iasecc_qsign_data)); - SHA1_Init(&sha); - SHA1_Update(&sha, in, in_len); + md = sc_evp_md(ctx, "SHA1"); + mdctx = EVP_MD_CTX_new(); + if (EVP_DigestInit_ex(mdctx, md, NULL) != 1) { + sc_log_openssl(ctx); + sc_log(ctx, "EVP_DigestInit_ex failed"); + goto end; + } + + md_data = EVP_MD_CTX_md_data(mdctx); + if (md_data == NULL) { + sc_log_openssl(ctx); + sc_log(ctx, "Failed to find md_data"); + r = SC_ERROR_NOT_SUPPORTED; + goto end; + } + + if (EVP_DigestUpdate(mdctx, in, in_len) != 1) { + sc_log_openssl(ctx); + sc_log(ctx, "EVP_DigestUpdate failed"); + goto end; + } + + hh[0] = &md_data->h0; + hh[1] = &md_data->h1; + hh[2] = &md_data->h2; + hh[3] = &md_data->h3; + hh[4] = &md_data->h4; for (jj=0; jjpre_hash_size = SHA_DIGEST_LENGTH; sc_log(ctx, "Pre SHA1:%s", sc_dump_hex(out->pre_hash, out->pre_hash_size)); - pre_hash_Nl = sha.Nl - (sha.Nl % (sizeof(sha.data) * 8)); + pre_hash_Nl = md_data->Nl - (md_data->Nl % (sizeof(md_data->data) *8)); for (ii=0; iicounter[ii] = (sha.Nh >> 8*(hh_size-1-ii)) &0xFF; + out->counter[ii] = (md_data->Nh >> 8*(hh_size-1-ii)) &0xFF; out->counter[hh_size+ii] = (pre_hash_Nl >> 8*(hh_size-1-ii)) &0xFF; } for (ii=0, out->counter_long=0; ii<(int)sizeof(out->counter); ii++) out->counter_long = out->counter_long*0x100 + out->counter[ii]; sc_log(ctx, "Pre counter(%li):%s", out->counter_long, sc_dump_hex(out->counter, sizeof(out->counter))); - if (sha.num) { - memcpy(out->last_block, in + in_len - sha.num, sha.num); - out->last_block_size = sha.num; + if (md_data->num) { + memcpy(out->last_block, in + in_len - md_data->num, md_data->num); + out->last_block_size = md_data->num; sc_log(ctx, "Last block(%"SC_FORMAT_LEN_SIZE_T"u):%s", out->last_block_size, sc_dump_hex(out->last_block, out->last_block_size)); } - SHA1_Final(out->hash, &sha); + if (EVP_DigestFinal_ex(mdctx, out->hash, &md_out_len) != 1) { + sc_log_openssl(ctx); + sc_log(ctx, "EVP_DigestFinal_ex failed"); + goto end; + } + out->hash_size = SHA_DIGEST_LENGTH; sc_log(ctx, "Expected digest %s\n", sc_dump_hex(out->hash, out->hash_size)); - LOG_FUNC_RETURN(ctx, SC_SUCCESS); + r = SC_SUCCESS; + +end: + EVP_MD_CTX_free(mdctx); + sc_evp_md_free(md); + + LOG_FUNC_RETURN(ctx, r); + +#else /* OPENSSL_VERSION_NUMBER < 0x30000000L */ + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); +#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */ } -#if OPENSSL_VERSION_NUMBER >= 0x00908000L static int iasecc_qsign_data_sha256(struct sc_context *ctx, const unsigned char *in, size_t in_len, struct iasecc_qsign_data *out) { - SHA256_CTX sha256; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + + int r = SC_ERROR_INTERNAL; + EVP_MD_CTX *mdctx = NULL; + EVP_MD *md = NULL; + SHA256_CTX *md_data; + unsigned int md_out_len; + SHA_LONG pre_hash_Nl; int jj, ii; int hh_size = sizeof(SHA_LONG), hh_num = SHA256_DIGEST_LENGTH / sizeof(SHA_LONG); @@ -3173,39 +3211,72 @@ iasecc_qsign_data_sha256(struct sc_context *ctx, const unsigned char *in, size_t in_len); memset(out, 0, sizeof(struct iasecc_qsign_data)); - SHA256_Init(&sha256); - SHA256_Update(&sha256, in, in_len); + md = sc_evp_md(ctx, "SHA256"); + mdctx = EVP_MD_CTX_new(); + if (EVP_DigestInit_ex(mdctx, md, NULL) != 1) { + sc_log_openssl(ctx); + sc_log(ctx, "EVP_DigestInit_ex failed"); + goto end; + } + + md_data = EVP_MD_CTX_md_data(mdctx); + if (md_data == NULL) { + sc_log_openssl(ctx); + sc_log(ctx, "Failed to find md_data"); + r = SC_ERROR_NOT_SUPPORTED; + goto end; + } + + if (EVP_DigestUpdate(mdctx, in, in_len) != 1) { + sc_log_openssl(ctx); + sc_log(ctx, "EVP_DigestUpdate failed"); + goto end; + } for (jj=0; jjpre_hash[jj*hh_size + ii] = ((sha256.h[jj] >> 8*(hh_size-1-ii)) & 0xFF); + out->pre_hash[jj*hh_size + ii] = ((md_data->h[jj] >> 8*(hh_size-1-ii)) & 0xFF); out->pre_hash_size = SHA256_DIGEST_LENGTH; sc_log(ctx, "Pre hash:%s", sc_dump_hex(out->pre_hash, out->pre_hash_size)); - pre_hash_Nl = sha256.Nl - (sha256.Nl % (sizeof(sha256.data) * 8)); + pre_hash_Nl = md_data->Nl - (md_data->Nl % (sizeof(md_data->data) * 8)); for (ii=0; iicounter[ii] = (sha256.Nh >> 8*(hh_size-1-ii)) &0xFF; + out->counter[ii] = (md_data->Nh >> 8*(hh_size-1-ii)) &0xFF; out->counter[hh_size+ii] = (pre_hash_Nl >> 8*(hh_size-1-ii)) &0xFF; } for (ii=0, out->counter_long=0; ii<(int)sizeof(out->counter); ii++) out->counter_long = out->counter_long*0x100 + out->counter[ii]; sc_log(ctx, "Pre counter(%li):%s", out->counter_long, sc_dump_hex(out->counter, sizeof(out->counter))); - if (sha256.num) { - memcpy(out->last_block, in + in_len - sha256.num, sha256.num); - out->last_block_size = sha256.num; + if (md_data->num) { + memcpy(out->last_block, in + in_len - md_data->num, md_data->num); + out->last_block_size = md_data->num; sc_log(ctx, "Last block(%"SC_FORMAT_LEN_SIZE_T"u):%s", out->last_block_size, sc_dump_hex(out->last_block, out->last_block_size)); } - SHA256_Final(out->hash, &sha256); + if (EVP_DigestFinal_ex(mdctx, out->hash, &md_out_len) != 1) { + sc_log_openssl(ctx); + sc_log(ctx, "EVP_DigestFinal_ex failed"); + goto end; + } + out->hash_size = SHA256_DIGEST_LENGTH; sc_log(ctx, "Expected digest %s\n", sc_dump_hex(out->hash, out->hash_size)); - LOG_FUNC_RETURN(ctx, SC_SUCCESS); + r = SC_SUCCESS; + +end: + EVP_MD_CTX_free(mdctx); + sc_evp_md_free(md); + + LOG_FUNC_RETURN(ctx, r); + +#else /* OPENSSL_VERSION_NUMBER < 0x30000000L */ + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); +#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */ } -#endif static int @@ -3213,7 +3284,7 @@ iasecc_compute_signature_dst(struct sc_card *card, const unsigned char *in, size_t in_len, unsigned char *out, size_t out_len) { struct sc_context *ctx = card->ctx; - struct iasecc_private_data *prv = (struct iasecc_private_data *) card->drv_data; + struct iasecc_private_data *prv = (struct iasecc_private_data *)card->drv_data; struct sc_security_env *env = &prv->security_env; struct iasecc_qsign_data qsign_data; struct sc_apdu apdu; @@ -3230,17 +3301,15 @@ iasecc_compute_signature_dst(struct sc_card *card, LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "It's not SC_SEC_OPERATION_SIGN"); else if (!(prv->key_size & 0x1E0) || (prv->key_size & ~0x1E0)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid key size for SC_SEC_OPERATION_SIGN"); + if (sizeof(rbuf) < prv->key_size) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); memset(&qsign_data, 0, sizeof(qsign_data)); if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) { rv = iasecc_qsign_data_sha1(card->ctx, in, in_len, &qsign_data); } else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA256) { -#if OPENSSL_VERSION_NUMBER >= 0x00908000L rv = iasecc_qsign_data_sha256(card->ctx, in, in_len, &qsign_data); -#else - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "SHA256 is not supported by OpenSSL previous to v0.9.8"); -#endif } else LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Need RSA_HASH_SHA1 or RSA_HASH_SHA256 algorithm"); @@ -3304,7 +3373,7 @@ iasecc_compute_signature_dst(struct sc_card *card, memcpy(out, apdu.resp, apdu.resplen); - LOG_FUNC_RETURN(ctx, apdu.resplen); + LOG_FUNC_RETURN(ctx, (int)apdu.resplen); } @@ -3313,7 +3382,7 @@ iasecc_compute_signature_at(struct sc_card *card, const unsigned char *in, size_t in_len, unsigned char *out, size_t out_len) { struct sc_context *ctx = card->ctx; - struct iasecc_private_data *prv = (struct iasecc_private_data *) card->drv_data; + struct iasecc_private_data *prv = (struct iasecc_private_data *)card->drv_data; struct sc_security_env *env = &prv->security_env; struct sc_apdu apdu; size_t offs = 0, sz = 0; @@ -3360,7 +3429,7 @@ iasecc_compute_signature_at(struct sc_card *card, } while(rv > 0); - LOG_FUNC_RETURN(ctx, offs); + LOG_FUNC_RETURN(ctx, (int)offs); } @@ -3376,7 +3445,7 @@ iasecc_compute_signature(struct sc_card *card, return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; - prv = (struct iasecc_private_data *) card->drv_data; + prv = (struct iasecc_private_data *)card->drv_data; env = &prv->security_env; LOG_FUNC_CALLED(ctx); @@ -3410,12 +3479,13 @@ iasecc_read_public_key(struct sc_card *card, unsigned type, sc_log(ctx, "read public kay(ref:%i;size:%i)", ref, size); + memset(&bn, 0, sizeof bn); memset(&sdo, 0, sizeof(sdo)); sdo.sdo_class = IASECC_SDO_CLASS_RSA_PUBLIC; sdo.sdo_ref = ref & ~IASECC_OBJECT_REF_LOCAL; rv = iasecc_sdo_get_data(card, &sdo); - LOG_TEST_RET(ctx, rv, "failed to read public key: cannot get RSA SDO data"); + LOG_TEST_GOTO_ERR(ctx, rv, "failed to read public key: cannot get RSA SDO data"); if (out) *out = NULL; @@ -3424,13 +3494,13 @@ iasecc_read_public_key(struct sc_card *card, unsigned type, bn[0].data = (unsigned char *) malloc(sdo.data.pub_key.n.size); if (!bn[0].data) - LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "failed to read public key: cannot allocate modulus"); + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_OUT_OF_MEMORY, "failed to read public key: cannot allocate modulus"); bn[0].len = sdo.data.pub_key.n.size; memcpy(bn[0].data, sdo.data.pub_key.n.value, sdo.data.pub_key.n.size); bn[1].data = (unsigned char *) malloc(sdo.data.pub_key.e.size); if (!bn[1].data) - LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "failed to read public key: cannot allocate exponent"); + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_OUT_OF_MEMORY, "failed to read public key: cannot allocate exponent"); bn[1].len = sdo.data.pub_key.e.size; memcpy(bn[1].data, sdo.data.pub_key.e.value, sdo.data.pub_key.e.size); @@ -3438,11 +3508,12 @@ iasecc_read_public_key(struct sc_card *card, unsigned type, rsa_key.exponent = bn[1]; rv = sc_pkcs15_encode_pubkey_rsa(ctx, &rsa_key, out, out_len); - LOG_TEST_RET(ctx, rv, "failed to read public key: cannot encode RSA public key"); + LOG_TEST_GOTO_ERR(ctx, rv, "failed to read public key: cannot encode RSA public key"); if (out && out_len) sc_log(ctx, "encoded public key: %s", sc_dump_hex(*out, *out_len)); +err: if (bn[0].data) free(bn[0].data); if (bn[1].data) @@ -3486,10 +3557,15 @@ iasecc_get_free_reference(struct sc_card *card, struct iasecc_ctl_get_free_refer sc_log(ctx, "found empty key slot %i", idx); break; + } else if (rv != SC_SUCCESS) { + iasecc_sdo_free(card, sdo); + + sc_log(ctx, "get new key reference failed"); + LOG_FUNC_RETURN(ctx, rv); } - else - LOG_TEST_RET(ctx, rv, "get new key reference failed"); + if (sdo->docp.size.size < 2) + continue; sz = *(sdo->docp.size.value + 0) * 0x100 + *(sdo->docp.size.value + 1); sc_log(ctx, "SDO(idx:%i) size %"SC_FORMAT_LEN_SIZE_T"u; key_size %"SC_FORMAT_LEN_SIZE_T"u", @@ -3584,7 +3660,7 @@ sc_get_driver(void) /* update_record: Untested */ iasecc_ops.select_file = iasecc_select_file; /* get_response: Untested */ - /* get_challenge: ISO7816 implementation works */ + iasecc_ops.get_challenge = iasecc_get_challenge; iasecc_ops.logout = iasecc_logout; /* restore_security_env */ iasecc_ops.set_security_env = iasecc_set_security_env; diff --git a/src/libopensc/card-idprime.c b/src/libopensc/card-idprime.c new file mode 100644 index 0000000000..6c3e079a3e --- /dev/null +++ b/src/libopensc/card-idprime.c @@ -0,0 +1,1281 @@ +/* + * card-idprime.c: Support for Gemalto IDPrime smart cards + * + * Copyright (c) 2019 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "internal.h" +#include +#include +#include + +#include "cardctl.h" +#include "pkcs15.h" + +static const struct sc_card_operations *iso_ops = NULL; + +static struct sc_card_operations idprime_ops; +static struct sc_card_driver idprime_drv = { + "Gemalto IDPrime", + "idprime", + &idprime_ops, + NULL, 0, NULL +}; + +/* This ATR says, there is no EF.DIR nor EF.ATR so ISO discovery mechanisms + * are not useful here */ +// clang-format off +static const struct sc_atr_table idprime_atrs[] = { + /* known ATRs for IDPrime 3810: + * 3b:7f:96:00:00:80:31:80:65:b0:84:41:3d:f6:12:0f:fe:82:90:00 Jakuje/xhanulik + */ + { "3b:7f:96:00:00:80:31:80:65:b0:84:41:3d:f6:12:0f:fe:82:90:00", + "ff:ff:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:ff:ff:ff", + "Gemalto IDPrime 3810", + SC_CARD_TYPE_IDPRIME_3810, 0, NULL }, + /* known ATRs for IDPrime 930: + * 3b:7f:96:00:00:80:31:80:65:b0:84:56:51:10:12:0f:fe:82:90:00 Jakuje/xhanulik + */ + { "3b:7f:96:00:00:80:31:80:65:b0:84:56:51:10:12:0f:fe:82:90:00", + "ff:ff:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:ff:ff:ff", + "Gemalto IDPrime 830", + SC_CARD_TYPE_IDPRIME_830, 0, NULL }, + /* known ATRs for IDPrime 930: + * 3b:7f:96:00:00:80:31:80:65:b0:84:61:60:fb:12:0f:fd:82:90:00 Jakuje/xhanulik + */ + { "3b:7f:96:00:00:80:31:80:65:b0:84:61:60:fb:12:0f:fe:82:90:00", + "ff:ff:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:ff:ff:ff", + "Gemalto IDPrime 930/3930", + SC_CARD_TYPE_IDPRIME_930, 0, NULL }, + /* known ATRs: + * 3b:ff:96:00:00:81:31:fe:43:80:31:80:65:b0:84:65:66:fb:12:01:78:82:90:00:85 metsma + */ + { "3b:ff:96:00:00:81:31:fe:43:80:31:80:65:b0:84:65:66:fb:12:01:78:82:90:00:85", + "ff:ff:00:ff:ff:ff:ff:00:ff:ff:ff:ff:ff:ff:ff:00:00:00:ff:ff:ff:ff:ff:ff:00", + "based Gemalto IDPrime 930 (eToken 5110+ FIPS)", + SC_CARD_TYPE_IDPRIME_930_PLUS, 0, NULL }, + /* known ATR for IDPrime 940: Placing in front of the 940 as its mask overlaps this one! + * 3b:7f:96:00:00:80:31:80:65:b0:85:03:00:ef:12:0f:fe:82:90:00 msetina + */ + { "3b:7f:96:00:00:80:31:80:65:b0:85:03:00:ef:12:0f:fe:82:90:00", + "ff:ff:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:ff:ff:ff", + "Gemalto IDPrime 840", + SC_CARD_TYPE_IDPRIME_840, 0, NULL }, + /* known ATR for IDPrime 940: + * 3b:7f:96:00:00:80:31:80:65:b0:85:59:56:fb:12:0f:fe:82:90:00 Jakuje/xhanulik, msetina, kirichkov + */ + { "3b:7f:96:00:00:80:31:80:65:b0:85:59:56:fb:12:0f:fe:82:90:00", + "ff:ff:00:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:ff:00:00:ff:ff:ff", + "Gemalto IDPrime 940", + SC_CARD_TYPE_IDPRIME_940, 0, NULL }, + /* Known ATRs: + * 3b:7f:96:00:00:80:31:80:65:b0:85:05:00:39:12:0f:fe:82:90:00 vbonamy + */ + { "3b:7f:96:00:00:80:31:80:65:b0:85:05:00:39:12:0f:fe:82:90:00", + "ff:ff:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:ff:ff:ff", + "Gemalto IDPrime 940C", + SC_CARD_TYPE_IDPRIME_940, 0, NULL }, + /* Known ATRs for IDPrime 940 (eToken 5110) + * 3b:ff:96:00:00:81:31:fe:43:80:31:80:65:b0:85:59:56:fb:12:0f:fe:82:90:00:00 metsma, jurajsarinay + * 3b:ff:96:00:00:81:31:fe:43:80:31:80:65:b0:85:59:56:fb:12:01:78:82:90:00:88 hardening + */ + { "3b:ff:96:00:00:81:31:fe:43:80:31:80:65:b0:85:59:56:fb:12:0f:fe:82:90:00:00", + "ff:ff:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:ff:f0:00:ff:ff:ff:00", + "Gemalto IDPrime MD 940 (eToken 5110)", + SC_CARD_TYPE_IDPRIME_940, 0, NULL }, + { "3b:7f:96:00:00:80:31:80:65:b0:84:41:3d:f6:12:0f:fe:82:90:00", + "ff:ff:00:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:ff:00:00:ff:ff:ff", + "Gemalto IDPrime MD 8840, 3840, 3810, 840, 830 and MD 940 Cards", + SC_CARD_TYPE_IDPRIME_GENERIC, 0, NULL }, + /* Known ATRs: Overlaps partially with 930 and 940 + * 3b:ff:96:00:00:81:31:80:43:80:31:80:65:b0:85:03:00:ef:12:0f:fe:82:90:00:66 metsma + */ + { "3b:ff:96:00:00:81:31:80:43:80:31:80:65:b0:85:03:00:ef:12:0f:fe:82:90:00:66", + "ff:ff:00:ff:ff:ff:ff:00:ff:ff:ff:ff:ff:ff:00:00:00:00:ff:ff:ff:ff:ff:ff:00", + "Gemalto IDPrime MD 8840, 3840, 3810, 840 and 830 Cards (eToken)", + SC_CARD_TYPE_IDPRIME_GENERIC, 0, NULL }, + { NULL, NULL, NULL, 0, 0, NULL } +}; +// clang-format on + +static const sc_path_t idprime_path = { + "", 0, + 0, 0, SC_PATH_TYPE_DF_NAME, + { "\xA0\x00\x00\x00\x18\x80\x00\x00\x00\x06\x62", 11 } +}; + +/* data structures to store meta data about IDPrime objects */ +typedef struct idprime_object { + int fd; + int key_reference; + int valid_key_ref; + u8 df[2]; + unsigned short length; + int pin_index; +} idprime_object_t; + +/* + * IDPrime Container structure + * Simplification of auxiliary data from aux-data.c + */ +#define MAX_CONTAINER_NAME_LEN 39 +#define CONTAINER_OBJ_LEN 86 +typedef struct idprime_container { + uint8_t index; /* Index of the container */ + char guid[MAX_CONTAINER_NAME_LEN + 1]; /* Container name */ +} idprime_container_t; + +/* + * IDPrime key reference structure + */ +#define KEYREF_OBJ_LEN 8 +typedef struct idprime_keyref { + uint8_t index; /* Index of the key reference */ + uint8_t pin_index; /* Index of the auth pin used for accessing key */ + int key_reference; /* Key reference used for accessing key */ +} idprime_keyref_t; + +/* + * IDPrime private data per card state + */ +typedef struct idprime_private_data { + u8 *cache_buf; /* cached version of the currently selected file */ + size_t cache_buf_len; /* length of the cached selected file */ + int cached; /* is the cached selected file valid */ + size_t file_size; /* this is real file size since IDPrime is quite strict about lengths */ + list_t pki_list; /* list of pki containers */ + idprime_object_t *pki_current; /* current pki object _ctl function */ + int tinfo_present; /* Token Info Label object is present*/ + u8 tinfo_df[2]; /* DF of object with Token Info Label */ + unsigned long current_op; /* current operation set by idprime_set_security_env */ + list_t containers; /* list of private key containers */ + list_t keyrefmap; /* list of key references for private keys */ +} idprime_private_data_t; + +/* For SimCList autocopy, we need to know the size of the data elements */ +static size_t idprime_list_meter(const void *el) { + return sizeof(idprime_object_t); +} + +static size_t idprime_container_list_meter(const void *el) { + return sizeof(idprime_container_t); +} + +static size_t idprime_keyref_list_meter(const void *el) { + return sizeof(idprime_keyref_t); +} + +static int idprime_add_container_to_list(list_t *list, const idprime_container_t *container) +{ + if (list_append(list, container) < 0) + return SC_ERROR_INTERNAL; + return SC_SUCCESS; +} + +static int idprime_container_list_seeker(const void *el, const void *key) +{ + const idprime_container_t *container = (idprime_container_t *)el; + + if ((el == NULL) || (key == NULL)) + return 0; + if (container->index == *(uint8_t *)key) + return 1; + return 0; +} + +static int idprime_add_keyref_to_list(list_t *list, const idprime_keyref_t *keyref) +{ + if (list_append(list, keyref) < 0) + return SC_ERROR_INTERNAL; + return SC_SUCCESS; +} + +static int idprime_keyref_list_seeker(const void *el, const void *key) +{ + const idprime_keyref_t *keyref = (idprime_keyref_t *)el; + + if ((el == NULL) || (key == NULL)) + return 0; + if (keyref->index == *(uint8_t *)key) + return 1; + return 0; +} + +void idprime_free_private_data(idprime_private_data_t *priv) +{ + free(priv->cache_buf); + list_destroy(&priv->pki_list); + list_destroy(&priv->containers); + list_destroy(&priv->keyrefmap); + free(priv); + return; +} + +idprime_private_data_t *idprime_new_private_data(void) +{ + idprime_private_data_t *priv; + + priv = calloc(1, sizeof(idprime_private_data_t)); + if (priv == NULL) + return NULL; + + /* Initialize PKI Applets list */ + if (list_init(&priv->pki_list) != 0 || + list_attributes_copy(&priv->pki_list, idprime_list_meter, 1) != 0) { + idprime_free_private_data(priv); + return NULL; + } + + /* Initialize container list */ + if (list_init(&priv->containers) != 0 || + list_attributes_copy(&priv->containers, idprime_container_list_meter, 1) != 0 || + list_attributes_seeker(&priv->containers, idprime_container_list_seeker) != 0) { + idprime_free_private_data(priv); + return NULL; + } + + /* Initialize keyref list */ + if (list_init(&priv->keyrefmap) != 0 || + list_attributes_copy(&priv->keyrefmap, idprime_keyref_list_meter, 1) != 0 || + list_attributes_seeker(&priv->keyrefmap, idprime_keyref_list_seeker) != 0) { + idprime_free_private_data(priv); + return NULL; + } + return priv; +} + +int idprime_add_object_to_list(list_t *list, const idprime_object_t *object) +{ + if (list_append(list, object) < 0) + return SC_ERROR_INTERNAL; + return SC_SUCCESS; +} + +/* This selects main IDPrime AID which is used for communication with + * the card */ +static int idprime_select_idprime(sc_card_t *card) +{ + return iso_ops->select_file(card, &idprime_path, NULL); +} + +/* Select file by string path */ +static int idprime_select_file_by_path(sc_card_t *card, const char *str_path) +{ + int r; + sc_file_t *file = NULL; + sc_path_t index_path; + + /* First, we need to make sure the IDPrime AID is selected */ + r = idprime_select_idprime(card); + if (r != SC_SUCCESS) { + LOG_FUNC_RETURN(card->ctx, r); + } + + /* Returns FCI with expected length of data */ + sc_format_path(str_path, &index_path); + r = iso_ops->select_file(card, &index_path, &file); + + if (r != SC_SUCCESS) { + LOG_FUNC_RETURN(card->ctx, r); + } + /* Ignore too large files */ + if (file->size > MAX_FILE_SIZE) { + r = SC_ERROR_INVALID_DATA; + } else { + r = (int)file->size; + } + sc_file_free(file); + LOG_FUNC_RETURN(card->ctx, r); +} + +static int idprime_process_containermap(sc_card_t *card, idprime_private_data_t *priv, int length) +{ + u8 *buf = NULL; + int r = SC_ERROR_OUT_OF_MEMORY; + int i; + uint8_t max_entries, container_index; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + buf = malloc(length); + if (buf == NULL) { + goto done; + } + + r = 0; + do { + /* Read at most CONTAINER_OBJ_LEN bytes */ + int read_length = length - r > CONTAINER_OBJ_LEN ? CONTAINER_OBJ_LEN : length - r; + if (length == r) { + r = SC_ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + const int got = iso_ops->read_binary(card, r, buf + r, read_length, 0); + if (got < 1) { + r = SC_ERROR_WRONG_LENGTH; + goto done; + } + + r += got; + /* Try to read chunks of container size and stop when last container looks empty */ + container_index = r > CONTAINER_OBJ_LEN ? (r / CONTAINER_OBJ_LEN - 1) * CONTAINER_OBJ_LEN : 0; + } while(length - r > 0 && buf[container_index] != 0); + max_entries = r / CONTAINER_OBJ_LEN; + + for (i = 0; i < max_entries; i++) { + u8 *start = &buf[i * CONTAINER_OBJ_LEN]; + idprime_container_t new_container = {0}; + if (start[0] == 0) /* Empty record */ + break; + + new_container.index = i; + /* Reading UNICODE characters but skipping second byte */ + int j = 0; + for (j = 0; j < MAX_CONTAINER_NAME_LEN; j++) { + if (start[2 * j] == 0) + break; + new_container.guid[j] = start[2 * j]; + } + + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Found container with index=%d, guid=%s", new_container.index, new_container.guid); + + if ((r = idprime_add_container_to_list(&priv->containers, &new_container)) != SC_SUCCESS) { + goto done; + } + } + + r = SC_SUCCESS; +done: + free(buf); + LOG_FUNC_RETURN(card->ctx, r); +} + +static int idprime_process_keyrefmap(sc_card_t *card, idprime_private_data_t *priv, int length) +{ + u8 *buf = NULL; + int r = SC_ERROR_OUT_OF_MEMORY; + int i, max_entries; + + buf = malloc(length); + if (buf == NULL) { + goto done; + } + + r = 0; + do { + if (length == r) { + r = SC_ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + const int got = iso_ops->read_binary(card, r, buf + r, length - r, 0); + if (got < 1) { + r = SC_ERROR_WRONG_LENGTH; + goto done; + } + + r += got; + } while(length - r > 0); + max_entries = r / KEYREF_OBJ_LEN; + + for (i = 0; i < max_entries; i++) { + idprime_keyref_t new_keyref; + u8 *start = &buf[i * KEYREF_OBJ_LEN]; + if (start[0] == 0) /* Empty key ref */ + continue; + + new_keyref.index = start[2]; + new_keyref.key_reference = start[1]; + new_keyref.pin_index = start[7]; + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Found key reference with index=%d, pin=%d, keyref=%d", new_keyref.index, new_keyref.pin_index, new_keyref.key_reference); + + if ((r = idprime_add_keyref_to_list(&priv->keyrefmap, &new_keyref)) != SC_SUCCESS) { + goto done; + } + } + r = SC_SUCCESS; +done: + free(buf); + LOG_FUNC_RETURN(card->ctx, r); +} + +static int idprime_process_index(sc_card_t *card, idprime_private_data_t *priv, int length) +{ + u8 *buf = NULL; + int r = SC_ERROR_OUT_OF_MEMORY; + int i, num_entries; + idprime_object_t new_object; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + buf = malloc(length); + if (buf == NULL) { + goto done; + } + + r = 0; + do { + if (length == r) { + r = SC_ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + const int got = iso_ops->read_binary(card, r, buf + r, length - r, 0); + if (got < 1) { + r = SC_ERROR_WRONG_LENGTH; + goto done; + } + /* First byte shows the number of entries, each of them 21 bytes long */ + num_entries = buf[0]; + r += got; + } while(r < num_entries * 21 + 1); + + new_object.fd = 0; + for (i = 0; i < num_entries; i++) { + u8 *start = &buf[i*21+1]; + + /* First two bytes specify the object DF */ + new_object.df[0] = start[0]; + new_object.df[1] = start[1]; + /* Second two bytes refer to the object size */ + new_object.length = bebytes2ushort(&start[2]); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "df=%s, len=%u", + sc_dump_hex(new_object.df, sizeof(new_object.df)), new_object.length); + /* in minidriver, mscp/kxcNN or kscNN lists certificates */ + if (((memcmp(&start[4], "ksc", 3) == 0) || memcmp(&start[4], "kxc", 3) == 0) + && (memcmp(&start[12], "mscp", 5) == 0)) { + uint8_t cert_id = 0; + idprime_container_t *container = NULL; + + if (start[7] >= '0' && start[7] <= '9' && start[8] >= '0' && start[8] <= '9') { + cert_id = (start[7] - '0') * 10 + start[8] - '0'; + } + new_object.fd++; + new_object.key_reference = -1; + new_object.valid_key_ref = 0; + new_object.pin_index = 1; + + container = (idprime_container_t *) list_seek(&priv->containers, &cert_id); + if (!container) { + /* Container map missing container with certificate ID */ + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "No corresponding container with private key found for certificate with id=%d", cert_id); + if (card->type != SC_CARD_TYPE_IDPRIME_940) { + /* For cards other than the 940, we don't know how to recognize + certificates missing keys other than to check + that there is a corresponding entry in the container map.*/ + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Adding certificate with fd=%d", new_object.fd); + idprime_add_object_to_list(&priv->pki_list, &new_object); + continue; + } + } + + switch (card->type) { + case SC_CARD_TYPE_IDPRIME_3810: + new_object.key_reference = 0x31 + cert_id; + break; + case SC_CARD_TYPE_IDPRIME_830: + new_object.key_reference = 0x41 + cert_id; + break; + case SC_CARD_TYPE_IDPRIME_930: + new_object.key_reference = 0x11 + cert_id * 2; + break; + case SC_CARD_TYPE_IDPRIME_930_PLUS: + new_object.key_reference = 0x10 + cert_id * 2; + break; + case SC_CARD_TYPE_IDPRIME_940: { + idprime_keyref_t *keyref = (idprime_keyref_t *) list_seek(&priv->keyrefmap, &cert_id); + if (!keyref) { + /* Key reference file does not contain record of the key for given certificate */ + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "No corresponding key reference found for certificate with id=%d", cert_id); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Adding certificate with fd=%d", new_object.fd); + idprime_add_object_to_list(&priv->pki_list, &new_object); + continue; + } + new_object.key_reference = keyref->key_reference; + new_object.pin_index = keyref->pin_index; + break; + } + case SC_CARD_TYPE_IDPRIME_840: + new_object.key_reference = 0xf7 + cert_id; + break; + default: + new_object.key_reference = 0x56 + cert_id; + break; + } + new_object.valid_key_ref = 1; + if (container != NULL) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Found certificate with fd=%d, key_ref=%d corresponding to container \"%s\"", + new_object.fd, new_object.key_reference, container->guid); + } else { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Found certificate with fd=%d, key_ref=%d without corresponding container", + new_object.fd, new_object.key_reference); + } + + idprime_add_object_to_list(&priv->pki_list, &new_object); + + /* This looks like non-standard extension listing pkcs11 token info label in my card */ + } else if ((memcmp(&start[4], "tinfo", 6) == 0) && (memcmp(&start[12], "p11", 4) == 0)) { + memcpy(priv->tinfo_df, new_object.df, sizeof(priv->tinfo_df)); + priv->tinfo_present = 1; + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Found p11/tinfo object"); + } else if ((memcmp(&start[4], "cmapfile", 8) == 0) && (memcmp(&start[12], "mscp", 4) == 0)) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Found mscp/cmapfile object %s", + (start[0] == 02 && start[1] == 04 ? "(already processed)" : "(in non-standard path!)")); + } else if (memcmp(&start[4], "cardapps", 8) == 0) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Found cardapps object"); + } else if (memcmp(&start[4], "cardid", 6) == 0) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Found cardid object"); + } else if (memcmp(&start[4], "cardcf", 6) == 0) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Found cardcf object"); + } + } + + r = SC_SUCCESS; +done: + free(buf); + LOG_FUNC_RETURN(card->ctx, r); +} + +/* CPLC has 42 bytes, but we get it with 3B header */ +#define CPLC_LENGTH 45 +static int idprime_init(sc_card_t *card) +{ + int r; + unsigned long flags, ext_flags; + idprime_private_data_t *priv = NULL; + struct sc_apdu apdu; + u8 rbuf[CPLC_LENGTH]; + size_t rbuflen = sizeof(rbuf); + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* We need to differentiate the OS version since they behave slightly differently */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0xCA, 0x9F, 0x7F); + apdu.resp = rbuf; + apdu.resplen = rbuflen; + apdu.le = rbuflen; + r = sc_transmit_apdu(card, &apdu); + if (r == SC_SUCCESS && apdu.resplen == CPLC_LENGTH) { + /* We are interested in the OS release level here */ + switch (rbuf[11]) { + case 0x01: + sc_log(card->ctx, "Detected IDPrime applet version 1"); + break; + case 0x02: + sc_log(card->ctx, "Detected IDPrime applet version 2"); + break; + case 0x03: + sc_log(card->ctx, "Detected IDPrime applet version 3"); + break; + case 0x04: + sc_log(card->ctx, "Detected IDPrime applet version 4"); + break; + default: + sc_log(card->ctx, "Unknown OS version received: %d", rbuf[11]); + break; + } + } else { + sc_log(card->ctx, "Failed to get CPLC data or invalid length returned, " + "err=%d, len=%"SC_FORMAT_LEN_SIZE_T"u", + r, apdu.resplen); + } + + /* Proprietary data -- Applet version */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0xCA, 0xDF, 0x30); + apdu.resp = rbuf; + apdu.resplen = rbuflen; + apdu.le = rbuflen; + r = sc_transmit_apdu(card, &apdu); + if (r == SC_SUCCESS && apdu.resplen >= 10) { + /* Ber-TLV encoded */ + if (rbuf[0] == 0xDF && rbuf[1] == 0x30 && rbuf[2] == apdu.resplen - 3) { + sc_log(card->ctx, "IDPrime Java Applet version %.*s", (int)apdu.resplen - 3, rbuf + 3); + } + } + + priv = idprime_new_private_data(); + if (!priv) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + + /* Select and process container file */ + r = idprime_select_file_by_path(card, "0204");; + if (r <= 0) { + idprime_free_private_data(priv); + if (r == 0) + r = SC_ERROR_INVALID_DATA; + LOG_FUNC_RETURN(card->ctx, r); + } + + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Container file found"); + + r = idprime_process_containermap(card, priv, r); + if (r != SC_SUCCESS) { + idprime_free_private_data(priv); + LOG_FUNC_RETURN(card->ctx, r); + } + + if (card->type == SC_CARD_TYPE_IDPRIME_940) { + if ((r = idprime_select_file_by_path(card, "0005")) <= 0) { + idprime_free_private_data(priv); + if (r == 0) + r = SC_ERROR_INVALID_DATA; + LOG_FUNC_RETURN(card->ctx, r); + } + + if ((r = idprime_process_keyrefmap(card, priv, r)) != SC_SUCCESS) { + idprime_free_private_data(priv); + LOG_FUNC_RETURN(card->ctx, r); + } + } + + /* Select and process the index file */ + r = idprime_select_file_by_path(card, "0101"); + if (r <= 0) { + idprime_free_private_data(priv); + if (r == 0) + r = SC_ERROR_INVALID_DATA; + LOG_FUNC_RETURN(card->ctx, r); + } + + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Index file found"); + + r = idprime_process_index(card, priv, r); + if (r != SC_SUCCESS) { + idprime_free_private_data(priv); + LOG_FUNC_RETURN(card->ctx, r); + } + + card->drv_data = priv; + + switch (card->type) { + case SC_CARD_TYPE_IDPRIME_3810: + card->name = "Gemalto IDPrime 3810"; + break; + case SC_CARD_TYPE_IDPRIME_830: + card->name = "Gemalto IDPrime MD 830"; + break; + case SC_CARD_TYPE_IDPRIME_930: + case SC_CARD_TYPE_IDPRIME_930_PLUS: + card->name = "Gemalto IDPrime 930/3930"; + break; + case SC_CARD_TYPE_IDPRIME_940: + card->name = "Gemalto IDPrime 940"; + break; + case SC_CARD_TYPE_IDPRIME_840: + card->name = "Gemalto IDPrime MD 840"; + break; + case SC_CARD_TYPE_IDPRIME_GENERIC: + default: + card->name = "Gemalto IDPrime (generic)"; + break; + } + card->cla = 0x00; + + /* Set up algorithm info for RSA. */ + flags = SC_ALGORITHM_RSA_PAD_PKCS1 + | SC_ALGORITHM_RSA_PAD_PSS + | SC_ALGORITHM_RSA_PAD_OAEP + /* SHA-1 mechanisms are not allowed in the card I have */ + | (SC_ALGORITHM_RSA_HASH_SHA256 | SC_ALGORITHM_RSA_HASH_SHA384 | SC_ALGORITHM_RSA_HASH_SHA512) + | (SC_ALGORITHM_MGF1_SHA256 | SC_ALGORITHM_MGF1_SHA384 | SC_ALGORITHM_MGF1_SHA512) + ; + + _sc_card_add_rsa_alg(card, 1024, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); + switch (card->type) { + case SC_CARD_TYPE_IDPRIME_930_PLUS: + case SC_CARD_TYPE_IDPRIME_940: + _sc_card_add_rsa_alg(card, 3072, flags, 0); + /* fallthrough */ + case SC_CARD_TYPE_IDPRIME_930: + _sc_card_add_rsa_alg(card, 4096, flags, 0); + /* fallthrough */ + case SC_CARD_TYPE_IDPRIME_840: + /* Set up algorithm info for EC */ + flags = SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDSA_HASH_NONE; + if (card->type == SC_CARD_TYPE_IDPRIME_930_PLUS || card->type == SC_CARD_TYPE_IDPRIME_940) { + flags |= SC_ALGORITHM_ECDH_CDH_RAW; + } + ext_flags = SC_ALGORITHM_EXT_EC_F_P + | SC_ALGORITHM_EXT_EC_ECPARAMETERS + | SC_ALGORITHM_EXT_EC_NAMEDCURVE + | SC_ALGORITHM_EXT_EC_UNCOMPRESES + ; + _sc_card_add_ec_alg(card, 256, flags, ext_flags, NULL); + _sc_card_add_ec_alg(card, 384, flags, ext_flags, NULL); + _sc_card_add_ec_alg(card, 521, flags, ext_flags, NULL); + break; + default: + break; + } + + card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; + + card->caps |= SC_CARD_CAP_RNG; + + LOG_FUNC_RETURN(card->ctx, 0); +} + +static int idprime_finish(sc_card_t *card) +{ + idprime_private_data_t * priv = card->drv_data; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + if (priv) { + idprime_free_private_data(priv); + } + return SC_SUCCESS; +} + +static int idprime_match_card(sc_card_t *card) +{ + int i, r; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + i = _sc_match_atr(card, idprime_atrs, &card->type); + if (i < 0) + return 0; + + r = idprime_select_file_by_path(card, "0101"); + LOG_FUNC_RETURN(card->ctx, r > 0); +} + +/* initialize getting a list and return the number of elements in the list */ +static int idprime_get_init_and_get_count(list_t *list, idprime_object_t **entry, int *countp) +{ + if (countp == NULL || entry == NULL) { + return SC_ERROR_INVALID_ARGUMENTS; + } + *countp = list_size(list); + list_iterator_start(list); + *entry = list_iterator_next(list); + return SC_SUCCESS; +} + +/* finalize the list iterator */ +static int idprime_final_iterator(list_t *list) +{ + list_iterator_stop(list); + return SC_SUCCESS; +} + +/* fill in the prkey_info for the current object on the list and advance to the next object */ +static int idprime_fill_prkey_info(list_t *list, idprime_object_t **entry, sc_pkcs15_prkey_info_t *prkey_info) +{ + memset(prkey_info, 0, sizeof(sc_pkcs15_prkey_info_t)); + if (*entry == NULL) { + return SC_ERROR_FILE_END_REACHED; + } + + prkey_info->path.len = sizeof((*entry)->df); + memcpy(prkey_info->path.value, (*entry)->df, sizeof((*entry)->df)); + prkey_info->path.type = SC_PATH_TYPE_FILE_ID; + /* Do not specify the length -- it will be read from the FCI */ + prkey_info->path.count = -1; + + /* TODO figure out the IDs as the original driver? */ + prkey_info->id.value[0] = ((*entry)->fd >> 8) & 0xff; + prkey_info->id.value[1] = (*entry)->fd & 0xff; + prkey_info->id.len = 2; + if ((*entry)->valid_key_ref) + prkey_info->key_reference = (*entry)->key_reference; + else + prkey_info->key_reference = -1; + *entry = list_iterator_next(list); + return SC_SUCCESS; +} + +/* get PIN id of the current object on the list */ +static int idprime_get_pin_id(list_t *list, idprime_object_t **entry, const char **pin_id) +{ + if (pin_id == NULL || entry == NULL) { + return SC_ERROR_INVALID_ARGUMENTS; + } + *pin_id = "11"; // normal PIN id + if ((*entry)->pin_index != 1) + *pin_id = "83"; // signature PIN id + return SC_SUCCESS; +} + +#define IDPRIME_CARDID_LEN 16 + +static int idprime_get_serial(sc_card_t* card, sc_serial_number_t* serial) +{ + sc_path_t cardid_path; + sc_file_t *file = NULL; + u8 buf[IDPRIME_CARDID_LEN]; + int r; + + LOG_FUNC_CALLED(card->ctx); + + /* XXX this is assumed to be cardid for windows. It can be read from the index file */ + sc_format_path("0201", &cardid_path); + r = iso_ops->select_file(card, &cardid_path, &file); + if (r != SC_SUCCESS || file->size != IDPRIME_CARDID_LEN) { /* The cardid is always 16 B */ + sc_file_free(file); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH); + } + + r = iso_ops->read_binary(card, 0, buf, file->size, 0); + sc_file_free(file); + if (r < 1) { + LOG_FUNC_RETURN(card->ctx, r); + } else if (r != IDPRIME_CARDID_LEN) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); + } + + serial->len = MIN(IDPRIME_CARDID_LEN, SC_MAX_SERIALNR); + memcpy(serial->value, buf, serial->len); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int idprime_get_token_name(sc_card_t* card, char** tname) +{ + idprime_private_data_t * priv = card->drv_data; + sc_path_t tinfo_path = {"\x00\x00", 2, 0, 0, SC_PATH_TYPE_PATH, {"", 0}}; + sc_file_t *file = NULL; + u8 buf[2]; + char *name; + int r; + + LOG_FUNC_CALLED(card->ctx); + + if (tname == NULL) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + if (!priv->tinfo_present) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + + memcpy(tinfo_path.value, priv->tinfo_df, 2); + r = iso_ops->select_file(card, &tinfo_path, &file); + if (r != SC_SUCCESS || file->size == 0) { + sc_file_free(file); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + + /* First two bytes lists 0x01, the second indicates length */ + r = iso_ops->read_binary(card, 0, buf, 2, 0); + if (r < 2 || buf[1] > file->size) { /* make sure we do not overrun */ + sc_file_free(file); + LOG_FUNC_RETURN(card->ctx, r); + } + sc_file_free(file); + + name = malloc(buf[1]); + if (name == NULL) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + + r = iso_ops->read_binary(card, 2, (unsigned char *)name, buf[1], 0); + if (r < 1) { + free(name); + LOG_FUNC_RETURN(card->ctx, r); + } + + if (name[r-1] != '\0') { + name[r-1] = '\0'; + } + *tname = name; + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int idprime_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) +{ + idprime_private_data_t * priv = card->drv_data; + + LOG_FUNC_CALLED(card->ctx); + sc_log(card->ctx, "cmd=%ld ptr=%p", cmd, ptr); + + if (priv == NULL) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + switch (cmd) { + case SC_CARDCTL_GET_SERIALNR: + return idprime_get_serial(card, (sc_serial_number_t *) ptr); + case SC_CARDCTL_IDPRIME_GET_TOKEN_NAME: + return idprime_get_token_name(card, (char **) ptr); + case SC_CARDCTL_IDPRIME_INIT_GET_OBJECTS: + return idprime_get_init_and_get_count(&priv->pki_list, &priv->pki_current, + (int *)ptr); + case SC_CARDCTL_IDPRIME_GET_NEXT_OBJECT: + return idprime_fill_prkey_info(&priv->pki_list, &priv->pki_current, + (sc_pkcs15_prkey_info_t *)ptr); + case SC_CARDCTL_IDPRIME_FINAL_GET_OBJECTS: + return idprime_final_iterator(&priv->pki_list); + case SC_CARDCTL_IDPRIME_GET_PIN_ID: + return idprime_get_pin_id(&priv->pki_list, &priv->pki_current, + (const char **)ptr); + } + + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); +} + +#define HEADER_LEN 4 + +static int idprime_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) +{ + int r; + idprime_private_data_t * priv = card->drv_data; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* forget any old cached values */ + if (priv->cache_buf) { + free(priv->cache_buf); + priv->cache_buf = NULL; + } + priv->cache_buf_len = 0; + priv->cached = 0; + + r = iso_ops->select_file(card, in_path, file_out); + if (r == SC_SUCCESS && file_out != NULL) { + /* Cache the real file size for the caching read_binary() */ + priv->file_size = (*file_out)->size; + } + /* Return the exit code of the select command */ + return r; +} + +// used to read existing certificates +static int idprime_read_binary(sc_card_t *card, unsigned int offset, + unsigned char *buf, size_t count, unsigned long *flags) +{ + struct idprime_private_data *priv = card->drv_data; + int r = 0; + int size; + size_t sz; + + sc_log(card->ctx, "called; %"SC_FORMAT_LEN_SIZE_T"u bytes at offset %d", + count, offset); + + if (!priv->cached && offset == 0) { + /* Read what was reported by FCI from select command */ + size_t left = priv->file_size; + unsigned read = 0; + + // this function is called to read and uncompress the certificate + u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE]; + u8 *data_buffer = buffer; + if (sizeof(buffer) < count || sizeof(buffer) < priv->file_size) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + while (left > 0) { + r = iso_ops->read_binary(card, read, buffer + read, priv->file_size - read, flags); + if (r <= 0) { + LOG_FUNC_RETURN(card->ctx, r); + } + left -= r; + read += r; + } + if (read < 4 || read != priv->file_size) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); + } + if (buffer[0] == 1 && buffer[1] == 0) { + /* Data will be decompressed later */ + data_buffer += 4; + sz = priv->file_size - 4; + if (flags) + *flags |= SC_FILE_FLAG_COMPRESSED_AUTO; + } else { + sz = priv->file_size; + } + priv->cache_buf = malloc(sz); + if (priv->cache_buf == NULL) { + return SC_ERROR_OUT_OF_MEMORY; + } + memcpy(priv->cache_buf, data_buffer, sz); + priv->cache_buf_len = sz; + priv->cached = 1; + } + if (offset >= priv->cache_buf_len) { + return 0; + } + size = (int) MIN((priv->cache_buf_len - offset), count); + memcpy(buf, priv->cache_buf + offset, size); + return size; +} + +static int +idprime_set_security_env(struct sc_card *card, + const struct sc_security_env *env, int se_num) +{ + int r; + struct sc_security_env new_env; + idprime_private_data_t *priv = NULL; + + if (card == NULL || env == NULL) { + return SC_ERROR_INVALID_ARGUMENTS; + } + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + priv = card->drv_data; + + /* The card requires algorithm reference here */ + new_env = *env; + new_env.flags |= SC_SEC_ENV_ALG_REF_PRESENT; + /* SHA-1 mechanisms are not allowed in the card I have available */ + switch (env->operation) { + case SC_SEC_OPERATION_DERIVE: + priv->current_op = SC_ALGORITHM_EC; + new_env.flags &= ~SC_SEC_ENV_ALG_REF_PRESENT; + break; + case SC_SEC_OPERATION_DECIPHER: + if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_OAEP) { + if (env->algorithm_flags & SC_ALGORITHM_MGF1_SHA1) { + new_env.algorithm_ref = 0x1D; + } else if (env->algorithm_flags & SC_ALGORITHM_MGF1_SHA256) { + new_env.algorithm_ref = 0x4D; + } else if (env->algorithm_flags & SC_ALGORITHM_MGF1_SHA384) { + new_env.algorithm_ref = 0x5D; + } else if (env->algorithm_flags & SC_ALGORITHM_MGF1_SHA512) { + new_env.algorithm_ref = 0x6D; + } + } else { /* RSA-PKCS without hashing */ + new_env.algorithm_ref = 0x1A; + } + break; + case SC_SEC_OPERATION_SIGN: + if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PSS) { + if (env->algorithm_flags & SC_ALGORITHM_MGF1_SHA256) { + new_env.algorithm_ref = 0x45; + } else if (env->algorithm_flags & SC_ALGORITHM_MGF1_SHA384) { + new_env.algorithm_ref = 0x55; + } else if (env->algorithm_flags & SC_ALGORITHM_MGF1_SHA512) { + new_env.algorithm_ref = 0x65; + } + priv->current_op = SC_ALGORITHM_RSA; + } else if (env->algorithm_flags & (SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_PAD_OAEP)) { + if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA256) { + new_env.algorithm_ref = 0x42; + } else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA384) { + new_env.algorithm_ref = 0x52; + } else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA512) { + new_env.algorithm_ref = 0x62; + } else { /* RSA-PKCS without hashing */ + new_env.algorithm_ref = 0x02; + } + priv->current_op = SC_ALGORITHM_RSA; + } else if (env->algorithm == SC_ALGORITHM_EC) { + new_env.algorithm_ref = 0x44; + priv->current_op = SC_ALGORITHM_EC; + } + break; + default: + return SC_ERROR_INVALID_ARGUMENTS; + } + r = iso_ops->set_security_env(card, + (const struct sc_security_env *) &new_env, se_num); + + LOG_FUNC_RETURN(card->ctx, r); +} + +/* These are mostly ISO versions updated to IDPrime specifics */ +static int +idprime_compute_signature(struct sc_card *card, + const u8 * data, size_t datalen, u8 * out, size_t outlen) +{ + int r; + struct sc_apdu apdu; + u8 *p; + u8 sbuf[128] = {0}; /* For SHA-512 we need 64 + 2 bytes */ + u8 rbuf[4096]; /* needs work. for 3072 keys, needs 384+2 or so */ + size_t rbuflen = sizeof(rbuf); + idprime_private_data_t *priv = card->drv_data; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* We should be signing hashes only so we should not reach this limit */ + if (datalen + 2 > sizeof(sbuf)) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + + /* The data for ECDSA should be padded to the length of a multiple of 8 */ + size_t pad = 0; + if (priv->current_op == SC_ALGORITHM_EC && datalen % 8 != 0) { + pad = 8 - (datalen % 8); + datalen += pad; + } + + p = sbuf; + *(p++) = 0x90; + *(p++) = datalen; + memcpy(p + pad, data, datalen - pad); + p += datalen; + + /* INS: 0x2A PERFORM SECURITY OPERATION + * P1: 0x90 Hash code + * P2: 0xA0 Input template for the computation of a hash-code (the template is hashed) */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x90, 0xA0); + apdu.resp = rbuf; + apdu.resplen = rbuflen; + apdu.le = datalen; + + apdu.data = sbuf; + apdu.lc = p - sbuf; + apdu.datalen = p - sbuf; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + /* This just returns the passed data (hash code) (for verification?) */ + if (apdu.resplen != datalen || memcmp(rbuf + pad, data, datalen - pad) != 0) { + sc_log(card->ctx, "The initial APDU did not return the same data"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + /* INS: 0x2A PERFORM SECURITY OPERATION + * P1: 0x9E Resp: Digital Signature + * P2: 0x9A Cmd: Input for Digital Signature */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x2A, 0x9E, 0x9A); + apdu.resp = out; + apdu.resplen = outlen; + apdu.le = outlen; + iso7816_fixup_transceive_length(card, &apdu); + + apdu.data = NULL; + apdu.datalen = 0; + apdu.lc = 0; + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "Card returned error"); + + LOG_FUNC_RETURN(card->ctx, r); +} + +/* These are mostly ISO versions updated to IDPrime specifics */ +static int +idprime_decipher(struct sc_card *card, + const u8 * crgram, size_t crgram_len, + u8 * out, size_t outlen) +{ + int r; + struct sc_apdu apdu; + u8 *sbuf = NULL; + idprime_private_data_t *priv; + + if (card == NULL || crgram == NULL || out == NULL) { + return SC_ERROR_INVALID_ARGUMENTS; + } + LOG_FUNC_CALLED(card->ctx); + priv = card->drv_data; + sc_log(card->ctx, + "IDPrime decipher: in-len %"SC_FORMAT_LEN_SIZE_T"u, out-len %"SC_FORMAT_LEN_SIZE_T"u", + crgram_len, outlen); + + sbuf = malloc(crgram_len + 1); + if (sbuf == NULL) + return SC_ERROR_OUT_OF_MEMORY; + + /* INS: 0x2A PERFORM SECURITY OPERATION + * P1: 0x80 Resp: Plain value + * P2: 0x86 Cmd: Padding indicator byte followed by cryptogram */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x80, 0x86); + apdu.resp = out; + apdu.resplen = outlen; + apdu.le = outlen; + + sbuf[0] = priv->current_op == SC_ALGORITHM_EC ? 0x00 : 0x81; /* padding indicator byte, 0x81 = Proprietary, 0x00 = No further indication */ + memcpy(sbuf + 1, crgram, crgram_len); + apdu.data = sbuf; + apdu.lc = crgram_len + 1; + iso7816_fixup_transceive_length(card, &apdu); + apdu.datalen = crgram_len + 1; + + r = sc_transmit_apdu(card, &apdu); + sc_mem_clear(sbuf, crgram_len + 1); + free(sbuf); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); + else + LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); +} + +static int +idprime_get_challenge(struct sc_card *card, u8 *rnd, size_t len) +{ + u8 rbuf[16]; + size_t out_len; + struct sc_apdu apdu; + int r; + + LOG_FUNC_CALLED(card->ctx); + + if (len <= 8) { + /* official closed driver always calls this regardless the length */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x84, 0x00, 0x01); + apdu.le = apdu.resplen = 8; + } else { + /* this was discovered accidentally - all 16 bytes seem random */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x84, 0x00, 0x00); + apdu.le = apdu.resplen = 16; + } + apdu.resp = rbuf; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "GET CHALLENGE failed"); + + out_len = len < apdu.resplen ? len : apdu.resplen; + memcpy(rnd, rbuf, out_len); + + LOG_FUNC_RETURN(card->ctx, (int) out_len); +} + +static struct sc_card_driver * sc_get_driver(void) +{ + if (iso_ops == NULL) { + iso_ops = sc_get_iso7816_driver()->ops; + } + + idprime_ops = *iso_ops; + idprime_ops.match_card = idprime_match_card; + idprime_ops.init = idprime_init; + idprime_ops.finish = idprime_finish; + + idprime_ops.read_binary = idprime_read_binary; + idprime_ops.select_file = idprime_select_file; + idprime_ops.card_ctl = idprime_card_ctl; + idprime_ops.set_security_env = idprime_set_security_env; + idprime_ops.compute_signature = idprime_compute_signature; + idprime_ops.decipher = idprime_decipher; + + idprime_ops.get_challenge = idprime_get_challenge; + + return &idprime_drv; +} + +struct sc_card_driver * sc_get_idprime_driver(void) +{ + return sc_get_driver(); +} diff --git a/src/libopensc/card-incrypto34.c b/src/libopensc/card-incrypto34.c deleted file mode 100644 index 676a755dc7..0000000000 --- a/src/libopensc/card-incrypto34.c +++ /dev/null @@ -1,888 +0,0 @@ -/* - * card-incrypto34.c: Support for Incard Incrypto34 based cards and tokens - * (for example Italian CNS) - * - * Copyright (C) 2005 ST Incard srl, Giuseppe Amato , - * Copyright (C) 2002 Andreas Jellinghaus - * Copyright (C) 2001 Juha Yrjölä - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include - -#include "internal.h" -#include "cardctl.h" - -/* andreas says: hm, my card only works for small payloads */ -/* comment by okir: one of the examples in the developer guide - * also talks about copying data in chunks of 128. - * Either coincidence, or a known problem. */ -#define INCRYPTO34_MAX_PAYLOAD 120 - -static const struct sc_card_operations *iso_ops = NULL; - -static struct sc_card_operations incrypto34_ops; -static struct sc_card_driver incrypto34_drv = { - "Incard Incripto34", - "incrypto34", - &incrypto34_ops, - NULL, 0, NULL -}; - -static struct sc_atr_table incrypto34_atrs[] = { - { "3b:ff:18:00:ff:81:31:fe:55:00:6b:02:09:02:00:01:01:01:44:53:44:10:31:80:92", NULL, NULL, SC_CARD_TYPE_INCRYPTO34_GENERIC, 0, NULL }, - { NULL, NULL, NULL, 0, 0, NULL } -}; - -static int incrypto34_match_card(struct sc_card *card) -{ - int i; - - i = _sc_match_atr(card, incrypto34_atrs, &card->type); - if (i < 0) - return 0; - return 1; -} - -static int incrypto34_init(sc_card_t *card) -{ - unsigned long flags; - - card->name = "Incrypto34"; - card->cla = 0x00; - - /* Set up algorithm info. */ - flags = SC_ALGORITHM_NEED_USAGE - | SC_ALGORITHM_RSA_RAW - | SC_ALGORITHM_RSA_HASH_NONE - | SC_ALGORITHM_ONBOARD_KEY_GEN - ; - _sc_card_add_rsa_alg(card, 512, flags, 0); - _sc_card_add_rsa_alg(card, 768, flags, 0); - _sc_card_add_rsa_alg(card, 1024, flags, 0); - - return 0; -} - -static const struct sc_card_error incrypto34_errors[] = { -/* some error inside the card */ -/* i.e. nothing you can do */ -{ 0x6581, SC_ERROR_MEMORY_FAILURE, "EEPROM error; command aborted"}, -{ 0x6fff, SC_ERROR_CARD_CMD_FAILED, "internal assertion error"}, -{ 0x6700, SC_ERROR_WRONG_LENGTH, "LC invalid"}, -{ 0x6985, SC_ERROR_CARD_CMD_FAILED, "no random number available"}, -{ 0x6f81, SC_ERROR_CARD_CMD_FAILED, "file invalid, maybe checksum error"}, -{ 0x6f82, SC_ERROR_CARD_CMD_FAILED, "not enough memory in xram"}, -{ 0x6f84, SC_ERROR_CARD_CMD_FAILED, "general protection fault"}, - -/* the card doesn't now thic combination of ins+cla+p1+p2 */ -/* i.e. command will never work */ -{ 0x6881, SC_ERROR_NO_CARD_SUPPORT, "logical channel not supported"}, -{ 0x6a86, SC_ERROR_INCORRECT_PARAMETERS,"p1/p2 invalid"}, -{ 0x6d00, SC_ERROR_INS_NOT_SUPPORTED, "ins invalid"}, -{ 0x6e00, SC_ERROR_CLASS_NOT_SUPPORTED, "class invalid (hi nibble)"}, - -/* known command, but incorrectly used */ -/* i.e. command could work, but you need to change something */ -{ 0x6981, SC_ERROR_CARD_CMD_FAILED, "command cannot be used for file structure"}, -{ 0x6a80, SC_ERROR_INCORRECT_PARAMETERS,"invalid parameters in data field"}, -{ 0x6a81, SC_ERROR_NOT_SUPPORTED, "function/mode not supported"}, -{ 0x6a85, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit the tlv structure"}, -{ 0x6986, SC_ERROR_INCORRECT_PARAMETERS,"no current ef selected"}, -{ 0x6a87, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit p1/p2"}, -{ 0x6c00, SC_ERROR_WRONG_LENGTH, "le does not fit the data to be sent"}, -{ 0x6f83, SC_ERROR_CARD_CMD_FAILED, "command must not be used in transaction"}, - -/* (something) not found */ -{ 0x6987, SC_ERROR_INCORRECT_PARAMETERS,"key object for sm not found"}, -{ 0x6f86, SC_ERROR_CARD_CMD_FAILED, "key object not found"}, -{ 0x6a82, SC_ERROR_FILE_NOT_FOUND, "file not found"}, -{ 0x6a83, SC_ERROR_RECORD_NOT_FOUND, "record not found"}, -{ 0x6a88, SC_ERROR_CARD_CMD_FAILED, "object not found"}, -{ 0x6a89, SC_ERROR_FILE_ALREADY_EXISTS, "file/object already exists"}, - -/* (something) invalid */ -{ 0x6884, SC_ERROR_CARD_CMD_FAILED, "chaining error"}, -{ 0x6984, SC_ERROR_CARD_CMD_FAILED, "bs object has invalid format"}, -{ 0x6988, SC_ERROR_INCORRECT_PARAMETERS,"key object used for sm has invalid format"}, - -/* (something) deactivated */ -{ 0x6283, SC_ERROR_CARD_CMD_FAILED, "file is deactivated" }, -{ 0x6983, SC_ERROR_AUTH_METHOD_BLOCKED, "bs object blocked"}, - -/* access denied */ -{ 0x6300, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"authentication failed"}, -{ 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"required access right not granted"}, - -/* other errors */ -{ 0x6a84, SC_ERROR_CARD_CMD_FAILED, "not enough memory"}, - -/* command ok, execution failed */ -{ 0x6f00, SC_ERROR_CARD_CMD_FAILED, "technical error (see incrypto34 developers guide)"}, - -/* no error, maybe a note */ -{ 0x9000, SC_SUCCESS, NULL}, -{ 0x9001, SC_SUCCESS, "success, but eeprom weakness detected"}, -{ 0x9850, SC_SUCCESS, "over/underflow useing in/decrease"} -}; - -static int incrypto34_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) -{ - const int err_count = sizeof(incrypto34_errors)/sizeof(incrypto34_errors[0]); - int i; - - for (i = 0; i < err_count; i++) { - if (incrypto34_errors[i].SWs == ((sw1 << 8) | sw2)) { - if ( incrypto34_errors[i].errorstr ) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n", - incrypto34_errors[i].errorstr); - return incrypto34_errors[i].errorno; - } - } - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2); - return SC_ERROR_CARD_CMD_FAILED; -} - -static int incrypto34_list_files(sc_card_t *card, u8 *buf, size_t buflen) -{ - sc_apdu_t apdu; - u8 rbuf[256]; - int r; - size_t fids; - u8 offset; - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - - fids=0; - offset=0; - - /* INS 0xFC: SCAN DF*/ - /* P1 0x00: list both DF and EF */ - /* P2 0x00/0x01: first/next element */ - /* LE 0x03*/ - /* - returns 3 bytes: FILE_TYPE + FID_HI_BYTE + FID_LO_BYTE - */ - -get_next_part: - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xFC, 0x00, offset?0x01:0x00); - apdu.cla = 0xB0; - apdu.le = 3; - apdu.resplen = sizeof(rbuf); - apdu.resp = rbuf; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if (apdu.sw1 == 0x6a && apdu.sw2 == 0x82) - goto end; /* no more files */ - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "DIRECTORY command returned error"); - - if (apdu.resplen >= 3 - && ((rbuf[0] >= 0x01 && rbuf[0] <= 0x07) || 0x38 == rbuf[0]) - && fids + 2 >= buflen) - { - buf[fids++] = rbuf[1]; - buf[fids++] = rbuf[2]; - } - - ++offset; - goto get_next_part; - -end: - r = fids; - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - -static void add_acl_entry(sc_file_t *file, int op, u8 byte) -{ - unsigned int method, key_ref = SC_AC_KEY_REF_NONE; - - switch (byte) { - case 0x00: - method = SC_AC_NONE; - break; - case 0xFF: - method = SC_AC_NEVER; - break; - default: - if (byte > 0x7F) { - method = SC_AC_UNKNOWN; - } else { - method = SC_AC_CHV; - key_ref = byte; - } - break; - } - sc_file_add_acl_entry(file, op, method, key_ref); -} - -static int acl_to_byte(const sc_acl_entry_t *e) -{ - if (e != NULL) { - switch (e->method) { - case SC_AC_NONE: - return 0x00; - case SC_AC_NEVER: - return 0xFF; - case SC_AC_CHV: - case SC_AC_TERM: - case SC_AC_AUT: - if (e->key_ref == SC_AC_KEY_REF_NONE) - return -1; - if (e->key_ref > 0x7F) - return -1; - return e->key_ref; - } - } - return 0x00; -} - -static const int df_acl[9] = { - -1, /* LCYCLE (life cycle change) */ - SC_AC_OP_UPDATE, /* UPDATE Objects */ - -1, /* APPEND Objects */ - - SC_AC_OP_INVALIDATE, /* DF */ - SC_AC_OP_REHABILITATE, /* DF */ - SC_AC_OP_DELETE, /* DF */ - - -1, /* ADMIN DF */ - SC_AC_OP_CREATE, /* Files */ - -1 /* Reserved */ -}; -static const int ef_acl[9] = { - SC_AC_OP_READ, /* Data */ - SC_AC_OP_UPDATE, /* Data (write file content) */ - SC_AC_OP_WRITE, /* */ - - SC_AC_OP_INVALIDATE, /* EF */ - SC_AC_OP_REHABILITATE, /* EF */ - SC_AC_OP_ERASE, /* (delete) EF */ - - /* XXX: ADMIN should be an ACL type of its own, or mapped - * to erase */ - -1, /* ADMIN EF (modify meta information?) */ - -1, /* INC (-> cylic fixed files) */ - -1 /* DEC */ -}; - -static void parse_sec_attr(sc_file_t *file, const u8 *buf, size_t len) -{ - size_t i; - const int *idx; - - idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl; - - /* acl defaults to 0xFF if unspecified */ - for (i = 0; i < 9; i++) - if (idx[i] != -1) - add_acl_entry(file, idx[i], (u8)((i < len) ? buf[i] : 0xFF)); -} - -static int incrypto34_select_file(sc_card_t *card, - const sc_path_t *in_path, - sc_file_t **file) -{ - int r; - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - r = iso_ops->select_file(card, in_path, file); - if (r >= 0 && file) - parse_sec_attr((*file), (*file)->sec_attr, (*file)->sec_attr_len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - -static int incrypto34_create_file(sc_card_t *card, sc_file_t *file) -{ - int r, i, byte; - const int *idx; - u8 acl[9], type[3], status[3]; - char pbuf[128+1]; - size_t n; - - for (n = 0; n < file->path.len; n++) { - snprintf(pbuf + 2 * n, sizeof(pbuf) - 2 * n, - "%02X", file->path.value[n]); - } - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "incrypto34_create_file(%s)\n", pbuf); - - if (file->type_attr_len == 0) { - memset(type, 0, sizeof(type)); - type[0] = 0x00; - switch (file->type) { - case SC_FILE_TYPE_WORKING_EF: - break; - case SC_FILE_TYPE_INTERNAL_EF: - type[0] = 0x08; - break; - case SC_FILE_TYPE_DF: - type[0] = 0x38; - break; - default: - r = SC_ERROR_NOT_SUPPORTED; - goto out; - } - if (file->type != SC_FILE_TYPE_DF) { - switch (file->ef_structure) { - case SC_FILE_EF_LINEAR_FIXED_TLV: - case SC_FILE_EF_LINEAR_VARIABLE: - case SC_FILE_EF_CYCLIC_TLV: - r = SC_ERROR_NOT_SUPPORTED; - goto out; - /* No idea what this means, but it - * seems to be required for key - * generation. */ - case SC_FILE_EF_LINEAR_VARIABLE_TLV: - type[1] = 0xff; - default: - type[0] |= file->ef_structure & 7; - break; - } - } - r = sc_file_set_type_attr(file, type, sizeof(type)); - if (r) - goto out; - } - if (file->prop_attr_len == 0) { - status[0] = 0x01; - if (file->type == SC_FILE_TYPE_DF) { - status[1] = file->size >> 8; - status[2] = file->size; - } else { - status[1] = status[2] = 0x00; /* not used */ - } - r = sc_file_set_prop_attr(file, status, sizeof(status)); - if (r) - goto out; - } - if (file->sec_attr_len == 0) { - idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl; - for (i = 0; i < 9; i++) { - if (idx[i] < 0) - byte = 0x00; - else - byte = acl_to_byte( - sc_file_get_acl_entry(file, idx[i])); - if (byte < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid ACL\n"); - r = SC_ERROR_INVALID_ARGUMENTS; - goto out; - } - acl[i] = byte; - } - r = sc_file_set_sec_attr(file, acl, sizeof(acl)); - if (r) - goto out; - } - r = iso_ops->create_file(card, file); - - /* FIXME: if this is a DF and there's an AID, set it here - * using PUT_DATA_FCI */ - -out: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - -/* - * Restore the indicated SE - */ -static int incrypto34_restore_security_env(sc_card_t *card, int se_num) -{ - sc_apdu_t apdu; - int r; - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0xF3, se_num); - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - -/* - * Set the security context - * Things get a little messy here. It seems you cannot do any - * crypto without a security environment - but there isn't really - * a way to specify the security environment in PKCS15. - * What I'm doing here (for now) is to assume that for a key - * object with ID 0xNN there is always a corresponding SE object - * with the same ID. - * XXX Need to find out how the Aladdin drivers do it. - */ -static int incrypto34_set_security_env(sc_card_t *card, - const sc_security_env_t *env, int se_num) -{ - sc_apdu_t apdu; - u8 data[3]; - int key_id, r; - - assert(card != NULL && env != NULL); - - if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) - || env->key_ref_len != 1) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No or invalid key reference\n"); - return SC_ERROR_INVALID_ARGUMENTS; - } - key_id = env->key_ref[0]; - - r = incrypto34_restore_security_env(card, 1); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF1, 0); - switch (env->operation) { - case SC_SEC_OPERATION_DECIPHER: - apdu.p2 = 0xB8; - break; - case SC_SEC_OPERATION_SIGN: - apdu.p2 = 0xB6; - break; - default: - return SC_ERROR_INVALID_ARGUMENTS; - } - - data[0] = 0x83; - data[1] = 0x01; - data[2] = key_id; - apdu.lc = apdu.datalen = 3; - apdu.data = data; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - -/* - * Compute digital signature - */ - -/* internal function to do the actual signature computation */ -static int do_compute_signature(sc_card_t *card, - const u8 *data, size_t datalen, u8 *out, size_t outlen) -{ - int r; - sc_apdu_t apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - - if (datalen > SC_MAX_APDU_BUFFER_SIZE || - outlen > SC_MAX_APDU_BUFFER_SIZE) - return SC_ERROR_INTERNAL; - - /* INS: 0x2A PERFORM SECURITY OPERATION - * P1: 0x9E Resp: Digital Signature - * P2: 0x9A Cmd: Input for Digital Signature */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); - apdu.resp = rbuf; - apdu.le = outlen; - apdu.resplen = sizeof(rbuf); - - memcpy(sbuf, data, datalen); - apdu.data = sbuf; - apdu.lc = datalen; - apdu.datalen = datalen; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - - if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { - memcpy(out, rbuf, outlen); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen); - } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); -} - -static int -incrypto34_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, - u8 *out, size_t outlen) -{ - int r; - u8 buf[SC_MAX_APDU_BUFFER_SIZE]; - size_t buf_len = sizeof(buf), tmp_len = buf_len; - sc_context_t *ctx; - - assert(card != NULL && data != NULL && out != NULL); - ctx = card->ctx; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); - - if (datalen > 255) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); - if (outlen < datalen) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_BUFFER_TOO_SMALL); - outlen = datalen; - - /* XXX As we don't know what operations are allowed with a - * certain key, let's try RSA_PURE etc. and see which operation - * succeeds (this is not really beautiful, but currently the - * only way I see) -- Nils - */ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "trying RSA_PURE_SIG (padded DigestInfo)\n"); - r = do_compute_signature(card, data, datalen, out, outlen); - if (r >= SC_SUCCESS) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "trying RSA_SIG (just the DigestInfo)\n"); - /* remove padding: first try pkcs1 bt01 padding */ - r = sc_pkcs1_strip_01_padding(ctx, data, datalen, buf, &tmp_len); - if (r != SC_SUCCESS) { - const u8 *p = data; - /* no pkcs1 bt01 padding => let's try zero padding. - * This can only work if the data tbs doesn't have a - * leading 0 byte. */ - tmp_len = buf_len; - while (*p == 0 && tmp_len != 0) { - ++p; - --tmp_len; - } - memcpy(buf, p, tmp_len); - } - r = do_compute_signature(card, buf, tmp_len, out, outlen); - if (r >= SC_SUCCESS) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "trying to sign raw hash value\n"); - r = sc_pkcs1_strip_digest_info_prefix(NULL,buf,tmp_len,buf,&buf_len); - if (r != SC_SUCCESS) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); - return do_compute_signature(card, buf, buf_len, out, outlen); -} - -static int -incrypto34_lifecycle_get(sc_card_t *card, int *mode) -{ - sc_apdu_t apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - int r; - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 01, 0x83); - apdu.cla = 0x00; - apdu.le = 256; - apdu.resplen = sizeof(rbuf); - apdu.resp = rbuf; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - if (apdu.resplen < 1) { - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Lifecycle byte not in response"); - } - - r = SC_SUCCESS; - switch (rbuf[0]) { - case 0x10: - *mode = SC_CARDCTRL_LIFECYCLE_USER; - break; - case 0x20: - *mode = SC_CARDCTRL_LIFECYCLE_ADMIN; - break; - case 0x34: /* MANUFACTURING */ - *mode = SC_CARDCTRL_LIFECYCLE_OTHER; - break; - default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown lifecycle byte %d", rbuf[0]); - r = SC_ERROR_INTERNAL; - } - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - -static int -incrypto34_put_data_oci(sc_card_t *card, - struct sc_cardctl_incrypto34_obj_info *args) -{ - sc_apdu_t apdu; - int r; - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - - memset(&apdu, 0, sizeof(apdu)); - apdu.cse = SC_APDU_CASE_3_SHORT; - apdu.cla = 0x00; - apdu.ins = 0xda; - apdu.p1 = 0x01; - apdu.p2 = 0x6e; - apdu.lc = args->len; - apdu.data = args->data; - apdu.datalen = args->len; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - -static int -incrypto34_change_key_data(struct sc_card *card, - struct sc_cardctl_incrypto34_obj_info *args) -{ - struct sc_apdu apdu; - int r; - - memset(&apdu, 0, sizeof(apdu)); - apdu.cse = SC_APDU_CASE_3_SHORT; - apdu.cla = 0x90; - apdu.ins = 0x24; - apdu.p1 = args->key_class; - apdu.p2 = args->key_id; - apdu.lc = args->len; - apdu.data = args->data; - apdu.datalen = args->len; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - return r; -} - -static int -incrypto34_put_data_seci(sc_card_t *card, - struct sc_cardctl_incrypto34_obj_info *args) -{ - sc_apdu_t apdu; - int r; - - memset(&apdu, 0, sizeof(apdu)); - apdu.cse = SC_APDU_CASE_3_SHORT; - apdu.cla = 0x00; - apdu.ins = 0xda; - apdu.p1 = 0x01; - apdu.p2 = 0x6d; - apdu.lc = args->len; - apdu.data = args->data; - apdu.datalen = args->len; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - return r; -} - -static int -incrypto34_generate_key(sc_card_t *card, - struct sc_cardctl_incrypto34_genkey_info *args) -{ - sc_apdu_t apdu; - u8 data[8]; - int r; - - data[0] = 0x20; /* store as PSO object */ - data[1] = args->key_id; - data[2] = args->fid >> 8; - data[3] = args->fid & 0xff; - data[4] = 0; /* additional Rabin Miller tests */ - data[5] = 0x10; /* length difference between p, q (bits) */ - data[6] = 0; /* default length of exponent, MSB */ - data[7] = 0x20; /* default length of exponent, LSB */ - - memset(&apdu, 0, sizeof(apdu)); - apdu.cse = SC_APDU_CASE_3_SHORT; - apdu.cla = 0x00; - apdu.ins = 0x46; - apdu.p1 = 0x00; - apdu.p2 = args->key_id;/* doc is not clear, it just says "ID" */ - apdu.le = 0x00; - apdu.data= data; - apdu.datalen = apdu.lc = sizeof(data); - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "GENERATE_KEY failed"); - - return r; -} - -static int -incrypto34_erase_files(sc_card_t *card) -{ - sc_apdu_t apdu; - int r; - static u8 pCreateAtrFile[] = { - 0x62, 0x1b, - 0x80, 0x02, 0x00, 0x1e, - 0x82, 0x03, 0x01, 0xff, 0xff, - 0x83, 0x02, 0x2f, 0x01, - 0x85, 0x01, 0x01, - 0x86, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - static u8 pWriteAtr[] = { 0x19, 0x3b, 0xff, - 0x18, 0x00, 0xff, 0x81, 0x31, 0xfe, 0x55, 0x00, - 0x6b, 0x02, 0x09, 0x02, 0x00, 0x01, 0x01, 0x01, - 0x43, 0x4e, 0x53, 0x10, 0x31, 0x80, 0x9f }; - static u8 pCreateEF_DIR_ADOFile[] = { 0x6F, 0x1D, - 0x83, 0x02, - 0xFD, 0x01, 0x85, 0x03, 0x01, 0xff, 0xff, - 0x81, 0x02, - 0x00, 0x64, - 0x82, 0x03, 0x05, 0xff, 0xff, - 0x86, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - - /* Erasing Filesystem */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xf5, 0, 0); - apdu.cla = 0xb0; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error Erasing Filesystem"); - - /* Creating ATR file*/ - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe0, 0, 0); - apdu.data = pCreateAtrFile; - apdu.datalen = apdu.lc = sizeof(pCreateAtrFile); - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error Creating ATR file"); - - /* Filling ATR file*/ - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xd6, 0, 0); - apdu.data = pWriteAtr; - apdu.datalen = apdu.lc = sizeof(pWriteAtr); - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error Filling ATR file"); - - /* Creating DIR-ADO file*/ - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe0, 0, 0); - apdu.data = pCreateEF_DIR_ADOFile; - apdu.datalen = apdu.lc = sizeof(pCreateEF_DIR_ADOFile); - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error Creating DIR-ADO file"); - - return r; - -} - - -static int -incrypto34_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) -{ - switch (cmd) { - case SC_CARDCTL_INCRYPTO34_PUT_DATA_FCI: - break; - case SC_CARDCTL_INCRYPTO34_PUT_DATA_OCI: - return incrypto34_put_data_oci(card, - (struct sc_cardctl_incrypto34_obj_info *) ptr); - break; - case SC_CARDCTL_INCRYPTO34_PUT_DATA_SECI: - return incrypto34_put_data_seci(card, - (struct sc_cardctl_incrypto34_obj_info *) ptr); - break; - case SC_CARDCTL_INCRYPTO34_GENERATE_KEY: - return incrypto34_generate_key(card, - (struct sc_cardctl_incrypto34_genkey_info *) ptr); - case SC_CARDCTL_LIFECYCLE_GET: - return incrypto34_lifecycle_get(card, (int *) ptr); - case SC_CARDCTL_LIFECYCLE_SET: - return 0; - case SC_CARDCTL_INCRYPTO34_CHANGE_KEY_DATA: - return incrypto34_change_key_data(card, (struct sc_cardctl_incrypto34_obj_info*) ptr); - case SC_CARDCTL_INCRYPTO34_ERASE_FILES: - return incrypto34_erase_files(card); - } - return SC_ERROR_NOT_SUPPORTED; -} - -/* - * The 0x80 thing tells the card it's okay to search parent - * directories as well for the referenced object. - * Unfortunately, it doesn't seem to work without this flag :-/ - */ -static int -incrypto34_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, - int *tries_left) -{ - data->flags |= SC_PIN_CMD_NEED_PADDING; - data->pin_reference |= 0x80; - /* FIXME: the following values depend on what pin length was - * used when creating the BS objects */ - if (data->pin1.max_length == 0) - data->pin1.max_length = 8; - if (data->pin2.max_length == 0) - data->pin2.max_length = 8; - return iso_ops->pin_cmd(card, data, tries_left); -} - - - -static struct sc_card_driver * sc_get_driver(void) -{ - if (iso_ops == NULL) - iso_ops = sc_get_iso7816_driver()->ops; - incrypto34_ops = *iso_ops; - incrypto34_ops.match_card = incrypto34_match_card; - incrypto34_ops.init = incrypto34_init; - incrypto34_ops.select_file = incrypto34_select_file; - incrypto34_ops.create_file = incrypto34_create_file; - incrypto34_ops.set_security_env = incrypto34_set_security_env; - incrypto34_ops.restore_security_env = incrypto34_restore_security_env; - incrypto34_ops.compute_signature = incrypto34_compute_signature; - - incrypto34_ops.list_files = incrypto34_list_files; - incrypto34_ops.check_sw = incrypto34_check_sw; - incrypto34_ops.card_ctl = incrypto34_card_ctl; - incrypto34_ops.pin_cmd = incrypto34_pin_cmd; - - return &incrypto34_drv; -} - -#if 1 -struct sc_card_driver * sc_get_incrypto34_driver(void) -{ - return sc_get_driver(); -} -#endif diff --git a/src/libopensc/card-isoApplet.c b/src/libopensc/card-isoApplet.c index 2682608f74..55355c682b 100644 --- a/src/libopensc/card-isoApplet.c +++ b/src/libopensc/card-isoApplet.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include @@ -31,27 +31,30 @@ #define ISOAPPLET_ALG_REF_ECDSA 0x21 #define ISOAPPLET_ALG_REF_RSA_PAD_PKCS1 0x11 +#define ISOAPPLET_ALG_REF_RSA_PAD_PSS 0x12 -#define ISOAPPLET_API_VERSION_MAJOR 0x00 -#define ISOAPPLET_API_VERSION_MINOR 0x06 +#define ISOAPPLET_VERSION_V0 0x0006 +#define ISOAPPLET_VERSION_V1 0x0100 #define ISOAPPLET_API_FEATURE_EXT_APDU 0x01 #define ISOAPPLET_API_FEATURE_SECURE_RANDOM 0x02 #define ISOAPPLET_API_FEATURE_ECC 0x04 +#define ISOAPPLET_API_FEATURE_RSA_PSS 0x08 +#define ISOAPPLET_API_FEATURE_RSA_4096 0x20 -#define ISOAPPLET_AID_LEN 12 static const u8 isoApplet_aid[] = {0xf2,0x76,0xa2,0x88,0xbc,0xfb,0xa6,0x9d,0x34,0xf3,0x10,0x01}; struct isoApplet_drv_data { /* Save the current algorithm reference * (ISOAPPLET_ALG_REF_ECDSA, ISOAPPLET_ALG_REF_RSA_PAD_PKCS1) - * to be able to distiguish between RSA and ECC operations. + * to be able to distinguish between RSA and ECC operations. * If ECC is being used, the signatures generated by the card * have to be modified. */ unsigned int sec_env_alg_ref; - unsigned int sec_env_ec_field_length; + unsigned long sec_env_ec_field_length; unsigned int isoapplet_version; + unsigned int isoapplet_features; }; #define DRVDATA(card) ((struct isoApplet_drv_data *) ((card)->drv_data)) @@ -89,51 +92,6 @@ static struct isoapplet_supported_ec_curves { {{{-1}}, 0, 0} /* This entry must not be touched. */ }; -/* - * SELECT an applet on the smartcard. (Not in the emulated filesystem.) - * The response will be written to resp. - * - * @param[in] card - * @param[in] aid The applet ID. - * @param[in] aid_len The legth of aid. - * @param[out] resp The response of the applet upon selection. - * @param[in,out] resp_len In: The buffer size of resp. Out: The length of the response. - * - * @return SC_SUCCESS: The applet is present and could be selected. - * any other: Transmit failure or the card returned an error. - * The card will return an error when the applet is - * not present. - */ -static int -isoApplet_select_applet(sc_card_t *card, const u8 *aid, const size_t aid_len, u8 *resp, size_t *resp_len) -{ - int rv; - sc_context_t *ctx = card->ctx; - sc_apdu_t apdu; - - LOG_FUNC_CALLED(card->ctx); - - if(aid_len > SC_MAX_APDU_BUFFER_SIZE) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); - - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xa4, 0x04, 0x00); - apdu.lc = aid_len; - apdu.data = aid; - apdu.datalen = aid_len; - apdu.resp = resp; - apdu.resplen = *resp_len; - apdu.le = 0; - - rv = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(ctx, rv, "APDU transmit faiure."); - - rv = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(card->ctx, rv, "Card returned error"); - - *resp_len = apdu.resplen; - LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); -} - static int isoApplet_finish(sc_card_t *card) { @@ -151,54 +109,54 @@ isoApplet_finish(sc_card_t *card) static int isoApplet_match_card(sc_card_t *card) { - size_t rlen = SC_MAX_APDU_BUFFER_SIZE; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int rv; - rv = isoApplet_select_applet(card, isoApplet_aid, ISOAPPLET_AID_LEN, rbuf, &rlen); - + rv = iso7816_select_aid(card, isoApplet_aid, sizeof(isoApplet_aid), NULL, NULL); if(rv != SC_SUCCESS) { return 0; } - /* The IsoApplet should return an API version (major and minor) and a feature bitmap. - * We expect 3 bytes: MAJOR API version - MINOR API version - API feature bitmap. - * If applet does not return API version, versions 0x00 will match */ - if(rlen < 3) - { - assert(sizeof(rbuf) >= 3); - memset(rbuf, 0x00, 3); + return 1; +} + +static int +isoApplet_get_info(sc_card_t * card, struct isoApplet_drv_data * drvdata) { + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + int rv; + sc_context_t * ctx = card->ctx; + + rv = sc_get_data(card, 0x0101, rbuf, 3); + if(rv == SC_ERROR_INS_NOT_SUPPORTED) { + /* INS not supported. This is an older IsoApplet that might return the + * applet information upon selection. For backward compatibility, try this. */ + size_t rlen = sizeof(rbuf); + rv = iso7816_select_aid(card, isoApplet_aid, sizeof(isoApplet_aid), rbuf, &rlen); + LOG_TEST_RET(ctx, rv, "Error selecting applet."); + rv = (int)rlen; } - if(rbuf[0] != ISOAPPLET_API_VERSION_MAJOR) - { - sc_log(card->ctx, "IsoApplet: Mismatching major API version. Not proceeding. " - "API versions: Driver (%02X-%02X), applet (%02X-%02X). Please update accordingly.", - ISOAPPLET_API_VERSION_MAJOR, ISOAPPLET_API_VERSION_MINOR, rbuf[0], rbuf[1]); - return 0; + if (rv < 0) { + LOG_TEST_RET(ctx, rv, "Card returned error."); } - if(rbuf[1] != ISOAPPLET_API_VERSION_MINOR) + /* Fill up drvdata */ + if(rv >= 3) { - sc_log(card->ctx, "IsoApplet: Mismatching minor API version. Proceeding anyway. " - "API versions: Driver (%02X-%02X), applet (%02X-%02X). " - "Please update accordingly whenever possible.", - ISOAPPLET_API_VERSION_MAJOR, ISOAPPLET_API_VERSION_MINOR, rbuf[0], rbuf[1]); + drvdata->isoapplet_version = rbuf[0] << 8 | rbuf[1]; + drvdata->isoapplet_features = rbuf[2]; } - return 1; + return SC_SUCCESS; } static int isoApplet_init(sc_card_t *card) { - int r; - int i; + int i, r; + unsigned int major_version = 0; unsigned long flags = 0; unsigned long ext_flags = 0; - size_t rlen = SC_MAX_APDU_BUFFER_SIZE; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; struct isoApplet_drv_data *drvdata; LOG_FUNC_CALLED(card->ctx); @@ -211,19 +169,32 @@ isoApplet_init(sc_card_t *card) card->cla = 0x00; /* Obtain applet version and specific features */ - r = isoApplet_select_applet(card, isoApplet_aid, ISOAPPLET_AID_LEN, rbuf, &rlen); - LOG_TEST_RET(card->ctx, r, "Error obtaining applet version."); - if(rlen < 3) + r = isoApplet_get_info(card, drvdata); + LOG_TEST_GOTO_ERR(card->ctx, r, "Error obtaining information about applet."); + + major_version = drvdata->isoapplet_version & 0xFF00; + if(major_version != (ISOAPPLET_VERSION_V0 & 0xFF00) && major_version != (ISOAPPLET_VERSION_V1 & 0xFF00)) + { + sc_log(card->ctx, "IsoApplet: Mismatching major API version. Not proceeding. " + "API versions: Driver (%04X or %04X), applet (%04X). Please update accordingly.", + ISOAPPLET_VERSION_V0, ISOAPPLET_VERSION_V1, drvdata->isoapplet_version); + r = SC_ERROR_INVALID_CARD; + goto err; + } + else if(drvdata->isoapplet_version != ISOAPPLET_VERSION_V0 && drvdata->isoapplet_version != ISOAPPLET_VERSION_V1) { - assert(sizeof(rbuf) >= 3); - memset(rbuf, 0x00, 3); + sc_log(card->ctx, "IsoApplet: Mismatching minor version. Proceeding anyway. " + "API versions: Driver (%04X or %04X), applet (%04X). " + "Please update accordingly whenever possible.", + ISOAPPLET_VERSION_V0, ISOAPPLET_VERSION_V1, drvdata->isoapplet_version); } - drvdata->isoapplet_version = ((unsigned int)rbuf[0] << 8) | rbuf[1]; - if(rbuf[2] & ISOAPPLET_API_FEATURE_EXT_APDU) + + if(drvdata->isoapplet_features & ISOAPPLET_API_FEATURE_EXT_APDU) card->caps |= SC_CARD_CAP_APDU_EXT; - if(rbuf[2] & ISOAPPLET_API_FEATURE_SECURE_RANDOM) + if(drvdata->isoapplet_features & ISOAPPLET_API_FEATURE_SECURE_RANDOM) card->caps |= SC_CARD_CAP_RNG; - if(drvdata->isoapplet_version <= 0x0005 || rbuf[2] & ISOAPPLET_API_FEATURE_ECC) + if(drvdata->isoapplet_version <= 0x0005 + || drvdata->isoapplet_features & ISOAPPLET_API_FEATURE_ECC) { /* There are Java Cards that do not support ECDSA at all. The IsoApplet * started to report this with version 00.06. @@ -232,8 +203,12 @@ isoApplet_init(sc_card_t *card) * should be kept in sync with the explicit parameters in the pkcs15-init * driver. */ flags = 0; - flags |= SC_ALGORITHM_ECDSA_RAW; - flags |= SC_ALGORITHM_ECDSA_HASH_SHA1; + if (major_version == (ISOAPPLET_VERSION_V0 & 0xFF00)) { + flags |= SC_ALGORITHM_ECDSA_HASH_SHA1; + } else { // ISOAPPLET_VERSION_V1 + flags |= SC_ALGORITHM_ECDSA_RAW; + flags |= SC_ALGORITHM_ECDSA_HASH_NONE; + } flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; ext_flags = SC_ALGORITHM_EXT_EC_UNCOMPRESES; ext_flags |= SC_ALGORITHM_EXT_EC_NAMEDCURVE; @@ -247,16 +222,23 @@ isoApplet_init(sc_card_t *card) /* RSA */ flags = 0; - /* Padding schemes: */ flags |= SC_ALGORITHM_RSA_PAD_PKCS1; - /* Hashes are to be done by the host for RSA */ flags |= SC_ALGORITHM_RSA_HASH_NONE; + if(drvdata->isoapplet_features & ISOAPPLET_API_FEATURE_RSA_PSS) { + flags |= SC_ALGORITHM_RSA_PAD_PSS; + } /* Key-generation: */ flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; /* Modulus lengths: */ _sc_card_add_rsa_alg(card, 2048, flags, 0); + if (drvdata->isoapplet_features & ISOAPPLET_API_FEATURE_RSA_4096) { + _sc_card_add_rsa_alg(card, 4096, flags, 0); + } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +err: + free(drvdata); + LOG_FUNC_RETURN(card->ctx, r); } /* @@ -479,7 +461,7 @@ isoApplet_process_fci(sc_card_t *card, sc_file_t *file, } /* - * @brief Encode the EC parameters as a concatenation of TLV enrties. + * @brief Encode the EC parameters as a concatenation of TLV entries. * * The format is: * 81 - prime @@ -546,8 +528,8 @@ isoApplet_ctl_generate_key(sc_card_t *card, sc_cardctl_isoApplet_genkey_t *args) { int r; sc_apdu_t apdu; - u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; - u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; + u8 rbuf[SC_MAX_EXT_APDU_RESP_SIZE]; + u8 sbuf[SC_MAX_EXT_APDU_DATA_SIZE]; u8 *p; const u8 *inner_tag_value; const u8 *outer_tag_value; @@ -555,6 +537,7 @@ isoApplet_ctl_generate_key(sc_card_t *card, sc_cardctl_isoApplet_genkey_t *args) size_t outer_tag_len; size_t inner_tag_len; unsigned int cla; + size_t sz; LOG_FUNC_CALLED(card->ctx); @@ -570,11 +553,11 @@ isoApplet_ctl_generate_key(sc_card_t *card, sc_cardctl_isoApplet_genkey_t *args) *p++ = 0x01; *p++ = args->priv_key_ref; - r = p - sbuf; + sz = p - sbuf; p = NULL; - apdu.lc = r; - apdu.datalen = r; + apdu.lc = sz; + apdu.datalen = sz; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); @@ -589,7 +572,7 @@ isoApplet_ctl_generate_key(sc_card_t *card, sc_cardctl_isoApplet_genkey_t *args) * There are two cases: * 1) The card can do ext. apdus: The data fits in one apdu. * 2) The card can't do ext. apdus: sc_transmit_apdu will handle that - the - * card will send SW_BYTES_REMAINING, OpenSC will automaticall do a + * card will send SW_BYTES_REMAINING, OpenSC will automatically do a * GET RESPONSE to get the remaining data, and will append it to the data * buffer. */ if(args->algorithm_ref == SC_ISOAPPLET_ALG_REF_EC_GEN) @@ -615,7 +598,11 @@ isoApplet_ctl_generate_key(sc_card_t *card, sc_cardctl_isoApplet_genkey_t *args) apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); - apdu.le = 256; + if (card->caps & SC_CARD_CAP_APDU_EXT) { + apdu.le = apdu.resplen; + } else { + apdu.le = 256; + } r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); @@ -644,9 +631,11 @@ isoApplet_ctl_generate_key(sc_card_t *card, sc_cardctl_isoApplet_genkey_t *args) { case SC_ISOAPPLET_ALG_REF_RSA_GEN_2048: + case SC_ISOAPPLET_ALG_REF_RSA_GEN_4096: /* Search for the modulus tag (81). */ inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x81, &inner_tag_len); - if(inner_tag_value == NULL || inner_tag_len != 256) + const size_t expected_modulus_len = args->algorithm_ref == SC_ISOAPPLET_ALG_REF_RSA_GEN_2048 ? 256 : 512; + if(inner_tag_value == NULL || inner_tag_len != expected_modulus_len) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid modulus."); } @@ -739,7 +728,7 @@ static int isoApplet_put_data_prkey_rsa(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args) { sc_apdu_t apdu; - u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; + u8 sbuf[SC_MAX_EXT_APDU_DATA_SIZE]; u8 *p = NULL; int r; size_t tags_len; @@ -864,7 +853,7 @@ static int isoApplet_put_data_prkey_ec(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args) { sc_apdu_t apdu; - u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; + u8 sbuf[SC_MAX_EXT_APDU_DATA_SIZE]; int r; u8 *p; size_t tags_len; @@ -928,6 +917,10 @@ isoApplet_put_data_prkey_ec(sc_card_t *card, sc_cardctl_isoApplet_import_key_t * apdu.lc = p - sbuf; apdu.datalen = p - sbuf; apdu.data = sbuf; + if ((apdu.datalen > 255) && !(card->caps & SC_CARD_CAP_APDU_EXT)) + { + apdu.flags |= SC_APDU_FLAGS_CHAINING; + } r = sc_transmit_apdu(card, &apdu); if(r < 0) { @@ -974,6 +967,7 @@ static int isoApplet_ctl_import_key(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args) { int r; + size_t sz; sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 *p; @@ -988,7 +982,7 @@ isoApplet_ctl_import_key(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *arg * * The first step is to perform a MANAGE SECURITY ENVIRONMENT as it would be done * with on-card key generation. The second step is PUT DATA (instead of - * GENERATE ASYMMETRIC KEYPAIR). + * GENERATE ASYMMETRIC KEY PAIR). */ /* MANAGE SECURITY ENVIRONMENT (SET). Set the algorithm and key references. */ @@ -1003,11 +997,11 @@ isoApplet_ctl_import_key(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *arg *p++ = 0x01; *p++ = args->priv_key_ref; - r = p - sbuf; + sz = p - sbuf; p = NULL; - apdu.lc = r; - apdu.datalen = r; + apdu.lc = sz; + apdu.datalen = sz; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); @@ -1032,7 +1026,7 @@ isoApplet_ctl_import_key(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *arg break; default: - LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Uknown algorithm refernce."); + LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unknown algorithm reference."); } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); @@ -1068,16 +1062,22 @@ isoApplet_set_security_env(sc_card_t *card, u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 *p; int r; - struct isoApplet_drv_data *drvdata = DRVDATA(card); + size_t sz; + struct isoApplet_drv_data *drvdata = NULL; + + if (card == NULL || env == NULL) { + return SC_ERROR_INTERNAL; + } LOG_FUNC_CALLED(card->ctx); + drvdata = DRVDATA(card); + if(se_num != 0) { LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "IsoApplet does not support storing of security environments."); } - assert(card != NULL && env != NULL); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0); switch (env->operation) { @@ -1103,22 +1103,19 @@ isoApplet_set_security_env(sc_card_t *card, { drvdata->sec_env_alg_ref = ISOAPPLET_ALG_REF_RSA_PAD_PKCS1; } + else if( env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PSS ) + { + drvdata->sec_env_alg_ref = ISOAPPLET_ALG_REF_RSA_PAD_PSS; + } else { - LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "IsoApplet only supports RSA with PKCS1 padding."); + LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "IsoApplet does not support requested padding/hash combination"); } break; case SC_ALGORITHM_EC: - if( env->algorithm_flags & SC_ALGORITHM_ECDSA_RAW ) - { - drvdata->sec_env_alg_ref = ISOAPPLET_ALG_REF_ECDSA; - drvdata->sec_env_ec_field_length = env->algorithm_ref; - } - else - { - LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "IsoApplet only supports raw ECDSA."); - } + drvdata->sec_env_alg_ref = ISOAPPLET_ALG_REF_ECDSA; + drvdata->sec_env_ec_field_length = env->algorithm_ref; break; default: @@ -1134,26 +1131,29 @@ isoApplet_set_security_env(sc_card_t *card, { *p++ = 0x81; *p++ = env->file_ref.len; - assert(sizeof(sbuf) - (p - sbuf) >= env->file_ref.len); + if (sizeof(sbuf) - (p - sbuf) < env->file_ref.len) + return SC_ERROR_INTERNAL; memcpy(p, env->file_ref.value, env->file_ref.len); p += env->file_ref.len; } if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { - if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) + if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) *p++ = 0x83; else *p++ = 0x84; *p++ = env->key_ref_len; - assert(sizeof(sbuf) - (p - sbuf) >= env->key_ref_len); + if (sizeof(sbuf) - (p - sbuf) < env->key_ref_len) + return SC_ERROR_INTERNAL; memcpy(p, env->key_ref, env->key_ref_len); p += env->key_ref_len; } - r = p - sbuf; - apdu.lc = r; - apdu.datalen = r; + sz = p - sbuf; + apdu.lc = sz; + apdu.datalen = sz; apdu.data = sbuf; + r = (int)sz; if (apdu.datalen != 0) { @@ -1177,33 +1177,46 @@ isoApplet_compute_signature(struct sc_card *card, LOG_FUNC_CALLED(ctx); - r = iso_ops->compute_signature(card, data, datalen, out, outlen); - if(r < 0) - { - LOG_FUNC_RETURN(ctx, r); - } - - /* If ECDSA was used, the ASN.1 sequence of integers R,S returned by the - * card needs to be converted to the raw concatenation of R,S for PKCS#11. */ - if(drvdata->sec_env_alg_ref == ISOAPPLET_ALG_REF_ECDSA) - { - u8* p = NULL; - size_t len = (drvdata->sec_env_ec_field_length + 7) / 8 * 2; + if (drvdata->sec_env_alg_ref == ISOAPPLET_ALG_REF_RSA_PAD_PSS) { + // For RSA-PSS signature schemes the IsoApplet expects only the hash. + u8 tmp[64]; // large enough for SHA512 + size_t tmplen = sizeof(tmp); + r = sc_pkcs1_strip_digest_info_prefix(NULL, data, datalen, tmp, &tmplen); + if (r == SC_SUCCESS) { + r = iso_ops->compute_signature(card, tmp, tmplen, out, outlen); + } else { + /* No digest info present? Use the value as it is */ + r = iso_ops->compute_signature(card, data, datalen, out, outlen); + } + } else if (drvdata->sec_env_alg_ref == ISOAPPLET_ALG_REF_ECDSA) { + /* + * The card returns ECDSA signatures as an ASN.1 sequence of integers R,S + * while PKCS#11 expects the raw concatenation of R,S for PKCS#11. + * We cannot expect the caller to provide an out buffer that is large enough for the ASN.1 sequence. + * Therefore, we allocate a temporary buffer for the card output, and then convert it to raw R,S. + * The card supports no curves with field sizes larger than 384bit (EC:secp384r1 which yields an ASN.1 + * encoded signature of 104 byte: + * R and S = 384 bit = 48 byte + 1 zero byte if the first bit is set (otherwise they are interpreted as negative). + * Seq-Tag&Len (2 bytes) + R-Tag&Len (2 bytes) + R (49 bytes) + S-Tag&Len (2 bytes) + S (49 bytes) + */ + u8 seqbuf[104]; + size_t seqlen = sizeof(seqbuf); + r = iso_ops->compute_signature(card, data, datalen, seqbuf, seqlen); + + if (r < 0) { + LOG_FUNC_RETURN(ctx, r); + } + /* Convert ASN.1 sequence of integers R,S to the raw concatenation of R,S for PKCS#11. */ + size_t len = BYTES4BITS(drvdata->sec_env_ec_field_length) * 2; if (len > outlen) LOG_FUNC_RETURN(ctx, SC_ERROR_BUFFER_TOO_SMALL); - p = calloc(1,len); - if (!p) - LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); - - r = sc_asn1_sig_value_sequence_to_rs(ctx, out, r, p, len); - if (!r) { - memcpy(out, p, len); - r = len; - } - - free(p); + r = sc_asn1_sig_value_sequence_to_rs(ctx, seqbuf, r, out, len); + LOG_TEST_RET(ctx, r, "Failed to convert ASN.1 signature to raw RS"); + r = (int)len; + } else { + r = iso_ops->compute_signature(card, data, datalen, out, outlen); } LOG_FUNC_RETURN(ctx, r); } @@ -1211,17 +1224,35 @@ isoApplet_compute_signature(struct sc_card *card, static int isoApplet_get_challenge(struct sc_card *card, u8 *rnd, size_t len) { - struct sc_context *ctx = card->ctx; int r; - LOG_FUNC_CALLED(ctx); + LOG_FUNC_CALLED(card->ctx); - if(card->caps & SC_CARD_CAP_RNG) { + if(card->caps & SC_CARD_CAP_RNG) { r = iso_ops->get_challenge(card, rnd, len); } else { r = SC_ERROR_NOT_SUPPORTED; } - LOG_FUNC_RETURN(ctx, r); + + LOG_FUNC_RETURN(card->ctx, r); +} + +static int isoApplet_card_reader_lock_obtained(sc_card_t *card, int was_reset) +{ + int r = SC_SUCCESS; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (was_reset > 0) { + r = iso7816_select_aid(card, isoApplet_aid, sizeof(isoApplet_aid), NULL, NULL); + } + + LOG_FUNC_RETURN(card->ctx, r); +} + +static int isoApplet_logout(sc_card_t *card) +{ + return iso7816_select_aid(card, isoApplet_aid, sizeof(isoApplet_aid), NULL, NULL); } static struct sc_card_driver *sc_get_driver(void) @@ -1246,6 +1277,8 @@ static struct sc_card_driver *sc_get_driver(void) isoApplet_ops.set_security_env = isoApplet_set_security_env; isoApplet_ops.compute_signature = isoApplet_compute_signature; isoApplet_ops.get_challenge = isoApplet_get_challenge; + isoApplet_ops.card_reader_lock_obtained = isoApplet_card_reader_lock_obtained; + isoApplet_ops.logout = isoApplet_logout; /* unsupported functions */ isoApplet_ops.write_binary = NULL; diff --git a/src/libopensc/card-itacns.c b/src/libopensc/card-itacns.c index f322f8950e..095ce97bd0 100644 --- a/src/libopensc/card-itacns.c +++ b/src/libopensc/card-itacns.c @@ -18,7 +18,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* @@ -51,144 +51,83 @@ static struct sc_card_driver itacns_drv = { /* List of ATR's for "hard" matching. */ -static struct sc_atr_table itacns_atrs[] = { +static const struct sc_atr_table itacns_atrs[] = { { "3b:f4:18:00:ff:81:31:80:55:00:31:80:00:c7", NULL, NULL, SC_CARD_TYPE_ITACNS_CIE_V1, 0, NULL}, + { "3b:8b:80:01:00:31:c1:64:00:00:00:00:00:00:00:00", + "ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00", + "Idemia (Oberthur)", SC_CARD_TYPE_ITACNS_CNS_IDEMIA_2021, 0, NULL}, { NULL, NULL, NULL, 0, 0, NULL} }; -/* Output debug info */ -#define matchdebug(idx, c) do { \ - sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, \ - "Matching %x against atr[%d] == %x", c, idx, atr[idx]); \ - } while(0); - -/* Check that we are not looking at values beyond the ATR's length. - * If we are, then the card does not match. */ -#define itacns_atr_l(idx) do {if (idx >= card->atr.len) return 0;} while(0); - -/* Match byte exactly and increment index. */ -#define itacns_atr_match(idx, c) do { \ - itacns_atr_l(idx); \ - matchdebug(idx, c); \ - if (((u8)atr[idx]) != c) return 0; \ - idx++; \ - } while(0); - -/* Match masked bits and increment index. */ -#define itacns_atr_mmatch(idx, c, mask) do { \ - itacns_atr_l(idx); \ - if ((((u8)atr[idx]) & mask) != c) return 0; \ - idx ++; \ - } while(0); - /* Macro to access private driver data. */ #define DRVDATA(card) ((itacns_drv_data_t *) card->drv_data) - -static int itacns_match_cns_card(sc_card_t *card, unsigned int i) +static void itacns_init_cns_card(sc_card_t *card) { - unsigned char *atr = card->atr.value; - sc_context_t *ctx; - ctx = card->ctx; - + if (15 != card->reader->atr_info.hist_bytes_len) + return; - itacns_atr_match(i, 0x01); /* H7 */ - i += 2; /* H8, H9 */ - itacns_atr_match(i, 'C'); /* H10 */ - itacns_atr_match(i, 'N'); /* H11 */ - itacns_atr_match(i, 'S'); /* H12 */ + u8 cns_version = card->reader->atr_info.hist_bytes[12]; + card->version.hw_major = (cns_version >> 4) & 0x0f; + card->version.hw_minor = cns_version & 0x0f; - /* H13 */ - /* Version byte: h.l, h in the high nibble, l in the low nibble. */ - if(card->driver) { - DRVDATA(card)->cns_version = atr[i]; - } - /* Warn if the version is not 1.0. */ - if(atr[i] != 0x10) { + /* Warn if version is not 1.X. */ + if (cns_version != 0x10 && cns_version != 0x11) { char version[8]; - snprintf(version, sizeof(version), "%d.%d", (atr[i] >> 4) & 0x0f, atr[i] & 0x0f); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "CNS card version %s; no official specifications " - "are published. Proceeding anyway.\n", version); + snprintf(version, sizeof(version), "%d.%d", card->version.hw_major, card->version.hw_minor); + sc_log(card->ctx, "CNS card version %s; no official specifications " + "are published. Proceeding anyway.\n", version); } - i++; +} + +static int itacns_match_cns_card(sc_card_t *card) +{ + u8 manufacturer_code; + u8 manufacturer_mask; + u8 fw_major; - itacns_atr_match(i, 0x31); /* H14 */ - itacns_atr_match(i, 0x80); /* H15 */ + if (15 != card->reader->atr_info.hist_bytes_len || + 0 != memcmp(card->reader->atr_info.hist_bytes+9, "CNS", 3)) + return 0; card->type = SC_CARD_TYPE_ITACNS_CNS; + manufacturer_code = card->reader->atr_info.hist_bytes[2]; + manufacturer_mask = card->reader->atr_info.hist_bytes[3]; + fw_major = card->reader->atr_info.hist_bytes[4]; + + if (manufacturer_code == ITACNS_ICMAN_INFINEON && + manufacturer_mask == ITACNS_MASKMAN_IDEMIA && + fw_major >= 32) { + card->type = SC_CARD_TYPE_ITACNS_CNS_IDEMIA_2021; + } + return 1; } -static int itacns_match_cie_card(sc_card_t *card, unsigned int i) +static int itacns_match_cie_card(sc_card_t *card) { - unsigned char *atr = card->atr.value; - sc_context_t *ctx; - ctx = card->ctx; - - itacns_atr_match(i, 0x02); /* H7 */ - itacns_atr_match(i, 'I'); /* H8 */ - itacns_atr_match(i, 'T'); /* H9 */ - itacns_atr_match(i, 'I'); /* H10 */ - itacns_atr_match(i, 'D'); /* H11 */ - itacns_atr_match(i, 0x20); /* H12 */ - itacns_atr_match(i, 0x20); /* H13 */ - itacns_atr_match(i, 0x31); /* H14 */ - itacns_atr_match(i, 0x80); /* H15 */ + u8 h7_to_h15[] = { 0x02, 'I', 'T', 'I', 'D', 0x20, 0x20, 0x31, 0x80, }; + if (15 != card->reader->atr_info.hist_bytes_len || + 0 != memcmp(card->reader->atr_info.hist_bytes+6, + h7_to_h15, sizeof h7_to_h15)) + return 0; card->type = SC_CARD_TYPE_ITACNS_CIE_V2; - return 1; } static int itacns_match_card(sc_card_t *card) { - unsigned int i = 0; - int r; - unsigned char *atr = card->atr.value; - int td1_idx; - sc_context_t *ctx; - ctx = card->ctx; + int r = 0; /* Try table first */ r = _sc_match_atr(card, itacns_atrs, &card->type); if(r >= 0) return 1; - /* The ATR was not recognized; try to match it - according to the official specs. */ - - /* Check ATR up to byte H6 */ - itacns_atr_match(i, 0x3b); /* TS */ - itacns_atr_mmatch(i, 0x8f, 0x8f); /* T0 */ - /* TA1, TB1, TC1 */ - if(atr[1] & 0x40) i++; - if(atr[1] & 0x20) i++; - if(atr[1] & 0x10) i++; - /* TD1 */ - td1_idx = i; - itacns_atr_mmatch(i, 0x81, 0x8f); - /* TA2, TB2, TC2 */ - if(atr[td1_idx] & 0x40) i++; - if(atr[td1_idx] & 0x20) i++; - if(atr[td1_idx] & 0x10) i++; - /* TD2 */ - itacns_atr_match(i, 0x31); - i += 2; /* TA3, TB3 */ - itacns_atr_match(i, 0x00); /* H1 */ - itacns_atr_match(i, 0x6b); /* H2 */ - /* Store interesting data */ - if(card->driver) { - DRVDATA(card)->ic_manufacturer_code = card->atr.value[i]; - DRVDATA(card)->mask_manufacturer_code = card->atr.value[i+1]; - DRVDATA(card)->os_version_h = card->atr.value[i+2]; - DRVDATA(card)->os_version_l = card->atr.value[i+3]; - } - i += 4; /* H3, H4, H5, H6 */ - - /* Check final part. */ - if (itacns_match_cns_card(card, i)) return 1; - if (itacns_match_cie_card(card, i)) return 1; + if (itacns_match_cns_card(card)) return 1; + if (itacns_match_cie_card(card)) return 1; /* No card type was matched. */ return 0; @@ -211,16 +150,27 @@ static int itacns_init(sc_card_t *card) if (!card->drv_data) return SC_ERROR_OUT_OF_MEMORY; - /* Match ATR again to find the card data. */ - itacns_match_card(card); + if (card->type == SC_CARD_TYPE_ITACNS_CNS) + itacns_init_cns_card(card); + + DRVDATA(card)->ic_manufacturer_code = card->reader->atr_info.hist_bytes[2]; + DRVDATA(card)->mask_manufacturer_code = card->reader->atr_info.hist_bytes[3]; + card->version.fw_major = card->reader->atr_info.hist_bytes[4]; + card->version.fw_minor = card->reader->atr_info.hist_bytes[5]; /* Set up algorithm info. */ flags = SC_ALGORITHM_NEED_USAGE | SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASHES ; - _sc_card_add_rsa_alg(card, 1024, flags, 0); + if ((card->version.hw_major >= 1 && card->version.hw_minor >= 1) || + card->type == SC_CARD_TYPE_ITACNS_CNS_IDEMIA_2021) { + card->caps |= SC_CARD_CAP_APDU_EXT; + _sc_card_add_rsa_alg(card, 2048, flags, 0); + } else { + _sc_card_add_rsa_alg(card, 1024, flags, 0); + } return SC_SUCCESS; } @@ -260,10 +210,10 @@ static int itacns_restore_security_env(sc_card_t *card, int se_num) apdu.le = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); + LOG_TEST_RET(card->ctx, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, 1, r); } @@ -284,15 +234,16 @@ static int itacns_set_security_env(sc_card_t *card, sc_apdu_t apdu; u8 data[3]; int key_id, r; - + /* Do not complain about se_num; the argument is part of the API. */ (void) se_num; - assert(card != NULL && env != NULL); + if (card == NULL || env == NULL) + return SC_ERROR_INTERNAL; if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "No or invalid key reference\n"); return SC_ERROR_INVALID_ARGUMENTS; } @@ -302,7 +253,7 @@ static int itacns_set_security_env(sc_card_t *card, so far want 0x03. */ r = itacns_restore_security_env(card, (card->type == SC_CARD_TYPE_ITACNS_CIE_V1 ? 0x30 : 0x03)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF1, 0); switch (env->operation) { @@ -312,9 +263,6 @@ static int itacns_set_security_env(sc_card_t *card, case SC_SEC_OPERATION_SIGN: apdu.p2 = 0xB6; break; - case SC_SEC_OPERATION_AUTHENTICATE: - apdu.p2 = 0xA4; - break; default: return SC_ERROR_INVALID_ARGUMENTS; } @@ -329,10 +277,10 @@ static int itacns_set_security_env(sc_card_t *card, apdu.data = data; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); + LOG_TEST_RET(card->ctx, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, 1, r); } @@ -345,8 +293,7 @@ static int itacns_set_security_env(sc_card_t *card, * cards by STIncard. */ static int -itacns_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, - int *tries_left) +itacns_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) { data->flags |= SC_PIN_CMD_NEED_PADDING; /* Enable backtracking for STIncard cards. */ @@ -360,27 +307,28 @@ itacns_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, data->pin1.max_length = 8; if (data->pin2.max_length == 0) data->pin2.max_length = 8; - return default_ops->pin_cmd(card, data, tries_left); + return default_ops->pin_cmd(card, data); } static int itacns_read_binary(sc_card_t *card, unsigned int idx, u8 *buf, size_t count, - unsigned long flags) + unsigned long *flags) { size_t already_read = 0; - int requested; + size_t requested; int r; while(1) { requested = count - already_read; if(requested > ITACNS_MAX_PAYLOAD) requested = ITACNS_MAX_PAYLOAD; - r = default_ops->read_binary(card, idx+already_read, + r = default_ops->read_binary(card, (unsigned)(idx + already_read), &buf[already_read], requested, flags); - if(r < 0) return r; + if(r < 0) + return r; already_read += r; - if (r == 0 || r < requested || already_read == count) { + if (r == 0 || (size_t)r < requested || already_read == count) { /* We have finished */ - return already_read; + return (int)already_read; } } } @@ -392,7 +340,8 @@ static int itacns_list_files(sc_card_t *card, u8 *buf, size_t buflen) { == ITACNS_MASKMAN_SIEMENS)) { list_ops = sc_get_cardos_driver()->ops; } else { - list_ops = sc_get_incrypto34_driver()->ops; + // incrypto34 no longer supported + return SC_ERROR_NO_CARD_SUPPORT; } return list_ops->list_files(card, buf, buflen); } @@ -446,7 +395,7 @@ static const int ef_acl[9] = { /* XXX: ADMIN should be an ACL type of its own, or mapped * to erase */ SC_AC_OP_ERASE, /* ADMIN EF (modify meta information?) */ - -1, /* INC (-> cylic fixed files) */ + -1, /* INC (-> cyclic fixed files) */ -1 /* DEC */ }; @@ -478,7 +427,7 @@ static int itacns_select_file(sc_card_t *card, parse_sec_attr((*file), (*file)->sec_attr, (*file)->sec_attr_len); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } static int itacns_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) @@ -497,7 +446,7 @@ static int itacns_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) return SC_SUCCESS; } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Reading EF_IDCarta.\n"); + sc_log(card->ctx, "Reading EF_IDCarta.\n"); sc_format_path("3F0010001003", &path); @@ -506,13 +455,14 @@ static int itacns_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) return SC_ERROR_WRONG_CARD; } len = file->size; + sc_file_free(file); - //Returned file->size should be 16. - //We choose to not consider it as critical, because some cards + //Returned file->size should be 16. + //We choose to not consider it as critical, because some cards //do not return FCI/FCP templates that include the file size. - //Notify abnormal lenght anyway. + //Notify abnormal length anyway. if (len != 16) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Unexpected file length of EF_IDCarta (%lu)\n", (unsigned long) len); } @@ -541,6 +491,15 @@ itacns_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) return SC_ERROR_NOT_SUPPORTED; } +static int +itacns_get_challenge(sc_card_t *card, u8 *rnd, size_t len) +{ + if (card->type == SC_CARD_TYPE_ITACNS_CNS_IDEMIA_2021) + len = MIN (0x20, len); + + return default_ops->get_challenge(card, rnd, len); +} + static struct sc_card_driver * sc_get_driver(void) { if (!default_ops) @@ -556,6 +515,7 @@ static struct sc_card_driver * sc_get_driver(void) itacns_ops.list_files = itacns_list_files; itacns_ops.select_file = itacns_select_file; itacns_ops.card_ctl = itacns_card_ctl; + itacns_ops.get_challenge = itacns_get_challenge; return &itacns_drv; } diff --git a/src/libopensc/card-jcop.c b/src/libopensc/card-jcop.c deleted file mode 100644 index a87ea4e4a3..0000000000 --- a/src/libopensc/card-jcop.c +++ /dev/null @@ -1,932 +0,0 @@ -/* - * card-jcop.c - * - * Copyright (C) 2003 Chaskiel Grundman - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include - -#include "internal.h" -#include "cardctl.h" - -static struct sc_atr_table jcop_atrs[] = { - { "3B:E6:00:FF:81:31:FE:45:4A:43:4F:50:33:31:06", NULL, NULL, SC_CARD_TYPE_JCOP_GENERIC, 0, NULL }, - { NULL, NULL, NULL, 0, 0, NULL } -}; - -static struct sc_card_operations jcop_ops; -static struct sc_card_driver jcop_drv = { - "JCOP cards with BlueZ PKCS#15 applet", - "jcop", - &jcop_ops, - NULL, 0, NULL -}; - -#define SELECT_MF 0 -#define SELECT_EFDIR 1 -#define SELECT_APPDF 2 -#define SELECT_EF 3 -#define SELECT_UNKNOWN 4 -#define SELECTING_TARGET 0xf -#define SELECTING_ABS 0x80 -#define SELECTING_VIA_APPDF 0x100 - -struct jcop_private_data -{ - sc_file_t *virtmf; - sc_file_t *virtdir; - sc_path_t aid; - int selected; - int invalid_senv; - int nfiles; - u8 *filelist; -}; -#define DRVDATA(card) ((struct jcop_private_data *) ((card)->drv_data)) - -static int jcop_finish(sc_card_t *card) -{ - struct jcop_private_data *drvdata=DRVDATA(card); - if (drvdata) { - sc_file_free(drvdata->virtmf); - sc_file_free(drvdata->virtdir); - free(drvdata); - card->drv_data=NULL; - } - - return 0; -} - -static int jcop_match_card(sc_card_t *card) -{ - int i; - - i = _sc_match_atr(card, jcop_atrs, &card->type); - if (i < 0) - return 0; - return 1; -} - -static unsigned char ef_dir_contents[128] = { - 0x61, 0x21, - 0x4f, 0xc, 0xA0, 0x0, 0x0, 0x0, 0x63, 'P', 'K', 'C', 'S', '-', '1', '5', - 0x50, 0xb, 'O', 'p', 'e', 'n', 'S', 'C', ' ', 'C', 'a', 'r', 'd', - 0x51, 0x04, 0x3f, 0x00, 0x50, 0x15 -}; - - -static int jcop_init(sc_card_t *card) -{ - struct jcop_private_data *drvdata; - sc_file_t *f; - int flags; - - drvdata=malloc(sizeof(struct jcop_private_data)); - if (!drvdata) - return SC_ERROR_OUT_OF_MEMORY; - memset(drvdata, 0, sizeof(struct jcop_private_data)); - - sc_format_path("A000:0000:6350:4B43:532D:3135", &drvdata->aid); - drvdata->aid.type = SC_PATH_TYPE_DF_NAME; - drvdata->selected=SELECT_MF; - drvdata->invalid_senv=1; - drvdata->nfiles=-1; - drvdata->filelist=NULL; - f=sc_file_new(); - if (!f){ - free(drvdata); - return SC_ERROR_OUT_OF_MEMORY; - } - - sc_format_path("3f00", &f->path); - f->type=SC_FILE_TYPE_DF; - f->shareable=0; - f->ef_structure=SC_FILE_EF_UNKNOWN; - f->size=0; - f->id=0x3f00; - f->status=SC_FILE_STATUS_ACTIVATED; - sc_file_add_acl_entry(f, SC_AC_OP_SELECT, SC_AC_NONE, 0); - sc_file_add_acl_entry(f, SC_AC_OP_LIST_FILES, SC_AC_NONE, 0); - sc_file_add_acl_entry(f, SC_AC_OP_LOCK, SC_AC_NEVER, 0); - sc_file_add_acl_entry(f, SC_AC_OP_DELETE, SC_AC_NEVER, 0); - sc_file_add_acl_entry(f, SC_AC_OP_CREATE, SC_AC_NEVER, 0); - - drvdata->virtmf=f; - - f=sc_file_new(); - if (!f){ - sc_file_free(drvdata->virtmf); - free(drvdata); - return SC_ERROR_OUT_OF_MEMORY; - } - - sc_format_path("3f002f00", &f->path); - f->type=SC_FILE_TYPE_WORKING_EF; - f->shareable=0; - f->ef_structure=SC_FILE_EF_TRANSPARENT; - f->size=128; - f->id=0x2f00; - f->status=SC_FILE_STATUS_ACTIVATED; - sc_file_add_acl_entry(f, SC_AC_OP_READ, SC_AC_NONE, 0); - sc_file_add_acl_entry(f, SC_AC_OP_LOCK, SC_AC_NEVER, 0); - sc_file_add_acl_entry(f, SC_AC_OP_ERASE, SC_AC_NEVER, 0); - sc_file_add_acl_entry(f, SC_AC_OP_UPDATE, SC_AC_NEVER, 0); - sc_file_add_acl_entry(f, SC_AC_OP_WRITE, SC_AC_NEVER, 0); - sc_file_add_acl_entry(f, SC_AC_OP_CRYPTO, SC_AC_NEVER, 0); - - drvdata->virtdir=f; - - - card->drv_data = drvdata; - card->cla = 0x00; - - /* card supports host-side padding, but not raw rsa */ - flags = SC_ALGORITHM_RSA_PAD_PKCS1; - flags |= SC_ALGORITHM_RSA_HASH_NONE; - flags |= SC_ALGORITHM_RSA_HASH_SHA1; - flags |= SC_ALGORITHM_RSA_HASH_MD5; - /* only supports keygen with 3 and F-4 exponents */ - flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; - _sc_card_add_rsa_alg(card, 512, flags, 0); - _sc_card_add_rsa_alg(card, 768, flags, 0); - _sc_card_add_rsa_alg(card, 1024, flags, 0); - _sc_card_add_rsa_alg(card, 2048, flags, 0); - /* State that we have an RNG */ - card->caps |= SC_CARD_CAP_RNG; - - return 0; -} - -static int jcop_get_default_key(sc_card_t *card, - struct sc_cardctl_default_key *data) -{ - const char *key; - - if (data->method != SC_AC_PRO || data->key_ref > 2) - return SC_ERROR_NO_DEFAULT_KEY; - - key = "40:41:42:43:44:45:46:47:48:49:4A:4B:4C:4D:4E:4F"; - return sc_hex_to_bin(key, data->key_data, &data->len); -} - -/* since the card is actually a javacard, we're expected to use ISO - 7816-4 direct application selection instead of reading the DIR - ourselves and selecting the AppDF by path. Since opensc doesn' do - that, I fake an MF containing the AppDF and a fixed DIR pointing at - the fake AppDF. This has the added advantage of allowing - opensc-explorer to be used with this driver */ -static int jcop_select_file(sc_card_t *card, const sc_path_t *path, - sc_file_t **file) -{ - struct jcop_private_data *drvdata=DRVDATA(card); - int r,selecting; - struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); - const struct sc_card_operations *iso_ops = iso_drv->ops; - sc_path_t shortpath; - sc_file_t *tfile, **fileptr; - - if (!drvdata) - return SC_ERROR_FILE_NOT_FOUND; - - /* Something about the card does not like Case 4 APDU's to be sent as - Case 3. you must send a length and accept a response. */ - - if (file) { - fileptr=file; - } else { - fileptr=&tfile; - } - - /* Selecting the MF. return a copy of the constructed MF */ - if (path->len == 2 && memcmp(path->value, "\x3F\x00", 2) == 0) { - drvdata->selected=SELECT_MF; - if (file) { - sc_file_dup(file, drvdata->virtmf); - if (*file == NULL) - return SC_ERROR_OUT_OF_MEMORY; - } - return 0; - } - /* Selecting the EF(DIR). return a copy of the constructed EF(DIR) */ - if ((path->len == 4 && - memcmp(path->value, "\x3F\x00\x2F\x00", 4) == 0) || - (drvdata->selected == SELECT_MF && path->len == 2 && - memcmp(path->value, "\x2F\x00", 2) == 0)) { - drvdata->selected=SELECT_EFDIR; - if (file) { - sc_file_dup(file, drvdata->virtdir); - if (*file == NULL) - return SC_ERROR_OUT_OF_MEMORY; - } - return 0; - } - /* selecting the PKCS15 AppDF or a file in it. Select the applet, then - pass through any remaining path components to the applet's select - command - */ - selecting=SELECT_UNKNOWN; - - if (path->len >= 4 && - memcmp(path->value, "\x3F\x00\x50\x15", 4) == 0) { - if (path->len == 4) - selecting = SELECTING_ABS | SELECT_APPDF; - else - selecting = SELECTING_ABS | SELECT_EF; - } - - if (drvdata->selected==SELECT_MF && - memcmp(path->value, "\x50\x15", 2) == 0) { - if (path->len == 2) - selecting = SELECTING_VIA_APPDF | SELECT_APPDF; - else - selecting = SELECTING_VIA_APPDF | SELECT_EF; - } - - if (selecting & (SELECTING_ABS|SELECTING_VIA_APPDF)) - { - if (file == NULL && - (selecting & SELECTING_TARGET) == SELECT_APPDF && - drvdata->selected == SELECT_APPDF) { - return 0; - } - if ((r = iso_ops->select_file(card, &drvdata->aid, fileptr)) < 0) - return r; - if (fileptr && (selecting & SELECTING_TARGET) == SELECT_APPDF) { - (*fileptr)->type = SC_FILE_TYPE_DF; - drvdata->selected=SELECT_APPDF; - goto select_ok; - } - sc_file_free(*fileptr); - *fileptr=NULL; - memset(&shortpath, 0, sizeof(sc_path_t)); - if (selecting & SELECTING_ABS) { - memcpy(&shortpath.value, &path->value[4], path->len-4); - shortpath.len=path->len-4; - } else { - memcpy(&shortpath.value, &path->value[2], path->len-2); - shortpath.len=path->len-2; - } - shortpath.type = shortpath.len == 2 ? SC_PATH_TYPE_FILE_ID : - path->type; - shortpath.index=path->index; - shortpath.count=path->count; - path=&shortpath; - } else { - /* There seems to be better debugging output if I call sc_check_sw - * with appropriate input than if I just return the appropriate - * SC_ERROR_*, so that's what I do for all errors returned by code - * related to the MF/DIR emulation - */ - if (drvdata->selected == SELECT_MF || - drvdata->selected == SELECT_EFDIR) - return sc_check_sw(card, 0x6A, 0x82); - } - - r = iso_ops->select_file(card, path, fileptr); - if (r) - return r; - drvdata->selected=SELECT_EF; - select_ok: - if (!file) { - sc_file_free(*fileptr); - } - return 0; -} - -static int jcop_read_binary(sc_card_t *card, unsigned int idx, - u8 * buf, size_t count, unsigned long flags) { - struct jcop_private_data *drvdata=DRVDATA(card); - struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); - const struct sc_card_operations *iso_ops = iso_drv->ops; - int r; - - if (drvdata->selected == SELECT_MF) { - return sc_check_sw(card, 0x69, 0x86); - } - if (drvdata->selected == SELECT_EFDIR) { - if (idx > 127) { - return sc_check_sw(card, 0x6A, 0x86); - } - if (idx + count > 128) { - count=128-idx; - } - r = iso_ops->select_file(card, &drvdata->aid, NULL); - if (r < 0) { /* no pkcs15 app, so return empty DIR. */ - memset(buf, 0, count); - } else { - memcpy(buf, (u8 *)(ef_dir_contents + idx), count); - } - return count; - } - return iso_ops->read_binary(card, idx, buf, count, flags); -} - -static int jcop_list_files(sc_card_t *card, u8 *buf, size_t buflen) { - struct jcop_private_data *drvdata=DRVDATA(card); - struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); - const struct sc_card_operations *iso_ops = iso_drv->ops; - int r; - - if (drvdata->selected == SELECT_MF) { - if (buflen < 2) - return 0; - memcpy(buf, "\x2f\x00", 2); - if (buflen < 4) - return 2; - /* AppDF only exists if applet is selectable */ - r = iso_ops->select_file(card, &drvdata->aid, NULL); - if (r < 0) { - return 2; - } else { - memcpy(buf+2, "\x50\x15", 2); - return 4; - } - } - - if (drvdata->nfiles == -1) - return SC_ERROR_NOT_ALLOWED; - if (drvdata->nfiles == 0) - return 0; - if (buflen > 2 * (size_t)drvdata->nfiles) - buflen=2*drvdata->nfiles; - memcpy(buf, drvdata->filelist, buflen); - return buflen; -} - -static int sa_to_acl(sc_file_t *file, unsigned int operation, - int nibble) { - switch (nibble & 0x7) { - case 0: - sc_file_add_acl_entry(file, operation, SC_AC_NONE, SC_AC_KEY_REF_NONE); - break; - case 1: - sc_file_add_acl_entry(file, operation, SC_AC_NEVER, SC_AC_KEY_REF_NONE); - break; - case 2: - sc_file_add_acl_entry(file, operation, SC_AC_CHV, 1); - break; - case 3: - sc_file_add_acl_entry(file, operation, SC_AC_CHV, 2); - break; - case 4: - sc_file_add_acl_entry(file, operation, SC_AC_CHV, 3); - break; - case 5: - sc_file_add_acl_entry(file, operation, SC_AC_AUT, SC_AC_KEY_REF_NONE); - break; - case 6: - sc_file_add_acl_entry(file, operation, SC_AC_PRO, SC_AC_KEY_REF_NONE); - break; - default: - sc_file_add_acl_entry(file, operation, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE); - } - return 0; -} - - -static int jcop_process_fci(sc_card_t *card, sc_file_t *file, - const u8 *buf, size_t buflen) { - struct jcop_private_data *drvdata=DRVDATA(card); - struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); - const struct sc_card_operations *iso_ops = iso_drv->ops; - u8 *sa; - int r; - - /* the FCI for EF's includes a bogus length for the overall structure! */ - if (buflen == 19) - buflen=24; - r=iso_ops->process_fci(card, file, buf, buflen); - - if (r < 0) - return r; - if (file->type != SC_FILE_TYPE_DF) { - if (drvdata->nfiles) { - drvdata->nfiles=-1; - free(drvdata->filelist); - drvdata->filelist=NULL; - } - if(file->sec_attr_len >=3) { - /* The security attribute bytes are divided into nibbles and are - as follows: - READ | MODIFY || SIGN | ENCIPHER || DECIPHER | DELETE - */ - sa=file->sec_attr; - sa_to_acl(file, SC_AC_OP_READ, sa[0] >> 4); - sa_to_acl(file, SC_AC_OP_UPDATE, sa[0] & 0xf); - /* Files may be locked by anyone who can MODIFY. */ - /* opensc seems to think LOCK ACs are only on DFs */ - /* sa_to_acl(file, SC_AC_OP_LOCK, sa[0] & 0xf); */ - /* there are seperate SIGN, ENCIPHER, and DECIPHER ACs. - I use SIGN for SC_AC_OP_CRYPTO unless it is NEVER, in - which case I use DECIPHER */ - if ((sa[1] & 0xf0) == 0x10) - sa_to_acl(file, SC_AC_OP_CRYPTO, sa[1] >> 4); - else - sa_to_acl(file, SC_AC_OP_CRYPTO, sa[2] >> 4); - sa_to_acl(file, SC_AC_OP_ERASE, sa[2] & 0xf); - } - } else { - /* No AC information is reported for the AppDF */ - sc_file_add_acl_entry(file, SC_AC_OP_SELECT, SC_AC_NONE, 0); - sc_file_add_acl_entry(file, SC_AC_OP_CREATE, SC_AC_CHV, 3); - sc_file_add_acl_entry(file, SC_AC_OP_DELETE, SC_AC_NONE, 0); - sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, SC_AC_NONE, 0); - if (drvdata->nfiles) { - drvdata->nfiles=0; - free(drvdata->filelist); - drvdata->filelist=NULL; - } - /* the format of the poprietary attributes is: - 4 bytes unique id - 1 byte # files in DF - 2 bytes 1st File ID - 2 bytes 2nd File ID - ... - */ - if (file->prop_attr_len > 4) { - int nfiles; - u8 *filelist; - nfiles=file->prop_attr[4]; - if (nfiles) { - filelist=malloc(2*nfiles); - if (!filelist) - return SC_ERROR_OUT_OF_MEMORY; - memcpy(filelist, &file->prop_attr[5], 2*nfiles); - drvdata->nfiles=nfiles; - drvdata->filelist=filelist; - } - } - } - - return r; -} -static int acl_to_ac_nibble(const sc_acl_entry_t *e) -{ - if (e == NULL) - return -1; - if (e->next != NULL) /* FIXME */ - return -1; - switch (e->method) { - case SC_AC_NONE: - return 0x00; - case SC_AC_NEVER: - return 0x01; - case SC_AC_CHV: - switch (e->key_ref) { - case 1: - return 0x02; - case 2: - return 0x03; - case 3: - return 0x04; - } - return -1; - case SC_AC_AUT: - return 0x05; - case SC_AC_PRO: - return 0x06; - } - return -1; -} - - -static int jcop_create_file(sc_card_t *card, sc_file_t *file) { - struct jcop_private_data *drvdata=DRVDATA(card); - unsigned char sec_attr_data[3]; - int ops[6]; - int i, r; - struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); - const struct sc_card_operations *iso_ops = iso_drv->ops; - - if (drvdata->selected == SELECT_MF || drvdata->selected == SELECT_EFDIR ) - return sc_check_sw(card, 0x69, 0x82); - - /* Can't create DFs */ - if (file->type != SC_FILE_TYPE_WORKING_EF) - return sc_check_sw(card, 0x6A, 0x80); - - ops[0] = SC_AC_OP_READ; /* read */ - ops[1] = SC_AC_OP_UPDATE; /* modify */ - ops[2] = SC_AC_OP_CRYPTO; /* sign */ - ops[3] = -1; /* encipher */ - ops[4] = SC_AC_OP_CRYPTO; /* decipher */ - ops[5] = SC_AC_OP_ERASE; /* delete */ - memset(sec_attr_data, 0, 3); - for (i = 0; i < 6; i++) { - const sc_acl_entry_t *entry; - if (ops[i] == -1) { - sec_attr_data[i/2] |= 1 << ((i % 2) ? 0 : 4); - continue; - } - - entry = sc_file_get_acl_entry(file, ops[i]); - r = acl_to_ac_nibble(entry); - sec_attr_data[i/2] |= r << ((i % 2) ? 0 : 4); - } - - sc_file_set_sec_attr(file, sec_attr_data, 3); - - r=iso_ops->create_file(card, file); - if (r > 0) - drvdata->selected=SELECT_EF; - return r; -} - - -/* We need to trap these functions so that proper errors can be returned - when one of the virtual files is selected */ -static int jcop_write_binary(sc_card_t *card, - unsigned int idx, const u8 *buf, - size_t count, unsigned long flags) { - struct jcop_private_data *drvdata=DRVDATA(card); - struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); - const struct sc_card_operations *iso_ops = iso_drv->ops; - - if (drvdata->selected == SELECT_MF) - return sc_check_sw(card, 0x6A, 0x86); - if (drvdata->selected == SELECT_EFDIR) - return sc_check_sw(card, 0x69, 0x82); - - return iso_ops->write_binary(card, idx, buf, count, flags); -} - - -static int jcop_update_binary(sc_card_t *card, - unsigned int idx, const u8 *buf, - size_t count, unsigned long flags) { - - struct jcop_private_data *drvdata=DRVDATA(card); - struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); - const struct sc_card_operations *iso_ops = iso_drv->ops; - if (drvdata->selected == SELECT_MF) - return sc_check_sw(card, 0x69, 0x86); - if (drvdata->selected == SELECT_EFDIR) - return sc_check_sw(card, 0x69, 0x82); - - return iso_ops->update_binary(card, idx, buf, count, flags); -} - -static int jcop_delete_file(sc_card_t *card, const sc_path_t *path) { - struct jcop_private_data *drvdata=DRVDATA(card); - struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); - const struct sc_card_operations *iso_ops = iso_drv->ops; - - if (drvdata->selected == SELECT_MF || drvdata->selected == SELECT_EFDIR ) - return sc_check_sw(card, 0x69, 0x82); - - return iso_ops->delete_file(card, path); -} - - -/* BlueZ doesn't support stored security environments. you have - to construct one with SET every time */ -static int jcop_set_security_env(sc_card_t *card, - const sc_security_env_t *env, - int se_num) -{ - sc_apdu_t apdu; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 *p; - int r; - struct jcop_private_data *drvdata=DRVDATA(card); - - assert(card != NULL && env != NULL); - if (se_num) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); - if (drvdata->selected == SELECT_MF || - drvdata->selected == SELECT_EFDIR) { - drvdata->invalid_senv=1; - return 0; - } - - if (env->flags & SC_SEC_ENV_ALG_PRESENT) { - sc_security_env_t tmp; - - tmp = *env; - tmp.flags &= ~SC_SEC_ENV_ALG_PRESENT; - tmp.flags |= SC_SEC_ENV_ALG_REF_PRESENT; - if (tmp.algorithm != SC_ALGORITHM_RSA) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Only RSA algorithm supported.\n"); - return SC_ERROR_NOT_SUPPORTED; - } - if (!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)){ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Card requires RSA padding\n"); - return SC_ERROR_NOT_SUPPORTED; - } - tmp.algorithm_ref = 0x02; - /* potential FIXME: return an error, if an unsupported - * pad or hash was requested, although this shouldn't happen. - */ - if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) - tmp.algorithm_ref |= 0x10; - if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5) - tmp.algorithm_ref |= 0x20; - - memcpy((sc_security_env_t *) env, &tmp, sizeof(struct sc_security_env)); - } - - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xC1, 0); - switch (env->operation) { - case SC_SEC_OPERATION_DECIPHER: - apdu.p2 = 0xB8; - break; - case SC_SEC_OPERATION_SIGN: - apdu.p2 = 0xB6; - break; - default: - return SC_ERROR_INVALID_ARGUMENTS; - } - apdu.le = 0; - if (!env->flags & SC_SEC_ENV_ALG_REF_PRESENT) - return SC_ERROR_INVALID_ARGUMENTS; - if (!(env->flags & SC_SEC_ENV_FILE_REF_PRESENT)) - return SC_ERROR_INVALID_ARGUMENTS; - if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { - if (env->key_ref_len > 1 || env->key_ref[0] != 0) - return SC_ERROR_INVALID_ARGUMENTS; - } - - p = sbuf; - *p++ = 0x80; /* algorithm reference */ - *p++ = 0x01; - *p++ = env->algorithm_ref & 0xFF; - - *p++ = 0x81; - *p++ = env->file_ref.len; - memcpy(p, env->file_ref.value, env->file_ref.len); - p += env->file_ref.len; - - r = p - sbuf; - apdu.lc = r; - apdu.datalen = r; - apdu.data = sbuf; - apdu.resplen = 0; - r = sc_transmit_apdu(card, &apdu); - if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "%s: APDU transmit failed", sc_strerror(r)); - return r; - } - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "%s: Card returned error", sc_strerror(r)); - return r; - } - drvdata->invalid_senv=0; - return 0; -} -static int jcop_compute_signature(sc_card_t *card, - const u8 * data, size_t datalen, - u8 * out, size_t outlen) { - - - int r; - sc_apdu_t apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - struct jcop_private_data *drvdata=DRVDATA(card); - - assert(card != NULL && data != NULL && out != NULL); - if (datalen > 256) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); - - if (drvdata->invalid_senv) - return sc_check_sw(card, 0x69, 0x88); - - /* INS: 0x2A PERFORM SECURITY OPERATION - * P1: 0x9E Resp: Digital Signature - * P2: 0x9A Cmd: Input for Digital Signature */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, - 0x9A); - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); /* FIXME */ - apdu.le = 256; - if (datalen == 256) { - apdu.p2 = data[0]; - memcpy(sbuf, data+1, datalen-1); - apdu.lc = datalen - 1; - apdu.datalen = datalen - 1; - } else { - memcpy(sbuf, data, datalen); - apdu.lc = datalen; - apdu.datalen = datalen; - } - - apdu.data = sbuf; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { - int len = apdu.resplen > outlen ? outlen : apdu.resplen; - - memcpy(out, apdu.resp, len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); - } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); -} - - - -static int jcop_decipher(sc_card_t *card, - const u8 * crgram, size_t crgram_len, - u8 * out, size_t outlen) { - - int r; - sc_apdu_t apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - struct jcop_private_data *drvdata=DRVDATA(card); - - assert(card != NULL && crgram != NULL && out != NULL); - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); - if (crgram_len > 256) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); - if (drvdata->invalid_senv) - return sc_check_sw(card, 0x69, 0x88); - - /* INS: 0x2A PERFORM SECURITY OPERATION - * P1: 0x80 Resp: Plain value - * P2: 0x86 Cmd: Padding indicator byte followed by cryptogram */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); /* FIXME */ - apdu.le = crgram_len; - - if (crgram_len == 256) { - apdu.p2 = crgram[0]; - memcpy(sbuf, crgram+1, crgram_len-1); - apdu.lc = crgram_len - 1; - apdu.datalen = crgram_len -1; - } else { - sbuf[0] = 0; /* padding indicator byte, 0x00 = No further indication */ - memcpy(sbuf + 1, crgram, crgram_len); - apdu.lc = crgram_len + 1; - apdu.datalen = crgram_len + 1; - } - - apdu.data = sbuf; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { - int len = apdu.resplen > outlen ? outlen : apdu.resplen; - - memcpy(out, apdu.resp, len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); - } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); -} - -static int jcop_generate_key(sc_card_t *card, struct sc_cardctl_jcop_genkey *a) { - int r; - sc_apdu_t apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 *p; - int is_f4; - struct jcop_private_data *drvdata=DRVDATA(card); - - if (drvdata->selected == SELECT_MF || drvdata->selected == SELECT_EFDIR ) - return sc_check_sw(card, 0x6A, 0x82); - - is_f4=0; - - if (a->exponent == 0x10001) { - is_f4=1; - } else if (a->exponent != 3) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "%s: Invalid exponent", sc_strerror(SC_ERROR_NOT_SUPPORTED)); - return SC_ERROR_NOT_SUPPORTED; - } - - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xC1, 0xB6); - - p = sbuf; - *p++ = 0x80; /* algorithm reference */ - *p++ = 0x01; - *p++ = is_f4 ? 0x6E : 0x6D; - - *p++ = 0x81; - *p++ = a->pub_file_ref.len; - memcpy(p, a->pub_file_ref.value, a->pub_file_ref.len); - p += a->pub_file_ref.len; - - *p++ = 0x81; - *p++ = a->pri_file_ref.len; - memcpy(p, a->pri_file_ref.value, a->pri_file_ref.len); - p += a->pri_file_ref.len; - - r = p - sbuf; - - apdu.lc = r; - apdu.datalen = r; - apdu.data = sbuf; - apdu.resplen = 0; - r = sc_transmit_apdu(card, &apdu); - if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "%s: APDU transmit failed", sc_strerror(r)); - return r; - } - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "%s: Card returned error", sc_strerror(r)); - return r; - } - - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x46, 0, 0); - - apdu.le = 256; - apdu.resp=rbuf; - apdu.resplen = sizeof(rbuf); - - r = sc_transmit_apdu(card, &apdu); - if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "%s: APDU transmit failed", sc_strerror(r)); - return r; - } - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "%s: Card returned error", sc_strerror(r)); - return r; - } - - if (rbuf[0] != 0x4) { - return SC_ERROR_INVALID_DATA; - } - if (a->pubkey_len < rbuf[1]) - return SC_ERROR_BUFFER_TOO_SMALL; - a->pubkey_len=rbuf[1] * 4; - memcpy(a->pubkey, &rbuf[2], a->pubkey_len); - - return 0; -} - -static int jcop_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) -{ - switch (cmd) { - case SC_CARDCTL_GET_DEFAULT_KEY: - return jcop_get_default_key(card, - (struct sc_cardctl_default_key *) ptr); - case SC_CARDCTL_JCOP_GENERATE_KEY: - return jcop_generate_key(card, - (struct sc_cardctl_jcop_genkey *) ptr); - } - - return SC_ERROR_NOT_SUPPORTED; -} - -static struct sc_card_driver * sc_get_driver(void) -{ - struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); - - jcop_ops = *iso_drv->ops; - jcop_ops.match_card = jcop_match_card; - jcop_ops.init = jcop_init; - jcop_ops.finish = jcop_finish; - /* no record oriented file services */ - jcop_ops.read_record = NULL; - jcop_ops.write_record = NULL; - jcop_ops.append_record = NULL; - jcop_ops.update_record = NULL; - jcop_ops.read_binary = jcop_read_binary; - jcop_ops.write_binary = jcop_write_binary; - jcop_ops.update_binary = jcop_update_binary; - jcop_ops.select_file = jcop_select_file; - jcop_ops.create_file = jcop_create_file; - jcop_ops.delete_file = jcop_delete_file; - jcop_ops.list_files = jcop_list_files; - jcop_ops.set_security_env = jcop_set_security_env; - jcop_ops.compute_signature = jcop_compute_signature; - jcop_ops.decipher = jcop_decipher; - jcop_ops.process_fci = jcop_process_fci; - jcop_ops.card_ctl = jcop_card_ctl; - - return &jcop_drv; -} - -struct sc_card_driver * sc_get_jcop_driver(void) -{ - return sc_get_driver(); -} - diff --git a/src/libopensc/card-jpki.c b/src/libopensc/card-jpki.c index 069470fac7..7a5e7f2ab1 100644 --- a/src/libopensc/card-jpki.c +++ b/src/libopensc/card-jpki.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -28,7 +28,7 @@ #include "internal.h" #include "jpki.h" -static struct sc_atr_table jpki_atrs[] = { +static const struct sc_atr_table jpki_atrs[] = { {"3b:e0:00:ff:81:31:fe:45:14", NULL, NULL, SC_CARD_TYPE_JPKI_BASE, 0, NULL}, {NULL, NULL, NULL, 0, 0, NULL} @@ -82,8 +82,8 @@ jpki_finish(sc_card_t * card) struct jpki_private_data *drvdata = JPKI_DRVDATA(card); LOG_FUNC_CALLED(card->ctx); - if (drvdata) { + sc_file_free(drvdata->mf); free(drvdata); card->drv_data = NULL; } @@ -193,6 +193,8 @@ jpki_select_file(struct sc_card *card, u8 buf[4]; rc = sc_read_binary(card, 0, buf, 4, 0); LOG_TEST_RET(card->ctx, rc, "SW Check failed"); + if (rc < 4) + LOG_TEST_RET(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Received data too short"); file = sc_file_new(); if (!file) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); @@ -205,21 +207,7 @@ jpki_select_file(struct sc_card *card, } static int -jpki_read_binary(sc_card_t * card, unsigned int idx, - u8 * buf, size_t count, unsigned long flags) -{ - struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); - const struct sc_card_operations *iso_ops = iso_drv->ops; - int rc; - - LOG_FUNC_CALLED(card->ctx); - - rc = iso_ops->read_binary(card, idx, buf, count, flags); - LOG_FUNC_RETURN(card->ctx, rc); -} - -static int -jpki_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) +jpki_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) { int rc; sc_path_t path; @@ -229,10 +217,6 @@ jpki_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) LOG_FUNC_CALLED(card->ctx); - if (tries_left) { - *tries_left = -1; - } - switch (data->pin_reference) { case 1: sc_format_path(JPKI_AUTH_PIN, &path); @@ -254,6 +238,9 @@ jpki_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) switch (data->cmd) { case SC_PIN_CMD_VERIFY: + /* detect overloaded APDU with SC_PIN_CMD_GET_INFO */ + if (data->pin1.len == 0 && !(data->flags & SC_PIN_CMD_USE_PINPAD)) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_PIN_LENGTH); sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0x20, 0x00, 0x80); apdu.data = data->pin1.data; apdu.datalen = data->pin1.len; @@ -281,9 +268,6 @@ jpki_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) } data->pin1.logged_in = priv->logged_in; data->pin1.tries_left = apdu.sw2 & 0xF; - if (tries_left) { - *tries_left = data->pin1.tries_left; - } break; default: sc_log(card->ctx, "Card does not support PIN command: %d", data->cmd); @@ -302,7 +286,7 @@ jpki_set_security_env(sc_card_t * card, LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, - "flags=%08lx op=%d alg=%d algf=%08x algr=%08x kr0=%02x, krfl=%"SC_FORMAT_LEN_SIZE_T"u", + "flags=%08lx op=%d alg=%lu algf=%08lx algr=%08lx kr0=%02x, krfl=%"SC_FORMAT_LEN_SIZE_T"u", env->flags, env->operation, env->algorithm, env->algorithm_flags, env->algorithm_ref, env->key_ref[0], env->key_ref_len); @@ -357,7 +341,25 @@ jpki_compute_signature(sc_card_t * card, LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(out, resp, apdu.resplen); - LOG_FUNC_RETURN(card->ctx, apdu.resplen); + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); +} + +static int jpki_card_reader_lock_obtained(sc_card_t *card, int was_reset) +{ + int r = SC_SUCCESS; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (was_reset > 0) { + r = jpki_select_ap(card); + } + + LOG_FUNC_RETURN(card->ctx, r); +} + +static int jpki_logout(sc_card_t *card) +{ + return jpki_select_ap(card); } static struct sc_card_driver * @@ -370,10 +372,11 @@ sc_get_driver(void) jpki_ops.init = jpki_init; jpki_ops.finish = jpki_finish; jpki_ops.select_file = jpki_select_file; - jpki_ops.read_binary = jpki_read_binary; jpki_ops.pin_cmd = jpki_pin_cmd; jpki_ops.set_security_env = jpki_set_security_env; jpki_ops.compute_signature = jpki_compute_signature; + jpki_ops.card_reader_lock_obtained = jpki_card_reader_lock_obtained; + jpki_ops.logout = jpki_logout; return &jpki_drv; } diff --git a/src/libopensc/card-lteid.c b/src/libopensc/card-lteid.c new file mode 100644 index 0000000000..a582eb7b94 --- /dev/null +++ b/src/libopensc/card-lteid.c @@ -0,0 +1,583 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) + +#include +#include + +#include "asn1.h" +#include "common/compat_strlcat.h" +#include "internal.h" +#include "opensc.h" +#include "sm/sm-eac.h" + +static const struct sc_card_operations *iso_ops = NULL; +static struct sc_card_operations lteid_ops; + +static struct sc_card_driver lteid_drv = { + "Lithuanian eID card (asmens tapatybės kortelė)", "lteid", + <eid_ops, NULL, 0, NULL}; + +#define DRVDATA(card) ((struct lteid_drv_data *)((card)->drv_data)) +#define LTEID_CAN_LENGTH 6 + +struct lteid_drv_data { + unsigned char pace; + unsigned char pace_pin_ref; + unsigned char can[LTEID_CAN_LENGTH]; + unsigned char can_from_cache; +}; + +void +lteid_get_can_cache_path(sc_card_t *card, char *buf, size_t buf_len) +{ + sc_get_cache_dir(card->ctx, buf, buf_len); + +#ifdef _WIN32 + strlcat(buf, "\\", buf_len); +#else + strlcat(buf, "/", buf_len); +#endif + + strlcat(buf, "lteid_can", buf_len); +} + +static int +lteid_get_cached_can(sc_card_t *card, unsigned char *can) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + char path[PATH_MAX]; + + lteid_get_can_cache_path(card, path, sizeof(path)); + + FILE *fd = fopen(path, "r"); + + if (!fd) { + LOG_FUNC_RETURN(card->ctx, 0); + } + + if (LTEID_CAN_LENGTH != fread(can, 1, LTEID_CAN_LENGTH, fd)) { + LOG_FUNC_RETURN(card->ctx, 0); + } + + fclose(fd); + + LOG_FUNC_RETURN(card->ctx, 1); +} + +static int +lteid_cache_can(sc_card_t *card, const char *can) +{ + int rv; + char path[PATH_MAX]; + + lteid_get_can_cache_path(card, path, sizeof(path)); + + FILE *fd = fopen(path, "w"); + + if (!fd && errno == ENOENT) { + if ((rv = sc_make_cache_dir(card->ctx)) < 0) + return rv; + + fd = fopen(path, "w"); + } + + if (!fd) { + return SC_ERROR_INTERNAL; + } + + fwrite(can, 1, 6, fd); + fclose(fd); + + return SC_SUCCESS; +} + +static int +lteid_clear_cached_can(sc_card_t *card) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + char path[PATH_MAX]; + + lteid_get_can_cache_path(card, path, sizeof(path)); + + if (!unlink(path)) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +lteid_get_can(sc_card_t *card, struct establish_pace_channel_input *pace_input) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + struct lteid_drv_data *drv_data = DRVDATA(card); + int got_can = 0; + + drv_data->can_from_cache = 0; + + // Try env variables first + const char *can_from_env = getenv("LTEID_CAN"); + if (can_from_env && strlen(can_from_env) == LTEID_CAN_LENGTH) { + got_can = 1; + memcpy(drv_data->can, can_from_env, LTEID_CAN_LENGTH); + } + + // Try getting one from the cache + if (!got_can && lteid_get_cached_can(card, drv_data->can)) { + got_can = 1; + drv_data->can_from_cache = 1; + } + + // Finally see if there is a default in configuration + if (!got_can) { + const char *can_from_config = NULL; + + for (size_t i = 0; card->ctx->conf_blocks[i]; ++i) { + scconf_block **blocks = scconf_find_blocks(card->ctx->conf, card->ctx->conf_blocks[i], "card_driver", "lteid"); + if (!blocks) + continue; + for (size_t j = 0; blocks[j]; ++j) + if ((can_from_config = scconf_get_str(blocks[j], "can", NULL))) + break; + free(blocks); + } + + if (can_from_config && strlen(can_from_config) == LTEID_CAN_LENGTH) { + got_can = 1; + memcpy(drv_data->can, can_from_config, LTEID_CAN_LENGTH); + } + } + + if (!got_can) { + sc_log(card->ctx, "Missing or invalid CAN. 6 digits required."); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN); + } + + pace_input->pin_id = PACE_PIN_ID_CAN; + pace_input->pin = drv_data->can; + pace_input->pin_length = LTEID_CAN_LENGTH; + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +lteid_perform_pace(struct sc_card *card, const int ref, const unsigned char *pin, size_t pinlen, int *tries_left) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + struct lteid_drv_data *drv_data = DRVDATA(card); + struct establish_pace_channel_input pace_input = {0}; + struct establish_pace_channel_output pace_output = {0}; + + if (drv_data->pace) { + sc_sm_stop(card); + drv_data->pace = 0; + drv_data->pace_pin_ref = 0; + } + + if (ref == PACE_PIN_ID_CAN && !pin) { + if (SC_SUCCESS != lteid_get_can(card, &pace_input)) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN); + } + } else { + pace_input.pin_id = ref; + pace_input.pin = pin; + pace_input.pin_length = pinlen; + } + + int rv = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02); + if (rv != SC_SUCCESS) { + sc_log(card->ctx, "Error performing PACE for pin ref 0x%02x.", ref); + + drv_data->pace = 0; + drv_data->pace_pin_ref = 0; + + // Special case after entering incorrect PACE PIN twice. Card locks with 1 PIN attempt remaining. + if (ref == PACE_PIN_ID_PIN && rv == SC_ERROR_NO_CARD_SUPPORT) { + rv = SC_ERROR_AUTH_METHOD_BLOCKED; + } + + // When CAN code authentication fails and CAN code comes from cache - clear it. + // We don't want to make multiple attempts if code is wrong. User should run lteid-tool + // again to set up the card. + if (ref == PACE_PIN_ID_CAN && drv_data->can_from_cache && rv != SC_ERROR_INTERNAL) { + if (lteid_clear_cached_can(card)) { + drv_data->can_from_cache = 0; + } + } + + LOG_FUNC_RETURN(card->ctx, rv); + } + + // If caller provided CAN is valid - cache it. + if (ref == PACE_PIN_ID_CAN && pin) { + lteid_cache_can(card, (const char *)pin); + } + + // Track PACE status + drv_data->pace = 1; + drv_data->pace_pin_ref = ref; + + free(pace_output.ef_cardaccess); + free(pace_output.recent_car); + free(pace_output.previous_car); + free(pace_output.id_icc); + free(pace_output.id_pcd); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +lteid_unlock(sc_card_t *card) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (SC_SUCCESS != lteid_perform_pace(card, PACE_PIN_ID_CAN, NULL, 0, NULL)) { + sc_log(card->ctx, "Unlock with CAN code failed. No CAN found in environment, opensc.conf nor cache."); + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +lteid_init(sc_card_t *card) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + int rv; + + struct lteid_drv_data *drv_data = calloc(1, sizeof(struct lteid_drv_data)); + + if (drv_data == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + + drv_data->pace = 0; + drv_data->pace_pin_ref = 0; + card->drv_data = drv_data; + + memset(&card->sm_ctx, 0, sizeof card->sm_ctx); + + card->max_send_size = 65535; + card->max_recv_size = 65535; + card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO | SC_CARD_CAP_APDU_EXT; + + _sc_card_add_ec_alg(card, 384, SC_ALGORITHM_ECDSA_HASH_NONE | SC_ALGORITHM_ECDSA_RAW, 0, NULL); + + rv = sc_enum_apps(card); + LOG_TEST_RET(card->ctx, rv, "Enumerate apps failed"); + + rv = lteid_unlock(card); + LOG_TEST_RET(card->ctx, rv, "Unlock card failed"); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +lteid_finish(sc_card_t *card) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + sc_sm_stop(card); + + free(card->drv_data); + card->drv_data = NULL; + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +lteid_logout(sc_card_t *card) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + sc_sm_stop(card); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +lteid_pin_cmd_verify(struct sc_card *card, struct sc_pin_cmd_data *data) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + int rv; + + // Authentication key refers to PACE PIN -> 0x03 + // Meanwhile, signing key refers to PIN.QES -> 0x81. This pin is verifiable via regular iso7816 cmd verify call. + switch (data->pin_reference) { + case PACE_PIN_ID_CAN: + case PACE_PIN_ID_PIN: + case PACE_PIN_ID_PUK: + rv = lteid_perform_pace(card, data->pin_reference, data->pin1.data, data->pin1.len, &data->pin1.tries_left); + break; + default: + rv = iso_ops->pin_cmd(card, data); + break; + } + + LOG_FUNC_RETURN(card->ctx, rv); +} + +static int +lteid_pin_cmd_get_info(struct sc_card *card, struct sc_pin_cmd_data *data) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + struct lteid_drv_data *drv_data = DRVDATA(card); + int rv; + + // PACE CAN code info: as far as we know there's no limit to CAN verification attempts + if (data->pin_reference == PACE_PIN_ID_CAN) { + data->pin1.max_tries = -1; + data->pin1.tries_left = -1; + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + } + + // PACE PIN and PUK codes: max and remaining attempts are stored in ACE3 and ACE4 files. + if (data->pin_reference == PACE_PIN_ID_PIN || data->pin_reference == PACE_PIN_ID_PUK) { + struct sc_apdu apdu; + unsigned char buf[0xbe] = {0}; + u8 id[] = {0xac, 0x00}; + size_t taglen = 0; + + switch (data->pin_reference) { + case PACE_PIN_ID_PIN: + id[1] = 0xe3; + break; + case PACE_PIN_ID_PUK: + id[1] = 0xe4; + break; + default: + break; + } + + sc_format_apdu_ex(&apdu, 0x00, 0xa4, 0x00, 0x04, id, sizeof(id), buf, sizeof(buf)); + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + const u8 *tag = sc_asn1_find_tag(card->ctx, buf, apdu.resplen, 0x62, &taglen); + tag = sc_asn1_find_tag(card->ctx, tag, taglen, 0xa5, &taglen); + tag = sc_asn1_find_tag(card->ctx, tag, taglen, 0xa2, &taglen); + tag = sc_asn1_find_tag(card->ctx, tag, taglen, 0xa3, &taglen); + tag = sc_asn1_find_tag(card->ctx, tag, taglen, 0x82, &taglen); + + if (tag && taglen == 2) { + data->pin1.tries_left = tag[0]; + data->pin1.max_tries = tag[1]; + } else { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_FOUND); + } + + if (drv_data->pace_pin_ref == data->pin_reference) { + data->pin1.logged_in = SC_PIN_STATE_LOGGED_IN; + } else { + data->pin1.logged_in = SC_PIN_STATE_LOGGED_OUT; + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + } + + // PIN.QES is a regular iso7816 pin + if (data->pin_reference == 0x81) { + rv = iso_ops->pin_cmd(card, data); + LOG_FUNC_RETURN(card->ctx, rv); + } + + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_FOUND); +} + +static int +lteid_pin_cmd_unblock(struct sc_card *card, struct sc_pin_cmd_data *data) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + int rv; + + // PACE PIN for unblocking command is known as pin ref 0x07 + if (data->pin_reference == PACE_PIN_ID_PIN) { + struct sc_pin_cmd_data with_changed_pin_ref = *data; + + with_changed_pin_ref.pin_reference = 0x07; + rv = iso_ops->pin_cmd(card, &with_changed_pin_ref); + LOG_FUNC_RETURN(card->ctx, rv); + } + + // PIN.QES is a regular iso7816 pin + if (data->pin_reference == 0x81) { + rv = iso_ops->pin_cmd(card, data); + LOG_FUNC_RETURN(card->ctx, rv); + } + + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_FOUND); +} + +static int +lteid_pin_cmd_change(struct sc_card *card, struct sc_pin_cmd_data *data) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + int rv; + + // PACE PIN for unblocking command is known as pin ref 0x07 + if (data->pin_reference == PACE_PIN_ID_PIN) { + struct sc_pin_cmd_data with_changed_pin_ref = *data; + + with_changed_pin_ref.pin_reference = 0x07; + rv = iso_ops->pin_cmd(card, &with_changed_pin_ref); + LOG_FUNC_RETURN(card->ctx, rv); + } + + // PIN.QES is a regular iso7816 pin + if (data->pin_reference == 0x81) { + rv = iso_ops->pin_cmd(card, data); + LOG_FUNC_RETURN(card->ctx, rv); + } + + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_FOUND); +} + +static int +lteid_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + int rv; + + switch (data->cmd) { + case SC_PIN_CMD_VERIFY: + rv = lteid_pin_cmd_verify(card, data); + break; + case SC_PIN_CMD_GET_INFO: + rv = lteid_pin_cmd_get_info(card, data); + break; + case SC_PIN_CMD_UNBLOCK: + rv = lteid_pin_cmd_unblock(card, data); + break; + case SC_PIN_CMD_CHANGE: + rv = lteid_pin_cmd_change(card, data); + break; + default: + rv = SC_ERROR_NOT_SUPPORTED; + break; + } + + LOG_FUNC_RETURN(card->ctx, rv); +} + +static int +lteid_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num) +{ + LOG_FUNC_CALLED(card->ctx); + + struct sc_apdu apdu; + int rv; + + if (env == NULL || env->key_ref_len != 1) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + + sc_log(card->ctx, "algo: %lu operation: %d keyref: %d", env->algorithm, env->operation, env->key_ref[0]); + + if (env->algorithm != SC_ALGORITHM_EC || env->operation != SC_SEC_OPERATION_SIGN) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + + const u8 data[] = {0x84, 0x01, env->key_ref[0]}; + sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xB6, data, sizeof(data), NULL, 0); + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, rv, "SET SECURITY ENV failed"); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +lteid_compute_signature(struct sc_card *card, const u8 *data, size_t data_len, u8 *out, size_t outlen) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + // Usually this is called with 104 bytes buffer. But we expect card to return 96 byte hash. + if (outlen < 96) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); + + memset(out, 0, outlen); + + const int rv = iso_ops->compute_signature(card, data, data_len, out, 96); + + LOG_FUNC_RETURN(card->ctx, rv); +} + +static int +lteid_process_fci(struct sc_card *card, struct sc_file *file, const u8 *buf, size_t buflen) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + int rv = iso_ops->process_fci(card, file, buf, buflen); + + if (rv != SC_SUCCESS) { + LOG_FUNC_RETURN(card->ctx, rv); + } + + // Card reports most of the file size as 0, even if they're not empty. + // This confuses PKCS#15 loader, and it fails to load/init keys/certs/pins/etc. + // As a quick workaround - lets report size to be something large enough to fit any object. + if (file->size == 0) { + file->size = 2048; + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +struct sc_card_driver * +sc_get_lteid_driver(void) +{ + struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); + + if (iso_ops == NULL) + iso_ops = iso_drv->ops; + + lteid_ops = *iso_ops; + lteid_ops.init = lteid_init; + lteid_ops.finish = lteid_finish; + lteid_ops.set_security_env = lteid_set_security_env; + lteid_ops.compute_signature = lteid_compute_signature; + lteid_ops.pin_cmd = lteid_pin_cmd; + lteid_ops.logout = lteid_logout; + lteid_ops.process_fci = lteid_process_fci; + + return <eid_drv; +} + +#else + +#include "opensc.h" + +struct sc_card_driver * +sc_get_lteid_driver(void) +{ + return NULL; +} + +#endif diff --git a/src/libopensc/card-masktech.c b/src/libopensc/card-masktech.c index ee8f6d308b..a1dca7b9be 100644 --- a/src/libopensc/card-masktech.c +++ b/src/libopensc/card-masktech.c @@ -17,10 +17,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -32,12 +32,12 @@ #include "iso7816.h" static struct sc_atr_table masktech_atrs[] = { - {"3B:89:80:01:4D:54:43:4F:53:70:02:00:04:31", + {"3B:89:80:01:4D:54:43:4F:53:70:02:00:04:31", "FF:FF:FF:FF:FF:FF:FF:FF:FF:FC:FF:FC:F4:F5" , NULL, SC_CARD_TYPE_MASKTECH_GENERIC, 0, NULL}, {"3B:88:80:01:00:00:00:00:77:81:80:00:6E", "FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:EE:FF:EE", NULL, SC_CARD_TYPE_MASKTECH_GENERIC, 0, NULL}, - {"3B:9D:13:81:31:60:35:80:31:C0:69:4D:54:43:4F:53:73:02:00:00:40", + {"3B:9D:13:81:31:60:35:80:31:C0:69:4D:54:43:4F:53:73:02:00:00:40", "FF:FF:FF:FF:FF:FF:FD:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FC:F0:F0", NULL, SC_CARD_TYPE_MASKTECH_GENERIC, 0, NULL}, {NULL, NULL, NULL, 0, 0, NULL} @@ -72,7 +72,7 @@ static int masktech_init(sc_card_t * card) unsigned long flags; struct masktech_private_data *data; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "masktech_init()\n"); + sc_log(card->ctx, "masktech_init()\n"); /* private data kept during the live of the driver */ if (!(data = (struct masktech_private_data *) malloc(sizeof(*data)))) @@ -104,7 +104,7 @@ static int masktech_set_security_env(sc_card_t *card, int se_num) { struct masktech_private_data *private_data; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "masktech_set_security_env(), keyRef = 0x%0x, algo = 0x%0x\n", + sc_log(card->ctx, "masktech_set_security_env(), keyRef = 0x%0x, algo = 0x%0lx\n", *env->key_ref, env->algorithm_flags); private_data = (struct masktech_private_data *) card->drv_data; @@ -114,7 +114,7 @@ static int masktech_set_security_env(sc_card_t *card, /* save the key reference */ if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { if (env->key_ref_len != 1) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid key reference supplied.\n"); + sc_log(card->ctx, "Invalid key reference supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } private_data->rsa_key_ref = env->key_ref[0]; @@ -136,8 +136,9 @@ static int masktech_compute_signature(sc_card_t *card, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 }; - assert(card != NULL && data != NULL && out != NULL); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "masktech_compute_signature()\n"); + if (card == NULL || data == NULL || out == NULL) + return SC_ERROR_INTERNAL; + sc_log(card->ctx, "masktech_compute_signature()\n"); /* retrieve the key reference */ private_data = (struct masktech_private_data *) card->drv_data; @@ -150,7 +151,7 @@ static int masktech_compute_signature(sc_card_t *card, /* check that it is a SHA256 with digest info*/ if ((datalen != sizeof(hdr_sha256) + 32) || (memcmp(hdr_sha256, data, sizeof(hdr_sha256)) != 0)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "It is not a SHA256 with digestinfo\n"); + sc_log(card->ctx, "It is not a SHA256 with digestinfo\n"); return SC_ERROR_NOT_SUPPORTED; } /* extract the SHA-256 hash */ @@ -175,8 +176,9 @@ static int masktech_decipher(sc_card_t *card, sc_apdu_t apdu; u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; - assert(card != NULL && crgram != NULL && out != NULL); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "masktech_decipher()\n"); + if (card == NULL || crgram == NULL || out == NULL) + return SC_ERROR_INTERNAL; + sc_log(card->ctx, "masktech_decipher()\n"); if (crgram_len > SC_MAX_EXT_APDU_BUFFER_SIZE) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); @@ -193,20 +195,19 @@ static int masktech_decipher(sc_card_t *card, apdu.datalen = crgram_len; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } /* unblock pin cmd */ static int masktech_pin_unblock(sc_card_t *card, - struct sc_pin_cmd_data *data, - int *tries_left) + struct sc_pin_cmd_data *data) { int rv = 0; struct sc_pin_cmd_data verify_data; @@ -221,8 +222,8 @@ static int masktech_pin_unblock(sc_card_t *card, verify_data.flags = data->flags; verify_data.pin1.prompt = data->pin1.prompt; - rv = iso_ops->pin_cmd(card, &verify_data, tries_left); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed - verify unblock PIN"); + rv = iso_ops->pin_cmd(card, &verify_data); + LOG_TEST_RET(card->ctx, rv, "APDU transmit failed - verify unblock PIN"); /* Build a SC_PIN_CMD_UNBLOCK APDU */ memset(&reset_data, 0, sizeof(reset_data)); @@ -235,15 +236,14 @@ static int masktech_pin_unblock(sc_card_t *card, reset_data.flags = data->flags | SC_PIN_CMD_IMPLICIT_CHANGE; reset_data.pin2.prompt = data->pin2.prompt; - rv = iso_ops->pin_cmd(card, &reset_data, tries_left); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed - reset unblock PIN"); + rv = iso_ops->pin_cmd(card, &reset_data); + LOG_TEST_RET(card->ctx, rv, "APDU transmit failed - reset unblock PIN"); return 0; } static int masktech_pin_change(sc_card_t *card, - struct sc_pin_cmd_data *data, - int *tries_left) + struct sc_pin_cmd_data *data) { int rv = 0; struct sc_pin_cmd_data verify_data; @@ -258,8 +258,8 @@ static int masktech_pin_change(sc_card_t *card, verify_data.flags = data->flags; verify_data.pin1.prompt = data->pin1.prompt; - rv = iso_ops->pin_cmd(card, &verify_data, tries_left); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed - verify change PIN"); + rv = iso_ops->pin_cmd(card, &verify_data); + LOG_TEST_RET(card->ctx, rv, "APDU transmit failed - verify change PIN"); /* Build a SC_PIN_CMD_CHANGE APDU */ memset(&change_data, 0, sizeof(change_data)); @@ -272,15 +272,14 @@ static int masktech_pin_change(sc_card_t *card, change_data.flags = data->flags | SC_PIN_CMD_IMPLICIT_CHANGE; change_data.pin2.prompt = data->pin2.prompt; - rv = iso_ops->pin_cmd(card, &change_data, tries_left); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed - chnage PIN"); + rv = iso_ops->pin_cmd(card, &change_data); + LOG_TEST_RET(card->ctx, rv, "APDU transmit failed - change PIN"); return 0; } static int masktech_pin_cmd(sc_card_t *card, - struct sc_pin_cmd_data *data, - int *tries_left) + struct sc_pin_cmd_data *data) { int rv; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); @@ -288,13 +287,13 @@ static int masktech_pin_cmd(sc_card_t *card, switch(data->cmd) { case SC_PIN_CMD_UNBLOCK: - rv = masktech_pin_unblock(card, data, tries_left); + rv = masktech_pin_unblock(card, data); break; case SC_PIN_CMD_CHANGE: - rv = masktech_pin_change(card, data, tries_left); + rv = masktech_pin_change(card, data); break; default: - rv = iso_ops->pin_cmd(card, data, tries_left); + rv = iso_ops->pin_cmd(card, data); break; } return rv; @@ -309,7 +308,7 @@ static int masktech_get_serialnr(sc_card_t * card, sc_serial_number_t * serial) int rv; if (!serial) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); /* Get smart card serial number */ card->cla = 0x80; @@ -320,14 +319,14 @@ static int masktech_get_serialnr(sc_card_t * card, sc_serial_number_t * serial) rv = sc_transmit_apdu(card, &apdu); card->cla = 0x00; - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; if (SC_MAX_SERIALNR < apdu.resplen) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } /* cache serial number */ card->serialnr.len = apdu.resplen; @@ -337,13 +336,13 @@ static int masktech_get_serialnr(sc_card_t * card, sc_serial_number_t * serial) if (serial) memcpy(serial, &card->serialnr, sizeof(*serial)); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int masktech_card_ctl(sc_card_t * card, unsigned long cmd, void *ptr) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "masktech_card_ctl()\n"); + sc_log(card->ctx, "masktech_card_ctl()\n"); switch (cmd) { case SC_CARDCTL_GET_SERIALNR: return masktech_get_serialnr(card, (sc_serial_number_t *) ptr); diff --git a/src/libopensc/card-mcrd.c b/src/libopensc/card-mcrd.c index cbba3fdfa4..6f9462a44a 100644 --- a/src/libopensc/card-mcrd.c +++ b/src/libopensc/card-mcrd.c @@ -19,10 +19,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -33,39 +33,22 @@ #include "internal.h" #include "asn1.h" #include "cardctl.h" -#include "esteid.h" +#include "gp.h" -static struct sc_atr_table mcrd_atrs[] = { +static const struct sc_atr_table mcrd_atrs[] = { {"3B:FF:94:00:FF:80:B1:FE:45:1F:03:00:68:D2:76:00:00:28:FF:05:1E:31:80:00:90:00:23", NULL, "Micardo 2.1/German BMI/D-Trust", SC_CARD_TYPE_MCRD_GENERIC, 0, NULL}, {"3b:6f:00:ff:00:68:d2:76:00:00:28:ff:05:1e:31:80:00:90:00", NULL, - "D-Trust", SC_CARD_TYPE_MCRD_DTRUST, 0, NULL}, + "D-Trust", SC_CARD_TYPE_MCRD_GENERIC, 0, NULL}, {"3b:ff:11:00:ff:80:b1:fe:45:1f:03:00:68:d2:76:00:00:28:ff:05:1e:31:80:00:90:00:a6", NULL, - "D-Trust", SC_CARD_TYPE_MCRD_DTRUST, 0, NULL}, - /* Certain pcsc-lite versions (1.5.3 for example on Ubuntu 10.04) incorrectly trunkate the wram ATR to the length of the cold ATR */ - /* See opensc.conf for further information */ - {"3B:FE:94:00:FF:80:B1:FA:45:1F:03:45:73:74:45:49:44:20", NULL, "Broken EstEID 1.1 warm", SC_CARD_TYPE_MCRD_ESTEID_V11, 0, NULL}, - {"3b:fe:94:00:ff:80:b1:fa:45:1f:03:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:43", NULL, "EstEID 1.0 cold", SC_CARD_TYPE_MCRD_ESTEID_V10, 0, NULL}, - {"3b:6e:00:ff:45:73:74:45:49:44:20:76:65:72:20:31:2e:30", NULL, "EstEID 1.0 cold", SC_CARD_TYPE_MCRD_ESTEID_V10, 0, NULL}, - {"3b:de:18:ff:c0:80:b1:fe:45:1f:03:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:2b", NULL, "EstEID 1.0 cold 2006", SC_CARD_TYPE_MCRD_ESTEID_V10, 0, NULL}, - {"3b:5e:11:ff:45:73:74:45:49:44:20:76:65:72:20:31:2e:30", NULL, "EstEID 1.0 warm 2006", SC_CARD_TYPE_MCRD_ESTEID_V10, 0, NULL}, - {"3b:6e:00:00:45:73:74:45:49:44:20:76:65:72:20:31:2e:30", NULL, "EstEID 1.1 cold", SC_CARD_TYPE_MCRD_ESTEID_V11, 0, NULL}, - {"3B:FE:18:00:00:80:31:FE:45:45:73:74:45:49:44:20:76:65:72:20:31:2E:30:A8", NULL, "EstEID 3.0 (dev1) cold", SC_CARD_TYPE_MCRD_ESTEID_V30, 0, NULL}, - {"3B:FE:18:00:00:80:31:FE:45:80:31:80:66:40:90:A4:56:1B:16:83:01:90:00:86", NULL, "EstEID 3.0 (dev1) warm", SC_CARD_TYPE_MCRD_ESTEID_V30, 0, NULL}, - {"3b:fe:18:00:00:80:31:fe:45:80:31:80:66:40:90:a4:16:2a:00:83:01:90:00:e1", NULL, "EstEID 3.0 (dev2) warm", SC_CARD_TYPE_MCRD_ESTEID_V30, 0, NULL}, - {"3b:fe:18:00:00:80:31:fe:45:80:31:80:66:40:90:a4:16:2a:00:83:0f:90:00:ef", NULL, "EstEID 3.0 (18.01.2011) warm", SC_CARD_TYPE_MCRD_ESTEID_V30, 0, NULL}, - {"3b:fa:18:00:00:80:31:fe:45:fe:65:49:44:20:2f:20:50:4b:49:03", NULL, "EstEID 3.5 cold", SC_CARD_TYPE_MCRD_ESTEID_V30, 0, NULL }, - {"3b:f8:18:00:00:80:31:fe:45:fe:41:5a:45:20:44:49:54:33", NULL, "AzeDIT 3.5 cold", SC_CARD_TYPE_MCRD_ESTEID_V30, 0, NULL }, + "D-Trust", SC_CARD_TYPE_MCRD_GENERIC, 0, NULL}, {NULL, NULL, NULL, 0, 0, NULL} }; -static unsigned char EstEID_v3_AID[] = {0xF0, 0x45, 0x73, 0x74, 0x45, 0x49, 0x44, 0x20, 0x76, 0x65, 0x72, 0x20, 0x31, 0x2E, 0x30}; -static unsigned char EstEID_v35_AID[] = {0xD2, 0x33, 0x00, 0x00, 0x00, 0x45, 0x73, 0x74, 0x45, 0x49, 0x44, 0x20, 0x76, 0x33, 0x35}; -static unsigned char AzeDIT_v35_AID[] = {0xD0, 0x31, 0x00, 0x00, 0x00, 0x44, 0x69, 0x67, 0x69, 0x49, 0x44}; static struct sc_card_operations mcrd_ops; static struct sc_card_driver mcrd_drv = { - "MICARDO 2.1 / EstEID 1.0 - 3.0", + "MICARDO 2.1", "mcrd", &mcrd_ops, NULL, 0, NULL @@ -89,14 +72,14 @@ enum { struct rule_record_s { struct rule_record_s *next; - int recno; + unsigned int recno; size_t datalen; u8 data[1]; }; struct keyd_record_s { struct keyd_record_s *next; - int recno; + unsigned int recno; size_t datalen; u8 data[1]; }; @@ -111,17 +94,16 @@ struct df_info_s { struct mcrd_priv_data { unsigned short curpath[MAX_CURPATH]; /* The currently selected path. */ - size_t curpathlen; /* Length of this path or 0 if unknown. */ int is_ef; /* True if the path points to an EF. */ + size_t curpathlen; /* Length of this path or 0 if unknown. */ struct df_info_s *df_infos; sc_security_env_t sec_env; /* current security environment */ }; -#define DRVDATA(card) ((struct mcrd_priv_data *) ((card)->drv_data)) +#define DRVDATA(card) ((struct mcrd_priv_data *) ((card)->drv_data)) static int load_special_files(sc_card_t * card); -static int select_part(sc_card_t * card, u8 kind, unsigned short int fid, - sc_file_t ** file); +static int select_part(sc_card_t * card, u8 kind, unsigned short int fid, sc_file_t ** file); /* Return the DF_info for the current path. If does not yet exist, create it. Returns NULL on error. */ @@ -131,23 +113,24 @@ static struct df_info_s *get_df_info(sc_card_t * card) struct mcrd_priv_data *priv = DRVDATA(card); struct df_info_s *dfi; - assert(!priv->is_ef); + if (priv->is_ef) + return NULL; if (!priv->curpathlen) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "no current path to find the df_info\n"); + sc_log(ctx, "no current path to find the df_info\n"); return NULL; } for (dfi = priv->df_infos; dfi; dfi = dfi->next) { if (dfi->pathlen == priv->curpathlen - && !memcmp(dfi->path, priv->curpath, - dfi->pathlen * sizeof *dfi->path)) + && !memcmp(dfi->path, priv->curpath, + dfi->pathlen * sizeof *dfi->path)) return dfi; } /* Not found, create it. */ dfi = calloc(1, sizeof *dfi); if (!dfi) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "out of memory while allocating df_info\n"); + sc_log(ctx, "out of memory while allocating df_info\n"); return NULL; } dfi->pathlen = priv->curpathlen; @@ -173,7 +156,7 @@ static void clear_special_files(struct df_info_s *dfi) } } -/* Some functionality straight from the EstEID manual. +/* * Official notice: Refer to the Micardo 2.1 Public manual. * Sad side: not available without a NDA. */ @@ -182,18 +165,12 @@ static int mcrd_delete_ref_to_authkey(sc_card_t * card) { sc_apdu_t apdu; int r; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - - assert(card != NULL); - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xA4); - - sbuf[0] = 0x83; - sbuf[1] = 0x00; - apdu.data = sbuf; - apdu.lc = 2; - apdu.datalen = 2; + u8 sbuf[2] = { 0x83, 0x00 }; + if(card == NULL) + return SC_ERROR_INTERNAL; + sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xA4, sbuf, 2, NULL, 0); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } @@ -201,83 +178,18 @@ static int mcrd_delete_ref_to_signkey(sc_card_t * card) { sc_apdu_t apdu; int r; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - assert(card != NULL); - - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB6); - - sbuf[0] = 0x83; - sbuf[1] = 0x00; - apdu.data = sbuf; - apdu.lc = 2; - apdu.datalen = 2; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); - -} - -static int mcrd_set_decipher_key_ref(sc_card_t * card, int key_reference) -{ - sc_apdu_t apdu; - sc_path_t path; - int r; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 keyref_data[SC_ESTEID_KEYREF_FILE_RECLEN]; - assert(card != NULL); - - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB8); - /* track the active keypair */ - sc_format_path("0033", &path); - r = sc_select_file(card, &path, NULL); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Can't select keyref info file 0x0033"); - r = sc_read_record(card, 1, keyref_data, - SC_ESTEID_KEYREF_FILE_RECLEN, SC_RECORD_BY_REC_NR); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Can't read keyref info file!"); - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "authkey reference 0x%02x%02x\n", - keyref_data[9], keyref_data[10]); - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "signkey reference 0x%02x%02x\n", - keyref_data[19], keyref_data[20]); - - sbuf[0] = 0x83; - sbuf[1] = 0x03; - sbuf[2] = 0x80; - switch (key_reference) { - case 1: - sbuf[3] = keyref_data[9]; - sbuf[4] = keyref_data[10]; - break; - case 2: - sbuf[3] = keyref_data[19]; - sbuf[4] = keyref_data[20]; - break; - } - apdu.data = sbuf; - apdu.lc = 5; - apdu.datalen = 5; + u8 sbuf[2] = { 0x83, 0x00 }; + if(card == NULL) + return SC_ERROR_INTERNAL; + sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xB6, sbuf, 2, NULL, 0); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } -int is_esteid_card(sc_card_t *card) { - switch(card->type) { - case SC_CARD_TYPE_MCRD_ESTEID_V10: - case SC_CARD_TYPE_MCRD_ESTEID_V11: - case SC_CARD_TYPE_MCRD_ESTEID_V30: - return 1; - } - - return 0; -} static int mcrd_match_card(sc_card_t * card) { - int i = 0, r = 0; - sc_apdu_t apdu; + int i = 0; i = _sc_match_atr(card, mcrd_atrs, &card->type); if (i >= 0) { @@ -285,112 +197,32 @@ static int mcrd_match_card(sc_card_t * card) return 1; } - sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xA4, 0x04, 0x00); - apdu.lc = sizeof(EstEID_v35_AID); - apdu.data = EstEID_v35_AID; - apdu.datalen = sizeof(EstEID_v35_AID); - apdu.resplen = 0; - apdu.le = 0; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "SELECT AID: %02X%02X", apdu.sw1, apdu.sw2); - if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { - sc_log(card->ctx, "AID found"); - card->type = SC_CARD_TYPE_MCRD_ESTEID_V30; - return 1; - } return 0; } static int mcrd_init(sc_card_t * card) { unsigned long flags; - struct mcrd_priv_data *priv; - int r; - sc_path_t tmppath; - sc_apdu_t apdu; - - priv = calloc(1, sizeof *priv); + struct mcrd_priv_data *priv = calloc(1, sizeof *priv); if (!priv) return SC_ERROR_OUT_OF_MEMORY; + priv->curpath[0] = MFID; + priv->curpathlen = 1; card->drv_data = priv; card->cla = 0x00; card->caps = SC_CARD_CAP_RNG; + flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE; + _sc_card_add_rsa_alg(card, 512, flags, 0); + _sc_card_add_rsa_alg(card, 768, flags, 0); + _sc_card_add_rsa_alg(card, 1024, flags, 0); - if (is_esteid_card(card)) { - /* Reset the MULTOS card to get to a known state */ - if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V11) - sc_reset(card, 0); - - /* Select the EstEID AID to get to a known state. - * For some reason a reset is required as well... */ - if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V30) { - flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASH_SHA1 | SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA256; - /* EstEID v3.0 has 2048 bit keys */ - _sc_card_add_rsa_alg(card, 2048, flags, 0); - sc_reset(card, 0); - - sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xA4, 0x04, 0x00); - apdu.lc = sizeof(EstEID_v3_AID); - apdu.data = EstEID_v3_AID; - apdu.datalen = sizeof(EstEID_v3_AID); - apdu.resplen = 0; - apdu.le = 0; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "SELECT AID: %02X%02X", apdu.sw1, apdu.sw2); - if(apdu.sw1 != 0x90 && apdu.sw2 != 0x00) - { - sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xA4, 0x04, 0x00); - apdu.lc = sizeof(EstEID_v35_AID); - apdu.data = EstEID_v35_AID; - apdu.datalen = sizeof(EstEID_v35_AID); - apdu.resplen = 0; - apdu.le = 0; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "SELECT AID: %02X%02X", apdu.sw1, apdu.sw2); - if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) { - sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xA4, 0x04, 0x00); - apdu.lc = sizeof(AzeDIT_v35_AID); - apdu.data = AzeDIT_v35_AID; - apdu.datalen = sizeof(AzeDIT_v35_AID); - apdu.resplen = 0; - apdu.le = 0; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "SELECT AID: %02X%02X", apdu.sw1, apdu.sw2); - if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) { - free(card->drv_data); - card->drv_data = NULL; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_CARD); - } - } - } - } else { - /* EstEID v1.0 and 1.1 have 1024 bit keys */ - flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA1; - _sc_card_add_rsa_alg(card, 1024, flags, 0); - } - } else { - flags = SC_ALGORITHM_RSA_RAW |SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE; - _sc_card_add_rsa_alg(card, 512, flags, 0); - _sc_card_add_rsa_alg(card, 768, flags, 0); - _sc_card_add_rsa_alg(card, 1024, flags, 0); - } - - priv->curpath[0] = MFID; - priv->curpathlen = 1; - - sc_format_path ("3f00", &tmppath); - r = sc_select_file (card, &tmppath, NULL); + if (SC_SUCCESS != sc_select_file (card, sc_get_mf_path(), NULL)) + sc_log(card->ctx, "Warning: select MF failed"); - /* Not needed for the fixed EstEID profile */ - if (!is_esteid_card(card)) - load_special_files(card); + load_special_files(card); - return r; + return SC_SUCCESS; } static int mcrd_finish(sc_card_t * card) @@ -403,6 +235,7 @@ static int mcrd_finish(sc_card_t * card) while (priv->df_infos) { struct df_info_s *tmp = priv->df_infos->next; clear_special_files(priv->df_infos); + free(priv->df_infos); priv->df_infos = tmp; } free(priv); @@ -414,7 +247,8 @@ static int mcrd_finish(sc_card_t * card) static int load_special_files(sc_card_t * card) { sc_context_t *ctx = card->ctx; - int r, recno; + int r; + unsigned int recno; struct df_info_s *dfi; struct rule_record_s *rule; struct keyd_record_s *keyd; @@ -425,129 +259,72 @@ static int load_special_files(sc_card_t * card) return 0; /* yes. */ clear_special_files(dfi); if (!dfi) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); /* Read rule file. Note that we bypass our cache here. */ r = select_part(card, MCRD_SEL_EF, EF_Rule, NULL); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "selecting EF_Rule failed"); + LOG_TEST_RET(ctx, r, "selecting EF_Rule failed"); for (recno = 1;; recno++) { u8 recbuf[256]; - r = sc_read_record(card, recno, recbuf, sizeof(recbuf), - SC_RECORD_BY_REC_NR); + r = sc_read_record(card, recno, 0, recbuf, sizeof(recbuf), + SC_RECORD_BY_REC_NR); if (r == SC_ERROR_RECORD_NOT_FOUND) break; - else if (r < 0) { + if (r < 0) { SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } else { - rule = malloc(sizeof *rule + r); + rule = malloc(sizeof *rule + (size_t)r); if (!rule) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); rule->recno = recno; - rule->datalen = r; + rule->datalen = (size_t)r; memcpy(rule->data, recbuf, r); rule->next = dfi->rule_file; dfi->rule_file = rule; } } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "new EF_Rule file loaded (%d records)\n", recno - 1); + sc_log(ctx, "new EF_Rule file loaded (%d records)\n", recno - 1); /* Read the KeyD file. Note that we bypass our cache here. */ r = select_part(card, MCRD_SEL_EF, EF_KeyD, NULL); if (r == SC_ERROR_FILE_NOT_FOUND) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "no EF_KeyD file available\n"); + sc_log(ctx, "no EF_KeyD file available\n"); return 0; /* That is okay. */ } - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "selecting EF_KeyD failed"); + LOG_TEST_RET(ctx, r, "selecting EF_KeyD failed"); for (recno = 1;; recno++) { u8 recbuf[256]; - r = sc_read_record(card, recno, recbuf, sizeof(recbuf), - SC_RECORD_BY_REC_NR); + r = sc_read_record(card, recno, 0, recbuf, sizeof(recbuf), + SC_RECORD_BY_REC_NR); if (r == SC_ERROR_RECORD_NOT_FOUND) break; - else if (r < 0) { + if (r < 0) { SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } else { - keyd = malloc(sizeof *keyd + r); + keyd = malloc(sizeof *keyd + (size_t)r); if (!keyd) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); keyd->recno = recno; - keyd->datalen = r; + keyd->datalen = (size_t) r; memcpy(keyd->data, recbuf, r); keyd->next = dfi->keyd_file; dfi->keyd_file = keyd; } } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "new EF_KeyD file loaded (%d records)\n", recno - 1); + sc_log(ctx, "new EF_KeyD file loaded (%d records)\n", recno - 1); /* FIXME: Do we need to restore the current DF? I guess it is not required, but we could try to do so by selecting 3fff? */ return 0; } -/* Return the SE number from the keyD for the FID. If ref_data is not - NULL the reference data is returned; this shoudl be an array of at - least 2 bytes. Returns -1 on error. */ -static int get_se_num_from_keyd(sc_card_t * card, unsigned short fid, - u8 * ref_data) -{ - sc_context_t *ctx = card->ctx; - struct df_info_s *dfi; - struct keyd_record_s *keyd; - size_t len, taglen; - const u8 *p, *tag; - char dbgbuf[2048]; - u8 fidbuf[2]; - - fidbuf[0] = (fid >> 8) & 0xFF; - fidbuf[1] = fid & 0xFF; - - dfi = get_df_info(card); - if (!dfi || !dfi->keyd_file) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "EF_keyD not loaded\n"); - return -1; - } - - for (keyd = dfi->keyd_file; keyd; keyd = keyd->next) { - p = keyd->data; - len = keyd->datalen; - - sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, - p, len, dbgbuf, sizeof dbgbuf); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "keyd no %d:\n%s", keyd->recno, dbgbuf); - - tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen); - if (!tag || taglen != 4 || - !(tag[2] == fidbuf[0] && tag[3] == fidbuf[1])) - continue; - /* Found a matching record. */ - if (ref_data) { - ref_data[0] = tag[0]; - ref_data[1] = tag[1]; - } - /* Look for the SE-DO */ - tag = sc_asn1_find_tag(ctx, p, len, 0x7B, &taglen); - if (!tag || !taglen) - continue; - p = tag; - len = taglen; - /* And now look for the referenced SE. */ - tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); - if (!tag || taglen != 1) - continue; - return *tag; /* found. */ - } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "EF_keyD for %04hx not found\n", fid); - return -1; -} - /* Process an ARR (7816-9/8.5.4) and setup the ACL. */ -static void process_arr(sc_card_t * card, sc_file_t * file, - const u8 * buf, size_t buflen) +static void process_arr(sc_card_t * card, const u8 * buf, size_t buflen) { sc_context_t *ctx = card->ctx; struct df_info_s *dfi; @@ -560,21 +337,20 @@ static void process_arr(sc_card_t * card, sc_file_t * file, /* Currently we support only the short for. */ if (buflen != 1) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "can't handle long ARRs\n"); + sc_log(ctx, "can't handle long ARRs\n"); return; } dfi = get_df_info(card); for (rule = dfi ? dfi->rule_file : NULL; rule && rule->recno != *buf; - rule = rule->next) ; + rule = rule->next) ; if (!rule) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "referenced EF_rule record %d not found\n", *buf); + sc_log(ctx, "referenced EF_rule record %d not found\n", *buf); return; } - sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, - rule->data, rule->datalen, dbgbuf, sizeof dbgbuf); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_hex_dump(rule->data, rule->datalen, dbgbuf, sizeof dbgbuf); + sc_log(ctx, "rule for record %d:\n%s", *buf, dbgbuf); p = rule->data; @@ -582,20 +358,20 @@ static void process_arr(sc_card_t * card, sc_file_t * file, skip = 1; /* Skip over initial unknown SC DOs. */ for (;;) { buf = p; - if (sc_asn1_read_tag(&p, left, &cla, &tag, &taglen) != - SC_SUCCESS) + if (sc_asn1_read_tag(&p, left, &cla, &tag, &taglen) != SC_SUCCESS + || p == NULL) break; - left -= (p - buf); + left -= (size_t)(p - buf); tag |= cla; if (tag == 0x80 && taglen != 1) { skip = 1; } else if (tag == 0x80) { /* AM byte. */ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " AM_DO: %02x\n", *p); + sc_log(ctx, " AM_DO: %02x\n", *p); skip = 0; } else if (tag >= 0x81 && tag <= 0x8f) { /* Cmd description */ - sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, p, taglen, dbgbuf, sizeof dbgbuf); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " AM_DO: cmd[%s%s%s%s] %s", + sc_hex_dump(p, taglen, dbgbuf, sizeof dbgbuf); + sc_log(ctx, " AM_DO: cmd[%s%s%s%s] %s", (tag & 8) ? "C" : "", (tag & 4) ? "I" : "", (tag & 2) ? "1" : "", @@ -604,33 +380,32 @@ static void process_arr(sc_card_t * card, sc_file_t * file, } else if (tag == 0x9C) { /* Proprietary state machine descrip. */ skip = 1; } else if (!skip) { - sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, p, taglen, dbgbuf, sizeof dbgbuf); switch (tag) { case 0x90: /* Always */ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " SC: always\n"); + sc_log(ctx, " SC: always\n"); break; case 0x97: /* Never */ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " SC: never\n"); + sc_log(ctx, " SC: never\n"); break; case 0xA4: /* Authentication, value is a CRT. */ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " SC: auth %s", dbgbuf); + sc_log_hex(ctx, " SC: auth", p, taglen); break; case 0xB4: case 0xB6: case 0xB8: /* Cmd or resp with SM, value is a CRT. */ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " SC: cmd/resp %s", dbgbuf); + sc_log_hex(ctx, " SC: cmd/resp", p, taglen); break; case 0x9E: /* Security Condition byte. */ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " SC: condition %s", dbgbuf); + sc_log_hex(ctx, " SC: condition", p, taglen); break; case 0xA0: /* OR template. */ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " SC: OR\n"); + sc_log(ctx, " SC: OR\n"); break; case 0xAF: /* AND template. */ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " SC: AND\n"); + sc_log(ctx, " SC: AND\n"); break; } } @@ -648,13 +423,13 @@ static void process_fcp(sc_card_t * card, sc_file_t * file, const u8 *tag = NULL, *p = buf; int bad_fde = 0; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "processing FCI bytes\n"); + sc_log(ctx, "processing FCI bytes\n"); /* File identifier. */ tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen); if (tag != NULL && taglen == 2) { file->id = (tag[0] << 8) | tag[1]; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, " file identifier: 0x%02X%02X\n", tag[0], tag[1]); } /* Number of data bytes in the file including structural information. */ @@ -669,17 +444,17 @@ static void process_fcp(sc_card_t * card, sc_file_t * file, } if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, " bytes in file: %d\n", bytes); - file->size = bytes; + file->size = (size_t)bytes; } if (tag == NULL) { tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, " bytes in file: %d\n", bytes); - file->size = bytes; + file->size = (size_t)bytes; } } @@ -692,7 +467,7 @@ static void process_fcp(sc_card_t * card, sc_file_t * file, const char *type; file->shareable = byte & 0x40 ? 1 : 0; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, " shareable: %s\n", (byte & 0x40) ? "yes" : "no"); file->ef_structure = byte & 0x07; @@ -713,9 +488,9 @@ static void process_fcp(sc_card_t * card, sc_file_t * file, type = "unknown"; break; } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, " type: %s\n", type); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, " EF structure: %d\n", byte & 0x07); } } @@ -730,14 +505,13 @@ static void process_fcp(sc_card_t * card, sc_file_t * file, file->namelen = taglen; for (i = 0; i < taglen; i++) { - if (isalnum(tag[i]) || ispunct(tag[i]) - || isspace(tag[i])) - name[i] = tag[i]; + if (isalnum(tag[i]) || ispunct(tag[i]) || isspace(tag[i])) + name[i] = (const char)tag[i]; else name[i] = '?'; } name[taglen] = 0; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " file name: %s\n", name); + sc_log(ctx, " file name: %s\n", name); } /* Proprietary information. */ @@ -761,15 +535,15 @@ static void process_fcp(sc_card_t * card, sc_file_t * file, /* Security attributes, reference to expanded format. */ tag = sc_asn1_find_tag(ctx, p, len, 0x8B, &taglen); - if (tag && taglen && !is_esteid_card(card)) { - process_arr(card, file, tag, taglen); + if (tag && taglen) { + process_arr(card, tag, taglen); } else if ((tag = sc_asn1_find_tag(ctx, p, len, 0xA1, &taglen)) - && taglen) { + && taglen) { /* Not found, but there is a Security Attribute Template for interface mode. */ tag = sc_asn1_find_tag(ctx, tag, taglen, 0x8B, &taglen); if (tag && taglen) - process_arr(card, file, tag, taglen); + process_arr(card, tag, taglen); } file->magic = SC_FILE_MAGIC; @@ -788,22 +562,13 @@ do_select(sc_card_t * card, u8 kind, if (kind == MCRD_SEL_EF) p2 = 0x04; if (kind == MCRD_SEL_DF) p2 = 0x0C; - sc_format_apdu(card, &apdu, buflen?SC_APDU_CASE_4_SHORT:SC_APDU_CASE_2_SHORT, 0xA4, kind, p2); - apdu.data = buf; - apdu.datalen = buflen; - apdu.lc = apdu.datalen; - apdu.resp = resbuf; - apdu.resplen = sizeof(resbuf); - apdu.le = 256; - + sc_format_apdu_ex(&apdu, 0x00, 0xA4, kind, p2, buf, buflen, resbuf, 256); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (!file) { if (apdu.sw1 == 0x61) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 0); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (!r && kind == MCRD_SEL_AID) - card->cache.current_path.len = 0; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } r = sc_check_sw(card, apdu.sw1, apdu.sw2); @@ -814,29 +579,28 @@ do_select(sc_card_t * card, u8 kind, if (file) { *file = sc_file_new(); if (!*file) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); (*file)->type = SC_FILE_TYPE_DF; return SC_SUCCESS; } } - if (p2 == 0x04 && apdu.resp[0] == 0x62) { + if (p2 == 0x04 && apdu.resplen > 2 && apdu.resp[0] == 0x62) { + if (apdu.resp[1] > apdu.resplen - 2) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); *file = sc_file_new(); if (!*file) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); - /* EstEID v3.0 cards are buggy and sometimes return a double 0x62 tag */ - if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V30 && apdu.resp[2] == 0x62) - process_fcp(card, *file, apdu.resp + 4, apdu.resp[3]); - else - process_fcp(card, *file, apdu.resp + 2, apdu.resp[1]); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + process_fcp(card, *file, apdu.resp + 2, apdu.resp[1]); return SC_SUCCESS; } - if (p2 != 0x0C && apdu.resp[0] == 0x6F) { + if (p2 != 0x0C && apdu.resplen > 2 && apdu.resp[0] == 0x6F) { + if (apdu.resp[1] > apdu.resplen - 2) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); *file = sc_file_new(); if (!*file) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); - if (apdu.resp[1] <= apdu.resplen) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); process_fcp(card, *file, apdu.resp + 2, apdu.resp[1]); return SC_SUCCESS; } @@ -847,13 +611,13 @@ do_select(sc_card_t * card, u8 kind, required. */ static int select_part(sc_card_t * card, u8 kind, unsigned short int fid, - sc_file_t ** file) + sc_file_t ** file) { u8 fbuf[2]; unsigned int len; int r; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "select_part (0x%04X, kind=%u)\n", fid, kind); if (fid == MFID) { @@ -875,8 +639,8 @@ select_part(sc_card_t * card, u8 kind, unsigned short int fid, to figure out whether the last path item is a DF or EF. */ static int select_down(sc_card_t * card, - unsigned short *pathptr, size_t pathlen, - int df_only, sc_file_t ** file) + unsigned short *pathptr, size_t pathlen, + int df_only, sc_file_t ** file) { struct mcrd_priv_data *priv = DRVDATA(card); int r; @@ -887,25 +651,25 @@ select_down(sc_card_t * card, for (; pathlen; pathlen--, pathptr++) { if (priv->curpathlen == MAX_CURPATH) - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL, - "path too long for cache"); + LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, + "path too long for cache"); r = -1; /* force DF select. */ if (pathlen == 1 && !df_only) { /* first try to select an EF and retry an DF on error. */ r = select_part(card, MCRD_SEL_EF, *pathptr, file); - if (!r) + if (r == SC_SUCCESS) found_ef = 1; } - if (r) + if (r != SC_SUCCESS) r = select_part(card, MCRD_SEL_DF, *pathptr, pathlen == 1 ? file : NULL); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to select DF"); + LOG_TEST_RET(card->ctx, r, "unable to select DF"); priv->curpath[priv->curpathlen] = *pathptr; priv->curpathlen++; } priv->is_ef = found_ef; - if (!found_ef && !is_esteid_card(card)) + if (!found_ef) load_special_files(card); return 0; @@ -920,7 +684,7 @@ select_down(sc_card_t * card, static int select_file_by_path(sc_card_t * card, unsigned short *pathptr, - size_t pathlen, sc_file_t ** file) + size_t pathlen, sc_file_t ** file) { struct mcrd_priv_data *priv = DRVDATA(card); int r; @@ -928,7 +692,8 @@ select_file_by_path(sc_card_t * card, unsigned short *pathptr, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert(!priv->curpathlen || priv->curpath[0] == MFID); + if (!(!priv->curpathlen || priv->curpath[0] == MFID)) + return SC_ERROR_INTERNAL; if (pathlen && *pathptr == 0x3FFF) { pathlen--; @@ -941,7 +706,7 @@ select_file_by_path(sc_card_t * card, unsigned short *pathptr, /* MF requested: clear the cache and select it. */ priv->curpathlen = 0; r = select_part(card, MCRD_SEL_MF, pathptr[0], file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to select MF"); + LOG_TEST_RET(card->ctx, r, "unable to select MF"); priv->curpath[0] = pathptr[0]; priv->curpathlen = 1; priv->is_ef = 0; @@ -949,7 +714,7 @@ select_file_by_path(sc_card_t * card, unsigned short *pathptr, /* Absolute addressing, check cache to avoid unnecessary selects. */ for (i = 0; (i < pathlen && i < priv->curpathlen - && pathptr[i] == priv->curpath[i]); i++) ; + && pathptr[i] == priv->curpath[i]); i++) ; if (!priv->curpathlen) { /* Need to do all selects starting at the root. */ priv->curpathlen = 0; @@ -969,7 +734,8 @@ select_file_by_path(sc_card_t * card, unsigned short *pathptr, /* This EF or DF was already selected, but we need to get the FCI, so we have to select again. */ - assert(priv->curpathlen > 1); + if (!(priv->curpathlen > 1)) + return SC_ERROR_INTERNAL; priv->curpathlen--; priv->is_ef = 0; r = select_down(card, pathptr + pathlen - 1, 1, @@ -988,16 +754,22 @@ select_file_by_path(sc_card_t * card, unsigned short *pathptr, /* Relative addressing without a current path. So we select the MF first. */ r = select_part(card, MCRD_SEL_MF, pathptr[0], file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to select MF"); + LOG_TEST_RET(card->ctx, r, "unable to select MF"); priv->curpath[0] = pathptr[0]; priv->curpathlen = 1; priv->is_ef = 0; } if (priv->is_ef) { - assert(priv->curpathlen > 1); + if(!(priv->curpathlen > 1)) + return SC_ERROR_INTERNAL; priv->curpathlen--; priv->is_ef = 0; } + /* Free the previously allocated file so we do not leak memory here */ + if (file) { + sc_file_free(*file); + *file = NULL; + } r = select_down(card, pathptr, pathlen, 0, file); } return r; @@ -1005,14 +777,15 @@ select_file_by_path(sc_card_t * card, unsigned short *pathptr, static int select_file_by_fid(sc_card_t * card, unsigned short *pathptr, - size_t pathlen, sc_file_t ** file) + size_t pathlen, sc_file_t ** file) { struct mcrd_priv_data *priv = DRVDATA(card); int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert(!priv->curpathlen || priv->curpath[0] == MFID); + if (!(!priv->curpathlen || priv->curpath[0] == MFID)) + return SC_ERROR_INTERNAL; if (pathlen > 1) return SC_ERROR_INVALID_ARGUMENTS; @@ -1028,7 +801,8 @@ select_file_by_fid(sc_card_t * card, unsigned short *pathptr, /* There is no current file. */ r = SC_ERROR_INTERNAL; } else { - assert(priv->curpathlen > 1); + if (!(priv->curpathlen > 1)) + return SC_ERROR_INTERNAL; priv->curpathlen--; priv->is_ef = 0; r = select_down(card, pathptr, 1, 0, file); @@ -1037,7 +811,7 @@ select_file_by_fid(sc_card_t * card, unsigned short *pathptr, /* MF requested: clear the cache and select it. */ priv->curpathlen = 0; r = select_part(card, MCRD_SEL_MF, MFID, file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to select MF"); + LOG_TEST_RET(card->ctx, r, "unable to select MF"); priv->curpath[0] = MFID; priv->curpathlen = 1; priv->is_ef = 0; @@ -1047,16 +821,22 @@ select_file_by_fid(sc_card_t * card, unsigned short *pathptr, /* Relative addressing without a current path. So we select the MF first. */ r = select_part(card, MCRD_SEL_MF, pathptr[0], file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to select MF"); + LOG_TEST_RET(card->ctx, r, "unable to select MF"); priv->curpath[0] = pathptr[0]; priv->curpathlen = 1; priv->is_ef = 0; } if (priv->is_ef) { - assert(priv->curpathlen > 1); + if (!(priv->curpathlen > 1)) + return SC_ERROR_INTERNAL; priv->curpathlen--; priv->is_ef = 0; } + /* Free the previously allocated file so we do not leak memory here */ + if (file) { + sc_file_free(*file); + *file = NULL; + } r = select_down(card, pathptr, 1, 0, file); } @@ -1072,21 +852,6 @@ mcrd_select_file(sc_card_t * card, const sc_path_t * path, sc_file_t ** file) SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - { - char line[256], *linep; - size_t i; - - linep = line; - linep += sprintf(linep, "ef=%d, curpath=", priv->is_ef); - - for (i = 0; i < priv->curpathlen; i++) { - sprintf(linep, "%04X", priv->curpath[i]); - linep += 4; - } - strcpy(linep, "\n"); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s", line); - } - if (path->type == SC_PATH_TYPE_DF_NAME) { if (path->len > 16) return SC_ERROR_INVALID_ARGUMENTS; @@ -1101,10 +866,11 @@ mcrd_select_file(sc_card_t * card, const sc_path_t * path, sc_file_t ** file) if ((path->len & 1) || path->len > sizeof(pathtmp)) return SC_ERROR_INVALID_ARGUMENTS; + memset(pathtmp, 0, sizeof pathtmp); pathptr = pathtmp; for (n = 0; n < path->len; n += 2) pathptr[n >> 1] = - (path->value[n] << 8) | path->value[n + 1]; + (unsigned short)((path->value[n] << 8) | path->value[n + 1]); pathlen = path->len >> 1; if (pathlen == priv->curpathlen && priv->is_ef != 2) { @@ -1127,205 +893,85 @@ mcrd_select_file(sc_card_t * card, const sc_path_t * path, sc_file_t ** file) if (samepath != 1 || priv->is_ef == 0 || priv->is_ef == 1) { if (path->type == SC_PATH_TYPE_PATH) - r = select_file_by_path(card, pathptr, pathlen, - file); + r = select_file_by_path(card, pathptr, pathlen, file); else { /* SC_PATH_TYPE_FILEID */ - r = select_file_by_fid(card, pathptr, pathlen, - file); + r = select_file_by_fid(card, pathptr, pathlen, file); } } } - { - char line[256], *linep = line; - size_t i; - linep += - sprintf(linep, " result=%d, ef=%d, curpath=", r, - priv->is_ef); - for (i = 0; i < priv->curpathlen; i++) { - sprintf(linep, "%04X", priv->curpath[i]); - linep += 4; - } - strcpy(linep, "\n"); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s", line); - } return r; } -/* Crypto operations */ -static int mcrd_restore_se(sc_card_t * card, int se_num) -{ - sc_apdu_t apdu; - int r; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0xF3, se_num); - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - return sc_check_sw(card, apdu.sw1, apdu.sw2); -} - - /* It seems that MICARDO does not fully comply with ISO, so I use - values gathered from peeking actual signing opeations using a + values gathered from peeking actual signing operations using a different system. It has been generalized [?] and modified by information coming from - openpgp card implementation, EstEID 'manual' and some other sources. -mp + openpgp card implementation and some other sources. -mp */ static int mcrd_set_security_env(sc_card_t * card, const sc_security_env_t * env, int se_num) { - struct mcrd_priv_data *priv = DRVDATA(card); + struct mcrd_priv_data *priv; sc_apdu_t apdu; - sc_path_t tmppath; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 *p; - int r, locked = 0; - - assert(card != NULL && env != NULL); - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); - - /* special environment handling for esteid, stolen from openpgp */ - if (is_esteid_card(card)) { - /* some sanity checks */ - if (env->flags & SC_SEC_ENV_ALG_PRESENT) { - if (env->algorithm != SC_ALGORITHM_RSA) - return SC_ERROR_INVALID_ARGUMENTS; - } - if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) - || env->key_ref_len != 1) - return SC_ERROR_INVALID_ARGUMENTS; - - /* Make sure we always start from MF */ - sc_format_path ("3f00", &tmppath); - r = sc_select_file (card, &tmppath, NULL); - if (r < 0) - return r; - /* We now know that cache is not valid */ - select_esteid_df(card); - switch (env->operation) { - case SC_SEC_OPERATION_DECIPHER: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "Using keyref %d to dechiper\n", - env->key_ref[0]); - mcrd_restore_se(card, 6); - mcrd_delete_ref_to_authkey(card); - mcrd_delete_ref_to_signkey(card); - mcrd_set_decipher_key_ref(card, env->key_ref[0]); - break; - case SC_SEC_OPERATION_SIGN: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Using keyref %d to sign\n", - env->key_ref[0]); - mcrd_restore_se(card, 1); - break; - default: - return SC_ERROR_INVALID_ARGUMENTS; - } - priv->sec_env = *env; - return 0; - } + u8 sbuf[5] = {0x83, 0x03, 0x80, 0, 0}; + int r = 0, locked = 0; - if (card->type == SC_CARD_TYPE_MCRD_DTRUST - || card->type == SC_CARD_TYPE_MCRD_GENERIC) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Using SC_CARD_TYPE_MCRD_DTRUST\n"); - /* some sanity checks */ - if (env->flags & SC_SEC_ENV_ALG_PRESENT) { - if (env->algorithm != SC_ALGORITHM_RSA) - return SC_ERROR_INVALID_ARGUMENTS; - } - if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) - || env->key_ref_len != 1) - return SC_ERROR_INVALID_ARGUMENTS; + if (card == NULL || env == NULL) + return SC_ERROR_INTERNAL; + LOG_FUNC_CALLED(card->ctx); + priv = DRVDATA(card); - switch (env->operation) { - case SC_SEC_OPERATION_DECIPHER: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "Using keyref %d to dechiper\n", - env->key_ref[0]); - mcrd_delete_ref_to_authkey(card); - mcrd_delete_ref_to_signkey(card); - mcrd_set_decipher_key_ref(card, env->key_ref[0]); - break; - case SC_SEC_OPERATION_SIGN: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Using keyref %d to sign\n", - env->key_ref[0]); - break; - default: + /* some sanity checks */ + if (env->flags & SC_SEC_ENV_ALG_PRESENT) { + if (env->algorithm != SC_ALGORITHM_RSA) return SC_ERROR_INVALID_ARGUMENTS; - } - priv->sec_env = *env; } + if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) + || env->key_ref_len != 1) + return SC_ERROR_INVALID_ARGUMENTS; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); - apdu.le = 0; - p = sbuf; switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: - apdu.p1 = 0x41; - apdu.p2 = 0xB8; + sc_log(card->ctx, "Using keyref %d to decipher\n", env->key_ref[0]); + mcrd_delete_ref_to_authkey(card); + mcrd_delete_ref_to_signkey(card); break; case SC_SEC_OPERATION_SIGN: - apdu.p1 = 0x41; - apdu.p2 = 0xB6; + sc_log(card->ctx, "Using keyref %d to sign\n", env->key_ref[0]); break; default: return SC_ERROR_INVALID_ARGUMENTS; } - *p++ = 0x83; - *p++ = 0x03; - *p++ = 0x80; - - if (card->type == SC_CARD_TYPE_MCRD_DTRUST - || card->type == SC_CARD_TYPE_MCRD_GENERIC) { - unsigned char fid; - - fid = env->key_ref[0]; - *p = fid; - p++; - *p = 0; - p++; - } else if (is_esteid_card(card)) { - if ((env->flags & SC_SEC_ENV_FILE_REF_PRESENT) - && env->file_ref.len > 1) { - unsigned short fid; - int num; - - fid = env->file_ref.value[env->file_ref.len - 2] << 8; - fid |= env->file_ref.value[env->file_ref.len - 1]; - num = get_se_num_from_keyd(card, fid, p); - if (num != -1) { - /* Need to restore the security environmnet. */ - if (num) { - r = mcrd_restore_se(card, num); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, - "mcrd_enable_se failed"); - } - p += 2; - } - } - } else { + priv->sec_env = *env; + + sbuf[3] = env->key_ref[0]; + switch (env->operation) { + case SC_SEC_OPERATION_DECIPHER: + sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xB8, sbuf, 5, NULL, 0); + break; + case SC_SEC_OPERATION_SIGN: + sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xB6, sbuf, 5, NULL, 0); + break; + default: return SC_ERROR_INVALID_ARGUMENTS; } - r = p - sbuf; - apdu.lc = r; - apdu.datalen = r; - apdu.data = sbuf; - apdu.resplen = 0; if (se_num > 0) { r = sc_lock(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); locked = 1; } if (apdu.datalen != 0) { r = sc_transmit_apdu(card, &apdu); if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "%s: APDU transmit failed", sc_strerror(r)); goto err; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "%s: Card returned error", sc_strerror(r)); goto err; } @@ -1333,9 +979,9 @@ static int mcrd_set_security_env(sc_card_t * card, if (se_num <= 0) return 0; sc_unlock(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); - err: +err: if (locked) sc_unlock(card); return r; @@ -1343,97 +989,59 @@ static int mcrd_set_security_env(sc_card_t * card, /* heavily modified by -mp */ static int mcrd_compute_signature(sc_card_t * card, - const u8 * data, size_t datalen, - u8 * out, size_t outlen) + const u8 * data, size_t datalen, + u8 * out, size_t outlen) { struct mcrd_priv_data *priv = DRVDATA(card); - sc_security_env_t *env = &priv->sec_env; + sc_security_env_t *env = NULL; int r; sc_apdu_t apdu; - assert(card != NULL && data != NULL && out != NULL); - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + if (data == NULL || out == NULL) + return SC_ERROR_INVALID_ARGUMENTS; + env = &priv->sec_env; + + LOG_FUNC_CALLED(card->ctx); if (env->operation != SC_SEC_OPERATION_SIGN) return SC_ERROR_INVALID_ARGUMENTS; if (datalen > 255) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "Will compute signature (%d) for %"SC_FORMAT_LEN_SIZE_T"u (0x%02"SC_FORMAT_LEN_SIZE_T"x) bytes using key %d algorithm %d flags %d\n", + sc_log(card->ctx, + "Will compute signature (%d) for %"SC_FORMAT_LEN_SIZE_T"u (0x%02"SC_FORMAT_LEN_SIZE_T"x) bytes using key %d algorithm %lu flags %lu\n", env->operation, datalen, datalen, env->key_ref[0], env->algorithm, env->algorithm_flags); - switch (env->key_ref[0]) { - case SC_ESTEID_AUTH: /* authentication key */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0, 0); - break; - default: - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, - 0x2A, 0x9E, 0x9A); - - } - apdu.lc = datalen; - apdu.data = data; - apdu.datalen = datalen; - apdu.le = 0x80; - apdu.resp = out; - apdu.resplen = outlen; - + if (env->key_ref[0] == 1) /* authentication key */ + sc_format_apdu_ex(&apdu, 0x00, 0x88, 0, 0, data, datalen, out, MIN(0x80U, outlen)); + else + sc_format_apdu_ex(&apdu, 0x00, 0x2A, 0x9E, 0x9A, data, datalen, out, MIN(0x80U, outlen)); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); + LOG_TEST_RET(card->ctx, r, "Card returned error"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)apdu.resplen); } /* added by -mp, to give pin information in the card driver (pkcs15emu->driver needed) */ -static int mcrd_pin_cmd(sc_card_t * card, struct sc_pin_cmd_data *data, - int *tries_left) +static int mcrd_pin_cmd(sc_card_t * card, struct sc_pin_cmd_data *data) { - int r; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); data->pin1.offset = 5; - data->pin1.length_offset = 4; data->pin2.offset = 5; - data->pin2.length_offset = 4; - - if (is_esteid_card(card) && data->cmd == SC_PIN_CMD_GET_INFO) { - sc_path_t tmppath; - u8 buf[16]; - int ref_to_record[] = {3,1,2}; - - /* the file with key pin info (tries left) 4.5 EF_PwdC */ - /* XXX: cheat the file path cache by always starting fresh from MF */ - sc_format_path ("3f00", &tmppath); - r = sc_select_file (card, &tmppath, NULL); - if (r < 0) - return SC_ERROR_INTERNAL; - - sc_format_path ("3f000016", &tmppath); - r = sc_select_file (card, &tmppath, NULL); - if (r < 0) - return SC_ERROR_INTERNAL; - - /* read the number of tries left for the PIN */ - r = sc_read_record (card, ref_to_record[data->pin_reference], buf, sizeof(buf), SC_RECORD_BY_REC_NR); - if (r < 0) - return SC_ERROR_INTERNAL; - if (buf[0] != 0x80 || buf[3] != 0x90) - return SC_ERROR_INTERNAL; - data->pin1.tries_left = buf[5]; - data->pin1.max_tries = buf[2]; - data->pin1.logged_in = SC_PIN_STATE_UNKNOWN; - return SC_SUCCESS; - } - if (card->type == SC_CARD_TYPE_MCRD_DTRUST - || card->type == SC_CARD_TYPE_MCRD_GENERIC) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "modify pin reference for D-Trust\n"); + if (card->type == SC_CARD_TYPE_MCRD_GENERIC) { + sc_log(card->ctx, "modify pin reference for D-Trust\n"); if (data->pin_reference == 0x02) data->pin_reference = data->pin_reference | 0x80; } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, iso_ops->pin_cmd(card, data, tries_left)); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, iso_ops->pin_cmd(card, data)); +} + +static int mcrd_logout(sc_card_t * card) +{ + return SC_ERROR_NOT_SUPPORTED; } /* Driver binding */ @@ -1451,6 +1059,7 @@ static struct sc_card_driver *sc_get_driver(void) mcrd_ops.set_security_env = mcrd_set_security_env; mcrd_ops.compute_signature = mcrd_compute_signature; mcrd_ops.pin_cmd = mcrd_pin_cmd; + mcrd_ops.logout = mcrd_logout; return &mcrd_drv; } diff --git a/src/libopensc/card-miocos.c b/src/libopensc/card-miocos.c deleted file mode 100644 index a40451a0d9..0000000000 --- a/src/libopensc/card-miocos.c +++ /dev/null @@ -1,504 +0,0 @@ -/* - * card-miocos.c: Support for PKI cards by Miotec - * - * Copyright (C) 2002 Juha Yrjölä - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include - -#include "internal.h" -#include "asn1.h" -#include "cardctl.h" - -static struct sc_atr_table miocos_atrs[] = { - /* Test card with 32 kB memory */ - { "3B:9D:94:40:23:00:68:10:11:4D:69:6F:43:4F:53:00:90:00", NULL, NULL, SC_CARD_TYPE_MIOCOS_GENERIC, 0, NULL }, - /* Test card with 64 kB memory */ - { "3B:9D:94:40:23:00:68:20:01:4D:69:6F:43:4F:53:00:90:00", NULL, NULL, SC_CARD_TYPE_MIOCOS_GENERIC, 0, NULL }, - { NULL, NULL, NULL, 0, 0, NULL } -}; - -static struct sc_card_operations miocos_ops; -static struct sc_card_driver miocos_drv = { - "MioCOS 1.1", - "miocos", - &miocos_ops, - NULL, 0, NULL -}; - -static int miocos_match_card(sc_card_t *card) -{ - int i; - - i = _sc_match_atr(card, miocos_atrs, &card->type); - if (i < 0) - return 0; - return 1; -} - -static int miocos_init(sc_card_t *card) -{ - card->name = "MioCOS"; - card->cla = 0x00; - - if (1) { - unsigned long flags; - - flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1; - flags |= SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_HASH_SHA1; - - _sc_card_add_rsa_alg(card, 1024, flags, 0); - } - - /* read_binary and friends shouldn't do more than 244 bytes - * per operation */ - card->max_send_size = 244; - card->max_recv_size = 244; - - return 0; -} - -static const struct sc_card_operations *iso_ops = NULL; - -static int acl_to_byte(const sc_acl_entry_t *e) -{ - switch (e->method) { - case SC_AC_NONE: - return 0x00; - case SC_AC_CHV: - case SC_AC_TERM: - case SC_AC_AUT: - if (e->key_ref == SC_AC_KEY_REF_NONE) - return -1; - if (e->key_ref < 1 || e->key_ref > 14) - return -1; - return e->key_ref; - case SC_AC_NEVER: - return 0x0F; - } - return 0x00; -} - -static int encode_file_structure(sc_card_t *card, const sc_file_t *file, - u8 *buf, size_t *buflen) -{ - u8 *p = buf; - const int df_ops[8] = { - SC_AC_OP_DELETE, SC_AC_OP_CREATE, - /* RFU */ -1, /* CREATE AC */ SC_AC_OP_CREATE, - /* UPDATE AC */ SC_AC_OP_CREATE, -1, -1, -1 - }; - const int ef_ops[8] = { - /* DELETE */ SC_AC_OP_UPDATE, -1, SC_AC_OP_READ, - SC_AC_OP_UPDATE, -1, -1, SC_AC_OP_INVALIDATE, - SC_AC_OP_REHABILITATE - }; - const int key_ops[8] = { - /* DELETE */ SC_AC_OP_UPDATE, -1, -1, - SC_AC_OP_UPDATE, SC_AC_OP_CRYPTO, -1, SC_AC_OP_INVALIDATE, - SC_AC_OP_REHABILITATE - }; - const int *ops; - int i; - - *p++ = file->id >> 8; - *p++ = file->id & 0xFF; - switch (file->type) { - case SC_FILE_TYPE_DF: - *p++ = 0x20; - ops = df_ops; - break; - case SC_FILE_TYPE_WORKING_EF: - switch (file->ef_structure) { - case SC_FILE_EF_TRANSPARENT: - *p++ = 0x40; - break; - case SC_FILE_EF_LINEAR_FIXED: - *p++ = 0x41; - break; - case SC_FILE_EF_CYCLIC: - *p++ = 0x43; - break; - default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid EF structure\n"); - return SC_ERROR_INVALID_ARGUMENTS; - } - ops = ef_ops; - break; - case SC_FILE_TYPE_INTERNAL_EF: - *p++ = 0x44; - ops = key_ops; - break; - default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown file type\n"); - return SC_ERROR_INVALID_ARGUMENTS; - } - if (file->type == SC_FILE_TYPE_DF) { - *p++ = 0; - *p++ = 0; - } else { - *p++ = file->size >> 8; - *p++ = file->size & 0xFF; - } - if (file->sec_attr_len == 4) { - memcpy(p, file->sec_attr, 4); - p += 4; - } else for (i = 0; i < 8; i++) { - u8 nibble; - - if (ops[i] == -1) - nibble = 0x00; - else { - int byte = acl_to_byte(sc_file_get_acl_entry(file, ops[i])); - if (byte < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid ACL\n"); - return SC_ERROR_INVALID_ARGUMENTS; - } - nibble = byte; - } - if ((i & 1) == 0) - *p = nibble << 4; - else { - *p |= nibble & 0x0F; - p++; - } - } - if (file->type == SC_FILE_TYPE_WORKING_EF && - file->ef_structure != SC_FILE_EF_TRANSPARENT) - *p++ = file->record_length; - else - *p++ = 0; - if (file->status & SC_FILE_STATUS_INVALIDATED) - *p++ = 0; - else - *p++ = 0x01; - if (file->type == SC_FILE_TYPE_DF && file->namelen) { - assert(file->namelen <= 16); - memcpy(p, file->name, file->namelen); - p += file->namelen; - } - *buflen = p - buf; - - return 0; -} - -static int miocos_create_file(sc_card_t *card, sc_file_t *file) -{ - sc_apdu_t apdu; - u8 sbuf[32]; - size_t buflen; - int r; - - r = encode_file_structure(card, file, sbuf, &buflen); - if (r) - return r; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); - apdu.data = sbuf; - apdu.datalen = buflen; - apdu.lc = buflen; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if (apdu.sw1 == 0x6A && apdu.sw2 == 0x89) - return SC_ERROR_FILE_ALREADY_EXISTS; - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error"); - - return 0; -} - -static int miocos_set_security_env(sc_card_t *card, - const sc_security_env_t *env, - int se_num) -{ - if (env->flags & SC_SEC_ENV_ALG_PRESENT) { - sc_security_env_t tmp; - - tmp = *env; - tmp.flags &= ~SC_SEC_ENV_ALG_PRESENT; - tmp.flags |= SC_SEC_ENV_ALG_REF_PRESENT; - if (tmp.algorithm != SC_ALGORITHM_RSA) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Only RSA algorithm supported.\n"); - return SC_ERROR_NOT_SUPPORTED; - } - tmp.algorithm_ref = 0x00; - /* potential FIXME: return an error, if an unsupported - * pad or hash was requested, although this shouldn't happen. - */ - if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) - tmp.algorithm_ref = 0x02; - if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) - tmp.algorithm_ref |= 0x10; - return iso_ops->set_security_env(card, &tmp, se_num); - } - return iso_ops->set_security_env(card, env, se_num); -} - -static void add_acl_entry(sc_file_t *file, int op, u8 byte) -{ - unsigned int method, key_ref = SC_AC_KEY_REF_NONE; - - switch (byte) { - case 0: - method = SC_AC_NONE; - break; - case 15: - method = SC_AC_NEVER; - break; - default: - method = SC_AC_CHV; - key_ref = byte; - break; - } - sc_file_add_acl_entry(file, op, method, key_ref); -} - -static void parse_sec_attr(sc_file_t *file, const u8 *buf, size_t len) -{ - int i; - const int df_ops[8] = { - SC_AC_OP_DELETE, SC_AC_OP_CREATE, - -1, /* CREATE AC */ -1, /* UPDATE AC */ -1, -1, -1, -1 - }; - const int ef_ops[8] = { - SC_AC_OP_DELETE, -1, SC_AC_OP_READ, - SC_AC_OP_UPDATE, -1, -1, SC_AC_OP_INVALIDATE, - SC_AC_OP_REHABILITATE - }; - const int key_ops[8] = { - SC_AC_OP_DELETE, -1, -1, - SC_AC_OP_UPDATE, SC_AC_OP_CRYPTO, -1, SC_AC_OP_INVALIDATE, - SC_AC_OP_REHABILITATE - }; - const int *ops; - - if (len < 4) - return; - switch (file->type) { - case SC_FILE_TYPE_WORKING_EF: - ops = ef_ops; - break; - case SC_FILE_TYPE_INTERNAL_EF: - ops = key_ops; - break; - case SC_FILE_TYPE_DF: - ops = df_ops; - break; - default: - return; - } - for (i = 0; i < 8; i++) { - if (ops[i] == -1) - continue; - if ((i & 1) == 0) - add_acl_entry(file, ops[i], (u8)(buf[i / 2] >> 4)); - else - add_acl_entry(file, ops[i], (u8)(buf[i / 2] & 0x0F)); - } -} - -static int miocos_get_acl(sc_card_t *card, sc_file_t *file) -{ - sc_apdu_t apdu; - u8 rbuf[256]; - const u8 *seq = rbuf; - size_t left; - int r; - unsigned int i; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0x01); - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - apdu.le = sizeof(rbuf); - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if (apdu.resplen == 0) - return sc_check_sw(card, apdu.sw1, apdu.sw2); - left = apdu.resplen; - seq = sc_asn1_skip_tag(card->ctx, &seq, &left, - SC_ASN1_SEQUENCE | SC_ASN1_CONS, &left); - if (seq == NULL) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to process reply"); - for (i = 1; i < 15; i++) { - int j; - const u8 *tag; - size_t taglen; - - tag = sc_asn1_skip_tag(card->ctx, &seq, &left, - SC_ASN1_CTX | i, &taglen); - if (tag == NULL || taglen == 0) - continue; - for (j = 0; j < SC_MAX_AC_OPS; j++) { - sc_acl_entry_t *e; - - e = (sc_acl_entry_t *) sc_file_get_acl_entry(file, j); - if (e == NULL) - continue; - if (e->method != SC_AC_CHV) - continue; - if (e->key_ref != i) - continue; - switch (tag[0]) { - case 0x01: - e->method = SC_AC_CHV; - break; - case 0x02: - e->method = SC_AC_AUT; - break; - default: - e->method = SC_AC_UNKNOWN; - break; - } - } - } - return 0; -} - -static int miocos_select_file(sc_card_t *card, - const sc_path_t *in_path, - sc_file_t **file) -{ - int r; - - r = iso_ops->select_file(card, in_path, file); - if (r) - return r; - if (file != NULL && *file != NULL) { - parse_sec_attr(*file, (*file)->sec_attr, (*file)->sec_attr_len); - miocos_get_acl(card, *file); - } - - return 0; -} - -static int miocos_list_files(sc_card_t *card, u8 *buf, size_t buflen) -{ - sc_apdu_t apdu; - int r; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0); - apdu.resp = buf; - apdu.resplen = buflen; - apdu.le = buflen > 256 ? 256 : buflen; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if (apdu.resplen == 0) - return sc_check_sw(card, apdu.sw1, apdu.sw2); - return apdu.resplen; -} - -static int miocos_delete_file(sc_card_t *card, const sc_path_t *path) -{ - int r; - sc_apdu_t apdu; - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type has to be SC_PATH_TYPE_FILE_ID\n"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); - } - r = sc_select_file(card, path, NULL); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select file to be deleted"); - - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, 0x00, 0x00); - apdu.cla = 0xA0; - - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - return sc_check_sw(card, apdu.sw1, apdu.sw2); -} - -static int miocos_create_ac(sc_card_t *card, - struct sc_cardctl_miocos_ac_info *ac) -{ - sc_apdu_t apdu; - u8 sbuf[20]; - int miocos_type, r; - size_t sendsize; - - if (ac->max_tries > 15) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); - switch (ac->type) { - case SC_CARDCTL_MIOCOS_AC_PIN: - if (ac->max_unblock_tries > 15) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); - miocos_type = 0x01; - sbuf[0] = (ac->max_tries << 4) | ac->max_tries; - sbuf[1] = 0xFF; /* FIXME... */ - memcpy(sbuf + 2, ac->key_value, 8); - sbuf[10] = (ac->max_unblock_tries << 4) | ac->max_unblock_tries; - sbuf[11] = 0xFF; - memcpy(sbuf + 12, ac->unblock_value, 8); - sendsize = 20; - break; - default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "AC type %d not supported\n", ac->type); - return SC_ERROR_NOT_SUPPORTED; - } - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x1E, miocos_type, - ac->ref); - apdu.lc = sendsize; - apdu.datalen = sendsize; - apdu.data = sbuf; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - return sc_check_sw(card, apdu.sw1, apdu.sw2); -} - -static int miocos_card_ctl(sc_card_t *card, unsigned long cmd, - void *arg) -{ - switch (cmd) { - case SC_CARDCTL_MIOCOS_CREATE_AC: - return miocos_create_ac(card, (struct sc_cardctl_miocos_ac_info *) arg); - } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "card_ctl command 0x%lX not supported\n", cmd); - return SC_ERROR_NOT_SUPPORTED; -} - - -static struct sc_card_driver * sc_get_driver(void) -{ - struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); - - miocos_ops = *iso_drv->ops; - miocos_ops.match_card = miocos_match_card; - miocos_ops.init = miocos_init; - if (iso_ops == NULL) - iso_ops = iso_drv->ops; - miocos_ops.create_file = miocos_create_file; - miocos_ops.set_security_env = miocos_set_security_env; - miocos_ops.select_file = miocos_select_file; - miocos_ops.list_files = miocos_list_files; - miocos_ops.delete_file = miocos_delete_file; - miocos_ops.card_ctl = miocos_card_ctl; - - return &miocos_drv; -} - -#if 1 -struct sc_card_driver * sc_get_miocos_driver(void) -{ - return sc_get_driver(); -} -#endif diff --git a/src/libopensc/card-muscle.c b/src/libopensc/card-muscle.c index 85862b7e91..18ca233a90 100644 --- a/src/libopensc/card-muscle.c +++ b/src/libopensc/card-muscle.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -42,7 +42,7 @@ static struct sc_card_driver muscle_drv = { NULL, 0, NULL }; -static struct sc_atr_table muscle_atrs[] = { +static const struct sc_atr_table muscle_atrs[] = { /* Tyfone JCOP 242R2 cards */ { "3b:6d:00:00:ff:54:79:66:6f:6e:65:20:32:34:32:52:32", NULL, NULL, SC_CARD_TYPE_MUSCLE_JCOP242R2_NO_EXT_APDU, 0, NULL }, /* Aladdin eToken PRO USB 72K Java */ @@ -81,10 +81,6 @@ static int muscle_match_card(sc_card_t *card) u8 response[64]; int r; - /* Since we send an APDU, the card's logout function may be called... - * however it's not always properly nulled out... */ - card->ops->logout = NULL; - if (msc_select_applet(card, muscleAppletId, sizeof muscleAppletId) == 1) { /* Muscle applet is present, check the protocol version to be sure */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x3C, 0x00, 0x00); @@ -93,10 +89,12 @@ static int muscle_match_card(sc_card_t *card) apdu.resplen = 64; apdu.resp = response; r = sc_transmit_apdu(card, &apdu); - if (r == SC_SUCCESS && response[0] == 0x01) { - card->type = SC_CARD_TYPE_MUSCLE_V1; - return 1; + if (r == SC_SUCCESS && apdu.resplen > 1 && response[0] == 0x01) { + card->type = SC_CARD_TYPE_MUSCLE_V1; + } else { + card->type = SC_CARD_TYPE_MUSCLE_GENERIC; } + return 1; } return 0; } @@ -120,7 +118,9 @@ static unsigned short muscle_parse_singleAcl(const sc_acl_entry_t* acl) case SC_AC_UNKNOWN: break; case SC_AC_CHV: - acl_entry |= (1 << key); /* Assuming key 0 == SO */ + /* Ignore shift with more bits that acl_entry has */ + if ((size_t) key < sizeof(acl_entry) * 8) + acl_entry |= (1u << key); /* Assuming key 0 == SO */ break; case SC_AC_AUT: case SC_AC_TERM: @@ -136,7 +136,8 @@ static unsigned short muscle_parse_singleAcl(const sc_acl_entry_t* acl) static void muscle_parse_acls(const sc_file_t* file, unsigned short* read_perm, unsigned short* write_perm, unsigned short* delete_perm) { - assert(read_perm && write_perm && delete_perm); + if (read_perm == NULL || write_perm == NULL || delete_perm == NULL) + return; *read_perm = muscle_parse_singleAcl(sc_file_get_acl_entry(file, SC_AC_OP_READ)); *write_perm = muscle_parse_singleAcl(sc_file_get_acl_entry(file, SC_AC_OP_UPDATE)); *delete_perm = muscle_parse_singleAcl(sc_file_get_acl_entry(file, SC_AC_OP_DELETE)); @@ -149,7 +150,7 @@ static int muscle_create_directory(sc_card_t *card, sc_file_t *file) u8* oid = objectId.id; unsigned id = file->id; unsigned short read_perm = 0, write_perm = 0, delete_perm = 0; - int objectSize; + size_t objectSize; int r; if(id == 0) /* No null name files */ return SC_ERROR_INVALID_ARGUMENTS; @@ -174,7 +175,7 @@ static int muscle_create_directory(sc_card_t *card, sc_file_t *file) static int muscle_create_file(sc_card_t *card, sc_file_t *file) { mscfs_t *fs = MUSCLE_FS(card); - int objectSize = file->size; + size_t objectSize = file->size; unsigned short read_perm = 0, write_perm = 0, delete_perm = 0; msc_id objectId; int r; @@ -194,7 +195,7 @@ static int muscle_create_file(sc_card_t *card, sc_file_t *file) return r; } -static int muscle_read_binary(sc_card_t *card, unsigned int idx, u8* buf, size_t count, unsigned long flags) +static int muscle_read_binary(sc_card_t *card, unsigned int idx, u8* buf, size_t count, unsigned long *flags) { mscfs_t *fs = MUSCLE_FS(card); int r; @@ -203,7 +204,7 @@ static int muscle_read_binary(sc_card_t *card, unsigned int idx, u8* buf, size_t mscfs_file_t *file; r = mscfs_check_selection(fs, -1); - if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + if(r < 0) LOG_FUNC_RETURN(card->ctx, r); file = &fs->cache.array[fs->currentFileIndex]; objectId = file->objectId; /* memcpy(objectId.id, file->objectId.id, 4); */ @@ -213,7 +214,7 @@ static int muscle_read_binary(sc_card_t *card, unsigned int idx, u8* buf, size_t oid[2] = oid[3] = 0; } r = msc_read_object(card, objectId, idx, buf, count); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } static int muscle_update_binary(sc_card_t *card, unsigned int idx, const u8* buf, size_t count, unsigned long flags) @@ -225,7 +226,7 @@ static int muscle_update_binary(sc_card_t *card, unsigned int idx, const u8* buf u8* oid = objectId.id; r = mscfs_check_selection(fs, -1); - if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + if(r < 0) LOG_FUNC_RETURN(card->ctx, r); file = &fs->cache.array[fs->currentFileIndex]; objectId = file->objectId; @@ -236,12 +237,12 @@ static int muscle_update_binary(sc_card_t *card, unsigned int idx, const u8* buf oid[2] = oid[3] = 0; } if(file->size < idx + count) { - int newFileSize = idx + count; + size_t newFileSize = idx + count; u8* buffer = malloc(newFileSize); - if(buffer == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + if(buffer == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); r = msc_read_object(card, objectId, 0, buffer, file->size); - /* TODO: RETREIVE ACLS */ + /* TODO: RETRIEVE ACLS */ if(r < 0) goto update_bin_free_buffer; r = msc_delete_object(card, objectId, 0); if(r < 0) goto update_bin_free_buffer; @@ -253,7 +254,7 @@ static int muscle_update_binary(sc_card_t *card, unsigned int idx, const u8* buf file->size = newFileSize; update_bin_free_buffer: free(buffer); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } else { r = msc_update_object(card, objectId, idx, buf, count); } @@ -268,14 +269,16 @@ static int muscle_delete_mscfs_file(sc_card_t *card, mscfs_file_t *file_data) msc_id id = file_data->objectId; u8* oid = id.id; int r; + file_data->deleteFile = 1; if(!file_data->ef) { int x; mscfs_file_t *childFile; /* Delete children */ - mscfs_check_cache(fs); + r = mscfs_check_cache(fs); + if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "DELETING Children of: %02X%02X%02X%02X\n", oid[0],oid[1],oid[2],oid[3]); for(x = 0; x < fs->cache.size; x++) { @@ -283,13 +286,14 @@ static int muscle_delete_mscfs_file(sc_card_t *card, mscfs_file_t *file_data) childFile = &fs->cache.array[x]; objectId = childFile->objectId; - if(0 == memcmp(oid + 2, objectId.id, 2)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + if(0 == memcmp(oid + 2, objectId.id, 2) && !childFile->deleteFile) { + sc_log(card->ctx, "DELETING: %02X%02X%02X%02X\n", objectId.id[0],objectId.id[1], objectId.id[2],objectId.id[3]); r = muscle_delete_mscfs_file(card, childFile); if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); + } } oid[0] = oid[2]; @@ -297,9 +301,6 @@ static int muscle_delete_mscfs_file(sc_card_t *card, mscfs_file_t *file_data) oid[2] = oid[3] = 0; /* ??? objectId = objectId >> 16; */ } - if((0 == memcmp(oid, "\x3F\x00\x00\x00", 4)) - || (0 == memcmp(oid, "\x3F\x00\x3F\x00", 4))) { - } r = msc_delete_object(card, id, 1); /* Check if its the root... this file generally is virtual * So don't return an error if it fails */ @@ -323,6 +324,10 @@ static int muscle_delete_file(sc_card_t *card, const sc_path_t *path_in) r = mscfs_loadFileInfo(fs, path_in->value, path_in->len, &file_data, NULL); if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); + for(int x = 0; x < fs->cache.size; x++) { + mscfs_file_t *file = &fs->cache.array[x]; + file->deleteFile = 0; + } r = muscle_delete_mscfs_file(card, file_data); mscfs_clear_cache(fs); if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); @@ -365,18 +370,19 @@ static int select_item(sc_card_t *card, const sc_path_t *path_in, sc_file_t ** f { mscfs_t *fs = MUSCLE_FS(card); mscfs_file_t *file_data = NULL; - int pathlen = path_in->len; + size_t pathlen = path_in->len; int r = 0; int objectIndex; u8* oid; - mscfs_check_cache(fs); + r = mscfs_check_cache(fs); + if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); r = mscfs_loadFileInfo(fs, path_in->value, path_in->len, &file_data, &objectIndex); if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); /* Check if its the right type */ if(requiredType >= 0 && requiredType != file_data->ef) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } oid = file_data->objectId.id; /* Is it a file or directory */ @@ -386,6 +392,9 @@ static int select_item(sc_card_t *card, const sc_path_t *path_in, sc_file_t ** f fs->currentFile[0] = oid[2]; fs->currentFile[1] = oid[3]; } else { + if(pathlen < 2) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } fs->currentPath[0] = oid[pathlen - 2]; fs->currentPath[1] = oid[pathlen - 1]; fs->currentFile[0] = 0; @@ -425,7 +434,8 @@ static int muscle_select_file(sc_card_t *card, const sc_path_t *path_in, { int r; - assert(card != NULL && path_in != NULL); + if (card == NULL || path_in == NULL) + return SC_ERROR_INTERNAL; switch (path_in->type) { case SC_PATH_TYPE_FILE_ID: @@ -453,11 +463,12 @@ static int _listFile(mscfs_file_t *file, int reset, void *udata) static int muscle_init(sc_card_t *card) { muscle_private_t *priv; + int r; card->name = "MuscleApplet"; card->drv_data = malloc(sizeof(muscle_private_t)); if(!card->drv_data) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } memset(card->drv_data, 0, sizeof(muscle_private_t)); priv = MUSCLE_DATA(card); @@ -465,7 +476,7 @@ static int muscle_init(sc_card_t *card) priv->fs = mscfs_new(); if(!priv->fs) { free(card->drv_data); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } priv->fs->udata = card; priv->fs->listFile = _listFile; @@ -476,18 +487,20 @@ static int muscle_init(sc_card_t *card) card->caps |= SC_CARD_CAP_RNG; /* Card type detection */ - if (_sc_match_atr(card, muscle_atrs, &card->type) < 0) { - free(priv->fs); - free(card->drv_data); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + r = _sc_match_atr(card, muscle_atrs, &card->type); + if (r < 0) { + sc_log(card->ctx, "Failed to match the ATRs"); } - if(card->type == SC_CARD_TYPE_MUSCLE_ETOKEN_72K) { card->caps |= SC_CARD_CAP_APDU_EXT; } if(card->type == SC_CARD_TYPE_MUSCLE_JCOP241) { card->caps |= SC_CARD_CAP_APDU_EXT; } + if (!(card->caps & SC_CARD_CAP_APDU_EXT)) { + card->max_recv_size = 255; + card->max_send_size = 255; + } if(card->type == SC_CARD_TYPE_MUSCLE_JCOP242R2_NO_EXT_APDU) { /* Tyfone JCOP v242R2 card that doesn't support extended APDUs */ } @@ -511,14 +524,17 @@ static int muscle_list_files(sc_card_t *card, u8 *buf, size_t bufLen) { muscle_private_t* priv = MUSCLE_DATA(card); mscfs_t *fs = priv->fs; - int x; + int x, r; int count = 0; - mscfs_check_cache(priv->fs); + r = mscfs_check_cache(priv->fs); + if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); for(x = 0; x < fs->cache.size; x++) { - u8* oid= fs->cache.array[x].objectId.id; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + u8* oid = fs->cache.array[x].objectId.id; + if (bufLen < 2) + break; + sc_log(card->ctx, "FILE: %02X%02X%02X%02X\n", oid[0],oid[1],oid[2],oid[3]); if(0 == memcmp(fs->currentPath, oid, 2)) { @@ -526,14 +542,14 @@ static int muscle_list_files(sc_card_t *card, u8 *buf, size_t bufLen) buf[1] = oid[3]; if(buf[0] == 0x00 && buf[1] == 0x00) continue; /* No directories/null names outside of root */ buf += 2; - count+=2; + count += 2; + bufLen -= 2; } } return count; } -static int muscle_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *cmd, - int *tries_left) +static int muscle_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *cmd) { muscle_private_t* priv = MUSCLE_DATA(card); const int bufferLength = MSC_MAX_PIN_COMMAND_LENGTH; @@ -544,10 +560,12 @@ static int muscle_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *cmd, case SC_AC_CHV: { sc_apdu_t apdu; int r; - msc_verify_pin_apdu(card, &apdu, buffer, bufferLength, cmd->pin_reference, cmd->pin1.data, cmd->pin1.len); + r = msc_verify_pin_apdu(card, &apdu, buffer, bufferLength, cmd->pin_reference, cmd->pin1.data, cmd->pin1.len); + if (r < 0) + return r; cmd->apdu = &apdu; cmd->pin1.offset = 5; - r = iso_ops->pin_cmd(card, cmd, tries_left); + r = iso_ops->pin_cmd(card, cmd); if(r >= 0) priv->verifiedPins |= (1 << cmd->pin_reference); return r; @@ -557,43 +575,49 @@ static int muscle_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *cmd, case SC_AC_AUT: case SC_AC_NONE: default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported authentication method\n"); + sc_log(card->ctx, "Unsupported authentication method\n"); return SC_ERROR_NOT_SUPPORTED; } case SC_PIN_CMD_CHANGE: switch(cmd->pin_type) { case SC_AC_CHV: { sc_apdu_t apdu; - msc_change_pin_apdu(card, &apdu, buffer, bufferLength, cmd->pin_reference, cmd->pin1.data, cmd->pin1.len, cmd->pin2.data, cmd->pin2.len); + int r; + r = msc_change_pin_apdu(card, &apdu, buffer, bufferLength, cmd->pin_reference, cmd->pin1.data, cmd->pin1.len, cmd->pin2.data, cmd->pin2.len); + if (r < 0) + return r; cmd->apdu = &apdu; - return iso_ops->pin_cmd(card, cmd, tries_left); + return iso_ops->pin_cmd(card, cmd); } case SC_AC_TERM: case SC_AC_PRO: case SC_AC_AUT: case SC_AC_NONE: default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported authentication method\n"); + sc_log(card->ctx, "Unsupported authentication method\n"); return SC_ERROR_NOT_SUPPORTED; } case SC_PIN_CMD_UNBLOCK: switch(cmd->pin_type) { case SC_AC_CHV: { sc_apdu_t apdu; - msc_unblock_pin_apdu(card, &apdu, buffer, bufferLength, cmd->pin_reference, cmd->pin1.data, cmd->pin1.len); + int r; + r = msc_unblock_pin_apdu(card, &apdu, buffer, bufferLength, cmd->pin_reference, cmd->pin1.data, cmd->pin1.len); + if (r < 0) + return r; cmd->apdu = &apdu; - return iso_ops->pin_cmd(card, cmd, tries_left); + return iso_ops->pin_cmd(card, cmd); } case SC_AC_TERM: case SC_AC_PRO: case SC_AC_AUT: case SC_AC_NONE: default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported authentication method\n"); + sc_log(card->ctx, "Unsupported authentication method\n"); return SC_ERROR_NOT_SUPPORTED; } default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported command\n"); + sc_log(card->ctx, "Unsupported command\n"); return SC_ERROR_NOT_SUPPORTED; } @@ -602,7 +626,7 @@ static int muscle_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *cmd, static int muscle_card_extract_key(sc_card_t *card, sc_cardctl_muscle_key_info_t *info) { - /* CURRENTLY DONT SUPPOT EXTRACTING PRIVATE KEYS... */ + /* CURRENTLY DONT SUPPORT EXTRACTING PRIVATE KEYS... */ switch(info->keyType) { case 1: /* RSA */ return msc_extract_rsa_public_key(card, @@ -618,7 +642,7 @@ static int muscle_card_extract_key(sc_card_t *card, sc_cardctl_muscle_key_info_t static int muscle_card_import_key(sc_card_t *card, sc_cardctl_muscle_key_info_t *info) { - /* CURRENTLY DONT SUPPOT EXTRACTING PRIVATE KEYS... */ + /* CURRENTLY DONT SUPPORT EXTRACTING PRIVATE KEYS... */ switch(info->keyType) { case 0x02: /* RSA_PRIVATE */ case 0x03: /* RSA_PRIVATE_CRT */ @@ -670,34 +694,34 @@ static int muscle_set_security_env(sc_card_t *card, if (env->operation != SC_SEC_OPERATION_SIGN && env->operation != SC_SEC_OPERATION_DECIPHER) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid crypto operation supplied.\n"); + sc_log(card->ctx, "Invalid crypto operation supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } if (env->algorithm != SC_ALGORITHM_RSA) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid crypto algorithm supplied.\n"); + sc_log(card->ctx, "Invalid crypto algorithm supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } /* ADJUST FOR PKCS1 padding support for decryption only */ if ((env->algorithm_flags & SC_ALGORITHM_RSA_PADS) || (env->algorithm_flags & SC_ALGORITHM_RSA_HASHES)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Card supports only raw RSA.\n"); + sc_log(card->ctx, "Card supports only raw RSA.\n"); return SC_ERROR_NOT_SUPPORTED; } if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { if (env->key_ref_len != 1 || (env->key_ref[0] > 0x0F)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid key reference supplied.\n"); + sc_log(card->ctx, "Invalid key reference supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } priv->rsa_key_ref = env->key_ref[0]; } if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Algorithm reference not supported.\n"); + sc_log(card->ctx, "Algorithm reference not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } /* if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) if (memcmp(env->file_ref.value, "\x00\x12", 2) != 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File reference is not 0012.\n"); + sc_log(card->ctx, "File reference is not 0012.\n"); return SC_ERROR_NOT_SUPPORTED; } */ priv->env = *env; @@ -721,14 +745,14 @@ static int muscle_decipher(sc_card_t * card, u8 key_id; int r; - /* saniti check */ + /* sanity check */ if (priv->env.operation != SC_SEC_OPERATION_DECIPHER) return SC_ERROR_INVALID_ARGUMENTS; key_id = priv->rsa_key_ref * 2; /* Private key */ if (out_len < crgram_len) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Output buffer too small"); + sc_log(card->ctx, "Output buffer too small"); return SC_ERROR_BUFFER_TOO_SMALL; } @@ -740,7 +764,7 @@ static int muscle_decipher(sc_card_t * card, out, crgram_len, out_len); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card signature failed"); + LOG_TEST_RET(card->ctx, r, "Card signature failed"); return r; } @@ -754,7 +778,7 @@ static int muscle_compute_signature(sc_card_t *card, const u8 *data, key_id = priv->rsa_key_ref * 2; /* Private key */ if (outlen < data_len) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Output buffer too small"); + sc_log(card->ctx, "Output buffer too small"); return SC_ERROR_BUFFER_TOO_SMALL; } @@ -766,7 +790,7 @@ static int muscle_compute_signature(sc_card_t *card, const u8 *data, out, data_len, outlen); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card signature failed"); + LOG_TEST_RET(card->ctx, r, "Card signature failed"); return r; } @@ -774,8 +798,12 @@ static int muscle_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { if (len == 0) return SC_SUCCESS; - else - return msc_get_challenge(card, len, 0, NULL, rnd); + else { + LOG_TEST_RET(card->ctx, + msc_get_challenge(card, len, 0, NULL, rnd), + "GET CHALLENGE cmd failed"); + return (int) len; + } } static int muscle_check_sw(sc_card_t * card, unsigned int sw1, unsigned int sw2) { @@ -810,6 +838,34 @@ static int muscle_check_sw(sc_card_t * card, unsigned int sw1, unsigned int sw2) return iso_ops->check_sw(card, sw1, sw2); } +static int muscle_card_reader_lock_obtained(sc_card_t *card, int was_reset) +{ + int r = SC_SUCCESS; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (was_reset > 0) { + if (msc_select_applet(card, muscleAppletId, sizeof muscleAppletId) != 1) { + r = SC_ERROR_INVALID_CARD; + } + } + + LOG_FUNC_RETURN(card->ctx, r); +} + +static int muscle_logout(sc_card_t *card) +{ + int r = SC_ERROR_NOT_SUPPORTED; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (msc_select_applet(card, muscleAppletId, sizeof muscleAppletId) == 1) { + r = SC_SUCCESS; + } + + LOG_FUNC_RETURN(card->ctx, r); +} + static struct sc_card_driver * sc_get_driver(void) { @@ -837,6 +893,8 @@ static struct sc_card_driver * sc_get_driver(void) muscle_ops.select_file = muscle_select_file; muscle_ops.delete_file = muscle_delete_file; muscle_ops.list_files = muscle_list_files; + muscle_ops.card_reader_lock_obtained = muscle_card_reader_lock_obtained; + muscle_ops.logout = muscle_logout; return &muscle_drv; } diff --git a/src/libopensc/card-myeid.c b/src/libopensc/card-myeid.c index 1b053a03c7..8ce80ab3a2 100644 --- a/src/libopensc/card-myeid.c +++ b/src/libopensc/card-myeid.c @@ -1,7 +1,7 @@ /* * card-myeid.c * - * Copyright (C) 2008-2009 Aventra Ltd. + * Copyright (C) 2008-2019 Aventra Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -30,23 +30,18 @@ #include "cardctl.h" #include "types.h" -#define LOAD_KEY_MODULUS 0x80 -#define LOAD_KEY_PUBLIC_EXPONENT 0x81 -#define LOAD_KEY_PRIME_P 0x83 -#define LOAD_KEY_PRIME_Q 0x84 -#define LOAD_KEY_DP1 0x85 -#define LOAD_KEY_DQ1 0x86 -#define LOAD_KEY_INVQ 0x87 -#define LOAD_KEY_MODE_EC_PRIV 0x87 -#define LOAD_KEY_MODE_EC_PUB 0x86 - -#define LOAD_KEY_EC_PRIVATE 0x97 -#define LOAD_KEY_EC_PUBLIC 0x96 - -#define LOAD_KEY_SYMMETRIC 0xa0 - -#define MYEID_STATE_CREATION 0x01 -#define MYEID_STATE_ACTIVATED 0x07 +/* Low byte is the MyEID card's key type specific component ID. High byte is used + * internally for key type, so myeid_loadkey() is aware of the exact component. */ +#define LOAD_KEY_MODULUS 0x0080 +#define LOAD_KEY_PUBLIC_EXPONENT 0x0081 +#define LOAD_KEY_PRIME_P 0x0083 +#define LOAD_KEY_PRIME_Q 0x0084 +#define LOAD_KEY_DP1 0x0085 +#define LOAD_KEY_DQ1 0x0086 +#define LOAD_KEY_INVQ 0x0087 +#define LOAD_KEY_EC_PUBLIC 0x1086 +#define LOAD_KEY_EC_PRIVATE 0x1087 +#define LOAD_KEY_SYMMETRIC 0x20a0 #define MYEID_CARD_NAME_MAX_LEN 100 @@ -59,7 +54,13 @@ #define MYEID_CARD_CAP_GRIDPIN 0x10 #define MYEID_CARD_CAP_PIV_EMU 0x20 +#define MYEID_MAX_APDU_DATA_LEN 0xFF +#define MYEID_MAX_RSA_KEY_LEN 4096 + +#define MYEID_MAX_EXT_APDU_BUFFER_SIZE (MYEID_MAX_RSA_KEY_LEN/8+16) + static const char *myeid_card_name = "MyEID"; +static const char *oseid_card_name = "OsEID"; static char card_name_buf[MYEID_CARD_NAME_MAX_LEN]; static struct sc_card_operations myeid_ops; @@ -72,25 +73,24 @@ static struct sc_card_driver myeid_drv = { NULL }; -static const char *myeid_atrs[] = { - "3B:F5:18:00:FF:81:31:FE:45:4D:79:45:49:44:65", - "3B:F5:18:00:00:81:31:FE:45:4D:79:45:49:44:9A", - "3B:85:80:01:4D:79:45:49:44:78", - "3B:89:80:01:09:38:33:B1:4D:79:45:49:44:4C", - "3B:F5:96:00:00:80:31:FE:45:4D:79:45:49:44:15", /* Infineon's chip */ - "3B:F5:96:00:00:81:31:FE:45:4D:79:45:49:44:14", - NULL -}; - typedef struct myeid_private_data { int card_state; unsigned short change_counter; + unsigned char cap_chaining; /* the driver sets sec_env pointer in myeid_set_security_env and it is used immediately in myeid_decipher to differentiate between RSA decryption and ECDH key agreement. Note that this pointer is usually not valid after this pair of calls and must not be used elsewhere. */ const struct sc_security_env* sec_env; + int disable_hw_pkcs1_padding; + /* buffers for AES(DES) block cipher */ + uint8_t sym_crypt_buffer[16]; + uint8_t sym_plain_buffer[16]; + uint8_t sym_crypt_buffer_len; + uint8_t sym_plain_buffer_len; + /* PSO for AES/DES need algo+flags from sec env */ + unsigned long algorithm, algorithm_flags; } myeid_private_data_t; typedef struct myeid_card_caps { @@ -120,27 +120,51 @@ static int myeid_get_card_caps(struct sc_card *card, myeid_card_caps_t* card_cap static int myeid_match_card(struct sc_card *card) { - int i, match = -1; + size_t len = card->reader->atr_info.hist_bytes_len; + /* Normally the historical bytes are exactly "MyEID", but there might + * be some historic units which have a small prefix byte sequence. */ + if (len >= 5) { + if (!memcmp(&card->reader->atr_info.hist_bytes[len - 5], "MyEID", 5)) { + sc_log(card->ctx, "Matched MyEID card"); + card->type = SC_CARD_TYPE_MYEID_GENERIC; + return 1; + } + /* The software implementation of MyEID is identified by OsEID bytes */ + if (!memcmp(&card->reader->atr_info.hist_bytes[len - 5], "OsEID", 5)) { + sc_log(card->ctx, "Matched OsEID card"); + card->type = SC_CARD_TYPE_MYEID_OSEID; + return 1; + } + } + return 0; +} - for (i = 0; myeid_atrs[i] != NULL; i++) - { - u8 defatr[SC_MAX_ATR_SIZE]; - size_t len = sizeof(defatr); - const char *atrp = myeid_atrs[i]; +static int myeid_load_options(sc_context_t *ctx, myeid_private_data_t *priv) +{ + int r; + size_t i, j; + scconf_block **found_blocks, *block; - if (sc_hex_to_bin(atrp, defatr, &len)) - continue; - if (len != card->atr.len) - continue; - if (memcmp(card->atr.value, defatr, len) != 0) + if (!ctx || !priv) { + r = SC_ERROR_INTERNAL; + goto err; + } + priv->disable_hw_pkcs1_padding = 0; + for (i = 0; ctx->conf_blocks[i]; i++) { + found_blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], + "card_driver", "myeid"); + if (!found_blocks) continue; - match = i; - break; + for (j = 0, block = found_blocks[j]; block; j++, block = found_blocks[j]) { + priv->disable_hw_pkcs1_padding = scconf_get_int(block, "disable_hw_pkcs1_padding", 0); + sc_log(ctx,"Found config option: disable_hw_pkcs1_padding = %d\n", priv->disable_hw_pkcs1_padding); + } + free(found_blocks); } - if (match == -1) - return 0; + r = SC_SUCCESS; - return 1; +err: + return r; } static int myeid_init(struct sc_card *card) @@ -149,40 +173,48 @@ static int myeid_init(struct sc_card *card) myeid_private_data_t *priv; u8 appletInfo[20]; size_t appletInfoLen; - int r; myeid_card_caps_t card_caps; + static struct sc_aid myeid_aid = { "\xA0\x00\x00\x00\x63\x50\x4B\x43\x53\x2D\x31\x35", 0x0C }; + int rv = 0; + void *old_drv_data = card->drv_data; LOG_FUNC_CALLED(card->ctx); - card->name = myeid_card_name; + switch (card->type) { + case SC_CARD_TYPE_MYEID_OSEID: + card->name = oseid_card_name; + break; + case SC_CARD_TYPE_MYEID_GENERIC: + card->name = myeid_card_name; + break; + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); + } priv = calloc(1, sizeof(myeid_private_data_t)); if (!priv) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + rv = myeid_load_options (card->ctx, priv); + LOG_TEST_GOTO_ERR(card->ctx, rv, "Unable to read options from opensc.conf"); + priv->card_state = SC_FILE_STATUS_CREATION; card->drv_data = priv; + /* Ensure that the MyEID applet is selected. */ + rv = iso7816_select_aid(card, myeid_aid.value, myeid_aid.len, NULL, NULL); + LOG_TEST_GOTO_ERR(card->ctx, rv, "Failed to select MyEID applet."); + /* find out MyEID version */ appletInfoLen = 20; - r = myeid_get_info(card, appletInfo, appletInfoLen); - - LOG_TEST_RET(card->ctx, r, "Failed to get MyEID applet information."); + if (0 > myeid_get_info(card, appletInfo, appletInfoLen)) + LOG_TEST_GOTO_ERR(card->ctx, SC_ERROR_INVALID_CARD, "Failed to get MyEID applet information."); priv->change_counter = appletInfo[19] | appletInfo[18] << 8; - flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_ONBOARD_KEY_GEN; - flags |= SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_HASH_SHA1; - - _sc_card_add_rsa_alg(card, 512, flags, 0); - _sc_card_add_rsa_alg(card, 768, flags, 0); - _sc_card_add_rsa_alg(card, 1024, flags, 0); - _sc_card_add_rsa_alg(card, 1536, flags, 0); - _sc_card_add_rsa_alg(card, 2048, flags, 0); - memset(&card_caps, 0, sizeof(myeid_card_caps_t)); card_caps.max_ecc_key_length = 256; card_caps.max_rsa_key_length = 2048; @@ -190,19 +222,35 @@ static int myeid_init(struct sc_card *card) if (card->version.fw_major >= 40) { /* Since 4.0, we can query available algorithms and key sizes. * Since 3.5.0 RSA up to 2048 and ECC up to 256 are always supported, so we check only max ECC key length. */ - r = myeid_get_card_caps(card, &card_caps); - - if (r != SC_SUCCESS) { - sc_log(card->ctx, "Failed to get card capabilities. Using default max ECC key length 256."); + if (myeid_get_card_caps(card, &card_caps) != SC_SUCCESS) { + sc_log(card->ctx, "Failed to get card capabilities. Using default max ECC key length 256."); } } + flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN; + if (priv->disable_hw_pkcs1_padding == 0) + flags |= SC_ALGORITHM_RSA_PAD_PKCS1; + flags |= SC_ALGORITHM_RSA_HASH_NONE; + + _sc_card_add_rsa_alg(card, 512, flags, 0); + _sc_card_add_rsa_alg(card, 768, flags, 0); + _sc_card_add_rsa_alg(card, 1024, flags, 0); + _sc_card_add_rsa_alg(card, 1536, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); + + if (card_caps.card_supported_features & MYEID_CARD_CAP_RSA) { + if (card_caps.max_rsa_key_length >= 3072) + _sc_card_add_rsa_alg(card, 3072, flags, 0); + if (card_caps.max_rsa_key_length >= 4096) + _sc_card_add_rsa_alg(card, 4096, flags, 0); + } + /* show ECC algorithms if the applet version of the inserted card supports them */ if (card->version.fw_major >= 35) { int i; flags = SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDH_CDH_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN; - flags |= SC_ALGORITHM_ECDSA_HASH_NONE | SC_ALGORITHM_ECDSA_HASH_SHA1; + flags |= SC_ALGORITHM_ECDSA_HASH_NONE; ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES; for (i=0; ec_curves[i].curve_name != NULL; i++) { @@ -214,8 +262,8 @@ static int myeid_init(struct sc_card *card) /* show supported symmetric algorithms */ flags = 0; if (card_caps.card_supported_features & MYEID_CARD_CAP_3DES) { - if (card_caps.max_des_key_length >= 56) - _sc_card_add_symmetric_alg(card, SC_ALGORITHM_DES, 56, flags); + if (card_caps.max_des_key_length >= 64) + _sc_card_add_symmetric_alg(card, SC_ALGORITHM_DES, 64, flags); if (card_caps.max_des_key_length >= 128) _sc_card_add_symmetric_alg(card, SC_ALGORITHM_3DES, 128, flags); if (card_caps.max_des_key_length >= 192) @@ -231,16 +279,36 @@ static int myeid_init(struct sc_card *card) /* State that we have an RNG */ card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO; - card->max_recv_size = 255; + if ((card->version.fw_major == 40 && card->version.fw_minor >= 10 ) + || card->version.fw_major >= 41) + card->caps |= SC_CARD_CAP_WRAP_KEY | SC_CARD_CAP_UNWRAP_KEY + | SC_CARD_CAP_ONCARD_SESSION_OBJECTS; + + if (card->version.fw_major >= 45) + priv->cap_chaining = 1; + if (card->version.fw_major >= 40) + card->max_recv_size = 256; + else + card->max_recv_size = 255; card->max_send_size = 255; - LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + rv = SC_SUCCESS; + +err: + if (rv < 0) { + free(priv); + card->drv_data = old_drv_data; + } + + LOG_FUNC_RETURN(card->ctx, rv); } static const struct sc_card_operations *iso_ops = NULL; static int acl_to_byte(const struct sc_acl_entry *e) { + if (NULL == e) + return 0x00; switch (e->method) { case SC_AC_NONE: return 0x00; @@ -333,6 +401,21 @@ static int myeid_select_file(struct sc_card *card, const struct sc_path *in_path LOG_FUNC_RETURN(card->ctx, r); } +static int myeid_logout(struct sc_card *card) +{ + sc_apdu_t apdu; + int r; + + LOG_FUNC_CALLED(card->ctx); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2E, 0x00, 0x00 /*pin ref: 0x01-0x0E, 0=ALL*/); + apdu.cla = 0; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); +} static int myeid_list_files(struct sc_card *card, u8 *buf, size_t buflen) { @@ -351,7 +434,7 @@ static int myeid_list_files(struct sc_card *card, u8 *buf, size_t buflen) LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.resplen == 0) return sc_check_sw(card, apdu.sw1, apdu.sw2); - return apdu.resplen; + return (int)apdu.resplen; } static int myeid_process_fci(struct sc_card *card, struct sc_file *file, @@ -380,20 +463,18 @@ static int myeid_process_fci(struct sc_card *card, struct sc_file *file, sc_log(card->ctx, "id (%X) sec_attr (%X %X %X)", file->id, file->sec_attr[0],file->sec_attr[1],file->sec_attr[2]); } - tag = sc_asn1_find_tag(NULL, buf, buflen, 0x8A, &taglen); - if (tag != NULL && taglen > 0) - { - if(tag[0] == MYEID_STATE_CREATION) { - file->status = SC_FILE_STATUS_CREATION; - sc_log(card->ctx, "File id (%X) status SC_FILE_STATUS_CREATION (0x%X)", - file->id, tag[0]); - } - else if(tag[0] == MYEID_STATE_ACTIVATED) { - file->status = SC_FILE_STATUS_ACTIVATED; - sc_log(card->ctx, "File id (%X) status SC_FILE_STATUS_ACTIVATED (0x%X)", - file->id, tag[0]); - } - priv->card_state = file->status; + + priv->card_state = file->status; + switch (file->status) { + case SC_FILE_STATUS_CREATION: + file->acl_inactive = 1; + sc_log(card->ctx, "File id (%X) status SC_FILE_STATUS_CREATION", file->id); + break; + case SC_FILE_STATUS_ACTIVATED: + sc_log(card->ctx, "File id (%X) status SC_FILE_STATUS_ACTIVATED", file->id); + break; + default: + sc_log(card->ctx, "File id (%X) unusual status (0x%X)", file->id, file->status); } LOG_FUNC_RETURN(card->ctx, 0); @@ -487,10 +568,13 @@ static int encode_file_structure(sc_card_t *card, const sc_file_t *file, /* Proprietary Information */ buf[18] = 0x85; buf[19] = 0x02; - /* AC right to clear default 0 */ - /* TODO: Implement this */ - buf[20] = 0x00; /*(SC_FILE_TYPE_INTERNAL_EF == file->type ? 0x00 : 0x80);*/ - buf[21] = 0x00; + if (file->prop_attr_len == 2 && file->prop_attr != NULL) + memcpy(&buf[20], file->prop_attr, 2); + else + { + buf[20] = 0x00; + buf[21] = 0x00; + } /* Life Cycle Status tag */ buf[22] = 0x8A; @@ -579,14 +663,13 @@ static int myeid_delete_file(struct sc_card *card, const struct sc_path *path) LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); } -static int myeid_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, - int *tries_left) +static int myeid_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) { myeid_private_data_t *priv = (myeid_private_data_t *) card->drv_data; LOG_FUNC_CALLED(card->ctx); - sc_log(card->ctx, "ref (%d), pin1 len(%d), pin2 len (%d)\n", + sc_log(card->ctx, "ref (%d), pin1 len(%zu), pin2 len (%zu)\n", data->pin_reference, data->pin1.len, data->pin2.len); if(data->pin1.len > 8 || data->pin2.len > 8) @@ -600,9 +683,11 @@ static int myeid_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, return SC_SUCCESS; } - LOG_FUNC_RETURN(card->ctx, iso_ops->pin_cmd(card, data, tries_left)); + LOG_FUNC_RETURN(card->ctx, iso_ops->pin_cmd(card, data)); } +#define IS_SYMETRIC_CRYPT(x) ((x) == SC_SEC_OPERATION_ENCRYPT_SYM || (x) == SC_SEC_OPERATION_DECRYPT_SYM) + static int myeid_set_security_env_rsa(sc_card_t *card, const sc_security_env_t *env, int se_num) { @@ -610,13 +695,16 @@ static int myeid_set_security_env_rsa(sc_card_t *card, const sc_security_env_t * u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 *p; int r; + size_t i, sz; + sc_path_t *target_file; - assert(card != NULL && env != NULL); + if (card == NULL || env == NULL) + return SC_ERROR_INTERNAL; LOG_FUNC_CALLED(card->ctx); - if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) + if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) { - sc_log(card->ctx, "asymmetric keyref not supported.\n"); + sc_log(card->ctx, "symmetric keyref not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } if (se_num > 0) @@ -636,6 +724,22 @@ static int myeid_set_security_env_rsa(sc_card_t *card, const sc_security_env_t * apdu.p1 = 0x41; apdu.p2 = 0xB6; break; + case SC_SEC_OPERATION_UNWRAP: + apdu.p1 = 0x41; + apdu.p2 = 0xB8; + break; + case SC_SEC_OPERATION_WRAP: + apdu.p1 = 0x81; + apdu.p2 = 0xB8; + break; + case SC_SEC_OPERATION_ENCRYPT_SYM: + apdu.p1 = 0x81; + apdu.p2 = 0xB8; + break; + case SC_SEC_OPERATION_DECRYPT_SYM: + apdu.p1 = 0x41; + apdu.p2 = 0xB8; + break; default: return SC_ERROR_INVALID_ARGUMENTS; } @@ -654,17 +758,67 @@ static int myeid_set_security_env_rsa(sc_card_t *card, const sc_security_env_t * memcpy(p, env->file_ref.value, 2); p += 2; } - if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) - { + /* symmetric operations: we need to set the key reference */ + if (IS_SYMETRIC_CRYPT(env->operation)) { + *p++ = 0x83; + *p++ = 1; + *p++ = 0; + } + if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT && env->operation != SC_SEC_OPERATION_UNWRAP && + env->operation != SC_SEC_OPERATION_WRAP && + env->operation != SC_SEC_OPERATION_ENCRYPT_SYM && + env->operation != SC_SEC_OPERATION_DECRYPT_SYM) { *p++ = 0x84; *p++ = 1; *p++ = 0; } - r = p - sbuf; - apdu.lc = r; - apdu.datalen = r; + for (i = 0; i < SC_SEC_ENV_MAX_PARAMS; i++) + if (env->params[i].param_type == SC_SEC_ENV_PARAM_TARGET_FILE) { + target_file = (sc_path_t*) env->params[i].value; + if (env->params[i].value_len < sizeof(sc_path_t) || target_file->len != 2) { + sc_log(card->ctx, "wrong length of target file reference.\n"); + return SC_ERROR_WRONG_LENGTH; + } + *p++ = 0x83; + *p++ = 2; + memcpy(p, target_file->value, 2); + p+= 2; + break; + } + + r = 0; + if (env->operation == SC_SEC_OPERATION_UNWRAP || env->operation == SC_SEC_OPERATION_WRAP || + IS_SYMETRIC_CRYPT(env->operation)) { + /* add IV if present */ + for (i = 0; i < SC_SEC_ENV_MAX_PARAMS; i++) + if (env->params[i].param_type == SC_SEC_ENV_PARAM_IV) { + r = 1; + *p++ = 0x87; + *p++ = (unsigned char) env->params[i].value_len; + if (p + env->params[i].value_len >= sbuf + SC_MAX_APDU_BUFFER_SIZE) { + sc_log(card->ctx, "IV too long.\n"); + return SC_ERROR_WRONG_LENGTH; + } + memcpy(p, env->params[i].value, env->params[i].value_len); + p+=(unsigned char) env->params[i].value_len; + break; + } + } + /* for AES_ECB we need to reset the IV but we respect if the IV is already present */ + if (IS_SYMETRIC_CRYPT(env->operation) && env->algorithm == SC_ALGORITHM_AES && + env->algorithm_flags == SC_ALGORITHM_AES_ECB && r == 0) { + *p++ = 0x87; + *p++ = 16; + memset(p, 0, 16); + p += 16; + } + + sz = p - sbuf; + apdu.lc = sz; + apdu.datalen = sz; apdu.data = sbuf; apdu.resplen = 0; + r = (int)sz; if (apdu.datalen != 0) { r = sc_transmit_apdu(card, &apdu); @@ -692,14 +846,16 @@ static int myeid_set_security_env_ec(sc_card_t *card, const sc_security_env_t *e sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 *p; + size_t sz; int r; - assert(card != NULL && env != NULL); + if (card == NULL || env == NULL) + return SC_ERROR_INTERNAL; LOG_FUNC_CALLED(card->ctx); - if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) + if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) { - sc_log(card->ctx, "asymmetric keyref not supported."); + sc_log(card->ctx, "symmetric keyref not supported."); return SC_ERROR_NOT_SUPPORTED; } if (se_num > 0) @@ -747,11 +903,12 @@ static int myeid_set_security_env_ec(sc_card_t *card, const sc_security_env_t *e *p++ = 1; *p++ = 0; } - r = p - sbuf; - apdu.lc = r; - apdu.datalen = r; + sz = p - sbuf; + apdu.lc = sz; + apdu.datalen = sz; apdu.data = sbuf; apdu.resplen = 0; + r = (int)sz; if (apdu.datalen != 0) { r = sc_transmit_apdu(card, &apdu); @@ -785,6 +942,10 @@ static int myeid_set_security_env(struct sc_card *card, /* store security environment to differentiate between ECDH and RSA in decipher - Hannu*/ priv->sec_env = env; + /* for symmetric operation save algo and algo flags */ + priv->algorithm_flags = env->algorithm_flags; + priv->algorithm = env->algorithm; + if (env->flags & SC_SEC_ENV_ALG_PRESENT) { sc_security_env_t tmp; @@ -795,13 +956,21 @@ static int myeid_set_security_env(struct sc_card *card, if (tmp.algorithm == SC_ALGORITHM_RSA) { - tmp.algorithm_ref = 0x00; - /* potential FIXME: return an error, if an unsupported - * pad or hash was requested, although this shouldn't happen */ - if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) - tmp.algorithm_ref = 0x02; - if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) - tmp.algorithm_ref |= 0x10; + if (tmp.operation == SC_SEC_OPERATION_UNWRAP || tmp.operation == SC_SEC_OPERATION_WRAP) + { + tmp.algorithm_ref = 0x0A; + } + else + { + tmp.algorithm_ref = 0x00; + /* potential FIXME: return an error, if an unsupported + * pad or hash was requested, although this shouldn't happen */ + if ((env->operation == SC_SEC_OPERATION_SIGN && env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01) + || (env->operation == SC_SEC_OPERATION_DECIPHER && env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02)) + tmp.algorithm_ref = 0x02; + if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) + tmp.algorithm_ref |= 0x10; + } return myeid_set_security_env_rsa(card, &tmp, se_num); } @@ -811,6 +980,30 @@ static int myeid_set_security_env(struct sc_card *card, tmp.algorithm_flags = 0; return myeid_set_security_env_ec(card, &tmp, se_num); } + else if (tmp.algorithm == SC_ALGORITHM_AES) + { + if (tmp.operation == SC_SEC_OPERATION_UNWRAP || tmp.operation == SC_SEC_OPERATION_WRAP) + { + tmp.algorithm_ref = 0x0A; + } + else + { + tmp.algorithm_ref = 0x00; + } + + if ((tmp.algorithm_flags & SC_ALGORITHM_AES_CBC_PAD) == SC_ALGORITHM_AES_CBC_PAD) + tmp.algorithm_ref |= 0x80; /* set PKCS#7 padding */ + /* Tag 0x80 algorithm_ref - value 0x80 or 0x8A is working only for UNWRAP/WRAP + * AES is supported from version 4.0 but without pkcs#7 padding. + * For SC_SEC_OPERATION_ENCRYPT_SYM and SC_SEC_OPERATION_DECRYPT_SYM we running + * PKCS#7 in software, here we fix the algorithm_ref variable. + */ + if (IS_SYMETRIC_CRYPT(env->operation)) + tmp.algorithm_ref &= ~0x80; /* do not handle padding in card */ + + /* from this point, there's no difference to RSA SE */ + return myeid_set_security_env_rsa(card, &tmp, se_num); + } else { @@ -831,7 +1024,8 @@ myeid_convert_ec_signature(struct sc_context *ctx, size_t s_len, unsigned char * size_t len_size = 1; size_t sig_len = 0; - assert(data && datalen && datalen > 3); + if (!data || !datalen || datalen <= 3) + return SC_ERROR_INTERNAL; /* * When validating the signature data, we have to consider that length of the signature @@ -862,7 +1056,7 @@ myeid_convert_ec_signature(struct sc_context *ctx, size_t s_len, unsigned char * return SC_ERROR_INVALID_DATA; /* test&fail early */ - buflen = (s_len + 7)/8*2; + buflen = BYTES4BITS(s_len) * 2; if (buflen > datalen) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); @@ -879,9 +1073,74 @@ myeid_convert_ec_signature(struct sc_context *ctx, size_t s_len, unsigned char * memmove(data, buf, buflen); free(buf); - return buflen; + return (int)buflen; } +/* MyEID cards before version 4.5 do not support RAW RSA signature for 2048 bit RSA keys. + * (Source: MyEID reference manual 2.1.4) + * + * This function uses decipher operation for calculating RAW 2048 bit signature. */ +static int +myeid_compute_raw_2048_signature(struct sc_card *card, const u8 * data, size_t datalen, + u8 * out, size_t outlen) +{ + int r; + struct sc_context *ctx; + struct myeid_private_data *priv; + struct sc_apdu apdu; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + sc_security_env_t env; + + ctx = card->ctx; + LOG_FUNC_CALLED(ctx); + + priv = (myeid_private_data_t *) card->drv_data; +/* security env change - use DECIPHER operation */ + memcpy(&env, priv->sec_env, sizeof(sc_security_env_t)); + env.flags |= SC_SEC_ENV_ALG_REF_PRESENT; + env.flags |= SC_SEC_ENV_FILE_REF_PRESENT; + env.flags |= SC_SEC_ENV_KEY_REF_PRESENT; + env.operation = SC_SEC_OPERATION_DECIPHER; + myeid_set_security_env_rsa(card, &env, 0); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x80, 0x86); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 0; /* there is no response to 1st part of data */ + +/* prepare 1st part of data */ + sbuf[0] = 0x81; + memcpy(sbuf + 1, data, datalen / 2); + apdu.lc = datalen / 2 + 1; + apdu.datalen = apdu.lc; + apdu.data = sbuf; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { +/* prepare 2nd part of data */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = datalen; + sbuf[0] = 0x82; + memcpy(sbuf + 1, data + datalen / 2, datalen / 2); + apdu.lc = datalen / 2 + 1; + apdu.datalen = apdu.lc; + apdu.data = sbuf; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; + memcpy(out, apdu.resp, len); + LOG_FUNC_RETURN(card->ctx, (int)len); + } + } + LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); +} static int myeid_compute_signature(struct sc_card *card, const u8 * data, size_t datalen, @@ -889,54 +1148,50 @@ myeid_compute_signature(struct sc_card *card, const u8 * data, size_t datalen, { struct sc_context *ctx; struct sc_apdu apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 rbuf[MYEID_MAX_EXT_APDU_BUFFER_SIZE]; + u8 sbuf[MYEID_MAX_EXT_APDU_BUFFER_SIZE]; struct myeid_private_data* priv; int r; size_t field_length = 0; size_t pad_chars = 0; - - assert(card != NULL && data != NULL && out != NULL); + if (card == NULL || data == NULL || out == NULL) + return SC_ERROR_INTERNAL; ctx = card->ctx; LOG_FUNC_CALLED(ctx); priv = (myeid_private_data_t*) card->drv_data; - sc_log(ctx, "key type %i, key length %i", priv->sec_env->algorithm, priv->sec_env->algorithm_ref); + sc_log(ctx, "key type %lu, key length %lu", priv->sec_env->algorithm, priv->sec_env->algorithm_ref); if (priv->sec_env->algorithm == SC_ALGORITHM_EC ) { field_length = priv->sec_env->algorithm_ref; /* pad with zeros if needed */ - if (datalen < (field_length + 7) / 8 ) { - pad_chars = ((field_length + 7) / 8) - datalen; + if (datalen < BYTES4BITS(field_length)) { + pad_chars = BYTES4BITS(field_length) - datalen; memset(sbuf, 0, pad_chars); } } - if ((datalen + pad_chars) > 256) + if ((datalen + pad_chars) > sizeof(sbuf)) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + if (priv->sec_env->algorithm == SC_ALGORITHM_RSA && datalen == 256 && !priv->cap_chaining) + return myeid_compute_raw_2048_signature(card, data, datalen, out, outlen); + /* INS: 0x2A PERFORM SECURITY OPERATION * P1: 0x9E Resp: Digital Signature * P2: 0x9A Cmd: Input for Digital Signature */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); + apdu.flags |= SC_APDU_FLAGS_CHAINING; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; - if (datalen == 256) { - apdu.p2 = data[0]; - memcpy(sbuf, data+1, datalen-1); - apdu.lc = datalen - 1; - apdu.datalen = datalen - 1; - } - else { - memcpy(sbuf + pad_chars, data, datalen); - apdu.lc = datalen + pad_chars; - apdu.datalen = datalen + pad_chars; - } + memcpy(sbuf + pad_chars, data, datalen); + apdu.lc = datalen + pad_chars; + apdu.datalen = datalen + pad_chars; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); @@ -944,7 +1199,7 @@ myeid_compute_signature(struct sc_card *card, const u8 * data, size_t datalen, r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, r, "compute_signature failed"); - if (priv->sec_env->algorithm == SC_ALGORITHM_EC) { + if (priv->sec_env->algorithm == SC_ALGORITHM_EC) { r = myeid_convert_ec_signature(ctx, priv->sec_env->algorithm_ref, apdu.resp, apdu.resplen); LOG_TEST_RET(ctx, r, "compute_signature convert signature failed"); apdu.resplen = r; @@ -954,7 +1209,7 @@ myeid_compute_signature(struct sc_card *card, const u8 * data, size_t datalen, LOG_FUNC_RETURN(ctx, SC_ERROR_BUFFER_TOO_SMALL); memcpy(out, apdu.resp, apdu.resplen); - LOG_FUNC_RETURN(ctx, apdu.resplen); + LOG_FUNC_RETURN(ctx, (int)apdu.resplen); } @@ -965,11 +1220,13 @@ int myeid_ecdh_derive(struct sc_card *card, const u8* pubkey, size_t pubkey_len, /* MyEID uses GENERAL AUTHENTICATE ISO command for ECDH */ struct sc_apdu apdu; + u8 pbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; - size_t ext_len_bytes; + size_t pbuf_len = sizeof(pbuf); + u8 *ptr; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x86, 0x00, 0x00); @@ -978,33 +1235,19 @@ int myeid_ecdh_derive(struct sc_card *card, const u8* pubkey, size_t pubkey_len, /* Fill in "Data objects in dynamic authentication template" (tag 0x7C) structure * - * TODO: encode the structure using OpenSC's ASN1-functions. - * * Size of the structure depends on key length. With 521 bit keys two bytes are needed for defining length of a point. */ - sbuf[0] = 0x7C; - ext_len_bytes = 0; - - if (pubkey_len > 127) - { - sbuf[1] = 0x81; - sbuf[2] = (u8) (pubkey_len + 3); - sbuf[3] = 0x85; - sbuf[4] = 0x81; - sbuf[5] = (u8) (pubkey_len); - ext_len_bytes = 2; - } - else - { - sbuf[1] = pubkey_len + 2; - sbuf[2] = 0x85; - sbuf[3] = pubkey_len; - } + /* Inner TLV 0x85 | LEN | pubkey */ + r = sc_asn1_put_tag(0x85, pubkey, pubkey_len, pbuf, sizeof(pbuf), &ptr); + LOG_TEST_RET(card->ctx, r, "Failed to encode inner tlv"); + pbuf_len = (ptr - pbuf); - memcpy(&sbuf[4 + ext_len_bytes], pubkey, pubkey_len); + /* Outer TLV 0x7C | LEN | Inner TLV */ + r = sc_asn1_put_tag(0x7C, pbuf, pbuf_len, sbuf, sizeof(sbuf), &ptr); + LOG_TEST_RET(card->ctx, r, "Failed to encode outer tlv"); - apdu.lc = pubkey_len + 4 + ext_len_bytes; + apdu.lc = (ptr - sbuf); apdu.le = pubkey_len / 2; apdu.datalen = apdu.lc; apdu.data = sbuf; @@ -1024,106 +1267,195 @@ int myeid_ecdh_derive(struct sc_card *card, const u8* pubkey, size_t pubkey_len, memcpy(out, rbuf, apdu.resplen); - LOG_FUNC_RETURN(card->ctx, apdu.resplen); + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); +} + +static int myeid_transmit_decipher_pi_split(struct sc_card *card, struct sc_apdu *apdu, u8 *sbuf) +{ + /* MyEID before 4.5.x does not support APDU chaining. The payload + * is split to two regular APDUs and Padding Indicator field is used to + * describe which slice it is. */ + size_t crgram_len = apdu->lc - 1; + size_t crgram_half = crgram_len / 2; + size_t resplen = apdu->resplen; + unsigned char *resp = apdu->resp; + int r; + + LOG_FUNC_CALLED(card->ctx); + + /* Send 1st part, no response */ + apdu->cse = SC_APDU_CASE_3_SHORT; + apdu->data = &sbuf[0]; + apdu->datalen = apdu->lc = crgram_half + 1; + apdu->resp = 0; + apdu->resplen = 0; + apdu->le = 0; + sbuf[0] = 0x81; /* Padding Indicator, 0x81 = First half */ + + r = sc_transmit_apdu(card, apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu->sw1 != 0x90 || apdu->sw2 != 0x00) + return 0; + + /* Send 2nd part, expect response */ + apdu->cse = resplen ? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_3_SHORT; + apdu->data = &sbuf[crgram_half]; + apdu->datalen = apdu->lc = crgram_len - crgram_half + 1; + apdu->resp = resp; + apdu->resplen = resplen; + apdu->le = resplen ? MIN(card->max_recv_size, crgram_len) : 0; + sbuf[crgram_half] = 0x82; /* Padding Indicator, 0x82 = Second half */ + + r = sc_transmit_apdu(card, apdu); + LOG_FUNC_RETURN(card->ctx, r); } +static int myeid_transmit_decipher(struct sc_card *card, u8 p1, u8 p2, + const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) +{ + myeid_private_data_t *priv = card->drv_data; + struct sc_apdu apdu; + u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; + u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; + int r; + + LOG_FUNC_CALLED(card->ctx); + + /* INS: 0x2A PERFORM SECURITY OPERATION + * P1: 0x00 Resp: No response (unwrapping) + * P1: 0x80 Resp: Plain value + * P2: 0x84 Cmd: Cryptogram (no padding byte) + * P2: 0x86 Cmd: Padding indicator byte followed by cryptogram */ + sc_format_apdu(card, &apdu, p1 ? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_3_SHORT, 0x2A, p1, p2); + if (p2 == 0x86) { + if (crgram_len+1 > sizeof(sbuf)) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + sbuf[0] = 0; /* Padding indicator: 0x00 = No further indication */ + memcpy(sbuf + 1, crgram, crgram_len); + apdu.data = sbuf; + apdu.datalen = apdu.lc = crgram_len + 1; + } else { + apdu.data = crgram; + apdu.datalen = apdu.lc = crgram_len; + } + if (p1 != 0x00) { + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = MIN(card->max_recv_size, crgram_len); + } + + /* In MyEID 4.5.x, unwrapping with 2K RSA using APDU chaining doesn't work properly. Split the APDU in the old way in this case. */ + if (p2 == 0x86 && crgram_len == 256 && priv && (!priv->cap_chaining || (card->version.fw_major == 45 && priv->sec_env != NULL && priv->sec_env->operation == SC_SEC_OPERATION_UNWRAP))) { + r = myeid_transmit_decipher_pi_split(card, &apdu, sbuf); + } else { + apdu.flags |= SC_APDU_FLAGS_CHAINING; + r = sc_transmit_apdu(card, &apdu); + } + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "DECIPHER returned error"); + + if (out && outlen) { + outlen = MIN(apdu.resplen, outlen); + memcpy(out, apdu.resp, outlen); + } else { + outlen = 0; + } + LOG_FUNC_RETURN(card->ctx, (int)outlen); +} static int myeid_decipher(struct sc_card *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) { int r; myeid_private_data_t* priv; - struct sc_apdu apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + + if (card == NULL || crgram == NULL || out == NULL) + return SC_ERROR_INTERNAL; LOG_FUNC_CALLED(card->ctx); - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); - assert(card != NULL && crgram != NULL && out != NULL); - - priv = (myeid_private_data_t*) card->drv_data; + priv = (myeid_private_data_t*)card->drv_data; if (priv->sec_env && priv->sec_env->algorithm == SC_ALGORITHM_EC && priv->sec_env->operation == SC_SEC_OPERATION_DERIVE - && priv->sec_env->algorithm_flags & SC_ALGORITHM_ECDSA_RAW) + && priv->sec_env->algorithm_flags & SC_ALGORITHM_ECDH_CDH_RAW) { r = myeid_ecdh_derive(card, crgram, crgram_len, out, outlen); priv->sec_env = NULL; /* clear after operation */ LOG_FUNC_RETURN(card->ctx, r); } - if (crgram_len > 256) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + r = myeid_transmit_decipher(card, 0x80, 0x86, crgram, crgram_len, out, outlen); + LOG_FUNC_RETURN(card->ctx, r); +} - /* INS: 0x2A PERFORM SECURITY OPERATION - * P1: 0x80 Resp: Plain value - * P2: 0x86 Cmd: Padding indicator byte followed by cryptogram */ - sc_format_apdu(card, &apdu, - (crgram_len < 256) ? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_3_SHORT, - 0x2A, 0x80, 0x86); +static int myeid_wrap_key(struct sc_card *card, u8 *out, size_t outlen) +{ + struct sc_context *ctx; + struct sc_apdu apdu; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + int r; + + if (card == NULL) + return SC_ERROR_INTERNAL; + ctx = card->ctx; + LOG_FUNC_CALLED(ctx); + + /* INS: 0x2A PERFORM SECURITY OPERATION + P1: 0x84 Resp: Return a cryptogram + * P2: 0x00 The data field is absent */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x2A, 0x84, 0x00); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); - apdu.le = crgram_len; + apdu.le = sizeof(rbuf) <= 256 ? sizeof(rbuf) : 256; + apdu.lc = 0; - if (crgram_len == 256) - { - apdu.le = 0; - /* padding indicator byte, 0x81 = first half of 2048 bit cryptogram */ - sbuf[0] = 0x81; - memcpy(sbuf + 1, crgram, crgram_len / 2); - apdu.lc = crgram_len / 2 + 1; - } - else - { - sbuf[0] = 0; /* padding indicator byte, 0x00 = No further indication */ - memcpy(sbuf + 1, crgram, crgram_len); - apdu.lc = crgram_len + 1; - } + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, r, "wrap key failed"); - apdu.datalen = apdu.lc; - apdu.data = sbuf; + if (apdu.resplen <= outlen && out != NULL) + memcpy(out, apdu.resp, apdu.resplen); - r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) - { - if (crgram_len == 256) - { - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, - 0x2A, 0x80, 0x86); - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - apdu.le = crgram_len; - /* padding indicator byte, - * 0x82 = Second half of 2048 bit cryptogram */ - sbuf[0] = 0x82; - memcpy(sbuf + 1, crgram + crgram_len / 2, crgram_len / 2); - apdu.lc = crgram_len / 2 + 1; - apdu.datalen = apdu.lc; - apdu.data = sbuf; - - r = sc_transmit_apdu(card, &apdu); - - LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - - if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) - { - int len = apdu.resplen > outlen ? outlen : apdu.resplen; - memcpy(out, apdu.resp, len); - LOG_FUNC_RETURN(card->ctx, len); - } - } - else - { - int len = apdu.resplen > outlen ? outlen : apdu.resplen; + LOG_FUNC_RETURN(ctx, (int)apdu.resplen); +} - memcpy(out, apdu.resp, len); - LOG_FUNC_RETURN(card->ctx, len); - } +static int myeid_unwrap_key(struct sc_card *card, const u8 *crgram, size_t crgram_len) +{ + myeid_private_data_t* priv; + u8 p2 = 0x86; /* init P2 for asymmetric crypto by default.*/ + int r; + + if (card == NULL || crgram == NULL) + return SC_ERROR_INVALID_ARGUMENTS; + priv = card->drv_data; + + LOG_FUNC_CALLED(card->ctx); + + if (crgram_len > MYEID_MAX_RSA_KEY_LEN / 8) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + if (priv && priv->sec_env) + { + if (priv->sec_env->algorithm == SC_ALGORITHM_AES || + priv->sec_env->algorithm == SC_ALGORITHM_3DES || + priv->sec_env->algorithm == SC_ALGORITHM_DES) + p2 = 0x84; } - LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); + + if (p2 == 0x84 && crgram_len > MYEID_MAX_APDU_DATA_LEN) + LOG_TEST_RET(card->ctx, SC_ERROR_WRONG_LENGTH, "Unwrapping symmetric data longer that 255 bytes is not supported\n"); + + /* INS: 0x2A PERFORM SECURITY OPERATION + * P1: 0x00 Do not expect response - the deciphered data will be placed into the target key EF. + * P2: 0x86 Cmd: Padding indicator byte followed by cryptogram + * P2: 0x84 Cmd: AES/3DES Cryptogram (plain value encoded in BER-TLV DO, but not including SM DOs) */ + r = myeid_transmit_decipher(card, 0x00, p2, crgram, crgram_len, 0, 0); + LOG_FUNC_RETURN(card->ctx, r); } @@ -1190,78 +1522,55 @@ static int myeid_getdata(struct sc_card *card, struct sc_cardctl_myeid_data_obj* LOG_FUNC_RETURN(card->ctx, r); } -static int myeid_loadkey(sc_card_t *card, int mode, u8* value, int value_len) +static int myeid_loadkey(sc_card_t *card, unsigned mode, u8 *value, size_t value_len) { + myeid_private_data_t *priv = (myeid_private_data_t *) card->drv_data; sc_apdu_t apdu; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; - int r, len; + u8 sbuf[MYEID_MAX_EXT_APDU_BUFFER_SIZE]; + int r; LOG_FUNC_CALLED(card->ctx); - len = 0; - if(value_len == 0 || value == NULL) + if (value_len == 0 || value == NULL) return 0; - if(value != NULL && - value[0] != 0x0 && - mode != LOAD_KEY_PUBLIC_EXPONENT && - mode != LOAD_KEY_SYMMETRIC) - sbuf[len++] = 0x0; - - if(mode == LOAD_KEY_MODULUS && value_len >= 256) + if (mode == LOAD_KEY_MODULUS && value_len == 256 && !priv->cap_chaining) { - r=0; - if((value_len % 2) > 0 && value[0] == 0x00) - { - value_len--; - memmove(value, value + 1, value_len); - } - mode = 0x88; - len = 128; - memcpy(sbuf,value, 128); - + mode = 0x88; memset(&apdu, 0, sizeof(apdu)); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDA, 0x01, mode); apdu.cla = 0x00; - apdu.data = sbuf; - apdu.datalen = len; - apdu.lc = len; + apdu.data = value; + apdu.datalen = 128; + apdu.lc = 128; r = sc_transmit_apdu(card, &apdu); - if(r < 0) - return r; + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if(r < 0) - return r; + LOG_TEST_RET(card->ctx, r, "LOAD KEY returned error"); mode = 0x89; - len = value_len - 128; - memset(&sbuf, 0, SC_MAX_APDU_BUFFER_SIZE); - memcpy(sbuf,value + 128, value_len - 128); - } - else if(mode == LOAD_KEY_EC_PRIVATE) { - memcpy(sbuf, value, value_len); - len = value_len; - mode = LOAD_KEY_MODE_EC_PRIV; + value += 128; + value_len -= 128; } - else if(mode == LOAD_KEY_EC_PUBLIC) { - memcpy(sbuf, value, value_len); - len = value_len; - mode = LOAD_KEY_MODE_EC_PUB; - } - else + else if ((mode & 0xff00) == 0 && mode != LOAD_KEY_PUBLIC_EXPONENT && + value[0] != 0x00) { - memcpy(sbuf + len, value, value_len); - len += value_len; + /* RSA components needing leading zero byte */ + sbuf[0] = 0x0; + memcpy(&sbuf[1], value, value_len); + value = sbuf; + value_len ++; } memset(&apdu, 0, sizeof(apdu)); - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDA, 0x01, mode); - + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDA, 0x01, mode & 0xFF); + apdu.flags = SC_APDU_FLAGS_CHAINING; apdu.cla = 0x00; - apdu.data = sbuf; - apdu.datalen = len; - apdu.lc = len; + apdu.data = value; + apdu.datalen = value_len; + apdu.lc = value_len; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); @@ -1439,6 +1748,23 @@ static int myeid_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) LOG_FUNC_RETURN(card->ctx, r); } +static int +myeid_get_change_counter(sc_card_t *card, size_t *change_counter) +{ + int r; + u8 rbuf[256]; + + LOG_FUNC_CALLED(card->ctx); + + /* get change counter from card */ + r = myeid_get_info(card, rbuf, sizeof(rbuf)); + LOG_TEST_RET(card->ctx, r, "Get applet info failed"); + + *change_counter = rbuf[18] * 256 + rbuf[19]; + + LOG_FUNC_RETURN(card->ctx, r); +} + /* Get information of features that the card supports. MyEID 4.x cards are available on different hardware and maximum key sizes cannot be determined simply from the version number anymore. @@ -1502,6 +1828,9 @@ static int myeid_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) case SC_CARDCTL_GET_SERIALNR: r = myeid_get_serialnr(card, (sc_serial_number_t *)ptr); break; + case SC_CARDCTL_GET_CHANGE_COUNTER: + r = myeid_get_change_counter(card, (size_t *)ptr); + break; case SC_CARDCTL_GET_DEFAULT_KEY: case SC_CARDCTL_LIFECYCLE_SET: case SC_CARDCTL_LIFECYCLE_GET: @@ -1517,6 +1846,253 @@ static int myeid_finish(sc_card_t * card) return SC_SUCCESS; } +static int +myeid_enc_dec_sym(struct sc_card *card, const u8 *data, size_t datalen, + u8 *out, size_t *outlen, int decipher) +{ + + struct sc_context *ctx; + + struct sc_apdu apdu; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 *sdata; + int r, padding = 0, cbc = 0; + + size_t block_size; + size_t len, rest_len; + size_t return_len = 0; + + size_t max_apdu_datalen; + size_t apdu_datalen; + + if (card == NULL) + return SC_ERROR_INTERNAL; + + ctx = card->ctx; + LOG_FUNC_CALLED(ctx); + + myeid_private_data_t *priv; + priv = (myeid_private_data_t *)card->drv_data; + + /* How many cipher blocks will fit in the APDU. We do not use the APDU chaining + * mechanism from OpenSC, because we need the size of the APDU data block + * to match a multiple of the cipher block size */ + + max_apdu_datalen = sc_get_max_send_size(card); + if (max_apdu_datalen > sc_get_max_recv_size(card)) + max_apdu_datalen = sc_get_max_recv_size(card); + + if (max_apdu_datalen > SC_MAX_APDU_BUFFER_SIZE) + max_apdu_datalen = SC_MAX_APDU_BUFFER_SIZE; + + sc_log(ctx, "algorithm %lu algorithm_flags %lx", priv->algorithm, priv->algorithm_flags); + + /* for C_Encrypt/C_EncryptUpdate/C_EncryptFinalize/C_Decrypt/C_DecryptUpdate/C_DecryptFinalize + * the 'outlen' is always not NULL (src/pkcs11/framework-pkcs15.c). + * For C_EncryptInit and C_DecrytpInit the 'outlen' is set to NULL + */ + if (outlen == NULL) { + /* C_EncryptInit/C_DecryptInit - clear buffers */ + sc_log(ctx, "%s (symmetric key) initialized", decipher ? "C_DecryptInit" : "C_EncryptInit"); + priv->sym_crypt_buffer_len = 0; + priv->sym_plain_buffer_len = 0; + return SC_SUCCESS; + } + + switch (priv->algorithm) { + case SC_ALGORITHM_AES: + block_size = 16; + if (priv->algorithm_flags & SC_ALGORITHM_AES_ECB) { + padding = 0; + cbc = 0; + } else if (priv->algorithm_flags & SC_ALGORITHM_AES_CBC) { + padding = 0; + cbc = 1; + } else if (priv->algorithm_flags & SC_ALGORITHM_AES_CBC_PAD) { + padding = 1; + cbc = 1; + } + break; + default: + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + } + + /* MyEID: ECB APDU must match exact cipher block size in CBC + * mode up to 240 bytes can be handled in one APDU + * round max_apdu_datalen to multiple of block_size (CBC mode) */ + + if (cbc) + max_apdu_datalen -= max_apdu_datalen % block_size; + else + max_apdu_datalen = block_size; + + /* Maybe we have more input data (from previous PSO operation). */ + rest_len = priv->sym_crypt_buffer_len; + + /* no input data from application (this is C_EncryptFinalize/C_DecryptFinalize */ + if (data == NULL) { + if (datalen != 0) + LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_LENGTH); + if (decipher) { + /* C_DecryptFinalize */ + /* decrypted buffer size must match the block size */ + if (priv->sym_plain_buffer_len != block_size) + LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_LENGTH); + /* do we have any encrypted data left? */ + if (rest_len) + LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_LENGTH); + + return_len = block_size; + if (padding) { + /* check padding */ + uint8_t i, pad_byte = *(priv->sym_plain_buffer + block_size - 1); + + sc_log(ctx, "Found padding byte %02x", pad_byte); + if (pad_byte == 0 || pad_byte > block_size) + LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_PADDING); + sdata = priv->sym_plain_buffer + block_size; + for (i = 0; i < pad_byte; i++) + if (*(--sdata) != pad_byte) + LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_PADDING); + return_len = block_size - pad_byte; + } + /* application can request buffer size or actual buffer size is too small */ + if (out == NULL) { + *outlen = return_len; + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + } + if (return_len > *outlen) + LOG_FUNC_RETURN(ctx, SC_ERROR_BUFFER_TOO_SMALL); + *outlen = return_len; + memcpy(out, priv->sym_plain_buffer, return_len); + sc_log(ctx, "C_DecryptFinal %zu bytes", *outlen); + return SC_SUCCESS; + } else { + /* C_EncryptFinalize */ + if (padding) { + uint8_t pad_byte = block_size - rest_len; + sc_log(ctx, "Generating padding, padding byte: %d", pad_byte); + sdata = priv->sym_crypt_buffer + rest_len; + memset(sdata, pad_byte, pad_byte); + rest_len = block_size; + + } else if (rest_len) { + LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_LENGTH); + } + /* fall through - encipher last block */ + } + } + /* check output buffer size */ + len = datalen + rest_len; + + sc_log(ctx, "datalen=%zu rest_len=%zu len=%zu outlen=%zu", datalen, rest_len, len, *outlen); + /* there is block_size bytes space that can be saved to next run */ + len -= (len % block_size); + + /* application can request buffer size or actual buffer size is too small */ + *outlen = len; + if (out == NULL) + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + /* application buffer is too small */ + if (*outlen < len) + LOG_FUNC_RETURN(ctx, SC_ERROR_BUFFER_TOO_SMALL); + + /* main loop */ + while (len >= block_size) { + if (!decipher) + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x84, 0x80); + else + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x84); + apdu.cla = 0; + + if (len > max_apdu_datalen) + apdu_datalen = max_apdu_datalen; + else + apdu_datalen = len; + + if (cbc) + apdu.cla = 0x10; + + len -= apdu_datalen; + sdata = sbuf; + + apdu.le = apdu_datalen; + apdu.lc = apdu_datalen; + apdu.datalen = apdu_datalen; + apdu.data = sbuf; + apdu.resplen = sizeof(rbuf); + apdu.resp = rbuf; + + /* do we have any data from the previous step ? */ + if (rest_len) { + memcpy(sbuf, priv->sym_crypt_buffer, rest_len); + sdata += rest_len; + apdu_datalen -= rest_len; + priv->sym_crypt_buffer_len = 0; + rest_len = 0; + } + if (data) { + memcpy(sdata, data, apdu_datalen); + data += apdu_datalen; + datalen -= apdu_datalen; + } + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, r, "decrypt_sym/encrypt_sym failed"); + if (apdu.resplen != apdu.datalen) + LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_LENGTH); + memcpy(out, apdu.resp, apdu.resplen); + out += apdu.resplen; + return_len += apdu.resplen; + } + /* last block is stored in buffer and is returned to application + * in next call to C_DecryptUpdate or C_DecryptFinal. This allow us + * to compute how many bytes is to be returned after padding removal. + * Whole handling of this is here, because "data" and "out" buffer + * can be in the same place. + */ + if (decipher) { + uint8_t tmp_buf[16]; + if (return_len >= block_size) { + /* save last block to temp buffer */ + memcpy(tmp_buf, out - block_size, block_size); + if (priv->sym_plain_buffer_len) { + /* insert previous last block to output buffer */ + sc_log(ctx, "inserting block from previous decrypt"); + memmove(out - return_len + block_size, out - return_len, return_len - block_size); + memcpy(out - return_len, priv->sym_plain_buffer, block_size); + } else + return_len -= block_size; + /* save last (decrypted) block */ + memcpy(priv->sym_plain_buffer, tmp_buf, block_size); + priv->sym_plain_buffer_len = block_size; + + } else + priv->sym_plain_buffer_len = 0; + } + /* save rest of data for next run */ + priv->sym_crypt_buffer_len = datalen; + sc_log(ctx, "rest data len = %zu", datalen); + if (data) + memcpy(priv->sym_crypt_buffer, data, datalen); + sc_log(ctx, "return data len = %zu", return_len); + *outlen = return_len; + return SC_SUCCESS; +} + +static int +myeid_encrypt_sym(struct sc_card *card, const u8 *data, size_t datalen, u8 *out, size_t *outlen) +{ + return myeid_enc_dec_sym(card, data, datalen, out, outlen, 0); +} + +static int +myeid_decrypt_sym(struct sc_card *card, const u8 *data, size_t datalen, u8 *out, size_t *outlen) +{ + return myeid_enc_dec_sym(card, data, datalen, out, outlen, 1); +} static struct sc_card_driver * sc_get_driver(void) { @@ -1535,6 +2111,8 @@ static struct sc_card_driver * sc_get_driver(void) myeid_ops.append_record = NULL; myeid_ops.update_record = NULL; myeid_ops.select_file = myeid_select_file; + myeid_ops.get_response = iso_ops->get_response; + myeid_ops.logout = myeid_logout; myeid_ops.create_file = myeid_create_file; myeid_ops.delete_file = myeid_delete_file; myeid_ops.list_files = myeid_list_files; @@ -1544,6 +2122,10 @@ static struct sc_card_driver * sc_get_driver(void) myeid_ops.process_fci = myeid_process_fci; myeid_ops.card_ctl = myeid_card_ctl; myeid_ops.pin_cmd = myeid_pin_cmd; + myeid_ops.wrap = myeid_wrap_key; + myeid_ops.unwrap = myeid_unwrap_key; + myeid_ops.encrypt_sym = myeid_encrypt_sym; + myeid_ops.decrypt_sym = myeid_decrypt_sym; return &myeid_drv; } diff --git a/src/libopensc/card-npa.c b/src/libopensc/card-npa.c index 2de14d29ec..deef8b9cda 100644 --- a/src/libopensc/card-npa.c +++ b/src/libopensc/card-npa.c @@ -1,7 +1,7 @@ /* * card-npa.c: Recognize known German identity cards * - * Copyright (C) 2011-2015 Frank Morgner + * Copyright (C) 2011-2018 Frank Morgner * * This file is part of OpenSC. * @@ -17,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -31,6 +31,11 @@ #include "sm/sm-eac.h" #include +#ifdef ENABLE_OPENSSL +#include +#endif + +static int fread_to_eof(const char *file, unsigned char **buf, size_t *buflen); #include "../tools/fread_to_eof.c" struct npa_drv_data { @@ -65,22 +70,6 @@ static void npa_drv_data_free(struct npa_drv_data *drv_data) } } -static struct sc_atr_table npa_atrs[] = { - {"3B:8A:80:01:80:31:F8:73:F7:41:E0:82:90:00:75", - "FF:FF:FF:FF:FF:FF:00:FF:00:00:FF:FF:FF:FF:00", - "German ID card (neuer Personalausweis, nPA)", SC_CARD_TYPE_NPA, 0, NULL}, - {"3B:88:80:01:00:00:00:00:00:00:00:00:09", NULL, - "German ID card (neuer Personalausweis, nPA)", SC_CARD_TYPE_NPA, 0, NULL}, - {"3B:87:80:01:80:31:B8:73:84:01:E0:19", NULL, - "German ID card (neuer Personalausweis, nPA)", SC_CARD_TYPE_NPA, 0, NULL}, - {"3B:84:80:01:00:00:90:00:95", NULL, - "German ID card (Test neuer Personalausweis)", SC_CARD_TYPE_NPA_TEST, 0, NULL}, - {"3B:88:80:01:00:E1:F3:5E:13:77:83:00:00", - "FF:FF:FF:FF:00:FF:FF:FF:FF:FF:FF:FF:00", - "German ID card (Test Online-Ausweisfunktion)", SC_CARD_TYPE_NPA_ONLINE, 0, NULL}, - {NULL, NULL, NULL, 0, 0, NULL} -}; - static struct sc_card_operations npa_ops; static struct sc_card_driver npa_drv = { "German ID card (neuer Personalausweis, nPA)", @@ -147,11 +136,35 @@ static int npa_load_options(sc_context_t *ctx, struct npa_drv_data *drv_data) return r; } +// clang-format off +unsigned char dir_content_ref[] = { + 0x61, 0x32, 0x4F, 0x0F, 0xE8, 0x28, 0xBD, 0x08, 0x0F, 0xA0, 0x00, 0x00, + 0x01, 0x67, 0x45, 0x53, 0x49, 0x47, 0x4E, 0x50, 0x0F, 0x43, 0x49, 0x41, + 0x20, 0x7A, 0x75, 0x20, 0x44, 0x46, 0x2E, 0x65, 0x53, 0x69, 0x67, 0x6E, + 0x51, 0x00, 0x73, 0x0C, 0x4F, 0x0A, 0xA0, 0x00, 0x00, 0x01, 0x67, 0x45, + 0x53, 0x49, 0x47, 0x4E, 0x61, 0x09, 0x4F, 0x07, 0xA0, 0x00, 0x00, 0x02, + 0x47, 0x10, 0x01, 0x61, 0x0B, 0x4F, 0x09, 0xE8, 0x07, 0x04, 0x00, 0x7F, + 0x00, 0x07, 0x03, 0x02, 0x61, 0x0C, 0x4F, 0x0A, 0xA0, 0x00, 0x00, 0x01, + 0x67, 0x45, 0x53, 0x49, 0x47, 0x4E, +}; +// clang-format on + static int npa_match_card(sc_card_t * card) { - if (_sc_match_atr(card, npa_atrs, &card->type) < 0) - return 0; - return 1; + unsigned char dir_content[sizeof dir_content_ref]; + unsigned char id[] = {0x2F, 0x00}; + sc_apdu_t select_ef_dir; + + sc_format_apdu_ex(&select_ef_dir, 0x00, 0xA4, 0x02, 0x0C, id, sizeof id, NULL, 0); + + if (SC_SUCCESS == sc_select_file(card, sc_get_mf_path(), NULL) + && SC_SUCCESS == sc_transmit_apdu(card, &select_ef_dir) + && select_ef_dir.sw1 == 0x90 && select_ef_dir.sw2 == 0x00 + && sizeof dir_content == sc_read_binary(card, 0, dir_content, sizeof dir_content, 0) + && 0 == memcmp(dir_content_ref, dir_content, sizeof dir_content)) + return 1; + + return 0; } static void npa_get_cached_pace_params(sc_card_t *card, @@ -291,11 +304,10 @@ static int npa_unlock_esign(sc_card_t *card) } /* FIXME set flags with opensc.conf */ - npa_default_flags |= NPA_FLAG_DISABLE_CHECK_ALL; - npa_default_flags |= NPA_FLAG_DISABLE_CHECK_TA; - npa_default_flags |= NPA_FLAG_DISABLE_CHECK_CA; + eac_default_flags |= EAC_FLAG_DISABLE_CHECK_TA; + eac_default_flags |= EAC_FLAG_DISABLE_CHECK_CA; - /* FIXME show an alert to the user if can == NULL */ + /* FIXME show an alert to the user if CAN is NULL */ r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02); if (SC_SUCCESS != r) { sc_log(card->ctx, "Error verifying CAN.\n"); @@ -313,7 +325,7 @@ static int npa_unlock_esign(sc_card_t *card) } r = perform_chip_authentication(card, &ef_cardsecurity, &ef_cardsecurity_len); if ( SC_SUCCESS != r) { - sc_log(card->ctx, "Error verifying the chips authenticy.\n"); + sc_log(card->ctx, "Error verifying the chip's authenticity.\n"); } sc_log(card->ctx, "Proved Access rights to eSign application with configured key as ST.\n"); @@ -331,6 +343,51 @@ static int npa_unlock_esign(sc_card_t *card) return r; } +static int npa_finish(sc_card_t * card) +{ + sc_sm_stop(card); + npa_drv_data_free(card->drv_data); + card->drv_data = NULL; + + return SC_SUCCESS; +} + +static void npa_init_apps(sc_card_t * card) +{ + /* this initializes the internal structures with the data from + * `dir_content_ref` */ + const u8 *aids[] = { + (const u8 *) "\xa0\x00\x00\x02\x47\x10\x01", + (const u8 *) "\xe8\x07\x04\x00\x7f\x00\x07\x03\x02", + (const u8 *) "\xa0\x00\x00\x01\x67\x45\x53\x49\x47\x4e", + }; + const size_t lens[] = {7, 9, 10}; + size_t i; + + sc_free_apps(card); + card->app_count = 0; + + for (i = 0; i < 3; i++) { + const u8 *aid = aids[i]; + size_t aid_len = lens[i]; + struct sc_app_info *app = calloc(1, sizeof *app); + if (NULL == app) + continue; + + app->aid.len = aid_len; + memcpy(app->aid.value, aid, aid_len); + + app->path.len = aid_len; + memcpy(app->path.value, aid, aid_len); + app->path.type = SC_PATH_TYPE_DF_NAME; + + app->rec_nr = -1; + + card->app[card->app_count] = app; + card->app_count++; + } +} + static int npa_init(sc_card_t * card) { int flags = SC_ALGORITHM_ECDSA_RAW; @@ -343,7 +400,7 @@ static int npa_init(sc_card_t * card) } card->caps |= SC_CARD_CAP_APDU_EXT | SC_CARD_CAP_RNG; - /* 1520 bytes is the minimum lenght of the communication buffer in all + /* 1520 bytes is the minimum length of the communication buffer in all * Chip/OS variants */ card->max_recv_size = 1520; card->max_send_size = 1520; @@ -367,11 +424,9 @@ static int npa_init(sc_card_t * card) if (r != SC_SUCCESS) goto err; -#ifdef ENABLE_OPENPACE - EAC_init(); -#endif card->drv_data = npa_drv_data_create(); if (!card->drv_data) { + npa_finish(card); r = SC_ERROR_OUT_OF_MEMORY; goto err; } @@ -379,27 +434,18 @@ static int npa_init(sc_card_t * card) if (r != SC_SUCCESS) goto err; + npa_init_apps(card); + /* unlock the eSign application for reading the certificates * by the PKCS#15 layer (i.e. sc_pkcs15_bind_internal) */ - if (SC_SUCCESS != npa_unlock_esign(card)) - sc_log(card->ctx, "Propably not all functionality will be available.\n"); + if (SC_SUCCESS != npa_unlock_esign(card)) { + sc_log(card->ctx, "Probably not all functionality will be available.\n"); + } err: return r; } -static int npa_finish(sc_card_t * card) -{ - sc_sm_stop(card); - npa_drv_data_free(card->drv_data); - card->drv_data = NULL; -#ifdef ENABLE_OPENPACE - EAC_cleanup(); -#endif - - return SC_SUCCESS; -} - static int npa_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num) { @@ -426,12 +472,12 @@ static int npa_set_security_env(struct sc_card *card, } static int npa_pin_cmd_get_info(struct sc_card *card, - struct sc_pin_cmd_data *data, int *tries_left) + struct sc_pin_cmd_data *data) { int r; u8 pin_reference; - if (!data || data->pin_type != SC_AC_CHV || !tries_left) { + if (!data || data->pin_type != SC_AC_CHV) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } @@ -441,28 +487,25 @@ static int npa_pin_cmd_get_info(struct sc_card *card, case PACE_PIN_ID_CAN: case PACE_PIN_ID_MRZ: /* usually unlimited number of retries */ - *tries_left = -1; - data->pin1.max_tries = -1; data->pin1.tries_left = -1; + data->pin1.max_tries = -1; r = SC_SUCCESS; break; case PACE_PIN_ID_PUK: /* usually 10 tries */ - *tries_left = 10; + data->pin1.tries_left = 10; data->pin1.max_tries = 10; - r = npa_pace_get_tries_left(card, - pin_reference, tries_left); - data->pin1.tries_left = *tries_left; + r = eac_pace_get_tries_left(card, + pin_reference, &data->pin1.tries_left); break; case PACE_PIN_ID_PIN: /* usually 3 tries */ - *tries_left = 3; + data->pin1.tries_left = 3; data->pin1.max_tries = 3; - r = npa_pace_get_tries_left(card, - pin_reference, tries_left); - data->pin1.tries_left = *tries_left; + r = eac_pace_get_tries_left(card, + pin_reference, &data->pin1.tries_left); break; default: @@ -476,7 +519,7 @@ static int npa_pin_cmd_get_info(struct sc_card *card, static int npa_pace_verify(struct sc_card *card, unsigned char pin_reference, struct sc_pin_cmd_pin *pin, - const unsigned char *chat, size_t chat_length, int *tries_left) + const unsigned char *chat, size_t chat_length) { int r; struct establish_pace_channel_input pace_input; @@ -497,13 +540,11 @@ static int npa_pace_verify(struct sc_card *card, r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02); - if (tries_left) { - if (pace_output.mse_set_at_sw1 == 0x63 - && (pace_output.mse_set_at_sw2 & 0xc0) == 0xc0) { - *tries_left = pace_output.mse_set_at_sw2 & 0x0f; - } else { - *tries_left = -1; - } + if (pace_output.mse_set_at_sw1 == 0x63 + && (pace_output.mse_set_at_sw2 & 0xc0) == 0xc0) { + pin->tries_left = pace_output.mse_set_at_sw2 & 0x0f; + } else { + pin->tries_left = -1; } /* resume the PIN if needed */ @@ -511,10 +552,10 @@ static int npa_pace_verify(struct sc_card *card, && r != SC_SUCCESS && pace_output.mse_set_at_sw1 == 0x63 && (pace_output.mse_set_at_sw2 & 0xc0) == 0xc0 - && (pace_output.mse_set_at_sw2 & 0x0f) <= UC_PIN_SUSPENDED) { + && (pace_output.mse_set_at_sw2 & 0x0f) <= EAC_UC_PIN_SUSPENDED) { /* TODO ask for user consent when automatically resuming the PIN */ sc_log(card->ctx, "%s is suspended. Will try to resume it with %s.\n", - npa_secret_name(pin_reference), npa_secret_name(PACE_PIN_ID_CAN)); + eac_secret_name(pin_reference), eac_secret_name(PACE_PIN_ID_CAN)); pace_input.pin_id = PACE_PIN_ID_CAN; pace_input.pin = NULL; @@ -532,30 +573,26 @@ static int npa_pace_verify(struct sc_card *card, r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02); if (r == SC_SUCCESS) { - sc_log(card->ctx, "%s resumed.\n", npa_secret_name(pin_reference)); - if (tries_left) { - *tries_left = MAX_PIN_TRIES; - } + sc_log(card->ctx, "%s resumed.\n", eac_secret_name(pin_reference)); + pin->tries_left = EAC_MAX_PIN_TRIES; } else { - if (tries_left) { - if (pace_output.mse_set_at_sw1 == 0x63 - && (pace_output.mse_set_at_sw2 & 0xc0) == 0xc0) { - *tries_left = pace_output.mse_set_at_sw2 & 0x0f; - } else { - *tries_left = -1; - } + if (pace_output.mse_set_at_sw1 == 0x63 + && (pace_output.mse_set_at_sw2 & 0xc0) == 0xc0) { + pin->tries_left = pace_output.mse_set_at_sw2 & 0x0f; + } else { + pin->tries_left = -1; } } } } - if (pin_reference == PACE_PIN_ID_PIN && tries_left) { - if (*tries_left == 0) { + if (pin_reference == PACE_PIN_ID_PIN) { + if (pin->tries_left == 0) { sc_log(card->ctx, "%s is suspended and must be resumed.\n", - npa_secret_name(pin_reference)); - } else if (*tries_left == 1) { + eac_secret_name(pin_reference)); + } else if (pin->tries_left == 1) { sc_log(card->ctx, "%s is blocked and must be unblocked.\n", - npa_secret_name(pin_reference)); + eac_secret_name(pin_reference)); } } @@ -570,7 +607,7 @@ static int npa_pace_verify(struct sc_card *card, } static int npa_standard_pin_cmd(struct sc_card *card, - struct sc_pin_cmd_data *data, int *tries_left) + struct sc_pin_cmd_data *data) { int r; struct sc_card_driver *iso_drv; @@ -580,14 +617,85 @@ static int npa_standard_pin_cmd(struct sc_card *card, if (!iso_drv || !iso_drv->ops || !iso_drv->ops->pin_cmd) { r = SC_ERROR_INTERNAL; } else { - r = iso_drv->ops->pin_cmd(card, data, tries_left); + r = iso_drv->ops->pin_cmd(card, data); + } + + return r; +} + +int +npa_reset_retry_counter(sc_card_t *card, enum s_type pin_id, + int ask_for_secret, const char *new, size_t new_len) +{ + sc_apdu_t apdu; + char *p = NULL; + int r; + + if (ask_for_secret && (!new || !new_len)) { + if (!(SC_READER_CAP_PIN_PAD & card->reader->capabilities)) { +#ifdef ENABLE_OPENSSL + p = malloc(EAC_MAX_PIN_LEN+1); + if (!p) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Not enough memory for new PIN.\n"); + return SC_ERROR_OUT_OF_MEMORY; + } + if (0 > EVP_read_pw_string_min(p, + EAC_MIN_PIN_LEN, EAC_MAX_PIN_LEN+1, + "Please enter your new PIN: ", 0)) { + sc_log_openssl(card->ctx); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not read new PIN.\n"); + free(p); + return SC_ERROR_INTERNAL; + } + new_len = strlen(p); + if (new_len > EAC_MAX_PIN_LEN) { + free(p); + return SC_ERROR_INVALID_PIN_LENGTH; + } + new = p; +#else + return SC_ERROR_NOT_SUPPORTED; +#endif + } + } + + sc_format_apdu(card, &apdu, 0, 0x2C, 0, pin_id); + apdu.data = (u8 *) new; + apdu.datalen = new_len; + apdu.lc = apdu.datalen; + + if (new_len || ask_for_secret) { + apdu.p1 = 0x02; + apdu.cse = SC_APDU_CASE_3_SHORT; + } else { + apdu.p1 = 0x03; + apdu.cse = SC_APDU_CASE_1; + } + + if (ask_for_secret && !new_len) { + struct sc_pin_cmd_data data; + data.apdu = &apdu; + data.cmd = SC_PIN_CMD_CHANGE; + data.flags = SC_PIN_CMD_IMPLICIT_CHANGE; + data.pin2.encoding = SC_PIN_ENCODING_ASCII; + data.pin2.offset = 5; + data.pin2.max_length = EAC_MAX_PIN_LEN; + data.pin2.min_length = EAC_MIN_PIN_LEN; + data.pin2.pad_length = 0; + r = card->reader->ops->perform_verify(card->reader, &data); + } else + r = sc_transmit_apdu(card, &apdu); + + if (p) { + sc_mem_clear(p, new_len); + free(p); } return r; } static int npa_pin_cmd(struct sc_card *card, - struct sc_pin_cmd_data *data, int *tries_left) + struct sc_pin_cmd_data *data) { int r; @@ -603,7 +711,7 @@ static int npa_pin_cmd(struct sc_card *card, switch (data->cmd) { case SC_PIN_CMD_GET_INFO: - r = npa_pin_cmd_get_info(card, data, tries_left); + r = npa_pin_cmd_get_info(card, data); if (r != SC_SUCCESS) goto err; break; @@ -620,7 +728,7 @@ static int npa_pin_cmd(struct sc_card *card, if (card->sm_ctx.sm_mode != SM_MODE_TRANSMIT) { /* PUK has not yet been verified */ r = npa_pace_verify(card, PACE_PIN_ID_PUK, &(data->pin1), NULL, - 0, NULL); + 0); if (r != SC_SUCCESS) goto err; } @@ -639,7 +747,7 @@ static int npa_pin_cmd(struct sc_card *card, case PACE_PIN_ID_MRZ: case PACE_PIN_ID_PIN: r = npa_pace_verify(card, data->pin_reference, - &(data->pin1), NULL, 0, tries_left); + &(data->pin1), NULL, 0); if (r != SC_SUCCESS) goto err; break; @@ -651,7 +759,7 @@ static int npa_pin_cmd(struct sc_card *card, * unlocked, see npa_init(). * * Now, verify the QES PIN. */ - r = npa_standard_pin_cmd(card, data, tries_left); + r = npa_standard_pin_cmd(card, data); if (r != SC_SUCCESS) goto err; break; @@ -673,7 +781,7 @@ static int npa_pin_cmd(struct sc_card *card, } err: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } static int npa_logout(sc_card_t *card) @@ -684,7 +792,7 @@ static int npa_logout(sc_card_t *card) if (card->reader->capabilities & SC_READER_CAP_PACE_GENERIC) { /* If PACE is done between reader and card, SM is transparent to us as - * it ends at the reader. With CLA=0x0C we provoque a SM error to + * it ends at the reader. With CLA=0x0C we provoke a SM error to * disable SM on the reader. */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xA4, 0x00, 0x00); apdu.cla = 0x0C; diff --git a/src/libopensc/card-npa.h b/src/libopensc/card-npa.h index eca8516771..46b9ae00d1 100644 --- a/src/libopensc/card-npa.h +++ b/src/libopensc/card-npa.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CARD_NPA_H @@ -25,13 +25,52 @@ extern "C" { #endif -const unsigned char esign_chat[] = { +#include "sm/sm-eac.h" + +static const unsigned char esign_chat[] = { 0x7F, 0x4C, 0x0E, 0x06, 0x09, 0x04, 0x00, 0x7F, 0x00, 0x07, 0x03, 0x01, 0x02, 0x03, 0x53, 0x01, 0x03, }; -const unsigned char df_esign_aid[] = { 0xa0, 0x00, 0x00, 0x01, 0x67, 0x45, 0x53, 0x49, 0x47, 0x4e}; +static const unsigned char df_esign_aid[] = { 0xa0, 0x00, 0x00, 0x01, 0x67, 0x45, 0x53, 0x49, 0x47, 0x4e}; + +/** + * @brief Sends a reset retry counter APDU + * + * According to TR-03110 the reset retry counter APDU is used to set a new PIN + * or to reset the retry counter of the PIN. The standard requires this + * operation to be authorized either by an established PACE channel or by the + * effective authorization of the terminal's certificate. + * + * @param[in] card + * @param[in] pin_id Type of secret (usually PIN or CAN). You may use enum s_type from \c . + * @param[in] ask_for_secret whether to ask the user for the secret (\c 1) or not (\c 0) + * @param[in] new (optional) new secret + * @param[in] new_len (optional) length of \a new + * + * @return \c SC_SUCCESS or error code if an error occurred + */ +int npa_reset_retry_counter(sc_card_t *card, + enum s_type pin_id, int ask_for_secret, + const char *new, size_t new_len); + +/** + * @brief Send APDU to unblock the PIN + * + * @param[in] card + */ +#define npa_unblock_pin(card) \ + npa_reset_retry_counter(card, PACE_PIN, 0, NULL, 0) +/** + * @brief Send APDU to set a new PIN + * + * @param[in] card + * @param[in] newp (optional) new PIN + * @param[in] newplen (optional) length of \a new + */ +#define npa_change_pin(card, newp, newplen) \ + npa_reset_retry_counter(card, PACE_PIN, 1, newp, newplen) #ifdef __cplusplus } diff --git a/src/libopensc/card-nqApplet.c b/src/libopensc/card-nqApplet.c new file mode 100644 index 0000000000..233741a3cb --- /dev/null +++ b/src/libopensc/card-nqApplet.c @@ -0,0 +1,526 @@ +/* + * Support for the JCOP4 Cards with NQ-Applet + * + * Copyright (C) 2021 jozsefd + * + * 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 + */ + +#include +#include + +#include "opensc.h" +#include "asn1.h" +#include "cardctl.h" +#include "internal.h" +#include "log.h" + +#define APPLET_VERSION_LEN 2 +#define APPLET_MEMTYPE_LEN 1 +#define APPLET_SERIALNR_LEN 8 + +/* card constants */ +static const struct sc_atr_table nqapplet_atrs[] = { + {"3b:d5:18:ff:81:91:fe:1f:c3:80:73:c8:21:10:0a", NULL, NULL, SC_CARD_TYPE_NQ_APPLET, 0, NULL}, // JCOP4 + {"3b:d8:18:ff:81:b1:fe:45:1f:c3:80:73:c8:21:10:52:b7:7f:f8", NULL, NULL, SC_CARD_TYPE_NQ_APPLET, 0, NULL}, // JCOP4.52 + {"3b:85:80:01:80:73:C8:21:10:0e", NULL, NULL, SC_CARD_TYPE_NQ_APPLET_RFID, 0, NULL}, + {NULL, NULL, NULL, 0, 0, NULL} +}; + +static const u8 nqapplet_aid[] = {0xd2, 0x76, 0x00, 0x01, 0x80, 0xBA, 0x01, 0x44, 0x02, 0x01, 0x00}; + +static struct sc_card_operations nqapplet_operations; +static struct sc_card_operations *iso_operations = NULL; + +#define KEY_REFERENCE_NO_KEY 0x00 +#define KEY_REFERENCE_AUTH_KEY 0x01 +#define KEY_REFERENCE_ENCR_KEY 0x02 + +struct nqapplet_driver_data { + u8 version_minor; + u8 version_major; + u8 key_reference; +}; +typedef struct nqapplet_driver_data *nqapplet_driver_data_ptr; + +static struct sc_card_driver nqapplet_driver = { + "NQ-Applet", // name + "nqapplet", // short name + &nqapplet_operations, // operations + NULL, // atr table + 0, // nr of atr + NULL // dll? +}; + +static const struct sc_card_error nqapplet_errors[] = { + {0x6700, SC_ERROR_WRONG_LENGTH, "Invalid LC or LE"}, + {0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED, "Security status not satisfied"}, // TODO MK/DK?? + {0x6985, SC_ERROR_NOT_ALLOWED, "Invalid PIN or key"}, + {0x6986, SC_ERROR_NOT_ALLOWED, "Conditions of use not satisfied"}, + {0x6A80, SC_ERROR_INVALID_ARGUMENTS, "Invalid parameters"}, + {0x6A82, SC_ERROR_OBJECT_NOT_FOUND, "Data object not found"}, + {0x6A84, SC_ERROR_NOT_ENOUGH_MEMORY, "Not enough memory"}, + {0x6A86, SC_ERROR_INCORRECT_PARAMETERS, "Invalid P1 or P2"}, + {0x6A88, SC_ERROR_INVALID_ARGUMENTS, "Wrong key ID"}, + {0x6D00, SC_ERROR_FILE_NOT_FOUND, "Applet not found"}}; + +/* convenience functions */ + +static int init_driver_data(sc_card_t *card, u8 version_major, u8 version_minor) +{ + nqapplet_driver_data_ptr data = calloc(1, sizeof(struct nqapplet_driver_data)); + if (data == NULL) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + + data->version_major = version_major; + data->version_minor = version_minor; + data->key_reference = KEY_REFERENCE_NO_KEY; + card->drv_data = (void *)data; + return SC_SUCCESS; +} + +/** + * SELECT NQ-Applet, on success it returns the applet version and card serial nr. + * + * @param[in] card + * @param[out,opt] version_major Version major of the applet + * @param[out,opt] version_minor Version minor of the applet + * @param[out,opt] serial_nr Buffer to receive serial number octets + * @param[in] cb_serial_nr Size of buffer in octets + * @param[out,opt] serial_nr_len The actual number of octet copied into serial_nr buffer + * + * @return SC_SUCCESS: The applet is present and selected. + * + */ +static int select_nqapplet(sc_card_t *card, u8 *version_major, u8 *version_minor, u8 *serial_nr, + size_t cb_serial_nr, size_t *serial_nr_len) +{ + int rv; + sc_context_t *ctx = card->ctx; + u8 buffer[APPLET_VERSION_LEN + APPLET_MEMTYPE_LEN + APPLET_SERIALNR_LEN + 2]; + size_t cb_buffer = sizeof(buffer); + size_t cb_aid = sizeof(nqapplet_aid); + + LOG_FUNC_CALLED(card->ctx); + + rv = iso7816_select_aid(card, nqapplet_aid, cb_aid, buffer, &cb_buffer); + LOG_TEST_RET(card->ctx, rv, "Failed to select NQ-Applet."); + + if (cb_buffer < APPLET_VERSION_LEN + APPLET_MEMTYPE_LEN + APPLET_SERIALNR_LEN) { + SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_WRONG_LENGTH); + } + + if (version_major != NULL) { + *version_major = buffer[0]; + } + if (version_minor != NULL) { + *version_minor = buffer[1]; + } + if (serial_nr != NULL && cb_serial_nr > 0 && serial_nr_len != NULL) { + size_t cb = MIN(APPLET_SERIALNR_LEN, cb_serial_nr); + memcpy(serial_nr, buffer + 3, cb); + *serial_nr_len = cb; + } + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + +/* driver operations API */ +static int nqapplet_match_card(struct sc_card *card) +{ + int rv = _sc_match_atr(card, nqapplet_atrs, &card->type); + return (rv >= 0); +} + +static int nqapplet_init(struct sc_card *card) +{ + u8 version_major; + u8 version_minor; + u8 serial_nr[APPLET_SERIALNR_LEN]; + size_t cb_serial_nr = sizeof(serial_nr); + unsigned long rsa_flags = 0; + + LOG_FUNC_CALLED(card->ctx); + int rv = + select_nqapplet(card, &version_major, &version_minor, serial_nr, cb_serial_nr, &cb_serial_nr); + if (rv != SC_SUCCESS) { + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_CARD, "Cannot select NQ-Applet."); + } + + rv = init_driver_data(card, version_major, version_minor); + LOG_TEST_RET(card->ctx, rv, "Failed to initialize driver data."); + + card->max_send_size = 255; + card->max_recv_size = 256; + card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO; + rsa_flags |= SC_ALGORITHM_RSA_RAW; + _sc_card_add_rsa_alg(card, 3072, rsa_flags, 0); + + card->serialnr.len = MIN(sizeof(card->serialnr.value), cb_serial_nr); + memcpy(card->serialnr.value, serial_nr, card->serialnr.len); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int nqapplet_finish(struct sc_card *card) +{ + nqapplet_driver_data_ptr data = (nqapplet_driver_data_ptr)card->drv_data; + + LOG_FUNC_CALLED(card->ctx); + if (data != NULL) { + free(data); + card->drv_data = NULL; + } + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +nqapplet_get_response(struct sc_card *card, size_t *cb_resp, u8 *resp) +{ + struct sc_apdu apdu = {0}; + int rv; + size_t resplen; + + LOG_FUNC_CALLED(card->ctx); + resplen = MIN(sc_get_max_recv_size(card), *cb_resp); + + sc_format_apdu_ex(&apdu, 0x80, 0xC0, 0x00, 0x00, NULL, 0, resp, resplen); + apdu.flags |= SC_APDU_FLAGS_NO_GET_RESP; + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + *cb_resp = apdu.resplen; + + if (apdu.resplen == 0) { + LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); + } + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + rv = SC_SUCCESS; + } else if (apdu.sw1 == 0x61) { + rv = apdu.sw2 == 0 ? 256 : apdu.sw2; + } else if (apdu.sw1 == 0x62 && apdu.sw2 == 0x82) { + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + } + + LOG_FUNC_RETURN(card->ctx, rv); +} + +static int nqapplet_get_challenge(struct sc_card *card, u8 *buf, size_t count) +{ + int r; + struct sc_apdu apdu; + + LOG_FUNC_CALLED(card->ctx); + sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x84, 0x00, 0x00); + apdu.le = count; + apdu.resp = buf; + apdu.resplen = count; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "GET CHALLENGE failed"); + + if (count < apdu.resplen) { + return (int)count; + } + + return (int)apdu.resplen; +} + +static int nqapplet_logout(struct sc_card *card) +{ + LOG_FUNC_CALLED(card->ctx); + /* selecting NQ-Applet again will reset the applet status and unauthorize PINs */ + int rv = select_nqapplet(card, NULL, NULL, NULL, 0, NULL); + if (rv != SC_SUCCESS) { + LOG_TEST_RET(card->ctx, rv, "Failed to logout"); + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +int nqapplet_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num) +{ + /* Note: the NQ-Applet does not have APDU for SET SECURITY ENV, + this function checks the intended parameters and sets card_data.key_reference */ + nqapplet_driver_data_ptr data; + u8 key_reference = KEY_REFERENCE_NO_KEY; + + LOG_FUNC_CALLED(card->ctx); + + data = (nqapplet_driver_data_ptr)card->drv_data; + data->key_reference = KEY_REFERENCE_NO_KEY; + + if (se_num != 0) { + LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, + "Storing of security environment is not supported"); + } + if (env->key_ref_len == 1) { + key_reference = env->key_ref[0]; + } + + switch (env->operation) { + case SC_SEC_OPERATION_DECIPHER: + if (key_reference != KEY_REFERENCE_AUTH_KEY && key_reference != KEY_REFERENCE_ENCR_KEY) { + LOG_TEST_RET(card->ctx, SC_ERROR_INCOMPATIBLE_KEY, + "Decipher operation is only supported with AUTH and ENCR keys."); + } + data->key_reference = key_reference; + break; + case SC_SEC_OPERATION_SIGN: + if (key_reference != KEY_REFERENCE_AUTH_KEY) { + LOG_TEST_RET(card->ctx, SC_ERROR_INCOMPATIBLE_KEY, + "Sign operation is only supported with AUTH key."); + } + data->key_reference = key_reference; + break; + default: + LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported sec. operation."); + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int nqapplet_decipher(struct sc_card *card, const u8 *data, size_t cb_data, u8 *out, size_t outlen) +{ + int rv; + struct sc_apdu apdu; + u8 p1 = 0x80; + u8 p2 = 0x86; + nqapplet_driver_data_ptr drv_data; + + LOG_FUNC_CALLED(card->ctx); + + drv_data = (nqapplet_driver_data_ptr)card->drv_data; + + if (drv_data->key_reference == KEY_REFERENCE_AUTH_KEY) { + p1 = 0x9E; + p2 = 0x9A; + } else if (drv_data->key_reference != KEY_REFERENCE_ENCR_KEY) { + LOG_TEST_RET(card->ctx, SC_ERROR_INCOMPATIBLE_KEY, + "Decipher operation is only supported with AUTH and ENCR keys."); + } + + /* the applet supports only 3072 RAW RSA, input buffer size must be 384 octets, + output buffer size must be at least 384 octets */ + sc_format_apdu_ex(&apdu, 0x80, 0x2A, p1, p2, data, cb_data, out, outlen); + apdu.le = 256; + apdu.flags |= SC_APDU_FLAGS_CHAINING; + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + rv = (int)apdu.resplen; + } else if (apdu.sw1 == 0x61) { + rv = apdu.sw2 == 0 ? 256 : apdu.sw2; + } else { + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + } + + LOG_FUNC_RETURN(card->ctx, rv); +} + +static int nqapplet_compute_signature(struct sc_card *card, const u8 *data, size_t cb_data, u8 *out, + size_t outlen) +{ + int rv; + struct sc_apdu apdu; + nqapplet_driver_data_ptr drv_data; + + LOG_FUNC_CALLED(card->ctx); + drv_data = (nqapplet_driver_data_ptr)card->drv_data; + + if (drv_data->key_reference != KEY_REFERENCE_AUTH_KEY) { + LOG_TEST_RET(card->ctx, SC_ERROR_INCOMPATIBLE_KEY, + "Sign operation is only supported with AUTH key."); + } + + /* the applet supports only 3072 RAW RSA, input buffer size must be 384 octets, + output buffer size must be at least 384 octets */ + sc_format_apdu_ex(&apdu, 0x80, 0x2A, 0x9E, 0x9A, data, cb_data, out, outlen); + apdu.le = 256; + apdu.flags |= SC_APDU_FLAGS_CHAINING; + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + rv = (int)apdu.resplen; + } else if (apdu.sw1 == 0x61) { + rv = apdu.sw2 == 0 ? 256 : apdu.sw2; + } else { + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + } + + LOG_FUNC_RETURN(card->ctx, rv); +} + +static int nqapplet_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) +{ + const int nqapplet_error_count = sizeof(nqapplet_errors) / sizeof(struct sc_card_error); + int i; + + LOG_FUNC_CALLED(card->ctx); + sc_log(card->ctx, "Checking sw1 = 0x%02x, sw2 = 0x%02x\n", sw1, sw2); + + for (i = 0; i < nqapplet_error_count; i++) { + if (nqapplet_errors[i].SWs == ((sw1 << 8) | sw2)) { + LOG_TEST_RET(card->ctx, nqapplet_errors[i].errorno, nqapplet_errors[i].errorstr); + } + } + + return iso_operations->check_sw(card, sw1, sw2); +} + +static int nqapplet_get_data(struct sc_card *card, unsigned int id, u8 *resp, size_t cb_resp) +{ + struct sc_apdu apdu; + int rv; + + LOG_FUNC_CALLED(card->ctx); + + sc_format_apdu_ex(&apdu, 0x80, 0xB0, 0x00, (u8)id, NULL, 0, resp, cb_resp); + apdu.le = 256; + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + rv = (int)apdu.resplen; + } else if (apdu.sw1 == 0x61) { + rv = apdu.sw2 == 0 ? 256 : apdu.sw2; + } else if (apdu.sw1 == 0x62 && apdu.sw2 == 0x82) { + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + } + + LOG_FUNC_RETURN(card->ctx, rv); +} + +static int nqapplet_select_file(struct sc_card *card, const struct sc_path *in_path, + struct sc_file **file_out) +{ + LOG_FUNC_CALLED(card->ctx); + + /* the applet does not support SELECT EF/DF except for SELECT APPLET. + In order to enable opensc-explorer add support for virtually selecting MF only */ + if (in_path->type == SC_PATH_TYPE_PATH && in_path->len == 2 && + memcmp(in_path->value, "\x3F\x00", 2) == 0) { + if (file_out != NULL) { + struct sc_file *file = sc_file_new(); + if (file == NULL) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + file->path = *in_path; + *file_out = file; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + } + } + // TODO allow selecting Applet AID + + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); +} + +static int nqapplet_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) +{ + switch (cmd) { + case SC_CARDCTL_GET_SERIALNR: + if (card->serialnr.len) { + sc_serial_number_t *serial = (sc_serial_number_t *)ptr; + memcpy(serial->value, card->serialnr.value, card->serialnr.len); + serial->len = card->serialnr.len; + return SC_SUCCESS; + } + break; + } + return SC_ERROR_NOT_SUPPORTED; +} + +static int +nqapplet_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) +{ + int r; + + LOG_FUNC_CALLED(card->ctx); + r = iso_operations->pin_cmd(card, data); + if (r == SC_ERROR_OBJECT_NOT_FOUND) { + /* it is possible that the NQ-Applet is not active, try to activate it */ + r = select_nqapplet(card, NULL, NULL, NULL, 0, NULL); + if (r == SC_SUCCESS) { + r = iso_operations->pin_cmd(card, data); + } + } + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); +} + +struct sc_card_driver *sc_get_nqApplet_driver(void) +{ + sc_card_driver_t *iso_driver = sc_get_iso7816_driver(); + + if (iso_operations == NULL) { + iso_operations = iso_driver->ops; + } + + nqapplet_operations = *iso_driver->ops; + + /* supported operations */ + nqapplet_operations.match_card = nqapplet_match_card; + nqapplet_operations.init = nqapplet_init; + nqapplet_operations.finish = nqapplet_finish; + nqapplet_operations.get_response = nqapplet_get_response; + nqapplet_operations.get_challenge = nqapplet_get_challenge; + nqapplet_operations.logout = nqapplet_logout; + nqapplet_operations.set_security_env = nqapplet_set_security_env; + nqapplet_operations.decipher = nqapplet_decipher; + nqapplet_operations.compute_signature = nqapplet_compute_signature; + nqapplet_operations.check_sw = nqapplet_check_sw; + nqapplet_operations.get_data = nqapplet_get_data; + nqapplet_operations.select_file = nqapplet_select_file; + nqapplet_operations.card_ctl = nqapplet_card_ctl; + nqapplet_operations.pin_cmd = nqapplet_pin_cmd; + + /* unsupported operations */ + nqapplet_operations.read_binary = NULL; + nqapplet_operations.write_binary = NULL; + nqapplet_operations.update_binary = NULL; + nqapplet_operations.erase_binary = NULL; + nqapplet_operations.read_record = NULL; + nqapplet_operations.write_record = NULL; + nqapplet_operations.append_record = NULL; + nqapplet_operations.update_record = NULL; + + nqapplet_operations.verify = NULL; + nqapplet_operations.restore_security_env = NULL; + nqapplet_operations.change_reference_data = NULL; + nqapplet_operations.reset_retry_counter = NULL; + nqapplet_operations.create_file = NULL; + nqapplet_operations.delete_file = NULL; + nqapplet_operations.list_files = NULL; + nqapplet_operations.process_fci = NULL; + nqapplet_operations.construct_fci = NULL; + nqapplet_operations.put_data = NULL; + nqapplet_operations.delete_record = NULL; + nqapplet_operations.read_public_key = NULL; + + /* let iso driver handle these operations + nqapplet_operations.card_reader_lock_obtained; + nqapplet_operations.wrap; + nqapplet_operations.unwrap; + */ + + return &nqapplet_driver; +} diff --git a/src/libopensc/card-oberthur.c b/src/libopensc/card-oberthur.c index 9842055a08..c3b6f0b60e 100644 --- a/src/libopensc/card-oberthur.c +++ b/src/libopensc/card-oberthur.c @@ -18,12 +18,12 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * best view with tabstop=4 */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -31,14 +31,12 @@ #include #include #include -#include #include -#include -#include #include "internal.h" #include "cardctl.h" #include "pkcs15.h" +#include "gp.h" #define OBERTHUR_PIN_LOCAL 0x80 #define OBERTHUR_PIN_REFERENCE_USER 0x81 @@ -46,15 +44,7 @@ #define OBERTHUR_PIN_REFERENCE_SO 0x04 #define OBERTHUR_PIN_REFERENCE_PUK 0x84 -/* keep OpenSSL 0.9.6 users happy ;-) */ -#if OPENSSL_VERSION_NUMBER < 0x00907000L -#define DES_cblock des_cblock -#define DES_key_schedule des_key_schedule -#define DES_set_key_unchecked(a,b) des_set_key_unchecked(a,*b) -#define DES_ecb_encrypt(a,b,c,d) des_ecb_encrypt(a,b,*c,d) -#endif - -static struct sc_atr_table oberthur_atrs[] = { +static const struct sc_atr_table oberthur_atrs[] = { { "3B:7D:18:00:00:00:31:80:71:8E:64:77:E3:01:00:82:90:00", NULL, "Oberthur 64k v4/2.1.1", SC_CARD_TYPE_OBERTHUR_64K, 0, NULL }, { "3B:7D:18:00:00:00:31:80:71:8E:64:77:E3:02:00:82:90:00", NULL, @@ -71,7 +61,7 @@ static struct sc_atr_table oberthur_atrs[] = { }; struct auth_senv { - unsigned int algorithm; + unsigned long algorithm; int key_file_id; size_t key_size; }; @@ -90,7 +80,7 @@ struct auth_update_component_info { enum SC_CARDCTL_OBERTHUR_KEY_TYPE type; unsigned int component; unsigned char *data; - unsigned int len; + size_t len; }; @@ -112,7 +102,7 @@ static const char *nameAidAuthentIC_V5 = "AuthentIC v5"; #define PUBKEY_2048_ASN1_SIZE 0x10E static unsigned char rsa_der[PUBKEY_2048_ASN1_SIZE]; -static int rsa_der_len = 0; +static size_t rsa_der_len = 0; static struct sc_file *auth_current_ef = NULL, *auth_current_df = NULL; static struct sc_card_operations auth_ops; @@ -132,9 +122,9 @@ static int auth_read_component(struct sc_card *card, static int auth_pin_is_verified(struct sc_card *card, int pin_reference, int *tries_left); static int auth_pin_verify(struct sc_card *card, unsigned int type, - struct sc_pin_cmd_data *data, int *tries_left); + struct sc_pin_cmd_data *data); static int auth_pin_reset(struct sc_card *card, unsigned int type, - struct sc_pin_cmd_data *data, int *tries_left); + struct sc_pin_cmd_data *data); static int auth_create_reference_data (struct sc_card *card, struct sc_cardctl_oberthur_createpin_info *args); static int auth_get_serialnr(struct sc_card *card, struct sc_serial_number *serial); @@ -155,21 +145,12 @@ auth_select_aid(struct sc_card *card) { struct sc_apdu apdu; unsigned char apdu_resp[SC_MAX_APDU_BUFFER_SIZE]; - struct auth_private_data *data = (struct auth_private_data *) card->drv_data; + struct auth_private_data *data = (struct auth_private_data *)card->drv_data; int rv, ii; - unsigned char cm[7] = {0xA0,0x00,0x00,0x00,0x03,0x00,0x00}; struct sc_path tmp_path; /* Select Card Manager (to deselect previously selected application) */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x0C); - apdu.lc = sizeof(cm); - /* apdu.le = sizeof(cm)+4; */ - apdu.data = cm; - apdu.datalen = sizeof(cm); - apdu.resplen = sizeof(apdu_resp); - apdu.resp = apdu_resp; - - rv = sc_transmit_apdu(card, &apdu); + rv = gp_select_card_manager(card); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); /* Get smart card serial number */ @@ -181,6 +162,9 @@ auth_select_aid(struct sc_card *card) rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); + if (apdu.resplen < 20) { + LOG_TEST_RET(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Serial number has incorrect length"); + } card->serialnr.len = 4; memcpy(card->serialnr.value, apdu.resp+15, 4); @@ -198,10 +182,13 @@ auth_select_aid(struct sc_card *card) LOG_TEST_RET(card->ctx, rv, "select parent failed"); sc_format_path("3F00", &tmp_path); + sc_file_free(auth_current_df); + auth_current_df = NULL; rv = iso_ops->select_file(card, &tmp_path, &auth_current_df); LOG_TEST_RET(card->ctx, rv, "select parent failed"); - sc_format_path("3F00", &card->cache.current_path); + sc_file_free(auth_current_ef); + auth_current_ef = NULL; sc_file_dup(&auth_current_ef, auth_current_df); memcpy(data->aid, aidAuthentIC_V5, lenAidAuthentIC_V5); @@ -240,22 +227,29 @@ auth_init(struct sc_card *card) card->caps |= SC_CARD_CAP_RNG; card->caps |= SC_CARD_CAP_USE_FCI_AC; - if (auth_select_aid(card)) { + if (auth_select_aid(card)) { sc_log(card->ctx, "Failed to initialize %s", card->name); - LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_CARD, "Failed to initialize"); + rv = SC_ERROR_INVALID_CARD; + LOG_TEST_GOTO_ERR(card->ctx, SC_ERROR_INVALID_CARD, "Failed to initialize"); } - flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_PAD_ISO9796; - flags |= SC_ALGORITHM_RSA_HASH_NONE; - flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; - - _sc_card_add_rsa_alg(card, 512, flags, 0); - _sc_card_add_rsa_alg(card, 1024, flags, 0); - _sc_card_add_rsa_alg(card, 2048, flags, 0); - sc_format_path("3F00", &path); rv = auth_select_file(card, &path, NULL); +err: + if (rv == SC_SUCCESS) { + flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_PAD_ISO9796; + flags |= SC_ALGORITHM_RSA_HASH_NONE; + flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; + + _sc_card_add_rsa_alg(card, 512, flags, 0); + _sc_card_add_rsa_alg(card, 1024, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); + } else { + free(card->drv_data); + card->drv_data = NULL; + } + LOG_FUNC_RETURN(card->ctx, rv); } @@ -264,7 +258,7 @@ static void add_acl_entry(struct sc_card *card, struct sc_file *file, unsigned int op, unsigned char acl_byte) { - if ((acl_byte & 0xE0) == 0x60) { + if ((acl_byte & 0xE0) == 0x60) { sc_log(card->ctx, "called; op 0x%X; SC_AC_PRO; ref 0x%X", op, acl_byte); sc_file_add_acl_entry(file, op, SC_AC_PRO, acl_byte); return; @@ -297,82 +291,57 @@ add_acl_entry(struct sc_card *card, struct sc_file *file, unsigned int op, } -static int -tlv_get(const unsigned char *msg, int len, unsigned char tag, - unsigned char *ret, int *ret_len) -{ - int cur = 0; - - while (cur < len) { - if (*(msg+cur)==tag) { - int ii, ln = *(msg+cur+1); - - if (ln > *ret_len) - return SC_ERROR_WRONG_LENGTH; - - for (ii=0; iictx); - attr_len = sizeof(attr); - if (tlv_get(buf, buflen, 0x82, attr, &attr_len)) + attr = sc_asn1_find_tag(card->ctx, buf, buflen, 0x82, &attr_len); + if (!attr || attr_len < 1) LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); type = attr[0]; - attr_len = sizeof(attr); - if (tlv_get(buf, buflen, 0x83, attr, &attr_len)) + attr = sc_asn1_find_tag(card->ctx, buf, buflen, 0x83, &attr_len); + if (!attr || attr_len < 2) LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); file->id = attr[0]*0x100 + attr[1]; - attr_len = sizeof(attr); - if (tlv_get(buf, buflen, type==0x01 ? 0x80 : 0x85, attr, &attr_len)) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); - if (attr_len<2 && type != 0x04) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); - + attr = sc_asn1_find_tag(card->ctx, buf, buflen, type==0x01 ? 0x80 : 0x85, &attr_len); switch (type) { case 0x01: + if (!attr || attr_len < 2) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->size = attr[0]*0x100 + attr[1]; break; case 0x04: + if (!attr || attr_len < 1) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_LINEAR_VARIABLE; file->size = attr[0]; - attr_len = sizeof(attr); - if (tlv_get(buf, buflen, 0x82, attr, &attr_len)) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); - if (attr_len!=5) + attr = sc_asn1_find_tag(card->ctx, buf, buflen, 0x82, &attr_len); + if (!attr || attr_len < 5) LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); file->record_length = attr[2]*0x100+attr[3]; file->record_count = attr[4]; break; case 0x11: + if (!attr || attr_len < 2) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); file->type = SC_FILE_TYPE_INTERNAL_EF; file->ef_structure = SC_CARDCTL_OBERTHUR_KEY_DES; file->size = attr[0]*0x100 + attr[1]; file->size /= 8; break; case 0x12: + if (!attr || attr_len < 2) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); file->type = SC_FILE_TYPE_INTERNAL_EF; file->ef_structure = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; @@ -383,7 +352,7 @@ auth_process_fci(struct sc_card *card, struct sc_file *file, file->size = PUBKEY_1024_ASN1_SIZE; else if (file->size==2048) file->size = PUBKEY_2048_ASN1_SIZE; - else { + else { sc_log(card->ctx, "Not supported public key size: %"SC_FORMAT_LEN_SIZE_T"u", file->size); @@ -391,11 +360,15 @@ auth_process_fci(struct sc_card *card, struct sc_file *file, } break; case 0x14: + if (!attr || attr_len < 2) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); file->type = SC_FILE_TYPE_INTERNAL_EF; file->ef_structure = SC_CARDCTL_OBERTHUR_KEY_RSA_CRT; file->size = attr[0]*0x100 + attr[1]; break; case 0x38: + if (!attr || attr_len < 1) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); file->type = SC_FILE_TYPE_DF; file->size = attr[0]; if (SC_SUCCESS != sc_file_set_type_attr(file,attr,attr_len)) @@ -405,10 +378,8 @@ auth_process_fci(struct sc_card *card, struct sc_file *file, LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } - attr_len = sizeof(attr); - if (tlv_get(buf, buflen, 0x86, attr, &attr_len)) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); - if (attr_len<8) + attr = sc_asn1_find_tag(card->ctx, buf, buflen, 0x86, &attr_len); + if (!attr || attr_len < 8) LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); if (file->type == SC_FILE_TYPE_DF) { @@ -420,8 +391,7 @@ auth_process_fci(struct sc_card *card, struct sc_file *file, add_acl_entry(card, file, SC_AC_OP_PIN_CHANGE, attr[5]); add_acl_entry(card, file, SC_AC_OP_PIN_RESET, attr[6]); sc_log(card->ctx, "SC_FILE_TYPE_DF:CRYPTO %X", attr[1]); - } - else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { /* EF */ + } else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { /* EF */ switch (file->ef_structure) { case SC_CARDCTL_OBERTHUR_KEY_DES: add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[0]); @@ -445,8 +415,7 @@ auth_process_fci(struct sc_card *card, struct sc_file *file, add_acl_entry(card, file, SC_AC_OP_INTERNAL_AUTHENTICATE, attr[5]); break; } - } - else { + } else { switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: add_acl_entry(card, file, SC_AC_OP_WRITE, attr[0]); @@ -479,11 +448,16 @@ auth_select_file(struct sc_card *card, const struct sc_path *in_path, size_t offs, ii; int rv; + if (card == NULL || in_path == NULL) + return SC_ERROR_INTERNAL; + LOG_FUNC_CALLED(card->ctx); - assert(card != NULL && in_path != NULL); memcpy(&path, in_path, sizeof(struct sc_path)); + if (!auth_current_df) + return SC_ERROR_OBJECT_NOT_FOUND; + sc_log(card->ctx, "in_path; type=%d, path=%s, out %p", in_path->type, sc_print_path(in_path), file_out); sc_log(card->ctx, "current path; type=%d, path=%s", @@ -492,7 +466,7 @@ auth_select_file(struct sc_card *card, const struct sc_path *in_path, sc_log(card->ctx, "current file; type=%d, path=%s", auth_current_ef->path.type, sc_print_path(&auth_current_ef->path)); - if (path.type == SC_PATH_TYPE_PARENT || path.type == SC_PATH_TYPE_FILE_ID) { + if (path.type == SC_PATH_TYPE_PARENT || path.type == SC_PATH_TYPE_FILE_ID) { sc_file_free(auth_current_ef); auth_current_ef = NULL; @@ -501,85 +475,98 @@ auth_select_file(struct sc_card *card, const struct sc_path *in_path, if (!tmp_file) return SC_ERROR_OBJECT_NOT_FOUND; - if (path.type == SC_PATH_TYPE_PARENT) { + if (path.type == SC_PATH_TYPE_PARENT) { memcpy(&tmp_file->path, &auth_current_df->path, sizeof(struct sc_path)); if (tmp_file->path.len > 2) tmp_file->path.len -= 2; sc_file_free(auth_current_df); + auth_current_df = NULL; sc_file_dup(&auth_current_df, tmp_file); - } - else { - if (tmp_file->type == SC_FILE_TYPE_DF) { + } else { + if (tmp_file->type == SC_FILE_TYPE_DF) { sc_concatenate_path(&tmp_file->path, &auth_current_df->path, &path); sc_file_free(auth_current_df); + auth_current_df = NULL; sc_file_dup(&auth_current_df, tmp_file); - } - else { + } else { sc_file_free(auth_current_ef); + auth_current_ef = NULL; sc_file_dup(&auth_current_ef, tmp_file); sc_concatenate_path(&auth_current_ef->path, &auth_current_df->path, &path); } } - if (file_out) + if (file_out) { + sc_file_free(*file_out); sc_file_dup(file_out, tmp_file); + } sc_file_free(tmp_file); - } - else if (path.type == SC_PATH_TYPE_DF_NAME) { + } else if (path.type == SC_PATH_TYPE_DF_NAME) { rv = iso_ops->select_file(card, &path, NULL); - if (rv) { + if (rv) { sc_file_free(auth_current_ef); auth_current_ef = NULL; } LOG_TEST_RET(card->ctx, rv, "select file failed"); - } - else { + } else { for (offs = 0; offs < path.len && offs < auth_current_df->path.len; offs += 2) if (path.value[offs] != auth_current_df->path.value[offs] || path.value[offs + 1] != auth_current_df->path.value[offs + 1]) break; sc_log(card->ctx, "offs %"SC_FORMAT_LEN_SIZE_T"u", offs); - if (offs && offs < auth_current_df->path.len) { + if (offs && offs < auth_current_df->path.len) { size_t deep = auth_current_df->path.len - offs; sc_log(card->ctx, "deep %"SC_FORMAT_LEN_SIZE_T"u", deep); - for (ii=0; iipath, sizeof(struct sc_path)); tmp_path.type = SC_PATH_TYPE_PARENT; + if (file_out) { + sc_file_free(*file_out); + *file_out = NULL; + } + rv = auth_select_file (card, &tmp_path, file_out); LOG_TEST_RET(card->ctx, rv, "select file failed"); } } - if (path.len - offs > 0) { + if (path.len > offs) { struct sc_path tmp_path; memset(&tmp_path, 0, sizeof(struct sc_path)); tmp_path.type = SC_PATH_TYPE_FILE_ID; tmp_path.len = 2; - for (ii=0; ii < path.len - offs; ii+=2) { + for (ii = 0; ii < path.len - offs; ii += 2) { memcpy(tmp_path.value, path.value + offs + ii, 2); + if (file_out) { + sc_file_free(*file_out); + *file_out = NULL; + } + rv = auth_select_file(card, &tmp_path, file_out); LOG_TEST_RET(card->ctx, rv, "select file failed"); } - } - else if (path.len - offs == 0 && file_out) { - if (sc_compare_path(&path, &auth_current_df->path)) + } else if (path.len - offs == 0 && file_out) { + if (sc_compare_path(&path, &auth_current_df->path) && file_out) { + sc_file_free(*file_out); sc_file_dup(file_out, auth_current_df); - else if (auth_current_ef) + } else if (auth_current_ef && file_out) { + sc_file_free(*file_out); sc_file_dup(file_out, auth_current_ef); - else + } else { LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, "No current EF"); + } } } @@ -610,10 +597,10 @@ auth_list_files(struct sc_card *card, unsigned char *buf, size_t buflen) if (apdu.resplen == 0x100 && rbuf[0]==0 && rbuf[1]==0) LOG_FUNC_RETURN(card->ctx, 0); - buflen = buflen < apdu.resplen ? buflen : apdu.resplen; + buflen = MIN(buflen, apdu.resplen); memcpy(buf, rbuf, buflen); - LOG_FUNC_RETURN(card->ctx, buflen); + LOG_FUNC_RETURN(card->ctx, (int)buflen); } @@ -633,12 +620,12 @@ auth_delete_file(struct sc_card *card, const struct sc_path *path) sc_log(card->ctx, "path; type=%d, path=%s", path->type, pbuf); - if (path->len < 2) { + if (path->len < 2) { sc_log(card->ctx, "Invalid path length"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } - if (path->len > 2) { + if (path->len > 2) { struct sc_path parent = *path; parent.len -= 2; @@ -661,7 +648,7 @@ auth_delete_file(struct sc_card *card, const struct sc_path *path) rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); - if (apdu.sw1==0x6A && apdu.sw2==0x82) { + if (apdu.sw1 == 0x6A && apdu.sw2 == 0x82) { /* Clean up tDF contents.*/ struct sc_path tmp_path; int ii, len; @@ -677,7 +664,7 @@ auth_delete_file(struct sc_card *card, const struct sc_path *path) len = auth_list_files(card, lbuf, sizeof(lbuf)); LOG_TEST_RET(card->ctx, len, "list DF failed"); - for (ii=0; iitype == SC_FILE_TYPE_DF) { + if (file->type == SC_FILE_TYPE_DF) { p[4] = 0x38; p[5] = 0x00; - } - else if (file->type == SC_FILE_TYPE_WORKING_EF) { + } else if (file->type == SC_FILE_TYPE_WORKING_EF) { switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: p[4] = 0x01; @@ -778,8 +765,7 @@ encode_file_structure_V5(struct sc_card *card, const struct sc_file *file, rv = SC_ERROR_INVALID_ARGUMENTS; break; } - } - else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { + } else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { switch (file->ef_structure) { case SC_CARDCTL_OBERTHUR_KEY_DES: p[4] = 0x11; @@ -797,11 +783,10 @@ encode_file_structure_V5(struct sc_card *card, const struct sc_file *file, rv = -1; break; } - } - else + } else rv = SC_ERROR_INVALID_ARGUMENTS; - if (rv) { + if (rv) { sc_log(card->ctx, "Invalid EF structure 0x%X/0x%X", file->type, file->ef_structure); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); } @@ -816,11 +801,10 @@ encode_file_structure_V5(struct sc_card *card, const struct sc_file *file, size = file->size; - if (file->type == SC_FILE_TYPE_DF) { + if (file->type == SC_FILE_TYPE_DF) { size &= 0xFF; - } - else if (file->type == SC_FILE_TYPE_INTERNAL_EF && - file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + } else if (file->type == SC_FILE_TYPE_INTERNAL_EF && + file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { sc_log(card->ctx, "ef %s","SC_FILE_EF_RSA_PUBLIC"); if (file->size == PUBKEY_512_ASN1_SIZE || file->size == 512) size = 512; @@ -828,22 +812,21 @@ encode_file_structure_V5(struct sc_card *card, const struct sc_file *file, size = 1024; else if (file->size == PUBKEY_2048_ASN1_SIZE || file->size == 2048) size = 2048; - else { + else { sc_log(card->ctx, "incorrect RSA size %"SC_FORMAT_LEN_SIZE_T"X", file->size); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); } - } - else if (file->type == SC_FILE_TYPE_INTERNAL_EF && - file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_DES) { + } else if (file->type == SC_FILE_TYPE_INTERNAL_EF && + file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_DES) { if (file->size == 8 || file->size == 64) size = 64; else if (file->size == 16 || file->size == 128) size = 128; else if (file->size == 24 || file->size == 192) size = 192; - else { + else { sc_log(card->ctx, "incorrect DES size %"SC_FORMAT_LEN_SIZE_T"u", file->size); @@ -865,25 +848,22 @@ encode_file_structure_V5(struct sc_card *card, const struct sc_file *file, ops[4] = SC_AC_OP_PIN_DEFINE; ops[5] = SC_AC_OP_PIN_CHANGE; ops[6] = SC_AC_OP_PIN_RESET; - } - else if (file->type == SC_FILE_TYPE_WORKING_EF) { - if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { + } else if (file->type == SC_FILE_TYPE_WORKING_EF) { + if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { sc_log(card->ctx, "SC_FILE_EF_TRANSPARENT"); ops[0] = SC_AC_OP_WRITE; ops[1] = SC_AC_OP_UPDATE; ops[2] = SC_AC_OP_READ; ops[3] = SC_AC_OP_ERASE; - } - else if (file->ef_structure == SC_FILE_EF_LINEAR_VARIABLE) { + } else if (file->ef_structure == SC_FILE_EF_LINEAR_VARIABLE) { sc_log(card->ctx, "SC_FILE_EF_LINEAR_VARIABLE"); ops[0] = SC_AC_OP_WRITE; ops[1] = SC_AC_OP_UPDATE; ops[2] = SC_AC_OP_READ; ops[3] = SC_AC_OP_ERASE; } - } - else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { - if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_DES) { + } else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { + if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_DES) { sc_log(card->ctx, "EF_DES"); ops[0] = SC_AC_OP_UPDATE; ops[1] = SC_AC_OP_PSO_DECRYPT; @@ -892,15 +872,13 @@ encode_file_structure_V5(struct sc_card *card, const struct sc_file *file, ops[4] = SC_AC_OP_PSO_VERIFY_CHECKSUM; ops[5] = SC_AC_OP_INTERNAL_AUTHENTICATE; ops[6] = SC_AC_OP_EXTERNAL_AUTHENTICATE; - } - else if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + } else if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { sc_log(card->ctx, "EF_RSA_PUBLIC"); ops[0] = SC_AC_OP_UPDATE; ops[2] = SC_AC_OP_PSO_ENCRYPT; ops[4] = SC_AC_OP_PSO_VERIFY_SIGNATURE; ops[6] = SC_AC_OP_EXTERNAL_AUTHENTICATE; - } - else if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_CRT) { + } else if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_CRT) { sc_log(card->ctx, "EF_RSA_PRIVATE"); ops[0] = SC_AC_OP_UPDATE; ops[1] = SC_AC_OP_PSO_DECRYPT; @@ -951,16 +929,12 @@ auth_create_file(struct sc_card *card, struct sc_file *file) if (file->id==0x0000 || file->id==0xFFFF || file->id==0x3FFF) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); - rv = sc_path_print(pbuf, sizeof(pbuf), &card->cache.current_path); - if (rv != SC_SUCCESS) - pbuf[0] = '\0'; - - if (file->path.len) { + if (file->path.len) { memcpy(&path, &file->path, sizeof(path)); if (path.len>2) path.len -= 2; - if (auth_select_file(card, &path, NULL)) { + if (auth_select_file(card, &path, NULL)) { sc_log(card->ctx, "Cannot select parent DF."); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } @@ -970,7 +944,7 @@ auth_create_file(struct sc_card *card, struct sc_file *file) LOG_TEST_RET(card->ctx, rv, "File structure encoding failed"); if (file->type != SC_FILE_TYPE_DF && file->ef_structure != SC_FILE_EF_TRANSPARENT) - rec_nr = file->record_count; + rec_nr = (int)file->record_count; else rec_nr = 0; @@ -985,7 +959,7 @@ auth_create_file(struct sc_card *card, struct sc_file *file) LOG_TEST_RET(card->ctx, rv, "Card returned error"); /* select created DF. */ - if (file->type == SC_FILE_TYPE_DF) { + if (file->type == SC_FILE_TYPE_DF) { struct sc_path tmp_path; struct sc_file *df_file = NULL; @@ -996,9 +970,11 @@ auth_create_file(struct sc_card *card, struct sc_file *file) tmp_path.len = 2; rv = auth_select_file(card, &tmp_path, &df_file); sc_log(card->ctx, "rv %i", rv); + sc_file_free(df_file); } sc_file_free(auth_current_ef); + auth_current_ef = NULL; sc_file_dup(&auth_current_ef, file); LOG_FUNC_RETURN(card->ctx, rv); @@ -1024,7 +1000,7 @@ auth_set_security_env(struct sc_card *card, LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, - "op %i; path %s; key_ref 0x%X; algos 0x%X; flags 0x%lX", + "op %i; path %s; key_ref 0x%X; algos 0x%lX; flags 0x%lX", env->operation, sc_print_path(&env->file_ref), env->key_ref[0], env->algorithm_flags, env->flags); @@ -1033,20 +1009,19 @@ auth_set_security_env(struct sc_card *card, if (!(env->flags & SC_SEC_ENV_FILE_REF_PRESENT)) LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, "Key file is not selected."); - switch (env->algorithm) { + switch (env->algorithm) { case SC_ALGORITHM_DES: case SC_ALGORITHM_3DES: sc_log(card->ctx, - "algo SC_ALGORITHM_xDES: ref %X, flags %lX", + "algo SC_ALGORITHM_xDES: ref %lX, flags %lX", env->algorithm_ref, env->flags); - if (env->operation == SC_SEC_OPERATION_DECIPHER) { + if (env->operation == SC_SEC_OPERATION_DECIPHER) { sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB8); apdu.lc = 3; apdu.data = des_sbuf; apdu.datalen = 3; - } - else { + } else { sc_log(card->ctx, "Invalid crypto operation: %X", env->operation); LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Invalid crypto operation"); } @@ -1058,28 +1033,26 @@ auth_set_security_env(struct sc_card *card, LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "No support for hashes."); } - if (pads & (~supported_pads)) { + if (pads & (~supported_pads)) { sc_log(card->ctx, "No support for PAD %lX", pads); LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "No padding support."); } - if (env->operation == SC_SEC_OPERATION_SIGN) { + if (env->operation == SC_SEC_OPERATION_SIGN) { rsa_sbuf[2] = 0x11; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB6); apdu.lc = sizeof(rsa_sbuf); apdu.datalen = sizeof(rsa_sbuf); apdu.data = rsa_sbuf; - } - else if (env->operation == SC_SEC_OPERATION_DECIPHER) { + } else if (env->operation == SC_SEC_OPERATION_DECIPHER) { rsa_sbuf[2] = 0x11; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB8); apdu.lc = sizeof(rsa_sbuf); apdu.datalen = sizeof(rsa_sbuf); apdu.data = rsa_sbuf; - } - else { + } else { sc_log(card->ctx, "Invalid crypto operation: %X", env->operation); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } @@ -1115,10 +1088,9 @@ auth_compute_signature(struct sc_card *card, const unsigned char *in, size_t ile unsigned char resp[SC_MAX_APDU_BUFFER_SIZE]; int rv; - if (!card || !in || !out) { + if (!card || !in || !out) { return SC_ERROR_INVALID_ARGUMENTS; - } - else if (ilen > 96) { + } else if (ilen > 96) { sc_log(card->ctx, "Illegal input length %"SC_FORMAT_LEN_SIZE_T"u", ilen); @@ -1134,16 +1106,16 @@ auth_compute_signature(struct sc_card *card, const unsigned char *in, size_t ile apdu.datalen = ilen; apdu.data = in; apdu.lc = ilen; - apdu.le = olen > 256 ? 256 : olen; + apdu.le = MIN(olen, SC_MAX_APDU_RESP_SIZE); apdu.resp = resp; - apdu.resplen = olen; + apdu.resplen = SC_MAX_APDU_BUFFER_SIZE; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, rv, "Compute signature failed"); - if (apdu.resplen > olen) { + if (apdu.resplen > olen) { sc_log(card->ctx, "Compute signature failed: invalid response length %"SC_FORMAT_LEN_SIZE_T"u", apdu.resplen); @@ -1152,7 +1124,7 @@ auth_compute_signature(struct sc_card *card, const unsigned char *in, size_t ile memcpy(out, apdu.resp, apdu.resplen); - LOG_FUNC_RETURN(card->ctx, apdu.resplen); + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); } @@ -1162,7 +1134,8 @@ auth_decipher(struct sc_card *card, const unsigned char *in, size_t inlen, { struct sc_apdu apdu; unsigned char resp[SC_MAX_APDU_BUFFER_SIZE]; - int rv, _inlen = inlen; + int rv; + size_t _inlen = inlen; LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, @@ -1174,20 +1147,20 @@ auth_decipher(struct sc_card *card, const unsigned char *in, size_t inlen, sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); sc_log(card->ctx, "algorithm SC_ALGORITHM_RSA"); - if (inlen % 64) { + if (inlen % 64) { rv = SC_ERROR_INVALID_ARGUMENTS; goto done; } _inlen = inlen; - if (_inlen == 256) { + if (_inlen == SC_MAX_APDU_RESP_SIZE) { apdu.cla |= 0x10; apdu.data = in; apdu.datalen = 8; apdu.resp = resp; apdu.resplen = SC_MAX_APDU_BUFFER_SIZE; apdu.lc = 8; - apdu.le = 256; + apdu.le = SC_MAX_APDU_RESP_SIZE; rv = sc_transmit_apdu(card, &apdu); sc_log(card->ctx, "rv %i", rv); @@ -1219,7 +1192,7 @@ auth_decipher(struct sc_card *card, const unsigned char *in, size_t inlen, outlen = apdu.resplen; memcpy(out, apdu.resp, outlen); - rv = outlen; + rv = (int)outlen; done: LOG_FUNC_RETURN(card->ctx, rv); @@ -1249,7 +1222,7 @@ auth_encode_exponent(unsigned long exponent, unsigned char *buff, size_t buff_le if (ii==buff_len) return 0; else - return ii; + return (int)ii; } @@ -1265,7 +1238,7 @@ auth_generate_key(struct sc_card *card, int use_sm, LOG_FUNC_CALLED(card->ctx); if (data->key_bits < 512 || data->key_bits > 2048 || - (data->key_bits%0x20)!=0) { + (data->key_bits % 0x20) != 0) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Illegal key length"); } @@ -1273,7 +1246,7 @@ auth_generate_key(struct sc_card *card, int use_sm, sbuf[1] = data->id_pub & 0xFF; sbuf[2] = (data->id_prv >> 8) & 0xFF; sbuf[3] = data->id_prv & 0xFF; - if (data->exponent != 0x10001) { + if (data->exponent != 0x10001) { rv = auth_encode_exponent(data->exponent, &sbuf[5],SC_MAX_APDU_BUFFER_SIZE-6); LOG_TEST_RET(card->ctx, rv, "Cannot encode exponent"); @@ -1293,9 +1266,9 @@ auth_generate_key(struct sc_card *card, int use_sm, apdu.datalen = rv + 4; rv = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); + LOG_TEST_GOTO_ERR(card->ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(card->ctx, rv, "Card returned error"); + LOG_TEST_GOTO_ERR(card->ctx, rv, "Card returned error"); memset(&tmp_path, 0, sizeof(struct sc_path)); tmp_path.type = SC_PATH_TYPE_FILE_ID; @@ -1303,17 +1276,19 @@ auth_generate_key(struct sc_card *card, int use_sm, memcpy(tmp_path.value, sbuf, 2); rv = auth_select_file(card, &tmp_path, NULL); - LOG_TEST_RET(card->ctx, rv, "cannot select public key"); + LOG_TEST_GOTO_ERR(card->ctx, rv, "cannot select public key"); rv = auth_read_component(card, SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC, 1, apdu.resp, data->key_bits/8); - LOG_TEST_RET(card->ctx, rv, "auth_read_component() returned error"); + LOG_TEST_GOTO_ERR(card->ctx, rv, "auth_read_component() returned error"); apdu.resplen = rv; - if (data->pubkey) { - if (data->pubkey_len < apdu.resplen) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + if (data->pubkey) { + if (data->pubkey_len < apdu.resplen) { + rv = SC_ERROR_INVALID_ARGUMENTS; + LOG_TEST_GOTO_ERR(card->ctx, rv, "invalid length received"); + } memcpy(data->pubkey,apdu.resp,apdu.resplen); } @@ -1324,6 +1299,9 @@ auth_generate_key(struct sc_card *card, int use_sm, sc_log(card->ctx, "resulted public key len %"SC_FORMAT_LEN_SIZE_T"u", apdu.resplen); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +err: + free(apdu.resp); + return rv; } @@ -1339,7 +1317,7 @@ auth_update_component(struct sc_card *card, struct auth_update_component_info *a if (args->len > sizeof(sbuf) || args->len > 0x100) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(card->ctx, "nn %i; len %i", args->component, args->len); + sc_log(card->ctx, "nn %i; len %zu", args->component, args->len); ins = 0xD8; p1 = args->component; p2 = 0x04; @@ -1350,27 +1328,37 @@ auth_update_component(struct sc_card *card, struct auth_update_component_info *a memcpy(sbuf + len, args->data, args->len); len += args->len; - if (args->type == SC_CARDCTL_OBERTHUR_KEY_DES) { + if (args->type == SC_CARDCTL_OBERTHUR_KEY_DES) { int outl; const unsigned char in[8] = {0,0,0,0,0,0,0,0}; unsigned char out[8]; EVP_CIPHER_CTX * ctx = NULL; + EVP_CIPHER *alg = NULL; if (args->len!=8 && args->len!=24) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL,SC_ERROR_OUT_OF_MEMORY); + if (ctx == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); p2 = 0; if (args->len == 24) - EVP_EncryptInit_ex(ctx, EVP_des_ede(), NULL, args->data, NULL); + alg = sc_evp_cipher(card->ctx, "DES-EDE"); else - EVP_EncryptInit_ex(ctx, EVP_des_ecb(), NULL, args->data, NULL); + alg = sc_evp_cipher(card->ctx, "DES-ECB"); + rv = EVP_EncryptInit_ex(ctx, alg, NULL, args->data, NULL); + if (rv == 0) { + sc_evp_cipher_free(alg); + sc_log_openssl(card->ctx); + sc_log(card->ctx, "OpenSSL encryption error."); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } rv = EVP_EncryptUpdate(ctx, out, &outl, in, 8); EVP_CIPHER_CTX_free(ctx); + sc_evp_cipher_free(alg); if (rv == 0) { + sc_log_openssl(card->ctx); sc_log(card->ctx, "OpenSSL encryption error."); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } @@ -1378,8 +1366,7 @@ auth_update_component(struct sc_card *card, struct auth_update_component_info *a sbuf[len++] = 0x03; memcpy(sbuf + len, out, 3); len += 3; - } - else { + } else { sbuf[len++] = 0; } @@ -1388,7 +1375,7 @@ auth_update_component(struct sc_card *card, struct auth_update_component_info *a apdu.data = sbuf; apdu.datalen = len; apdu.lc = len; - if (args->len == 0x100) { + if (args->len == 0x100) { sbuf[0] = args->type; sbuf[1] = 0x20; memcpy(sbuf + 2, args->data, 0x20); @@ -1429,7 +1416,7 @@ auth_update_key(struct sc_card *card, struct sc_cardctl_oberthur_updatekey_info if (info->data_len != sizeof(void *) || !info->data) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); - if (info->type == SC_CARDCTL_OBERTHUR_KEY_RSA_CRT) { + if (info->type == SC_CARDCTL_OBERTHUR_KEY_RSA_CRT) { struct sc_pkcs15_prkey_rsa *rsa = (struct sc_pkcs15_prkey_rsa *)info->data; struct sc_pkcs15_bignum bn[5]; @@ -1439,7 +1426,7 @@ auth_update_key(struct sc_card *card, struct sc_cardctl_oberthur_updatekey_info bn[2] = rsa->iqmp; bn[3] = rsa->dmp1; bn[4] = rsa->dmq1; - for (ii=0;ii<5;ii++) { + for (ii = 0; ii < 5; ii++) { struct auth_update_component_info args; memset(&args, 0, sizeof(args)); @@ -1451,11 +1438,9 @@ auth_update_key(struct sc_card *card, struct sc_cardctl_oberthur_updatekey_info rv = auth_update_component(card, &args); LOG_TEST_RET(card->ctx, rv, "Update RSA component failed"); } - } - else if (info->type == SC_CARDCTL_OBERTHUR_KEY_DES) { + } else if (info->type == SC_CARDCTL_OBERTHUR_KEY_DES) { rv = SC_ERROR_NOT_SUPPORTED; - } - else { + } else { rv = SC_ERROR_INVALID_DATA; } @@ -1496,7 +1481,7 @@ auth_read_component(struct sc_card *card, enum SC_CARDCTL_OBERTHUR_KEY_TYPE type { struct sc_apdu apdu; int rv; - unsigned char resp[256]; + unsigned char resp[SC_MAX_APDU_RESP_SIZE]; LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, "num %i, outlen %"SC_FORMAT_LEN_SIZE_T"u, type %i", @@ -1520,7 +1505,7 @@ auth_read_component(struct sc_card *card, enum SC_CARDCTL_OBERTHUR_KEY_TYPE type LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH); memcpy(out, apdu.resp, apdu.resplen); - LOG_FUNC_RETURN(card->ctx, apdu.resplen); + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); } @@ -1557,11 +1542,10 @@ auth_init_pin_info(struct sc_card *card, struct sc_pin_cmd_pin *pin, pin->pad_char = 0xFF; pin->encoding = SC_PIN_ENCODING_ASCII; - if (type == OBERTHUR_AUTH_TYPE_PIN) { + if (type == OBERTHUR_AUTH_TYPE_PIN) { pin->max_length = OBERTHUR_AUTH_MAX_LENGTH_PIN; pin->pad_length = OBERTHUR_AUTH_MAX_LENGTH_PIN; - } - else { + } else { pin->max_length = OBERTHUR_AUTH_MAX_LENGTH_PUK; pin->pad_length = OBERTHUR_AUTH_MAX_LENGTH_PUK; } @@ -1582,8 +1566,8 @@ auth_pin_verify_pinpad(struct sc_card *card, int pin_reference, int *tries_left) memset(ffs1, 0xFF, sizeof(ffs1)); memset(&pin_cmd, 0, sizeof(pin_cmd)); - rv = auth_pin_is_verified(card, pin_reference, tries_left); - sc_log(card->ctx, "auth_pin_is_verified returned rv %i", rv); + rv = auth_pin_is_verified(card, pin_reference, tries_left); + sc_log(card->ctx, "auth_pin_is_verified returned rv %i", rv); /* Return SUCCESS without verifying if * PIN has been already verified and PIN pad has to be used. */ @@ -1593,7 +1577,7 @@ auth_pin_verify_pinpad(struct sc_card *card, int pin_reference, int *tries_left) pin_cmd.flags |= SC_PIN_CMD_NEED_PADDING; /* For Oberthur card, PIN command data length has to be 0x40. - * In PCSC10 v2.06 the uppler limit of pin.max_length is 8. + * In PCSC10 v2.06 the upper limit of pin.max_length is 8. * * The standard sc_build_pin() throws an error when 'pin.len > pin.max_length' . * So, let's build our own APDU. @@ -1617,7 +1601,7 @@ auth_pin_verify_pinpad(struct sc_card *card, int pin_reference, int *tries_left) pin_cmd.pin1.len = OBERTHUR_AUTH_MAX_LENGTH_PIN; pin_cmd.pin1.pad_length = OBERTHUR_AUTH_MAX_LENGTH_PIN; - rv = iso_drv->ops->pin_cmd(card, &pin_cmd, tries_left); + rv = iso_drv->ops->pin_cmd(card, &pin_cmd); LOG_TEST_RET(card->ctx, rv, "PIN CMD 'VERIFY' with pinpad failed"); LOG_FUNC_RETURN(card->ctx, rv); @@ -1626,7 +1610,7 @@ auth_pin_verify_pinpad(struct sc_card *card, int pin_reference, int *tries_left) static int auth_pin_verify(struct sc_card *card, unsigned int type, - struct sc_pin_cmd_data *data, int *tries_left) + struct sc_pin_cmd_data *data) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); int rv; @@ -1645,7 +1629,7 @@ auth_pin_verify(struct sc_card *card, unsigned int type, || data->pin_reference == OBERTHUR_PIN_REFERENCE_ONETIME) data->pin_reference |= OBERTHUR_PIN_LOCAL; - rv = auth_pin_is_verified(card, data->pin_reference, tries_left); + rv = auth_pin_is_verified(card, data->pin_reference, &data->pin1.tries_left); sc_log(card->ctx, "auth_pin_is_verified returned rv %i", rv); /* Return if only PIN status has been asked. */ @@ -1658,9 +1642,9 @@ auth_pin_verify(struct sc_card *card, unsigned int type, LOG_FUNC_RETURN(card->ctx, rv); if (!data->pin1.data && !data->pin1.len) - rv = auth_pin_verify_pinpad(card, data->pin_reference, tries_left); + rv = auth_pin_verify_pinpad(card, data->pin_reference, &data->pin1.tries_left); else - rv = iso_drv->ops->pin_cmd(card, data, tries_left); + rv = iso_drv->ops->pin_cmd(card, data); LOG_FUNC_RETURN(card->ctx, rv); } @@ -1681,7 +1665,7 @@ auth_pin_is_verified(struct sc_card *card, int pin_reference, int *tries_left) *tries_left = apdu.sw2 & 0x0F; /* Replace 'no tries left' with 'auth method blocked' */ - if (apdu.sw1 == 0x63 && apdu.sw2 == 0xC0) { + if (apdu.sw1 == 0x63 && apdu.sw2 == 0xC0) { apdu.sw1 = 0x69; apdu.sw2 = 0x83; } @@ -1693,8 +1677,7 @@ auth_pin_is_verified(struct sc_card *card, int pin_reference, int *tries_left) static int -auth_pin_change_pinpad(struct sc_card *card, struct sc_pin_cmd_data *data, - int *tries_left) +auth_pin_change_pinpad(struct sc_card *card, struct sc_pin_cmd_data *data) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); struct sc_pin_cmd_data pin_cmd; @@ -1742,7 +1725,7 @@ auth_pin_change_pinpad(struct sc_card *card, struct sc_pin_cmd_data *data, pin_cmd.pin1.offset = 5; pin_cmd.pin2.data = ffs2; - rv = iso_drv->ops->pin_cmd(card, &pin_cmd, tries_left); + rv = iso_drv->ops->pin_cmd(card, &pin_cmd); LOG_TEST_RET(card->ctx, rv, "PIN CMD 'VERIFY' with pinpad failed"); LOG_FUNC_RETURN(card->ctx, rv); @@ -1751,14 +1734,14 @@ auth_pin_change_pinpad(struct sc_card *card, struct sc_pin_cmd_data *data, static int auth_pin_change(struct sc_card *card, unsigned int type, - struct sc_pin_cmd_data *data, int *tries_left) + struct sc_pin_cmd_data *data) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); int rv = SC_ERROR_INTERNAL; LOG_FUNC_CALLED(card->ctx); - if (data->pin1.len && data->pin2.len) { + if (data->pin1.len && data->pin2.len) { /* Direct unblock style */ data->flags |= SC_PIN_CMD_NEED_PADDING; data->flags &= ~SC_PIN_CMD_USE_PINPAD; @@ -1769,15 +1752,13 @@ auth_pin_change(struct sc_card *card, unsigned int type, auth_init_pin_info(card, &data->pin1, OBERTHUR_AUTH_TYPE_PIN); auth_init_pin_info(card, &data->pin2, OBERTHUR_AUTH_TYPE_PIN); - rv = iso_drv->ops->pin_cmd(card, data, tries_left); + rv = iso_drv->ops->pin_cmd(card, data); LOG_TEST_RET(card->ctx, rv, "CMD 'PIN CHANGE' failed"); - } - else if (!data->pin1.len && !data->pin2.len) { + } else if (!data->pin1.len && !data->pin2.len) { /* Oberthur unblock style with PIN pad. */ - rv = auth_pin_change_pinpad(card, data, tries_left); - LOG_TEST_RET(card->ctx, rv, "'PIN CHANGE' failedi: SOPIN verify with pinpad failed"); - } - else { + rv = auth_pin_change_pinpad(card, data); + LOG_TEST_RET(card->ctx, rv, "'PIN CHANGE' failed: SOPIN verify with pinpad failed"); + } else { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "'PIN CHANGE' failed"); } @@ -1787,7 +1768,7 @@ auth_pin_change(struct sc_card *card, unsigned int type, static int auth_pin_reset_oberthur_style(struct sc_card *card, unsigned int type, - struct sc_pin_cmd_data *data, int *tries_left) + struct sc_pin_cmd_data *data) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); struct sc_pin_cmd_data pin_cmd; @@ -1813,7 +1794,7 @@ auth_pin_reset_oberthur_style(struct sc_card *card, unsigned int type, pin_cmd.pin_reference = OBERTHUR_PIN_REFERENCE_PUK; memcpy(&pin_cmd.pin1, &data->pin1, sizeof(pin_cmd.pin1)); - rv = auth_pin_verify(card, SC_AC_CHV, &pin_cmd, tries_left); + rv = auth_pin_verify(card, SC_AC_CHV, &pin_cmd); LOG_TEST_RET(card->ctx, rv, "Oberthur style 'PIN RESET' failed: SOPIN verify error"); sc_format_path("2000", &tmp_path); @@ -1821,8 +1802,11 @@ auth_pin_reset_oberthur_style(struct sc_card *card, unsigned int type, rv = iso_ops->select_file(card, &tmp_path, &tmp_file); LOG_TEST_RET(card->ctx, rv, "select PUK file"); - if (!tmp_file || tmp_file->size < OBERTHUR_AUTH_MAX_LENGTH_PUK) + if (!tmp_file || tmp_file->size < OBERTHUR_AUTH_MAX_LENGTH_PUK) { + sc_file_free(tmp_file); LOG_TEST_RET(card->ctx, SC_ERROR_FILE_TOO_SMALL, "Oberthur style 'PIN RESET' failed"); + } + sc_file_free(tmp_file); rv = iso_ops->read_binary(card, 0, puk, OBERTHUR_AUTH_MAX_LENGTH_PUK, 0); LOG_TEST_RET(card->ctx, rv, "read PUK file error"); @@ -1840,9 +1824,9 @@ auth_pin_reset_oberthur_style(struct sc_card *card, unsigned int type, pin_cmd.pin1.data = ffs1; pin_cmd.pin1.len = OBERTHUR_AUTH_MAX_LENGTH_PUK; - if (data->pin2.data) { + if (data->pin2.data) { memcpy(&pin_cmd.pin2, &data->pin2, sizeof(pin_cmd.pin2)); - rv = auth_pin_reset(card, SC_AC_CHV, &pin_cmd, tries_left); + rv = auth_pin_reset(card, SC_AC_CHV, &pin_cmd); LOG_FUNC_RETURN(card->ctx, rv); } @@ -1866,14 +1850,21 @@ auth_pin_reset_oberthur_style(struct sc_card *card, unsigned int type, pin_cmd.pin2.max_length = 8; pin_cmd.pin2.encoding = SC_PIN_ENCODING_ASCII; - rvv = iso_drv->ops->pin_cmd(card, &pin_cmd, tries_left); + rvv = iso_drv->ops->pin_cmd(card, &pin_cmd); if (rvv) sc_log(card->ctx, "%s: PIN CMD 'VERIFY' with pinpad failed", sc_strerror(rvv)); - if (auth_current_ef) - rv = iso_ops->select_file(card, &auth_current_ef->path, &auth_current_ef); + if (auth_current_ef) { + struct sc_file *ef = NULL; + rv = iso_ops->select_file(card, &auth_current_ef->path, &ef); + if (rv == SC_SUCCESS) { + sc_file_free(auth_current_ef); + auth_current_ef = ef; + } else + sc_file_free(ef); + } if (rv > 0) rv = 0; @@ -1884,14 +1875,14 @@ auth_pin_reset_oberthur_style(struct sc_card *card, unsigned int type, static int auth_pin_reset(struct sc_card *card, unsigned int type, - struct sc_pin_cmd_data *data, int *tries_left) + struct sc_pin_cmd_data *data) { int rv; LOG_FUNC_CALLED(card->ctx); /* Oberthur unblock style: PUK value is a SOPIN */ - rv = auth_pin_reset_oberthur_style(card, SC_AC_CHV, data, tries_left); + rv = auth_pin_reset_oberthur_style(card, SC_AC_CHV, data); LOG_TEST_RET(card->ctx, rv, "Oberthur style 'PIN RESET' failed"); LOG_FUNC_RETURN(card->ctx, rv); @@ -1899,7 +1890,7 @@ auth_pin_reset(struct sc_card *card, unsigned int type, static int -auth_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +auth_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data) { int rv = SC_ERROR_INTERNAL; @@ -1907,20 +1898,20 @@ auth_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left if (data->pin_type != SC_AC_CHV) LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "auth_pin_cmd() unsupported PIN type"); - sc_log(card->ctx, "PIN CMD:%i; reference:%i; pin1:%p/%i, pin2:%p/%i", data->cmd, + sc_log(card->ctx, "PIN CMD:%i; reference:%i; pin1:%p/%zu, pin2:%p/%zu", data->cmd, data->pin_reference, data->pin1.data, data->pin1.len, data->pin2.data, data->pin2.len); switch (data->cmd) { case SC_PIN_CMD_VERIFY: - rv = auth_pin_verify(card, SC_AC_CHV, data, tries_left); + rv = auth_pin_verify(card, SC_AC_CHV, data); LOG_TEST_RET(card->ctx, rv, "CMD 'PIN VERIFY' failed"); break; case SC_PIN_CMD_CHANGE: - rv = auth_pin_change(card, SC_AC_CHV, data, tries_left); + rv = auth_pin_change(card, SC_AC_CHV, data); LOG_TEST_RET(card->ctx, rv, "CMD 'PIN VERIFY' failed"); break; case SC_PIN_CMD_UNBLOCK: - rv = auth_pin_reset(card, SC_AC_CHV, data, tries_left); + rv = auth_pin_reset(card, SC_AC_CHV, data); LOG_TEST_RET(card->ctx, rv, "CMD 'PIN VERIFY' failed"); break; default: @@ -1968,7 +1959,7 @@ auth_create_reference_data (struct sc_card *card, len += pin_info.pad_length; sc_log(card->ctx, "len %i", len); - if (args->puk && args->puk_len) { + if (args->puk && args->puk_len) { sbuf[len++] = args->puk_tries; sbuf[len++] = args->puk_len / puk_info.pad_length; sc_log(card->ctx, "len %i", len); @@ -1999,7 +1990,7 @@ auth_logout(struct sc_card *card) int ii, rv = 0, pin_ref; int reset_flag = 0x20; - for (ii=0; ii < 4; ii++) { + for (ii = 0; ii < 4; ii++) { rv = auth_get_pin_reference (card, SC_AC_CHV, ii+1, SC_PIN_CMD_UNBLOCK, &pin_ref); LOG_TEST_RET(card->ctx, rv, "Cannot get PIN reference"); @@ -2008,7 +1999,6 @@ auth_logout(struct sc_card *card) apdu.p2 = pin_ref | reset_flag; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); - } LOG_FUNC_RETURN(card->ctx, rv); @@ -2023,16 +2013,10 @@ write_publickey (struct sc_card *card, unsigned int offset, struct sc_pkcs15_pubkey_rsa key; int ii, rv; size_t len = 0, der_size = 0; - char debug_buf[2048]; LOG_FUNC_CALLED(card->ctx); - debug_buf[0] = 0; - sc_hex_dump(card->ctx, SC_LOG_DEBUG_NORMAL, - buf, count, debug_buf, sizeof(debug_buf)); - sc_log(card->ctx, - "write_publickey in %"SC_FORMAT_LEN_SIZE_T"u bytes :\n%s", - count, debug_buf); + sc_log_hex(card->ctx, "write_publickey", buf, count); if (1+offset > sizeof(rsa_der)) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid offset value"); @@ -2042,7 +2026,7 @@ write_publickey (struct sc_card *card, unsigned int offset, memcpy(rsa_der + offset, buf, len); rsa_der_len = offset + len; - if (rsa_der[0]==0x30) { + if (rsa_der[0] == 0x30) { if (rsa_der[1] & 0x80) for (ii=0; ii < (rsa_der[1]&0x0F); ii++) der_size = der_size*0x100 + rsa_der[2+ii]; @@ -2052,12 +2036,21 @@ write_publickey (struct sc_card *card, unsigned int offset, sc_log(card->ctx, "der_size %"SC_FORMAT_LEN_SIZE_T"u", der_size); if (offset + len < der_size + 2) - LOG_FUNC_RETURN(card->ctx, len); + LOG_FUNC_RETURN(card->ctx, (int)len); rv = sc_pkcs15_decode_pubkey_rsa(card->ctx, &key, rsa_der, rsa_der_len); rsa_der_len = 0; memset(rsa_der, 0, sizeof(rsa_der)); LOG_TEST_RET(card->ctx, rv, "cannot decode public key"); + if (key.modulus.len == 0 || key.exponent.len == 0) { + if (key.modulus.len > 0) { + free(key.modulus.data); + } + if (key.exponent.len > 0) { + free(key.exponent.data); + } + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid key decoded from DER"); + } memset(&args, 0, sizeof(args)); args.type = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; @@ -2065,7 +2058,11 @@ write_publickey (struct sc_card *card, unsigned int offset, args.data = key.modulus.data; args.len = key.modulus.len; rv = auth_update_component(card, &args); - LOG_TEST_RET(card->ctx, rv, "Update component failed"); + free(args.data); + if (rv != SC_SUCCESS) { + free(key.exponent.data); + LOG_TEST_RET(card->ctx, rv, "Update component failed"); + } memset(&args, 0, sizeof(args)); args.type = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; @@ -2073,9 +2070,10 @@ write_publickey (struct sc_card *card, unsigned int offset, args.data = key.exponent.data; args.len = key.exponent.len; rv = auth_update_component(card, &args); + free(args.data); LOG_TEST_RET(card->ctx, rv, "Update component failed"); - LOG_FUNC_RETURN(card->ctx, len); + LOG_FUNC_RETURN(card->ctx, (int)len); } @@ -2086,6 +2084,10 @@ auth_update_binary(struct sc_card *card, unsigned int offset, int rv = 0; LOG_FUNC_CALLED(card->ctx); + + if (!auth_current_ef) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid auth_current_ef"); + sc_log(card->ctx, "offset %i; count %"SC_FORMAT_LEN_SIZE_T"u", offset, count); sc_log(card->ctx, "last selected : magic %X; ef %X", @@ -2094,12 +2096,11 @@ auth_update_binary(struct sc_card *card, unsigned int offset, if (offset & ~0x7FFF) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid file offset"); - if (auth_current_ef->magic==SC_FILE_MAGIC && - auth_current_ef->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + if (auth_current_ef->magic == SC_FILE_MAGIC && + auth_current_ef->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { rv = write_publickey(card, offset, buf, count); - } - else if (auth_current_ef->magic==SC_FILE_MAGIC && - auth_current_ef->ef_structure == SC_CARDCTL_OBERTHUR_KEY_DES) { + } else if (auth_current_ef->magic == SC_FILE_MAGIC && + auth_current_ef->ef_structure == SC_CARDCTL_OBERTHUR_KEY_DES) { struct auth_update_component_info args; memset(&args, 0, sizeof(args)); @@ -2107,8 +2108,7 @@ auth_update_binary(struct sc_card *card, unsigned int offset, args.data = (unsigned char *)buf; args.len = count; rv = auth_update_component(card, &args); - } - else { + } else { rv = iso_ops->update_binary(card, offset, buf, count, 0); } @@ -2118,29 +2118,33 @@ auth_update_binary(struct sc_card *card, unsigned int offset, static int auth_read_binary(struct sc_card *card, unsigned int offset, - unsigned char *buf, size_t count, unsigned long flags) + unsigned char *buf, size_t count, unsigned long *flags) { int rv; - char debug_buf[2048]; + size_t sz; struct sc_pkcs15_bignum bn[2]; unsigned char *out = NULL; bn[0].data = NULL; bn[1].data = NULL; LOG_FUNC_CALLED(card->ctx); + + if (!auth_current_ef) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid auth_current_ef"); + sc_log(card->ctx, "offset %i; size %"SC_FORMAT_LEN_SIZE_T"u; flags 0x%lX", - offset, count, flags); + offset, count, flags ? *flags : 0); sc_log(card->ctx,"last selected : magic %X; ef %X", auth_current_ef->magic, auth_current_ef->ef_structure); if (offset & ~0x7FFF) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid file offset"); - if (auth_current_ef->magic==SC_FILE_MAGIC && - auth_current_ef->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + if (auth_current_ef->magic == SC_FILE_MAGIC && + auth_current_ef->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { int jj; - unsigned char resp[256]; + unsigned char resp[SC_MAX_APDU_RESP_SIZE]; size_t resp_len, out_len; struct sc_pkcs15_pubkey_rsa key; @@ -2152,6 +2156,8 @@ auth_read_binary(struct sc_card *card, unsigned int offset, for (jj=0; jjctx, &key, &out, &out_len)) { + if (sc_pkcs15_encode_pubkey_rsa(card->ctx, &key, &out, &out_len) != SC_SUCCESS) { rv = SC_ERROR_INVALID_ASN1_OBJECT; LOG_TEST_GOTO_ERR(card->ctx, rv, "cannot encode RSA public key"); } else { - rv = out_len - offset > count ? count : out_len - offset; - memcpy(buf, out + offset, rv); + if (out_len < offset) { + rv = SC_ERROR_UNKNOWN_DATA_RECEIVED; + goto err; + } + sz = MIN(out_len - offset, count); + memcpy(buf, out + offset, sz); - debug_buf[0] = 0; - sc_hex_dump(card->ctx, SC_LOG_DEBUG_NORMAL, - buf, rv, debug_buf, sizeof(debug_buf)); - sc_log(card->ctx, - "write_publickey in %"SC_FORMAT_LEN_SIZE_T"u bytes :\n%s", - count, debug_buf); + sc_log_hex(card->ctx, "write_publickey", buf, sz); + rv = (int)sz; } - } - else { + } else { rv = iso_ops->read_binary(card, offset, buf, count, 0); } @@ -2205,7 +2210,7 @@ auth_read_binary(struct sc_card *card, unsigned int offset, static int -auth_read_record(struct sc_card *card, unsigned int nr_rec, +auth_read_record(struct sc_card *card, unsigned int nr_rec, unsigned int idx, unsigned char *buf, size_t count, unsigned long flags) { struct sc_apdu apdu; @@ -2216,25 +2221,30 @@ auth_read_record(struct sc_card *card, unsigned int nr_rec, "auth_read_record(): nr_rec %i; count %"SC_FORMAT_LEN_SIZE_T"u", nr_rec, count); + if (nr_rec > 0xFF || idx != 0) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB2, nr_rec, 0); apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3; if (flags & SC_RECORD_BY_REC_NR) apdu.p2 |= 0x04; - apdu.le = count; - apdu.resplen = count; + apdu.le = MIN(count, SC_MAX_APDU_BUFFER_SIZE); + apdu.resplen = SC_MAX_APDU_BUFFER_SIZE; apdu.resp = recvbuf; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); if (apdu.resplen == 0) LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); + if (count < apdu.resplen) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH); memcpy(buf, recvbuf, apdu.resplen); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, rv, "Card returned error"); - LOG_FUNC_RETURN(card->ctx, apdu.resplen); + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); } @@ -2286,8 +2296,8 @@ auth_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) { int ii; - for (ii=0; auth_warnings[ii].SWs; ii++) { - if (auth_warnings[ii].SWs == ((sw1 << 8) | sw2)) { + for (ii = 0; auth_warnings[ii].SWs; ii++) { + if (auth_warnings[ii].SWs == ((sw1 << 8) | sw2)) { sc_log(card->ctx, "%s", auth_warnings[ii].errorstr); return auth_warnings[ii].errorno; } diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c index cf4de26f70..89ece9636b 100644 --- a/src/libopensc/card-openpgp.c +++ b/src/libopensc/card-openpgp.c @@ -15,19 +15,27 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Specifications: - * http://www.g10code.de/docs/openpgp-card-1.0.pdf (obsolete) - * http://www.g10code.de/docs/openpgp-card-1.1.pdf - * http://www.g10code.de/docs/openpgp-card-2.0.pdf - * http://www.g10code.de/docs/openpgp-card-2.1.pdf (minor changes to v2.0) - * http://www.g10code.de/docs/openpgp-card-3.0.pdf (not yet supported) + * (all available from: https://gnupg.org/ftp/specs/) + * https://gnupg.org/ftp/specs/openpgp-card-1.0.pdf (obsolete) + * https://gnupg.org/ftp/specs/openpgp-card-1.1.pdf + * https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-2.0.pdf + * https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-2.1.pdf + * https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-2.2.pdf + * https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.0.pdf + * https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.1.pdf + * https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.2.pdf + * https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.3.pdf + * https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.3.0.pdf + * https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.3.1.pdf + * https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.4.pdf */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -41,17 +49,35 @@ #include "cardctl.h" #include "errors.h" #ifdef ENABLE_OPENSSL +#include #include #endif /* ENABLE_OPENSSL */ -static struct sc_atr_table pgp_atrs[] = { - { "3b:fa:13:00:ff:81:31:80:45:00:31:c1:73:c0:01:00:00:90:00:b1", NULL, "OpenPGP card v1.0/1.1", SC_CARD_TYPE_OPENPGP_V1, 0, NULL }, - { "3b:da:18:ff:81:b1:fe:75:1f:03:00:31:c5:73:c0:01:40:00:90:00:0c", NULL, "CryptoStick v1.2 (OpenPGP v2.0)", SC_CARD_TYPE_OPENPGP_V2, 0, NULL }, - { "3b:da:11:ff:81:b1:fe:55:1f:03:00:31:84:73:80:01:80:00:90:00:e4", NULL, "Gnuk v1.0.x (OpenPGP v2.0)", SC_CARD_TYPE_OPENPGP_GNUK, 0, NULL }, +#include "card-openpgp.h" + + +static const char default_cardname[] = "OpenPGP card"; +static const char default_cardname_v1[] = "OpenPGP card v1.x"; +static const char default_cardname_v2[] = "OpenPGP card v2.x"; +static const char default_cardname_v3[] = "OpenPGP card v3.x"; + + +static const struct sc_atr_table pgp_atrs[] = { + { "3b:fa:13:00:ff:81:31:80:45:00:31:c1:73:c0:01:00:00:90:00:b1", NULL, default_cardname_v1, SC_CARD_TYPE_OPENPGP_V1, 0, NULL }, + { "3b:da:18:ff:81:b1:fe:75:1f:03:00:31:c5:73:c0:01:40:00:90:00:0c", NULL, default_cardname_v2, SC_CARD_TYPE_OPENPGP_V2, 0, NULL }, + { + "3b:da:11:ff:81:b1:fe:55:1f:03:00:31:84:73:80:01:80:00:90:00:e4", + "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:ff:ff:00", + "Gnuk v1.x.x (OpenPGP v2.0)", SC_CARD_TYPE_OPENPGP_GNUK, 0, NULL + }, { "3b:fc:13:00:00:81:31:fe:15:59:75:62:69:6b:65:79:4e:45:4f:72:33:e1", NULL, "Yubikey NEO (OpenPGP v2.0)", SC_CARD_TYPE_OPENPGP_V2, 0, NULL }, + { "3b:f8:13:00:00:81:31:fe:15:59:75:62:69:6b:65:79:34:d4", NULL, "Yubikey 4 (OpenPGP v2.1)", SC_CARD_TYPE_OPENPGP_V2, 0, NULL }, + { "3b:fd:13:00:00:81:31:fe:15:80:73:c0:21:c0:57:59:75:62:69:4b:65:79:40", NULL, "Yubikey 5 (OpenPGP v3.4)", SC_CARD_TYPE_OPENPGP_V3, 0, NULL }, + { "3b:da:18:ff:81:b1:fe:75:1f:03:00:31:f5:73:c0:01:60:00:90:00:1c", NULL, default_cardname_v3, SC_CARD_TYPE_OPENPGP_V3, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; + static struct sc_card_operations *iso_ops; static struct sc_card_operations pgp_ops; static struct sc_card_driver pgp_drv = { @@ -61,6 +87,53 @@ static struct sc_card_driver pgp_drv = { NULL, 0, NULL }; +// clang-format off +static pgp_ec_curves_t ec_curves_openpgp34[] = { + /* OpenPGP 3.4+ Ed25519 and Curve25519 */ + {{{1, 3, 6, 1, 4, 1, 3029, 1, 5, 1, -1}}, 255, SC_ALGORITHM_XEDDSA}, /* curve25519 for encryption => CKK_EC_MONTGOMERY */ + {{{1, 3, 101, 110, -1}}, 255, SC_ALGORITHM_XEDDSA}, /* RFC8410 OID equivalent to curve25519 */ + {{{1, 3, 6, 1, 4, 1, 11591, 15, 1, -1}}, 255, SC_ALGORITHM_EDDSA}, /* ed25519 for signatures => CKK_EC_EDWARDS */ + {{{1, 3, 101, 112, -1}}, 255, SC_ALGORITHM_EDDSA}, /* RFC8410 OID equivalent to ed25519 */ + + /* v3.0+ supports: [RFC 4880 & 6637] 0x12 = ECDH, 0x13 = ECDSA */ + {{{1, 2, 840, 10045, 3, 1, 7, -1}}, 256, SC_ALGORITHM_EC}, /* ansiX9p256r1 */ + {{{1, 3, 132, 0, 34, -1}}, 384, SC_ALGORITHM_EC}, /* ansiX9p384r1 */ + {{{1, 3, 132, 0, 35, -1}}, 521, SC_ALGORITHM_EC}, /* ansiX9p521r1 */ + {{{1, 3, 36, 3, 3, 2, 8, 1, 1, 7, -1}}, 256, SC_ALGORITHM_EC}, /* brainpoolP256r1 */ + {{{1, 3, 36, 3, 3, 2, 8, 1, 1, 11, -1}}, 384, SC_ALGORITHM_EC}, /* brainpoolP384r1 */ + {{{1, 3, 36, 3, 3, 2, 8, 1, 1, 13, -1}}, 512, SC_ALGORITHM_EC}, /* brainpoolP512r1 */ + {{{1, 3, 36, 3, 3, 2, 8, 1, 1, 8, -1}}, 256, SC_ALGORITHM_EC}, /* brainpoolP256t1 */ + {{{1, 3, 36, 3, 3, 2, 8, 1, 1, 12, -1}}, 384, SC_ALGORITHM_EC}, /* brainpoolP384t1 */ + {{{1, 3, 36, 3, 3, 2, 8, 1, 1, 14, -1}}, 512, SC_ALGORITHM_EC}, /* brainpoolP512t1 */ + {{{-1}}, 0, 0 } /* This entry must not be touched. */ +}; + +#ifdef ENABLE_OPENSSL +static pgp_ec_curves_alt_t ec_curves_alt[] = { + {{{1, 3, 6, 1, 4, 1, 3029, 1, 5, 1, -1}}, {{1, 3, 101, 110, -1}}, 255}, /* curve25519 CKK_EC_MONTGOMERY X25519 */ + {{{1, 3, 6, 1, 4, 1, 11591, 15, 1, -1}}, {{1, 3, 101, 112, -1}}, 255}, /* ed25519 CKK_EC_EDWARDS Ed25519 */ + {{{-1}}, {{-1}}, 0 } /* This entry must not be touched. */ +}; + +#endif /* ENABLE_OPENSSL */ + +static pgp_ec_curves_t *ec_curves_openpgp = ec_curves_openpgp34 + 4; + +struct sc_object_id curve25519_oid = {{1, 3, 6, 1, 4, 1, 3029, 1, 5, 1, -1}}; +struct sc_object_id X25519_oid = {{1, 3, 101, 110, -1}}; /* need to check for RFC8410 version? */ + +/* Gnuk supports NIST, SECG and Curve25519 since version 1.2 */ +static pgp_ec_curves_t ec_curves_gnuk[] = { + {{{1, 2, 840, 10045, 3, 1, 7, -1}}, 256, SC_ALGORITHM_EC}, /* ansiX9p256r1 */ + {{{1, 3, 132, 0, 10, -1}}, 256, SC_ALGORITHM_EC}, /* secp256k1 */ + {{{1, 3, 6, 1, 4, 1, 3029, 1, 5, 1, -1}}, 255, SC_ALGORITHM_XEDDSA}, /* curve25519 for encryption => CKK_EC_MONTGOMERY */ + {{{1, 3, 101, 110, -1}}, 255, SC_ALGORITHM_XEDDSA}, /* RFC8410 OID equivalent to curve25519 */ + {{{1, 3, 6, 1, 4, 1, 11591, 15, 1, -1}}, 255, SC_ALGORITHM_EDDSA}, /* ed25519 for signatures => CKK_EC_EDWARDS */ + {{{1, 3, 101, 112, -1}}, 255, SC_ALGORITHM_EDDSA}, /* RFC8410 OID equivalent to ed25519 */ + {{{-1}}, 0, 0} /* This entry must not be touched. */ +}; +// clang-format on + /* * The OpenPGP card doesn't have a file system, instead everything * is stored in data objects that are accessed through GET/PUT. @@ -73,123 +146,20 @@ static struct sc_card_driver pgp_drv = { * Everything else is mapped to "file" IDs. */ -enum _type { /* DO type */ - SIMPLE = SC_FILE_TYPE_WORKING_EF, - CONSTRUCTED = SC_FILE_TYPE_DF -}; - -enum _version { /* 2-byte BCD-alike encoded version number */ - OPENPGP_CARD_1_0 = 0x0100, - OPENPGP_CARD_1_1 = 0x0101, - OPENPGP_CARD_2_0 = 0x0200, - OPENPGP_CARD_2_1 = 0x0201, - OPENPGP_CARD_3_0 = 0x0300 -}; - -enum _access { /* access flags for the respective DO/file */ - READ_NEVER = 0x0010, - READ_PIN1 = 0x0011, - READ_PIN2 = 0x0012, - READ_PIN3 = 0x0014, - READ_ALWAYS = 0x0018, - READ_MASK = 0x00FF, - WRITE_NEVER = 0x1000, - WRITE_PIN1 = 0x1100, - WRITE_PIN2 = 0x1200, - WRITE_PIN3 = 0x1400, - WRITE_ALWAYS = 0x1800, - WRITE_MASK = 0x1F00 -}; - -enum _ext_caps { /* extended capabilities/features */ - EXT_CAP_ALG_ATTR_CHANGEABLE = 0x0004, - EXT_CAP_PRIVATE_DO = 0x0008, - EXT_CAP_C4_CHANGEABLE = 0x0010, - EXT_CAP_KEY_IMPORT = 0x0020, - EXT_CAP_GET_CHALLENGE = 0x0040, - EXT_CAP_SM = 0x0080, - EXT_CAP_CHAINING = 0x1000, - EXT_CAP_APDU_EXT = 0x2000 -}; - -enum _card_state { - CARD_STATE_UNKNOWN = 0x00, - CARD_STATE_INITIALIZATION = 0x03, - CARD_STATE_ACTIVATED = 0x05 -}; - -typedef struct pgp_blob { - struct pgp_blob * next; /* pointer to next sibling */ - struct pgp_blob * parent; /* pointer to parent */ - struct do_info *info; - - sc_file_t * file; - unsigned int id; - int status; - - unsigned char * data; - unsigned int len; - struct pgp_blob * files; /* pointer to 1st child */ -} pgp_blob_t; - -struct do_info { - unsigned int id; /* ID of the DO in question */ - - enum _type type; /* constructed DO or not */ - enum _access access; /* R/W access levels for the DO */ - - /* function to get the DO from the card: - * only != NULL is DO if readable and not only a part of a constructed DO */ - int (*get_fn)(sc_card_t *, unsigned int, u8 *, size_t); - /* function to write the DO to the card: - * only != NULL if DO is writeable under some conditions */ - int (*put_fn)(sc_card_t *, unsigned int, const u8 *, size_t); -}; - static int pgp_get_card_features(sc_card_t *card); static int pgp_finish(sc_card_t *card); -static void pgp_iterate_blobs(pgp_blob_t *, int, void (*func)()); +static void pgp_free_blobs(pgp_blob_t *); static int pgp_get_blob(sc_card_t *card, pgp_blob_t *blob, unsigned int id, pgp_blob_t **ret); -static pgp_blob_t * pgp_new_blob(sc_card_t *, pgp_blob_t *, unsigned int, sc_file_t *); +static pgp_blob_t *pgp_new_blob(sc_card_t *, pgp_blob_t *, unsigned int, sc_file_t *); static void pgp_free_blob(pgp_blob_t *); -static int pgp_get_pubkey(sc_card_t *, unsigned int, - u8 *, size_t); -static int pgp_get_pubkey_pem(sc_card_t *, unsigned int, - u8 *, size_t); - -/* The DO holding X.509 certificate is constructed but does not contain a child DO. - * We should notice this when building fake file system later. */ -#define DO_CERT 0x7f21 -/* Control Reference Template of private keys. Ref: Section 4.3.3.7 of OpenPGP card v2 spec. - * Here we treat them as DOs just for convenience */ -#define DO_SIGN 0xb600 -#define DO_ENCR 0xb800 -#define DO_AUTH 0xa400 -/* These DOs do not exist. They are defined and used just for ease of implementation */ -#define DO_SIGN_SYM 0xb601 -#define DO_ENCR_SYM 0xb801 -#define DO_AUTH_SYM 0xa401 -/* Private DOs */ -#define DO_PRIV1 0x0101 -#define DO_PRIV2 0x0102 -#define DO_PRIV3 0x0103 -#define DO_PRIV4 0x0104 -/* Cardholder information DOs */ -#define DO_CARDHOLDER 0x65 -#define DO_NAME 0x5b -#define DO_LANG_PREF 0x5f2d -#define DO_SEX 0x5f35 - - -/* Maximum length for response buffer when reading pubkey. - * This value is calculated with 4096-bit key length */ -#define MAXLEN_RESP_PUBKEY 527 -/* Gnuk only supports 1 key length (2048 bit) */ -#define MAXLEN_RESP_PUBKEY_GNUK 271 - -static struct do_info pgp1_objects[] = { /* OpenPGP card spec 1.1 */ +static int pgp_get_pubkey(sc_card_t *, unsigned int, u8 *, size_t); +static int pgp_get_pubkey_pem(sc_card_t *, unsigned int, u8 *, size_t); +static int pgp_enumerate_blob(sc_card_t *card, pgp_blob_t *blob); + +// clang-format off +static pgp_do_info_t pgp1x_objects[] = { /* OpenPGP card spec 1.1 */ { 0x004f, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x005b, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x005e, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data }, @@ -238,7 +208,32 @@ static struct do_info pgp1_objects[] = { /* OpenPGP card spec 1.1 */ { 0, 0, 0, NULL, NULL }, }; -static struct do_info pgp2_objects[] = { /* OpenPGP card spec 2.0 */ +static pgp_do_info_t pgp34_objects[] = { /**** OpenPGP card spec 3.4 ****/ + { 0x00d9, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, + { 0x00da, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, + { 0x00db, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, + { 0x00dc, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, + { 0x00de, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, + { 0x00de, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, + /* DO FA is CONSTRUCTED in spec; we treat it as SIMPLE for the time being */ + { 0x00fa, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL }, + /* DO FB is CONSTRUCTED in spec; we treat it as SIMPLE for the time being */ + { 0x00fb, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, + /* DO FC is CONSTRUCTED in spec; we treat it as SIMPLE for the time being */ + { 0x00fc, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, + /**** OpenPGP card spec 3.3 ****/ + { DO_KDF, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data }, + /**** OpenPGP card spec 3.0 - 3.2 ****/ + { 0x00d6, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, + { 0x00d7, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, + { 0x00d8, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, + /* DO 7F66 is CONSTRUCTED in spec; we treat it as SIMPLE: no need to parse TLV */ + { 0x7f66, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, sc_put_data }, + /* DO 7F74 is CONSTRUCTED in spec; we treat it as SIMPLE for the time being */ + { 0x7f74, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, sc_put_data }, + /**** OpenPGP card spec 2.1 & 2.2 ****/ + { 0x00d5, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, + /**** OpenPGP card spec 2.0 ****/ { 0x004d, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x004f, SIMPLE, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL }, { 0x005b, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, @@ -281,13 +276,12 @@ static struct do_info pgp2_objects[] = { /* OpenPGP card spec 2.0 */ { 0x5f48, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x5f50, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data }, { 0x5f52, SIMPLE, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL }, - /* The 7F21 is constructed DO in spec, but in practice, its content can be retrieved - * as simple DO (no need to parse TLV). */ - { DO_CERT, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data }, + /* DO 7F21 is CONSTRUCTED in spec; we treat it as SIMPLE: no need to parse TLV */ + { DO_CERT, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data }, { 0x7f48, CONSTRUCTED, READ_NEVER | WRITE_NEVER, NULL, NULL }, { 0x7f49, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { DO_AUTH, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, - /* The 0xA401, 0xB601, 0xB801 are just symbolic, it does not represent any real DO. + /* The DOs 0xA401, 0xB601, 0xB801 are virtual DOs, they do not represent any real DO. * However, their R/W access condition may block the process of importing key in pkcs15init. * So we set their accesses condition as WRITE_PIN3 (writable). */ { DO_AUTH_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, @@ -297,145 +291,230 @@ static struct do_info pgp2_objects[] = { /* OpenPGP card spec 2.0 */ { DO_ENCR_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, { 0, 0, 0, NULL, NULL }, }; +// clang-format on -#define DRVDATA(card) ((struct pgp_priv_data *) ((card)->drv_data)) -struct pgp_priv_data { - pgp_blob_t * mf; - pgp_blob_t * current; /* currently selected file */ +static pgp_do_info_t *pgp33_objects = pgp34_objects + 9; +static pgp_do_info_t *pgp30_objects = pgp34_objects + 10; +static pgp_do_info_t *pgp21_objects = pgp34_objects + 15; +static pgp_do_info_t *pgp20_objects = pgp34_objects + 16; - enum _version bcd_version; - struct do_info *pgp_objects; - enum _card_state state; /* card state */ - enum _ext_caps ext_caps; /* extended capabilities */ +/** + * Internal: get OpenPGP application identifier from AID DO 004F + */ +static int +get_full_pgp_aid(sc_card_t *card, sc_file_t *file) +{ + int r = SC_ERROR_INVALID_ARGUMENTS; - size_t max_challenge_size; - size_t max_cert_size; + if (file != NULL) { + /* explicitly get the full aid */ + r = sc_get_data(card, 0x004F, file->name, sizeof file->name); + file->namelen = MAX(r, 0); + } - sc_security_env_t sec_env; -}; + return r; +} /** * ABI: check if card's ATR matches one of driver's - * or if the OpenPGP application is present. + * or if the OpenPGP application is present on the card. */ static int pgp_match_card(sc_card_t *card) { int i; + LOG_FUNC_CALLED(card->ctx); + i = _sc_match_atr(card, pgp_atrs, &card->type); if (i >= 0) { card->name = pgp_atrs[i].name; - return 1; + LOG_FUNC_RETURN(card->ctx, 1); } else { sc_path_t partial_aid; - unsigned char aid[16]; + sc_file_t *file = NULL; /* select application "OpenPGP" */ sc_format_path("D276:0001:2401", &partial_aid); partial_aid.type = SC_PATH_TYPE_DF_NAME; - if (SC_SUCCESS == iso_ops->select_file(card, &partial_aid, NULL)) { - /* read information from AID */ - i = sc_get_data(card, 0x004F, aid, sizeof aid); - if (i == 16) { - switch ((aid[6] << 8) | aid[7]) { /* BCD-coded bytes */ - case 0x0101: - card->type = SC_CARD_TYPE_OPENPGP_V1; - sc_log(card->ctx, "OpenPGPv1-type card found"); - return 1; - case 0x0200: - case 0x0201: - card->type = SC_CARD_TYPE_OPENPGP_V2; - sc_log(card->ctx, "OpenPGPv2-type card found"); - return 1; - default: - sc_log(card->ctx, "unsupported OpenPGP-type card found"); - /* fall through */ + /* OpenPGP card only supports selection *with* requested FCI */ + i = iso_ops->select_file(card, &partial_aid, &file); + if (SC_SUCCESS == i) { + card->type = SC_CARD_TYPE_OPENPGP_BASE; + card->name = default_cardname; + + if (file->namelen != 16) + (void) get_full_pgp_aid(card, file); + if (file->namelen == 16) { + unsigned char major = BCD2UCHAR(file->name[6]); + + switch (major) { + case 1: + card->type = SC_CARD_TYPE_OPENPGP_V1; + card->name = default_cardname_v1; + break; + case 2: + card->type = SC_CARD_TYPE_OPENPGP_V2; + card->name = default_cardname_v2; + break; + case 3: + card->type = SC_CARD_TYPE_OPENPGP_V3; + card->name = default_cardname_v3; + break; + default: + break; } } + sc_file_free(file); + LOG_FUNC_RETURN(card->ctx, 1); } } - return 0; + LOG_FUNC_RETURN(card->ctx, 0); } -#define BCD2CHAR(x) (((((x) & 0xF0) >> 4) * 10) + ((x) & 0x0F)) +/* populate MF - add matching blobs listed in the pgp_objects table */ +int populate_blobs_to_mf(sc_card_t *card, struct pgp_priv_data *priv) +{ + pgp_do_info_t *info; + for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++) { + if (((info->access & READ_MASK) != READ_NEVER) && (info->get_fn != NULL)) { + pgp_blob_t *child = NULL; + sc_file_t *file = sc_file_new(); + + child = pgp_new_blob(card, priv->mf, info->id, file); + + /* catch out of memory condition */ + if (child == NULL) { + sc_file_free(file); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + } + } + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} /** - * ABI: initialize driver. + * ABI: initialize driver & allocate private data. */ static int pgp_init(sc_card_t *card) { struct pgp_priv_data *priv; - sc_path_t aid; + sc_path_t path; sc_file_t *file = NULL; - struct do_info *info; - int r; - pgp_blob_t *child = NULL; + int r, i; LOG_FUNC_CALLED(card->ctx); priv = calloc (1, sizeof *priv); if (!priv) - return SC_ERROR_OUT_OF_MEMORY; + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); card->drv_data = priv; card->cla = 0x00; - /* set pointer to correct list of card objects */ - priv->pgp_objects = (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK) - ? pgp2_objects : pgp1_objects; - - /* set detailed card version */ - priv->bcd_version = (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK) - ? OPENPGP_CARD_2_0 : OPENPGP_CARD_1_1; - /* select application "OpenPGP" */ - sc_format_path("D276:0001:2401", &aid); - aid.type = SC_PATH_TYPE_DF_NAME; - if ((r = iso_ops->select_file(card, &aid, &file)) < 0) { + sc_format_path("D276:0001:2401", &path); + path.type = SC_PATH_TYPE_DF_NAME; + if ((r = iso_ops->select_file(card, &path, &file)) < 0) { + sc_file_free(file); pgp_finish(card); - LOG_FUNC_RETURN(card->ctx, r); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } /* defensive programming check */ if (!file) { pgp_finish(card); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_FOUND); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } if (file->namelen != 16) { /* explicitly get the full aid */ - r = sc_get_data(card, 0x004F, file->name, sizeof file->name); + r = get_full_pgp_aid(card, file); if (r < 0) { + sc_file_free(file); pgp_finish(card); - return r; + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } - file->namelen = r; } /* read information from AID */ if (file->namelen == 16) { - /* OpenPGP card spec 1.1 & 2.0, section 4.2.1 & 4.1.2.1 */ + static char card_name[SC_MAX_APDU_BUFFER_SIZE] = "OpenPGP card"; + + /* OpenPGP card spec 1.1, 2.x & 3.x, section 4.2.1 & 4.1.2.1 */ priv->bcd_version = bebytes2ushort(file->name + 6); - card->version.fw_major = card->version.hw_major = BCD2CHAR(file->name[6]); - card->version.fw_minor = card->version.hw_minor = BCD2CHAR(file->name[7]); + card->version.fw_major = card->version.hw_major = BCD2UCHAR(file->name[6]); + card->version.fw_minor = card->version.hw_minor = BCD2UCHAR(file->name[7]); + + /* for "standard" cards, include detailed card version & serial no. in card name */ + if (card->name == default_cardname_v1 || + card->name == default_cardname_v2 || + card->name == default_cardname_v3) { + snprintf(card_name, sizeof(card_name), + "OpenPGP card v%u.%u (%04X %08lX)", + card->version.hw_major, card->version.hw_minor, + bebytes2ushort(file->name + 8), + bebytes2ulong(file->name + 10)); + } + else if (card->name != NULL) { + /* for other cards, append serial number to the card name */ + snprintf(card_name, sizeof(card_name), + "%s (%04X %08lX)", + card->name, + bebytes2ushort(file->name + 8), + bebytes2ulong(file->name + 10)); + } + card->name = card_name; - /* kludge: get card's serial number from manufacturer ID + serial number */ + /* GPG compatibility: set card's serial number to manufacturer ID + serial number */ memcpy(card->serialnr.value, file->name + 8, 6); card->serialnr.len = 6; + } else { + /* set detailed card version */ + switch (card->type) { + case SC_CARD_TYPE_OPENPGP_V3: + priv->bcd_version = OPENPGP_CARD_3_0; + break; + case SC_CARD_TYPE_OPENPGP_GNUK: + case SC_CARD_TYPE_OPENPGP_V2: + priv->bcd_version = OPENPGP_CARD_2_0; + break; + default: + priv->bcd_version = OPENPGP_CARD_1_1; + break; + } + } + + /* set pointer to correct list of card objects */ + priv->pgp_objects = (priv->bcd_version < OPENPGP_CARD_2_0) ? pgp1x_objects + : (priv->bcd_version < OPENPGP_CARD_2_1) ? pgp20_objects + : (priv->bcd_version < OPENPGP_CARD_3_0) ? pgp21_objects + : (priv->bcd_version < OPENPGP_CARD_3_3) ? pgp30_objects + : (priv->bcd_version < OPENPGP_CARD_3_4) ? pgp33_objects + : pgp34_objects; + + /* With gnuk, we use different curves */ + if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) { + priv->ec_curves = ec_curves_gnuk; + } else if (priv->bcd_version >= OPENPGP_CARD_3_4) { + priv->ec_curves = ec_curves_openpgp34; + } else { + priv->ec_curves = ec_curves_openpgp; } - /* change file path to MF for re-use in MF */ + /* change file path to MF for reuse in MF */ sc_format_path("3f00", &file->path); /* set up the root of our fake file tree */ + /* Transfers ownership of the file to the priv->mf structure */ priv->mf = pgp_new_blob(card, NULL, 0x3f00, file); if (!priv->mf) { + sc_file_free(file); pgp_finish(card); LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } @@ -443,89 +522,478 @@ pgp_init(sc_card_t *card) /* select MF */ priv->current = priv->mf; - /* populate MF - add matching blobs listed in the pgp_objects table */ - for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++) { - if (((info->access & READ_MASK) != READ_NEVER) && - (info->get_fn != NULL)) { - child = pgp_new_blob(card, priv->mf, info->id, sc_file_new()); - - /* catch out of memory condition */ - if (child == NULL) { - pgp_finish(card); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); - } - } + r = populate_blobs_to_mf(card, priv); + if (r < 0) { + pgp_finish(card); + LOG_FUNC_RETURN(card->ctx, r); } /* get card_features from ATR & DOs */ - pgp_get_card_features(card); + if (pgp_get_card_features(card)) { + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + } + + /* if algorithm attributes can be changed, + * add supported algorithms based on specification for pkcs15-init */ + if (priv->ext_caps & EXT_CAP_ALG_ATTR_CHANGEABLE) { + unsigned long flags_rsa, flags_ecc, ext_flags; + unsigned long flags_eddsa, flags_xeddsa; + + /* OpenPGP card spec 1.1 & 2.x, section 7.2.9 & 7.2.10 / v3.x section 7.2.11 & 7.2.12 */ + flags_rsa = SC_ALGORITHM_RSA_PAD_PKCS1| + SC_ALGORITHM_RSA_HASH_NONE| + SC_ALGORITHM_ONBOARD_KEY_GEN; + flags_ecc = SC_ALGORITHM_ECDSA_RAW| + SC_ALGORITHM_ECDH_CDH_RAW| + SC_ALGORITHM_ECDSA_HASH_NONE| + SC_ALGORITHM_ONBOARD_KEY_GEN; + ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE; + + flags_eddsa = SC_ALGORITHM_EDDSA_RAW | + SC_ALGORITHM_ONBOARD_KEY_GEN; + /* xeddsa may allow signing at some time */ + flags_xeddsa = SC_ALGORITHM_ECDH_CDH_RAW | + SC_ALGORITHM_ONBOARD_KEY_GEN; + + switch (card->type) { + case SC_CARD_TYPE_OPENPGP_V3: + /* RSA 1024 was removed for v3+ */ + _sc_card_add_rsa_alg(card, 4096, flags_rsa, 0); + _sc_card_add_rsa_alg(card, 3072, flags_rsa, 0); + /* fallthrough */ + case SC_CARD_TYPE_OPENPGP_GNUK: + _sc_card_add_rsa_alg(card, 2048, flags_rsa, 0); + for (i=0; priv->ec_curves[i].oid.value[0] >= 0; i++) + { + if (priv->ec_curves[i].key_type == SC_ALGORITHM_EC) + _sc_card_add_ec_alg(card, priv->ec_curves[i].size, + flags_ecc, ext_flags, &priv->ec_curves[i].oid); + else if (priv->ec_curves[i].key_type == SC_ALGORITHM_EDDSA) + _sc_card_add_eddsa_alg(card, priv->ec_curves[i].size, + flags_eddsa, ext_flags, &priv->ec_curves[i].oid); + else if (priv->ec_curves[i].key_type == SC_ALGORITHM_XEDDSA) + _sc_card_add_xeddsa_alg(card, priv->ec_curves[i].size, + flags_xeddsa, ext_flags, &priv->ec_curves[i].oid); + else + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + break; + case SC_CARD_TYPE_OPENPGP_V2: + default: + _sc_card_add_rsa_alg(card, 1024, flags_rsa, 0); + _sc_card_add_rsa_alg(card, 2048, flags_rsa, 0); + _sc_card_add_rsa_alg(card, 3072, flags_rsa, 0); + _sc_card_add_rsa_alg(card, 4096, flags_rsa, 0); + break; + } + } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /** - * Internal: get features of the card: capabilities, ... + * Internal: parse historic bytes to get card capabilities. */ -static int -pgp_get_card_features(sc_card_t *card) +static void +pgp_parse_hist_bytes(sc_card_t *card, u8 *ctlv, size_t ctlv_len) { struct pgp_priv_data *priv = DRVDATA(card); - unsigned char *hist_bytes = card->atr.value; - size_t atr_len = card->atr.len; - size_t i; - pgp_blob_t *blob, *blob6e, *blob73; - - /* parse card capabilities from historical bytes */ - for (i = 0; (i < atr_len) && (hist_bytes[i] != 0x73); i++) - ; - /* IS07816-4 hist bytes 3rd function table */ - if ((hist_bytes[i] == 0x73) && (atr_len > i+3)) { + const u8 *ptr; + + /* IS07816-4 hist bytes: 3rd function table */ + if ((ptr = sc_compacttlv_find_tag(ctlv, ctlv_len, 0x73, NULL)) != NULL) { /* bit 0x40 in byte 3 of TL 0x73 means "extended Le/Lc" */ - if (hist_bytes[i+3] & 0x40) { + if (ptr[2] & 0x40) { card->caps |= SC_CARD_CAP_APDU_EXT; priv->ext_caps |= EXT_CAP_APDU_EXT; } /* bit 0x80 in byte 3 of TL 0x73 means "Command chaining" */ - if (hist_bytes[i+3] & 0x80) + if (ptr[2] & 0x80) { priv->ext_caps |= EXT_CAP_CHAINING; + } + } + + if ((priv->bcd_version >= OPENPGP_CARD_3_0) && + ((ptr = sc_compacttlv_find_tag(ctlv, ctlv_len, 0x31, NULL)) != NULL)) { + // ToDo ... + } +} + + +/** + * Internal: parse an algorithm attributes DO + **/ +static int +pgp_parse_algo_attr_blob(sc_card_t *card, const pgp_blob_t *blob, + sc_cardctl_openpgp_key_gen_store_info_t *key_info) +{ + struct pgp_priv_data *priv = DRVDATA(card); + struct sc_object_id oid; + unsigned int j, r; + + LOG_FUNC_CALLED(card->ctx); + + if (blob == NULL || blob->data == NULL || blob->len == 0 || + blob->id < 0x00c1 || blob->id > 0x00c3 || key_info == NULL) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); + } + + key_info->key_id = blob->id - 0x00c0; /* attribute algorithm blobs are C1 - C3 */ + + switch (blob->data[0]) { + case SC_OPENPGP_KEYALGO_RSA: + if (blob->len < 5) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); + } + + key_info->algorithm = SC_OPENPGP_KEYALGO_RSA; + key_info->u.rsa.modulus_len = bebytes2ushort(blob->data + 1); + key_info->u.rsa.exponent_len = bebytes2ushort(blob->data + 3); + + key_info->u.rsa.keyformat = (blob->len > 5) + ? blob->data[5] + : SC_OPENPGP_KEYFORMAT_RSA_STD; + break; + case SC_OPENPGP_KEYALGO_ECDH: + case SC_OPENPGP_KEYALGO_ECDSA: + case SC_OPENPGP_KEYALGO_EDDSA: + + /* SC_OPENPGP_KEYALGO_ECDH || SC_OPENPGP_KEYALGO_ECDSA || SC_OPENPGP_KEYALGO_EDDSA */ + key_info->algorithm = blob->data[0]; + + /* last byte is set to 0xFF if pubkey import is supported */ + if (blob->data[blob->len-1] == SC_OPENPGP_KEYFORMAT_EC_STDPUB){ + if (blob->len < 3) + return SC_ERROR_INCORRECT_PARAMETERS; + key_info->u.ec.oidv_len = blob->len - 2; + key_info->u.ec.keyformat = SC_OPENPGP_KEYFORMAT_EC_STDPUB; + } + else { + /* otherwise, last byte could be 00, so let's ignore it, as + * it is not part of OID */ + if (blob->len < 2) + return SC_ERROR_INCORRECT_PARAMETERS; + if (blob->data[blob->len-1] == SC_OPENPGP_KEYFORMAT_EC_STD) + key_info->u.ec.oidv_len = blob->len - 2; + else + key_info->u.ec.oidv_len = blob->len - 1; + key_info->u.ec.keyformat = SC_OPENPGP_KEYFORMAT_EC_STD; + } + + /* Create copy of oid from blob */ + sc_init_oid(&oid); + r = sc_asn1_decode_object_id(&blob->data[1], key_info->u.ec.oidv_len, &oid); + + /* decoding failed, return sc_asn1_decode_object_id error code */ + if (r > 0){ + return r; + } + /* compare with list of supported ec_curves */ + for (j = 0; priv->ec_curves[j].oid.value[0] >= 0; j++) { + if (sc_compare_oid(&priv->ec_curves[j].oid, &oid)) { + sc_log(card->ctx, "Matched EC oid %s (%d)", + sc_dump_oid(&oid), j); + key_info->u.ec.oid = priv->ec_curves[j].oid; + key_info->u.ec.key_length = priv->ec_curves[j].size; + break; + } + } + /* We did not match the OID */ + if (priv->ec_curves[j].oid.value[0] < 0) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + } + break; + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +int +_pgp_handle_curve25519(sc_card_t *card, + sc_cardctl_openpgp_key_gen_store_info_t *key_info, unsigned int do_num) +{ + if (!card || !key_info) + return 0; + if (!sc_compare_oid(&key_info->u.ec.oid, &curve25519_oid) && + !sc_compare_oid(&key_info->u.ec.oid, &X25519_oid)) + return 0; + + /* CKM_XEDDSA supports both Sign and Derive, but + * OpenPGP card supports only derivation using these + * keys as far as I know */ + _sc_card_add_xeddsa_alg(card, key_info->u.ec.key_length, + SC_ALGORITHM_ECDH_CDH_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN, 0, &key_info->u.ec.oid); + sc_log(card->ctx, "DO %uX: Added XEDDSA algorithm (%d), mod_len = %zu", + do_num, SC_ALGORITHM_XEDDSA, key_info->u.ec.key_length); + return 1; +} + +int +_pgp_add_algo(sc_card_t *card, sc_cardctl_openpgp_key_gen_store_info_t *key_info, unsigned int do_num) +{ + unsigned long flags = 0, ext_flags = 0; + + if (!card || !key_info) + return 0; + + /* [RFC 4880], [draft-ietf-openpgp-crypto-refresh] */ + switch (key_info->algorithm) { + case SC_OPENPGP_KEYALGO_RSA: + /* OpenPGP card spec 1.1 & 2.x, section 7.2.9 & 7.2.10 / + * v3.x section 7.2.11 & 7.2.12 */ + flags = SC_ALGORITHM_RSA_PAD_PKCS1 | + SC_ALGORITHM_RSA_HASH_NONE | + SC_ALGORITHM_ONBOARD_KEY_GEN; /* key gen on card */ + + _sc_card_add_rsa_alg(card, key_info->u.rsa.modulus_len, flags, 0); + sc_log(card->ctx, "DO %uX: Added RSA algorithm, mod_len = %" + SC_FORMAT_LEN_SIZE_T"u", + do_num, key_info->u.rsa.modulus_len); + break; + case SC_OPENPGP_KEYALGO_ECDH: + /* The montgomery curve (curve25519) needs to go through + * different paths, otherwise we handle it as a normal EC key */ + if (_pgp_handle_curve25519(card, key_info, do_num)) + break; + /* fall through */ + case SC_OPENPGP_KEYALGO_ECDSA: + /* v3.0+: ECC [RFC 4880 & 6637] */ + + /* Allow curve to be used by both ECDH and ECDSA. + * pgp_init set these flags the same way */ + flags = SC_ALGORITHM_ECDH_CDH_RAW; + flags |= SC_ALGORITHM_ECDSA_RAW; + flags |= SC_ALGORITHM_ECDSA_HASH_NONE; + flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; + ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE; + + _sc_card_add_ec_alg(card, key_info->u.ec.key_length, flags, ext_flags, + &key_info->u.ec.oid); + sc_log(card->ctx, "DO %uX: Added EC algorithm (%d), mod_len = %zu" , + do_num, key_info->algorithm, key_info->u.ec.key_length); + break; + case SC_OPENPGP_KEYALGO_EDDSA: + /* EdDSA from draft-ietf-openpgp-rfc4880bis-08 */ + /* Handle Yubikey bug, that in DO FA curve25519 has EDDSA algo */ + if (_pgp_handle_curve25519(card, key_info, do_num)) + break; + _sc_card_add_eddsa_alg(card, key_info->u.ec.key_length, + SC_ALGORITHM_EDDSA_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN, 0, &key_info->u.ec.oid); + + sc_log(card->ctx, "DO %uX: Added EDDSA algorithm (%d), mod_len = %zu" , + do_num, key_info->algorithm, key_info->u.ec.key_length); + break; + default: + sc_log(card->ctx, "DO %uX: Unknown algorithm ID (%d)" , + do_num, key_info->algorithm); + /* return "false" if we do not understand algo */ + return 0; + } + /* return true */ + return 1; +} + +#ifdef ENABLE_OPENSSL +static int +pgp_decode_kdf_do(sc_card_t *card, struct pgp_priv_data *priv) +{ + pgp_pin_kdf_info_t *pin_kdf_info = (pgp_pin_kdf_info_t *)calloc(1, sizeof(pgp_pin_kdf_info_t)); + int r = SC_ERROR_OUT_OF_MEMORY; + pgp_blob_t *kdf_blob; + const uint8_t *data; + size_t data_len; + size_t tag_len; + const uint8_t *p; + pgp_blob_t *blob; + + if (!pin_kdf_info) { + return r; + } + + r = SC_ERROR_INVALID_DATA; + priv->pin_kdf_info = NULL; + if ((pgp_get_blob(card, priv->mf, DO_KDF, &kdf_blob) != SC_SUCCESS) || (kdf_blob->len < 3)) { + sc_log(card->ctx, "KDF DO does not exists or contains invalid data but card claims to support it"); + goto out; + } + + data = kdf_blob->data; + data_len = kdf_blob->len; + p = sc_asn1_find_tag(card->ctx, data, data_len, 0x81, &tag_len); + if (!p) { + sc_log(card->ctx, "KDF algorithm byte not found"); + goto out; + } + if (tag_len != 1) { + sc_log(card->ctx, "Unexpected KDF algorithm byte length, expects 1, got %" SC_FORMAT_LEN_SIZE_T "u", tag_len); + goto out; + } + + if (*p != 0x3) { + if (*p == 0x0) { + r = SC_SUCCESS; + sc_log(card->ctx, "KDF derived PIN format disabled"); + } else { + sc_log(card->ctx, "Unknown KDF algorithm ID %x", *p); + } + goto out; + } + + p = sc_asn1_find_tag(card->ctx, data, data_len, 0x82, &tag_len); + if (!p) { + sc_log(card->ctx, "KDF hash algorithm byte not found"); + goto out; + } + if (tag_len != 1) { + sc_log(card->ctx, "Unexpected KDF hash algorithm byte length, expects 1, got %" SC_FORMAT_LEN_SIZE_T "u", tag_len); + goto out; + } + + switch (*p) { + case 0x08: + pin_kdf_info->hash_algo = "SHA256"; + break; + case 0x0A: + pin_kdf_info->hash_algo = "SHA512"; + break; + default: + sc_log(card->ctx, "Unknown KDF hash algorithm ID %x", *p); + goto out; + } + + p = sc_asn1_find_tag(card->ctx, data, data_len, 0x83, &tag_len); + if (!p) { + sc_log(card->ctx, "KDF iteration count not found"); + goto out; + } + if (tag_len != 4) { + sc_log(card->ctx, "Unexpected KDF iteration count length, expects 4, got %" SC_FORMAT_LEN_SIZE_T "u", tag_len); + goto out; + } + pin_kdf_info->iterations = (uint32_t)bebytes2ulong(p); + + r = pgp_get_blob(card, kdf_blob, 0x84, &blob); + if (r != SC_SUCCESS) { + sc_log(card->ctx, "Failed to get salt bytes of user password"); + goto out; + } + pin_kdf_info->userpw_salt = blob; + + r = pgp_get_blob(card, kdf_blob, 0x86, &blob); + if (r != SC_SUCCESS) { + sc_log(card->ctx, "Failed to get salt bytes of admin password"); + goto out; + } + pin_kdf_info->adminpw_salt = blob; + + sc_log(card->ctx, "KDF derived PIN format enabled"); + priv->pin_kdf_info = pin_kdf_info; + return r; +out: + free(pin_kdf_info); + return r; +} +#endif /* ENABLE_OPENSSL */ + +/** + * Internal: get features of the card: capabilities, ... + */ +static int +pgp_get_card_features(sc_card_t *card) +{ + struct pgp_priv_data *priv = DRVDATA(card); + u8 *hist_bytes = card->reader->atr_info.hist_bytes; + size_t hist_bytes_len = card->reader->atr_info.hist_bytes_len; + unsigned int i; + pgp_blob_t *blob, *blob6e, *blob73, *blobfa; + int handled_algos = 0; + + LOG_FUNC_CALLED(card->ctx); + + /* parse card capabilities from historical bytes in ATR */ + if (hist_bytes_len > 0) { + /* category indicator 0x00, 0x10 or 0x80 => compact TLV (ISO) */ + switch (hist_bytes[0]) { + case 0x00: + if (hist_bytes_len > 4) { + pgp_parse_hist_bytes(card, hist_bytes+1, hist_bytes_len-4); + } + break; + case 0x80: + if (hist_bytes_len > 1) { + pgp_parse_hist_bytes(card, hist_bytes+1, hist_bytes_len-1); + } + break; + case 0x10: + if (hist_bytes_len > 2) { + pgp_parse_hist_bytes(card, hist_bytes+2, hist_bytes_len-2); + } + break; + } } + /* v1.1 does not support lifecycle via ACTIVATE & TERMINATE: set default */ + priv->ext_caps &= ~EXT_CAP_LCS; + if (priv->bcd_version >= OPENPGP_CARD_2_0) { /* get card capabilities from "historical bytes" DO */ if ((pgp_get_blob(card, priv->mf, 0x5f52, &blob) >= 0) && (blob->data != NULL) && (blob->data[0] == 0x00)) { - /* find beginning of "interesting" bytes */ - for (i = 0; (i < blob->len) && (blob->data[i] != 0x73); i++) - ; - /* IS07816-4 hist bytes 3rd function table */ - if ((blob->data[i] == 0x73) && (blob->len > i+3)) { - /* bit 0x40 in byte 3 of TL 0x73 means "extended Le/Lc" */ - if (blob->data[i+3] & 0x40) { - card->caps |= SC_CARD_CAP_APDU_EXT; - priv->ext_caps |= EXT_CAP_APDU_EXT; - } - /* bit 0x80 in byte 3 of TL 0x73 means "Command chaining" */ - if (blob->data[i+3] & 0x80) - priv->ext_caps |= EXT_CAP_CHAINING; + if (blob->len > 4) { + pgp_parse_hist_bytes(card, blob->data+1, blob->len-4); } /* get card status from historical bytes status indicator */ - if ((blob->data[0] == 0x00) && (blob->len >= 4)) + if ((blob->data[0] == 0x00) && (blob->len >= 4)) { priv->state = blob->data[blob->len-3]; + /* state not CARD_STATE_UNKNOWN => LCS supported */ + if (priv->state != CARD_STATE_UNKNOWN) + priv->ext_caps |= EXT_CAP_LCS; + } + } + } + + if (priv->bcd_version >= OPENPGP_CARD_3_1) { + card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; + } + + if (priv->bcd_version >= OPENPGP_CARD_3_4) { + /* Parse supported algorithms from Algorithm Information DO + * see OpenPGP card spec 3.4 section 4.4.3.11 */ + if (pgp_get_blob(card, priv->mf, 0x00fa, &blobfa) >= 0) { + pgp_blob_t *child; + pgp_enumerate_blob(card, blobfa); + /* There will be multiple children with the same ID, but + * different algos, so we need to iterate over all of them */ + for (child = blobfa->files; child; child = child->next) { + if ((child->id < 0x00c1) || (child->id > 0x00c3)) + continue; + sc_cardctl_openpgp_key_gen_store_info_t key_info; + if (pgp_parse_algo_attr_blob(card, child, &key_info) >= 0) + handled_algos += _pgp_add_algo(card, &key_info, 0x00fa); + } } } + /* v1.1 & v2.x: special DOs are limited to 254 bytes */ + priv->max_specialDO_size = 254; + if ((pgp_get_blob(card, priv->mf, 0x006e, &blob6e) >= 0) && (pgp_get_blob(card, blob6e, 0x0073, &blob73) >= 0)) { /* get "extended capabilities" DO */ if ((pgp_get_blob(card, blob73, 0x00c0, &blob) >= 0) && (blob->data != NULL) && (blob->len > 0)) { - /* in v2.0 bit 0x04 in first byte means "algorithm attributes changeable */ + if ((blob->data[0] & 0x01) && (priv->bcd_version >= OPENPGP_CARD_3_3)) { + priv->ext_caps |= EXT_CAP_KDF_DO; + } + /* v2.0+: bit 0x04 in first byte means "algorithm attributes changeable" */ if ((blob->data[0] & 0x04) && - (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK)) + (priv->bcd_version >= OPENPGP_CARD_2_0)) priv->ext_caps |= EXT_CAP_ALG_ATTR_CHANGEABLE; /* bit 0x08 in first byte means "support for private use DOs" */ if (blob->data[0] & 0x08) @@ -541,19 +1009,41 @@ pgp_get_card_features(sc_card_t *card) card->caps |= SC_CARD_CAP_RNG; priv->ext_caps |= EXT_CAP_GET_CHALLENGE; } - /* in v2.0 bit 0x80 in first byte means "support Secure Messaging" */ + /* v2.0+: bit 0x80 in first byte means "support Secure Messaging" */ if ((blob->data[0] & 0x80) && - (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK)) + (priv->bcd_version >= OPENPGP_CARD_2_0)) priv->ext_caps |= EXT_CAP_SM; if ((priv->bcd_version >= OPENPGP_CARD_2_0) && (blob->len >= 10)) { - /* max. challenge size is at bytes 3-4 */ + /* v2.0+: max. challenge size is at bytes 3-4 */ priv->max_challenge_size = bebytes2ushort(blob->data + 2); - /* max. cert size it at bytes 5-6 */ + /* v2.0+: max. cert size it at bytes 5-6 */ priv->max_cert_size = bebytes2ushort(blob->data + 4); - /* max. send/receive sizes are at bytes 7-8 resp. 9-10 */ - card->max_send_size = bebytes2ushort(blob->data + 6); - card->max_recv_size = bebytes2ushort(blob->data + 8); + + if (priv->bcd_version < OPENPGP_CARD_3_0) { + /* v2.x: SM algorithm is at byte 2: 0 == 3DES */ + priv->sm_algo = blob->data[1]; + if ((priv->sm_algo == SM_ALGO_NONE) && (priv->ext_caps & EXT_CAP_SM)) + priv->sm_algo = SM_ALGO_3DES; + + /* v2.x: max. send/receive sizes are at bytes 7-8 resp. 9-10 */ + card->max_send_size = bebytes2ushort(blob->data + 6); + card->max_recv_size = bebytes2ushort(blob->data + 8); + } + else { + /* v3.0+: SM algorithm is at byte 2: 0 == UNKNOWN */ + priv->sm_algo = blob->data[1]; + if ((priv->sm_algo == SM_ALGO_NONE) && (priv->ext_caps & EXT_CAP_SM)) + priv->sm_algo = SM_ALGO_UNKNOWN; + + /* v3.0+: max. size of special DOs is at bytes 7-8 */ + priv->max_specialDO_size = bebytes2ushort(blob->data + 6); + } + if (priv->bcd_version >= OPENPGP_CARD_3_3 && (blob->len >= 10)) { + /* v3.3+: MSE for key numbers 2(DEC) and 3(AUT) supported */ + if (blob->data[9]) + priv->ext_caps |= EXT_CAP_MSE; + } } } @@ -564,34 +1054,58 @@ pgp_get_card_features(sc_card_t *card) card->max_pin_len = blob->data[1]; } - /* get supported algorithms & key lengths from "algorithm attributes" DOs */ + if (priv->bcd_version >= OPENPGP_CARD_3_0) { + /* v3.0+: get length info from "extended length information" DO */ + if ((pgp_get_blob(card, blob6e, 0x7f66, &blob) >= 0) && + (blob->data != NULL) && (blob->len >= 8)) { + /* kludge: treat as SIMPLE DO and use appropriate offsets */ + card->max_send_size = bebytes2ushort(blob->data + 2); + card->max_recv_size = bebytes2ushort(blob->data + 6); + } + } + +#ifdef ENABLE_OPENSSL + if (priv->ext_caps & EXT_CAP_KDF_DO) { + int r = pgp_decode_kdf_do(card, priv); + if (r != SC_SUCCESS) { + sc_log(card->ctx, "Failed to decode KDF DO: %s", sc_strerror(r)); + } + } +#endif /* ENABLE_OPENSSL */ + + /* if we found at least one usable algo, let's skip other ways to find them */ + if (handled_algos) { + sc_log(card->ctx, "Algo list populated from Algorithm Information DO"); + LOG_FUNC_RETURN(card->ctx, handled_algos); + } + + /* get _current_ algorithms & key lengths from "algorithm attributes" DOs + * + * All available algorithms should be already provided by pgp_init. However, if another + * algorithm is found in the "algorithm attributes" DOs, it is supported by the card as + * well and therefore added + * see OpenPGP card spec 1.1 & 2.x section 4.3.3.6 / v3.x section 4.4.3.7 */ for (i = 0x00c1; i <= 0x00c3; i++) { - unsigned long flags; + sc_cardctl_openpgp_key_gen_store_info_t key_info; - /* Is this correct? */ - /* OpenPGP card spec 1.1 & 2.0, section 7.2.9 & 7.2.10 */ - flags = SC_ALGORITHM_RSA_PAD_PKCS1; - flags |= SC_ALGORITHM_RSA_HASH_NONE; - /* Can be generated in card */ - flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; + sc_log(card->ctx, "Parsing algorithm attributes DO %uX" , i); + /* OpenPGP card spec 1.1 & 2.x section 4.3.3.6 / v3.x section 4.4.3.7 */ if ((pgp_get_blob(card, blob73, i, &blob) >= 0) && - (blob->data != NULL) && (blob->len >= 4)) { - if (blob->data[0] == 0x01) { /* Algorithm ID [RFC4880]: RSA */ - unsigned int keylen = bebytes2ushort(blob->data + 1); /* Measured in bit */ - - _sc_card_add_rsa_alg(card, keylen, flags, 0); - } + (pgp_parse_algo_attr_blob(card, blob, &key_info) >= 0)) { + if (!_pgp_add_algo(card, &key_info, i)) + LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, "Cannot add algorithm"); } } + } - return SC_SUCCESS; + LOG_FUNC_RETURN(card->ctx, handled_algos); } /** - * ABI: terminate driver. + * ABI: terminate driver & free private data. */ static int pgp_finish(sc_card_t *card) @@ -601,7 +1115,10 @@ pgp_finish(sc_card_t *card) if (priv != NULL) { /* delete fake file hierarchy */ - pgp_iterate_blobs(priv->mf, 99, pgp_free_blob); + pgp_free_blobs(priv->mf); + + /* delete kdf do related data if exists */ + free(priv->pin_kdf_info); /* delete private data */ free(priv); @@ -625,7 +1142,7 @@ pgp_set_blob(pgp_blob_t *blob, const u8 *data, size_t len) blob->status = 0; if (len > 0) { - void *tmp = calloc(len, 1); + void *tmp = calloc(1, len); if (tmp == NULL) return SC_ERROR_OUT_OF_MEMORY; @@ -648,7 +1165,7 @@ pgp_set_blob(pgp_blob_t *blob, const u8 *data, size_t len) * The Access Control is derived from the DO access permission. **/ static void -pgp_attach_acl(sc_card_t *card, sc_file_t *file, struct do_info *info) +pgp_attach_acl(sc_card_t *card, sc_file_t *file, pgp_do_info_t *info) { unsigned int method = SC_AC_NONE; unsigned long key_ref = SC_AC_KEY_REF_NONE; @@ -725,7 +1242,7 @@ pgp_new_blob(sc_card_t *card, pgp_blob_t *parent, unsigned int file_id, if ((blob = calloc(1, sizeof(pgp_blob_t))) != NULL) { struct pgp_priv_data *priv = DRVDATA(card); - struct do_info *info; + pgp_do_info_t *info; blob->file = file; @@ -749,13 +1266,15 @@ pgp_new_blob(sc_card_t *card, pgp_blob_t *parent, unsigned int file_id, *p = blob; } else { - u8 id_str[2]; + char path[10] = "0000"; /* long enough */ /* no parent: set file's path = file's id */ - /* FIXME sc_format_path expects an hex string of a file - * identifier. ushort2bebytes instead delivers a two bytes binary - * string */ - sc_format_path((char *) ushort2bebytes(id_str, file_id), &blob->file->path); + if (4 != snprintf(path, sizeof(path), "%04X", file_id & 0xFFFF)) { + free(blob); + return NULL; + } + + sc_format_path(path, &blob->file->path); } /* find matching DO info: set file type depending on it */ @@ -799,23 +1318,21 @@ pgp_free_blob(pgp_blob_t *blob) /** - * Internal: iterate through the blob tree, calling a function for each blob. + * Internal: iterate through the blob tree, calling pgp_free_blob for each blob. */ static void -pgp_iterate_blobs(pgp_blob_t *blob, int level, void (*func)()) +pgp_free_blobs(pgp_blob_t *blob) { if (blob) { - if (level > 0) { - pgp_blob_t *child = blob->files; + pgp_blob_t *child = blob->files; - while (child != NULL) { - pgp_blob_t *next = child->next; + while (child != NULL) { + pgp_blob_t *next = child->next; - pgp_iterate_blobs(child, level-1, func); - child = next; - } + pgp_free_blobs(child); + child = next; } - func(blob); + pgp_free_blob(blob); } } @@ -834,7 +1351,7 @@ pgp_read_blob(sc_card_t *card, pgp_blob_t *blob) return blob->status; if (blob->info->get_fn) { /* readable, top-level DO */ - u8 buffer[2048]; + u8 buffer[MAX_OPENPGP_DO_SIZE]; size_t buf_len = sizeof(buffer); int r = SC_SUCCESS; @@ -851,7 +1368,7 @@ pgp_read_blob(sc_card_t *card, pgp_blob_t *blob) blob->id == DO_AUTH_SYM || blob->id == DO_SIGN_SYM || blob->id == DO_ENCR_SYM)) { - buf_len = MAXLEN_RESP_PUBKEY_GNUK; + buf_len = MIN(MAXLEN_RESP_PUBKEY_GNUK, sizeof(buffer)); } r = blob->info->get_fn(card, blob->id, buffer, buf_len); @@ -869,7 +1386,7 @@ pgp_read_blob(sc_card_t *card, pgp_blob_t *blob) } -/* +/** * Internal: enumerate contents of a data blob. * The OpenPGP card has a TLV encoding according ASN.1 BER-encoding rules. */ @@ -878,6 +1395,7 @@ pgp_enumerate_blob(sc_card_t *card, pgp_blob_t *blob) { const u8 *in; int r; + sc_file_t *file = NULL; if (blob->files != NULL) return SC_SUCCESS; @@ -893,11 +1411,19 @@ pgp_enumerate_blob(sc_card_t *card, pgp_blob_t *blob) const u8 *data = in; pgp_blob_t *new; + if (!in) + return SC_ERROR_OBJECT_NOT_VALID; + r = sc_asn1_read_tag(&data, blob->len - (in - blob->data), &cla, &tag, &len); - if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "Unexpected end of contents\n"); + if (r == SC_ERROR_INVALID_ASN1_OBJECT) { + sc_log(card->ctx, "Invalid ASN.1 object"); + return SC_ERROR_OBJECT_NOT_VALID; + } + /* Check for unknown error, or empty data */ + if (((r < 0) && (r != SC_ERROR_ASN1_END_OF_CONTENTS)) || + (data == NULL)) { + sc_log(card->ctx, "Unexpected end of contents"); return SC_ERROR_OBJECT_NOT_VALID; } @@ -906,6 +1432,15 @@ pgp_enumerate_blob(sc_card_t *card, pgp_blob_t *blob) cla <<= 8; } tag |= cla; + /* Check for length mismatch */ + if ((r == SC_ERROR_ASN1_END_OF_CONTENTS) || + (data + len > blob->data + blob->len)) { + // Check if it is not known Yubikey 5 issue + if ((tag != blob->id) || (tag != 0xfa)) { + sc_log(card->ctx, "Unexpected end of contents"); + return SC_ERROR_OBJECT_NOT_VALID; + } + } /* Awful hack for composite DOs that have * a TLV with the DO's id encompassing the @@ -917,9 +1452,15 @@ pgp_enumerate_blob(sc_card_t *card, pgp_blob_t *blob) /* create fake file system hierarchy by * using constructed DOs as DF */ - if ((new = pgp_new_blob(card, blob, tag, sc_file_new())) == NULL) + file = sc_file_new(); + if ((new = pgp_new_blob(card, blob, tag, file)) == NULL) { + sc_file_free(file); + return SC_ERROR_OUT_OF_MEMORY; + } + if (pgp_set_blob(new, data, len) != SC_SUCCESS) { + sc_file_free(file); return SC_ERROR_OUT_OF_MEMORY; - pgp_set_blob(new, data, len); + } in = data + len; } @@ -989,7 +1530,7 @@ pgp_seek_blob(sc_card_t *card, pgp_blob_t *root, unsigned int id, for (child = root->files; child; child = child->next) { /* The DO of SIMPLE type or the DO holding certificate * does not contain children */ - if (child->info->type == SIMPLE || child->id == DO_CERT) + if ((child->info && child->info->type == SIMPLE) || child->id == DO_CERT) continue; r = pgp_seek_blob(card, child, id, ret); if (r == 0) @@ -1027,11 +1568,11 @@ pgp_find_blob(sc_card_t *card, unsigned int tag) /** * Internal: get info for a specific tag. */ -static struct do_info * +static pgp_do_info_t * pgp_get_info_by_tag(sc_card_t *card, unsigned int tag) { struct pgp_priv_data *priv = DRVDATA(card); - struct do_info *info; + pgp_do_info_t *info; for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++) if (tag == info->id) @@ -1063,7 +1604,7 @@ pgp_strip_path(sc_card_t *card, const sc_path_t *path) /** - * ABI: SELECT FILE. + * ABI: ISO 7816-4 SELECT FILE - search given file & make it the currently selected one. */ static int pgp_select_file(sc_card_t *card, const sc_path_t *path, sc_file_t **ret) @@ -1104,7 +1645,7 @@ pgp_select_file(sc_card_t *card, const sc_path_t *path, sc_file_t **ret) LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } - /* ignore explicitely mentioned MF at the path's beginning */ + /* ignore explicitly mentioned MF at the path's beginning */ path_start = pgp_strip_path(card, path); /* starting with the MF ... */ @@ -1114,7 +1655,7 @@ pgp_select_file(sc_card_t *card, const sc_path_t *path, sc_file_t **ret) unsigned int id = bebytes2ushort(path->value + n); int r = pgp_get_blob(card, blob, id, &blob); - /* This file ID is refered when importing key&certificate via pkcs15init, like above. + /* This file ID is referred when importing key&certificate via pkcs15init, like above. * We pretend to successfully find this inexistent file. */ if (id == 0x4402 || id == 0x5f48) { if (ret == NULL) @@ -1143,7 +1684,7 @@ pgp_select_file(sc_card_t *card, const sc_path_t *path, sc_file_t **ret) /** - * ABI: LIST FILES. + * ABI: ISO 7816-4 LIST FILES - enumerate all files in current DF. */ static int pgp_list_files(sc_card_t *card, u8 *buf, size_t buflen) @@ -1180,11 +1721,34 @@ pgp_list_files(sc_card_t *card, u8 *buf, size_t buflen) /** - * ABI: READ BINARY. + * ABI: ISO 7816-4 GET CHALLENGE - generate random byte sequence. + */ +static int +pgp_get_challenge(struct sc_card *card, u8 *rnd, size_t len) +{ + struct pgp_priv_data *priv; + + LOG_FUNC_CALLED(card->ctx); + + priv = DRVDATA(card); + if (0 == (priv->ext_caps & EXT_CAP_GET_CHALLENGE)) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + + if (priv->max_challenge_size > 0 && len > priv->max_challenge_size) { + len = priv->max_challenge_size; + } + + LOG_FUNC_RETURN(card->ctx, iso_ops->get_challenge(card, rnd, len)); +} + + +/** + * ABI: ISO 7816-4 READ BINARY - read data from currently selected EF. */ static int pgp_read_binary(sc_card_t *card, unsigned int idx, - u8 *buf, size_t count, unsigned long flags) + u8 *buf, size_t count, unsigned long *flags) { struct pgp_priv_data *priv = DRVDATA(card); pgp_blob_t *blob; @@ -1216,18 +1780,7 @@ pgp_read_binary(sc_card_t *card, unsigned int idx, /** - * ABI: WRITE BINARY. - */ -static int -pgp_write_binary(sc_card_t *card, unsigned int idx, - const u8 *buf, size_t count, unsigned long flags) -{ - LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); -} - - -/** - * Internal: get public key from card: as DF + sub-wEFs. + * Internal: get public key from card - as DF + sub-wEFs. */ static int pgp_get_pubkey(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len) @@ -1259,36 +1812,124 @@ pgp_get_pubkey(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len) /** - * Internal: get public key from card: as one wEF. + * Internal: get public key from card - as one wEF. */ static int pgp_get_pubkey_pem(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len) { struct pgp_priv_data *priv = DRVDATA(card); - pgp_blob_t *blob, *mod_blob, *exp_blob; - sc_pkcs15_pubkey_t pubkey; - u8 *data; - size_t len; + pgp_blob_t *blob, *mod_blob, *exp_blob, *pubkey_blob, *blob6e, *blob73, *aa_blob; + sc_pkcs15_pubkey_t p15pubkey; + sc_cardctl_openpgp_key_gen_store_info_t key_info; + unsigned int aa_tag = 0; + u8 *data = NULL; + size_t len = 0; int r; sc_log(card->ctx, "called, tag=%04x\n", tag); + memset(&p15pubkey, 0, sizeof(p15pubkey)); if ((r = pgp_get_blob(card, priv->mf, tag & 0xFFFE, &blob)) < 0 - || (r = pgp_get_blob(card, blob, 0x7F49, &blob)) < 0 - || (r = pgp_get_blob(card, blob, 0x0081, &mod_blob)) < 0 - || (r = pgp_get_blob(card, blob, 0x0082, &exp_blob)) < 0 - || (r = pgp_read_blob(card, mod_blob)) < 0 - || (r = pgp_read_blob(card, exp_blob)) < 0) + || (r = pgp_get_blob(card, blob, 0x7F49, &blob)) < 0) LOG_TEST_RET(card->ctx, r, "error getting elements"); - memset(&pubkey, 0, sizeof(pubkey)); - pubkey.algorithm = SC_ALGORITHM_RSA; - pubkey.u.rsa.modulus.data = mod_blob->data; - pubkey.u.rsa.modulus.len = mod_blob->len; - pubkey.u.rsa.exponent.data = exp_blob->data; - pubkey.u.rsa.exponent.len = exp_blob->len; + /* RSA */ + if ((r = pgp_get_blob(card, blob, 0x0081, &mod_blob)) >= 0 + && (r = pgp_get_blob(card, blob, 0x0082, &exp_blob)) >= 0 + && (r = pgp_read_blob(card, mod_blob)) >= 0 + && (r = pgp_read_blob(card, exp_blob)) >= 0) { + + p15pubkey.algorithm = SC_ALGORITHM_RSA; + p15pubkey.u.rsa.modulus.data = mod_blob->data; + p15pubkey.u.rsa.modulus.len = mod_blob->len; + p15pubkey.u.rsa.exponent.data = exp_blob->data; + p15pubkey.u.rsa.exponent.len = exp_blob->len; + r = sc_pkcs15_encode_pubkey(card->ctx, &p15pubkey, &data, &len); + } + /* ECC */ + else if ((r = pgp_get_blob(card, blob, 0x0086, &pubkey_blob)) >= 0 + && (r = pgp_read_blob(card, pubkey_blob)) >= 0) { + + switch(tag & 0xFFFE) { + case DO_SIGN: aa_tag = 0x00C1; break; + case DO_ENCR: aa_tag = 0x00C2; break; + case DO_AUTH: aa_tag = 0x00C3; break; + default: r = SC_ERROR_INCORRECT_PARAMETERS; + } + + /* Get EC parameters from Algorithm Attribute if present */ + + if (aa_tag && ((r = pgp_get_blob(card, priv->mf, 0x006e, &blob6e)) >= 0) && + ((r = pgp_get_blob(card, blob6e, 0x0073, &blob73)) >= 0) && + ((r = pgp_get_blob(card, blob73, aa_tag, &aa_blob)) >= 0) && + ((r = pgp_parse_algo_attr_blob(card, aa_blob, &key_info)) >= 0)) { + switch (key_info.algorithm) { + case SC_OPENPGP_KEYALGO_EDDSA: + /* In EDDSA key case we do not have to care about OIDs + * as we support only one for now */ + p15pubkey.algorithm = SC_ALGORITHM_EDDSA; + p15pubkey.u.ec.ecpointQ.value = pubkey_blob->data; + p15pubkey.u.ec.ecpointQ.len = pubkey_blob->len; + /* PKCS#11 3.0: 2.3.5 Edwards EC public keys only support the use + * of the curveName selection to specify a curve name as defined + * in [RFC 8032] */ + r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, &p15pubkey, &data, &len); + break; + case SC_OPENPGP_KEYALGO_ECDH: + /* This yields either EC(DSA) key or EC_MONTGOMERY (curve25519) key */ + if (sc_compare_oid(&key_info.u.ec.oid, &curve25519_oid)) { + p15pubkey.algorithm = SC_ALGORITHM_XEDDSA; + p15pubkey.u.ec.ecpointQ.value = pubkey_blob->data; + p15pubkey.u.ec.ecpointQ.len = pubkey_blob->len; + /* PKCS#11 3.0 2.3.7 Montgomery EC public keys only support + * the use of the curveName selection to specify a curve + * name as defined in [RFC7748] */ + /* XXX only curve25519 supported now. Theoretically could be + * also curve448 or OIDs */ + + r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, &p15pubkey, &data, &len); + break; + } + /* fall through */ + case SC_OPENPGP_KEYALGO_ECDSA: + if ((r = sc_encode_oid(card->ctx, &key_info.u.ec.oid, + &p15pubkey.u.ec.params.der.value, + &p15pubkey.u.ec.params.der.len)) == 0) { + p15pubkey.algorithm = SC_ALGORITHM_EC; + p15pubkey.u.ec.ecpointQ.value = pubkey_blob->data; + p15pubkey.u.ec.ecpointQ.len = pubkey_blob->len; + p15pubkey.u.ec.params.type = 1; + r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, &p15pubkey, &data, &len); + } else { + sc_log(card->ctx, "Unable to encode EC curve OID from algorithm info"); + } + break; + default: + sc_log(card->ctx, "Unknown algorithm ID received (%d)", key_info.algorithm); + break; + } + } else { + sc_log(card->ctx, "Unable to find Algorithm Attribute for EC curve OID"); + } + } else { + LOG_TEST_RET(card->ctx, r, "error getting elements"); + } + + /* clean up anything we may have set in p15pubkey that can not be freed */ + if (p15pubkey.algorithm == SC_ALGORITHM_RSA) { + p15pubkey.u.rsa.modulus.data = NULL; + p15pubkey.u.rsa.modulus.len = 0; + p15pubkey.u.rsa.exponent.data = NULL; + p15pubkey.u.rsa.exponent.len = 0; + } else if (p15pubkey.algorithm == SC_ALGORITHM_EC || + p15pubkey.algorithm == SC_ALGORITHM_EDDSA || + p15pubkey.algorithm == SC_ALGORITHM_XEDDSA) { + p15pubkey.u.ec.ecpointQ.value = NULL; + p15pubkey.u.ec.ecpointQ.len = 0; + /* p15pubkey.u.ec.params.der and named_curve will be freed by sc_pkcs15_erase_pubkey */ + } + sc_pkcs15_erase_pubkey(&p15pubkey); - r = sc_pkcs15_encode_pubkey(card->ctx, &pubkey, &data, &len); LOG_TEST_RET(card->ctx, r, "public key encoding failed"); if (len > buf_len) @@ -1301,7 +1942,49 @@ pgp_get_pubkey_pem(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len) /** - * ABI: GET DATA. + * Internal: SELECT DATA - selects a DO within a DO tag with several instances + * (supported since OpenPGP Card v3 for DO 7F21 only, see section 7.2.5 of the specification; + * this enables us to store multiple Card holder certificates in DO 7F21) + * + * p1: number of an instance (DO 7F21: 0x00 for AUT, 0x01 for DEC and 0x02 for SIG) + */ +static int +pgp_select_data(sc_card_t *card, u8 p1) +{ + sc_apdu_t apdu; + u8 apdu_data[6]; + int r; + struct pgp_priv_data *priv = DRVDATA(card); + + LOG_FUNC_CALLED(card->ctx); + + if (priv->bcd_version < OPENPGP_CARD_3_0) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + + sc_log(card->ctx, "select data with: %u", p1); + + // create apdu data (taken from spec: SELECT DATA 7.2.5.) + apdu_data[0] = 0x60; + apdu_data[1] = 0x04; + apdu_data[2] = 0x5c; + apdu_data[3] = 0x02; + apdu_data[4] = 0x7f; + apdu_data[5] = 0x21; + + // apdu, cla, ins, p1, p2, data, datalen, resp, resplen + sc_format_apdu_ex(&apdu, 0x00, 0xA5, p1, 0x04, apdu_data, sizeof(apdu_data), NULL, 0); + + // transmit apdu + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "Card returned error"); + LOG_FUNC_RETURN(card->ctx, r); +} + + +/** + * ABI: ISO 7816-4 GET DATA - get contents of a DO. */ static int pgp_get_data(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len) @@ -1321,19 +2004,24 @@ pgp_get_data(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len) r = sc_check_sw(card, apdu.sw1, apdu.sw2); - /* For Gnuk card, if there is no certificate, it returns error instead of empty data. + /* Gnuk returns an error instead of empty data if there is no certificate or private DO. * So, for this case, we ignore error and consider success */ - if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND && card->type == SC_CARD_TYPE_OPENPGP_GNUK - && (tag == DO_CERT || tag == DO_PRIV1 || tag == DO_PRIV2 || tag == DO_PRIV3 || tag == DO_PRIV4)) { - r = SC_SUCCESS; - apdu.resplen = 0; + if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && + (tag == DO_CERT || + tag == DO_PRIV1 || + tag == DO_PRIV2 || + tag == DO_PRIV3 || + tag == DO_PRIV4)) { + if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) { + r = SC_SUCCESS; + apdu.resplen = 0; + } } LOG_TEST_RET(card->ctx, r, "Card returned error"); LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); } - /** * Internal: write certificate for Gnuk. */ @@ -1352,7 +2040,7 @@ gnuk_write_certificate(sc_card_t *card, const u8 *buf, size_t length) r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); /* Check response */ - LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "Certificate writing failed"); + LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); } /* Ref: gnuk_put_binary_libusb.py and gnuk_token.py in Gnuk source tree */ @@ -1368,7 +2056,7 @@ gnuk_write_certificate(sc_card_t *card, const u8 *buf, size_t length) i+1, i*256, plen); /* 1st chunk: P1 = 0x85, further chunks: P1 = chunk no */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, (i == 0) ? 0x85 : i, 0); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, (i == 0) ? 0x85 : (int)i, 0); apdu.flags |= SC_APDU_FLAGS_CHAINING; apdu.data = part; apdu.datalen = apdu.lc = plen; @@ -1409,7 +2097,7 @@ pgp_put_data_plain(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_ LOG_FUNC_CALLED(card->ctx); - /* Extended Header list (004D DO) needs a variant of PUT DATA command */ + /* Extended Header list (DO 004D) needs a variant of PUT DATA command */ if (tag == 0x004D) { ins = 0xDB; p1 = 0x3F; @@ -1438,30 +2126,27 @@ pgp_put_data_plain(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_ LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); /* check response */ r = sc_check_sw(card, apdu.sw1, apdu.sw2); - - if (r < 0) - LOG_FUNC_RETURN(card->ctx, r); + LOG_TEST_RET(card->ctx, r, "Card returned error"); LOG_FUNC_RETURN(card->ctx, (int)buf_len); } /** - * ABI: PUT DATA. + * ABI: ISO 7816-4 PUT DATA - write contents of a DO. */ static int pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len) { struct pgp_priv_data *priv = DRVDATA(card); pgp_blob_t *affected_blob = NULL; - struct do_info *dinfo = NULL; + pgp_do_info_t *dinfo = NULL; int r; LOG_FUNC_CALLED(card->ctx); - /* check if the tag is writable */ - if (priv->current->id != tag) - affected_blob = pgp_find_blob(card, tag); + /* Check if there is a blob for the given tag */ + affected_blob = pgp_find_blob(card, tag); /* Non-readable DOs have no represented blob, we have to check from pgp_get_info_by_tag */ if (affected_blob == NULL) @@ -1469,6 +2154,7 @@ pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len) else dinfo = affected_blob->info; + /* Make sure the DO exists and is writeable */ if (dinfo == NULL) { sc_log(card->ctx, "The DO %04X does not exist.", tag); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); @@ -1516,14 +2202,193 @@ pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len) LOG_FUNC_RETURN(card->ctx, (int)buf_len); } +#ifdef ENABLE_OPENSSL +/* KDF_ITERSALTED_S2K algorithm, a simplified version of openpgp_s2k from libgcrypt[1] + * that supports one-pass only and outputs the same key length as the hash length of + * the specified hash algorithm. + * [1]: https://github.com/gpg/libgcrypt/blob/libgcrypt-1.11.0/cipher/kdf.c#L32-L109 + */ +static int +kdf_itersalted_s2k(struct sc_context *ctx, const char *hash, const uint8_t *pin, size_t pinlen, const uint8_t *salt, size_t saltlen, uint32_t iterations, uint8_t **out, size_t *outlen) +{ + int r = SC_ERROR_INVALID_ARGUMENTS; + EVP_MD *md = NULL; + EVP_MD_CTX *hctx = NULL; + int keysize; + uint8_t *outkey = NULL; + size_t count; + int ret; + unsigned int outsize; + + if (!ctx || !hash || !pin || pinlen == 0 || !salt || saltlen != 8 || !out || !outlen) { + return r; + } + + md = sc_evp_md(ctx, hash); + if (md == NULL) { + return r; + } + + r = SC_ERROR_INTERNAL; + hctx = EVP_MD_CTX_new(); + if (hctx == NULL) { + goto out; + } + + if (!EVP_DigestInit(hctx, md)) { + goto out; + } + + keysize = EVP_MD_size(md); + if (keysize <= 0) { + goto out; + } + + outkey = (uint8_t *)sc_mem_secure_alloc(keysize); + if (outkey == NULL) { + r = SC_ERROR_NOT_ENOUGH_MEMORY; + goto out; + } + + count = iterations; + if (count < pinlen + saltlen) { + count = pinlen + saltlen; + } + + while (count >= pinlen + saltlen) { + ret = EVP_DigestUpdate(hctx, salt, saltlen); + if (!ret) { + goto out; + } + ret = EVP_DigestUpdate(hctx, pin, pinlen); + if (!ret) { + goto out; + } + count -= pinlen + saltlen; + } + + if (count <= saltlen) { + ret = EVP_DigestUpdate(hctx, salt, count); + } else { + ret = EVP_DigestUpdate(hctx, salt, saltlen); + if (!ret) { + goto out; + } + count -= saltlen; + ret = EVP_DigestUpdate(hctx, pin, count); + } + if (!ret) { + goto out; + } + + outsize = keysize; + ret = EVP_DigestFinal(hctx, outkey, &outsize); + if (ret && outsize == (unsigned int)keysize) { + r = SC_SUCCESS; + } +out: + if (r != SC_SUCCESS && outkey != NULL) { + sc_mem_secure_clear_free(outkey, keysize); + } + EVP_MD_CTX_free(hctx); + sc_evp_md_free(md); + if (r == SC_SUCCESS) { + *out = outkey; + *outlen = outsize; + } + return r; +} + +static int +pgp_kdf_do_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) +{ + int r = SC_ERROR_INVALID_ARGUMENTS; + struct pgp_priv_data *priv = DRVDATA(card); + const pgp_pin_kdf_info_t *const info = priv->pin_kdf_info; + uint8_t *pin1_derived = NULL; + uint8_t *pin2_derived = NULL; + size_t pin1_derived_len = 0; + size_t pin2_derived_len = 0; + const uint8_t *const pin1 = data->pin1.data; + const size_t pin1len = data->pin1.len; + const uint8_t *const pin2 = data->pin2.data; + const size_t pin2len = data->pin2.len; + const uint8_t *salt = NULL; + size_t saltlen = 0; + + switch (data->cmd) { + case SC_PIN_CMD_VERIFY: + case SC_PIN_CMD_CHANGE: + case SC_PIN_CMD_UNBLOCK: + break; + default: + LOG_FUNC_RETURN(card->ctx, iso_ops->pin_cmd(card, data)); + } + if (!info) { + return r; + } + + switch (data->pin_reference) { + case 0x81: + case 0x82: + salt = info->userpw_salt->data; + saltlen = info->userpw_salt->len; + break; + case 0x83: + salt = info->adminpw_salt->data; + saltlen = info->adminpw_salt->len; + break; + } + + switch (data->cmd) { + case SC_PIN_CMD_UNBLOCK: + case SC_PIN_CMD_CHANGE: + r = kdf_itersalted_s2k(card->ctx, info->hash_algo, pin2, pin2len, salt, saltlen, info->iterations, &pin2_derived, &pin2_derived_len); + if (r != SC_SUCCESS) { + break; + } + data->pin2.data = pin2_derived; + data->pin2.len = pin2_derived_len; + if (data->cmd == SC_PIN_CMD_UNBLOCK) { + salt = info->adminpw_salt->data; + saltlen = info->adminpw_salt->len; + } + // fallthrough + case SC_PIN_CMD_VERIFY: + r = kdf_itersalted_s2k(card->ctx, info->hash_algo, pin1, pin1len, salt, saltlen, info->iterations, &pin1_derived, &pin1_derived_len); + if (r != SC_SUCCESS) { + break; + } + data->pin1.data = pin1_derived; + data->pin1.len = pin1_derived_len; + break; + } + + if (r == SC_SUCCESS) { + r = iso_ops->pin_cmd(card, data); + } + if (pin1_derived) { + data->pin1.data = pin1; + data->pin1.len = pin1len; + sc_mem_secure_clear_free(pin1_derived, pin1_derived_len); + } + if (pin2_derived) { + data->pin2.data = pin2; + data->pin2.len = pin2len; + sc_mem_secure_clear_free(pin2_derived, pin2_derived_len); + } + LOG_FUNC_RETURN(card->ctx, r); +} +#endif /* ENABLE_OPENSSL */ /** - * ABI: PIN cmd: verify/change/unblock a PIN. + * ABI: ISO 7816-9 PIN CMD - verify/change/unblock a PIN. */ static int -pgp_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) +pgp_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) { struct pgp_priv_data *priv = DRVDATA(card); + struct sc_card_operations ops = {.pin_cmd = iso_ops->pin_cmd}; LOG_FUNC_CALLED(card->ctx); @@ -1533,7 +2398,7 @@ pgp_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) /* In general, the PIN Reference is extracted from the key-id, * for example, CHV0 -> Ref=0, CHV1 -> Ref=1. - * However, in the case of OpenGPG, the PIN Ref to compose APDU + * However, in the case of OpenPGP, the PIN Ref to compose APDU * must be 81, 82, 83. * So, if we receive Ref=1, Ref=2, we must convert to 81, 82... * In OpenPGP v1, the PINs are named CHV1, CHV2, CHV3. @@ -1567,7 +2432,7 @@ pgp_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) if (data->pin1.len == 0 && !(data->flags & SC_PIN_CMD_USE_PINPAD)) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, - "v2 cards don't support implicit old PIN for PIN change."); + "v2 cards don't support implicit old PIN for PIN change"); data->flags &= ~SC_PIN_CMD_IMPLICIT_CHANGE; } @@ -1587,19 +2452,86 @@ pgp_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) if (data->cmd == SC_PIN_CMD_UNBLOCK && data->pin2.len == 0 && !(data->flags & SC_PIN_CMD_USE_PINPAD)) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, - "new PIN must be provided for unblock operation."); + "new PIN must be provided for unblock operation"); /* ensure pin_reference is 81, 82, 83 */ - if (!(data->pin_reference == 0x81 || data->pin_reference == 0x82 || data->pin_reference == 0x83)) { + if (data->pin_reference < 0x81 || data->pin_reference > 0x83) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, - "key-id should be 1, 2, 3."); + "Invalid key ID; must be 1, 2, or 3"); + + /* emulate SC_PIN_CMD_GET_INFO command for cards not supporting it */ + if (data->cmd == SC_PIN_CMD_GET_INFO && (card->caps & SC_CARD_CAP_ISO7816_PIN_INFO) == 0) { + u8 c4data[10]; + int r; + + r = sc_get_data(card, 0x00c4, c4data, sizeof(c4data)); + LOG_TEST_RET(card->ctx, r, "reading CHV status bytes failed"); + + if (r != 7) + LOG_TEST_RET(card->ctx, SC_ERROR_OBJECT_NOT_VALID, + "CHV status bytes have unexpected length"); + + /* The definition of fields of DO C4 changed between OpenPGP + * card specification v1.1 and v2.0. There is no longer a separate + * CHV2 retry counter but only one retry counter for both PW1 mode 1 + * and mode 2 at byte 5 (count from 1) of the DO. + */ + if (priv->bcd_version >= OPENPGP_CARD_2_0 && data->pin_reference == 0x82) + data->pin_reference = 0x81; + + data->pin1.tries_left = c4data[3 + (data->pin_reference & 0x0F)]; + data->pin1.max_tries = 3; + data->pin1.logged_in = SC_PIN_STATE_UNKNOWN; + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } - LOG_FUNC_RETURN(card->ctx, iso_ops->pin_cmd(card, data, tries_left)); + +#ifdef ENABLE_OPENSSL + if (priv->pin_kdf_info) { + ops.pin_cmd = &pgp_kdf_do_pin_cmd; + } +#endif /* ENABLE_OPENSSL */ + + LOG_FUNC_RETURN(card->ctx, ops.pin_cmd(card, data)); } /** - * ABI: set security environment. + * ABI: ISO 7816-8 LOGOUT - reset all access rights gained. + */ +int pgp_logout(struct sc_card *card) +{ + int r = SC_SUCCESS; + struct pgp_priv_data *priv = DRVDATA(card); + + LOG_FUNC_CALLED(card->ctx); + + if (priv->bcd_version >= OPENPGP_CARD_3_1) { + unsigned char pin_reference; + for (pin_reference = 0x81; pin_reference <= 0x83; pin_reference++) { + int tmp = iso7816_logout(card, pin_reference); + if (r == SC_SUCCESS) { + r = tmp; + } + } + } else { + sc_path_t path; + sc_file_t *file = NULL; + + /* select application "OpenPGP" */ + sc_format_path("D276:0001:2401", &path); + path.type = SC_PATH_TYPE_DF_NAME; + r = iso_ops->select_file(card, &path, &file); + sc_file_free(file); + } + + LOG_FUNC_RETURN(card->ctx, r); +} + + +/** + * ABI: ISO 7816-8 SET SECURITY ENVIRONMENT. + * This is optional in the OpenPGP Card 3.4 specs */ static int pgp_set_security_env(sc_card_t *card, @@ -1609,7 +2541,11 @@ pgp_set_security_env(sc_card_t *card, LOG_FUNC_CALLED(card->ctx); - if ((env->flags & SC_SEC_ENV_ALG_PRESENT) && (env->algorithm != SC_ALGORITHM_RSA)) + /* The SC_SEC_ENV_ALG_PRESENT is set always so let it pass for GNUK */ + if ((env->flags & SC_SEC_ENV_ALG_PRESENT) + && (env->algorithm != SC_ALGORITHM_RSA) + && (priv->bcd_version < OPENPGP_CARD_3_0) + && (card->type != SC_CARD_TYPE_OPENPGP_GNUK)) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "only RSA algorithm supported"); @@ -1640,6 +2576,9 @@ pgp_set_security_env(sc_card_t *card, "requested usage"); } break; + case SC_SEC_OPERATION_DERIVE: + sc_log(card->ctx, "Operation: Derive: No particular action needed"); + break; default: LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid operation"); @@ -1652,7 +2591,52 @@ pgp_set_security_env(sc_card_t *card, /** - * ABI: COMPUTE DIGITAL SIGNATURE. + * set MANAGE SECURITY ENVIRONMENT as documented in 7.2.18 since OpenPGP Card v3.3 + * + * "This optional command (announced in Extended Capabilities) assigns a specific key to a + * command. The DEC-key (Key-Ref 2) can be assigned to the command INTERNAL AUTHENTICATE + * and the AUT-Key (Key.Ref 3) can be linked to the command PSO:DECIPHER also." + * + * key: Key-Ref to change (2 for DEC-Key or 3 for AUT-Key) + * p2: Usage to set (0xb8 for PSO:DECIPHER or 0xa4 for INTERNAL AUTHENTICATE) + **/ +static int +pgp_set_MSE(sc_card_t *card, int key, u8 p2) +{ + struct pgp_priv_data *priv = DRVDATA(card); + sc_apdu_t apdu; + u8 apdu_case = SC_APDU_CASE_3; + u8 apdu_data[3]; + int r; + + LOG_FUNC_CALLED(card->ctx); + + // check if MSE is supported + if (!(priv->ext_caps & EXT_CAP_MSE)) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + + // create apdu + sc_format_apdu(card, &apdu, apdu_case, 0x22, 0x41, p2); + apdu.lc = 3; + apdu_data[0] = 0x83; + apdu_data[1] = 0x01; + apdu_data[2] = key; + apdu.data = apdu_data; + apdu.datalen = 3; + + // transmit apdu + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "Card returned error"); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + +/** + * ABI: ISO 7816-8 COMPUTE DIGITAL SIGNATURE. */ static int pgp_compute_signature(sc_card_t *card, const u8 *data, @@ -1682,6 +2666,9 @@ pgp_compute_signature(sc_card_t *card, const u8 *data, break; case 0x01: default: + /* From PKCS #11 point of view, we should be able to use + * curve25519 to do digital signature, but it is not how it + * is used in OpenPGP so we will not allow it here */ LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid key reference"); } @@ -1708,7 +2695,7 @@ pgp_compute_signature(sc_card_t *card, const u8 *data, /** - * ABI: DECIPHER. + * ABI: ISO 7816-8 DECIPHER - perform deciphering operation. */ static int pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen, @@ -1718,21 +2705,87 @@ pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen, sc_security_env_t *env = &priv->sec_env; sc_apdu_t apdu; u8 apdu_case = SC_APDU_CASE_4; - u8 *temp = NULL; + u8 *temp = NULL, *p = NULL; + size_t templen, pklen, dolen; int r; LOG_FUNC_CALLED(card->ctx); - /* There's some funny padding indicator that must be - * prepended... hmm. */ - if (!(temp = malloc(inlen + 1))) + /* padding according to OpenPGP card spec 1.1 & 2.x section 7.2.9 / 3.x section 7.2.11 + * The longest possible prefix is 10 bytes for ECDH */ + templen = inlen + 10; + if (!(temp = malloc(templen))) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); - temp[0] = '\0'; - memcpy(temp + 1, in, inlen); + + /* padding byte: 0xa6 = ECC; 0x00 = RSA; 0x02 = AES */ + switch (env->algorithm) { + case SC_ALGORITHM_RSA: + /* This is just PKCS#1.5 start byte and it should be already + * provided by the padding routines. But it lets put it here + * to make sure it does not conflict with following indicators */ + temp[0] = 0x00; + memcpy(temp + 1, in, inlen); + inlen += 1; + break; + + case SC_ALGORITHM_EC: + case SC_ALGORITHM_XEDDSA: + /* Calculate length of External Public Key (0x86) */ + r = sc_asn1_put_tag(0x86, NULL, inlen, NULL, 0, NULL); + if (r <= 0) { + free(temp); + LOG_FUNC_RETURN(card->ctx, r); + } + pklen = r; + + /* Calculate length of Public Key DO (0x7F49) */ + r = sc_asn1_put_tag(0x7f49, NULL, pklen, NULL, 0, NULL); + if (r <= 0) { + free(temp); + LOG_FUNC_RETURN(card->ctx, r); + } + dolen = r; + + p = temp; + /* This is 0xA6 Cipher DO with associated length field */ + r = sc_asn1_put_tag(0xA6, NULL, dolen, p, templen - (p - temp), &p); + if (r != SC_SUCCESS) { + free(temp); + LOG_FUNC_RETURN(card->ctx, r); + } + + /* Public Key DO (0x7F49) with associated length field */ + r = sc_asn1_put_tag(0x7F49, NULL, pklen, p, templen - (p - temp), &p); + if (r != SC_SUCCESS) { + free(temp); + LOG_FUNC_RETURN(card->ctx, r); + } + + /* External Public Key (0x86) with associated length */ + r = sc_asn1_put_tag(0x86, in, inlen, p, templen - (p - temp), &p); + if (r != SC_SUCCESS) { + free(temp); + LOG_FUNC_RETURN(card->ctx, r); + } + inlen = (p - temp); + break; + case SC_ALGORITHM_AES: + /* not supported yet */ + /* + temp[0] = 0x02; + memcpy(temp + 1, in, inlen); + inlen += 1; + */ + /* fall through */ + default: + free(temp); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + in = temp; - inlen += 1; - if (env->operation != SC_SEC_OPERATION_DECIPHER) { + if (env->operation != SC_SEC_OPERATION_DECIPHER && + env->operation != SC_SEC_OPERATION_DERIVE) { free(temp); LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid operation"); @@ -1766,6 +2819,13 @@ pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen, apdu.resp = out; apdu.resplen = outlen; + /* For OpenPGP Card >=v3.3, key slot 3 instead of 2 can be used for deciphering, + * but this has to be set via MSE beforehand on every usage (slot 2 is used by default) + * see section 7.2.18 of the specification of OpenPGP Card v3.3 */ + if (priv->bcd_version >= OPENPGP_CARD_3_3 && env->key_ref[0] == 0x02){ + pgp_set_MSE(card, 3, 0xb8); + } + r = sc_transmit_apdu(card, &apdu); free(temp); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); @@ -1773,6 +2833,11 @@ pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen, r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); + /* For OpenPGP Card >=v3.3, use key slot 2 for deciphering again (set to default) */ + if (priv->bcd_version >= OPENPGP_CARD_3_3 && env->key_ref[0] == 0x02){ + pgp_set_MSE(card, 2, 0xb8); + } + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); } @@ -1782,59 +2847,99 @@ pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen, * Internal: update algorithm attribute for new key size (before generating key). **/ static int -pgp_update_new_algo_attr(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info) +pgp_update_new_algo_attr(sc_card_t *card, sc_cardctl_openpgp_key_gen_store_info_t *key_info) { struct pgp_priv_data *priv = DRVDATA(card); - pgp_blob_t *algo_blob; - unsigned int old_modulus_len; /* measured in bits */ - unsigned int old_exponent_len; - const unsigned int tag = 0x00C0 | key_info->keytype; - u8 changed = 0; + pgp_blob_t *algo_blob = NULL; + const unsigned int tag = 0x00C0 | key_info->key_id; int r = SC_SUCCESS; + unsigned int i; LOG_FUNC_CALLED(card->ctx); - /* get old algorithm attributes */ - r = pgp_seek_blob(card, priv->mf, (0x00C0 | key_info->keytype), &algo_blob); + + r = pgp_seek_blob(card, priv->mf, tag, &algo_blob); LOG_TEST_RET(card->ctx, r, "Cannot get old algorithm attributes"); - old_modulus_len = bebytes2ushort(algo_blob->data + 1); /* modulus length is coded in byte 2 & 3 */ - sc_log(card->ctx, - "Old modulus length %d, new %"SC_FORMAT_LEN_SIZE_T"u.", - old_modulus_len, key_info->modulus_len); - old_exponent_len = bebytes2ushort(algo_blob->data + 3); /* exponent length is coded in byte 3 & 4 */ - sc_log(card->ctx, - "Old exponent length %d, new %"SC_FORMAT_LEN_SIZE_T"u.", - old_exponent_len, key_info->exponent_len); - - /* Modulus */ - /* If passed modulus_len is zero, it means using old key size */ - if (key_info->modulus_len == 0) { - sc_log(card->ctx, "Use old modulus length (%d).", old_modulus_len); - key_info->modulus_len = old_modulus_len; - } - /* To generate key with new key size */ - else if (old_modulus_len != key_info->modulus_len) { - algo_blob->data[1] = (unsigned char)(key_info->modulus_len >> 8); - algo_blob->data[2] = (unsigned char)key_info->modulus_len; - changed = 1; - } - - /* Exponent */ - if (key_info->exponent_len == 0) { - sc_log(card->ctx, "Use old exponent length (%d).", old_exponent_len); - key_info->exponent_len = old_exponent_len; - } - else if (old_exponent_len != key_info->exponent_len) { - algo_blob->data[3] = (unsigned char)(key_info->exponent_len >> 8); - algo_blob->data[4] = (unsigned char)key_info->exponent_len; - changed = 1; - } - - /* If the key to-be-generated has different size, - * set this new value for GENERATE ASYMMETRIC KEY PAIR to work */ - if (changed) { - r = pgp_put_data(card, tag, algo_blob->data, 6); - /* Note: Don't use pgp_set_blob to set data, because it won't touch the real DO */ + + if (priv->ext_caps & EXT_CAP_ALG_ATTR_CHANGEABLE) { + /* ECDSA and ECDH */ + if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH + || key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA + || key_info->algorithm == SC_OPENPGP_KEYALGO_EDDSA){ + /* Note OpenPGP or current cards do not support 448 size keys yet */ + unsigned char *aoid = NULL; /* ASN1 */ + size_t aoid_len; + struct sc_object_id *scoid = NULL; + + scoid = &key_info->u.ec.oid; + /* + * Current OpenPGP cards use pre RFC8410 OIDs for ECDH and EdDSA + * so convert to older versions of the OIDs. + */ + for (i = 0; ec_curves_alt[i].size > 0; i++) { + if (sc_compare_oid(scoid, &ec_curves_alt[i].oid_alt)) { + scoid = &ec_curves_alt[i].oid; + break; + } + } + + r = sc_encode_oid(card->ctx, scoid, &aoid, &aoid_len); + LOG_TEST_RET(card->ctx, r, "invalid ec oid"); + if (aoid == NULL || aoid_len < 3 || aoid[1] > 127) { + free(aoid); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + + key_info->data_len = aoid_len + 1 - 2; /* +1 for algorithm -2 drop 06 len */ + key_info->data = malloc(key_info->data_len); + if (!key_info->data) { + free(aoid); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); + } + + key_info->data[0] = key_info->algorithm; + for (i = 0; i < aoid_len - 2; i++) + key_info->data[i + 1] = aoid[i + 2]; + + free(aoid); + } + + /* RSA */ + else if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA){ + + /* We can not rely on previous key attributes anymore, as it might be ECC */ + if (key_info->u.rsa.exponent_len == 0 || key_info->u.rsa.modulus_len == 0) + LOG_FUNC_RETURN(card->ctx,SC_ERROR_INVALID_ARGUMENTS); + + key_info->data_len = 6; + key_info->data = malloc(key_info->data_len); + if (!key_info->data) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); + + key_info->data[0] = key_info->algorithm; + ushort2bebytes(key_info->data + 1, key_info->u.rsa.modulus_len); + /* OpenPGP Card only accepts 32bit as exponent length field, + * although you can import keys with smaller exponent; + * thus we don't change rsa.exponent_len, but ignore it here */ + ushort2bebytes(key_info->data + 3, SC_OPENPGP_MAX_EXP_BITS); + /* Import-Format of private key (e,p,q) */ + key_info->data[5] = SC_OPENPGP_KEYFORMAT_RSA_STD; + } + else { + sc_log(card->ctx, "Unknown algorithm id"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + + r = pgp_put_data(card, tag, key_info->data, key_info->data_len); + /* Note: pgp_put_data calls pgp_set_blob */ LOG_TEST_RET(card->ctx, r, "Cannot set new algorithm attributes"); + } else { + sc_cardctl_openpgp_key_gen_store_info_t old_key_info; + + if (pgp_parse_algo_attr_blob(card, algo_blob, &old_key_info) != SC_SUCCESS + || old_key_info.algorithm != key_info->algorithm) + LOG_TEST_RET(card->ctx, SC_ERROR_NO_CARD_SUPPORT, + "Requested algorithm not supported"); + /* FIXME check whether the static parameters match the requested ones. */ } LOG_FUNC_RETURN(card->ctx, r); @@ -1855,12 +2960,13 @@ pgp_store_creationtime(sc_card_t *card, u8 key_id, time_t *outtime) const size_t timestrlen = 64; char timestring[65]; u8 buf[4]; + struct tm tm; LOG_FUNC_CALLED(card->ctx); - if (key_id == 0 || key_id > 3) { - sc_log(card->ctx, "Invalid key ID %d.", key_id); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); - } + + if (key_id < 1 || key_id > 3) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, + "Invalid key ID; must be 1, 2, or 3"); if (outtime != NULL && *outtime != 0) createtime = *outtime; @@ -1868,7 +2974,14 @@ pgp_store_creationtime(sc_card_t *card, u8 key_id, time_t *outtime) /* set output */ *outtime = createtime = time(NULL); - strftime(timestring, timestrlen, "%c %Z", gmtime(&createtime)); +#ifdef _WIN32 + if (0 != gmtime_s(&tm, &createtime)) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); +#else + if (NULL == gmtime_r(&createtime, &tm)) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); +#endif + strftime(timestring, timestrlen, "%c %Z", &tm); sc_log(card->ctx, "Creation time %s.", timestring); /* Code borrowed from GnuPG */ ulong2bebytes(buf, (unsigned long)createtime); @@ -1879,50 +2992,84 @@ pgp_store_creationtime(sc_card_t *card, u8 key_id, time_t *outtime) /** - * Internal: calculate PGP fingerprints. + * Internal: calculate and store PGP fingerprints. * Reference: GnuPG, app-openpgp.c. - * modulus and exponent are passed separately from key_info - * because key_info->exponent may be null. **/ static int pgp_calculate_and_store_fingerprint(sc_card_t *card, time_t ctime, - u8* modulus, u8* exponent, - sc_cardctl_openpgp_keygen_info_t *key_info) + sc_cardctl_openpgp_key_gen_store_info_t *key_info) { u8 fingerprint[SHA_DIGEST_LENGTH]; - size_t mlen = key_info->modulus_len >> 3; /* 1/8 */ - size_t elen = key_info->exponent_len >> 3; /* 1/8 */ u8 *fp_buffer = NULL; /* fingerprint buffer, not hashed */ size_t fp_buffer_len; u8 *p; /* use this pointer to set fp_buffer content */ size_t pk_packet_len; - unsigned int tag; - pgp_blob_t *fpseq_blob; - u8 *newdata; + unsigned int tag = 0x00C6 + key_info->key_id; + pgp_blob_t *fpseq_blob = NULL; + u8 *newdata = NULL; int r; LOG_FUNC_CALLED(card->ctx); - if (modulus == NULL || exponent == NULL || mlen == 0 || elen == 0) { - sc_log(card->ctx, "Null data (modulus or exponent)"); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + /* constructing public-key packet length */ + /* RSA */ + if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA) { + + if (key_info->u.rsa.modulus == NULL + || key_info->u.rsa.exponent == NULL + || (key_info->u.rsa.modulus_len) == 0 + || (key_info->u.rsa.exponent_len) == 0) { + + sc_log(card->ctx, "Null data (modulus or exponent)"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + /* https://tools.ietf.org/html/rfc4880 page 41, 72 */ + pk_packet_len = 1 /* version number */ + + 4 /* creation time */ + + 1 /* algorithm */ + + 2 /* algorithm-specific fields: RSA modulus+exponent */ + + (BYTES4BITS(key_info->u.rsa.modulus_len)) + + 2 + + (BYTES4BITS(key_info->u.rsa.exponent_len)); + } + /* ECC */ + else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH || /* also includes XEDDSA */ + key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA || + key_info->algorithm == SC_OPENPGP_KEYALGO_EDDSA) { + if (key_info->u.ec.ecpointQ == NULL || (key_info->u.ec.ecpointQ_len) == 0) { + sc_log(card->ctx, "Error: ecpoint required!"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } - /* http://tools.ietf.org/html/rfc4880 page 41, 72 */ - pk_packet_len = 1 /* version number */ - + 4 /* creation time */ - + 1 /* algorithm */ - + 2 /* algorithm-specific fields: RSA modulus+exponent */ - + mlen - + 2 - + elen; + /* https://tools.ietf.org/html/rfc4880 page 41, 72 + * and https://tools.ietf.org/html/rfc6637 section 9 (page 8 and 9) */ + pk_packet_len = 1 /* version number */ + + 4 /* creation time */ + + 1 /* algorithm */ + + 1 /* oid len */ + + key_info->u.ec.oidv_len /* oid */ + + key_info->u.ec.ecpointQ_len; /* ecpoint */ + + /* KDF parameters for ECDH */ + if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH) { + /* https://tools.ietf.org/html/rfc6637#section-8 */ + pk_packet_len += 1 /* number of bytes */ + + 1 /* version number */ + + 1 /* KDF algo */ + + 1; /* KEK algo */ + } + } else + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + sc_log(card->ctx, "pk_packet_len is %"SC_FORMAT_LEN_SIZE_T"u", pk_packet_len); fp_buffer_len = 3 + pk_packet_len; - p = fp_buffer = calloc(fp_buffer_len, 1); - if (!p) { + p = fp_buffer = calloc(1, fp_buffer_len); + if (p == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); - } + /* constructing public-key packet */ p[0] = 0x99; /* http://tools.ietf.org/html/rfc4880 page 71 */ ushort2bebytes(++p, (unsigned short)pk_packet_len); /* start pk_packet */ @@ -1930,15 +3077,63 @@ pgp_calculate_and_store_fingerprint(sc_card_t *card, time_t ctime, *p = 4; /* Version 4 key */ ulong2bebytes(++p, (unsigned long)ctime); /* Creation time */ p += 4; - *p = 1; /* RSA */ - /* algorithm-specific fields */ - ushort2bebytes(++p, (unsigned short)key_info->modulus_len); - p += 2; - memcpy(p, modulus, mlen); - p += mlen; - ushort2bebytes(++p, (unsigned short)key_info->exponent_len); - p += 2; - memcpy(p, exponent, elen); + + /* RSA */ + if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA) { + unsigned short bytes_length = 0; + + *p = 1; /* Algorithm ID, RSA */ + p += 1; + + /* Modulus */ + bytes_length = BYTES4BITS(key_info->u.rsa.modulus_len); + ushort2bebytes(p, (unsigned short)key_info->u.rsa.modulus_len); + p += 2; + memcpy(p, key_info->u.rsa.modulus, bytes_length); + p += bytes_length; + + /* Exponent */ + bytes_length = BYTES4BITS(key_info->u.rsa.exponent_len); + ushort2bebytes(p, (unsigned short)key_info->u.rsa.exponent_len); + p += 2; + memcpy(p, key_info->u.rsa.exponent, bytes_length); + } + /* ECC */ + else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH || /* includes XEDDSA */ + key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA || + key_info->algorithm == SC_OPENPGP_KEYALGO_EDDSA) { + /* Algorithm ID, see https://tools.ietf.org/html/rfc6637#section-5 */ + *p = key_info->algorithm + 6; + p += 1; + *p = key_info->u.ec.oidv_len; + p += 1; + memcpy(p, key_info->u.ec.oidv.value, key_info->u.ec.oidv_len); + p += key_info->u.ec.oidv_len; + memcpy(p, key_info->u.ec.ecpointQ, key_info->u.ec.ecpointQ_len); + + /* KDF parameters for ECDH */ + if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH) { + /* https://tools.ietf.org/html/rfc6637#section-8 + * This is copied from GnuPG's ecdh_params() function in app-openpgp.c */ + p += BYTES4BITS(key_info->u.ec.ecpointQ_len); + *p = 0x03; /* number of bytes following */ + p += 1; + *p = 0x01; /* version of this format */ + p += 1; + if (key_info->u.ec.key_length <= 256) { /* ec bit size <= 256 */ + *p = 0x08; /* KDF algo */ + *(p+1) = 0x07; /* KEK algo */ + } else if (key_info->u.ec.key_length <= 384) { /* ec bit size <= 384 */ + *p = 0x09; /* KDF algo */ + *(p+1) = 0x08; /* KEK algo */ + } else { /* ec bit size = 512 or 521*/ + *p = 0x0a; /* KDF algo */ + *(p+1) = 0x09; /* KEK algo */ + } + } + } else + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + p = NULL; /* hash with SHA-1 */ @@ -1946,34 +3141,39 @@ pgp_calculate_and_store_fingerprint(sc_card_t *card, time_t ctime, free(fp_buffer); /* store to DO */ - tag = 0x00C6 + key_info->keytype; - sc_log(card->ctx, "Write to DO %04X.", tag); - r = pgp_put_data(card, 0x00C6 + key_info->keytype, fingerprint, SHA_DIGEST_LENGTH); - LOG_TEST_RET(card->ctx, r, "Cannot write to DO."); + sc_log(card->ctx, "Writing to DO %04X.", tag); + r = pgp_put_data(card, tag, fingerprint, SHA_DIGEST_LENGTH); + LOG_TEST_RET(card->ctx, r, "Cannot write to DO"); /* update the blob containing fingerprints (00C5) */ - sc_log(card->ctx, "Update the blob containing fingerprints (00C5)"); + sc_log(card->ctx, "Updating fingerprint blob 00C5."); fpseq_blob = pgp_find_blob(card, 0x00C5); - if (!fpseq_blob) { - sc_log(card->ctx, "Not found 00C5"); - goto exit; + if (fpseq_blob == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(card->ctx, r, "Cannot find blob 00C5"); + } + if (20U * key_info->key_id > fpseq_blob->len) { + r = SC_ERROR_OBJECT_NOT_VALID; + LOG_TEST_GOTO_ERR(card->ctx, r, "The 00C5 blob is not large enough"); } + /* save the fingerprints sequence */ newdata = malloc(fpseq_blob->len); - if (!newdata) { - sc_log(card->ctx, "Not enough memory to update fingerprints blob."); - goto exit; + if (newdata == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(card->ctx, r, "Not enough memory to update fingerprint blob 00C5"); } + memcpy(newdata, fpseq_blob->data, fpseq_blob->len); /* move p to the portion holding the fingerprint of the current key */ - p = newdata + 20*(key_info->keytype - 1); + p = newdata + 20 * (key_info->key_id - 1); /* copy new fingerprint value */ memcpy(p, fingerprint, 20); /* set blob's data */ pgp_set_blob(fpseq_blob, newdata, fpseq_blob->len); free(newdata); -exit: +err: LOG_FUNC_RETURN(card->ctx, r); } @@ -1983,47 +3183,67 @@ pgp_calculate_and_store_fingerprint(sc_card_t *card, time_t ctime, * Note that modulus_len, exponent_len is measured in bit. **/ static int -pgp_update_pubkey_blob(sc_card_t *card, u8* modulus, size_t modulus_len, - u8* exponent, size_t exponent_len, u8 key_id) +pgp_update_pubkey_blob(sc_card_t *card, sc_cardctl_openpgp_key_gen_store_info_t *key_info) { struct pgp_priv_data *priv = DRVDATA(card); pgp_blob_t *pk_blob; - unsigned int blob_id; - sc_pkcs15_pubkey_t pubkey; + unsigned int blob_id = 0; + sc_pkcs15_pubkey_t p15pubkey; u8 *data = NULL; size_t len; int r; LOG_FUNC_CALLED(card->ctx); - if (key_id == SC_OPENPGP_KEY_SIGN) + if (key_info->key_id == SC_OPENPGP_KEY_SIGN) blob_id = DO_SIGN_SYM; - else if (key_id == SC_OPENPGP_KEY_ENCR) + else if (key_info->key_id == SC_OPENPGP_KEY_ENCR) blob_id = DO_ENCR_SYM; - else if (key_id == SC_OPENPGP_KEY_AUTH) + else if (key_info->key_id == SC_OPENPGP_KEY_AUTH) blob_id = DO_AUTH_SYM; else { - sc_log(card->ctx, "Unknown key id %X.", key_id); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, + "Invalid key ID; must be 1, 2, or 3"); } - sc_log(card->ctx, "Get the blob %X.", blob_id); + sc_log(card->ctx, "Retrieving blob %04X.", blob_id); r = pgp_get_blob(card, priv->mf, blob_id, &pk_blob); - LOG_TEST_RET(card->ctx, r, "Cannot get the blob."); + LOG_TEST_RET(card->ctx, r, "Cannot get the blob"); /* encode pubkey */ - memset(&pubkey, 0, sizeof(pubkey)); - pubkey.algorithm = SC_ALGORITHM_RSA; - pubkey.u.rsa.modulus.data = modulus; - pubkey.u.rsa.modulus.len = modulus_len >> 3; /* 1/8 */ - pubkey.u.rsa.exponent.data = exponent; - pubkey.u.rsa.exponent.len = exponent_len >> 3; + /* RSA */ + if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA){ + memset(&p15pubkey, 0, sizeof(p15pubkey)); + p15pubkey.algorithm = SC_ALGORITHM_RSA; + p15pubkey.u.rsa.modulus.data = key_info->u.rsa.modulus; + p15pubkey.u.rsa.modulus.len = BYTES4BITS(key_info->u.rsa.modulus_len); + p15pubkey.u.rsa.exponent.data = key_info->u.rsa.exponent; + p15pubkey.u.rsa.exponent.len = BYTES4BITS(key_info->u.rsa.exponent_len); + } + /* ECC */ + else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH || /* includes XEDDSA */ + key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA || + key_info->algorithm == SC_OPENPGP_KEYALGO_EDDSA) { + memset(&p15pubkey, 0, sizeof(p15pubkey)); + p15pubkey.algorithm = key_info->key_type; + p15pubkey.u.ec.ecpointQ.value = key_info->u.ec.ecpointQ; + p15pubkey.u.ec.ecpointQ.len = key_info->u.ec.ecpointQ_len; + } else + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); - r = sc_pkcs15_encode_pubkey(card->ctx, &pubkey, &data, &len); + r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, &p15pubkey, &data, &len); + /* + * key_info is missing an algo_id. sc_pkcs15_encode_pubkey_as_spki + * allocates one. Free it here. + */ + free(p15pubkey.alg_id); + p15pubkey.alg_id = NULL; + LOG_TEST_RET(card->ctx, r, "Cannot encode pubkey"); - sc_log(card->ctx, "Update blob content."); + sc_log(card->ctx, "Updating blob %04X's content.", blob_id); r = pgp_set_blob(pk_blob, data, len); - LOG_TEST_RET(card->ctx, r, "Cannot update blob content."); + free(data); + LOG_TEST_RET(card->ctx, r, "Cannot update blob content"); LOG_FUNC_RETURN(card->ctx, r); } @@ -2032,19 +3252,20 @@ pgp_update_pubkey_blob(sc_card_t *card, u8* modulus, size_t modulus_len, * Internal: parse response data and set output **/ static int -pgp_parse_and_set_pubkey_output(sc_card_t *card, u8* data, size_t data_len, - sc_cardctl_openpgp_keygen_info_t *key_info) +pgp_parse_and_set_pubkey_output(sc_card_t *card, u8 *data, size_t data_len, + sc_cardctl_openpgp_key_gen_store_info_t *key_info) { time_t ctime = 0; u8 *in = data; - u8 *modulus = NULL; - u8 *exponent = NULL; int r; LOG_FUNC_CALLED(card->ctx); /* store creation time */ - r = pgp_store_creationtime(card, key_info->keytype, &ctime); - LOG_TEST_RET(card->ctx, r, "Cannot store creation time"); + r = pgp_store_creationtime(card, key_info->key_id, &ctime); + if (r != SC_SUCCESS) { + /* TODO for now with GNUK at least, log but do not return error */ + sc_log(card->ctx, "Cannot store creation time"); + } /* parse response. Ref: pgp_enumerate_blob() */ while (data_len > (size_t) (in - data)) { @@ -2053,35 +3274,88 @@ pgp_parse_and_set_pubkey_output(sc_card_t *card, u8* data, size_t data_len, u8 *part = in; /* parse TLV structure */ - r = sc_asn1_read_tag((const u8**)&part, - data_len - (in - data), - &cla, &tag, &len); - LOG_TEST_RET(card->ctx, r, "Unexpected end of contents."); + r = sc_asn1_read_tag((const u8**)&part, data_len - (in - data), &cla, &tag, &len); + if (part == NULL) + r = SC_ERROR_ASN1_OBJECT_NOT_FOUND; + LOG_TEST_RET(card->ctx, r, "Unexpected end of contents"); /* undo ASN1's split of tag & class */ for (tmptag = tag; tmptag > 0x0FF; tmptag >>= 8) { cla <<= 8; } tag |= cla; + /* RSA modulus */ if (tag == 0x0081) { - /* set the output data */ - if (key_info->modulus) { - memcpy(key_info->modulus, part, len); + if (key_info->algorithm != SC_OPENPGP_KEYALGO_RSA) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + } + if ((BYTES4BITS(key_info->u.rsa.modulus_len) < len) /* modulus_len is in bits */ + || key_info->u.rsa.modulus == NULL) { + + free(key_info->u.rsa.modulus); + key_info->u.rsa.modulus = malloc(len); + if (key_info->u.rsa.modulus == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); } - /* always set output for modulus_len */ - key_info->modulus_len = len*8; - /* remember the modulus to calculate fingerprint later */ - modulus = part; + + /* set values */ + memcpy(key_info->u.rsa.modulus, part, len); + key_info->u.rsa.modulus_len = len * 8; /* store length in bits */ } + /* RSA public exponent */ else if (tag == 0x0082) { + if (key_info->algorithm != SC_OPENPGP_KEYALGO_RSA) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + } + if ((BYTES4BITS(key_info->u.rsa.exponent_len) < len) /* exponent_len is in bits */ + || key_info->u.rsa.exponent == NULL) { + + free(key_info->u.rsa.exponent); + key_info->u.rsa.exponent = malloc(len); + if (key_info->u.rsa.exponent == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); + } + + /* set values */ + memcpy(key_info->u.rsa.exponent, part, len); + key_info->u.rsa.exponent_len = len * 8; /* store length in bits */ + } + /* ECC or EDDSA public key */ + else if (tag == 0x0086) { + /* Note: SC_OPENPGP_KEYALGO_ECDH could be either EC or XEDDSA */ + if (key_info->algorithm != SC_OPENPGP_KEYALGO_ECDSA && + key_info->algorithm != SC_OPENPGP_KEYALGO_ECDH && + key_info->algorithm != SC_OPENPGP_KEYALGO_EDDSA) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + } /* set the output data */ - if (key_info->exponent) { - memcpy(key_info->exponent, part, len); + /* key_info->u.ec.ecpoint_len is already set based on + * key_info->key_type + * EC is in 04||x||y format i.e. + * (field_length + 7)/8 * 2 + 1 in bytes + * len is ecpoint length + format byte + * see section 7.2.14 of 3.3.1 specs + * EDDSA and XEDDSA have no format byte and one number + * (field_length + 7)/8 in bytes + */ + /* GNUK returns 04||x||y */ + + if (len != key_info->u.ec.ecpointQ_len) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + + switch (key_info->key_type) { + case SC_ALGORITHM_EC: + case SC_ALGORITHM_EDDSA: + case SC_ALGORITHM_XEDDSA: + key_info->u.ec.ecpointQ = malloc(len); + if (key_info->u.ec.ecpointQ == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); + memcpy(key_info->u.ec.ecpointQ, part, len); + key_info->u.ec.ecpointQ_len = len; + break; + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } - /* always set output for exponent_len */ - key_info->exponent_len = len*8; - /* remember the exponent to calculate fingerprint later */ - exponent = part; } /* go to next part to parse */ @@ -2091,12 +3365,14 @@ pgp_parse_and_set_pubkey_output(sc_card_t *card, u8* data, size_t data_len, /* calculate and store fingerprint */ sc_log(card->ctx, "Calculate and store fingerprint"); - r = pgp_calculate_and_store_fingerprint(card, ctime, modulus, exponent, key_info); - LOG_TEST_RET(card->ctx, r, "Cannot store fingerprint."); - /* update pubkey blobs (B601,B801, A401) */ + r = pgp_calculate_and_store_fingerprint(card, ctime, key_info); + LOG_TEST_RET(card->ctx, r, "Cannot store fingerprint"); + /* TODO only try and update pubkey if present */ + + /* update pubkey blobs (B601, B801, A401) */ sc_log(card->ctx, "Update blobs holding pubkey info."); - r = pgp_update_pubkey_blob(card, modulus, key_info->modulus_len, - exponent, key_info->exponent_len, key_info->keytype); + r = pgp_update_pubkey_blob(card, key_info); + LOG_FUNC_RETURN(card->ctx, r); } @@ -2105,13 +3381,20 @@ pgp_parse_and_set_pubkey_output(sc_card_t *card, u8* data, size_t data_len, * Internal: update card->algorithms */ static int -pgp_update_card_algorithms(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info) +pgp_update_card_algorithms(sc_card_t *card, sc_cardctl_openpgp_key_gen_store_info_t *key_info) { sc_algorithm_info_t *algo; - u8 id = key_info->keytype; + u8 id = key_info->key_id; + struct pgp_priv_data *priv = DRVDATA(card); LOG_FUNC_CALLED(card->ctx); + /* protect incompatible cards against non-RSA */ + if (key_info->algorithm != SC_OPENPGP_KEYALGO_RSA && + priv->bcd_version < OPENPGP_CARD_3_0 && + card->type != SC_CARD_TYPE_OPENPGP_GNUK) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + if (id > card->algorithm_count) { sc_log(card->ctx, "This key ID %u is out of the card's algorithm list.", @@ -2121,49 +3404,55 @@ pgp_update_card_algorithms(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *ke /* get the algorithm corresponding to the key ID */ algo = card->algorithms + (id - 1); - /* update new key length attribute */ - algo->key_length = (unsigned int)key_info->modulus_len; + /* update new key attribute */ + if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA) { + algo->algorithm = SC_ALGORITHM_RSA; + algo->key_length = (unsigned int)key_info->u.rsa.modulus_len; + } else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH || /* includes XEDDSA */ + key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA || + key_info->algorithm == SC_OPENPGP_KEYALGO_EDDSA) { + algo->algorithm = SC_ALGORITHM_EC; + algo->key_length = (unsigned int)((key_info->u.ec.ecpointQ_len)); + } else + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /** * ABI (card ctl): GENERATE ASYMMETRIC KEY PAIR - * Set key_info->modulus_len to zero if want to use old key size. - * Similarly for exponent length. - * key_info->modulus_len and key_info->exponent_len will be returned with new values. **/ static int -pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info) +pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_key_gen_store_info_t *key_info) { sc_apdu_t apdu; /* temporary variables to hold APDU params */ u8 apdu_case; - u8 *apdu_data; + u8 apdu_data[2] = { 0x00, 0x00 }; size_t apdu_le; size_t resplen = 0; int r = SC_SUCCESS; + struct pgp_priv_data *priv = DRVDATA(card); LOG_FUNC_CALLED(card->ctx); - /* FIXME the compilers don't assure that the buffers set here as - * apdu_data are present until the end of the function */ + /* protect incompatible cards against non-RSA */ + if (key_info->algorithm != SC_OPENPGP_KEYALGO_RSA && + priv->bcd_version < OPENPGP_CARD_3_0 && + card->type != SC_CARD_TYPE_OPENPGP_GNUK) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + /* set Control Reference Template for key */ - if (key_info->keytype == SC_OPENPGP_KEY_SIGN) - apdu_data = (unsigned char *) "\xb6"; - /* as a string, apdu_data will end with '\0' (B6 00) */ - else if (key_info->keytype == SC_OPENPGP_KEY_ENCR) - apdu_data = (unsigned char *) "\xb8"; - else if (key_info->keytype == SC_OPENPGP_KEY_AUTH) - apdu_data = (unsigned char *) "\xa4"; + if (key_info->key_id == SC_OPENPGP_KEY_SIGN) + ushort2bebytes(apdu_data, DO_SIGN); + else if (key_info->key_id == SC_OPENPGP_KEY_ENCR) + ushort2bebytes(apdu_data, DO_ENCR); + else if (key_info->key_id == SC_OPENPGP_KEY_AUTH) + ushort2bebytes(apdu_data, DO_AUTH); else { - sc_log(card->ctx, "Unknown key type %X.", key_info->keytype); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); - } - - if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && key_info->modulus_len != 2048) { - sc_log(card->ctx, "Gnuk does not support other key length than 2048."); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, + "Invalid key ID; must be 1, 2, or 3"); } /* set attributes for new-generated key */ @@ -2174,7 +3463,7 @@ pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info) * arbitrary modulus length which for sure fits into a short APDU. * This idea is borrowed from GnuPG code. */ if (card->caps & SC_CARD_CAP_APDU_EXT - && key_info->modulus_len > 1900 + && key_info->u.rsa.modulus_len > 1900 && card->type != SC_CARD_TYPE_OPENPGP_GNUK) { /* We won't store to apdu variable yet, because it will be reset in * sc_format_apdu() */ @@ -2193,13 +3482,13 @@ pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info) /* prepare APDU */ sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x80, 0); apdu.data = apdu_data; - apdu.datalen = 2; /* Data = B600 */ - apdu.lc = 2; + apdu.datalen = sizeof(apdu_data); + apdu.lc = sizeof(apdu_data); apdu.le = apdu_le; /* buffer to receive response */ apdu.resplen = (resplen > 0) ? resplen : apdu_le; - apdu.resp = calloc(apdu.resplen, 1); + apdu.resp = calloc(1, apdu.resplen); if (apdu.resp == NULL) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); } @@ -2208,24 +3497,23 @@ pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info) sc_log(card->ctx, "Waiting for the card to generate key..."); r = sc_transmit_apdu(card, &apdu); sc_log(card->ctx, "Card has done key generation."); - if (r < 0) { - sc_log(card->ctx, "APDU transmit failed. Error %s.", sc_strerror(r)); - goto finish; - } + LOG_TEST_GOTO_ERR(card->ctx, r, "APDU transmit failed"); /* check response */ r = sc_check_sw(card, apdu.sw1, apdu.sw2); /* instruct more in case of error */ if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Please verify PIN first."); - goto finish; + goto err; } + LOG_TEST_GOTO_ERR(card->ctx, r, "Card returned error"); /* parse response data and set output */ - pgp_parse_and_set_pubkey_output(card, apdu.resp, apdu.resplen, key_info); - pgp_update_card_algorithms(card, key_info); + r = pgp_parse_and_set_pubkey_output(card, apdu.resp, apdu.resplen, key_info); + LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to parse pubkey output"); + r = pgp_update_card_algorithms(card, key_info); -finish: +err: free(apdu.resp); LOG_FUNC_RETURN(card->ctx, r); } @@ -2255,7 +3543,8 @@ pgp_build_tlv(sc_context_t *ctx, unsigned int tag, u8 *data, size_t len, u8 **ou while ((tag >> 8*highest_order) != 0) { highest_order++; } - highest_order--; + if (highest_order != 0) + highest_order--; /* restore class bits in output */ if (highest_order < 4) @@ -2279,7 +3568,6 @@ set_taglength_tlv(u8 *buffer, unsigned int tag, size_t length) { u8 *p = buffer; - assert(tag <= 0xffff); if (tag > 0xff) *p++ = (tag >> 8) & 0xFF; *p++ = tag; @@ -2302,11 +3590,11 @@ set_taglength_tlv(u8 *buffer, unsigned int tag, size_t length) /** - * Internal: build Extended Header list (sec 4.3.3.7 - OpenPGP card spec v.2) + * Internal: build Extended Header list (sec 4.3.3.9 - OpenPGP card spec v.3) **/ static int -pgp_build_extended_header_list(sc_card_t *card, sc_cardctl_openpgp_keystore_info_t *key_info, - u8 **result, size_t *resultlen) +pgp_build_extended_header_list(sc_card_t *card, sc_cardctl_openpgp_key_gen_store_info_t *key_info, + u8 **result, size_t *resultlen) { sc_context_t *ctx = card->ctx; /* Cardholder private key template (7F48) part */ @@ -2330,69 +3618,118 @@ pgp_build_extended_header_list(sc_card_t *card, sc_cardctl_openpgp_keystore_info u8 *data = NULL; size_t len = 0; u8 *p = NULL; - u8 *components[] = {key_info->e, key_info->p, key_info->q, key_info->n}; - size_t componentlens[] = {key_info->e_len, key_info->p_len, key_info->q_len, key_info->n_len}; - unsigned int componenttags[] = {0x91, 0x92, 0x93, 0x97}; - char *componentnames[] = { - "public exponent", - "prime p", - "prime q", - "modulus" - }; - size_t comp_to_add = 3; - size_t req_e_len = 0; /* The exponent length specified in Algorithm Attributes */ - pgp_blob_t *alat_blob; + u8 *components[4]; + size_t componentlens[4]; + unsigned int componenttags[4]; + char *componentnames[4]; + size_t comp_to_add; u8 i; int r; LOG_FUNC_CALLED(ctx); - if (key_info->keyformat == SC_OPENPGP_KEYFORMAT_STDN - || key_info->keyformat == SC_OPENPGP_KEYFORMAT_CRTN) - comp_to_add = 4; - - /* validate */ - if (comp_to_add == 4 && (key_info->n == NULL || key_info->n_len == 0)){ - sc_log(ctx, "Error: Modulus required!"); - LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - } - - /* Cardholder private key template's data part */ - memset(pritemplate, 0, max_prtem_len); + /* RSA */ + if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA){ + + components[0] = key_info->u.rsa.exponent; + components[1] = key_info->u.rsa.p; + components[2] = key_info->u.rsa.q; + componentlens[0] = BYTES4BITS(key_info->u.rsa.exponent_len); + componentlens[1] = key_info->u.rsa.p_len; + componentlens[2] = key_info->u.rsa.q_len; + componenttags[0] = 0x91; + componenttags[1] = 0x92; + componenttags[2] = 0x93; + componentnames[0] = "public exponent"; + componentnames[1] = "prime p"; + componentnames[2] = "prime q"; + comp_to_add = 3; + + /* The maximum exponent length is 32 bit, as set on card + * we use this variable to check against actual exponent_len */ + size_t max_e_len_bytes = BYTES4BITS(SC_OPENPGP_MAX_EXP_BITS); + size_t e_len_bytes = BYTES4BITS(key_info->u.rsa.exponent_len); + + if (key_info->u.rsa.keyformat == SC_OPENPGP_KEYFORMAT_RSA_STDN + || key_info->u.rsa.keyformat == SC_OPENPGP_KEYFORMAT_RSA_CRTN){ + components[3] = key_info->u.rsa.modulus; + componentlens[3] = key_info->u.rsa.modulus_len; + componenttags[3] = 0x97; + componentnames[3] = "modulus"; + comp_to_add = 4; + + /* validate */ + if (key_info->u.rsa.modulus == NULL || key_info->u.rsa.modulus_len == 0) { + sc_log(ctx, "Error: Modulus required!"); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + } + } - /* get required exponent length */ - alat_blob = pgp_find_blob(card, 0x00C0 | key_info->keytype); - if (!alat_blob) { - sc_log(ctx, "Cannot read Algorithm Attributes."); - LOG_FUNC_RETURN(ctx, SC_ERROR_OBJECT_NOT_FOUND); + /* Cardholder private key template's data part */ + memset(pritemplate, 0, max_prtem_len); + + /* maximum 32 bit exponent length allowed on OpenPGP Card */ + if (key_info->u.rsa.exponent_len > SC_OPENPGP_MAX_EXP_BITS) + return SC_ERROR_INTERNAL; + + /* We need to right justify the exponent with allowed exponent length, + * e.g. from '01 00 01' to '00 01 00 01' */ + if (key_info->u.rsa.exponent_len < SC_OPENPGP_MAX_EXP_BITS) { + /* create new buffer */ + p = calloc(1, max_e_len_bytes); + if (!p) + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_ENOUGH_MEMORY); + + memcpy(p + (max_e_len_bytes - e_len_bytes), key_info->u.rsa.exponent, e_len_bytes); + /* set key_info->u.rsa.e to new buffer */ + free(key_info->u.rsa.exponent); + key_info->u.rsa.exponent = p; + components[0] = p; + key_info->u.rsa.exponent_len = SC_OPENPGP_MAX_EXP_BITS; /* we store info in bits */ + componentlens[0] = max_e_len_bytes; /* ... but in bytes for header list */ + } } - req_e_len = bebytes2ushort(alat_blob->data + 3) >> 3; /* 1/8 */ - assert(key_info->e_len <= req_e_len); + /* ECC */ + else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH || key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA || key_info->algorithm == SC_OPENPGP_KEYALGO_EDDSA) { + components[0] = key_info->u.ec.privateD; + componentlens[0] = key_info->u.ec.privateD_len; + componenttags[0] = 0x92; + componentnames[0] = "private key"; + comp_to_add = 1; + + /* import public key as well */ + if (key_info->u.ec.keyformat == SC_OPENPGP_KEYFORMAT_EC_STDPUB) { + components[1] = key_info->u.ec.ecpointQ; + componentlens[1] = key_info->u.ec.ecpointQ_len; + componenttags[1] = 0x99; + componentnames[1] = "public key"; + comp_to_add = 2; + + /* validate */ + if ((key_info->u.ec.ecpointQ == NULL || key_info->u.ec.ecpointQ_len == 0)) { + sc_log(ctx, "Error: ecpointQ required!"); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + } + } - /* We need to right justify the exponent with required length, - * e.g. from '01 00 01' to '00 01 00 01' */ - if (key_info->e_len < req_e_len) { - /* create new buffer */ - p = calloc(req_e_len, 1); - if (!p) - LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_ENOUGH_MEMORY); - memcpy(p + req_e_len - key_info->e_len, key_info->e, key_info->e_len); - key_info->e_len = req_e_len; - /* set key_info->e to new buffer */ - free(key_info->e); - key_info->e = p; - components[0] = p; - componentlens[0] = req_e_len; - } + /* Cardholder private key template's data part */ + memset(pritemplate, 0, max_prtem_len); + } else + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); /* start from beginning of pritemplate */ p = pritemplate; for (i = 0; i < comp_to_add; i++) { sc_log(ctx, "Set Tag+Length for %s (%X).", componentnames[i], componenttags[i]); + if (componenttags[i] > 0xffff) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); + } len = set_taglength_tlv(p, componenttags[i], componentlens[i]); tpl_len += len; + if (kdata_len + componentlens[i] > sizeof(kdata)) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); /* * <-- kdata_len --><-- Copy here --> * kdata |===============|___________________ @@ -2408,45 +3745,37 @@ pgp_build_extended_header_list(sc_card_t *card, sc_cardctl_openpgp_keystore_info /* TLV block for 7F48 */ r = pgp_build_tlv(ctx, 0x7F48, pritemplate, tpl_len, &tlv_7f48, &tlvlen_7f48); - LOG_TEST_RET(ctx, r, "Failed to build TLV for 7F48."); + LOG_TEST_RET(ctx, r, "Failed to build TLV for 7F48"); tlv_7f48[0] |= 0x7F; r = pgp_build_tlv(ctx, 0x5f48, kdata, kdata_len, &tlv_5f48, &tlvlen_5f48); - if (r < 0) { - sc_log(ctx, "Failed to build TLV for 5F48."); - goto out; - } + LOG_TEST_GOTO_ERR(ctx, r, "Failed to build TLV for 5F48"); /* data part's length for Extended Header list */ len = 2 + tlvlen_7f48 + tlvlen_5f48; /* set data part content */ - data = calloc(len, 1); - if (data == NULL) { - sc_log(ctx, "Not enough memory."); - r = SC_ERROR_NOT_ENOUGH_MEMORY; - goto out; - } - switch (key_info->keytype) { - case SC_OPENPGP_KEY_SIGN: - data[0] = 0xB6; - break; - case SC_OPENPGP_KEY_ENCR: - data[0] = 0xB8; - break; - case SC_OPENPGP_KEY_AUTH: - data[0] = 0xA4; - break; - default: - sc_log(ctx, "Unknown key type %d.", key_info->keytype); - r = SC_ERROR_INVALID_ARGUMENTS; - goto out; + data = calloc(1, len); + if (data == NULL) + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_NOT_ENOUGH_MEMORY, "Not enough memory"); + + switch (key_info->key_id) { + case SC_OPENPGP_KEY_SIGN: + ushort2bebytes(data, DO_SIGN); + break; + case SC_OPENPGP_KEY_ENCR: + ushort2bebytes(data, DO_ENCR); + break; + case SC_OPENPGP_KEY_AUTH: + ushort2bebytes(data, DO_AUTH); + break; + default: + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_INVALID_ARGUMENTS, + "Invalid key ID; must be 1, 2, or 3"); } memcpy(data + 2, tlv_7f48, tlvlen_7f48); memcpy(data + 2 + tlvlen_7f48, tlv_5f48, tlvlen_5f48); r = pgp_build_tlv(ctx, 0x4D, data, len, &tlvblock, &tlvlen); - if (r < 0) { - sc_log(ctx, "Cannot build TLV for Extended Header list."); - goto out; - } + LOG_TEST_GOTO_ERR(ctx, r, "Cannot build TLV for Extended Header list"); + /* set output */ if (result != NULL) { *result = tlvblock; @@ -2455,7 +3784,7 @@ pgp_build_extended_header_list(sc_card_t *card, sc_cardctl_openpgp_keystore_info free(tlvblock); } -out: +err: free(data); free(tlv_5f48); free(tlv_7f48); @@ -2467,92 +3796,118 @@ pgp_build_extended_header_list(sc_card_t *card, sc_cardctl_openpgp_keystore_info * ABI (card ctl): store key **/ static int -pgp_store_key(sc_card_t *card, sc_cardctl_openpgp_keystore_info_t *key_info) +pgp_store_key(sc_card_t *card, sc_cardctl_openpgp_key_gen_store_info_t *key_info) { - sc_context_t *ctx = card->ctx; - sc_cardctl_openpgp_keygen_info_t pubkey; - u8 *data; - size_t len; - int r; + u8 *data = NULL; + size_t len = 0; + int r = 0; + int has_pubkey = 0, has_privkey = 0; + struct pgp_priv_data *priv = DRVDATA(card); - LOG_FUNC_CALLED(ctx); + LOG_FUNC_CALLED(card->ctx); - /* Validate */ - if (key_info->keytype < 1 || key_info->keytype > 3) { - sc_log(ctx, "Unknown key type %d.", key_info->keytype); - LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - } - /* we just support standard key format */ - switch (key_info->keyformat) { - case SC_OPENPGP_KEYFORMAT_STD: - case SC_OPENPGP_KEYFORMAT_STDN: - break; + /* PKCS11 loads privkey separately from pubkey as two different operations + * So this routine will be called twice to create two different objects. + * pkcs15init only calls once, with both. + * OpenPGP 4.3.1 says modulus and ecpointQ are optional when + * creating the extended header. + * So we can tell the difference and only do appropriate parts of this + * routine. + */ - case SC_OPENPGP_KEYFORMAT_CRT: - case SC_OPENPGP_KEYFORMAT_CRTN: + /* protect incompatible cards against non-RSA */ + if (key_info->algorithm != SC_OPENPGP_KEYALGO_RSA && + priv->bcd_version < OPENPGP_CARD_3_0 && + card->type != SC_CARD_TYPE_OPENPGP_GNUK) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); - default: - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); - } - - /* we only support exponent of maximum 32 bits */ - if (key_info->e_len > 4) { - sc_log(card->ctx, - "Exponent %"SC_FORMAT_LEN_SIZE_T"u-bit (>32) is not supported.", - key_info->e_len * 8); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); - } + /* Validate */ + if (key_info->key_id < 1 || key_info->key_id > 3) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, + "Invalid key ID; must be 1, 2, or 3"); /* set algorithm attributes */ - memset(&pubkey, 0, sizeof(pubkey)); - pubkey.keytype = key_info->keytype; - if (key_info->n && key_info->n_len) { - pubkey.modulus = key_info->n; - pubkey.modulus_len = 8*key_info->n_len; - /* We won't update exponent length, because smaller exponent length - * will be padded later */ - } - r = pgp_update_new_algo_attr(card, &pubkey); - LOG_TEST_RET(card->ctx, r, "Failed to update new algorithm attributes"); - /* build Extended Header list */ - r = pgp_build_extended_header_list(card, key_info, &data, &len); - if (r < 0) { - sc_log(ctx, "Failed to build Extended Header list."); - goto out; + /* RSA */ + if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA){ + has_pubkey = (key_info->u.rsa.modulus && key_info->u.rsa.modulus_len && + key_info->u.rsa.exponent && key_info->u.rsa.exponent_len); + has_privkey = (has_pubkey && + key_info->u.rsa.p && key_info->u.rsa.p_len && + key_info->u.rsa.q && key_info->u.rsa.q_len); + + if (!has_pubkey && !has_privkey) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + /* we just support standard key format */ + if (has_privkey) { + switch (key_info->u.rsa.keyformat) { + case SC_OPENPGP_KEYFORMAT_RSA_STD: + case SC_OPENPGP_KEYFORMAT_RSA_STDN: + break; + + case SC_OPENPGP_KEYFORMAT_RSA_CRT: + case SC_OPENPGP_KEYFORMAT_RSA_CRTN: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + } + + /* we only support exponent of maximum 32 bits */ + if (key_info->u.rsa.exponent_len > SC_OPENPGP_MAX_EXP_BITS) { + sc_log(card->ctx, + "Exponent %" SC_FORMAT_LEN_SIZE_T "u-bit (>32) is not supported.", + key_info->u.rsa.exponent_len); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } } - /* write to DO */ - r = pgp_put_data(card, 0x4D, data, len); - if (r < 0) { - sc_log(ctx, "Failed to write to DO."); - goto out; + /* ECC */ + else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA || + key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH || /* includes XEDDSA */ + key_info->algorithm == SC_OPENPGP_KEYALGO_EDDSA) { + has_pubkey = (key_info->u.ec.ecpointQ && key_info->u.ec.ecpointQ_len); + has_privkey = (key_info->u.ec.privateD && key_info->u.ec.privateD_len); + + if (!has_pubkey && !has_privkey) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + r = pgp_update_new_algo_attr(card, key_info); + + LOG_TEST_RET(card->ctx, r, "Failed to update new algorithm attributes"); } - free(data); - data = NULL; + if (has_privkey) { + /* build Extended Header list */ + r = pgp_build_extended_header_list(card, key_info, &data, &len); + LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to build Extended Header list"); - /* store creation time */ - r = pgp_store_creationtime(card, key_info->keytype, &key_info->creationtime); - LOG_TEST_RET(card->ctx, r, "Cannot store creation time"); + /* write to DO */ + r = pgp_put_data(card, 0x4D, data, len); + LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to write to DO 004D"); - /* Calculate and store fingerprint */ - sc_log(card->ctx, "Calculate and store fingerprint"); - r = pgp_calculate_and_store_fingerprint(card, key_info->creationtime, key_info->n, key_info->e, &pubkey); - LOG_TEST_RET(card->ctx, r, "Cannot store fingerprint."); - /* update pubkey blobs (B601,B801, A401) */ - sc_log(card->ctx, "Update blobs holding pubkey info."); - r = pgp_update_pubkey_blob(card, key_info->n, 8*key_info->n_len, - key_info->e, 8*key_info->e_len, key_info->keytype); + /* store creation time */ + r = pgp_store_creationtime(card, key_info->key_id, &key_info->creationtime); + LOG_TEST_RET(card->ctx, r, "Cannot store creation time"); + } - sc_log(ctx, "Update card algorithms."); - pgp_update_card_algorithms(card, &pubkey); + if (has_pubkey) { + /* calculate and store fingerprint */ + sc_log(card->ctx, "Calculate and store fingerprint"); + r = pgp_calculate_and_store_fingerprint(card, key_info->creationtime, key_info); + LOG_TEST_RET(card->ctx, r, "Cannot store fingerprint"); -out: - if (data) { - free(data); - data = NULL; + /* update pubkey blobs (B601,B801, A401) */ + sc_log(card->ctx, "Update blobs holding pubkey info."); + r = pgp_update_pubkey_blob(card, key_info); + + sc_log(card->ctx, "Update card algorithms"); + pgp_update_card_algorithms(card, key_info); } - LOG_FUNC_RETURN(ctx, r); + +err: + free(data); + LOG_FUNC_RETURN(card->ctx, r); } #endif /* ENABLE_OPENSSL */ @@ -2568,7 +3923,7 @@ pgp_erase_card(sc_card_t *card) * according to https://www.crypto-stick.com/en/faq * (How to reset a Crypto Stick? question). * Gnuk is known not to support this feature. */ - static const char *apdu_hex[] = { + const char *apdu_hex[] = { /* block PIN1 */ "00:20:00:81:08:40:40:40:40:40:40:40:40", "00:20:00:81:08:40:40:40:40:40:40:40:40", @@ -2581,47 +3936,67 @@ pgp_erase_card(sc_card_t *card) "00:20:00:83:08:40:40:40:40:40:40:40:40", /* TERMINATE */ "00:e6:00:00", - /* ACTIVATE */ - "00:44:00:00", NULL }; + sc_apdu_t apdu; int i; int r = SC_SUCCESS; + struct pgp_priv_data *priv = DRVDATA(card); LOG_FUNC_CALLED(card->ctx); - /* check card version */ - if (card->type != SC_CARD_TYPE_OPENPGP_V2) { - sc_log(card->ctx, "Card is not OpenPGP v2"); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_NO_CARD_SUPPORT); + if ((priv->ext_caps & EXT_CAP_LCS) == 0) { + LOG_TEST_RET(card->ctx, SC_ERROR_NO_CARD_SUPPORT, + "Card does not offer life cycle management"); } - sc_log(card->ctx, "Card is OpenPGP v2. Erase card."); - - /* iterate over the commands above */ - for (i = 0; apdu_hex[i] != NULL; i++) { - u8 apdu_bin[25]; /* large enough to convert apdu_hex */ - size_t apdu_bin_len = sizeof(apdu_bin); - sc_apdu_t apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - /* convert hex array to bin array */ - r = sc_hex_to_bin(apdu_hex[i], apdu_bin, &apdu_bin_len); - LOG_TEST_RET(card->ctx, r, "Failed to convert APDU bytes"); + switch (priv->state) { + case CARD_STATE_ACTIVATED: + /* iterate over the commands above */ + for (i = 0; apdu_hex[i] != NULL; i++) { + u8 apdu_bin[25]; /* large enough to convert apdu_hex */ + size_t apdu_bin_len = sizeof(apdu_bin); + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + + /* convert hex array to bin array */ + r = sc_hex_to_bin(apdu_hex[i], apdu_bin, &apdu_bin_len); + LOG_TEST_RET(card->ctx, r, "Failed to convert APDU bytes"); + + /* build APDU from binary array */ + r = sc_bytes2apdu(card->ctx, apdu_bin, apdu_bin_len, &apdu); + if (r) + LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, + "Failed to build APDU"); + + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + + /* send APDU to card */ + sc_log(card->ctx, "Sending APDU%d %s", i, apdu_hex[i]); + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "Transmitting APDU failed"); + } + /* fall through */ + case CARD_STATE_INITIALIZATION: + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x44, 0, 0); + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "Transmitting APDU failed"); + break; + default: + LOG_TEST_RET(card->ctx, SC_ERROR_NO_CARD_SUPPORT, + "Card does not offer life cycle management"); + } - /* build APDU from binary array */ - r = sc_bytes2apdu(card->ctx, apdu_bin, apdu_bin_len, &apdu); - if (r) { - sc_log(card->ctx, "Failed to build APDU"); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + if (r == SC_SUCCESS && priv->mf) { + pgp_blob_t *new_mf = pgp_new_blob(card, NULL, priv->mf->id, priv->mf->file); + if (new_mf == NULL) { + LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, "Failed to allocate the new MF blob"); } + priv->mf->file = NULL; - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - - /* send APDU to card */ - sc_log(card->ctx, "Sending APDU%d %s", i, apdu_hex[i]); - r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "Transmitting APDU failed"); + pgp_free_blobs(priv->mf); + priv->mf = new_mf; + populate_blobs_to_mf(card, priv); } LOG_FUNC_RETURN(card->ctx, r); @@ -2629,7 +4004,7 @@ pgp_erase_card(sc_card_t *card) /** - * ABI: card ctl: perform special card-specific operations. + * ABI: ISO 7816-9 CARD CTL - perform special card-specific operations. */ static int pgp_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) @@ -2643,15 +4018,17 @@ pgp_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) memmove((sc_serial_number_t *) ptr, &card->serialnr, sizeof(card->serialnr)); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); break; - + case SC_CARDCTL_OPENPGP_SELECT_DATA: + r = pgp_select_data(card, *((u8 *) ptr)); + LOG_FUNC_RETURN(card->ctx, r); + break; #ifdef ENABLE_OPENSSL case SC_CARDCTL_OPENPGP_GENERATE_KEY: - r = pgp_gen_key(card, (sc_cardctl_openpgp_keygen_info_t *) ptr); + r = pgp_gen_key(card, (sc_cardctl_openpgp_key_gen_store_info_t *)ptr); LOG_FUNC_RETURN(card->ctx, r); break; - case SC_CARDCTL_OPENPGP_STORE_KEY: - r = pgp_store_key(card, (sc_cardctl_openpgp_keystore_info_t *) ptr); + r = pgp_store_key(card, (sc_cardctl_openpgp_key_gen_store_info_t *)ptr); LOG_FUNC_RETURN(card->ctx, r); break; #endif /* ENABLE_OPENSSL */ @@ -2666,21 +4043,20 @@ pgp_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) /** - * Internal: delete key. + * Internal: delete key (GnuK only). */ static int gnuk_delete_key(sc_card_t *card, u8 key_id) { sc_context_t *ctx = card->ctx; int r = SC_SUCCESS; - char *data = NULL; + u8 data[4] = { 0x4D, 0x02, 0x00, 0x00 }; LOG_FUNC_CALLED(ctx); - if (key_id < 1 || key_id > 3) { - sc_log(ctx, "Key ID %d is invalid. Should be 1, 2 or 3.", key_id); - LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - } + if (key_id < 1 || key_id > 3) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, + "Invalid key ID; must be 1, 2, or 3"); /* delete fingerprint */ sc_log(ctx, "Delete fingerprints"); @@ -2694,21 +4070,21 @@ gnuk_delete_key(sc_card_t *card, u8 key_id) /* rewrite Extended Header List */ sc_log(ctx, "Rewrite Extended Header List"); - if (key_id == 1) - data = "\x4D\x02\xB6"; - else if (key_id == 2) - data = "\x4D\x02\xB8"; - else if (key_id == 3) - data = "\x4D\x02\xA4"; + if (key_id == SC_OPENPGP_KEY_SIGN) + ushort2bebytes(data+2, DO_SIGN); + else if (key_id == SC_OPENPGP_KEY_ENCR) + ushort2bebytes(data+2, DO_ENCR); + else if (key_id == SC_OPENPGP_KEY_AUTH) + ushort2bebytes(data+2, DO_AUTH); - r = pgp_put_data(card, 0x4D, (const u8 *)data, strlen((const char *)data) + 1); + r = pgp_put_data(card, 0x4D, data, sizeof(data)); LOG_FUNC_RETURN(ctx, r); } /** - * ABI: DELETE FILE. + * ABI: ISO 7816-9 DELETE FILE - delete EF or DF given. */ static int pgp_delete_file(sc_card_t *card, const sc_path_t *path) @@ -2722,14 +4098,16 @@ pgp_delete_file(sc_card_t *card, const sc_path_t *path) /* sc_pkcs15init_delete_by_path() sets the path type to SC_PATH_TYPE_FILE_ID */ r = pgp_select_file(card, path, &file); - LOG_TEST_RET(card->ctx, r, "Cannot select file."); + LOG_TEST_RET(card->ctx, r, "Cannot select file"); /* save "current" blob */ blob = priv->current; - /* do try to delete MF */ - if (blob == priv->mf) + /* don't try to delete MF */ + if (blob == priv->mf) { + sc_file_free(file); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } if (card->type != SC_CARD_TYPE_OPENPGP_GNUK && (file->id == DO_SIGN_SYM || file->id == DO_ENCR_SYM || file->id == DO_AUTH_SYM)) { @@ -2749,6 +4127,7 @@ pgp_delete_file(sc_card_t *card, const sc_path_t *path) /* call pgp_put_data() with zero-sized NULL-buffer to zap the DO contents */ r = pgp_put_data(card, file->id, NULL, 0); } + sc_file_free(file); /* set "current" blob to parent */ priv->current = blob->parent; @@ -2758,7 +4137,7 @@ pgp_delete_file(sc_card_t *card, const sc_path_t *path) /** - * ABI: UPDATE BINARY. + * ABI: ISO 7816-4 UPDATE BINARY - update data in current EF. */ static int pgp_update_binary(sc_card_t *card, unsigned int idx, @@ -2786,10 +4165,49 @@ pgp_update_binary(sc_card_t *card, unsigned int idx, /** - * ABI: driver binding stuff. + * ABI: card reader lock obtained - re-select card applet if necessary. + */ +static int pgp_card_reader_lock_obtained(sc_card_t *card, int was_reset) +{ + struct pgp_priv_data *priv = DRVDATA(card); /* may be null during initialization */ + int r = SC_SUCCESS; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (card->flags & SC_CARD_FLAG_KEEP_ALIVE + && was_reset <= 0 + && priv != NULL && priv->mf && priv->mf->file) { + /* check whether applet is still selected */ + unsigned char aid[16]; + + r = sc_get_data(card, 0x004F, aid, sizeof aid); + if ((size_t) r != priv->mf->file->namelen + || 0 != memcmp(aid, priv->mf->file->name, r)) { + /* reselect is required */ + was_reset = 1; + } + r = SC_SUCCESS; + } + + if (was_reset > 0) { + sc_file_t *file = NULL; + sc_path_t path; + /* select application "OpenPGP" */ + sc_format_path("D276:0001:2401", &path); + path.type = SC_PATH_TYPE_DF_NAME; + r = iso_ops->select_file(card, &path, &file); + sc_file_free(file); + } + + LOG_FUNC_RETURN(card->ctx, r); +} + + +/** + * API: integrate OpenPGP driver into OpenSC's driver list. */ -static struct sc_card_driver * -sc_get_driver(void) +struct sc_card_driver * +sc_get_openpgp_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); @@ -2801,9 +4219,11 @@ sc_get_driver(void) pgp_ops.finish = pgp_finish; pgp_ops.select_file = pgp_select_file; pgp_ops.list_files = pgp_list_files; + pgp_ops.get_challenge = pgp_get_challenge; pgp_ops.read_binary = pgp_read_binary; - pgp_ops.write_binary = pgp_write_binary; + pgp_ops.write_binary = NULL; pgp_ops.pin_cmd = pgp_pin_cmd; + pgp_ops.logout = pgp_logout; pgp_ops.get_data = pgp_get_data; pgp_ops.put_data = pgp_put_data; pgp_ops.set_security_env= pgp_set_security_env; @@ -2812,16 +4232,7 @@ sc_get_driver(void) pgp_ops.card_ctl = pgp_card_ctl; pgp_ops.delete_file = pgp_delete_file; pgp_ops.update_binary = pgp_update_binary; + pgp_ops.card_reader_lock_obtained = pgp_card_reader_lock_obtained; return &pgp_drv; } - - -/** - * ABI: driver binding stuff. - */ -struct sc_card_driver * -sc_get_openpgp_driver(void) -{ - return sc_get_driver(); -} diff --git a/src/libopensc/card-openpgp.h b/src/libopensc/card-openpgp.h new file mode 100644 index 0000000000..26cdf1d553 --- /dev/null +++ b/src/libopensc/card-openpgp.h @@ -0,0 +1,218 @@ +/* + * card-openpgp.h: Support for OpenPGP card + * + * Copyright (C) 2020 Peter Marschall + * + * 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 + */ + +#ifndef _CARD_OPENPGP_H +#define _CARD_OPENPGP_H + +/* + * The OpenPGP card doesn't have a file system, instead everything + * is stored in data objects that are accessed through GET/PUT. + * + * However, much inside OpenSC's pkcs15 implementation is based on + * the assumption that we have a file system. So we fake one here. + * + * Selecting the MF causes us to select the OpenPGP AID. + * + * Everything else is mapped to "file" IDs. + */ + +typedef enum _pgp_do_type { /* DO type */ + SIMPLE = SC_FILE_TYPE_WORKING_EF, + CONSTRUCTED = SC_FILE_TYPE_DF +} pgp_do_type_t; + +typedef enum _pgp_version { /* 2-byte BCD-alike encoded version number */ + OPENPGP_CARD_1_0 = 0x0100, + OPENPGP_CARD_1_1 = 0x0101, + OPENPGP_CARD_2_0 = 0x0200, + OPENPGP_CARD_2_1 = 0x0201, + OPENPGP_CARD_2_2 = 0x0202, + OPENPGP_CARD_3_0 = 0x0300, + OPENPGP_CARD_3_1 = 0x0301, + OPENPGP_CARD_3_2 = 0x0302, + OPENPGP_CARD_3_3 = 0x0303, + OPENPGP_CARD_3_4 = 0x0304, +} pgp_version_t; + +typedef enum _pgp_access { /* access flags for the respective DO/file */ + READ_NEVER = 0x0010, + READ_PIN1 = 0x0011, + READ_PIN2 = 0x0012, + READ_PIN3 = 0x0014, + READ_ALWAYS = 0x0018, + READ_MASK = 0x00FF, + WRITE_NEVER = 0x1000, + WRITE_PIN1 = 0x1100, + WRITE_PIN2 = 0x1200, + WRITE_PIN3 = 0x1400, + WRITE_ALWAYS = 0x1800, + WRITE_MASK = 0x1F00 +} pgp_access_t; + +// clang-format off +typedef enum _pgp_ext_caps { /* extended capabilities/features: bit flags */ + EXT_CAP_KDF_DO = 0x0001, + EXT_CAP_ALG_ATTR_CHANGEABLE = 0x0004, + EXT_CAP_PRIVATE_DO = 0x0008, + EXT_CAP_C4_CHANGEABLE = 0x0010, + EXT_CAP_KEY_IMPORT = 0x0020, + EXT_CAP_GET_CHALLENGE = 0x0040, + EXT_CAP_SM = 0x0080, + EXT_CAP_LCS = 0x0100, + EXT_CAP_CHAINING = 0x1000, + EXT_CAP_APDU_EXT = 0x2000, + EXT_CAP_MSE = 0x4000 +} pgp_ext_caps_t; +// clang-format on + +typedef enum _pgp_card_state { + CARD_STATE_UNKNOWN = 0x00, + CARD_STATE_INITIALIZATION = 0x03, + CARD_STATE_ACTIVATED = 0x05 +} pgp_card_state_t; + +typedef enum _pgp_sm_algo { + SM_ALGO_NONE = 0, /* SM not supported */ + SM_ALGO_AES128 = 1, + SM_ALGO_AES256 = 2, + SM_ALGO_SCP11b = 3, + SM_ALGO_3DES = 256, /* 2.x: coded as 0 in DO C0 */ + SM_ALGO_UNKNOWN = 257 /* 3.x: coded as 0 in DO C0 */ +} pgp_sm_algo_t; + +typedef struct _pgp_do_info { + unsigned int id; /* ID of the DO in question */ + + pgp_do_type_t type; /* constructed DO or not */ + pgp_access_t access; /* R/W access levels for the DO */ + + /* function to get the DO from the card: + * only != NULL is DO if readable and not only a part of a constructed DO */ + int (*get_fn)(sc_card_t *, unsigned int, u8 *, size_t); + /* function to write the DO to the card: + * only != NULL if DO is writeable under some conditions */ + int (*put_fn)(sc_card_t *, unsigned int, const u8 *, size_t); +} pgp_do_info_t; + +typedef struct pgp_blob { + struct pgp_blob *next; /* pointer to next sibling */ + struct pgp_blob *parent; /* pointer to parent */ + pgp_do_info_t *info; + + sc_file_t *file; + unsigned int id; + int status; + + unsigned char *data; + unsigned int len; + struct pgp_blob *files; /* pointer to 1st child */ +} pgp_blob_t; + +typedef struct _pgp_pin_kdf_info { + const char *hash_algo; + uint32_t iterations; + pgp_blob_t *userpw_salt; + pgp_blob_t *adminpw_salt; +} pgp_pin_kdf_info_t; + +// clang-format off +/* The DO holding X.509 certificate is constructed but does not contain a child DO. + * We should notice this when building fake file system later. */ +#define DO_CERT 0x7f21 +/* Control Reference Template of private keys. Ref: Section 4.3.3.7 of OpenPGP card v2 spec. + * Here we treat them as DOs just for convenience */ +#define DO_SIGN 0xb600 +#define DO_ENCR 0xb800 +#define DO_AUTH 0xa400 +/* These DOs do not exist. They are defined and used just for ease of implementation */ +#define DO_SIGN_SYM 0xb601 +#define DO_ENCR_SYM 0xb801 +#define DO_AUTH_SYM 0xa401 +/* Private DOs */ +#define DO_PRIV1 0x0101 +#define DO_PRIV2 0x0102 +#define DO_PRIV3 0x0103 +#define DO_PRIV4 0x0104 +/* Cardholder information DOs */ +#define DO_CARDHOLDER 0x65 +#define DO_NAME 0x5b +#define DO_LANG_PREF 0x5f2d +#define DO_SEX 0x5f35 +/* KDF-DO */ +#define DO_KDF 0xf9 + + +/* Maximum length for response buffer when reading pubkey. + * This value is calculated with 4096-bit key length */ +#define MAXLEN_RESP_PUBKEY 527 +/* Gnuk only supports 1 key length (2048 bit) */ +#define MAXLEN_RESP_PUBKEY_GNUK 271 + +/* Maximal size of a DO: + * v2.0+: max. certificate size it at bytes 5-6 of Extended Capabilities DO 00C0 + * v3.0+: max. special DO size is at bytes 7-8 of Extended Capabilities DO 00C0 + * Theoretically we should have the 64k, but we currently limit to 8k. */ +#define MAX_OPENPGP_DO_SIZE 8192 +// clang-format on + +typedef struct _pgp_ec_curves { + struct sc_object_id oid; + size_t size; + unsigned int key_type; +} pgp_ec_curves_t; + +#ifdef ENABLE_OPENSSL +typedef struct _pgp_ec_curves_alt { + struct sc_object_id oid; + struct sc_object_id oid_alt; /* RFC8410 OIDs to be mapped to oid */ + size_t size; +} pgp_ec_curves_alt_t; +#endif /* ENABLE_OPENSSL */ + +#define DRVDATA(card) ((struct pgp_priv_data *) ((card)->drv_data)) + +// clang-format off +struct pgp_priv_data { + pgp_blob_t *mf; + pgp_blob_t *current; /* currently selected file */ + + pgp_version_t bcd_version; + pgp_do_info_t *pgp_objects; + + pgp_card_state_t state; /* card state */ + pgp_ext_caps_t ext_caps; /* extended capabilities */ + + pgp_sm_algo_t sm_algo; /* Secure Messaging algorithm */ + + pgp_pin_kdf_info_t *pin_kdf_info; /* KDF-DO */ + + size_t max_challenge_size; + size_t max_cert_size; + size_t max_specialDO_size; + + pgp_ec_curves_t *ec_curves; + + sc_security_env_t sec_env; +}; +// clang-format on + +#define BCD2UCHAR(x) (((((x) & 0xF0) >> 4) * 10) + ((x) & 0x0F)) + +#endif diff --git a/src/libopensc/card-piv.c b/src/libopensc/card-piv.c index 6f492fcc05..8db549b672 100644 --- a/src/libopensc/card-piv.c +++ b/src/libopensc/card-piv.c @@ -3,8 +3,7 @@ * card-default.c: Support for cards with no driver * * Copyright (C) 2001, 2002 Juha Yrjölä - * Copyright (C) 2005,2006,2007,2008,2009,2010 Douglas E. Engert - * Copyright (C) 2016 Douglas E. Engert + * Copyright (C) 2005-2026 Douglas E. Engert * Copyright (C) 2006, Identity Alliance, Thomas Harning * Copyright (C) 2007, EMC, Russell Larner * @@ -20,10 +19,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -40,21 +39,32 @@ #endif #ifdef ENABLE_OPENSSL - /* openssl only needed for card administration */ +/* openssl needed for card administration and SM */ #include -#include #include #include -#include -#endif /* ENABLE_OPENSSL */ +#include +#if !defined(OPENSSL_NO_EC) +#include +#endif +#endif +#include "common/compat_strlcat.h" +#include "common/compat_strlcpy.h" #include "internal.h" -#include "asn1.h" -#include "cardctl.h" -#ifdef ENABLE_ZLIB + +/* 800-73-4 SM and VCI need: ECC, SM and OpenSSL or LibreSSL */ +#if defined(ENABLE_PIV_SM) #include "compression.h" +#include #endif +#include "asn1.h" +#include "cardctl.h" +#include "simpletlv.h" + +// clang-format off + enum { PIV_OBJ_CCC = 0, PIV_OBJ_CHUI, @@ -90,6 +100,9 @@ enum { PIV_OBJ_RETIRED_X509_19, PIV_OBJ_RETIRED_X509_20, PIV_OBJ_IRIS_IMAGE, + PIV_OBJ_BITGT, + PIV_OBJ_SM_CERT_SIGNER, + PIV_OBJ_PCRDCS, PIV_OBJ_9B03, PIV_OBJ_9A06, PIV_OBJ_9C06, @@ -123,14 +136,17 @@ enum { * PIV_OBJ_CACHE_VALID means the data in the cache can be used. * It might have zero length indicating that the object was not found. * PIV_OBJ_CACHE_NOT_PRESENT means do not even try to read the object. - * These objects will only be present if the history object says + * Either because the object did not parse or + * these objects will only be present if the history object says * they are on the card, or the discovery or history object in not present. - * If the file lilsted in the history object offCardCertURL was found, + * If the file listed in the history object offCardCertURL was found, * its certs will be read into the cache and PIV_OBJ_CACHE_VALID set * and PIV_OBJ_CACHE_NOT_PRESENT unset. + * */ -#define PIV_OBJ_CACHE_VALID 1 +#define PIV_OBJ_CACHE_VALID 1 +#define PIV_OBJ_CACHE_COMPRESSED 2 #define PIV_OBJ_CACHE_NOT_PRESENT 8 typedef struct piv_obj_cache { @@ -141,16 +157,246 @@ typedef struct piv_obj_cache { int flags; } piv_obj_cache_t; +enum { + PIV_STATE_NORMAL = 0, + PIV_STATE_MATCH, + PIV_STATE_INIT +}; + +/* ccc_flags */ +#define PIV_CCC_FOUND 0x00000001 +#define PIV_CCC_F0_PIV 0x00000002 +#define PIV_CCC_F0_CAC 0x00000004 +#define PIV_CCC_F0_JAVA 0x00000008 +#define PIV_CCC_F3_CAC_PKI 0x00000010 + +#define PIV_CCC_TAG_F0 0xF0 +#define PIV_CCC_TAG_F3 0xF3 + +/* 800-73-4 Cipher Suite Table 14 */ +#define PIV_CS_CS2 0x27 +#define PIV_CS_CS7 0x2E + +#ifdef ENABLE_PIV_SM +#ifdef USE_OPENSSL3_LIBCTX +#define PIV_LIBCTX card->ctx->ossl3ctx->libctx +#else +#define PIV_LIBCTX NULL +#endif + + /* Table 14 and other constants */ + typedef struct cipher_suite { + u8 id; /* taken from AID "AC" tag */ + int field_length; + int nid; /* for OpenSSL curves */ + struct sc_object_id oid; /* for opensc */ + int p1; /* for APDU */ + size_t Qlen; /* size of pubkey 04||x||y for all keys */ + size_t AuthCryptogramlen; /* both H and ICC must match */ + size_t Zlen; /* size of shared secret from ECDH */ + size_t otherinfolen; /* used in 4.1.6 Key Derivation */ + + int o0len; /* first in otherinfo */ + u8 o0_char; + size_t CBhlen; + size_t T16Qehlen; + size_t IDsicclen; + size_t Nicclen; + size_t CBicclen; /* last in otherinfo */ + + int naeskeys; /* number of aes key generated */ + int aeskeylen; /* size of aes key bytes*/ + int kdf_hash_size; /* size of hash in bytes */ + EVP_MD *(*kdf_md)(void); + const EVP_CIPHER *(*cipher_cbc)(void); + const EVP_CIPHER *(*cipher_ecb)(void); + char *cipher_cbc_name; + char *cipher_ecb_name; + char *curve_group; + } cipher_suite_t; + +#define PIV_CSS_SIZE 2 +static cipher_suite_t css[PIV_CSS_SIZE] = { + {PIV_CS_CS2, 256, NID_X9_62_prime256v1, {{1, 2, 840, 10045, 3, 1, 7, -1}}, + PIV_CS_CS2, 65, 16, 32, 61, + 4, 0x09, 1, 16, 8, 16, 1, + 4, 128/8, SHA256_DIGEST_LENGTH, + (EVP_MD *(*)(void)) EVP_sha256, + (const EVP_CIPHER *(*)(void)) EVP_aes_128_cbc, + (const EVP_CIPHER *(*)(void)) EVP_aes_128_ecb, + "aes-128-cbc", "aes-128-ecb", + "prime256v1"}, + + {PIV_CS_CS7, 384, NID_secp384r1, {{1, 3, 132, 0, 34, -1}}, + PIV_CS_CS7, 97, 16, 48, 69, + 4, 0x0D, 1, 16, 8, 24, 1, + 4, 256/8, SHA384_DIGEST_LENGTH, + (EVP_MD *(*)(void)) EVP_sha384, + (const EVP_CIPHER *(*)(void)) EVP_aes_256_cbc, + (const EVP_CIPHER *(*)(void)) EVP_aes_256_ecb, + "aes-256-cbc", "aes-256-ecb", + "secp384r1"} + }; + +/* 800-73-4 4.1.5 Card Verifiable Certificates */ +typedef struct piv_cvc { + sc_pkcs15_der_t der; // Previous read der + int cpi; // Certificate profile indicator (0x80) + char issuerID[8]; // Issuer Identification Number + size_t issuerIDlen; // 8 bytes of sha-1 or 16 byte for GUID + u8 subjectID[16]; // Subject Identifier (8) or GUID (16) == CHUI + size_t subjectIDlen; // 8 bytes of sha-1 or 16 byte for GUID + struct sc_object_id pubKeyOID; // Public key algorithm object identifier + u8 *publicPoint; // Public point for ECC + size_t publicPointlen; + int roleID; // Role Identifier 0x00 or 0x12 + u8 *body; // signed part of CVC in DER + size_t bodylen; + struct sc_object_id signatureAlgOID; // Signature Algroithm Identifier + u8 *signature; // Certificate signature DER + size_t signaturelen; +} piv_cvc_t; + +#define PIV_SM_MAX_FIELD_LENGTH 384 +#define PIV_SM_MAX_MD_LENGTH SHA384_DIGEST_LENGTH + +#define PIV_SM_FLAGS_SM_CERT_SIGNER_VERIFIED 0x000000001lu +#define PIV_SM_FLAGS_SM_CVC_VERIFIED 0X000000002lu +#define PIV_SM_FLAGS_SM_IN_CVC_VERIFIED 0x000000004lu +#define PIV_SM_FLAGS_SM_CERT_SIGNER_PRESENT 0x000000010lu +#define PIV_SM_FLAGS_SM_CVC_PRESENT 0X000000020lu +#define PIV_SM_FLAGS_SM_IN_CVC_PRESENT 0x000000040lu +#define PIV_SM_FLAGS_SM_IS_ACTIVE 0x000000080lu /* SM has been started */ + /* if card supports SP800-73-4 SM: */ +#define PIV_SM_FLAGS_NEVER 0x000000100lu /* Don't use SM even if card support it */ + /* Default is use if card supports it */ + /* will use VCI if card supports it for contactless */ +#define PIV_SM_FLAGS_ALWAYS 0x000000200lu /* Use SM or quit, VCI requires SM */ +#define PIV_SM_FLAGS_DEFER_OPEN 0x000001000lu /* call sm_open from reader_lock_obtained */ +#define PIV_SM_VCI_ACTIVE 0x000002000lu /* VCI is active */ +#define PIV_SM_GET_DATA_IN_CLEAR 0x000004000lu /* OK to do this GET DATA in the clear */ + +typedef struct piv_sm_session { + /* set by piv_sm_open */ + int aes_size; /* 128 or 256 */ + + u8 SKcfrm[32]; + u8 SKmac[32]; + u8 SKenc[32]; /* keys are either AES 128 or AES 256 */ + u8 SKrmac[32]; + u8 enc_counter[16]; + u8 enc_counter_last[16]; + + u8 resp_enc_counter[16]; + u8 C_MCV[16]; + u8 C_MCV_last[16]; + u8 R_MCV[16]; + u8 R_MCV_last[16]; +} piv_sm_session_t; + +#define C_ASN1_PIV_CVC_PUBKEY_SIZE 3 + /* ECC key only */ +static const struct sc_asn1_entry c_asn1_piv_cvc_pubkey[C_ASN1_PIV_CVC_PUBKEY_SIZE] = { + { "publicKeyOID", SC_ASN1_OBJECT, SC_ASN1_UNI | SC_ASN1_OBJECT, 0, NULL, NULL }, + { "publicPoint", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 6, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } +}; + +#define C_ASN1_PIV_CVC_DSOBJ_SIZE 2 +static const struct sc_asn1_entry c_asn1_piv_cvc_dsobj[C_ASN1_PIV_CVC_DSOBJ_SIZE] = { + { "DigitalSignature", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_TAG_SEQUENCE, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } +}; + +#define C_ASN1_PIV_CVC_DSSIG_SIZE 3 +static const struct sc_asn1_entry c_asn1_piv_cvc_dssig[C_ASN1_PIV_CVC_DSSIG_SIZE] = { + { "signatureAlgorithmID", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_TAG_SEQUENCE, 0, NULL, NULL }, + { "signatureValue", SC_ASN1_BIT_STRING_NI, SC_ASN1_TAG_BIT_STRING, SC_ASN1_ALLOC, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } +}; + +#define C_ASN1_PIV_CVC_ALG_ID_SIZE 3 +static const struct sc_asn1_entry c_asn1_piv_cvc_alg_id[C_ASN1_PIV_CVC_ALG_ID_SIZE] = { + { "signatureAlgorithmOID", SC_ASN1_OBJECT, SC_ASN1_UNI | SC_ASN1_OBJECT, 0, NULL, NULL }, + { "nullParam", SC_ASN1_NULL, SC_ASN1_UNI | SC_ASN1_TAG_NULL, SC_ASN1_OPTIONAL, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } +}; + +#define C_ASN1_PIV_CVC_BODY_SIZE 7 +static const struct sc_asn1_entry c_asn1_piv_cvc_body[C_ASN1_PIV_CVC_BODY_SIZE] = { + { "certificateProfileIdentifier", SC_ASN1_INTEGER, SC_ASN1_APP | 0x1F29, 0, NULL, NULL }, + { "Issuer ID Number", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 2, 0, NULL, NULL }, + { "Subject Identifier", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 0x1F20, 0, NULL, NULL }, + { "publicKey", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 0x1F49, 0, NULL, NULL }, + { "roleIdentifier", SC_ASN1_CALLBACK, SC_ASN1_APP | 0x1F4C, 0, NULL, NULL }, + /* signature is over the above 5 entries treat roleIdentifier special to get end */ + { "DSignatureObject", SC_ASN1_STRUCT, SC_ASN1_APP | 0x1F37, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } +}; + + +#define C_ASN1_PIV_CVC_SIZE 2 +static const struct sc_asn1_entry c_asn1_piv_cvc[C_ASN1_PIV_CVC_SIZE] = { + { "CVC certificate", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 0x1F21, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } +}; + +#define C_ASN1_PIV_SM_RESPONSE_SIZE 4 +static const struct sc_asn1_entry c_asn1_sm_response[C_ASN1_PIV_SM_RESPONSE_SIZE] = { + { "encryptedData", SC_ASN1_CALLBACK, SC_ASN1_CTX | 7, SC_ASN1_OPTIONAL, NULL, NULL }, + { "statusWord", SC_ASN1_CALLBACK, SC_ASN1_CTX | 0x19, 0, NULL, NULL }, + { "mac", SC_ASN1_CALLBACK, SC_ASN1_CTX | 0x0E, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } +}; + +/* + * SW internal apdu response table. + * + * Override APDU response error codes from iso7816.c to allow + * handling of SM specific error + */ +static const struct sc_card_error piv_sm_errors[] = { + {0x6882, SC_ERROR_SM, "SM not supported"}, + {0x6982, SC_ERROR_SM_NO_SESSION_KEYS, "SM Security status not satisfied"}, /* no session established */ + {0x6987, SC_ERROR_SM, "Expected SM Data Object missing"}, + {0x6988, SC_ERROR_SM_INVALID_SESSION_KEY, "SM Data Object incorrect"}, /* other process interference */ + {0, 0, NULL} +}; +#endif /* ENABLE_PIV_SM */ + +/* 800-73-4 3.3.2 Discovery Object - PIN Usage Policy */ +#define PIV_PP_PIN 0x00004000u +#define PIV_PP_GLOBAL 0x00002000u +#define PIV_PP_OCC 0x00001000u +#define PIV_PP_VCI_IMPL 0x00000800u +#define PIV_PP_VCI_WITHOUT_PC 0x00000400u +#define PIV_PP_PIV_PRIMARY 0x00000010u +#define PIV_PP_GLOBAL_PRIMARY 0x00000020u + +/* init_flags */ +#define PIV_INIT_AID_PARSED 0x00000001u +#define PIV_INIT_AID_AC_SM 0x00000002u +#define PIV_INIT_DISCOVERY_PARSED 0x00000004u +#define PIV_INIT_DISCOVERY_PP 0x00000008u +#define PIV_INIT_IN_READER_LOCK_OBTAINED 0x00000010u +#define PIV_INIT_CONTACTLESS 0x00000020u + +#define PIV_PAIRING_CODE_LEN 8 + typedef struct piv_private_data { - sc_file_t *aid_file; + struct sc_lv_data aid_der; /* previous aid response to compare */ int enumtag; - int selected_obj; /* The index into the piv_objects last selected */ - int return_only_cert; /* return the cert from the object */ - int rwb_state; /* first time -1, 0, in middle, 1 at eof */ + int max_object_size; /* use setable option. In case objects get bigger */ + int selected_obj; /* The index into the piv_objects last selected */ + int return_only_cert; /* return the cert from the object */ + int rwb_state; /* first time -1, 0, in middle, 1 at eof */ int operation; /* saved from set_security_env */ - int algorithm; /* saved from set_security_env */ + unsigned long algorithm; /* saved from set_security_env */ int key_ref; /* saved from set_security_env and */ int alg_id; /* used in decrypt, signature, derive */ + unsigned int alg_ids; /* from AID response, NIST required, card version */ + unsigned char * al_label; /* from Select AID Application Label */ + int al_labellen; int key_size; /* RSA: modulus_bits EC: field_length in bits */ u8* w_buf; /* write_binary buffer */ size_t w_buf_len; /* length of w_buff */ @@ -160,42 +406,169 @@ typedef struct piv_private_data { char * offCardCertURL; int pin_preference; /* set from Discovery object */ int logged_in; + int pstate; int pin_cmd_verify; - int pin_cmd_noparse; + int context_specific; unsigned int pin_cmd_verify_sw1; unsigned int pin_cmd_verify_sw2; int tries_left; /* SC_PIN_CMD_GET_INFO tries_left from last */ unsigned int card_issues; /* card_issues flags for this card */ int object_test_verify; /* Can test this object to set verification state of card */ - int neo_version; /* 3 byte version number of NEO or Ybuikey4 as integer */ + int yubico_version; /* 3 byte version number of NEO or Yubikey4 as integer */ + unsigned int ccc_flags; /* From CCC indicate if CAC card */ + unsigned int pin_policy; /* from discovery */ + unsigned int init_flags; + u8 csID; /* 800-73-4 Cipher Suite ID 0x27 or 0x2E */ +#ifdef ENABLE_PIV_SM + cipher_suite_t *cs; /* active cipher_suite */ + piv_cvc_t sm_cvc; /* 800-73-4: SM CVC Table 15 */ + piv_cvc_t sm_in_cvc; /* Intermediate CVC Table 16 */ + unsigned long sm_flags; + unsigned char pairing_code[PIV_PAIRING_CODE_LEN]; /* 8 ASCII digits */ + piv_sm_session_t sm_session; +#endif /* ENABLE_PIV_SM */ } piv_private_data_t; #define PIV_DATA(card) ((piv_private_data_t*)card->drv_data) struct piv_aid { int enumtag; - size_t len_short; /* min lenght without version */ + size_t len_short; /* min length without version */ size_t len_long; /* With version and other stuff */ u8 *value; }; /* - * The Generic entry should be the "A0 00 00 03 08 00 00 01 00 " - * NIST published this on 10/6/2005 - * 800-73-2 Part 1 now refers to version "02 00" + * The Generic AID entry should be the "A0 00 00 03 08 00 00 10 00 " + * NIST published 800-73 on 10/6/2005 + * 800-73-1 March 2006 included Errata + * 800-73-2 Part 1 implies version is "02 00" * i.e. "A0 00 00 03 08 00 00 01 00 02 00". - * but we don't need the version number. but could get it from the PIX. + * but we don't need the version number. But could get it from the PIX. + * Discovery object was added. * - * 800-73-3 Part 1 now referes to "01 00" i.e. going back to 800-73-1. - * The main differences between 73-1, and 73-3 are the addition of the - * key History object and keys, as well as Discovery and Iris objects. - * These can be discovered by trying GET DATA + * 800-73-3 Part 1 now refers to "01 00" i.e. going back to 800-73-1. + * The main differences between 73-2, and 73-3 are the addition of the + * key History object, certs and keys and Iris objects. + * These can be discovered using GET DATA + + * 800-73-4 Has many changes, including optional Secure Messaging, + * optional Virtual Contact Interface and pairing code. */ -/* all have same AID */ +/* ATRs of cards known to have PIV applet. But must still be tested for a PIV applet */ +/* Yubico, NitroKey, Token2 are identified by ATR historic bytes */ +/* PivApplet does not provide ATR historic bytes */ + +static const struct sc_atr_table piv_atrs[] = { + /* CAC cards with PIV from: CAC-utilziation-and-variation-matrix-v2.03-20May2016.doc */ + /* + * https://www.cac.mil/Common-Access-Card/Developer-Resources/ + * https://www.cac.mil/Portals/53/Documents/DoD%20Token%20utilziation%20and%20variation%20matrix%20v2_06_17October2019.docx?ver=2019-10-18-102519-120 + */ + /* Oberthur Card Systems (PIV Endpoint) with PIV endpoint applet and PIV auth cert OBSOLETE */ + { "3B:DB:96:00:80:1F:03:00:31:C0:64:77:E3:03:00:82:90:00:C1", NULL, NULL, SC_CARD_TYPE_PIV_II_OBERTHUR, 0, NULL }, + + /* Gemalto (PIV Endpoint) with PIV endpoint applet and PIV auth cert OBSOLETE */ + { "3B 7D 96 00 00 80 31 80 65 B0 83 11 13 AC 83 00 90 00", NULL, NULL, SC_CARD_TYPE_PIV_II_GEMALTO, 0, NULL }, + + /* Gemalto (PIV Endpoint) 2 entries 2016, 2019 */ + { "3B:7D:96:00:00:80:31:80:65:B0:83:11:17:D6:83:00:90:00", NULL, NULL, SC_CARD_TYPE_PIV_II_GEMALTO, 0, NULL }, + + /* Oberthur Card System (PIV Endpoint) 2 entries 2016, 2019 */ + { "3B:DB:96:00:80:1F:03:00:31:C0:64:B0:F3:10:00:07:90:00:80", NULL, NULL, SC_CARD_TYPE_PIV_II_OBERTHUR, 0, NULL }, + /* Oberthur Card System with LCS 0F - Some VA cards have Terminated state */ + { "3B:DB:96:00:80:1F:03:00:31:C0:64:B0:F3:10:00:0F:90:00:88", NULL, NULL, SC_CARD_TYPE_PIV_II_OBERTHUR, 0, NULL }, + + /* Giesecke & Devrient (PIV Endpoint) 2 entries 2016, 2019 */ + { "3B:7A:18:00:00:73:66:74:65:20:63:64:31:34:34", NULL, NULL, SC_CARD_TYPE_PIV_II_GI_DE_DUAL_CAC, 0, NULL }, + /* Giesecke & Devrient (CAC PIV Endpoint) 2019 */ + { "3B:F9:18:00:00:00:53:43:45:37:20:03:00:20:46", NULL, NULL, SC_CARD_TYPE_PIV_II_GI_DE_DUAL_CAC, 0, NULL }, + /* Giesecke & Devrient SCE7 (PIV-only) (DoD Alternate Token G+D Sm@rtCafe Expert v7.0 144K DI 2025) */ + { "3B:F9:96:00:00:80:31:FE:45:53:43:45:37:20:0F:00:20:46:4E", NULL, NULL, SC_CARD_TYPE_PIV_II_GI_DE, 0, NULL }, + + /* IDEMIA (new name for Oberthur) (DoD Alternate Token IDEMIA Cosmo V8.0 2025 */ + { "3B:D8:18:00:80:B1:FE:45:1F:07:80:31:C1:64:08:06:92:0F:D5", NULL, NULL, SC_CARD_TYPE_PIV_II_OBERTHUR, 0, NULL }, + { "3b:86:80:01:80:31:c1:52:41:1a:7e", NULL, NULL, SC_CARD_TYPE_PIV_II_OBERTHUR, 0, NULL }, /* contactless */ + + /* Following PIVKEY entries are from Windows registry provided by gw@taglio.com 2022-09-05 */ + /* PIVKEY PIVKey Feitian (02) */ + { "3b:9f:95:81:31:fe:9f:00:66:46:53:05:10:00:11:71:df:00:00:00:00:00:02", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey Feitian (7C) aka C910 contactless */ + { "3b:8c:80:01:90:67:46:4a:00:64:16:06:f2:72:7e:00:7c", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /*PIVKey Feitian (E0) aka C910 */ + { "3b:fc:18:00:00:81:31:80:45:90:67:46:4a:00:64:16:06:f2:72:7e:00:e0", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey Feitian (FE) aka PIVKEY T600 token and T800 on Feitian eJAVA */ + { "3b:fc:18:00:00:81:31:80:45:90:67:46:4a:00:64:2d:70:c1:72:fe:e0:fe", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey JCOP241 (AD) */ + { "3b:f9:13:00:00:81:31:fe:45:53:50:49:56:4b:45:59:37:30:ad", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey JCOP242R2 (16) */ + { "3b:88:80:01:50:49:56:4b:45:59:37:30:16", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey JCOP242R2 (5E) */ + { "3b:88:80:01:4a:43:4f:50:76:32:34:31:5e", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey JCOP242R2 (B7) */ + { "3b:f8:13:00:00:81:31:fe:45:4a:43:4f:50:76:32:34:31:b7", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey JCOP3 (67) */ + { "3b:88:80:01:46:49:44:45:53:4d:4f:31:67", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey JCOP3 (8E) */ + { "3b:f8:13:00:00:81:31:fe:45:46:49:44:45:53:4d:4f:31:8e", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey JCOP31 (57) */ + { "3b:f9:18:00:ff:81:31:fe:45:4a:43:4f:50:33:31:56:32:32:57", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey NXP JCOP (03) */ + { "3b:8a:80:01:01:50:49:56:4b:45:59:37:30:16:03", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey NXP JCOP (FF) aka CP70 */ + { "3b:f8:13:00:00:81:31:fe:45:50:49:56:4b:45:59:37:30:ff", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey SLE78 (3B) */ + { "3b:8d:80:01:53:4c:4a:35:32:47:44:4c:31:32:38:43:52:3b", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey SLE78 (6D) */ + { "3b:88:80:01:00:00:00:11:77:81:83:00:6d", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey SLE78 (28) aka C980 */ + { "3b:f9:96:00:00:81:31:fe:45:53:50:49:56:4b:45:59:37:30:28", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey SLE78 (44) aka C980 contactless */ + { "3b:89:80:01:53:50:49:56:4b:45:59:37:30:44", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey SLE78 (57B) */ + { "3b:fd:96:00:00:81:31:fe:45:53:4c:4a:35:32:47:44:4c:31:32:38:43:52:57", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* This ATR is too generic. It will be accepted if Application Label in AID response matches */ + /* Which looks like there is no Application Label in AID response of these cards */ + /* PivApplet (at least when run in virtual machine) uses the same ATR */ + /* PIVKey uTrust (01) ISO 14443 Type B without historical bytes */ + /* { "3b:80:80:01:01", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, */ + + /* PIVKey uTrust (73) */ + { "3b:96:11:81:21:75:75:54:72:75:73:74:73", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + /* PIVKey uTrust FIDO2 (73) */ + { "3b:96:11:81:21:75:75:54:72:75:73:74:73", NULL, NULL, SC_CARD_TYPE_PIV_II_PIVKEY, 0, NULL }, + + /* Swissbit iShield Key Pro with PIV endpoint applet */ + { "3b:97:11:81:21:75:69:53:68:69:65:6c:64:05", NULL, NULL, SC_CARD_TYPE_PIV_II_SWISSBIT, 0, NULL }, + + /* ID-One PIV 2.4.1 on Cosmo V8.1 NIST sp800-73-4 with Secure Messaging and VCI 2020 */ + { "3b:d6:96:00:81:b1:fe:45:1f:87:80:31:c1:52:41:1a:2a", NULL, NULL, SC_CARD_TYPE_PIV_II_800_73_4, 0, NULL }, + { "3b:d6:97:00:81:b1:fe:45:1f:87:80:31:c1:52:41:12:23", + "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00", NULL, SC_CARD_TYPE_PIV_II_800_73_4, 0, NULL }, + { "3b:86:80:01:80:31:c1:52:41:12:76", NULL, NULL, SC_CARD_TYPE_PIV_II_800_73_4, 0, NULL }, /* contactless */ + + { NULL, NULL, NULL, 0, 0, NULL } +}; + +static struct piv_supported_ec_curves { + struct sc_object_id oid; + size_t size; + unsigned int key_type; +} ec_curves[] = { + {{{1, 2, 840, 10045, 3, 1, 7, -1}}, 256, SC_ALGORITHM_EC }, /* secp256r1, nistp256, prime256v1, ansiX9p256r1 */ + {{{1, 3, 132, 0, 34, -1}}, 384, SC_ALGORITHM_EC }, /* secp384r1, nistp384, prime384v1, ansiX9p384r1 */ + {{{1, 3, 101, 112, -1}}, 255, SC_ALGORITHM_EDDSA }, /* RFC8410 OID equivalent to ed25519 */ + {{{1, 3, 101, 110, -1}}, 255, SC_ALGORITHM_XEDDSA}, /* RFC8410 OID equivalent to curve25519 */ + {{{-1}}, 0, 0 } /* This entry must not be touched. */ +}; + +/* all cards must respond to entry 0, some may return different PIX which will accept */ static struct piv_aid piv_aids[] = { - {SC_CARD_TYPE_PIV_II_GENERIC, /* TODO not really card type but what PIV AID is supported */ + {SC_CARD_TYPE_PIV_II_GENERIC, /* Not really card type but what PIV AID is supported */ 9, 9, (u8 *) "\xA0\x00\x00\x03\x08\x00\x00\x10\x00" }, + {SC_CARD_TYPE_PIV_II_GI_DE, /* bug in some G&D cards in response to select aid */ + 9, 9, (u8 *) "\xA0\x00\x00\x03\x08\x00\x10\x00\x01" }, {0, 9, 0, NULL } }; @@ -203,15 +576,56 @@ static struct piv_aid piv_aids[] = { #define CI_VERIFY_630X 0x00000001U /* VERIFY tries left returns 630X rather then 63CX */ #define CI_VERIFY_LC0_FAIL 0x00000002U /* VERIFY Lc=0 never returns 90 00 if PIN not needed */ /* will also test after first PIN verify if protected object can be used instead */ +#define CI_NO_RANDOM 0x00000004U /* can not use Challenge to get random data or no 9B key */ #define CI_CANT_USE_GETDATA_FOR_STATE 0x00000008U /* No object to test verification inplace of VERIFY Lc=0 */ #define CI_LEAKS_FILE_NOT_FOUND 0x00000010U /* GET DATA of empty object returns 6A 82 even if PIN not verified */ +#define CI_DISCOVERY_USELESS 0x00000020U /* Discovery can not be used to query active AID invalid or no data returned */ +#define CI_PIV_AID_LOSE_STATE 0x00000040U /* PIV AID can lose the login state run with out it*/ #define CI_OTHER_AID_LOSE_STATE 0x00000100U /* Other drivers match routines may reset our security state and lose AID!!! */ #define CI_NFC_EXPOSE_TOO_MUCH 0x00000200U /* PIN, crypto and objects exposed over NFS in violation of 800-73-3 */ -#define CI_NO_RSA2048 0x00010000U /* does not have RSA 2048 */ -#define CI_NO_EC384 0x00020000U /* does not have EC 384 */ - +#define CI_NO_EC384 0x00000400U /* does not have EC 384 */ +#define CI_NO_EC 0x00000800U /* No EC at all */ + +/* PIV and PIV like cards are know to support these asymmetric algorithm identifiers used in alg_ids */ +#define AI_RSA_1024 0x00000001U +#define AI_RSA_2048 0x00000002U +#define AI_RSA_3072 0x00000004U +#define AI_RSA_4096 0x00000008U +#define AI_EC_256 0x00000100U +#define AI_EC_384 0x00000200U +#define AI_25519 0x00100000U +#define AI_X25519 0x00200000U +/* NIST cards have or do support these (non SM) algorithm identifiers */ +#define AI_NIST (AI_RSA_1024 | AI_RSA_2048 | AI_RSA_3072 | AI_EC_256 | AI_EC_384) + +static struct { + unsigned long ai_flag; + unsigned char ai_id; + unsigned long ai_sc_type; + int ai_bits; +} ai_map[] = { + { AI_RSA_3072, 0x05, SC_ALGORITHM_RSA, 3072 }, + { AI_RSA_1024, 0x06, SC_ALGORITHM_RSA, 1024 }, + { AI_RSA_2048, 0x07, SC_ALGORITHM_RSA, 2048 }, + { AI_RSA_4096, 0x16, SC_ALGORITHM_RSA,4096 }, + { AI_EC_256, 0x11, SC_ALGORITHM_EC, 256 }, + { AI_EC_384, 0x14, SC_ALGORITHM_EC, 384 }, + { AI_25519, 0xE0, SC_ALGORITHM_EDDSA, 255 }, + { AI_X25519, 0xE1, SC_ALGORITHM_XEDDSA, 255 }, + { 0, 0, 0, 0 } + }; + +/* Application Label from select AID response where ATR can not be matched */ +static struct { + int al_type; + unsigned char * al_label; + int al_labellen; +} al_map[] = { + { SC_CARD_TYPE_PIV_II_PIVAPPLET, (u8 *)"PivApplet v", 11 }, + { 0, NULL, 0 } + }; /* * Flags in the piv_object: @@ -219,13 +633,18 @@ static struct piv_aid piv_aids[] = { * indicated by the History object. */ -#define PIV_OBJECT_TYPE_CERT 1 -#define PIV_OBJECT_TYPE_PUBKEY 2 -#define PIV_OBJECT_NOT_PRESENT 4 +#define PIV_OBJECT_TYPE_CERT 0x01 +#define PIV_OBJECT_TYPE_PUBKEY 0x02 +#define PIV_OBJECT_NOT_PRESENT 0x04 +#define PIV_OBJECT_TYPE_CVC 0x08 /* is in cert object */ +#define PIV_OBJECT_NEEDS_PIN 0x10 /* On both contact and contactless */ +#define PIV_OBJECT_NEEDS_VCI 0x20 /* NIST sp800-73-4 Requires VCI on contactless and card enforces this. */ + /* But also See CI_NFC_EXPOSE_TOO_MUCH for non approved PIV-like cards */ struct piv_object { int enumtag; const char * name; + unsigned int resp_tag; const char * oidstring; size_t tag_len; u8 tag_value[3]; @@ -236,603 +655,2354 @@ struct piv_object { /* Must be in order, and one per enumerated PIV_OBJ */ static const struct piv_object piv_objects[] = { { PIV_OBJ_CCC, "Card Capability Container", - "2.16.840.1.101.3.7.1.219.0", 3, "\x5F\xC1\x07", "\xDB\x00", 0}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.1.219.0", 3, {0x5F, 0xC1, 0x07}, {0xDB, 0x00}, PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_CHUI, "Card Holder Unique Identifier", - "2.16.840.1.101.3.7.2.48.0", 3, "\x5F\xC1\x02", "\x30\x00", 0}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.48.0", 3, {0x5F, 0xC1, 0x02}, {0x30, 0x00}, 0}, { PIV_OBJ_X509_PIV_AUTH, "X.509 Certificate for PIV Authentication", - "2.16.840.1.101.3.7.2.1.1", 3, "\x5F\xC1\x05", "\x01\x01", PIV_OBJECT_TYPE_CERT} , + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.1.1", 3, {0x5F, 0xC1, 0x05}, {0x01, 0x01}, PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI} , { PIV_OBJ_CHF, "Card Holder Fingerprints", - "2.16.840.1.101.3.7.2.96.16", 3, "\x5F\xC1\x03", "\x60\x10", 0}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.96.16", 3, {0x5F, 0xC1, 0x03}, {0x60, 0x10}, PIV_OBJECT_NEEDS_PIN | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_PI, "Printed Information", - "2.16.840.1.101.3.7.2.48.1", 3, "\x5F\xC1\x09", "\x30\x01", 0}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.48.1", 3, {0x5F, 0xC1, 0x09}, {0x30, 0x01}, PIV_OBJECT_NEEDS_PIN | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_CHFI, "Cardholder Facial Images", - "2.16.840.1.101.3.7.2.96.48", 3, "\x5F\xC1\x08", "\x60\x30", 0}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.96.48", 3, {0x5F, 0xC1, 0x08}, {0x60, 0x30}, PIV_OBJECT_NEEDS_PIN | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_X509_DS, "X.509 Certificate for Digital Signature", - "2.16.840.1.101.3.7.2.1.0", 3, "\x5F\xC1\x0A", "\x01\x00", PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.1.0", 3, {0x5F, 0xC1, 0x0A}, {0x01, 0x00}, PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_X509_KM, "X.509 Certificate for Key Management", - "2.16.840.1.101.3.7.2.1.2", 3, "\x5F\xC1\x0B", "\x01\x02", PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.1.2", 3, {0x5F, 0xC1, 0x0B}, {0x01, 0x02}, PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_X509_CARD_AUTH, "X.509 Certificate for Card Authentication", - "2.16.840.1.101.3.7.2.5.0", 3, "\x5F\xC1\x01", "\x05\x00", PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.5.0", 3, {0x5F, 0xC1, 0x01}, {0x05, 0x00}, PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_SEC_OBJ, "Security Object", - "2.16.840.1.101.3.7.2.144.0", 3, "\x5F\xC1\x06", "\x90\x00", 0}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.144.0", 3, {0x5F, 0xC1, 0x06}, {0x90, 0x00}, PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_DISCOVERY, "Discovery Object", - "2.16.840.1.101.3.7.2.96.80", 1, "\x7E", "\x60\x50", 0}, + SC_ASN1_APP | SC_ASN1_CONS | 0x1E, + "2.16.840.1.101.3.7.2.96.80", 1, {0x7E}, {0x60, 0x50}, 0}, { PIV_OBJ_HISTORY, "Key History Object", - "2.16.840.1.101.3.7.2.96.96", 3, "\x5F\xC1\x0C", "\x60\x60", 0}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.96.96", 3, {0x5F, 0xC1, 0x0C}, {0x60, 0x60}, PIV_OBJECT_NEEDS_VCI}, /* 800-73-3, 21 new objects, 20 history certificates */ { PIV_OBJ_RETIRED_X509_1, "Retired X.509 Certificate for Key Management 1", - "2.16.840.1.101.3.7.2.16.1", 3, "\x5F\xC1\x0D", "\x10\x01", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.1", 3, {0x5F, 0xC1, 0x0D}, {0x10, 0x01}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_2, "Retired X.509 Certificate for Key Management 2", - "2.16.840.1.101.3.7.2.16.2", 3, "\x5F\xC1\x0E", "\x10\x02", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.2", 3, {0x5F, 0xC1, 0x0E}, {0x10, 0x02}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_3, "Retired X.509 Certificate for Key Management 3", - "2.16.840.1.101.3.7.2.16.3", 3, "\x5F\xC1\x0F", "\x10\x03", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.3", 3, {0x5F, 0xC1, 0x0F}, {0x10, 0x03}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_4, "Retired X.509 Certificate for Key Management 4", - "2.16.840.1.101.3.7.2.16.4", 3, "\x5F\xC1\x10", "\x10\x04", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.4", 3, {0x5F, 0xC1, 0x10}, {0x10, 0x04}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_5, "Retired X.509 Certificate for Key Management 5", - "2.16.840.1.101.3.7.2.16.5", 3, "\x5F\xC1\x11", "\x10\x05", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.5", 3, {0x5F, 0xC1, 0x11}, {0x10, 0x05}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_6, "Retired X.509 Certificate for Key Management 6", - "2.16.840.1.101.3.7.2.16.6", 3, "\x5F\xC1\x12", "\x10\x06", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.6", 3, {0x5F, 0xC1, 0x12}, {0x10, 0x06}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_7, "Retired X.509 Certificate for Key Management 7", - "2.16.840.1.101.3.7.2.16.7", 3, "\x5F\xC1\x13", "\x10\x07", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.7", 3, {0x5F, 0xC1, 0x13}, {0x10, 0x07}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_8, "Retired X.509 Certificate for Key Management 8", - "2.16.840.1.101.3.7.2.16.8", 3, "\x5F\xC1\x14", "\x10\x08", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.8", 3, {0x5F, 0xC1, 0x14}, {0x10, 0x08}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_9, "Retired X.509 Certificate for Key Management 9", - "2.16.840.1.101.3.7.2.16.9", 3, "\x5F\xC1\x15", "\x10\x09", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.9", 3, {0x5F, 0xC1, 0x15}, {0x10, 0x09}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_10, "Retired X.509 Certificate for Key Management 10", - "2.16.840.1.101.3.7.2.16.10", 3, "\x5F\xC1\x16", "\x10\x0A", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.10", 3, {0x5F, 0xC1, 0x16}, {0x10, 0x0A}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_11, "Retired X.509 Certificate for Key Management 11", - "2.16.840.1.101.3.7.2.16.11", 3, "\x5F\xC1\x17", "\x10\x0B", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.11", 3, {0x5F, 0xC1, 0x17}, {0x10, 0x0B}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_12, "Retired X.509 Certificate for Key Management 12", - "2.16.840.1.101.3.7.2.16.12", 3, "\x5F\xC1\x18", "\x10\x0C", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.12", 3, {0x5F, 0xC1, 0x18}, {0x10, 0x0C}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_13, "Retired X.509 Certificate for Key Management 13", - "2.16.840.1.101.3.7.2.16.13", 3, "\x5F\xC1\x19", "\x10\x0D", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.13", 3, {0x5F, 0xC1, 0x19}, {0x10, 0x0D}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_14, "Retired X.509 Certificate for Key Management 14", - "2.16.840.1.101.3.7.2.16.14", 3, "\x5F\xC1\x1A", "\x10\x0E", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.14", 3, {0x5F, 0xC1, 0x1A}, {0x10, 0x0E}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_15, "Retired X.509 Certificate for Key Management 15", - "2.16.840.1.101.3.7.2.16.15", 3, "\x5F\xC1\x1B", "\x10\x0F", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.15", 3, {0x5F, 0xC1, 0x1B}, {0x10, 0x0F}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_16, "Retired X.509 Certificate for Key Management 16", - "2.16.840.1.101.3.7.2.16.16", 3, "\x5F\xC1\x1C", "\x10\x10", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.16", 3, {0x5F, 0xC1, 0x1C}, {0x10, 0x10}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_17, "Retired X.509 Certificate for Key Management 17", - "2.16.840.1.101.3.7.2.16.17", 3, "\x5F\xC1\x1D", "\x10\x11", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.17", 3, {0x5F, 0xC1, 0x1D}, {0x10, 0x11}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_18, "Retired X.509 Certificate for Key Management 18", - "2.16.840.1.101.3.7.2.16.18", 3, "\x5F\xC1\x1E", "\x10\x12", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.18", 3, {0x5F, 0xC1, 0x1E}, {0x10, 0x12}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_19, "Retired X.509 Certificate for Key Management 19", - "2.16.840.1.101.3.7.2.16.19", 3, "\x5F\xC1\x1F", "\x10\x13", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.19", 3, {0x5F, 0xC1, 0x1F}, {0x10, 0x13}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_RETIRED_X509_20, "Retired X.509 Certificate for Key Management 20", - "2.16.840.1.101.3.7.2.16.20", 3, "\x5F\xC1\x20", "\x10\x14", - PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.20", 3, {0x5F, 0xC1, 0x20}, {0x10, 0x14}, + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_IRIS_IMAGE, "Cardholder Iris Images", - "2.16.840.1.101.3.7.2.16.21", 3, "\x5F\xC1\x21", "\x10\x15", 0}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.21", 3, {0x5F, 0xC1, 0x21}, {0x10, 0x15}, PIV_OBJECT_NEEDS_PIN | PIV_OBJECT_NEEDS_VCI}, + +/* 800-73-4, 3 new objects */ + { PIV_OBJ_BITGT, "Biometric Information Templates Group Template", + SC_ASN1_APP | SC_ASN1_CONS | 0x1F61, + "2.16.840.1.101.3.7.2.16.22", 2, {0x7F, 0x61}, {0x10, 0x16}, 0}, + { PIV_OBJ_SM_CERT_SIGNER, "Secure Messaging Certificate Signer", + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.23", 3, {0x5F, 0xC1, 0x22}, {0x10, 0x17}, + PIV_OBJECT_TYPE_CERT | PIV_OBJECT_TYPE_CVC}, + {PIV_OBJ_PCRDCS, "Pairing Code Reference Data Container", + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.16.24", 3, {0x5F, 0xC1, 0x23}, {0x10, 0x18}, PIV_OBJECT_NEEDS_PIN | PIV_OBJECT_NEEDS_VCI}, /* following not standard , to be used by piv-tool only for testing */ { PIV_OBJ_9B03, "3DES-ECB ADM", - "2.16.840.1.101.3.7.2.9999.3", 2, "\x9B\x03", "\x9B\x03", 0}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.3", 2, {0x9B, 0x03}, {0x9B, 0x03}, 0}, /* Only used when signing a cert req, usually from engine * after piv-tool generated the key and saved the pub key * to a file. Note RSA key can be 1024, 2048 or 3072 * but still use the "9x06" name. */ { PIV_OBJ_9A06, "RSA 9A Pub key from last genkey", - "2.16.840.1.101.3.7.2.9999.20", 2, "\x9A\x06", "\x9A\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.20", 2, {0x9A, 0x06}, {0x9A, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9C06, "Pub 9C key from last genkey", - "2.16.840.1.101.3.7.2.9999.21", 2, "\x9C\x06", "\x9C\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.21", 2, {0x9C, 0x06}, {0x9C, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9D06, "Pub 9D key from last genkey", - "2.16.840.1.101.3.7.2.9999.22", 2, "\x9D\x06", "\x9D\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.22", 2, {0x9D, 0x06}, {0x9D, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9E06, "Pub 9E key from last genkey", - "2.16.840.1.101.3.7.2.9999.23", 2, "\x9E\x06", "\x9E\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.23", 2, {0x9E, 0x06}, {0x9E, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8206, "Pub 82 key ", - "2.16.840.1.101.3.7.2.9999.101", 2, "\x82\x06", "\x82\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.101", 2, {0x82, 0x06}, {0x82, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8306, "Pub 83 key ", - "2.16.840.1.101.3.7.2.9999.102", 2, "\x83\x06", "\x83\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.102", 2, {0x83, 0x06}, {0x83, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8406, "Pub 84 key ", - "2.16.840.1.101.3.7.2.9999.103", 2, "\x84\x06", "\x84\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.103", 2, {0x84, 0x06}, {0x84, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8506, "Pub 85 key ", - "2.16.840.1.101.3.7.2.9999.104", 2, "\x85\x06", "\x85\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.104", 2, {0x85, 0x06}, {0x85, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8606, "Pub 86 key ", - "2.16.840.1.101.3.7.2.9999.105", 2, "\x86\x06", "\x86\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.105", 2, {0x86, 0x06}, {0x86, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8706, "Pub 87 key ", - "2.16.840.1.101.3.7.2.9999.106", 2, "\x87\x06", "\x87\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.106", 2, {0x87, 0x06}, {0x87, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8806, "Pub 88 key ", - "2.16.840.1.101.3.7.2.9999.107", 2, "\x88\x06", "\x88\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.107", 2, {0x88, 0x06}, {0x88, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8906, "Pub 89 key ", - "2.16.840.1.101.3.7.2.9999.108", 2, "\x89\x06", "\x89\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.108", 2, {0x89, 0x06}, {0x89, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8A06, "Pub 8A key ", - "2.16.840.1.101.3.7.2.9999.109", 2, "\x8A\x06", "\x8A\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.109", 2, {0x8A, 0x06}, {0x8A, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8B06, "Pub 8B key ", - "2.16.840.1.101.3.7.2.9999.110", 2, "\x8B\x06", "\x8B\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.110", 2, {0x8B, 0x06}, {0x8B, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8C06, "Pub 8C key ", - "2.16.840.1.101.3.7.2.9999.111", 2, "\x8C\x06", "\x8C\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.111", 2, {0x8C, 0x06}, {0x8C, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8D06, "Pub 8D key ", - "2.16.840.1.101.3.7.2.9999.112", 2, "\x8D\x06", "\x8D\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.112", 2, {0x8D, 0x06}, {0x8D, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8E06, "Pub 8E key ", - "2.16.840.1.101.3.7.2.9999.113", 2, "\x8E\x06", "\x8E\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.113", 2, {0x8E, 0x06}, {0x8E, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8F06, "Pub 8F key ", - "2.16.840.1.101.3.7.2.9999.114", 2, "\x8F\x06", "\x8F\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.114", 2, {0x8F, 0x06}, {0x8F, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9006, "Pub 90 key ", - "2.16.840.1.101.3.7.2.9999.115", 2, "\x90\x06", "\x90\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.115", 2, {0x90, 0x06}, {0x90, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9106, "Pub 91 key ", - "2.16.840.1.101.3.7.2.9999.116", 2, "\x91\x06", "\x91\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.116", 2, {0x91, 0x06}, {0x91, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9206, "Pub 92 key ", - "2.16.840.1.101.3.7.2.9999.117", 2, "\x92\x06", "\x92\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.117", 2, {0x92, 0x06}, {0x92, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9306, "Pub 93 key ", - "2.16.840.1.101.3.7.2.9999.118", 2, "\x93\x06", "\x93\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.118", 2, {0x93, 0x06}, {0x93, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9406, "Pub 94 key ", - "2.16.840.1.101.3.7.2.9999.119", 2, "\x94\x06", "\x94\x06", PIV_OBJECT_TYPE_PUBKEY}, + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.119", 2, {0x94, 0x06}, {0x94, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9506, "Pub 95 key ", - "2.16.840.1.101.3.7.2.9999.120", 2, "\x95\x06", "\x95\x06", PIV_OBJECT_TYPE_PUBKEY}, - { PIV_OBJ_LAST_ENUM, "", "", 0, "", "", 0} + SC_ASN1_APP | 0x13, + "2.16.840.1.101.3.7.2.9999.120", 2, {0x95, 0x06}, {0x95, 0x06}, PIV_OBJECT_TYPE_PUBKEY}, + /* + * "Secure Messaging Certificate Signer" is just a certificate. + * No pub or private key on the card. + */ + { PIV_OBJ_LAST_ENUM, "", 0, "", 0, "", "", 0} }; +// clang-format on static struct sc_card_operations piv_ops; static struct sc_card_driver piv_drv = { - "PIV-II for multiple cards", - "PIV-II", - &piv_ops, - NULL, 0, NULL -}; - - -static int -piv_find_obj_by_containerid(sc_card_t *card, const u8 * str) + "Personal Identity Verification Card", + "PIV-II", + &piv_ops, + NULL, 0, NULL}; + +static int piv_get_cached_data(sc_card_t *card, int enumtag, u8 **buf, size_t *buf_len); +static int piv_cache_internal_data(sc_card_t *card, int enumtag); +static int piv_logout(sc_card_t *card); +static int piv_match_card_continued(sc_card_t *card); +static int piv_obj_cache_free_entry(sc_card_t *card, int enumtag, int flags); +static int piv_ai_map_find_by_id(sc_card_t *card, u8); + +#ifdef ENABLE_PIV_SM +static void piv_inc(u8 *counter, size_t size); +static int piv_encode_apdu(sc_card_t *card, sc_apdu_t *plain, sc_apdu_t *sm_apdu); +static int piv_get_sm_apdu(sc_card_t *card, sc_apdu_t *plain, sc_apdu_t **sm_apdu); +static int piv_free_sm_apdu(sc_card_t *card, sc_apdu_t *plain, sc_apdu_t **sm_apdu); +static int piv_get_asn1_obj(sc_context_t *ctx, void *arg, const u8 *obj, size_t len, int depth); +static int piv_sm_open(struct sc_card *card); +static int piv_decode_apdu(sc_card_t *card, sc_apdu_t *plain, sc_apdu_t *sm_apdu); +static int piv_sm_close(sc_card_t *card); +static void piv_clear_cvc_content(piv_cvc_t *cvc); +static void piv_clear_sm_session(piv_sm_session_t *session); +static int piv_decode_cvc(sc_card_t *card, u8 **buf, size_t *buflen, piv_cvc_t *cvc); +static int piv_parse_pairing_code(sc_card_t *card, const char *option); +static int Q2OS(int fsize, u8 *Q, size_t Qlen, u8 *OS, size_t *OSlen); +static int piv_send_vci_pairing_code(struct sc_card *card, u8 *paring_code); +static int piv_sm_verify_sig(struct sc_card *card, const EVP_MD *type, + EVP_PKEY *pkey, u8 *data, size_t data_size, + unsigned char *sig, size_t siglen); +static int piv_sm_verify_certs(struct sc_card *card); + +static void +piv_inc(u8 *counter, size_t size) { + unsigned int c = 1; + unsigned int b; int i; - - LOG_FUNC_CALLED(card->ctx); - sc_log(card->ctx, "str=0x%02X%02X\n", str[0], str[1]); - - for (i = 0; piv_objects[i].enumtag < PIV_OBJ_LAST_ENUM; i++) { - if ( str[0] == piv_objects[i].containerid[0] && str[1] == piv_objects[i].containerid[1]) - LOG_FUNC_RETURN(card->ctx, i); + for (i = (int)size - 1; c != 0 && i >= 0; i--) { + b = c + counter[i]; + counter[i] = b & 0xff; + c = b >> 8; } - - LOG_FUNC_RETURN(card->ctx, -1); } /* - * If ptr == NULL, just return the size of the tag and lenght and data - * otherwise, store tag and length at **ptr, and increment + * Construct SM protected APDU */ -static size_t -put_tag_and_len(unsigned int tag, size_t len, u8 **ptr) +static int +piv_encode_apdu(sc_card_t *card, sc_apdu_t *plain, sc_apdu_t *sm_apdu) { - int i; + int r = 0; + piv_private_data_t *priv = PIV_DATA(card); + cipher_suite_t *cs = priv->cs; + u8 pad[16] = {0x80}; + u8 zeros[16] = {0x00}; + u8 IV[16]; + u8 header[16]; + int padlen = 16; /* may be less */ + u8 *sbuf = NULL; + size_t sbuflen = 0; + int MCVlen = 16; + int enc_datalen = 0; + int T87len; + int T97len = 2 + 1; + int T8Elen = 2 + 8; + + int outli = 0; + int outl = 0; + int outll = 0; + int outdl = 0; + u8 discard[16]; + int macdatalen; + size_t C_MCVlen = 16; /* debugging*/ + u8 *p; + EVP_CIPHER_CTX *ed_ctx = NULL; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + CMAC_CTX *cmac_ctx = NULL; +#else + EVP_MAC_CTX *cmac_ctx = NULL; + EVP_MAC *mac = NULL; + OSSL_PARAM cmac_params[2]; + size_t cmac_params_n; +#endif - if (len < 128) { - i = 2; - } else if (len < 256) { - i = 3; - } else { - i = 4; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + cmac_ctx = CMAC_CTX_new(); + if (cmac_ctx == NULL) { + sc_log_openssl(card->ctx); + r = SC_ERROR_INTERNAL; + goto err; + } +#else + mac = EVP_MAC_fetch(PIV_LIBCTX, "cmac", NULL); + cmac_params_n = 0; + cmac_params[cmac_params_n++] = OSSL_PARAM_construct_utf8_string("cipher", cs->cipher_cbc_name, 0); + cmac_params[cmac_params_n] = OSSL_PARAM_construct_end(); + if (mac == NULL || (cmac_ctx = EVP_MAC_CTX_new(mac)) == NULL) { + sc_log_openssl(card->ctx); + r = SC_ERROR_INTERNAL; + goto err; } +#endif - if (ptr) { - p = *ptr; - *p++ = (u8)tag; - switch (i) { - case 2: - *p++ = len; - break; - case 3: - *p++ = 0x81; - *p++ = len; - break; - case 4: - *p++ = 0x82; - *p++ = (u8) (len >> 8); - *p++ = (u8) (len & 0xff); - break; - } - *ptr = p; - } else { - i += len; + ed_ctx = EVP_CIPHER_CTX_new(); + if (ed_ctx == NULL) { + r = SC_ERROR_INTERNAL; + goto err; } - return i; -} -/* - * Send a command and receive data. There is always something to send. - * Used by GET DATA, PUT DATA, GENERAL AUTHENTICATE - * and GENERATE ASYMMETRIC KEY PAIR. - * GET DATA may call to get the first 128 bytes to get the lenght from the tag. - * - * A caller may provide a buffer, and length to read. If not provided, - * an internal 4096 byte buffer is used, and a copy is returned to the - * caller. that need to be freed by the caller. - */ + if (EVP_EncryptInit_ex(ed_ctx, (*cs->cipher_ecb)(), NULL, priv->sm_session.SKenc, zeros) != 1 || + EVP_CIPHER_CTX_set_padding(ed_ctx, 0) != 1 || + EVP_EncryptUpdate(ed_ctx, IV, &outli, priv->sm_session.enc_counter, 16) != 1 || + EVP_EncryptFinal_ex(ed_ctx, discard, &outdl) != 1 || + outdl != 0) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "SM encode failed in OpenSSL"); + r = SC_ERROR_INTERNAL; + goto err; + } -static int piv_general_io(sc_card_t *card, int ins, int p1, int p2, - const u8 * sendbuf, size_t sendbuflen, u8 ** recvbuf, - size_t * recvbuflen) -{ - int r; - sc_apdu_t apdu; - u8 rbufinitbuf[4096]; - u8 *rbuf; - size_t rbuflen; - unsigned int cla_out, tag_out; - const u8 *body; - size_t bodylen; - int find_len = 0; - piv_private_data_t * priv = PIV_DATA(card); + sm_apdu->cla = 0x0c; + sm_apdu->ins = plain->ins; + sm_apdu->p1 = plain->p1; + sm_apdu->p2 = plain->p2; + /* + * All APDUs will be converted to case as SM data is always sent and received + * if plain->cse == SC_APDU_CASE_1 it never has the the 0x20 bit set + * which "let OpenSC decides whether to use short or extended APDUs" + * PIV SM data added for plain->cse == SC_APDU_CASE_1 will not need extended APDUs. + * + * NIST 800-73-4 does not say if cards can or must support extended APDUs + * they must support command chaining and multiple get response APDUs and + * all examples use short APDUs. The following keep the option open to use extended + * APDUs in future specifications or "PIV like" cards are know to + * support extended APDUs. + * + * Turn off the CASE bits, and set CASE 4 in sm_apdu. + */ - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + sm_apdu->cse = (plain->cse & ~SC_APDU_SHORT_MASK) | SC_APDU_CASE_4_SHORT; - sc_log(card->ctx, - "%02x %02x %02x %"SC_FORMAT_LEN_SIZE_T"u : %"SC_FORMAT_LEN_SIZE_T"u %"SC_FORMAT_LEN_SIZE_T"u", - ins, p1, p2, sendbuflen, card->max_send_size, - card->max_recv_size); + p = header; /* to be included in CMAC */ + *p++ = 0x0c; + *p++ = plain->ins; + *p++ = plain->p1; + *p++ = plain->p2; + memcpy(p, pad, 12); - rbuf = rbufinitbuf; - rbuflen = sizeof(rbufinitbuf); + /* 800-73-4 say padding is 1 to 16 bytes, with 0x80 0x00... */ - /* if caller provided a buffer and length */ - if (recvbuf && *recvbuf && recvbuflen && *recvbuflen) { - rbuf = *recvbuf; - rbuflen = *recvbuflen; + /* may not need enc_data for cse 1 or 2 */ + if (plain->datalen == 0) { + enc_datalen = 0; + T87len = 0; + padlen = 0; + } else { + enc_datalen = (int)(((plain->datalen + 15) / 16) * 16); /* may add extra 16 bytes */ + padlen = enc_datalen - (int)plain->datalen; + r = T87len = sc_asn1_put_tag(0x87, NULL, 1 + enc_datalen, NULL, 0, NULL); + if (r < 0) + goto err; } - r = sc_lock(card); - if (r != SC_SUCCESS) - LOG_FUNC_RETURN(card->ctx, r); + if (plain->resplen == 0 || plain->le == 0) + T97len = 0; - sc_format_apdu(card, &apdu, - recvbuf ? SC_APDU_CASE_4_SHORT: SC_APDU_CASE_3_SHORT, - ins, p1, p2); - apdu.flags |= SC_APDU_FLAGS_CHAINING; - /* if looking for length of object, dont try and read the rest of buffer here */ - if (rbuflen == 8 && card->reader->active_protocol == SC_PROTO_T1) { - apdu.flags |= SC_APDU_FLAGS_NO_GET_RESP; - find_len = 1; + sbuflen = T87len + T97len + T8Elen; + + sbuf = calloc(1, sbuflen); + if (sbuf == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; } - apdu.lc = sendbuflen; - apdu.datalen = sendbuflen; - apdu.data = sendbuf; + p = sbuf; + if (T87len != 0) { + r = sc_asn1_put_tag(0x87, NULL, 1 + enc_datalen, sbuf, sbuflen, &p); + if (r != SC_SUCCESS) + goto err; - if (recvbuf) { - apdu.resp = rbuf; - apdu.le = (rbuflen > 256) ? 256 : rbuflen; - apdu.resplen = rbuflen; - } else { - apdu.resp = rbuf; - apdu.le = 0; - apdu.resplen = 0; + *p++ = 0x01; /* padding context indicator */ + + /* first round encryptes Enc counter with zero IV, and does not save the output */ + if (EVP_CIPHER_CTX_reset(ed_ctx) != 1 || + EVP_EncryptInit_ex(ed_ctx, (*cs->cipher_cbc)(), NULL, priv->sm_session.SKenc, IV) != 1 || + EVP_CIPHER_CTX_set_padding(ed_ctx, 0) != 1 || + EVP_EncryptUpdate(ed_ctx, p, &outl, plain->data, (int)plain->datalen) != 1 || + EVP_EncryptUpdate(ed_ctx, p + outl, &outll, pad, padlen) != 1 || + EVP_EncryptFinal_ex(ed_ctx, discard, &outdl) != 1 || + outdl != 0) { /* should not happen */ + sc_log_openssl(card->ctx); + sc_log(card->ctx, "SM _encode failed in OpenSSL"); + r = SC_ERROR_INTERNAL; + goto err; + } + p += enc_datalen; } - sc_log(card->ctx, - "calling sc_transmit_apdu flags=%lx le=%"SC_FORMAT_LEN_SIZE_T"u, resplen=%"SC_FORMAT_LEN_SIZE_T"u, resp=%p", - apdu.flags, apdu.le, apdu.resplen, apdu.resp); + if (T97len) { + *p++ = 0x97; + *p++ = 0x01; + *p++ = plain->le; + } + macdatalen = (int)(p - sbuf); - /* with new adpu.c and chaining, this actually reads the whole object */ - r = sc_transmit_apdu(card, &apdu); + memcpy(priv->sm_session.C_MCV_last, priv->sm_session.C_MCV, MCVlen); /* save is case fails */ - sc_log(card->ctx, - "DEE r=%d apdu.resplen=%"SC_FORMAT_LEN_SIZE_T"u sw1=%02x sw2=%02x", - r, apdu.resplen, apdu.sw1, apdu.sw2); - if (r < 0) { - sc_log(card->ctx, "Transmit failed"); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if (CMAC_Init(cmac_ctx, priv->sm_session.SKmac, priv->sm_session.aes_size, (*cs->cipher_cbc)(), NULL) != 1 || + CMAC_Update(cmac_ctx, priv->sm_session.C_MCV, MCVlen) != 1 || + CMAC_Update(cmac_ctx, header, sizeof(header)) != 1 || + CMAC_Update(cmac_ctx, sbuf, macdatalen) != 1 || + CMAC_Final(cmac_ctx, priv->sm_session.C_MCV, &C_MCVlen) != 1) { + sc_log_openssl(card->ctx); + r = SC_ERROR_INTERNAL; + goto err; + } +#else + if (!EVP_MAC_init(cmac_ctx, (const unsigned char *)priv->sm_session.SKmac, + priv->sm_session.aes_size, cmac_params) || + !EVP_MAC_update(cmac_ctx, priv->sm_session.C_MCV, MCVlen) || + !EVP_MAC_update(cmac_ctx, header, sizeof(header)) || + !EVP_MAC_update(cmac_ctx, sbuf, macdatalen) || + !EVP_MAC_final(cmac_ctx, priv->sm_session.C_MCV, &C_MCVlen, MCVlen)) { + sc_log_openssl(card->ctx); + r = SC_ERROR_INTERNAL; goto err; } +#endif - if (!(find_len && apdu.sw1 == 0x61)) - r = sc_check_sw(card, apdu.sw1, apdu.sw2); + *p++ = 0x8E; + *p++ = 0x08; + memcpy(p, priv->sm_session.C_MCV, 8); + p += 8; + if (p != sbuf + sbuflen) { /* debugging */ + r = SC_ERROR_INTERNAL; + goto err; + } + sm_apdu->data = sbuf; + sm_apdu->datalen = sbuflen; + sbuf = NULL; -/* TODO: - DEE look later at tag vs size read too */ - if (r < 0) { - sc_log(card->ctx, "Card returned error "); + sm_apdu->lc = sm_apdu->datalen; + if (sm_apdu->datalen > 255) + sm_apdu->flags |= SC_APDU_FLAGS_CHAINING; + + sm_apdu->resplen = plain->resplen + 40; /* expect at least tagged status and rmac8 */ + sm_apdu->resp = malloc(sm_apdu->resplen); + if (sm_apdu->resp == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; goto err; } + sm_apdu->le = 256; /* always ask for 256 */ - /* - * See how much we read and make sure it is asn1 - * if not, return 0 indicating no data found - */ + memcpy(priv->sm_session.enc_counter_last, priv->sm_session.enc_counter, sizeof(priv->sm_session.enc_counter)); + piv_inc(priv->sm_session.enc_counter, sizeof(priv->sm_session.enc_counter)); + + r = SC_SUCCESS; +err: + + free(sbuf); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + CMAC_CTX_free(cmac_ctx); +#else + EVP_MAC_CTX_free(cmac_ctx); + EVP_MAC_free(mac); +#endif + EVP_CIPHER_CTX_free(ed_ctx); - rbuflen = 0; /* in case rseplen < 3 i.e. not parseable */ - /* we may only be using get data to test the security status of the card, so zero length is OK */ - if ( recvbuflen && recvbuf && apdu.resplen > 3 && priv->pin_cmd_noparse != 1) { - *recvbuflen = 0; - /* we should have all the tag data, so we have to tell sc_asn1_find_tag - * the buffer is bigger, so it will not produce "ASN1.tag too long!" */ + LOG_FUNC_RETURN(card->ctx, r); +} - body = rbuf; - if (sc_asn1_read_tag(&body, 0xffff, &cla_out, &tag_out, &bodylen) != SC_SUCCESS) { - /* only early beta cards had this problem */ - sc_log(card->ctx, "***** received buffer tag MISSING "); - body = rbuf; - /* some readers/cards might return 6c 00 */ - if (apdu.sw1 == 0x61 || apdu.sw2 == 0x6c ) - bodylen = 12000; - else - bodylen = apdu.resplen; - } - - rbuflen = body - rbuf + bodylen; - - /* if using internal buffer, alloc new one */ - if (rbuf == rbufinitbuf) { - *recvbuf = malloc(rbuflen); - if (*recvbuf == NULL) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } +static int +piv_get_sm_apdu(sc_card_t *card, sc_apdu_t *plain, sc_apdu_t **sm_apdu) +{ + int r = SC_SUCCESS; + piv_private_data_t *priv = PIV_DATA(card); + cipher_suite_t *cs = priv->cs; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - memcpy(*recvbuf, rbuf, rbuflen); /* copy tag too */ + if (!plain || !sm_apdu) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + /* Does card support SM? Should not be here */ + if (priv->csID == 0 || cs == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_SM_NOT_APPLIED); + + switch (plain->ins) { + case 0xCB: /* GET_DATA */ + /* If not contactless, could read in clear */ + /* Discovery object never has PIV_SM_GET_DATA_IN_CLEAR set */ + sc_log(card->ctx, "init_flags:0x%8.8x sm_flags:0x%8.8lx", priv->init_flags, priv->sm_flags); + if (!(priv->init_flags & PIV_INIT_CONTACTLESS) && + !(priv->init_flags & PIV_INIT_IN_READER_LOCK_OBTAINED) && + (priv->sm_flags & PIV_SM_GET_DATA_IN_CLEAR)) { + priv->sm_flags &= ~PIV_SM_GET_DATA_IN_CLEAR; + LOG_FUNC_RETURN(card->ctx, SC_ERROR_SM_NOT_APPLIED); } + break; + case 0x20: /* VERIFY */ + break; + case 0x24: /* CHANGE REFERENCE DATA */ + break; + case 0x87: /* GENERAL AUTHENTICATE */ + break; + default: /* just issue the plain apdu */ + LOG_FUNC_RETURN(card->ctx, SC_ERROR_SM_NOT_APPLIED); } - if (recvbuflen) { - *recvbuflen = rbuflen; - r = *recvbuflen; + *sm_apdu = calloc(1, sizeof(sc_apdu_t)); + if (*sm_apdu == NULL) { + return SC_ERROR_OUT_OF_MEMORY; + } + + r = piv_encode_apdu(card, plain, *sm_apdu); + if (r < 0 && *sm_apdu) { + piv_free_sm_apdu(card, NULL, sm_apdu); } -err: - sc_unlock(card); LOG_FUNC_RETURN(card->ctx, r); } -/* Add the PIV-II operations */ -/* Should use our own keydata, actually should be common to all cards */ -/* RSA and EC are added. */ +/* ASN1 callback to save address and len of the object */ +static int +piv_get_asn1_obj(sc_context_t *ctx, void *arg, const u8 *obj, size_t len, int depth) +{ + struct sc_lv_data *al = arg; -static int piv_generate_key(sc_card_t *card, - sc_cardctl_piv_genkey_info_t *keydata) + if (!arg) + return SC_ERROR_INTERNAL; + + al->value = (u8 *)obj; + al->len = len; + return SC_SUCCESS; +} + +static int +piv_decode_apdu(sc_card_t *card, sc_apdu_t *plain, sc_apdu_t *sm_apdu) { - int r; - u8 *rbuf = NULL; - size_t rbuflen = 0; + int r = SC_SUCCESS; + int i; + piv_private_data_t *priv = PIV_DATA(card); + cipher_suite_t *cs = priv->cs; + struct sc_lv_data ee = {NULL, 0}; + struct sc_lv_data status = {NULL, 0}; + struct sc_lv_data rmac8 = {NULL, 0}; + u8 zeros[16] = {0}; + u8 IV[16]; u8 *p; - const u8 *tag; - u8 tagbuf[16]; - u8 outdata[3]; /* we could also add tag 81 for exponent */ - size_t taglen, i; - size_t out_len; - size_t in_len; - unsigned int cla_out, tag_out; + int outl; + int outli; + int outll; + int outdl; + u8 lastb[16]; + u8 discard[8]; + u8 *q = NULL; + int inlen; + int macdatalen; + + size_t MCVlen = 16; + size_t R_MCVlen = 0; + + EVP_CIPHER_CTX *ed_ctx = NULL; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + CMAC_CTX *cmac_ctx = NULL; +#else + EVP_MAC *mac = NULL; + EVP_MAC_CTX *cmac_ctx = NULL; + OSSL_PARAM cmac_params[2]; + size_t cmac_params_n = 0; +#endif + + struct sc_asn1_entry asn1_sm_response[C_ASN1_PIV_SM_RESPONSE_SIZE]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - keydata->exponent = 0; - keydata->pubkey = NULL; - keydata->pubkey_len = 0; - keydata->ecparam = NULL; /* will show size as we only support 2 curves */ - keydata->ecparam_len = 0; - keydata->ecpoint = NULL; - keydata->ecpoint_len = 0; + sc_copy_asn1_entry(c_asn1_sm_response, asn1_sm_response); - out_len = 3; - outdata[0] = 0x80; - outdata[1] = 0x01; - outdata[2] = keydata->key_algid; - switch (keydata->key_algid) { - case 0x05: keydata->key_bits = 3072; break; - case 0x06: keydata->key_bits = 1024; break; - case 0x07: keydata->key_bits = 2048; break; - /* TODO: - DEE For EC, also set the curve parameter as the OID */ - case 0x11: keydata->key_bits = 0; - keydata->ecparam =0; /* we only support prime256v1 for 11 */ - keydata->ecparam_len =0; - break; - case 0x14: keydata->key_bits = 0; - keydata->ecparam = 0; /* we only support secp384r1 */ - keydata->ecparam_len = 0; - break; - default: - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); - } + sc_format_asn1_entry(asn1_sm_response + 0, piv_get_asn1_obj, &ee, 0); + sc_format_asn1_entry(asn1_sm_response + 1, piv_get_asn1_obj, &status, 0); + sc_format_asn1_entry(asn1_sm_response + 2, piv_get_asn1_obj, &rmac8, 0); - p = tagbuf; + r = sc_asn1_decode(card->ctx, asn1_sm_response, sm_apdu->resp, sm_apdu->resplen, NULL, NULL); - put_tag_and_len(0xAC, out_len, &p); + if (r < 0) { + sc_log(card->ctx, "SM decode failed"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } - memcpy(p, outdata, out_len); - p+=out_len; + if (asn1_sm_response[0].flags & SC_ASN1_PRESENT && /* optional */ + (ee.value == NULL || ee.len <= 2)) { + sc_log(card->ctx, "SM BER-TLV not valid"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } - r = piv_general_io(card, 0x47, 0x00, keydata->key_num, - tagbuf, p - tagbuf, &rbuf, &rbuflen); + if ((asn1_sm_response[1].flags & SC_ASN1_PRESENT) == 0 || + (asn1_sm_response[2].flags & SC_ASN1_PRESENT) == 0) { + sc_log(card->ctx, "SM missing status or R-MAC"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } - if (r >= 0) { - const u8 *cp; - keydata->exponent = 0; + if (status.len != 2 || + status.value == NULL || + rmac8.len != 8 || + rmac8.value == NULL) { + sc_log(card->ctx, "SM status or R-MAC length invalid"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } - /* expected tag is 7f49. */ - /* we will whatever tag is present */ +#if OPENSSL_VERSION_NUMBER < 0x30000000L + cmac_ctx = CMAC_CTX_new(); + if (cmac_ctx == NULL) { + sc_log_openssl(card->ctx); + r = SC_ERROR_INTERNAL; + goto err; + } +#else + mac = EVP_MAC_fetch(PIV_LIBCTX, "cmac", NULL); + cmac_params[cmac_params_n++] = OSSL_PARAM_construct_utf8_string("cipher", cs->cipher_cbc_name, 0); + cmac_params[cmac_params_n] = OSSL_PARAM_construct_end(); + if (mac == NULL || (cmac_ctx = EVP_MAC_CTX_new(mac)) == NULL) { + sc_log_openssl(card->ctx); + r = SC_ERROR_INTERNAL; + goto err; + } +#endif - cp = rbuf; - in_len = rbuflen; + /* MCV is first, then BER TLV Encoded Encrypted PIV Data and Status */ + macdatalen = (int)(status.value + status.len - sm_apdu->resp); - r = sc_asn1_read_tag(&cp, rbuflen, &cla_out, &tag_out, &in_len); - if (r != SC_SUCCESS) { - sc_log(card->ctx, "Tag buffer not found"); - goto err; - } +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if (CMAC_Init(cmac_ctx, priv->sm_session.SKrmac, priv->sm_session.aes_size, (*cs->cipher_cbc)(), NULL) != 1 || + CMAC_Update(cmac_ctx, priv->sm_session.R_MCV, MCVlen) != 1 || + CMAC_Update(cmac_ctx, sm_apdu->resp, macdatalen) != 1 || + CMAC_Final(cmac_ctx, priv->sm_session.R_MCV, &R_MCVlen) != 1) { + sc_log_openssl(card->ctx); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } +#else + if (!EVP_MAC_init(cmac_ctx, (const unsigned char *)priv->sm_session.SKrmac, + priv->sm_session.aes_size, cmac_params) || + !EVP_MAC_update(cmac_ctx, priv->sm_session.R_MCV, MCVlen) || + !EVP_MAC_update(cmac_ctx, sm_apdu->resp, macdatalen) || + !EVP_MAC_final(cmac_ctx, priv->sm_session.R_MCV, &R_MCVlen, MCVlen)) { + sc_log_openssl(card->ctx); + r = SC_ERROR_INTERNAL; + goto err; + } +#endif - /* if RSA vs EC */ - if (keydata->key_bits > 0 ) { - tag = sc_asn1_find_tag(card->ctx, cp, in_len, 0x82, &taglen); - if (tag != NULL && taglen <= 4) { - keydata->exponent = 0; - for (i = 0; i < taglen;i++) - keydata->exponent = (keydata->exponent<<8) + tag[i]; + if (memcmp(priv->sm_session.R_MCV, rmac8.value, 8) != 0) { + sc_log(card->ctx, "SM 8 bytes of R-MAC do not match received R-MAC"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + ed_ctx = EVP_CIPHER_CTX_new(); + if (ed_ctx == NULL) { + r = SC_ERROR_INTERNAL; + goto err; + } + + /* generate same IV used to encrypt response on card */ + if (EVP_EncryptInit_ex(ed_ctx, (*cs->cipher_ecb)(), NULL, priv->sm_session.SKenc, zeros) != 1 || + EVP_CIPHER_CTX_set_padding(ed_ctx, 0) != 1 || + EVP_EncryptUpdate(ed_ctx, IV, &outli, priv->sm_session.resp_enc_counter, 16) != 1 || + EVP_EncryptFinal_ex(ed_ctx, discard, &outdl) != 1 || + outdl != 0) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "SM encode failed in OpenSSL"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + /* some commands do not have response data */ + if (ee.value == NULL) { + plain->resplen = 0; + } else { + p = ee.value; + inlen = (int)ee.len; + if (inlen < 17 || *p != 0x01) { /*padding and padding indicator are required */ + sc_log(card->ctx, "SM padding indicator not 0x01"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + p++; /* skip padding indicator */ + inlen--; + + if ((inlen % 16) != 0) { + sc_log(card->ctx, "SM encrypted data not multiple of 16"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + /* + * Encrypted data has 1 to 16 pad bytes, so may be 1 to 16 bytes longer + * then expected. i.e. plain->resp and resplen.So will do last block + * and recombine. + */ + + inlen -= 16; + if (plain->resplen < (unsigned)inlen || plain->resp == NULL) { + sc_log(card->ctx, "SM response will not fit in resp,resplen"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + q = plain->resp; + + /* first round encryptes counter with zero IV, and does not save the output */ + if (EVP_CIPHER_CTX_reset(ed_ctx) != 1 || + EVP_DecryptInit_ex(ed_ctx, (*cs->cipher_cbc)(), NULL, priv->sm_session.SKenc, IV) != 1 || + EVP_CIPHER_CTX_set_padding(ed_ctx, 0) != 1 || + EVP_DecryptUpdate(ed_ctx, q, &outl, p, inlen) != 1 || + EVP_DecryptUpdate(ed_ctx, lastb, &outll, p + inlen, 16) != 1 || + EVP_DecryptFinal_ex(ed_ctx, discard, &outdl) != 1 || + outdl != 0 || + outll != 16) { /* should not happen */ + sc_log_openssl(card->ctx); + sc_log(card->ctx, "SM _decode failed in OpenSSL"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + /* unpad last block and get bytes in last block */ + for (i = 15; i > 0; i--) { + if (lastb[i] == 0x80) + break; + if (lastb[i] == 0x00) + continue; + sc_log(card->ctx, "SM Padding not correct"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + if (lastb[i] != 0x80) { + sc_log(card->ctx, "SM Padding not correct"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + /* will response fit in plain resp buffer */ + if ((unsigned)inlen + i > plain->resplen || plain->resp == NULL) { + sc_log(card->ctx, "SM response bigger then resplen"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + /* copy bytes in last block if any */ + memcpy(plain->resp + inlen, lastb, i); + plain->resplen = inlen + i; + } + + plain->sw1 = *(status.value); + plain->sw2 = *(status.value + 1); + + piv_inc(priv->sm_session.resp_enc_counter, sizeof(priv->sm_session.resp_enc_counter)); + + r = SC_SUCCESS; +err: + if (r != 0 && plain) { + plain->sw1 = 0x69; + plain->sw2 = 0x88; + } + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + CMAC_CTX_free(cmac_ctx); +#else + EVP_MAC_CTX_free(cmac_ctx); + EVP_MAC_free(mac); +#endif + + EVP_CIPHER_CTX_free(ed_ctx); + + LOG_FUNC_RETURN(card->ctx, r); +} + +static int +piv_free_sm_apdu(sc_card_t *card, sc_apdu_t *plain, sc_apdu_t **sm_apdu) +{ + int r = SC_SUCCESS; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (!sm_apdu) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + if (!(*sm_apdu)) + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + + if (plain) { + plain->sw1 = (*sm_apdu)->sw1; + plain->sw2 = (*sm_apdu)->sw2; + if (((*sm_apdu)->sw1 == 0x90 && (*sm_apdu)->sw2 == 00) || + (*sm_apdu)->sw1 == 61) { + r = piv_decode_apdu(card, plain, *sm_apdu); + goto err; + } + sc_log(card->ctx, "SM response sw1:0x%2.2x sw2:0x%2.2x", plain->sw1, plain->sw2); + if (plain->sw1 == 0x69 && plain->sw2 == 0x88) { + /* BUT plain->sw1 and sw2 are not passed back as expected */ + r = SC_ERROR_SM_INVALID_CHECKSUM; /* will use this one one for now */ + goto err; + } else { + r = SC_ERROR_SM; + goto err; + } + } + +err: + free((unsigned char **)(*sm_apdu)->data); + free((*sm_apdu)->resp); + free(*sm_apdu); + *sm_apdu = NULL; + + LOG_FUNC_RETURN(card->ctx, r); +} + +static int +piv_sm_close(sc_card_t *card) +{ + int r = 0; + piv_private_data_t *priv = PIV_DATA(card); + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + sc_log(card->ctx, "priv->sm_flags: 0x%8.8lu", priv->sm_flags); + + /* sm.c tries to restart sm. Will defer */ + if ((priv->sm_flags & PIV_SM_FLAGS_SM_IS_ACTIVE)) { + priv->sm_flags |= PIV_SM_FLAGS_DEFER_OPEN; + priv->sm_flags &= ~PIV_SM_FLAGS_SM_IS_ACTIVE; + } + + LOG_FUNC_RETURN(card->ctx, r); +} + +static void +piv_clear_cvc_content(piv_cvc_t *cvc) +{ + if (!cvc) + return; + free(cvc->body); + free(cvc->signature); + free(cvc->publicPoint); + free(cvc->der.value); + memset(cvc, 0, sizeof(piv_cvc_t)); + return; +} + +static void +piv_clear_sm_session(piv_sm_session_t *session) +{ + if (!session) + return; + sc_mem_clear(session, sizeof(piv_sm_session_t)); + return; +} + +/* + * Decode a card verifiable certificate as defined in NIST 800-73-4 + */ +static int +piv_decode_cvc(sc_card_t *card, u8 **buf, size_t *buflen, + piv_cvc_t *cvc) +{ + struct sc_asn1_entry asn1_piv_cvc[C_ASN1_PIV_CVC_SIZE]; + struct sc_asn1_entry asn1_piv_cvc_body[C_ASN1_PIV_CVC_BODY_SIZE]; + struct sc_asn1_entry asn1_piv_cvc_pubkey[C_ASN1_PIV_CVC_PUBKEY_SIZE]; + struct sc_asn1_entry asn1_piv_cvc_dsobj[C_ASN1_PIV_CVC_DSOBJ_SIZE]; + struct sc_asn1_entry asn1_piv_cvc_dssig[C_ASN1_PIV_CVC_DSSIG_SIZE]; + struct sc_asn1_entry asn1_piv_cvc_alg_id[C_ASN1_PIV_CVC_ALG_ID_SIZE]; + struct sc_lv_data roleIDder = {NULL, 0}; + int r; + const u8 *buf_tmp; + unsigned int cla_out, tag_out; + size_t taglen; + size_t signaturebits; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (buf == NULL || *buf == NULL || cvc == NULL) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + /* If already read and matches previous version return SC_SUCCESS */ + if (cvc->der.value && (cvc->der.len == *buflen) && (memcmp(cvc->der.value, *buf, *buflen) == 0)) + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + + piv_clear_cvc_content(cvc); + + memset(cvc, 0, sizeof(piv_cvc_t)); + cvc->issuerIDlen = sizeof(cvc->issuerID); + cvc->subjectIDlen = sizeof(cvc->subjectID); + + sc_copy_asn1_entry(c_asn1_piv_cvc, asn1_piv_cvc); + sc_copy_asn1_entry(c_asn1_piv_cvc_body, asn1_piv_cvc_body); + sc_copy_asn1_entry(c_asn1_piv_cvc_pubkey, asn1_piv_cvc_pubkey); + sc_copy_asn1_entry(c_asn1_piv_cvc_dsobj, asn1_piv_cvc_dsobj); + sc_copy_asn1_entry(c_asn1_piv_cvc_dssig, asn1_piv_cvc_dssig); + sc_copy_asn1_entry(c_asn1_piv_cvc_alg_id, asn1_piv_cvc_alg_id); + + sc_format_asn1_entry(asn1_piv_cvc_alg_id, &cvc->signatureAlgOID, NULL, 1); + sc_format_asn1_entry(asn1_piv_cvc_alg_id + 1, NULL, NULL, 1); /* NULL */ + + sc_format_asn1_entry(asn1_piv_cvc_dssig, &asn1_piv_cvc_alg_id, NULL, 1); + sc_format_asn1_entry(asn1_piv_cvc_dssig + 1, &cvc->signature, &signaturebits, 1); + + sc_format_asn1_entry(asn1_piv_cvc_dsobj, &asn1_piv_cvc_dssig, NULL, 1); + + sc_format_asn1_entry(asn1_piv_cvc_pubkey, &cvc->pubKeyOID, NULL, 1); + sc_format_asn1_entry(asn1_piv_cvc_pubkey + 1, &cvc->publicPoint, &cvc->publicPointlen, 1); + + sc_format_asn1_entry(asn1_piv_cvc_body, &cvc->cpi, NULL, 1); + sc_format_asn1_entry(asn1_piv_cvc_body + 1, &cvc->issuerID, &cvc->issuerIDlen, 1); + sc_format_asn1_entry(asn1_piv_cvc_body + 2, &cvc->subjectID, &cvc->subjectIDlen, 1); + sc_format_asn1_entry(asn1_piv_cvc_body + 3, &asn1_piv_cvc_pubkey, NULL, 1); + sc_format_asn1_entry(asn1_piv_cvc_body + 4, piv_get_asn1_obj, &roleIDder, 1); + sc_format_asn1_entry(asn1_piv_cvc_body + 5, &asn1_piv_cvc_dsobj, NULL, 1); + + sc_format_asn1_entry(asn1_piv_cvc, &asn1_piv_cvc_body, NULL, 1); + + r = sc_asn1_decode(card->ctx, asn1_piv_cvc, *buf, *buflen, NULL, NULL); /*(const u8 **) &buf_tmp, &len);*/ + if (r < 0) { + piv_clear_cvc_content(cvc); + sc_log(card->ctx, "Could not decode card verifiable certificate"); + LOG_FUNC_RETURN(card->ctx, r); + } + + cvc->signaturelen = signaturebits / 8; + + if (roleIDder.len != 1) + LOG_TEST_RET(card->ctx, SC_ERROR_SM_AUTHENTICATION_FAILED, "roleID wrong length"); + + cvc->roleID = *roleIDder.value; + + /* save body der for verification */ + buf_tmp = *buf; + r = sc_asn1_read_tag(&buf_tmp, *buflen, &cla_out, &tag_out, &taglen); + LOG_TEST_RET(card->ctx, r, " failed to read tag"); + + cvc->bodylen = (roleIDder.value + roleIDder.len) - buf_tmp; + + cvc->body = malloc(cvc->bodylen); + if (cvc->body == NULL) + return SC_ERROR_OUT_OF_MEMORY; + memcpy(cvc->body, buf_tmp, cvc->bodylen); + + /* save to reuse */ + cvc->der.value = malloc(*buflen); + if (cvc->der.value == NULL) { + free(cvc->body); + return SC_ERROR_OUT_OF_MEMORY; + } + cvc->der.len = *buflen; + memcpy(cvc->der.value, *buf, cvc->der.len); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int +piv_parse_pairing_code(sc_card_t *card, const char *option) +{ + size_t i; + + if (strlen(option) != PIV_PAIRING_CODE_LEN) { + sc_log(card->ctx, "pairing code length invalid must be %d", PIV_PAIRING_CODE_LEN); + return SC_ERROR_INVALID_ARGUMENTS; + } + for (i = 0; i < PIV_PAIRING_CODE_LEN; i++) { + if (!isdigit(option[i])) { + sc_log(card->ctx, "pairing code must be %d decimal digits", PIV_PAIRING_CODE_LEN); + return SC_ERROR_INVALID_ARGUMENTS; + } + } + return SC_SUCCESS; +} +#endif + +static int +piv_load_options(sc_card_t *card) +{ + int r; + size_t i, j; + scconf_block **found_blocks, *block; + +#ifdef ENABLE_PIV_SM + piv_private_data_t *priv = PIV_DATA(card); + const char *option = NULL; + int piv_pairing_code_found = 0; + int piv_use_sm_found = 0; + + /* pairing code is 8 decimal digits and is card specific */ + if ((option = getenv("PIV_PAIRING_CODE")) != NULL) { + sc_log(card->ctx, "getenv(\"PIV_PAIRING_CODE\") found"); + if (piv_parse_pairing_code(card, option) == SC_SUCCESS) { + memcpy(priv->pairing_code, option, PIV_PAIRING_CODE_LEN); + piv_pairing_code_found = 1; + } + } + + if ((option = getenv("PIV_USE_SM")) != NULL) { + sc_log(card->ctx, "getenv(\"PIV_USE_SM\")=\"%s\"", option); + if (!strcmp(option, "never")) { + priv->sm_flags |= PIV_SM_FLAGS_NEVER; + piv_use_sm_found = 1; + } else if (!strcmp(option, "always")) { + priv->sm_flags |= PIV_SM_FLAGS_ALWAYS; + piv_use_sm_found = 1; + } else { + sc_log(card->ctx, "Invalid piv_use_sm: \"%s\"", option); + } + } +#endif + + for (i = 0; card->ctx->conf_blocks[i]; i++) { + found_blocks = scconf_find_blocks(card->ctx->conf, card->ctx->conf_blocks[i], + "card_driver", "PIV-II"); + if (!found_blocks) + continue; + + for (j = 0, block = found_blocks[j]; block; j++, block = found_blocks[j]) { + +#ifdef ENABLE_PIV_SM + /* + * "piv_use_sm" if card supports NIST sp800-73-4 sm, when should it be used + * never - use card like 800-73-3, i.e. contactless is very limited on + * true PIV cards. Some PIV-like" card may allow this. + * this security risk + * always - Use even for contact interface. + * PINS, crypto and reading of object will not show up in logs + * or over network. + */ + + if (piv_use_sm_found == 0) { + option = scconf_get_str(block, "piv_use_sm", "default"); + sc_log(card->ctx, "conf: \"piv_use_sm\"=\"%s\"", option); + if (!strcmp(option, "default")) { + /* no new flags */ + } else if (!strcmp(option, "never")) { + priv->sm_flags |= PIV_SM_FLAGS_NEVER; + } else if (!strcmp(option, "always")) { + priv->sm_flags |= PIV_SM_FLAGS_ALWAYS; + } else { + sc_log(card->ctx, "Invalid piv_use_sm: \"%s\"", option); + } + } + + /* This is really a card specific value and should not be in the conf file */ + if (piv_pairing_code_found == 0) { + option = scconf_get_str(block, "piv_pairing_code", NULL); + if (option && piv_parse_pairing_code(card, option) == SC_SUCCESS) { + memcpy(priv->pairing_code, option, PIV_PAIRING_CODE_LEN); + } + } +#endif + } + free(found_blocks); + } + r = SC_SUCCESS; + return r; +} + +static int +piv_find_obj_by_containerid(sc_card_t *card, const u8 *str) +{ + int i; + + LOG_FUNC_CALLED(card->ctx); + sc_log(card->ctx, "str=0x%02X%02X\n", str[0], str[1]); + + for (i = 0; piv_objects[i].enumtag < PIV_OBJ_LAST_ENUM; i++) { + if (str[0] == piv_objects[i].containerid[0] && str[1] == piv_objects[i].containerid[1]) + LOG_FUNC_RETURN(card->ctx, i); + } + + LOG_FUNC_RETURN(card->ctx, -1); +} + +/* + * Send a command and receive data. There is always something to send. + * Used by GET DATA, PUT DATA, GENERAL AUTHENTICATE + * and GENERATE ASYMMETRIC KEY PAIR. + */ + +static int +piv_general_io(sc_card_t *card, int ins, int p1, int p2, + const u8 *sendbuf, size_t sendbuflen, u8 *recvbuf, + size_t recvbuflen) +{ + int r; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + r = sc_lock(card); + if (r != SC_SUCCESS) + LOG_FUNC_RETURN(card->ctx, r); + + sc_format_apdu(card, &apdu, + recvbuf ? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_3_SHORT, + ins, p1, p2); + apdu.flags |= SC_APDU_FLAGS_CHAINING; +#ifdef ENABLE_PIV_SM + if (card->sm_ctx.sm_mode != SM_MODE_NONE && sendbuflen > 255) { + /* tell apdu.c to not do the chaining, let the SM get_apdu do it */ + apdu.flags |= SC_APDU_FLAGS_SM_CHAINING; + } +#endif + apdu.lc = sendbuflen; + apdu.datalen = sendbuflen; + apdu.data = sendbuf; + + if (recvbuf && recvbuflen) { + apdu.le = (recvbuflen > 256) ? 256 : recvbuflen; + apdu.resplen = recvbuflen; + } else { + apdu.le = 0; + apdu.resplen = 0; + } + apdu.resp = recvbuf; + + /* with new adpu.c and chaining, this actually reads the whole object */ + r = sc_transmit_apdu(card, &apdu); + + /* adpu will not have sw1,sw2 set because sc_sm_single_transmit called sc_sm_stop, */ + if (r < 0) { + sc_log(card->ctx, "Transmit failed"); + goto err; + } + + if (apdu.sw1 == 0x69 && apdu.sw2 == 0x88) + r = SC_ERROR_SM_INVALID_SESSION_KEY; + else + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + + if (r < 0) { + sc_log(card->ctx, "Card returned error "); + goto err; + } + + r = (int)apdu.resplen; + +err: + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, r); +} + +#ifdef ENABLE_PIV_SM +/* convert q as 04||x||y used in standard point formats to expanded leading + * zeros and concatenated X||Y as specified in SP80056A Appendix C.2 + * Field-Element-to-Byte-String Conversion which + * OpenSSL has already converted X and Y to big endian and skipped leading + * zero bytes. + */ +static int +Q2OS(int fsize, u8 *Q, size_t Qlen, u8 *OS, size_t *OSlen) +{ + size_t i; + size_t f = fsize / 8; + + i = (Qlen - 1) / 2; + + if (!OS || *OSlen < f * 2 || !Q || i > f) + return SC_ERROR_INTERNAL; + + memset(OS, 0, f * 2); + /* Check this if x and y have leading zero bytes, + * In UNCOMPRESSED FORMAT, x and Y must be same length, to tell when + * one ends and the other starts */ + memcpy(OS + f - i, Q + 1, i); + memcpy(OS + 2 * f - i, Q + f + 1, i); + *OSlen = f * 2; + return 0; +} + +/* + * if needed, send VCI pairing code to card just after the + * SM key establishment. Called from piv_sm_open under same lock + */ +static int +piv_send_vci_pairing_code(struct sc_card *card, u8 *paring_code) +{ + int r; + piv_private_data_t *priv = PIV_DATA(card); + sc_apdu_t plain; + sc_apdu_t sm_apdu; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (priv->pin_policy & PIV_PP_VCI_WITHOUT_PC) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); /* Not needed */ + + if ((priv->pin_policy & PIV_PP_VCI_IMPL) == 0) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NO_CARD_SUPPORT); + + sc_format_apdu(card, &plain, SC_APDU_CASE_3_SHORT, 0x20, 0x00, 0x98); + plain.datalen = plain.lc = 8; + plain.data = paring_code; + plain.resp = NULL; + plain.resplen = plain.le = 0; + + memset(&sm_apdu, 0, sizeof(sm_apdu)); + /* build sm_apdu and set alloc sm_apdu.resp */ + r = piv_encode_apdu(card, &plain, &sm_apdu); + if (r < 0) { + free(sm_apdu.resp); + sc_log(card->ctx, "piv_encode_apdu failed"); + LOG_FUNC_RETURN(card->ctx, r); + } + + sm_apdu.flags += SC_APDU_FLAGS_NO_SM; /* run as is */ + r = sc_transmit_apdu(card, &sm_apdu); + if (r < 0) { + free(sm_apdu.resp); + sc_log(card->ctx, "transmit failed"); + LOG_FUNC_RETURN(card->ctx, r); + } + + r = piv_decode_apdu(card, &plain, &sm_apdu); + free(sm_apdu.resp); + LOG_TEST_RET(card->ctx, r, "piv_decode_apdu failed"); + + r = sc_check_sw(card, plain.sw1, plain.sw2); + if (r < 0) + r = SC_ERROR_PIN_CODE_INCORRECT; + + LOG_FUNC_RETURN(card->ctx, r); +} + +/* Verify one signature using pubkey */ +static int +piv_sm_verify_sig(struct sc_card *card, const EVP_MD *type, + EVP_PKEY *pkey, + u8 *data, size_t data_size, + unsigned char *sig, size_t siglen) +{ + piv_private_data_t *priv = PIV_DATA(card); + cipher_suite_t *cs = priv->cs; + int r = 0; + EVP_MD_CTX *md_ctx = NULL; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (cs == NULL) { + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + if ((md_ctx = EVP_MD_CTX_new()) == NULL || + EVP_DigestVerifyInit(md_ctx, NULL, type, NULL, pkey) != 1 || + EVP_DigestVerifyUpdate(md_ctx, data, data_size) != 1 || + EVP_DigestVerifyFinal(md_ctx, sig, siglen) != 1) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "EVP_DigestVerifyFinal failed"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + r = SC_SUCCESS; +err: + EVP_MD_CTX_free(md_ctx); + LOG_FUNC_RETURN(card->ctx, r); +} + +/* + * If sm_in_cvc is present, verify PIV_OBJ_SM_CERT_SIGNER signed sm_in_cvc + * and sm_in_cvc signed sm_cvc. + * If sm_in_cvc is not present verify PIV_OBJ_SM_CERT_SIGNER signed sm_cvc. + */ + +static int +piv_sm_verify_certs(struct sc_card *card) +{ + piv_private_data_t *priv = PIV_DATA(card); + cipher_suite_t *cs = priv->cs; + int r = 0; + u8 *cert_blob_unzipped = NULL; /* free */ + u8 *cert_blob = NULL; /* do not free */ + size_t cert_bloblen = 0; + + u8 *rbuf; /* do not free*/ + size_t rbuflen; + X509 *cert = NULL; + EVP_PKEY *cert_pkey = NULL; /* do not free */ + EVP_PKEY *in_cvc_pkey = NULL; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + EC_GROUP *in_cvc_group = NULL; + EC_POINT *in_cvc_point = NULL; + EC_KEY *in_cvc_eckey = NULL; +#else + EVP_PKEY_CTX *in_cvc_pkey_ctx = NULL; + OSSL_PARAM params[3]; + size_t params_n; +#endif + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (cs == NULL) { + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + /* + * Get the PIV_OBJ_SM_CERT_SIGNER and optional sm_in_cvc in cache + * both are in same object. Rbuf, and rbuflen are needed but not used here + * sm_cvc and sm_in_cvc both have EC_keys sm_in_cvc may have RSA signature + */ + r = piv_get_cached_data(card, PIV_OBJ_SM_CERT_SIGNER, &rbuf, &rbuflen); + if (r < 0) { + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + r = piv_cache_internal_data(card, PIV_OBJ_SM_CERT_SIGNER); + if (r < 0) { + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + priv->sm_flags |= PIV_SM_FLAGS_SM_CERT_SIGNER_PRESENT; /* set for debugging */ + + /* get PIV_OBJ_SM_CERT_SIGNER cert DER from cache */ + if (priv->obj_cache[PIV_OBJ_SM_CERT_SIGNER].flags & PIV_OBJ_CACHE_COMPRESSED) { +#ifdef ENABLE_ZLIB + if (SC_SUCCESS != sc_decompress_alloc(&cert_blob_unzipped, &cert_bloblen, + priv->obj_cache[PIV_OBJ_SM_CERT_SIGNER].internal_obj_data, + priv->obj_cache[PIV_OBJ_SM_CERT_SIGNER].internal_obj_len, + COMPRESSION_AUTO)) { + sc_log(card->ctx, "PIV decompression of SM CERT_SIGNER failed"); + r = SC_ERROR_OBJECT_NOT_VALID; + goto err; + } + cert_blob = cert_blob_unzipped; +#else + sc_log(card->ctx, "PIV compression not supported, no zlib"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); +#endif + + } else { + cert_blob = priv->obj_cache[PIV_OBJ_SM_CERT_SIGNER].internal_obj_data; + cert_bloblen = priv->obj_cache[PIV_OBJ_SM_CERT_SIGNER].internal_obj_len; + } + + if (cert_blob == NULL || cert_bloblen == 0) { + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + if ((cert = d2i_X509(NULL, (const u8 **)&cert_blob, cert_bloblen)) == NULL || + (cert_pkey = X509_get0_pubkey(cert)) == NULL) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "OpenSSL failed to get pubkey from SM_CERT_SIGNER"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + /* if intermediate sm_in_cvc present, cert signed it and sm_cvc is signed by sm_in_cvc */ + if (priv->sm_flags & PIV_SM_FLAGS_SM_IN_CVC_PRESENT) { + r = piv_sm_verify_sig(card, cs->kdf_md(), cert_pkey, + priv->sm_in_cvc.body, priv->sm_in_cvc.bodylen, + priv->sm_in_cvc.signature, priv->sm_in_cvc.signaturelen); + if (r < 0) { + sc_log(card->ctx, "sm_in_cvc signature invalid"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if ((in_cvc_group = EC_GROUP_new_by_curve_name(cs->nid)) == NULL || + (in_cvc_pkey = EVP_PKEY_new()) == NULL || + (in_cvc_eckey = EC_KEY_new_by_curve_name(cs->nid)) == NULL || + (in_cvc_point = EC_POINT_new(in_cvc_group)) == NULL || + EC_POINT_oct2point(in_cvc_group, in_cvc_point, + priv->sm_in_cvc.publicPoint, priv->sm_in_cvc.publicPointlen, NULL) <= 0 || + EC_KEY_set_public_key(in_cvc_eckey, in_cvc_point) <= 0 || + EVP_PKEY_set1_EC_KEY(in_cvc_pkey, in_cvc_eckey) != 1) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "OpenSSL failed to set EC pubkey, during verify"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } +#else + params_n = 0; + params[params_n++] = OSSL_PARAM_construct_utf8_string("group", cs->curve_group, 0); + params[params_n++] = OSSL_PARAM_construct_octet_string("pub", + priv->sm_in_cvc.publicPoint, priv->sm_in_cvc.publicPointlen); + params[params_n] = OSSL_PARAM_construct_end(); + + if (!(in_cvc_pkey_ctx = EVP_PKEY_CTX_new_from_name(PIV_LIBCTX, "EC", NULL)) || + !EVP_PKEY_fromdata_init(in_cvc_pkey_ctx) || + !EVP_PKEY_fromdata(in_cvc_pkey_ctx, &in_cvc_pkey, EVP_PKEY_PUBLIC_KEY, params) || + !in_cvc_pkey) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "OpenSSL failed to set EC pubkey, during verify"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } +#endif + r = piv_sm_verify_sig(card, cs->kdf_md(), in_cvc_pkey, + priv->sm_cvc.body, priv->sm_cvc.bodylen, + priv->sm_cvc.signature, priv->sm_cvc.signaturelen); + + } else { /* cert signed sm_cvc */ + r = piv_sm_verify_sig(card, cs->kdf_md(), cert_pkey, + priv->sm_cvc.body, priv->sm_cvc.bodylen, + priv->sm_cvc.signature, priv->sm_cvc.signaturelen); + } + if (r < 0) { + sc_log(card->ctx, "sm_cvc signature invalid"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + /* cert chain signatures match for oncard certs */ + +err: + X509_free(cert); + free(cert_blob_unzipped); + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + EC_GROUP_free(in_cvc_group); + EC_POINT_free(in_cvc_point); + EC_KEY_free(in_cvc_eckey); +#else + EVP_PKEY_CTX_free(in_cvc_pkey_ctx); +#endif + EVP_PKEY_free(in_cvc_pkey); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); +} + +/* + * NIST SP800-73-4 4.1 The key Establishment Protocol + * Variable names and Steps are based on Client Application (h) + * and PIV Card Application (icc) + * Capital leters used for variable, and lower case for subscript names + */ +static int +piv_sm_open(struct sc_card *card) +{ + piv_private_data_t *priv = PIV_DATA(card); + cipher_suite_t *cs = priv->cs; + int r = 0; + int i; + int reps; + u8 CBh; + u8 CBicc; + u8 *p; + + /* ephemeral EC key */ + EVP_PKEY_CTX *eph_ctx = NULL; + EVP_PKEY *eph_pkey = NULL; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + EC_KEY *eph_eckey = NULL; /* don't free _get0_*/ + const EC_GROUP *eph_group = NULL; /* don't free _get0_ */ +#else + OSSL_PARAM eph_params[5]; + size_t eph_params_n; + size_t Qehxlen = 0; + u8 *Qehx = NULL; +#endif + size_t Qehlen = 0; + u8 Qeh[2 * PIV_SM_MAX_FIELD_LENGTH / 8 + 1]; /* big enough for 384 04||x||y if x and y have leading zeros, length may be less */ + size_t Qeh_OSlen = 0; + u8 Qeh_OS[2 * PIV_SM_MAX_FIELD_LENGTH / 8]; /* no leading 04, with leading zeros in X and Y */ + size_t Qsicc_OSlen = 0; + u8 Qsicc_OS[2 * PIV_SM_MAX_FIELD_LENGTH / 8]; /* no leading 04, with leading zeros in X and Y */ + + /* pub EC key from card Cicc in sm_cvc */ + EVP_PKEY_CTX *Cicc_ctx = NULL; + EVP_PKEY *Cicc_pkey = NULL; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + EC_KEY *Cicc_eckey = NULL; + EC_POINT *Cicc_point = NULL; + EC_GROUP *Cicc_group = NULL; +#endif + + /* shared secret key Z */ + EVP_PKEY_CTX *Z_ctx = NULL; + u8 *Z = NULL; + size_t Zlen = 0; + + u8 IDsh[8] = {0}; + unsigned long pid; + + u8 *sbuf = NULL; + size_t sbuflen; + int len2a, len2b; + + u8 rbuf[4096]; + size_t rbuflen = sizeof(rbuf); + + const u8 *body, *payload; + size_t bodylen, payloadlen; + u8 Nicc[24]; /* nonce */ + u8 AuthCryptogram[16]; + + u8 *cvcder = NULL; + size_t cvclen = 0; + size_t len; /* temp len */ + + u8 *kdf_in = NULL; + size_t kdf_inlen = 0; + unsigned int hashlen = 0; + u8 aeskeys[SHA384_DIGEST_LENGTH * 3] = {0}; /* 4 keys, Hash function is run 2 or 3 times max is 3 * 384/8 see below */ + EVP_MD_CTX *hash_ctx = NULL; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + CMAC_CTX *cmac_ctx = NULL; +#else + EVP_MAC *mac = NULL; + EVP_MAC_CTX *cmac_ctx = NULL; + OSSL_PARAM cmac_params[2]; + size_t cmac_params_n = 0; + OSSL_PARAM Cicc_params[3]; + size_t Cicc_params_n = 0; +#endif + + u8 IDsicc[8]; /* will only use 8 bytes for step H6 */ + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* + * The SM routines try and call this on their own. + * This routine should only be called by the card driver. + * which has set PIV_SM_FLAGS_DEFER_OPEN and unset in + * in reader_lock_obtained + * after testing PIC applet is active so SM is setup in same transaction + * as the command we are trying to run with SM. + * this avoids situation where the SM is established, and then reset by + * some other application without getting anything done or in + * a loop, each trying to reestablish a SM session and run command. + */ + if (!(priv->sm_flags & PIV_SM_FLAGS_DEFER_OPEN)) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ALLOWED); + } + if (cs == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + + r = sc_lock(card); + if (r != SC_SUCCESS) { + sc_log(card->ctx, "sc_lock failed"); + return r; + } + + /* use for several hash operations */ + if ((hash_ctx = EVP_MD_CTX_new()) == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + + /* Step 1 set CBh = 0 */ + CBh = 0; + + /* Step H2 generate ephemeral EC */ + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if ((eph_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) == NULL || + EVP_PKEY_keygen_init(eph_ctx) <= 0 || + EVP_PKEY_CTX_set_ec_paramgen_curve_nid(eph_ctx, cs->nid) <= 0 || + EVP_PKEY_keygen(eph_ctx, &eph_pkey) <= 0 || + (eph_eckey = EVP_PKEY_get0_EC_KEY(eph_pkey)) == NULL || + (eph_group = EC_KEY_get0_group(eph_eckey)) == NULL || + (Qehlen = EC_POINT_point2oct(eph_group, EC_KEY_get0_public_key(eph_eckey), + POINT_CONVERSION_UNCOMPRESSED, NULL, Qehlen, NULL)) <= 0 || /* get length */ + Qehlen > cs->Qlen || + (Qehlen = EC_POINT_point2oct(eph_group, EC_KEY_get0_public_key(eph_eckey), + POINT_CONVERSION_UNCOMPRESSED, Qeh, Qehlen, NULL)) <= 0 || + Qehlen > cs->Qlen) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "OpenSSL failed to create ephemeral EC key"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } +#else + /* generate Qeh */ + eph_params_n = 0; + eph_params[eph_params_n++] = OSSL_PARAM_construct_utf8_string("group", cs->curve_group, 0); + eph_params[eph_params_n++] = OSSL_PARAM_construct_utf8_string("point-format", "uncompressed", 0); + eph_params[eph_params_n] = OSSL_PARAM_construct_end(); + if (!(eph_ctx = EVP_PKEY_CTX_new_from_name(PIV_LIBCTX, "EC", NULL)) || + !EVP_PKEY_keygen_init(eph_ctx) || + !EVP_PKEY_CTX_set_params(eph_ctx, eph_params) || + !EVP_PKEY_generate(eph_ctx, &eph_pkey) || + !(Qehxlen = EVP_PKEY_get1_encoded_public_key(eph_pkey, &Qehx)) || + !Qehx || + Qehxlen > cs->Qlen) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "OpenSSL failed to create ephemeral EC key"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + memcpy(Qeh, Qehx, Qehxlen); + Qehlen = Qehxlen; +#endif + + /* For later use, get Qeh without 04 and full size X || Y */ + Qeh_OSlen = sizeof(Qeh_OS); + if (Q2OS(cs->field_length, Qeh, Qehlen, Qeh_OS, &Qeh_OSlen)) { + sc_log(card->ctx, "Q2OS for Qeh failed"); + r = SC_ERROR_INTERNAL; + goto err; + } + + r = len2a = sc_asn1_put_tag(0x81, NULL, 1 + sizeof(IDsh) + Qehlen, NULL, 0, NULL); + if (r < 0) + goto err; + r = len2b = sc_asn1_put_tag(0x80, NULL, 0, NULL, 0, NULL); + if (r < 0) + goto err; + sbuflen = r = sc_asn1_put_tag(0x7C, NULL, len2a + len2b, NULL, 0, NULL); + if (r < 0) + goto err; + + sbuf = malloc(sbuflen); + if (sbuf == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + p = sbuf; + + r = sc_asn1_put_tag(0x7C, NULL, len2a + len2b, sbuf, sbuflen, &p); + if (r != SC_SUCCESS) + goto err; + + r = sc_asn1_put_tag(0x81, NULL, 1 + sizeof(IDsh) + Qehlen, p, sbuflen - (p - sbuf), &p); + if (r != SC_SUCCESS) + goto err; + + /* Step H1 set CBh to 0x00 */ + *p++ = CBh; + +#ifdef WIN32 + pid = (unsigned long)GetCurrentProcessId(); +#else + pid = (unsigned long)getpid(); /* use PID as our ID so different from other processes */ +#endif + memcpy(IDsh, &pid, MIN(sizeof(pid), sizeof(IDsh))); + memcpy(p, IDsh, sizeof(IDsh)); + p += sizeof(IDsh); + memcpy(p, Qeh, Qehlen); + p += Qehlen; + + r = sc_asn1_put_tag(0x82, NULL, 0, p, sbuflen - (p - sbuf), &p); /* null data */ + if (r != SC_SUCCESS) + goto err; + + /* Step H3 send CBh||IDsh|| Qeh Qeh in 04||x||y */ + /* Or call sc_transmit directly */ + r = piv_general_io(card, 0x87, cs->p1, 0x04, sbuf, (p - sbuf), rbuf, rbuflen); + if (r <= 0) + goto err; + + rbuflen = r; + p = rbuf; + + body = sc_asn1_find_tag(card->ctx, rbuf, rbuflen, 0x7C, &bodylen); + if (body == NULL || bodylen < 20 || rbuf[0] != 0x7C) { + sc_log(card->ctx, "SM response data to short"); + r = SC_ERROR_SM_NO_SESSION_KEYS; + goto err; + } + + payload = sc_asn1_find_tag(card->ctx, body, bodylen, 0x82, &payloadlen); + if (payload == NULL || payloadlen < 1 + cs->Nicclen + cs->AuthCryptogramlen || *body != 0x82) { + sc_log(card->ctx, "SM response data to short"); + r = SC_ERROR_SM_NO_SESSION_KEYS; + goto err; + } + + /* payload is CBicc (1) || Nicc (16 or 24) || AuthCryptogram (CMAC 16 or 16) ||Cicc (variable) */ + p = (u8 *)payload; + + /* Step H4 check CBicc == 0x00 */ + CBicc = *p++; + if (CBicc != 0x00) { /* CBicc must be zero */ + sc_log(card->ctx, "SM card did not accept request"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + memcpy(Nicc, p, cs->Nicclen); + p += cs->Nicclen; + + memcpy(AuthCryptogram, p, cs->AuthCryptogramlen); + p += cs->AuthCryptogramlen; + + if (p > payload + payloadlen) { + sc_log(card->ctx, "SM card CVC is to short"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + cvclen = len = payloadlen - (p - payload); + if (len) { + cvcder = p; /* in rbuf */ + + r = piv_decode_cvc(card, &p, &len, &priv->sm_cvc); + if (r != SC_SUCCESS) { + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + priv->sm_flags |= PIV_SM_FLAGS_SM_CVC_PRESENT; + } + + /* Step H5 Verify Cicc CVC and pubkey */ + /* Verify Cicc (sm_cvc) is signed by sm_in_cvc or PIV_OBJ_SM_CERT_SIGNER */ + /* sm_in_cvc is signed by PIV_OBJ_SM_CERT_SIGNER */ + + /* Verify the cert chain is valid. */ + r = piv_sm_verify_certs(card); + if (r < 0) { + sc_log(card->ctx, "SM piv_sm_verify_certs r:%d", r); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + /* Step H6 need left most 8 bytes of hash of sm_cvc */ + { + u8 hash[SHA256_DIGEST_LENGTH] = {0}; + const u8 *tag; + size_t taglen; + const u8 *tmpder; + size_t tmpderlen; + + if ((tag = sc_asn1_find_tag(card->ctx, cvcder, cvclen, 0x7F21, &taglen)) == NULL || + *cvcder != 0x7F || *(cvcder + 1) != 0x21) { + + r = SC_ERROR_INTERNAL; + goto err; + } + + /* debug choice */ + tmpder = cvcder; + tmpderlen = cvclen; + + if (EVP_DigestInit(hash_ctx, EVP_sha256()) != 1 || + EVP_DigestUpdate(hash_ctx, tmpder, tmpderlen) != 1 || + EVP_DigestFinal_ex(hash_ctx, hash, NULL) != 1) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "IDsicc hash failed"); + r = SC_ERROR_INTERNAL; + goto err; + } + memcpy(IDsicc, hash, sizeof(IDsicc)); /* left 8 bytes */ + } + + /* Step H7 get the cards public key Qsicc into OpenSSL Cicc_eckey */ + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if ((Cicc_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) == NULL || + (Cicc_group = EC_GROUP_new_by_curve_name(cs->nid)) == NULL || + (Cicc_pkey = EVP_PKEY_new()) == NULL || + (Cicc_eckey = EC_KEY_new_by_curve_name(cs->nid)) == NULL || + (Cicc_point = EC_POINT_new(Cicc_group)) == NULL || + EC_POINT_oct2point(Cicc_group, Cicc_point, + priv->sm_cvc.publicPoint, priv->sm_cvc.publicPointlen, NULL) <= 0 || + EC_KEY_set_public_key(Cicc_eckey, Cicc_point) <= 0 || + EVP_PKEY_set1_EC_KEY(Cicc_pkey, Cicc_eckey) <= 0) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "OpenSSL failed to get card's EC pubkey"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } +#else + Cicc_params_n = 0; + Cicc_params[Cicc_params_n++] = OSSL_PARAM_construct_utf8_string("group", cs->curve_group, 0); + Cicc_params[Cicc_params_n++] = OSSL_PARAM_construct_octet_string("pub", + priv->sm_cvc.publicPoint, priv->sm_cvc.publicPointlen); + Cicc_params[Cicc_params_n] = OSSL_PARAM_construct_end(); + + if (!(Cicc_ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL)) || + !EVP_PKEY_fromdata_init(Cicc_ctx) || + !EVP_PKEY_fromdata(Cicc_ctx, &Cicc_pkey, EVP_PKEY_PUBLIC_KEY, Cicc_params) || + !Cicc_pkey) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "OpenSSL failed to set EC pubkey for Cicc"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } +#endif + + /* Qsicc without 04 and expanded x||y */ + Qsicc_OSlen = sizeof(Qsicc_OS); + if (Q2OS(cs->field_length, priv->sm_cvc.publicPoint, priv->sm_cvc.publicPointlen, Qsicc_OS, &Qsicc_OSlen)) { + sc_log(card->ctx, "Q2OS for Qsicc failed"); + r = SC_ERROR_INTERNAL; + goto err; + } + + /* Step H8 Compute the shared secret Z */ + if ((Z_ctx = EVP_PKEY_CTX_new(eph_pkey, NULL)) == NULL || + EVP_PKEY_derive_init(Z_ctx) <= 0 || + EVP_PKEY_derive_set_peer(Z_ctx, Cicc_pkey) <= 0 || + EVP_PKEY_derive(Z_ctx, NULL, &Zlen) <= 0 || + Zlen != cs->Zlen || + (Z = malloc(Zlen)) == NULL || + EVP_PKEY_derive(Z_ctx, Z, &Zlen) <= 0 || + Zlen != cs->Zlen) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "OpenSSL failed to create secret Z"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + + sc_log(card->ctx, "debug Zlen:%" SC_FORMAT_LEN_SIZE_T "u Z[0]:0x%2.2x", Zlen, Z[0]); + + /* Step H9 zeroize deh from step H2 */ + EVP_PKEY_free(eph_pkey); /* OpenSSL BN_clear_free calls OPENSSL_cleanse */ + eph_pkey = NULL; + + /* Step H10 Create AES session Keys */ + /* kdf in is 4byte counter || Z || otherinfo 800-56A 5.8.1 */ + + kdf_inlen = 4 + Zlen + cs->otherinfolen; + kdf_in = malloc(kdf_inlen); + if (kdf_in == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + p = kdf_in; + *p++ = 0x00; + *p++ = 0x00; + *p++ = 0x00; + *p++ = 0x01; + memcpy(p, Z, cs->Zlen); + p += Zlen; + + /* otherinfo */ + *p++ = cs->o0len; + for (i = 0; i < cs->o0len; i++) + *p++ = cs->o0_char; /* 0x09 or 0x0d */ + + *p++ = sizeof(IDsh); + memcpy(p, IDsh, sizeof(IDsh)); + p += sizeof(IDsh); + + *p++ = cs->CBhlen; + memcpy(p, &CBh, cs->CBhlen); + p += cs->CBhlen; + + *p++ = cs->T16Qehlen; + /* First 16 bytes of Qeh without 04 800-56A Appendix C.2 */ + memcpy(p, Qeh_OS, cs->T16Qehlen); + p += cs->T16Qehlen; + + *p++ = cs->IDsicclen; + memcpy(p, IDsicc, cs->IDsicclen); + p += cs->IDsicclen; + + *p++ = cs->Nicclen; + memcpy(p, Nicc, cs->Nicclen); + p += cs->Nicclen; + + *p++ = cs->CBicclen; + memcpy(p, &CBicc, cs->CBicclen); + p += cs->CBicclen; + + if (p != kdf_in + kdf_inlen) { + r = SC_ERROR_INTERNAL; + goto err; + } + + /* 4 keys needs reps = ceil (naeskeys * aeskeylen) / kdf_hash_size) */ + /* 800-56A-2007, 5.8.1 Process and 800-56C rev 3 2018 4.1 Process. */ + /* so it is 2 times for 128 or 3 times for 256 bit AES keys */ + p = aeskeys; /* 4 keys + overflow */ + reps = (cs->naeskeys * cs->aeskeylen + cs->kdf_hash_size - 1) / (cs->kdf_hash_size); + + EVP_MD_CTX_reset(hash_ctx); + for (i = 0; i < reps; i++) { + if (EVP_DigestInit(hash_ctx, (*cs->kdf_md)()) != 1 || + EVP_DigestUpdate(hash_ctx, kdf_in, kdf_inlen) != 1 || + EVP_DigestFinal_ex(hash_ctx, p, &hashlen) != 1) { + sc_log_openssl(card->ctx); + sc_log(card->ctx, "KDF hash failed"); + r = SC_ERROR_INTERNAL; + goto err; + } + kdf_in[3]++; /* inc the counter */ + p += cs->kdf_hash_size; + } + + /* copy keys used for APDU */ + memset(&priv->sm_session, 0, sizeof(piv_sm_session_t)); /* clear */ + priv->sm_session.aes_size = cs->aeskeylen; + memcpy(&priv->sm_session.SKcfrm, &aeskeys[cs->aeskeylen * 0], cs->aeskeylen); + memcpy(&priv->sm_session.SKmac, &aeskeys[cs->aeskeylen * 1], cs->aeskeylen); + memcpy(&priv->sm_session.SKenc, &aeskeys[cs->aeskeylen * 2], cs->aeskeylen); + memcpy(&priv->sm_session.SKrmac, &aeskeys[cs->aeskeylen * 3], cs->aeskeylen); + sc_mem_clear(&aeskeys, sizeof(aeskeys)); + + priv->sm_session.enc_counter[15] = 0x01; + priv->sm_session.resp_enc_counter[0] = 0x80; + priv->sm_session.resp_enc_counter[15] = 0x01; + /* C_MCV is zero */ + /* R_MCV is zero */ + + /* Step H11 Zeroize Z (and kdf_in which has Z) */ + if (Z && Zlen) { + sc_mem_clear(Z, Zlen); + free(Z); + Z = NULL; + Zlen = 0; + } + if (kdf_in && kdf_inlen) { + sc_mem_clear(kdf_in, kdf_inlen); + free(kdf_in); + kdf_in = NULL; + kdf_inlen = 0; + } + + /* Step H12 check AuthCryptogramting our version */ + /* Generate CMAC */ + + { + u8 Check_AuthCryptogram[32]; + size_t Check_Alen = 0; + + u8 MacData[200]; + int MacDatalen; + memset(MacData, 0, sizeof(MacData)); + + p = MacData; + memcpy(p, "\x4B\x43\x5f\x31\x5f\x56", 6); + p += 6; + memcpy(p, IDsicc, cs->IDsicclen); + p += cs->IDsicclen; + memcpy(p, IDsh, sizeof(IDsh)); + p += sizeof(IDsh); + + memcpy(p, Qeh_OS, Qeh_OSlen); + p += Qeh_OSlen; + MacDatalen = (int)(p - MacData); + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if ((cmac_ctx = CMAC_CTX_new()) == NULL || + CMAC_Init(cmac_ctx, priv->sm_session.SKcfrm, cs->aeskeylen, (*cs->cipher_cbc)(), NULL) != 1 || + CMAC_Update(cmac_ctx, MacData, MacDatalen) != 1 || + CMAC_Final(cmac_ctx, Check_AuthCryptogram, &Check_Alen) != 1) { + r = SC_ERROR_INTERNAL; + sc_log_openssl(card->ctx); + sc_log(card->ctx, "AES_CMAC failed %d", r); + goto err; + } +#else + mac = EVP_MAC_fetch(PIV_LIBCTX, "cmac", NULL); + cmac_params[cmac_params_n++] = OSSL_PARAM_construct_utf8_string("cipher", cs->cipher_cbc_name, 0); + + cmac_params[cmac_params_n] = OSSL_PARAM_construct_end(); + if (mac == NULL || + (cmac_ctx = EVP_MAC_CTX_new(mac)) == NULL || + !EVP_MAC_init(cmac_ctx, priv->sm_session.SKcfrm, + priv->sm_session.aes_size, cmac_params) || + !EVP_MAC_update(cmac_ctx, MacData, MacDatalen) || + !EVP_MAC_final(cmac_ctx, Check_AuthCryptogram, &Check_Alen, cs->AuthCryptogramlen)) { + sc_log_openssl(card->ctx); + r = SC_ERROR_INTERNAL; + sc_log(card->ctx, "AES_CMAC failed %d", r); + goto err; + } +#endif + + if (0 == memcmp(AuthCryptogram, Check_AuthCryptogram, cs->AuthCryptogramlen)) { + sc_log(card->ctx, "AuthCryptogram compare"); + r = 0; + } else { + sc_log(card->ctx, "AuthCryptogram compare failed"); + r = SC_ERROR_SM_AUTHENTICATION_FAILED; + goto err; + } + } + + /* VCI only needed for contactless */ + if (priv->init_flags & PIV_INIT_CONTACTLESS) { + /* Is pairing code required? */ + if (!(priv->pin_policy & PIV_PP_VCI_WITHOUT_PC)) { + r = piv_send_vci_pairing_code(card, priv->pairing_code); + if (r < 0) + goto err; + } + } + + r = 0; + priv->sm_flags |= PIV_SM_FLAGS_SM_IS_ACTIVE; + card->sm_ctx.sm_mode = SM_MODE_TRANSMIT; + +err: + priv->sm_flags &= ~PIV_SM_FLAGS_DEFER_OPEN; + if (r != 0) { + memset(&priv->sm_session, 0, sizeof(piv_sm_session_t)); + sc_log_openssl(card->ctx); /* catch any not logged above */ + } + + sc_unlock(card); + + free(sbuf); + free(kdf_in); + free(Z); + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + EC_GROUP_free(Cicc_group); + EC_POINT_free(Cicc_point); + EC_KEY_free(Cicc_eckey); +#endif + + EVP_PKEY_free(eph_pkey); /* in case not cleared in step H9 */ + EVP_PKEY_CTX_free(eph_ctx); + EVP_PKEY_free(Cicc_pkey); + EVP_PKEY_CTX_free(Cicc_ctx); + EVP_PKEY_CTX_free(Z_ctx); + EVP_MD_CTX_free(hash_ctx); + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + CMAC_CTX_free(cmac_ctx); +#else + EVP_MAC_CTX_free(cmac_ctx); + EVP_MAC_free(mac); + OPENSSL_free(Qehx); +#endif + + LOG_FUNC_RETURN(card->ctx, r); +} +#endif /* ENABLE_PIV_SM */ + +/* Add the PIV-II operations */ +/* Should use our own keydata, actually should be common to all cards */ +/* RSA and EC are added. */ + +static int +piv_generate_key(sc_card_t *card, + sc_cardctl_piv_genkey_info_t *keydata) +{ + int r; + u8 rbuf[4096]; + u8 *p; + const u8 *tag; + u8 tagbuf[16]; + u8 outdata[3]; /* we could also add tag 81 for exponent */ + size_t taglen; + size_t out_len; + size_t in_len; + unsigned int cla_out, tag_out; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + keydata->exponent = NULL; + keydata->exponent_len = 0; + keydata->pubkey = NULL; + keydata->pubkey_len = 0; + keydata->ecparam = NULL; /* will show size as we only support 2 curves */ + keydata->ecparam_len = 0; + keydata->ecpoint = NULL; + keydata->ecpoint_len = 0; + + out_len = 3; + outdata[0] = 0x80; + outdata[1] = 0x01; + outdata[2] = keydata->key_algid; + switch (keydata->key_algid) { + case 0x05: + keydata->key_bits = 3072; + break; + case 0x06: + keydata->key_bits = 1024; + break; + case 0x07: + keydata->key_bits = 2048; + break; + case 0x16: /* Yubico 5.7 support for 4096 */ + keydata->key_bits = 4096; + break; + case 0x11: + keydata->key_bits = 0; + keydata->ecparam = 0; /* we only support prime256v1 */ + keydata->ecparam_len = 0; + break; + case 0x14: + keydata->key_bits = 0; + keydata->ecparam = 0; /* we only support secp384r1 */ + keydata->ecparam_len = 0; + break; + case 0xE0: /* Yubico 5.7 support for EDDSA 25519 */ + case 0xE1: /* Yubico 5.7 support for XEDDSA 25519 */ + keydata->key_bits = 0; + keydata->ecparam = 0; + keydata->ecparam_len = 0; + break; + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + p = tagbuf; + + r = sc_asn1_put_tag(0xAC, outdata, out_len, tagbuf, sizeof(tagbuf), &p); + if (r != SC_SUCCESS) { + sc_log(card->ctx, "Failed to encode ASN1 tag"); + goto err; + } + + r = piv_general_io(card, 0x47, 0x00, keydata->key_num, + tagbuf, p - tagbuf, rbuf, sizeof rbuf); + + if (r >= 0) { + const u8 *cp; + + cp = rbuf; + in_len = r; + + /* expected tag is 0x7f49,returned as cla_out == 0x60 and tag_out = 0x1F49 */ + r = sc_asn1_read_tag(&cp, in_len, &cla_out, &tag_out, &in_len); + if (r < 0 || cp == NULL || in_len == 0 || cla_out != 0x60 || tag_out != 0x1f49) { + r = SC_ERROR_ASN1_OBJECT_NOT_FOUND; + } + if (r != SC_SUCCESS) { + sc_log(card->ctx, "Tag buffer not found"); + goto err; + } + + /* if RSA vs EC, ED25519 or X25519 */ + if (keydata->key_bits > 0) { + tag = sc_asn1_find_tag(card->ctx, cp, in_len, 0x82, &taglen); + if (tag != NULL && taglen <= 4) { + keydata->exponent = malloc(taglen); + if (keydata->exponent == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + keydata->exponent_len = taglen; + memcpy(keydata->exponent, tag, taglen); + } else { + sc_log(card->ctx, "Tag 0x82 not found"); + r = SC_ERROR_UNKNOWN_DATA_RECEIVED; + goto err; } tag = sc_asn1_find_tag(card->ctx, cp, in_len, 0x81, &taglen); if (tag != NULL && taglen > 0) { keydata->pubkey = malloc(taglen); - if (keydata->pubkey == NULL) + if (keydata->pubkey == NULL) { + free(keydata->exponent); LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } keydata->pubkey_len = taglen; - memcpy (keydata->pubkey, tag, taglen); + memcpy(keydata->pubkey, tag, taglen); + } else { + sc_log(card->ctx, "Tag 0x81 not found"); + r = SC_ERROR_UNKNOWN_DATA_RECEIVED; + goto err; } - } - else { /* must be EC */ + } else { /* must be EC, ED25519 or X25519 */ tag = sc_asn1_find_tag(card->ctx, cp, in_len, 0x86, &taglen); if (tag != NULL && taglen > 0) { keydata->ecpoint = malloc(taglen); if (keydata->ecpoint == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); keydata->ecpoint_len = taglen; - memcpy (keydata->ecpoint, tag, taglen); + memcpy(keydata->ecpoint, tag, taglen); + } else { + sc_log(card->ctx, "Tag 0x86 not found"); + r = SC_ERROR_UNKNOWN_DATA_RECEIVED; + goto err; } } - /* TODO: -DEE Could add key to cache so could use engine to generate key, - * and sign req in single operation */ + /* Could add key to cache so could use engine to generate key, + * and sign req in single operation or write temporary selfsigned + * certificate with new public key + */ r = 0; } err: - if (rbuf) - free(rbuf); + if (r < 0) { + free(keydata->exponent); + keydata->exponent = NULL; + free(keydata->pubkey); + keydata->pubkey = NULL; + free(keydata->ecpoint); + keydata->ecpoint = NULL; + } + LOG_FUNC_RETURN(card->ctx, r); } +static int +piv_ai_map_find_by_id(sc_card_t *card, u8 id) +{ + /* piv_private_data_t * priv = PIV_DATA(card); */ + int i; -static int piv_select_aid(sc_card_t* card, u8* aid, size_t aidlen, u8* response, size_t *responselen) -{ - sc_apdu_t apdu; - int r; - - LOG_FUNC_CALLED(card->ctx); - sc_log(card->ctx, - "Got args: aid=%p, aidlen=%"SC_FORMAT_LEN_SIZE_T"u, response=%p, responselen=%"SC_FORMAT_LEN_SIZE_T"u", - aid, aidlen, response, responselen ? *responselen : 0); - - sc_format_apdu(card, &apdu, - response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, 0xA4, 0x04, 0x00); - apdu.lc = aidlen; - apdu.data = aid; - apdu.datalen = aidlen; - apdu.resp = response; - apdu.resplen = responselen ? *responselen : 0; - apdu.le = response == NULL ? 0 : 256; /* could be 21 for fci */ - - r = sc_transmit_apdu(card, &apdu); - if (responselen) - *responselen = apdu.resplen; - LOG_TEST_RET(card->ctx, r, "PIV select failed"); - - LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); + for (i = 0; ai_map[i].ai_id != 0; i++) { + if (ai_map[i].ai_id == id) + return i; + } + return -1; } /* find the PIV AID on the card. If card->type already filled in, * then look for specific AID only - * Assumes that priv may not be present */ -static int piv_find_aid(sc_card_t * card, sc_file_t *aid_file) +static int +piv_find_aid(sc_card_t *card) { - sc_apdu_t apdu; + piv_private_data_t *priv = PIV_DATA(card); u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - int r,i; + int r, i, j; const u8 *tag; size_t taglen; + const u8 *nextac; + const u8 *next80; const u8 *pix; size_t pixlen; + const u8 *al_label; + size_t al_labellen; + const u8 *actag; /* Cipher Suite */ + size_t actaglen; + const u8 *csai; /* Cipher Suite Algorithm Identifier */ + size_t csailen; size_t resplen = sizeof(rbuf); +#ifdef ENABLE_PIV_SM + int found_csai = 0; +#endif SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - /* first see if the default applcation will return a template + /* first see if the default application will return a template * that we know about. */ - r = piv_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, rbuf, &resplen); - if (r >= 0 && resplen > 2 ) { + r = iso7816_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, rbuf, &resplen); + if (r > 0 && priv->aid_der.value && resplen == priv->aid_der.len && !memcmp(priv->aid_der.value, rbuf, resplen)) { + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + /* no need to parse again, same as last time */ + } + if (r >= 0 && resplen > 2) { tag = sc_asn1_find_tag(card->ctx, rbuf, resplen, 0x61, &taglen); if (tag != NULL) { + priv->init_flags |= PIV_INIT_AID_PARSED; + /* look for 800-73-4 0xAC for Cipher Suite Algorithm Identifier Table 14 */ + /* 800-73-4 only expects 1 0xAC tag len 6 with a 80 01 xx 06 01 00 + * where xx is the SM csID either 27 or 2E. + * Some vendors may include entries for supported Algorithms even when + * not required. We will use these if possible + * Will look for multiple 0x80 with in 0xAC value too. + */ + nextac = tag; + while ((actag = sc_asn1_find_tag(card->ctx, nextac, taglen - (nextac - tag), + 0xAC, &actaglen)) != NULL) { + nextac = actag + actaglen; + + next80 = actag; + while ((csai = sc_asn1_find_tag(card->ctx, next80, actaglen - (next80 - actag), + 0x80, &csailen)) != NULL) { + next80 = csai + csailen; + if (csailen == 1) { + sc_log(card->ctx, "0xAC 0x80 entry:0x%2.2x found", *csai); + j = piv_ai_map_find_by_id(card, *csai); + if (j >= 0) { + priv->alg_ids |= ai_map[j].ai_flag; + continue; + } +#ifdef ENABLE_PIV_SM + /* check if id is NIST SM */ + for (i = 0; i < PIV_CSS_SIZE; i++) { + if (*csai != css[i].id) + continue; + if (found_csai) { + sc_log(card->ctx, "found multiple csIDs, using first"); + } else { + priv->cs = &css[i]; + priv->csID = *csai; + found_csai++; + priv->init_flags |= PIV_INIT_AID_AC_SM; + } + } +#endif /* ENABLE_PIV_SM */ + } + } + } + + /* Last chance to distinguish card type based on Application Label '50' */ + al_label = sc_asn1_find_tag(card->ctx, tag, taglen, 0x50, &al_labellen); + if (al_label != NULL) { + sc_log_hex(card->ctx, "Application Label", al_label, al_labellen); + if ((priv->al_label = malloc(al_labellen)) == NULL) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + memcpy(priv->al_label, al_label, al_labellen); + priv->al_labellen = (int)al_labellen; + } + pix = sc_asn1_find_tag(card->ctx, tag, taglen, 0x4F, &pixlen); - if (pix != NULL ) { + if (pix != NULL) { sc_log(card->ctx, "found PIX"); /* early cards returned full AID, rather then just the pix */ for (i = 0; piv_aids[i].len_long != 0; i++) { - if ((pixlen >= 6 && memcmp(pix, piv_aids[i].value + 5, - piv_aids[i].len_long - 5 ) == 0) - || ((pixlen >= piv_aids[i].len_short && - memcmp(pix, piv_aids[i].value, - piv_aids[i].len_short) == 0))) { - if (card->type > SC_CARD_TYPE_PIV_II_BASE && - card->type < SC_CARD_TYPE_PIV_II_BASE+1000 && - card->type == piv_aids[i].enumtag) { - LOG_FUNC_RETURN(card->ctx, i); - } else { - LOG_FUNC_RETURN(card->ctx, i); + if ((pixlen >= piv_aids[i].len_long - 5 && + memcmp(pix, piv_aids[i].value + 5, piv_aids[i].len_long - 5) == 0) || + ((pixlen >= piv_aids[i].len_short && memcmp(pix, piv_aids[i].value, + piv_aids[i].len_short) == 0))) { + free(priv->aid_der.value); /* free previous value if any */ + if ((priv->aid_der.value = malloc(resplen)) == NULL) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } + memcpy(priv->aid_der.value, rbuf, resplen); + priv->aid_der.len = resplen; + LOG_FUNC_RETURN(card->ctx, i); } } } } } - /* for testing, we can force the use of a specific AID - * by using the card= parameter in conf file - */ - for (i = 0; piv_aids[i].len_long != 0; i++) { - if (card->type > SC_CARD_TYPE_PIV_II_BASE && - card->type < SC_CARD_TYPE_PIV_II_BASE+1000 && - card->type != piv_aids[i].enumtag) { - continue; - } - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x04, 0x00); - apdu.lc = piv_aids[i].len_long; - apdu.data = piv_aids[i].value; - - apdu.datalen = apdu.lc; - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - apdu.le = 256; - - r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r) { - if (card->type != 0 && card->type == piv_aids[i].enumtag) - LOG_FUNC_RETURN(card->ctx, i); - continue; - } - - if ( apdu.resplen == 0 && r == 0) { - /* could be the MSU card */ - continue; /* other cards will return a FCI */ - } - - if (apdu.resp[0] != 0x6f || apdu.resp[1] > apdu.resplen - 2 ) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NO_CARD_SUPPORT); - - card->ops->process_fci(card, aid_file, apdu.resp+2, apdu.resp[1]); - - LOG_FUNC_RETURN(card->ctx, i); - } - LOG_FUNC_RETURN(card->ctx, SC_ERROR_NO_CARD_SUPPORT); } @@ -842,15 +3012,17 @@ static int piv_find_aid(sc_card_t * card, sc_file_t *aid_file) * Also used for testing of History and Discovery objects from a file * when testing with a card that does not support these new objects. */ -static int piv_read_obj_from_file(sc_card_t * card, char * filename, - u8 **buf, size_t *buf_len) +static int +piv_read_obj_from_file(sc_card_t *card, char *filename, + u8 **buf, size_t *buf_len) { int r; + int r_tag; int f = -1; size_t len; u8 tagbuf[16]; size_t rbuflen; - const u8 * body; + const u8 *body; unsigned int cla_out, tag_out; size_t bodylen; @@ -860,20 +3032,23 @@ static int piv_read_obj_from_file(sc_card_t * card, char * filename, *buf_len = 0; f = open(filename, O_RDONLY); if (f < 0) { - sc_log(card->ctx, "Unable to load PIV off card file: \"%s\"",filename); - r = SC_ERROR_FILE_NOT_FOUND; - goto err; + sc_log(card->ctx, "Unable to load PIV off card file: \"%s\"", filename); + r = SC_ERROR_FILE_NOT_FOUND; + goto err; } len = read(f, tagbuf, sizeof(tagbuf)); /* get tag and length */ if (len < 2 || len > sizeof(tagbuf)) { - sc_log(card->ctx, "Problem with \"%s\"",filename); - r = SC_ERROR_DATA_OBJECT_NOT_FOUND; + sc_log(card->ctx, "Problem with \"%s\"", filename); + r = SC_ERROR_DATA_OBJECT_NOT_FOUND; goto err; } body = tagbuf; - if (sc_asn1_read_tag(&body, 0xfffff, &cla_out, &tag_out, &bodylen) != SC_SUCCESS) { + /* accept any tag for now, just get length */ + r_tag = sc_asn1_read_tag(&body, len, &cla_out, &tag_out, &bodylen); + if ((r_tag != SC_SUCCESS && r_tag != SC_ERROR_ASN1_END_OF_CONTENTS) || + body == NULL) { sc_log(card->ctx, "DER problem"); - r = SC_ERROR_INVALID_ASN1_OBJECT; + r = SC_ERROR_FILE_NOT_FOUND; goto err; } rbuflen = body - tagbuf + bodylen; @@ -882,17 +3057,18 @@ static int piv_read_obj_from_file(sc_card_t * card, char * filename, r = SC_ERROR_OUT_OF_MEMORY; goto err; } - memcpy(*buf, tagbuf, len); /* copy first or only part */ - if (rbuflen > len) { + memcpy(*buf, tagbuf, MIN(len, rbuflen)); /* copy first or only part */ + /* read rest of file */ + if (rbuflen > len + sizeof(tagbuf)) { len = read(f, *buf + sizeof(tagbuf), rbuflen - sizeof(tagbuf)); /* read rest */ if (len != rbuflen - sizeof(tagbuf)) { r = SC_ERROR_INVALID_ASN1_OBJECT; - free (*buf); + free(*buf); *buf = NULL; goto err; } } - r = rbuflen; + r = (int)rbuflen; *buf_len = rbuflen; err: if (f >= 0) @@ -902,96 +3078,143 @@ static int piv_read_obj_from_file(sc_card_t * card, char * filename, /* the tag is the PIV_OBJ_* */ static int -piv_get_data(sc_card_t * card, int enumtag, u8 **buf, size_t *buf_len) +piv_get_data(sc_card_t *card, int enumtag, u8 **buf, size_t *buf_len) { + piv_private_data_t *priv = PIV_DATA(card); u8 *p; + u8 *tbuf; int r = 0; u8 tagbuf[8]; size_t tag_len; + int alloc_buf = 0; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_log(card->ctx, "#%d", enumtag); - /* assert(enumtag >= 0 && enumtag < PIV_OBJ_LAST_ENUM); */ + sc_log(card->ctx, "#%d, %s", enumtag, piv_objects[enumtag].name); + + r = sc_lock(card); /* do check len and get data in same transaction */ + if (r != SC_SUCCESS) { + sc_log(card->ctx, "sc_lock failed"); + return r; + } tag_len = piv_objects[enumtag].tag_len; p = tagbuf; - put_tag_and_len(0x5c, tag_len, &p); - memcpy(p, piv_objects[enumtag].tag_value, tag_len); - p += tag_len; - - if (*buf_len == 1 && *buf == NULL) { /* we need to get the length */ - u8 rbufinitbuf[8]; /* tag of 53 with 82 xx xx will fit in 4 */ - u8 *rbuf; - size_t rbuflen; - size_t bodylen; - unsigned int cla_out, tag_out; - const u8 *body; - - sc_log(card->ctx, "get len of #%d", enumtag); - rbuf = rbufinitbuf; - rbuflen = sizeof(rbufinitbuf); - r = piv_general_io(card, 0xCB, 0x3F, 0xFF, tagbuf, p - tagbuf, &rbuf, &rbuflen); - if (r > 0) { - body = rbuf; - if (sc_asn1_read_tag(&body, 0xffff, &cla_out, &tag_out, &bodylen) != SC_SUCCESS) { - sc_log(card->ctx, "***** received buffer tag MISSING "); - r = SC_ERROR_FILE_NOT_FOUND; - goto err; - } - *buf_len = r; - } else if ( r == 0 ) { - r = SC_ERROR_FILE_NOT_FOUND; - goto err; - } else { - goto err; - } + r = sc_asn1_put_tag(0x5c, piv_objects[enumtag].tag_value, tag_len, tagbuf, sizeof(tagbuf), &p); + if (r != SC_SUCCESS) { + sc_log(card->ctx, "Failed to encode ASN1 tag"); + goto err; + } + + if (*buf_len == 1 && *buf == NULL) { + *buf_len = priv->max_object_size; /* will allocate below */ + alloc_buf = 1; } sc_log(card->ctx, - "buffer for #%d *buf=0x%p len=%"SC_FORMAT_LEN_SIZE_T"u", - enumtag, *buf, *buf_len); + "buffer for #%d *buf=0x%p len=%" SC_FORMAT_LEN_SIZE_T "u", + enumtag, *buf, *buf_len); if (*buf == NULL && *buf_len > 0) { + if (*buf_len > MAX_FILE_SIZE) { + r = SC_ERROR_INTERNAL; + goto err; + } *buf = malloc(*buf_len); - if (*buf == NULL ) { + if (*buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } } - r = piv_general_io(card, 0xCB, 0x3F, 0xFF, tagbuf, p - tagbuf, buf, buf_len); +#ifdef ENABLE_PIV_SM + /* + * Over contact reader, OK to read non sensitive object in clear even when SM is active + * but only if using default policy and we are not in reader_lock_obtained + * Discovery object will use SM from reader_lock_obtained to catch if SM is still valid + * i.e. no interference from other applications + */ + sc_log(card->ctx, "enumtag:%d sm_ctx.sm_mode:%d piv_objects[enumtag].flags:0x%8.8x sm_flags:0x%8.8lx it_flags:0x%8.8x", + enumtag, card->sm_ctx.sm_mode, piv_objects[enumtag].flags, priv->sm_flags, priv->init_flags); + if (priv->sm_flags & PIV_SM_FLAGS_SM_IS_ACTIVE && + enumtag != PIV_OBJ_DISCOVERY && + card->sm_ctx.sm_mode == SM_MODE_TRANSMIT && + !(piv_objects[enumtag].flags & PIV_OBJECT_NEEDS_PIN) && + !(priv->sm_flags & (PIV_SM_FLAGS_NEVER | PIV_SM_FLAGS_ALWAYS)) && + !(priv->init_flags & (PIV_INIT_CONTACTLESS | PIV_INIT_IN_READER_LOCK_OBTAINED))) { + sc_log(card->ctx, "Set PIV_SM_GET_DATA_IN_CLEAR"); + priv->sm_flags |= PIV_SM_GET_DATA_IN_CLEAR; + } + +#endif /* ENABLE_PIV_SM */ + r = piv_general_io(card, 0xCB, 0x3F, 0xFF, tagbuf, p - tagbuf, *buf, *buf_len); + if (r > 0) { + int r_tag; + unsigned int cla_out, tag_out; + size_t bodylen = 0; + const u8 *body = *buf; + r_tag = sc_asn1_read_tag(&body, r, &cla_out, &tag_out, &bodylen); + if (r_tag != SC_SUCCESS || body == NULL || + ((cla_out << 24 | tag_out) != piv_objects[enumtag].resp_tag)) { + sc_log(card->ctx, "invalid tag or length r_tag:%d body:%p", r_tag, body); + r = SC_ERROR_FILE_NOT_FOUND; + goto err; + } + *buf_len = (body - *buf) + bodylen; + } else if (r == 0) { + r = SC_ERROR_FILE_NOT_FOUND; + goto err; + } else { + goto err; + } + + if (alloc_buf && *buf) { + tbuf = malloc(r); + if (tbuf == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + memcpy(tbuf, *buf, r); + free(*buf); + alloc_buf = 0; + *buf = tbuf; + } err: + if (alloc_buf) { + free(*buf); + *buf = NULL; + } + sc_unlock(card); LOG_FUNC_RETURN(card->ctx, r); } - static int -piv_get_cached_data(sc_card_t * card, int enumtag, u8 **buf, size_t *buf_len) +piv_get_cached_data(sc_card_t *card, int enumtag, u8 **buf, size_t *buf_len) { - piv_private_data_t * priv = PIV_DATA(card); + piv_private_data_t *priv = PIV_DATA(card); int r; u8 *rbuf = NULL; size_t rbuflen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_log(card->ctx, "#%d", enumtag); - assert(enumtag >= 0 && enumtag < PIV_OBJ_LAST_ENUM); + if (enumtag < 0 || enumtag >= PIV_OBJ_LAST_ENUM) + return SC_ERROR_INTERNAL; + + sc_log(card->ctx, "#%d, %s", enumtag, piv_objects[enumtag].name); /* see if we have it cached */ if (priv->obj_cache[enumtag].flags & PIV_OBJ_CACHE_VALID) { sc_log(card->ctx, - "found #%d %p:%"SC_FORMAT_LEN_SIZE_T"u %p:%"SC_FORMAT_LEN_SIZE_T"u", - enumtag, - priv->obj_cache[enumtag].obj_data, - priv->obj_cache[enumtag].obj_len, - priv->obj_cache[enumtag].internal_obj_data, - priv->obj_cache[enumtag].internal_obj_len); - + "found #%d %p:%" SC_FORMAT_LEN_SIZE_T "u %p:%" SC_FORMAT_LEN_SIZE_T "u", + enumtag, + priv->obj_cache[enumtag].obj_data, + priv->obj_cache[enumtag].obj_len, + priv->obj_cache[enumtag].internal_obj_data, + priv->obj_cache[enumtag].internal_obj_len); if (priv->obj_cache[enumtag].obj_len == 0) { r = SC_ERROR_FILE_NOT_FOUND; @@ -1000,7 +3223,7 @@ piv_get_cached_data(sc_card_t * card, int enumtag, u8 **buf, size_t *buf_len) } *buf = priv->obj_cache[enumtag].obj_data; *buf_len = priv->obj_cache[enumtag].obj_len; - r = *buf_len; + r = (int)*buf_len; goto ok; } @@ -1008,6 +3231,7 @@ piv_get_cached_data(sc_card_t * card, int enumtag, u8 **buf, size_t *buf_len) * If we know it can not be on the card i.e. History object * has been read, and we know what other certs may or * may not be on the card. We can avoid extra overhead + * Also used if object on card was not parsable */ if (priv->obj_cache[enumtag].flags & PIV_OBJ_CACHE_NOT_PRESENT) { @@ -1017,7 +3241,7 @@ piv_get_cached_data(sc_card_t * card, int enumtag, u8 **buf, size_t *buf_len) } /* Not cached, try to get it, piv_get_data will allocate a buf */ - sc_log(card->ctx, "get #%d", enumtag); + sc_log(card->ctx, "get #%d", enumtag); rbuflen = 1; r = piv_get_data(card, enumtag, &rbuf, &rbuflen); if (r > 0) { @@ -1028,19 +3252,22 @@ piv_get_cached_data(sc_card_t * card, int enumtag, u8 **buf, size_t *buf_len) *buf_len = r; sc_log(card->ctx, - "added #%d %p:%"SC_FORMAT_LEN_SIZE_T"u %p:%"SC_FORMAT_LEN_SIZE_T"u", - enumtag, - priv->obj_cache[enumtag].obj_data, - priv->obj_cache[enumtag].obj_len, - priv->obj_cache[enumtag].internal_obj_data, - priv->obj_cache[enumtag].internal_obj_len); - - } else if (r == 0 || r == SC_ERROR_FILE_NOT_FOUND) { - r = SC_ERROR_FILE_NOT_FOUND; - priv->obj_cache[enumtag].flags |= PIV_OBJ_CACHE_VALID; - priv->obj_cache[enumtag].obj_len = 0; - } else if ( r < 0) { - goto err; + "added #%d %p:%" SC_FORMAT_LEN_SIZE_T "u %p:%" SC_FORMAT_LEN_SIZE_T "u", + enumtag, + priv->obj_cache[enumtag].obj_data, + priv->obj_cache[enumtag].obj_len, + priv->obj_cache[enumtag].internal_obj_data, + priv->obj_cache[enumtag].internal_obj_len); + + } else { + free(rbuf); + if (r == 0 || r == SC_ERROR_FILE_NOT_FOUND) { + r = SC_ERROR_FILE_NOT_FOUND; + priv->obj_cache[enumtag].flags |= PIV_OBJ_CACHE_VALID; + priv->obj_cache[enumtag].obj_len = 0; + } else { + goto err; + } } ok: @@ -1048,25 +3275,29 @@ piv_get_cached_data(sc_card_t * card, int enumtag, u8 **buf, size_t *buf_len) LOG_FUNC_RETURN(card->ctx, r); } - static int piv_cache_internal_data(sc_card_t *card, int enumtag) { - piv_private_data_t * priv = PIV_DATA(card); - const u8* tag; - const u8* body; + piv_private_data_t *priv = PIV_DATA(card); + const u8 *tag; + const u8 *body; size_t taglen; size_t bodylen; int compressed = 0; + int r = SC_SUCCESS; +#ifdef ENABLE_PIV_SM + u8 *cvc_start = NULL; + size_t cvc_len = 0; +#endif /* if already cached */ if (priv->obj_cache[enumtag].internal_obj_data && priv->obj_cache[enumtag].internal_obj_len) { sc_log(card->ctx, - "#%d found internal %p:%"SC_FORMAT_LEN_SIZE_T"u", - enumtag, - priv->obj_cache[enumtag].internal_obj_data, - priv->obj_cache[enumtag].internal_obj_len); - LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + "#%d found internal %p:%" SC_FORMAT_LEN_SIZE_T "u", + enumtag, + priv->obj_cache[enumtag].internal_obj_data, + priv->obj_cache[enumtag].internal_obj_len); + LOG_FUNC_RETURN(card->ctx, r); } body = sc_asn1_find_tag(card->ctx, @@ -1074,18 +3305,22 @@ piv_cache_internal_data(sc_card_t *card, int enumtag) priv->obj_cache[enumtag].obj_len, 0x53, &bodylen); - if (body == NULL) + if (body == NULL || priv->obj_cache[enumtag].obj_data[0] != 0x53) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_VALID); /* get the certificate out */ - if (piv_objects[enumtag].flags & PIV_OBJECT_TYPE_CERT) { + if (piv_objects[enumtag].flags & PIV_OBJECT_TYPE_CERT) { tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x71, &taglen); /* 800-72-1 not clear if this is 80 or 01 Sent comment to NIST for 800-72-2 */ /* 800-73-3 says it is 01, keep dual test so old cards still work */ - if (tag && (((*tag) & 0x80) || ((*tag) & 0x01))) + if (tag && taglen > 0 && (((*tag) & 0x80) || ((*tag) & 0x01))) compressed = 1; +#ifdef ENABLE_PIV_SM + cvc_start = (u8 *)tag + taglen; /* save for later as cvs (if present) follows 0x71 */ +#endif + tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x70, &taglen); if (tag == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_VALID); @@ -1093,33 +3328,41 @@ piv_cache_internal_data(sc_card_t *card, int enumtag) if (taglen == 0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND); - if(compressed) { -#ifdef ENABLE_ZLIB - size_t len; - u8* newBuf = NULL; - - if(SC_SUCCESS != sc_decompress_alloc(&newBuf, &len, tag, taglen, COMPRESSION_AUTO)) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_VALID); - - priv->obj_cache[enumtag].internal_obj_data = newBuf; - priv->obj_cache[enumtag].internal_obj_len = len; -#else - sc_log(card->ctx, "PIV compression not supported, no zlib"); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); -#endif + if (compressed) { + priv->obj_cache[enumtag].flags |= PIV_OBJ_CACHE_COMPRESSED; } - else { - if (!(priv->obj_cache[enumtag].internal_obj_data = malloc(taglen))) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + /* internal certificate remains compressed */ + if (!(priv->obj_cache[enumtag].internal_obj_data = malloc(taglen))) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + + memcpy(priv->obj_cache[enumtag].internal_obj_data, tag, taglen); + priv->obj_cache[enumtag].internal_obj_len = taglen; - memcpy(priv->obj_cache[enumtag].internal_obj_data, tag, taglen); - priv->obj_cache[enumtag].internal_obj_len = taglen; +#ifdef ENABLE_PIV_SM + /* PIV_OBJ_SM_CERT_SIGNER CERT OBJECT may also have a intermediate CVC */ + if (piv_objects[enumtag].flags & PIV_OBJECT_TYPE_CVC) { + /* cvc if present should be at cvc_start. + * find the tag(T) and get value(V) and len(L) from TLV. + * Could reconstruct ASN1 of (T)(L) stating location from length and known tag. + * as the size of (L) depends on the length of value + */ + if ((tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x7F21, &taglen)) != NULL && + cvc_start && + cvc_start < tag && + cvc_start[0] == 0x7f && cvc_start[1] == 0x21) { + cvc_len = tag - cvc_start + taglen; + /* decode the intermediate CVC */ + r = piv_decode_cvc(card, &cvc_start, &cvc_len, &priv->sm_in_cvc); + if (r < 0) { + sc_log(card->ctx, "unable to parse intermediate CVC: %d skipping", r); + } + priv->sm_flags |= PIV_SM_FLAGS_SM_IN_CVC_PRESENT; + } } +#endif /* ENABLE_PIV_SM */ - /* convert pub key to internal */ -/* TODO: -DEE need to fix ... would only be used if we cache the pub key, but we don't today */ - } - else if (piv_objects[enumtag].flags & PIV_OBJECT_TYPE_PUBKEY) { + /* convert pub key to internal */ + } else if (piv_objects[enumtag].flags & PIV_OBJECT_TYPE_PUBKEY) { tag = sc_asn1_find_tag(card->ctx, body, bodylen, *body, &taglen); if (tag == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_VALID); @@ -1132,29 +3375,27 @@ piv_cache_internal_data(sc_card_t *card, int enumtag) memcpy(priv->obj_cache[enumtag].internal_obj_data, tag, taglen); priv->obj_cache[enumtag].internal_obj_len = taglen; - } - else { + } else { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } - sc_log(card->ctx, "added #%d internal %p:%"SC_FORMAT_LEN_SIZE_T"u", - enumtag, - priv->obj_cache[enumtag].internal_obj_data, - priv->obj_cache[enumtag].internal_obj_len); + sc_log(card->ctx, "added #%d internal %p:%" SC_FORMAT_LEN_SIZE_T "u", + enumtag, + priv->obj_cache[enumtag].internal_obj_data, + priv->obj_cache[enumtag].internal_obj_len); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } - /* * Callers of this may be expecting a certificate, * select file will have saved the object type for us * as well as set that we want the cert from the object. */ static int -piv_read_binary(sc_card_t *card, unsigned int idx, unsigned char *buf, size_t count, unsigned long flags) +piv_read_binary(sc_card_t *card, unsigned int idx, unsigned char *buf, size_t count, unsigned long *flags) { - piv_private_data_t * priv = PIV_DATA(card); + piv_private_data_t *priv = PIV_DATA(card); int enumtag; int r; u8 *rbuf = NULL; @@ -1164,22 +3405,20 @@ piv_read_binary(sc_card_t *card, unsigned int idx, unsigned char *buf, size_t co SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (priv->selected_obj < 0) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); enumtag = piv_objects[priv->selected_obj].enumtag; if (priv->rwb_state == -1) { r = piv_get_cached_data(card, enumtag, &rbuf, &rbuflen); - if (r >=0) { + if (r >= 0) { /* an object with no data will be considered not found */ /* Discovery tag = 0x73, all others are 0x53 */ - if (!rbuf || rbuf[0] == 0x00 || ((rbuf[0]&0xDF) == 0x53 && rbuf[1] == 0x00)) { + if (!rbuf || rbuf[0] == 0x00 || ((rbuf[0] & 0xDF) == 0x53 && rbuf[1] == 0x00)) { r = SC_ERROR_FILE_NOT_FOUND; goto err; } - sc_log(card->ctx, - "DEE rbuf=%p,rbuflen=%"SC_FORMAT_LEN_SIZE_T"u,", - rbuf, rbuflen); + body = sc_asn1_find_tag(card->ctx, rbuf, rbuflen, rbuf[0], &bodylen); if (body == NULL) { /* if missing, assume its the body */ @@ -1190,8 +3429,8 @@ piv_read_binary(sc_card_t *card, unsigned int idx, unsigned char *buf, size_t co } if (bodylen > body - rbuf + rbuflen) { sc_log(card->ctx, - " ***** tag length > then data: %"SC_FORMAT_LEN_SIZE_T"u>%"SC_FORMAT_LEN_PTRDIFF_T"u+%"SC_FORMAT_LEN_SIZE_T"u", - bodylen, body - rbuf, rbuflen); + " ***** tag length > then data: %" SC_FORMAT_LEN_SIZE_T "u>%" SC_FORMAT_LEN_PTRDIFF_T "u+%" SC_FORMAT_LEN_SIZE_T "u", + bodylen, body - rbuf, rbuflen); r = SC_ERROR_INVALID_DATA; goto err; } @@ -1201,7 +3440,6 @@ piv_read_binary(sc_card_t *card, unsigned int idx, unsigned char *buf, size_t co if (r < 0) goto err; } - } priv->rwb_state = 0; } @@ -1209,27 +3447,29 @@ piv_read_binary(sc_card_t *card, unsigned int idx, unsigned char *buf, size_t co if (priv->return_only_cert || piv_objects[enumtag].flags & PIV_OBJECT_TYPE_PUBKEY) { rbuf = priv->obj_cache[enumtag].internal_obj_data; rbuflen = priv->obj_cache[enumtag].internal_obj_len; + if ((priv->obj_cache[enumtag].flags & PIV_OBJ_CACHE_COMPRESSED) && flags) { + *flags |= SC_FILE_FLAG_COMPRESSED_AUTO; + } } else { rbuf = priv->obj_cache[enumtag].obj_data; rbuflen = priv->obj_cache[enumtag].obj_len; } /* rbuf rbuflen has pointer and length to cached data */ - if ( rbuflen < idx + count) + if (rbuflen < idx + count) count = rbuflen - idx; if (count <= 0) { r = 0; priv->rwb_state = 1; } else { memcpy(buf, rbuf + idx, count); - r = count; + r = (int)count; } err: LOG_FUNC_RETURN(card->ctx, r); } - /* * the tag is the PIV_OBJ_* * The buf should have the 0x53 tag+len+tags and data @@ -1239,74 +3479,85 @@ static int piv_put_data(sc_card_t *card, int tag, const u8 *buf, size_t buf_len) { int r; - u8 * sbuf; + u8 *sbuf; size_t sbuflen; - u8 * p; + u8 *p; size_t tag_len; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); tag_len = piv_objects[tag].tag_len; - sbuflen = put_tag_and_len(0x5c, tag_len, NULL) + buf_len; - if (!(sbuf = malloc(sbuflen))) + r = sc_asn1_put_tag(0x5c, piv_objects[tag].tag_value, tag_len, NULL, 0, NULL); + if (r <= 0) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + sbuflen = r + buf_len; + if (!(sbuf = malloc(sbuflen))) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } p = sbuf; - put_tag_and_len(0x5c, tag_len, &p); - memcpy(p, piv_objects[tag].tag_value, tag_len); - p += tag_len; + r = sc_asn1_put_tag(0x5c, piv_objects[tag].tag_value, tag_len, sbuf, sbuflen, &p); + if (r != SC_SUCCESS) { + LOG_FUNC_RETURN(card->ctx, r); + } + /* This is safe as we calculated the size of buffer above */ memcpy(p, buf, buf_len); p += buf_len; - r = piv_general_io(card, 0xDB, 0x3F, 0xFF, sbuf, p - sbuf, NULL, NULL); + r = piv_general_io(card, 0xDB, 0x3F, 0xFF, sbuf, p - sbuf, NULL, 0); if (sbuf) free(sbuf); LOG_FUNC_RETURN(card->ctx, r); } - static int -piv_write_certificate(sc_card_t *card, const u8* buf, size_t count, unsigned long flags) +piv_write_certificate(sc_card_t *card, const u8 *buf, size_t count, unsigned long flags) { - piv_private_data_t * priv = PIV_DATA(card); - int enumtag; + piv_private_data_t *priv = PIV_DATA(card); + int enumtag, tmplen, tmplen2, tmplen3; int r = SC_SUCCESS; u8 *sbuf = NULL; u8 *p; size_t sbuflen; size_t taglen; - sc_log(card->ctx, "DEE cert len=%"SC_FORMAT_LEN_SIZE_T"u", count); - taglen = put_tag_and_len(0x70, count, NULL) - + put_tag_and_len(0x71, 1, NULL) - + put_tag_and_len(0xFE, 0, NULL); + if ((tmplen = sc_asn1_put_tag(0x70, buf, count, NULL, 0, NULL)) <= 0 || + (tmplen2 = sc_asn1_put_tag(0x71, NULL, 1, NULL, 0, NULL)) <= 0 || + (tmplen3 = sc_asn1_put_tag(0xFE, NULL, 0, NULL, 0, NULL)) <= 0) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } - sbuflen = put_tag_and_len(0x53, taglen, NULL); + taglen = tmplen + tmplen2 + tmplen3; + tmplen = sc_asn1_put_tag(0x53, NULL, taglen, NULL, 0, NULL); + if (tmplen <= 0) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + sbuflen = tmplen; sbuf = malloc(sbuflen); if (sbuf == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); p = sbuf; - put_tag_and_len(0x53, taglen, &p); - - put_tag_and_len(0x70, count, &p); - memcpy(p, buf, count); - p += count; - put_tag_and_len(0x71, 1, &p); + if ((r = sc_asn1_put_tag(0x53, NULL, taglen, sbuf, sbuflen, &p)) != SC_SUCCESS || + (r = sc_asn1_put_tag(0x70, buf, count, p, sbuflen - (p - sbuf), &p)) != SC_SUCCESS || + (r = sc_asn1_put_tag(0x71, NULL, 1, p, sbuflen - (p - sbuf), &p)) != SC_SUCCESS) { + goto out; + } /* Use 01 as per NIST 800-73-3 */ - *p++ = (flags)? 0x01:0x00; /* certinfo, i.e. gziped? */ - put_tag_and_len(0xFE,0,&p); /* LRC tag */ - - sc_log(card->ctx, "DEE buf %p len %"SC_FORMAT_LEN_PTRDIFF_T"u %"SC_FORMAT_LEN_SIZE_T"u", - sbuf, p - sbuf, sbuflen); + *p++ = (flags) ? 0x01 : 0x00; /* certinfo, i.e. gzipped? */ + r = sc_asn1_put_tag(0xFE, NULL, 0, p, sbuflen - (p - sbuf), &p); + if (r != SC_SUCCESS) { + goto out; + } enumtag = piv_objects[priv->selected_obj].enumtag; r = piv_put_data(card, enumtag, sbuf, sbuflen); - if (sbuf) - free(sbuf); +out: + free(sbuf); LOG_FUNC_RETURN(card->ctx, r); } @@ -1316,10 +3567,10 @@ piv_write_certificate(sc_card_t *card, const u8* buf, size_t count, unsigned lon * Note: the select file will have saved the object type for us * Write is used by piv-tool, so we will use flags: * length << 8 | 8bits: - * object xxxx0000 - * uncompresed cert xxx00001 - * compressed cert xxx10001 - * pubkey xxxx0010 + * object xxxx0000 + * uncompressed cert xxx00001 + * compressed cert xxx10001 + * pubkey xxxx0010 * * to indicate we are writing a cert and if is compressed * or if we are writing a pubkey in to the cache. @@ -1330,10 +3581,11 @@ piv_write_certificate(sc_card_t *card, const u8* buf, size_t count, unsigned lon * When the last chuck of the data is sent, we will write it. */ -static int piv_write_binary(sc_card_t *card, unsigned int idx, +static int +piv_write_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { - piv_private_data_t * priv = PIV_DATA(card); + piv_private_data_t *priv = PIV_DATA(card); int r; int enumtag; @@ -1344,35 +3596,25 @@ static int piv_write_binary(sc_card_t *card, unsigned int idx, enumtag = piv_objects[priv->selected_obj].enumtag; - if (priv->rwb_state == 1) /* trying to write at end */ + if (priv->rwb_state == 1) /* trying to write at end */ LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); if (priv->rwb_state == -1) { /* if cached, remove old entry */ if (priv->obj_cache[enumtag].flags & PIV_OBJ_CACHE_VALID) { - priv->obj_cache[enumtag].flags = 0; - if (priv->obj_cache[enumtag].obj_data) { - free(priv->obj_cache[enumtag].obj_data); - priv->obj_cache[enumtag].obj_data = NULL; - priv->obj_cache[enumtag].obj_len = 0; - } - if (priv->obj_cache[enumtag].internal_obj_data) { - free(priv->obj_cache[enumtag].internal_obj_data); - priv->obj_cache[enumtag].internal_obj_data = NULL; - priv->obj_cache[enumtag].internal_obj_len = 0; - } + piv_obj_cache_free_entry(card, enumtag, 0); } if (idx != 0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NO_CARD_SUPPORT); - priv->w_buf_len = flags>>8; + priv->w_buf_len = flags >> 8; if (priv->w_buf_len == 0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); priv->w_buf = malloc(priv->w_buf_len); - priv-> rwb_state = 0; + priv->rwb_state = 0; } /* on each pass make sure we have w_buf */ @@ -1386,21 +3628,20 @@ static int piv_write_binary(sc_card_t *card, unsigned int idx, /* if this was not the last chunk, return to get rest */ if (idx + count < priv->w_buf_len) - LOG_FUNC_RETURN(card->ctx, count); + LOG_FUNC_RETURN(card->ctx, (int)count); - priv-> rwb_state = 1; /* at end of object */ + priv->rwb_state = 1; /* at end of object */ switch (flags & 0x0f) { - case 1: - r = piv_write_certificate(card, priv->w_buf, priv->w_buf_len, flags & 0x10); - break; - case 2: /* pubkey to be added to cache, it should have 0x53 and 0x99 tags. */ - /* TODO: -DEE this is not fully implemented and not used */ - r = priv->w_buf_len; - break; - default: - r = piv_put_data(card, enumtag, priv->w_buf, priv->w_buf_len); - break; + case 1: + r = piv_write_certificate(card, priv->w_buf, priv->w_buf_len, flags & 0x10); + break; + case 2: /* pubkey to be added to cache, it should have 0x53 and 0x99 tags. */ + r = (int)priv->w_buf_len; + break; + default: + r = piv_put_data(card, enumtag, priv->w_buf, priv->w_buf_len); + break; } /* if it worked, will cache it */ if (r >= 0 && priv->w_buf) { @@ -1413,43 +3654,61 @@ static int piv_write_binary(sc_card_t *card, unsigned int idx, } priv->w_buf = NULL; priv->w_buf_len = 0; - LOG_FUNC_RETURN(card->ctx, (r < 0)? r : (int)count); + LOG_FUNC_RETURN(card->ctx, (r < 0) ? r : (int)count); } /* * Card initialization is NOT standard. - * Some cards use mutual or external authentication usings 3des or aes key. We - * will read in the key from a file either binary or hex encided. + * Some cards use mutual or external authentication using 3des or aes key. We + * will read in the key from a file either binary or hex encoded. * This is only needed during initialization/personalization of the card */ #ifdef ENABLE_OPENSSL -static const EVP_CIPHER *get_cipher_for_algo(int alg_id) +static EVP_CIPHER * +get_cipher_for_algo(sc_card_t *card, int alg_id) { + const char *algo; switch (alg_id) { - case 0x0: return EVP_des_ede3_ecb(); - case 0x1: return EVP_des_ede3_ecb(); /* 2TDES */ - case 0x3: return EVP_des_ede3_ecb(); - case 0x8: return EVP_aes_128_ecb(); - case 0xA: return EVP_aes_192_ecb(); - case 0xC: return EVP_aes_256_ecb(); - default: return NULL; + case 0x0: + case 0x1: /* 2TDES */ + case 0x3: + algo = "DES-EDE3-ECB"; + break; + case 0x8: + algo = "AES-128-ECB"; + break; + case 0xA: + algo = "AES-192-ECB"; + break; + case 0xC: + algo = "AES-256-ECB"; + break; + default: + return NULL; } + return sc_evp_cipher(card->ctx, algo); } -static int get_keylen(unsigned int alg_id, size_t *size) +static int +get_keylen(unsigned int alg_id, size_t *size) { - switch(alg_id) { - case 0x01: *size = 192/8; /* 2TDES still has 3 single des keys phase out by 12/31/2010 */ + switch (alg_id) { + case 0x01: + *size = 192 / 8; /* 2TDES still has 3 single des keys phase out by 12/31/2010 */ break; case 0x00: - case 0x03: *size = 192/8; + case 0x03: + *size = 192 / 8; break; - case 0x08: *size = 128/8; + case 0x08: + *size = 128 / 8; break; - case 0x0A: *size = 192/8; + case 0x0A: + *size = 192 / 8; break; - case 0x0C: *size = 256/8; + case 0x0C: + *size = 256 / 8; break; default: return SC_ERROR_INVALID_ARGUMENTS; @@ -1457,17 +3716,18 @@ static int get_keylen(unsigned int alg_id, size_t *size) return SC_SUCCESS; } -static int piv_get_key(sc_card_t *card, unsigned int alg_id, u8 **key, size_t *len) +static int +piv_get_key(sc_card_t *card, unsigned int alg_id, u8 **key, size_t *len) { int r; size_t fsize; FILE *f = NULL; - char * keyfilename = NULL; + char *keyfilename = NULL; size_t expected_keylen; - size_t keylen; - u8 * keybuf = NULL; - u8 * tkey = NULL; + size_t keylen, readlen; + u8 *keybuf = NULL; + u8 *tkey = NULL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); @@ -1475,13 +3735,13 @@ static int piv_get_key(sc_card_t *card, unsigned int alg_id, u8 **key, size_t *l if (keyfilename == NULL) { sc_log(card->ctx, - "Unable to get PIV_EXT_AUTH_KEY=(null) for general_external_authenticate"); + "Unable to get PIV_EXT_AUTH_KEY=(null) for general_external_authenticate"); r = SC_ERROR_FILE_NOT_FOUND; goto err; } r = get_keylen(alg_id, &expected_keylen); - if(r) { + if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid cipher selector, none found for: %02x", alg_id); r = SC_ERROR_INVALID_ARGUMENTS; goto err; @@ -1497,34 +3757,35 @@ static int piv_get_key(sc_card_t *card, unsigned int alg_id, u8 **key, size_t *l if (0 > fseek(f, 0L, SEEK_END)) r = SC_ERROR_INTERNAL; fsize = ftell(f); - if (0 > (long) fsize) + if (0 > (long)fsize) r = SC_ERROR_INTERNAL; if (0 > fseek(f, 0L, SEEK_SET)) r = SC_ERROR_INTERNAL; - if(r) { + if (r) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not read %s\n", keyfilename); goto err; } - keybuf = malloc(fsize+1); /* if not binary, need null to make it a string */ + keybuf = malloc(fsize + 1); /* if not binary, need null to make it a string */ if (!keybuf) { sc_log(card->ctx, " Unable to allocate key memory"); r = SC_ERROR_OUT_OF_MEMORY; goto err; } - keybuf[fsize] = 0x00; /* incase it is text need null */ + keybuf[fsize] = 0x00; /* in case it is text need null */ - if (fread(keybuf, 1, fsize, f) != fsize) { + if ((readlen = fread(keybuf, 1, fsize, f)) != fsize) { sc_log(card->ctx, " Unable to read key\n"); r = SC_ERROR_WRONG_LENGTH; goto err; } + keybuf[readlen] = '\0'; tkey = malloc(expected_keylen); if (!tkey) { - sc_log(card->ctx, " Unable to allocate key memory"); - r = SC_ERROR_OUT_OF_MEMORY; - goto err; + sc_log(card->ctx, " Unable to allocate key memory"); + r = SC_ERROR_OUT_OF_MEMORY; + goto err; } if (fsize == expected_keylen) { /* it must be binary */ @@ -1535,7 +3796,7 @@ static int piv_get_key(sc_card_t *card, unsigned int alg_id, u8 **key, size_t *l sc_right_trim(keybuf, fsize); keylen = expected_keylen; r = sc_hex_to_bin((char *)keybuf, tkey, &keylen); - if (keylen !=expected_keylen || r != 0 ) { + if (keylen != expected_keylen || r != 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error formatting key\n"); if (r == 0) r = SC_ERROR_INCOMPATIBLE_KEY; @@ -1569,15 +3830,15 @@ static int piv_get_key(sc_card_t *card, unsigned int alg_id, u8 **key, size_t *l * challenges, nonces etc from card are less then 114 (keeps tags simple) */ -static int piv_general_mutual_authenticate(sc_card_t *card, - unsigned int key_ref, unsigned int alg_id) +static int +piv_general_mutual_authenticate(sc_card_t *card, + unsigned int key_ref, unsigned int alg_id) { int r; #ifdef ENABLE_OPENSSL int N; int locked = 0; - u8 *rbuf = NULL; - size_t rbuflen; + u8 rbuf[4096]; u8 *nonce = NULL; size_t nonce_len; u8 *p; @@ -1597,21 +3858,23 @@ static int piv_general_mutual_authenticate(sc_card_t *card, size_t challenge_response_len; u8 *decrypted_reponse = NULL; size_t decrypted_reponse_len; - EVP_CIPHER_CTX * ctx = NULL; + EVP_CIPHER_CTX *ctx = NULL; u8 sbuf[255]; - const EVP_CIPHER *cipher; + EVP_CIPHER *cipher = NULL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) { + sc_log_openssl(card->ctx); r = SC_ERROR_OUT_OF_MEMORY; goto err; } - cipher = get_cipher_for_algo(alg_id); - if(!cipher) { + cipher = get_cipher_for_algo(card, alg_id); + if (!cipher) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid cipher selector, none found for: %02x\n", alg_id); r = SC_ERROR_INVALID_ARGUMENTS; goto err; @@ -1619,7 +3882,7 @@ static int piv_general_mutual_authenticate(sc_card_t *card, r = piv_get_key(card, alg_id, &key, &keylen); if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error getting General Auth key\n"); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error getting General Auth key\n"); goto err; } @@ -1637,25 +3900,26 @@ static int piv_general_mutual_authenticate(sc_card_t *card, *p++ = 0x00; /* get the encrypted nonce */ - r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, &rbuf, &rbuflen); + r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, rbuf, sizeof rbuf); - if (r < 0) goto err; + if (r < 0) + goto err; /* Remove the encompassing outer TLV of 0x7C and get the data */ body = sc_asn1_find_tag(card->ctx, rbuf, - r, 0x7C, &body_len); - if (!body) { + r, 0x7C, &body_len); + if (!body || rbuf[0] != 0x7C) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid Witness Data response of NULL\n"); - r = SC_ERROR_INVALID_DATA; + r = SC_ERROR_INVALID_DATA; goto err; } /* Get the witness data indicated by the TAG 0x80 */ witness_data = sc_asn1_find_tag(card->ctx, body, - body_len, 0x80, &witness_len); - if (!witness_len) { + body_len, 0x80, &witness_len); + if (!witness_len || body_len == 0 || body[0] != 0x80) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid Challenge Data none found in TLV\n"); - r = SC_ERROR_INVALID_DATA; + r = SC_ERROR_INVALID_DATA; goto err; } @@ -1670,20 +3934,23 @@ static int piv_general_mutual_authenticate(sc_card_t *card, /* decrypt the data from the card */ if (!EVP_DecryptInit(ctx, cipher, key, NULL)) { /* may fail if des parity of key is wrong. depends on OpenSSL options */ + sc_log_openssl(card->ctx); r = SC_ERROR_INTERNAL; goto err; } - EVP_CIPHER_CTX_set_padding(ctx,0); + EVP_CIPHER_CTX_set_padding(ctx, 0); p = plain_text; - if (!EVP_DecryptUpdate(ctx, p, &N, witness_data, witness_len)) { + if (!EVP_DecryptUpdate(ctx, p, &N, witness_data, (int)witness_len)) { + sc_log_openssl(card->ctx); r = SC_ERROR_INTERNAL; goto err; } plain_text_len = tmplen = N; p += tmplen; - if(!EVP_DecryptFinal(ctx, p, &N)) { + if (!EVP_DecryptFinal(ctx, p, &N)) { + sc_log_openssl(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1692,8 +3959,8 @@ static int piv_general_mutual_authenticate(sc_card_t *card, if (plain_text_len != witness_len) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, - "Encrypted and decrypted lengths do not match: %"SC_FORMAT_LEN_SIZE_T"u:%"SC_FORMAT_LEN_SIZE_T"u\n", - witness_len, plain_text_len); + "Encrypted and decrypted lengths do not match: %" SC_FORMAT_LEN_SIZE_T "u:%" SC_FORMAT_LEN_SIZE_T "u\n", + witness_len, plain_text_len); r = SC_ERROR_INTERNAL; goto err; } @@ -1705,39 +3972,54 @@ static int piv_general_mutual_authenticate(sc_card_t *card, * the card. */ nonce = malloc(witness_len); - if(!nonce) { + if (!nonce) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, - "OOM allocating nonce (%"SC_FORMAT_LEN_SIZE_T"u : %"SC_FORMAT_LEN_SIZE_T"u)\n", - witness_len, plain_text_len); + "OOM allocating nonce (%" SC_FORMAT_LEN_SIZE_T "u : %" SC_FORMAT_LEN_SIZE_T "u)\n", + witness_len, plain_text_len); r = SC_ERROR_INTERNAL; goto err; } nonce_len = witness_len; - r = RAND_bytes(nonce, witness_len); - if(!r) { + r = RAND_bytes(nonce, (int)witness_len); + if (!r) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, - "Generating random for nonce (%"SC_FORMAT_LEN_SIZE_T"u : %"SC_FORMAT_LEN_SIZE_T"u)\n", - witness_len, plain_text_len); + "Generating random for nonce (%" SC_FORMAT_LEN_SIZE_T "u : %" SC_FORMAT_LEN_SIZE_T "u)\n", + witness_len, plain_text_len); r = SC_ERROR_INTERNAL; goto err; } /* nonce for challenge */ - tmplen = put_tag_and_len(0x81, witness_len, NULL); + r = sc_asn1_put_tag(0x81, NULL, witness_len, NULL, 0, NULL); + if (r <= 0) { + r = SC_ERROR_INTERNAL; + goto err; + } + tmplen = r; /* plain text witness keep a length separate for the 0x7C tag */ - tmplen += put_tag_and_len(0x80, witness_len, NULL); + r = sc_asn1_put_tag(0x80, NULL, witness_len, NULL, 0, NULL); + if (r <= 0) { + r = SC_ERROR_INTERNAL; + goto err; + } + tmplen += r; tmplen2 = tmplen; /* outside 7C tag with 81:80 as innards */ - tmplen = put_tag_and_len(0x7C, tmplen, NULL); + r = sc_asn1_put_tag(0x7C, NULL, tmplen, NULL, 0, NULL); + if (r <= 0) { + r = SC_ERROR_INTERNAL; + goto err; + } - built_len = tmplen; + built_len = r; /* Build the response buffer */ p = built = malloc(built_len); - if(!built) { + if (!built) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "OOM Building witness response and challenge\n"); r = SC_ERROR_INTERNAL; goto err; @@ -1746,72 +4028,79 @@ static int piv_general_mutual_authenticate(sc_card_t *card, p = built; /* Start with the 7C Tag */ - put_tag_and_len(0x7C, tmplen2, &p); + r = sc_asn1_put_tag(0x7C, NULL, tmplen2, p, built_len, &p); + if (r != SC_SUCCESS) { + goto err; + } /* Add the DECRYPTED witness, tag 0x80 */ - put_tag_and_len(0x80, witness_len, &p); - memcpy(p, plain_text, witness_len); - p += witness_len; + r = sc_asn1_put_tag(0x80, plain_text, witness_len, p, built_len - (p - built), &p); + if (r != SC_SUCCESS) { + goto err; + } /* Add the challenge, tag 0x81 */ - put_tag_and_len(0x81, witness_len, &p); - memcpy(p, nonce, witness_len); - - /* Don't leak rbuf from above */ - free(rbuf); - rbuf = NULL; + r = sc_asn1_put_tag(0x81, nonce, witness_len, p, built_len - (p - built), &p); + if (r != SC_SUCCESS) { + goto err; + } /* Send constructed data */ - r = piv_general_io(card, 0x87, alg_id, key_ref, built,built_len, &rbuf, &rbuflen); - if (r < 0) goto err; + r = piv_general_io(card, 0x87, alg_id, key_ref, built, built_len, rbuf, sizeof rbuf); + if (r < 0) { + goto err; + } /* Remove the encompassing outer TLV of 0x7C and get the data */ body = sc_asn1_find_tag(card->ctx, rbuf, - r, 0x7C, &body_len); - if(!body) { + r, 0x7C, &body_len); + if (!body || rbuf[0] != 0x7C) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not find outer tag 0x7C in response"); - r = SC_ERROR_INVALID_DATA; + r = SC_ERROR_INVALID_DATA; goto err; } /* SP800-73 not clear if 80 or 82 */ challenge_response = sc_asn1_find_tag(card->ctx, body, - body_len, 0x82, &challenge_response_len); - if(!challenge_response) { + body_len, 0x82, &challenge_response_len); + if (!challenge_response) { challenge_response = sc_asn1_find_tag(card->ctx, body, body_len, 0x80, &challenge_response_len); - if(!challenge_response) { + if (!challenge_response) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not find tag 0x82 or 0x80 in response"); - r = SC_ERROR_INVALID_DATA; + r = SC_ERROR_INVALID_DATA; goto err; } } /* Decrypt challenge and check against nonce */ decrypted_reponse = malloc(challenge_response_len); - if(!decrypted_reponse) { + if (!decrypted_reponse) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "OOM Allocating decryption buffer"); - r = SC_ERROR_INVALID_DATA; + r = SC_ERROR_INVALID_DATA; goto err; } - EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_reset(ctx); if (!EVP_DecryptInit(ctx, cipher, key, NULL)) { + sc_log_openssl(card->ctx); r = SC_ERROR_INTERNAL; goto err; } - EVP_CIPHER_CTX_set_padding(ctx,0); + EVP_CIPHER_CTX_set_padding(ctx, 0); tmp = decrypted_reponse; - if (!EVP_DecryptUpdate(ctx, tmp, &N, challenge_response, challenge_response_len)) { + if (!EVP_DecryptUpdate(ctx, tmp, &N, challenge_response, (int)challenge_response_len)) { + sc_log_openssl(card->ctx); r = SC_ERROR_INTERNAL; goto err; } decrypted_reponse_len = tmplen = N; tmp += tmplen; - if(!EVP_DecryptFinal(ctx, tmp, &N)) { + if (!EVP_DecryptFinal(ctx, tmp, &N)) { + sc_log_openssl(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1820,20 +4109,19 @@ static int piv_general_mutual_authenticate(sc_card_t *card, if (decrypted_reponse_len != nonce_len || memcmp(nonce, decrypted_reponse, nonce_len) != 0) { sc_log(card->ctx, - "mutual authentication failed, card returned wrong value %"SC_FORMAT_LEN_SIZE_T"u:%"SC_FORMAT_LEN_SIZE_T"u", - decrypted_reponse_len, nonce_len); + "mutual authentication failed, card returned wrong value %" SC_FORMAT_LEN_SIZE_T "u:%" SC_FORMAT_LEN_SIZE_T "u", + decrypted_reponse_len, nonce_len); r = SC_ERROR_DECRYPT_FAILED; goto err; } r = SC_SUCCESS; err: + sc_evp_cipher_free(cipher); if (ctx) EVP_CIPHER_CTX_free(ctx); if (locked) sc_unlock(card); - if (rbuf) - free(rbuf); if (decrypted_reponse) free(decrypted_reponse); if (built) @@ -1853,45 +4141,46 @@ static int piv_general_mutual_authenticate(sc_card_t *card, LOG_FUNC_RETURN(card->ctx, r); } - /* Currently only used for card administration */ -static int piv_general_external_authenticate(sc_card_t *card, +static int +piv_general_external_authenticate(sc_card_t *card, unsigned int key_ref, unsigned int alg_id) { int r; #ifdef ENABLE_OPENSSL - int tmplen; + size_t tmplen; int outlen; int locked = 0; u8 *p; - u8 *rbuf = NULL; + u8 rbuf[4096]; u8 *key = NULL; - u8 *cypher_text = NULL; + u8 *cipher_text = NULL; u8 *output_buf = NULL; const u8 *body = NULL; const u8 *challenge_data = NULL; - size_t rbuflen; size_t body_len; size_t output_len; size_t challenge_len; size_t keylen = 0; - size_t cypher_text_len = 0; + size_t cipher_text_len = 0; u8 sbuf[255]; - EVP_CIPHER_CTX * ctx = NULL; - const EVP_CIPHER *cipher; + EVP_CIPHER_CTX *ctx = NULL; + EVP_CIPHER *cipher = NULL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; + sc_log_openssl(card->ctx); + r = SC_ERROR_OUT_OF_MEMORY; + goto err; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Selected cipher for algorithm id: %02x\n", alg_id); - cipher = get_cipher_for_algo(alg_id); - if(!cipher) { + cipher = get_cipher_for_algo(card, alg_id); + if (!cipher) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid cipher selector, none found for: %02x\n", alg_id); r = SC_ERROR_INVALID_ARGUMENTS; goto err; @@ -1899,7 +4188,7 @@ static int piv_general_external_authenticate(sc_card_t *card, r = piv_get_key(card, alg_id, &key, &keylen); if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error geting General Auth key\n"); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error getting General Auth key\n"); goto err; } @@ -1917,7 +4206,7 @@ static int piv_general_external_authenticate(sc_card_t *card, *p++ = 0x00; /* get a challenge */ - r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, &rbuf, &rbuflen); + r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, rbuf, sizeof rbuf); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error getting Challenge\n"); goto err; @@ -1931,67 +4220,70 @@ static int piv_general_external_authenticate(sc_card_t *card, /* Remove the encompassing outer TLV of 0x7C and get the data */ body = sc_asn1_find_tag(card->ctx, rbuf, - r, 0x7C, &body_len); - if (!body) { + r, 0x7C, &body_len); + if (!body || rbuf[0] != 0x7C) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid Challenge Data response of NULL\n"); - r = SC_ERROR_INVALID_DATA; + r = SC_ERROR_INVALID_DATA; goto err; } /* Get the challenge data indicated by the TAG 0x81 */ challenge_data = sc_asn1_find_tag(card->ctx, body, - body_len, 0x81, &challenge_len); + body_len, 0x81, &challenge_len); if (!challenge_data) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid Challenge Data none found in TLV\n"); - r = SC_ERROR_INVALID_DATA; + r = SC_ERROR_INVALID_DATA; goto err; } - /* Store this to sanity check that plaintext length and cyphertext lengths match */ - /* TODO is this required */ + /* Store this to sanity check that plaintext length and ciphertext lengths match */ tmplen = challenge_len; /* Encrypt the challenge with the secret */ if (!EVP_EncryptInit(ctx, cipher, key, NULL)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Encrypt fail\n"); r = SC_ERROR_INTERNAL; goto err; } - cypher_text = malloc(challenge_len); - if (!cypher_text) { + cipher_text = malloc(challenge_len); + if (!cipher_text) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not allocate buffer for cipher text\n"); r = SC_ERROR_INTERNAL; goto err; } - EVP_CIPHER_CTX_set_padding(ctx,0); - if (!EVP_EncryptUpdate(ctx, cypher_text, &outlen, challenge_data, challenge_len)) { + EVP_CIPHER_CTX_set_padding(ctx, 0); + if (!EVP_EncryptUpdate(ctx, cipher_text, &outlen, challenge_data, (int)challenge_len)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Encrypt update fail\n"); r = SC_ERROR_INTERNAL; goto err; } - cypher_text_len += outlen; + cipher_text_len += outlen; - if (!EVP_EncryptFinal(ctx, cypher_text + cypher_text_len, &outlen)) { + if (!EVP_EncryptFinal(ctx, cipher_text + cipher_text_len, &outlen)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Final fail\n"); r = SC_ERROR_INTERNAL; goto err; } - cypher_text_len += outlen; + cipher_text_len += outlen; /* * Actually perform the sanity check on lengths plaintext length vs * encrypted length */ - if (cypher_text_len != (size_t)tmplen) { + if (cipher_text_len != tmplen) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Length test fail\n"); r = SC_ERROR_INTERNAL; goto err; } output_buf = malloc(output_len); - if(!output_buf) { + if (!output_buf) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not allocate output buffer: %s\n", strerror(errno)); r = SC_ERROR_INTERNAL; @@ -2010,29 +4302,38 @@ static int piv_general_external_authenticate(sc_card_t *card, * memcopy the body past the 7C portion * Transmit */ - tmplen = put_tag_and_len(0x82, cypher_text_len, NULL); + tmplen = sc_asn1_put_tag(0x82, NULL, cipher_text_len, NULL, 0, NULL); + if (tmplen <= 0) { + r = SC_ERROR_INTERNAL; + goto err; + } - tmplen = put_tag_and_len(0x7C, tmplen, &p); + r = sc_asn1_put_tag(0x7C, NULL, tmplen, p, output_len, &p); + if (r != SC_SUCCESS) { + goto err; + } /* Build the 0x82 TLV and append to the 7C tag */ - tmplen += put_tag_and_len(0x82, cypher_text_len, &p); - - memcpy(p, cypher_text, cypher_text_len); - p += cypher_text_len; - tmplen += cypher_text_len; + r = sc_asn1_put_tag(0x82, cipher_text, cipher_text_len, p, output_len - (p - output_buf), &p); + if (r != SC_SUCCESS) { + goto err; + } /* Sanity check the lengths again */ - if(output_len != (size_t)tmplen) { + tmplen = sc_asn1_put_tag(0x7C, NULL, tmplen, NULL, 0, NULL); + if (output_len != (size_t)tmplen) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Allocated and computed lengths do not match! " - "Expected %"SC_FORMAT_LEN_SIZE_T"d, found: %d\n", output_len, tmplen); + "Expected %" SC_FORMAT_LEN_SIZE_T "d, found: %zu\n", + output_len, tmplen); r = SC_ERROR_INTERNAL; goto err; } - r = piv_general_io(card, 0x87, alg_id, key_ref, output_buf, output_len, NULL, NULL); + r = piv_general_io(card, 0x87, alg_id, key_ref, output_buf, output_len, NULL, 0); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Got response challenge\n"); err: + sc_evp_cipher_free(cipher); if (ctx) EVP_CIPHER_CTX_free(ctx); @@ -2044,11 +4345,8 @@ static int piv_general_external_authenticate(sc_card_t *card, free(key); } - if (rbuf) - free(rbuf); - - if (cypher_text) - free(cypher_text); + if (cipher_text) + free(cipher_text); if (output_buf) free(output_buf); @@ -2060,36 +4358,34 @@ static int piv_general_external_authenticate(sc_card_t *card, LOG_FUNC_RETURN(card->ctx, r); } - +/* + * with sp800-73-4 and SM GUID is also in sm_cvc.subjectID + */ static int -piv_get_serial_nr_from_CHUI(sc_card_t* card, sc_serial_number_t* serial) +piv_get_serial_nr_from_CHUI(sc_card_t *card, sc_serial_number_t *serial) { int r; int i; - u8 gbits; + u8 gbits = 0; /* 0 not present or wrong length or all zeros */ + u8 fbits = 0; /* 0 not present or wrong length or all zeros */ u8 *rbuf = NULL; const u8 *body; const u8 *fascn; const u8 *guid; size_t rbuflen = 0, bodylen, fascnlen, guidlen; - u8 temp[2000]; - size_t templen = sizeof(temp); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - if (card->serialnr.len) { + if (card->serialnr.len) { *serial = card->serialnr; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } - /* ensure we've got the PIV selected, and nothing else is in process */ - /* This fixes several problems due to previous incomplete APDUs during card detection */ - /* Note: We need the temp because (some?) Oberthur cards don't like selecting an applet without response data */ - /* 800-73-3 part1 draft, and CIO Council docs imply for PIV Compatible card - * The FASC-N Agency code should be 9999 and there should be a GUID - * based on RFC 4122. RIf so and the GUID is not all 0's + /* + * 800-73-3 Part 1 and CIO Council docs say for PIV Compatible cards + * the FASC-N Agency code should be 9999 and there should be a GUID + * based on RFC 4122. If GUID present and not zero * we will use the GUID as the serial number. */ - piv_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, temp, &templen); r = piv_get_cached_data(card, PIV_OBJ_CHUI, &rbuf, &rbuflen); LOG_TEST_RET(card->ctx, r, "Failure retrieving CHUI"); @@ -2097,38 +4393,52 @@ piv_get_serial_nr_from_CHUI(sc_card_t* card, sc_serial_number_t* serial) r = SC_ERROR_INTERNAL; if (rbuflen != 0) { body = sc_asn1_find_tag(card->ctx, rbuf, rbuflen, 0x53, &bodylen); /* Pass the outer wrapper asn1 */ - if (body != NULL && bodylen != 0) { + if (body != NULL && bodylen != 0 && rbuf[0] == 0x53) { fascn = sc_asn1_find_tag(card->ctx, body, bodylen, 0x30, &fascnlen); /* Find the FASC-N data */ + + if (fascn && fascnlen == 25) { + for (i = 0; i < 25; i++) { + fbits = fbits || fascn[i]; /* if all are zero, gbits will be zero */ + } + } + guid = sc_asn1_find_tag(card->ctx, body, bodylen, 0x34, &guidlen); - gbits = 0; /* if guid is valid, gbits will not be zero */ if (guid && guidlen == 16) { for (i = 0; i < 16; i++) { gbits = gbits | guid[i]; /* if all are zero, gbits will be zero */ } } sc_log(card->ctx, - "fascn=%p,fascnlen=%"SC_FORMAT_LEN_SIZE_T"u,guid=%p,guidlen=%"SC_FORMAT_LEN_SIZE_T"u,gbits=%2.2x", - fascn, fascnlen, guid, guidlen, gbits); - - if (fascn && fascnlen == 25) { - /* test if guid and the fascn starts with ;9999 (in ISO 4bit + partiy code) */ - if (!(gbits && fascn[0] == 0xD4 && fascn[1] == 0xE7 - && fascn[2] == 0x39 && (fascn[3] | 0x7F) == 0xFF)) { - serial->len = fascnlen < SC_MAX_SERIALNR ? fascnlen : SC_MAX_SERIALNR; - memcpy (serial->value, fascn, serial->len); + "fascn=%p,fascnlen=%" SC_FORMAT_LEN_SIZE_T "u,guid=%p,guidlen=%" SC_FORMAT_LEN_SIZE_T "u,gbits=%2.2x", + fascn, fascnlen, guid, guidlen, gbits); + + if (fascn && fascnlen == 25 && fbits) { + /* test if guid and the fascn starts with ;9999 (in ISO 4bit + parity code) */ + /* ;9999 is non-gov issued FASC-N, will use FASC-N for gov issued if no guid */ + if (!(gbits && fascn[0] == 0xD4 && fascn[1] == 0xE7 && + fascn[2] == 0x39 && (fascn[3] | 0x7F) == 0xFF)) { + /* fascnlen is 25 */ + serial->len = fascnlen; + memcpy(serial->value, fascn, serial->len); r = SC_SUCCESS; gbits = 0; /* set to skip using guid below */ } } - if (guid && gbits) { - serial->len = guidlen < SC_MAX_SERIALNR ? guidlen : SC_MAX_SERIALNR; - memcpy (serial->value, guid, serial->len); + if (guid && guidlen == 16 && gbits) { + serial->len = guidlen; + memcpy(serial->value, guid, serial->len); r = SC_SUCCESS; } } } + if (fbits == 0 && gbits == 0) { /* were not able to set the serial number */ + serial->len = 16; + memset(serial->value, 0x00, serial->len); + r = SC_ERROR_INTERNAL; + } + card->serialnr = *serial; LOG_FUNC_RETURN(card->ctx, r); } @@ -2144,9 +4454,10 @@ piv_get_serial_nr_from_CHUI(sc_card_t* card, sc_serial_number_t* serial) * pkcs15-piv.c calls this via cardctl. */ -static int piv_is_object_present(sc_card_t *card, u8 *ptr) +static int +piv_is_object_present(sc_card_t *card, u8 *ptr) { - piv_private_data_t * priv = PIV_DATA(card); + piv_private_data_t *priv = PIV_DATA(card); int r = 0; int enumtag; @@ -2162,124 +4473,128 @@ static int piv_is_object_present(sc_card_t *card, u8 *ptr) * or the global pin for the card 0x00. Look at Discovery object to get this. * called by pkcs15-piv.c via cardctl when setting up the pins. */ -static int piv_get_pin_preference(sc_card_t *card, int *ptr) +static int +piv_get_pin_preference(sc_card_t *card, int *pin_ref) { - piv_private_data_t * priv = PIV_DATA(card); + piv_private_data_t *priv = PIV_DATA(card); - *ptr = priv->pin_preference; + *pin_ref = priv->pin_preference; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } -static int piv_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) +static int +piv_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { - piv_private_data_t * priv = PIV_DATA(card); - u8 * opts; /* A or M, key_ref, alg_id */ + piv_private_data_t *priv = PIV_DATA(card); + u8 *opts; /* A or M, key_ref, alg_id */ LOG_FUNC_CALLED(card->ctx); - sc_log(card->ctx, "cmd=%ld ptr=%p", cmd, ptr); if (priv == NULL) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } - switch(cmd) { - case SC_CARDCTL_PIV_AUTHENTICATE: - opts = (u8 *)ptr; - switch (*opts) { - case 'A': - return piv_general_external_authenticate(card, - *(opts+1), *(opts+2)); - break; - case'M': - return piv_general_mutual_authenticate(card, - *(opts+1), *(opts+2)); - break; - } + switch (cmd) { + case SC_CARDCTL_PIV_AUTHENTICATE: + opts = (u8 *)ptr; + switch (*opts) { + case 'A': + return piv_general_external_authenticate(card, + *(opts + 1), *(opts + 2)); break; - case SC_CARDCTL_PIV_GENERATE_KEY: - return piv_generate_key(card, - (sc_cardctl_piv_genkey_info_t *) ptr); - break; - case SC_CARDCTL_GET_SERIALNR: - return piv_get_serial_nr_from_CHUI(card, (sc_serial_number_t *) ptr); - break; - case SC_CARDCTL_PIV_PIN_PREFERENCE: - return piv_get_pin_preference(card, ptr); - break; - case SC_CARDCTL_PIV_OBJECT_PRESENT: - return piv_is_object_present(card, ptr); + case 'M': + return piv_general_mutual_authenticate(card, + *(opts + 1), *(opts + 2)); break; + } + break; + case SC_CARDCTL_PIV_GENERATE_KEY: + return piv_generate_key(card, + (sc_cardctl_piv_genkey_info_t *)ptr); + break; + case SC_CARDCTL_GET_SERIALNR: + return piv_get_serial_nr_from_CHUI(card, (sc_serial_number_t *)ptr); + break; + case SC_CARDCTL_PIV_PIN_PREFERENCE: + return piv_get_pin_preference(card, ptr); + break; + case SC_CARDCTL_PIV_OBJECT_PRESENT: + return piv_is_object_present(card, ptr); + break; } LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } -static int piv_get_challenge(sc_card_t *card, u8 *rnd, size_t len) +static int +piv_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { - u8 sbuf[16]; - u8 *rbuf = NULL; - size_t rbuflen = 0; - u8 *p, *q; + /* Dynamic Authentication Template (Challenge) */ + u8 sbuf[] = {0x7c, 0x02, 0x81, 0x00}; + u8 rbuf[4096]; + const u8 *p; + size_t out_len = 0; int r; + unsigned int tag_out = 0, cla_out = 0; + piv_private_data_t *priv = PIV_DATA(card); - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + LOG_FUNC_CALLED(card->ctx); - sc_log(card->ctx, "challenge len=%"SC_FORMAT_LEN_SIZE_T"u", len); + if (priv->card_issues & CI_NO_RANDOM) { + r = SC_ERROR_NOT_SUPPORTED; + LOG_TEST_GOTO_ERR(card->ctx, r, "No support for random data"); + } - r = sc_lock(card); - if (r != SC_SUCCESS) - LOG_FUNC_RETURN(card->ctx, r); + /* NIST 800-73-3 says use 9B, previous versions used 00 */ + r = piv_general_io(card, 0x87, 0x00, 0x9B, sbuf, sizeof sbuf, rbuf, sizeof rbuf); + /* + * piv_get_challenge is called in a loop. + * some cards may allow 1 challenge expecting it to be part of + * NIST 800-73-3 part 2 "Authentication of PIV Card Application Administrator" + * and return "6A 80" if last command was a get_challenge. + * Now that the card returned error, we can try one more time. + */ + if (r == SC_ERROR_INCORRECT_PARAMETERS) { + r = piv_general_io(card, 0x87, 0x00, 0x9B, sbuf, sizeof sbuf, rbuf, sizeof rbuf); + if (r == SC_ERROR_INCORRECT_PARAMETERS) { + r = SC_ERROR_NOT_SUPPORTED; + } + } + LOG_TEST_GOTO_ERR(card->ctx, r, "GENERAL AUTHENTICATE failed"); - p = sbuf; - *p++ = 0x7c; - *p++ = 0x02; - *p++ = 0x81; - *p++ = 0x00; + p = rbuf; + r = sc_asn1_read_tag(&p, r, &cla_out, &tag_out, &out_len); + if (r < 0 || (cla_out | tag_out) != 0x7C) { + LOG_TEST_GOTO_ERR(card->ctx, SC_ERROR_INVALID_DATA, "Can't find Dynamic Authentication Template"); + } - /* assuming 8 byte response ? */ - /* should take what the card returns */ - while (len > 0) { - size_t n = len > 8 ? 8 : len; + r = sc_asn1_read_tag(&p, out_len, &cla_out, &tag_out, &out_len); + if (r < 0 || (cla_out | tag_out) != 0x81) { + LOG_TEST_GOTO_ERR(card->ctx, SC_ERROR_INVALID_DATA, "Can't find Challenge"); + } - /* NIST 800-73-3 says use 9B, previous verisons used 00 */ - r = piv_general_io(card, 0x87, 0x00, 0x9B, sbuf, p - sbuf, &rbuf, &rbuflen); - if (r < 0) { - sc_unlock(card); - LOG_FUNC_RETURN(card->ctx, r); - } - q = rbuf; - if ( (*q++ != 0x7C) - || (*q++ != rbuflen - 2) - || (*q++ != 0x81) - || (*q++ != rbuflen - 4)) { - r = SC_ERROR_INVALID_DATA; - sc_unlock(card); - LOG_FUNC_RETURN(card->ctx, r); - } - memcpy(rnd, q, n); - len -= n; - rnd += n; - free(rbuf); - rbuf = NULL; + if (len < out_len) { + out_len = len; } + memcpy(rnd, p, out_len); - r = sc_unlock(card); + r = (int)out_len; +err: LOG_FUNC_RETURN(card->ctx, r); - } static int piv_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { - piv_private_data_t * priv = PIV_DATA(card); + piv_private_data_t *priv = PIV_DATA(card); int r = 0; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_log(card->ctx, - "flags=%08lx op=%d alg=%d algf=%08x algr=%08x kr0=%02x, krfl=%"SC_FORMAT_LEN_SIZE_T"u", - env->flags, env->operation, env->algorithm, env->algorithm_flags, - env->algorithm_ref, env->key_ref[0], env->key_ref_len); + "flags=%08lx op=%d alg=%lu algf=%08lx algr=%08lx kr0=%02x, krfl=%" SC_FORMAT_LEN_SIZE_T "u", + env->flags, env->operation, env->algorithm, env->algorithm_flags, + env->algorithm_ref, env->key_ref[0], env->key_ref_len); priv->operation = env->operation; priv->algorithm = env->algorithm; @@ -2289,179 +4604,181 @@ piv_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) } else if (env->algorithm == SC_ALGORITHM_EC) { if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { switch (env->algorithm_ref) { - case 256: - priv->alg_id = 0x11; /* Say it is EC 256 */ - priv->key_size = 256; - break; - case 384: - priv->alg_id = 0x14; - priv->key_size = 384; - break; - default: - r = SC_ERROR_NO_CARD_SUPPORT; + case 256: + priv->alg_id = 0x11; /* Say it is EC 256 */ + priv->key_size = 256; + break; + case 384: + priv->alg_id = 0x14; + priv->key_size = 384; + break; + default: + r = SC_ERROR_NO_CARD_SUPPORT; } } else r = SC_ERROR_NO_CARD_SUPPORT; + } else if (env->algorithm == SC_ALGORITHM_EDDSA) { + priv->alg_id = 0xE0; + priv->key_size = 255; + } else if (env->algorithm == SC_ALGORITHM_XEDDSA) { + priv->alg_id = 0xE1; + priv->key_size = 255; } else - r = SC_ERROR_NO_CARD_SUPPORT; + r = SC_ERROR_NO_CARD_SUPPORT; priv->key_ref = env->key_ref[0]; LOG_FUNC_RETURN(card->ctx, r); } - -static int piv_restore_security_env(sc_card_t *card, int se_num) +static int +piv_restore_security_env(sc_card_t *card, int se_num) { LOG_FUNC_CALLED(card->ctx); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } - -static int piv_validate_general_authentication(sc_card_t *card, - const u8 * data, size_t datalen, - u8 * out, size_t outlen) +static int +piv_validate_general_authentication(sc_card_t *card, + const u8 *data, size_t datalen, + u8 *out, size_t outlen) { - piv_private_data_t * priv = PIV_DATA(card); - int r; + piv_private_data_t *priv = PIV_DATA(card); + int r, tmplen, tmplen2; u8 *p; - const u8 *tag; + const unsigned char *p2; size_t taglen; - const u8 *body; size_t bodylen; - unsigned int real_alg_id; + unsigned int cla, tag; + unsigned int real_alg_id, op_tag; - u8 sbuf[4096]; /* needs work. for 3072 keys, needs 384+10 or so */ - u8 *rbuf = NULL; - size_t rbuflen; + u8 sbuf[4096]; /* needs work. for 4096 needs 512+10 or so */ + size_t sbuflen = sizeof(sbuf); + u8 rbuf[4096]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* should assume large send data */ p = sbuf; - put_tag_and_len(0x7c, (2 + put_tag_and_len(0, datalen, NULL)) , &p); - put_tag_and_len(0x82, 0, &p); - if (priv->operation == SC_SEC_OPERATION_DERIVE - && priv->algorithm == SC_ALGORITHM_EC) - put_tag_and_len(0x85, datalen, &p); - else - put_tag_and_len(0x81, datalen, &p); - - memcpy(p, data, datalen); - p += datalen; + tmplen = sc_asn1_put_tag(0xff, NULL, datalen, NULL, 0, NULL); + tmplen2 = sc_asn1_put_tag(0x82, NULL, 0, NULL, 0, NULL); + if (tmplen <= 0 || tmplen2 <= 0) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + tmplen += tmplen2; + if ((r = sc_asn1_put_tag(0x7c, NULL, tmplen, p, sbuflen, &p)) != SC_SUCCESS || + (r = sc_asn1_put_tag(0x82, NULL, 0, p, sbuflen - (p - sbuf), &p)) != SC_SUCCESS) { + LOG_FUNC_RETURN(card->ctx, r); + } + if (priv->operation == SC_SEC_OPERATION_DERIVE && (priv->algorithm == SC_ALGORITHM_EC || priv->algorithm == SC_ALGORITHM_XEDDSA)) { + op_tag = 0x85; + } else { + op_tag = 0x81; + } + r = sc_asn1_put_tag(op_tag, data, datalen, p, sbuflen - (p - sbuf), &p); + if (r != SC_SUCCESS) { + LOG_FUNC_RETURN(card->ctx, r); + } /* * alg_id=06 is a place holder for all RSA keys. * Derive the real alg_id based on the size of the * the data, as we are always using raw mode. - * Non RSA keys needs some work in thia area. + * Non RSA keys needs some work in this area. */ real_alg_id = priv->alg_id; if (priv->alg_id == 0x06) { - switch (datalen) { - case 128: real_alg_id = 0x06; break; - case 256: real_alg_id = 0x07; break; - case 384: real_alg_id = 0x05; break; - default: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NO_CARD_SUPPORT); + switch (datalen) { + case 128: + real_alg_id = 0x06; + break; + case 256: + real_alg_id = 0x07; + break; + case 384: + real_alg_id = 0x05; + break; + case 512: + real_alg_id = 0x16; + break; + default: + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NO_CARD_SUPPORT); } } - /* EC alg_id was already set */ + /* EC and ED alg_id was already set */ r = piv_general_io(card, 0x87, real_alg_id, priv->key_ref, - sbuf, p - sbuf, &rbuf, &rbuflen); + sbuf, p - sbuf, rbuf, sizeof rbuf); + if (r < 0) + goto err; - if ( r >= 0) { - body = sc_asn1_find_tag(card->ctx, rbuf, rbuflen, 0x7c, &bodylen); + p2 = rbuf; + r = sc_asn1_read_tag(&p2, r, &cla, &tag, &bodylen); + if (p2 == NULL || r < 0 || bodylen == 0 || (cla | tag) != 0x7C) { + LOG_TEST_GOTO_ERR(card->ctx, SC_ERROR_INVALID_DATA, "Can't find 0x7C"); + } - if (body) { - tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x82, &taglen); - if (tag) { - memcpy(out, tag, taglen); - r = taglen; - } - } else - r = SC_ERROR_INVALID_DATA; + r = sc_asn1_read_tag(&p2, bodylen, &cla, &tag, &taglen); + if (p2 == NULL || r < 0 || taglen == 0 || (cla | tag) != 0x82) { + LOG_TEST_GOTO_ERR(card->ctx, SC_ERROR_INVALID_DATA, "Can't find 0x82"); } - if (rbuf) - free(rbuf); + if (taglen > outlen) { + LOG_TEST_GOTO_ERR(card->ctx, SC_ERROR_INVALID_DATA, "data read longer then buffer"); + } + + memcpy(out, p2, taglen); + r = (int)taglen; +err: LOG_FUNC_RETURN(card->ctx, r); } - static int -piv_compute_signature(sc_card_t *card, const u8 * data, size_t datalen, - u8 * out, size_t outlen) +piv_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, + u8 *out, size_t outlen) { - piv_private_data_t * priv = PIV_DATA(card); + piv_private_data_t *priv = PIV_DATA(card); int r; - int i; size_t nLen; u8 rbuf[128]; /* For EC conversions 384 will fit */ - size_t rbuflen = sizeof(rbuf); - const u8 * body; - size_t bodylen; - const u8 * tag; - size_t taglen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - /* The PIV returns a DER SEQUENCE{INTEGER, INTEGER} - * Which may have leading 00 to force positive - * TODO: -DEE should check if PKCS15 want the same - * But PKCS11 just wants 2* filed_length in bytes + * Which may have leading 00 to force a positive integer + * But PKCS11 just wants 2* field_length in bytes * So we have to strip out the integers - * if present and pad on left if too short. + * and pad on left if too short. */ - if (priv->alg_id == 0x11 || priv->alg_id == 0x14 ) { - nLen = (priv->key_size + 7) / 8; - if (outlen < 2*nLen) { + if (priv->alg_id == 0x11 || priv->alg_id == 0x14) { + nLen = BYTES4BITS(priv->key_size); + if (outlen < 2 * nLen) { sc_log(card->ctx, - " output too small for EC signature %"SC_FORMAT_LEN_SIZE_T"u < %"SC_FORMAT_LEN_SIZE_T"u", - outlen, 2 * nLen); + " output too small for EC signature %" SC_FORMAT_LEN_SIZE_T "u < %" SC_FORMAT_LEN_SIZE_T "u", + outlen, 2 * nLen); r = SC_ERROR_INVALID_DATA; goto err; } - memset(out, 0, outlen); - r = piv_validate_general_authentication(card, data, datalen, rbuf, rbuflen); + r = piv_validate_general_authentication(card, data, datalen, rbuf, sizeof rbuf); if (r < 0) goto err; - if ( r >= 0) { - body = sc_asn1_find_tag(card->ctx, rbuf, rbuflen, 0x30, &bodylen); - - for (i = 0; i<2; i++) { - if (body) { - tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x02, &taglen); - if (tag) { - bodylen -= taglen - (tag - body); - body = tag + taglen; - - if (taglen > nLen) { /* drop leading 00 if present */ - if (*tag != 0x00) { - r = SC_ERROR_INVALID_DATA; - goto err; - } - tag++; - taglen--; - } - memcpy(out + nLen*i + nLen - taglen , tag, taglen); - } else { - r = SC_ERROR_INVALID_DATA; - goto err; - } - } else { - r = SC_ERROR_INVALID_DATA; - goto err; - } - } - r = 2 * nLen; + r = sc_asn1_decode_ecdsa_signature(card->ctx, rbuf, r, nLen, &out, outlen); + /* Yubikey 5.7.x supports ED25519 */ + } else if (priv->alg_id == 0xE0) { + nLen = BYTES4BITS(priv->key_size); + if (outlen < nLen) { + sc_log(card->ctx, + " output too small for ED signature %" SC_FORMAT_LEN_SIZE_T "u < %" SC_FORMAT_LEN_SIZE_T "u", + outlen, nLen); + r = SC_ERROR_INVALID_DATA; + goto err; } + r = piv_validate_general_authentication(card, data, datalen, out, outlen); + } else { /* RSA is all set */ r = piv_validate_general_authentication(card, data, datalen, out, outlen); } @@ -2470,9 +4787,8 @@ piv_compute_signature(sc_card_t *card, const u8 * data, size_t datalen, SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } - static int -piv_decipher(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) +piv_decipher(sc_card_t *card, const u8 *data, size_t datalen, u8 *out, size_t outlen) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); @@ -2489,16 +4805,17 @@ piv_decipher(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t * All other requests should be to read. * Only if file_out != null, will we read to get length. */ -static int piv_select_file(sc_card_t *card, const sc_path_t *in_path, - sc_file_t **file_out) +static int +piv_select_file(sc_card_t *card, const sc_path_t *in_path, + sc_file_t **file_out) { - piv_private_data_t * priv = PIV_DATA(card); + piv_private_data_t *priv = PIV_DATA(card); int r; int i; const u8 *path; - int pathlen; + size_t pathlen; sc_file_t *file = NULL; - u8 * rbuf = NULL; + u8 *rbuf = NULL; size_t rbuflen = 0; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); @@ -2508,7 +4825,7 @@ static int piv_select_file(sc_card_t *card, const sc_path_t *in_path, /* only support single EF in current application */ /* - * PIV emulates files, and only does so becauses sc_pkcs15_* uses + * PIV emulates files, and only does so because sc_pkcs15_* uses * select_file and read_binary. The emulation adds path emulated structures * so piv_select_file will find it. * there is no dir. Only direct access to emulated files @@ -2560,7 +4877,7 @@ static int piv_select_file(sc_card_t *card, const sc_path_t *in_path, file->path = *in_path; /* this could be like the FCI */ - file->type = SC_FILE_TYPE_DF; + file->type = SC_FILE_TYPE_DF; file->shareable = 0; file->ef_structure = 0; if (priv->return_only_cert) @@ -2568,148 +4885,286 @@ static int piv_select_file(sc_card_t *card, const sc_path_t *in_path, else file->size = priv->obj_cache[i].obj_len; - file->id = (piv_objects[i].containerid[0]<<8) + piv_objects[i].containerid[1]; + file->id = (piv_objects[i].containerid[0] << 8) + piv_objects[i].containerid[1]; *file_out = file; } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); - } -static int piv_process_discovery(sc_card_t *card) +static int +piv_parse_discovery(sc_card_t *card, u8 *rbuf, size_t rbuflen, int aid_only) { - piv_private_data_t * priv = PIV_DATA(card); - int r; - u8 * rbuf = NULL; - size_t rbuflen = 0; - const u8 * body; + piv_private_data_t *priv = PIV_DATA(card); + int r = 0; + const u8 *body; size_t bodylen; - const u8 * aid; + const u8 *aid; size_t aidlen; - const u8 * pinp; + const u8 *pinp; size_t pinplen; unsigned int cla_out, tag_out; - - r = piv_get_cached_data(card, PIV_OBJ_DISCOVERY, &rbuf, &rbuflen); - if (r <= 0) { - priv->obj_cache[PIV_OBJ_DISCOVERY].flags |= PIV_OBJ_CACHE_NOT_PRESENT; - /* Discovery object is only object that has 3 byte Lc= 50017E - * and pree 800-73-3 cards may treat this as a strange error. - * So treat any error as not present - */ - r = 0; - goto err; - } - - sc_log(card->ctx, "Discovery = %p:%"SC_FORMAT_LEN_SIZE_T"u", rbuf, - rbuflen); - /* the object is now cached, see what we have */ if (rbuflen != 0) { body = rbuf; - if ((r = sc_asn1_read_tag(&body, rbuflen, &cla_out, &tag_out, &bodylen)) != SC_SUCCESS) { - sc_log(card->ctx, "DER problem %d",r); + if ((r = sc_asn1_read_tag(&body, rbuflen, &cla_out, &tag_out, &bodylen)) != SC_SUCCESS || + body == NULL || + bodylen == 0 || + ((cla_out | tag_out) != 0x7E)) { + sc_log(card->ctx, "DER problem %d", r); r = SC_ERROR_INVALID_ASN1_OBJECT; goto err; } - sc_log(card->ctx, - "Discovery 0x%2.2x 0x%2.2x %p:%"SC_FORMAT_LEN_SIZE_T"u", - cla_out, tag_out, body, bodylen); - if ( cla_out+tag_out == 0x7E && body != NULL && bodylen != 0) { + sc_log(card->ctx, + "Discovery 0x%2.2x 0x%2.2x %p:%" SC_FORMAT_LEN_SIZE_T "u", + cla_out, tag_out, body, bodylen); aidlen = 0; aid = sc_asn1_find_tag(card->ctx, body, bodylen, 0x4F, &aidlen); - sc_log(card->ctx, "Discovery aid=%p:%"SC_FORMAT_LEN_SIZE_T"u", - aid, aidlen); - if (aid == NULL || aidlen < piv_aids[0].len_short || - memcmp(aid,piv_aids[0].value,piv_aids[0].len_short) != 0) { /*TODO look at long */ - sc_log(card->ctx, "Discovery object not PIV"); - r = SC_SUCCESS; /* not an error could be some other appl */ - goto err; - } + if (aid == NULL || aidlen < piv_aids[0].len_short || + memcmp(aid, piv_aids[0].value, piv_aids[0].len_short) != 0) { + sc_log(card->ctx, "Discovery object not PIV"); + r = SC_ERROR_INVALID_CARD; /* This is an error */ + goto err; + } + if (aid_only == 0) { pinp = sc_asn1_find_tag(card->ctx, body, bodylen, 0x5F2F, &pinplen); - sc_log(card->ctx, - "Discovery pinp=%p:%"SC_FORMAT_LEN_SIZE_T"u", - pinp, pinplen); if (pinp && pinplen == 2) { - sc_log(card->ctx, "Discovery pinp flags=0x%2.2x 0x%2.2x",*pinp, *(pinp+1)); - r = SC_SUCCESS; - if (*pinp == 0x60 && *(pinp+1) == 0x20) { /* use Global pin */ + priv->init_flags |= PIV_INIT_DISCOVERY_PP; + priv->pin_policy = (*pinp << 8) + *(pinp + 1); + sc_log(card->ctx, "Discovery pinp flags=0x%2.2x 0x%2.2x", *pinp, *(pinp + 1)); + if ((priv->pin_policy & (PIV_PP_PIN | PIV_PP_GLOBAL)) == (PIV_PP_PIN | PIV_PP_GLOBAL) && + priv->pin_policy & PIV_PP_GLOBAL_PRIMARY) { sc_log(card->ctx, "Pin Preference - Global"); priv->pin_preference = 0x00; } } } + r = SC_SUCCESS; + priv->init_flags |= PIV_INIT_DISCOVERY_PARSED; + } + +err: + LOG_FUNC_RETURN(card->ctx, r); +} + +/* normal way to get the discovery object via cache */ +static int +piv_process_discovery(sc_card_t *card) +{ + int r; + u8 *rbuf = NULL; + size_t rbuflen = 0; + + r = piv_get_cached_data(card, PIV_OBJ_DISCOVERY, &rbuf, &rbuflen); + /* Note rbuf and rbuflen are now pointers into cache */ + if (r < 0) + goto err; + + /* the object is now cached, see what we have */ + r = piv_parse_discovery(card, rbuf, rbuflen, 0); + +err: + LOG_FUNC_RETURN(card->ctx, r); +} + +/* + * parse a CCC to test if this is a Dual CAC/PIV + * We read the CCC using the PIV API. + * Look for CAC RID=A0 00 00 00 79 + */ +static int +piv_parse_ccc(sc_card_t *card, u8 *rbuf, size_t rbuflen) +{ + int r = 0; + const u8 *body; + size_t bodylen; + unsigned int cla_out, tag_out; + + u8 tag; + const u8 *end; + size_t len; + + piv_private_data_t *priv = PIV_DATA(card); + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (rbuf == NULL || rbuflen == 0) { + r = SC_ERROR_WRONG_LENGTH; + goto err; + } + + /* Outer layer is a DER tlv */ + body = rbuf; + if ((r = sc_asn1_read_tag(&body, rbuflen, &cla_out, &tag_out, &bodylen)) != SC_SUCCESS || + body == NULL || + bodylen == 0 || + ((cla_out << 24 | tag_out) != piv_objects[PIV_OBJ_CCC].resp_tag)) { + sc_log(card->ctx, "DER problem %d", r); + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto err; + } + + priv->ccc_flags |= PIV_CCC_FOUND; + + /* CCC entries are simple tlv */ + end = body + bodylen; + for (; (body < end); body += len) { + r = sc_simpletlv_read_tag(&body, end - body, &tag, &len); + if (r < 0) + goto err; + switch (tag) { + case PIV_CCC_TAG_F0: + if (len == 0x15) { + if (memcmp(body, "\xA0\x00\x00\x03\08", 5) == 0) + priv->ccc_flags |= PIV_CCC_F0_PIV; + else if (memcmp(body, "\xA0\x00\x00\x00\x79", 5) == 0) + priv->ccc_flags |= PIV_CCC_F0_CAC; + if (*(body + 6) == 0x02) + priv->ccc_flags |= PIV_CCC_F0_JAVA; + } + break; + case PIV_CCC_TAG_F3: + if (len == 0x10) { + if (memcmp(body, "\xA0\x00\x00\x00\x79\x04", 6) == 0) + priv->ccc_flags |= PIV_CCC_F3_CAC_PKI; + } + break; + } } err: LOG_FUNC_RETURN(card->ctx, r); } +static int +piv_process_ccc(sc_card_t *card) +{ + int r = 0; + u8 *rbuf = NULL; + size_t rbuflen = 0; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + r = piv_get_cached_data(card, PIV_OBJ_CCC, &rbuf, &rbuflen); + + if (r < 0) + goto err; + + /* the object is now cached, see what we have */ + r = piv_parse_ccc(card, rbuf, rbuflen); +err: + LOG_FUNC_RETURN(card->ctx, r); +} + +static int +piv_find_discovery(sc_card_t *card) +{ + int r = 0; + size_t rbuflen; + u8 *rbuf = NULL; + piv_private_data_t *priv = PIV_DATA(card); + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* + * During piv_card_reader_lock_obtained, + * we use the discovery object to test if card present, and + * if PIV AID is active. + */ + if (priv->obj_cache[PIV_OBJ_DISCOVERY].flags & PIV_OBJ_CACHE_NOT_PRESENT) { + r = SC_ERROR_DATA_OBJECT_NOT_FOUND; + goto end; + } + + /* If not valid: read, test, cache */ + if (!(priv->obj_cache[PIV_OBJ_DISCOVERY].flags & PIV_OBJ_CACHE_VALID)) { + r = piv_process_discovery(card); + } else { + /* if already in cache,force read */ + rbuflen = 1; + r = piv_get_data(card, PIV_OBJ_DISCOVERY, &rbuf, &rbuflen); + /* if same response as last, no need to parse */ + if (r == 0 && priv->obj_cache[PIV_OBJ_DISCOVERY].obj_len == 0) + goto end; + + if (r >= 0 && priv->obj_cache[PIV_OBJ_DISCOVERY].obj_len == rbuflen && + priv->obj_cache[PIV_OBJ_DISCOVERY].obj_data && + !memcmp(rbuf, priv->obj_cache[PIV_OBJ_DISCOVERY].obj_data, rbuflen)) { + goto end; + } + /* This should not happen bad card */ + sc_log(card->ctx, "Discovery not the same as previously read object"); + r = SC_ERROR_CORRUPTED_DATA; + goto end; + } + +end: + free(rbuf); + LOG_FUNC_RETURN(card->ctx, r); +} + /* * The history object lists what retired keys and certs are on the card - * or listed in the offCardCertURL. The user may have read the offCardURL file, - * ahead of time, and if so will use it for the certs listed. - * TODO: -DEE - * If the offCardCertURL is not cached by the user, should we wget it here? - * Its may be out of scope to have OpenSC read the URL. + * or listed in the offCardCertURL. The user will need to download the + * the offCardURL file. + * If the offCardCertURL is not cached by the user it will not be used. */ static int piv_process_history(sc_card_t *card) { - piv_private_data_t * priv = PIV_DATA(card); + piv_private_data_t *priv = PIV_DATA(card); int r; - int i; + int i, tmplen, tmplen2, tmplen3; int enumtag; - u8 * rbuf = NULL; + u8 *rbuf = NULL; size_t rbuflen = 0; - const u8 * body; + const u8 *body; size_t bodylen; - const u8 * num; + const u8 *num; size_t numlen; - const u8 * url = NULL; + const u8 *url = NULL; size_t urllen; - u8 * ocfhfbuf = NULL; + u8 *ocfhfbuf = NULL; unsigned int cla_out, tag_out; size_t ocfhflen; - const u8 * seq; - const u8 * seqtag; + const u8 *seq; + const u8 *seqtag; size_t seqlen; - const u8 * keyref; + const u8 *keyref; size_t keyreflen; - const u8 * cert; + const u8 *cert; size_t certlen; size_t certobjlen, i2; - u8 * certobj; - u8 * cp; - + u8 *certobj; + u8 *cp; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); r = piv_get_cached_data(card, PIV_OBJ_HISTORY, &rbuf, &rbuflen); if (r == SC_ERROR_FILE_NOT_FOUND) - r = 0; /* OK if not found */ + r = 0; /* OK if not found */ if (r <= 0) { priv->obj_cache[PIV_OBJ_HISTORY].flags |= PIV_OBJ_CACHE_NOT_PRESENT; - goto err; /* no file, must be pre 800-73-3 card and not on card */ + goto err; /* no file, must be pre 800-73-3 card and not on card */ } /* the object is now cached, see what we have */ if (rbuflen != 0) { body = rbuf; - if ((r = sc_asn1_read_tag(&body, rbuflen, &cla_out, &tag_out, &bodylen)) != SC_SUCCESS) { - sc_log(card->ctx, "DER problem %d",r); + if ((r = sc_asn1_read_tag(&body, rbuflen, &cla_out, &tag_out, &bodylen)) != SC_SUCCESS || + ((cla_out << 24 | tag_out) != piv_objects[PIV_OBJ_HISTORY].resp_tag)) { + sc_log(card->ctx, "DER problem %d", r); r = SC_ERROR_INVALID_ASN1_OBJECT; goto err; } - if ( cla_out+tag_out == 0x53 && body != NULL && bodylen != 0) { + if (body != NULL && bodylen != 0) { numlen = 0; num = sc_asn1_find_tag(card->ctx, body, bodylen, 0xC1, &numlen); if (num) { - if (numlen != 1 || *num > PIV_OBJ_RETIRED_X509_20-PIV_OBJ_RETIRED_X509_1+1) { - r = SC_ERROR_INTERNAL; /* TODO some other error */ + if (numlen != 1 || *num > PIV_OBJ_RETIRED_X509_20 - PIV_OBJ_RETIRED_X509_1 + 1) { + r = SC_ERROR_INVALID_ASN1_OBJECT; goto err; } @@ -2719,8 +5174,8 @@ piv_process_history(sc_card_t *card) numlen = 0; num = sc_asn1_find_tag(card->ctx, body, bodylen, 0xC2, &numlen); if (num) { - if (numlen != 1 || *num > PIV_OBJ_RETIRED_X509_20-PIV_OBJ_RETIRED_X509_1+1) { - r = SC_ERROR_INTERNAL; /* TODO some other error */ + if (numlen != 1 || *num > PIV_OBJ_RETIRED_X509_20 - PIV_OBJ_RETIRED_X509_1 + 1) { + r = SC_ERROR_INVALID_ASN1_OBJECT; goto err; } @@ -2729,24 +5184,28 @@ piv_process_history(sc_card_t *card) url = sc_asn1_find_tag(card->ctx, body, bodylen, 0xF3, &urllen); if (url) { - priv->offCardCertURL = calloc(1,urllen+1); + if (urllen > 118) { + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto err; + } + priv->offCardCertURL = calloc(1, urllen + 1); if (priv->offCardCertURL == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(priv->offCardCertURL, url, urllen); } - } - else { - sc_log(card->ctx, "Problem with Histroy object\n"); + } else { + sc_log(card->ctx, "Problem with History object\n"); + r = SC_SUCCESS; /* OK if not found */ goto err; } } sc_log(card->ctx, "History on=%d off=%d URL=%s", priv->keysWithOnCardCerts, priv->keysWithOffCardCerts, - priv->offCardCertURL ? priv->offCardCertURL:"NONE"); + priv->offCardCertURL ? priv->offCardCertURL : "NONE"); /* now mark what objects are on the card */ - for (i=0; ikeysWithOnCardCerts; i++) - priv->obj_cache[PIV_OBJ_RETIRED_X509_1+i].flags &= ~PIV_OBJ_CACHE_NOT_PRESENT; + for (i = 0; i < priv->keysWithOnCardCerts; i++) + priv->obj_cache[PIV_OBJ_RETIRED_X509_1 + i].flags &= ~PIV_OBJ_CACHE_NOT_PRESENT; /* * If user has gotten copy of the file from the offCardCertsURL, @@ -2754,13 +5213,14 @@ piv_process_history(sc_card_t *card) * the card. some of the certs may be on the card as well. * * Get file name from url. verify that the filename is valid - * The URL ends in a SHA1 string. We will use this as the filename + * The URL ends in a SHA-256 string. We will use this as the filename * in the directory used for the PKCS15 cache + * "http://" "/" */ r = 0; if (priv->offCardCertURL) { - char * fp; + char *fp; char filename[PATH_MAX]; if (strncmp("http://", priv->offCardCertURL, 7)) { @@ -2768,26 +5228,36 @@ piv_process_history(sc_card_t *card) goto err; } /* find the last / so we have the filename part */ - fp = strrchr(priv->offCardCertURL + 7,'/'); + fp = strrchr(priv->offCardCertURL + 7, '/'); if (fp == NULL) { r = SC_ERROR_INVALID_DATA; goto err; } fp++; + if (strlen(fp) != 64) { /* ASCII-HEX encoded SHA-256 */ + r = SC_ERROR_INVALID_DATA; + goto err; + } + for (i = 0; i < 64; i++) { + if (isxdigit((unsigned char)fp[i]) == 0) { + r = SC_ERROR_INVALID_DATA; + goto err; + } + } /* Use the same directory as used for other OpenSC cached items */ r = sc_get_cache_dir(card->ctx, filename, sizeof(filename) - strlen(fp) - 2); if (r != SC_SUCCESS) goto err; #ifdef _WIN32 - strcat(filename,"\\"); + strlcat(filename, "\\", PATH_MAX); #else - strcat(filename,"/"); + strlcat(filename, "/", PATH_MAX); #endif - strcat(filename,fp); + strlcat(filename, fp, PATH_MAX); r = piv_read_obj_from_file(card, filename, - &ocfhfbuf, &ocfhflen); + &ocfhfbuf, &ocfhflen); if (r == SC_ERROR_FILE_NOT_FOUND) { r = 0; goto err; @@ -2798,9 +5268,10 @@ piv_process_history(sc_card_t *card) */ body = ocfhfbuf; - if (sc_asn1_read_tag(&body, ocfhflen, &cla_out, - &tag_out, &bodylen) != SC_SUCCESS || - cla_out+tag_out != 0x30) { + if (sc_asn1_read_tag(&body, ocfhflen, &cla_out, &tag_out, &bodylen) != SC_SUCCESS || + body == NULL || + bodylen == 0 || + (cla_out | tag_out) != 0x30) { sc_log(card->ctx, "DER problem"); r = SC_ERROR_INVALID_ASN1_OBJECT; goto err; @@ -2808,16 +5279,17 @@ piv_process_history(sc_card_t *card) seq = body; while (bodylen > 0) { seqtag = seq; - if (sc_asn1_read_tag(&seq, bodylen, &cla_out, - &tag_out, &seqlen) != SC_SUCCESS || - cla_out+tag_out != 0x30) { + if (sc_asn1_read_tag(&seq, bodylen, &cla_out, &tag_out, &seqlen) != SC_SUCCESS || + seq == 0 || + seqlen == 0 || + (cla_out | tag_out) != 0x30) { sc_log(card->ctx, "DER problem"); r = SC_ERROR_INVALID_ASN1_OBJECT; goto err; } keyref = sc_asn1_find_tag(card->ctx, seq, seqlen, 0x04, &keyreflen); if (!keyref || keyreflen != 1 || - (*keyref < 0x82 && *keyref > 0x95)) { + (*keyref < 0x82 || *keyref > 0x95)) { sc_log(card->ctx, "DER problem"); r = SC_ERROR_INVALID_ASN1_OBJECT; goto err; @@ -2828,25 +5300,36 @@ piv_process_history(sc_card_t *card) enumtag = PIV_OBJ_RETIRED_X509_1 + *keyref - 0x82; /* now add the cert like another object */ - i2 = put_tag_and_len(0x70,certlen, NULL) - + put_tag_and_len(0x71, 1, NULL) - + put_tag_and_len(0xFE, 0, NULL); - - certobjlen = put_tag_and_len(0x53, i2, NULL); + if ((tmplen = sc_asn1_put_tag(0x70, NULL, certlen, NULL, 0, NULL)) <= 0 || + (tmplen2 = sc_asn1_put_tag(0x71, NULL, 1, NULL, 0, NULL)) <= 0 || + (tmplen3 = sc_asn1_put_tag(0xFE, NULL, 0, NULL, 0, NULL)) <= 0) { + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto err; + } + i2 = tmplen + tmplen2 + tmplen3; + tmplen = sc_asn1_put_tag(0x53, NULL, i2, NULL, 0, NULL); + if (tmplen <= 0) { + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto err; + } + certobjlen = tmplen; certobj = malloc(certobjlen); if (certobj == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } cp = certobj; - put_tag_and_len(0x53, i2, &cp); - put_tag_and_len(0x70,certlen, &cp); - memcpy(cp, cert, certlen); - cp += certlen; - put_tag_and_len(0x71, 1,&cp); + if ((r = sc_asn1_put_tag(0x53, NULL, i2, cp, certobjlen, &cp)) != SC_SUCCESS || + (r = sc_asn1_put_tag(0x70, cert, certlen, cp, certobjlen - (cp - certobj), &cp)) != SC_SUCCESS || + (r = sc_asn1_put_tag(0x71, NULL, 1, cp, certobjlen - (cp - certobj), &cp)) != SC_SUCCESS) { + goto err; + } *cp++ = 0x00; - put_tag_and_len(0xFE, 0, &cp); + r = sc_asn1_put_tag(0xFE, NULL, 0, cp, certobjlen - (cp - certobj), &cp); + if (r != SC_SUCCESS) { + goto err; + } priv->obj_cache[enumtag].obj_data = certobj; priv->obj_cache[enumtag].obj_len = certobjlen; @@ -2854,15 +5337,13 @@ piv_process_history(sc_card_t *card) priv->obj_cache[enumtag].flags &= ~PIV_OBJ_CACHE_NOT_PRESENT; r = piv_cache_internal_data(card, enumtag); - sc_log(card->ctx, "got internal r=%d",r); - - certobj = NULL; + sc_log(card->ctx, "got internal r=%d", r); sc_log(card->ctx, - "Added from off card file #%d %p:%"SC_FORMAT_LEN_SIZE_T"u 0x%02X", - enumtag, - priv->obj_cache[enumtag].obj_data, - priv->obj_cache[enumtag].obj_len, *keyref); + "Added from off card file #%d %p:%" SC_FORMAT_LEN_SIZE_T "u 0x%02X", + enumtag, + priv->obj_cache[enumtag].obj_data, + priv->obj_cache[enumtag].obj_len, *keyref); bodylen -= (seqlen + seq - seqtag); seq += seqlen; @@ -2874,71 +5355,149 @@ piv_process_history(sc_card_t *card) LOG_FUNC_RETURN(card->ctx, r); } +static int +piv_obj_cache_free_entry(sc_card_t *card, int enumtag, int flags) +{ + piv_private_data_t *priv = PIV_DATA(card); + + if (priv->obj_cache[enumtag].obj_data) + free(priv->obj_cache[enumtag].obj_data); + priv->obj_cache[enumtag].obj_data = NULL; + priv->obj_cache[enumtag].obj_len = 0; + + if (priv->obj_cache[enumtag].internal_obj_data) + free(priv->obj_cache[enumtag].internal_obj_data); + priv->obj_cache[enumtag].internal_obj_data = NULL; + priv->obj_cache[enumtag].internal_obj_len = 0; + priv->obj_cache[enumtag].flags = flags; + + return SC_SUCCESS; +} static int piv_finish(sc_card_t *card) { - piv_private_data_t * priv = PIV_DATA(card); + piv_private_data_t *priv = PIV_DATA(card); int i; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (priv) { - sc_file_free(priv->aid_file); + if (priv->context_specific) { + sc_log(card->ctx, "Clearing CONTEXT_SPECIFIC lock"); + priv->context_specific = 0; + sc_unlock(card); + } + free(priv->aid_der.value); + free(priv->al_label); if (priv->w_buf) free(priv->w_buf); if (priv->offCardCertURL) free(priv->offCardCertURL); for (i = 0; i < PIV_OBJ_LAST_ENUM - 1; i++) { - sc_log(card->ctx, - "DEE freeing #%d, 0x%02x %p:%"SC_FORMAT_LEN_SIZE_T"u %p:%"SC_FORMAT_LEN_SIZE_T"u", - i, priv->obj_cache[i].flags, - priv->obj_cache[i].obj_data, - priv->obj_cache[i].obj_len, - priv->obj_cache[i].internal_obj_data, - priv->obj_cache[i].internal_obj_len); - if (priv->obj_cache[i].obj_data) - free(priv->obj_cache[i].obj_data); - if (priv->obj_cache[i].internal_obj_data) - free(priv->obj_cache[i].internal_obj_data); + piv_obj_cache_free_entry(card, i, 0); } +#ifdef ENABLE_PIV_SM + piv_clear_cvc_content(&priv->sm_cvc); + piv_clear_cvc_content(&priv->sm_in_cvc); + piv_clear_sm_session(&priv->sm_session); +#endif /* ENABLE_PIV_SM */ + free(priv); + card->drv_data = NULL; /* priv */ + } + return 0; +} + +static int +piv_match_card(sc_card_t *card) +{ + int r = 0; + + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d\n", card->type); + /* piv_match_card may be called with card->type, set by opensc.conf */ + /* user provided card type must be one we know */ + switch (card->type) { + case -1: + case SC_CARD_TYPE_PIV_II_BASE: + case SC_CARD_TYPE_PIV_II_GENERIC: + case SC_CARD_TYPE_PIV_II_HIST: + case SC_CARD_TYPE_PIV_II_NEO: + case SC_CARD_TYPE_PIV_II_YUBIKEY4: + case SC_CARD_TYPE_PIV_II_GI_DE_DUAL_CAC: + case SC_CARD_TYPE_PIV_II_GI_DE: + case SC_CARD_TYPE_PIV_II_GEMALTO_DUAL_CAC: + case SC_CARD_TYPE_PIV_II_GEMALTO: + case SC_CARD_TYPE_PIV_II_OBERTHUR_DUAL_CAC: + case SC_CARD_TYPE_PIV_II_OBERTHUR: + case SC_CARD_TYPE_PIV_II_PIVKEY: + case SC_CARD_TYPE_PIV_II_SWISSBIT: + case SC_CARD_TYPE_PIV_II_800_73_4: + case SC_CARD_TYPE_PIV_II_NITROKEY: + case SC_CARD_TYPE_PIV_II_TOKEN2: + case SC_CARD_TYPE_PIV_II_PIVAPPLET: + break; + default: + return 0; /* can not handle the card */ } - return 0; -} + r = sc_lock(card); + if (r < 0) + return 0; + /* its one we know, or we can test for it in piv_init */ + r = piv_match_card_continued(card); + sc_unlock(card); -static int piv_match_card(sc_card_t *card) -{ - int i, k; - size_t j; - u8 *p, *pe; - sc_file_t aidfile; - int type = -1; - piv_private_data_t *priv; + if (r < 0 || !card->drv_data) { + /* clean up what we left in card */ + piv_finish(card); + return 0; /* match failed */ + } - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d r:%d\n", card->type, r); + return 1; /* matched */ +} - /* Since we send an APDU, the card's logout function may be called... - * however it may be in dirty memory */ - card->ops->logout = NULL; +static int +piv_match_card_continued(sc_card_t *card) +{ + int i, r = 0, r2 = 0; + int type = -1; + piv_private_data_t *priv = NULL; + int saved_type = card->type; + sc_apdu_t apdu; + u8 yubico_version_buf[3] = {0}; /* piv_match_card may be called with card->type, set by opensc.conf */ - /* user provide card type must be one we know */ + /* User provided card type must be one we know */ + switch (card->type) { - case -1: - case SC_CARD_TYPE_PIV_II_GENERIC: - case SC_CARD_TYPE_PIV_II_HIST: - case SC_CARD_TYPE_PIV_II_NEO: - case SC_CARD_TYPE_PIV_II_YUBIKEY4: - type = card->type; - break; - default: - return 0; /* can not handle the card */ + case -1: + case SC_CARD_TYPE_PIV_II_BASE: + case SC_CARD_TYPE_PIV_II_GENERIC: + case SC_CARD_TYPE_PIV_II_HIST: + case SC_CARD_TYPE_PIV_II_NEO: + case SC_CARD_TYPE_PIV_II_YUBIKEY4: + case SC_CARD_TYPE_PIV_II_GI_DE_DUAL_CAC: + case SC_CARD_TYPE_PIV_II_GI_DE: + case SC_CARD_TYPE_PIV_II_GEMALTO_DUAL_CAC: + case SC_CARD_TYPE_PIV_II_GEMALTO: + case SC_CARD_TYPE_PIV_II_OBERTHUR_DUAL_CAC: + case SC_CARD_TYPE_PIV_II_OBERTHUR: + case SC_CARD_TYPE_PIV_II_PIVKEY: + case SC_CARD_TYPE_PIV_II_SWISSBIT: + case SC_CARD_TYPE_PIV_II_800_73_4: + case SC_CARD_TYPE_PIV_II_NITROKEY: + case SC_CARD_TYPE_PIV_II_TOKEN2: + case SC_CARD_TYPE_PIV_II_PIVAPPLET: + type = card->type; + break; + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_CARD); } + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d type:%d r:%d\n", card->type, type, r); if (type == -1) { - /* - *try to identify card by ATR or historical data in ATR + * Try to identify card by ATR or historical data in ATR * currently all PIV card will respond to piv_find_aid * the same. But in future may need to know card type first, * so do it here. @@ -2948,151 +5507,450 @@ static int piv_match_card(sc_card_t *card) if (card->reader->atr_info.hist_bytes_len == 8 && !(memcmp(card->reader->atr_info.hist_bytes, "Yubikey4", 8))) { type = SC_CARD_TYPE_PIV_II_YUBIKEY4; - } - else if (card->reader->atr_info.hist_bytes_len >= 7 && + } else if (card->reader->atr_info.hist_bytes_len >= 7 && !(memcmp(card->reader->atr_info.hist_bytes, "Yubikey", 7))) { type = SC_CARD_TYPE_PIV_II_NEO; + } else if (card->reader->atr_info.hist_bytes_len >= 6 && + !(memcmp(card->reader->atr_info.hist_bytes, "PIVKEY", 6))) { + type = SC_CARD_TYPE_PIV_II_PIVKEY; + } else if (card->reader->atr_info.hist_bytes_len >= 6 && + !(memcmp(card->reader->atr_info.hist_bytes, (u8 *)"TK\x00PIV", 6))) { + type = SC_CARD_TYPE_PIV_II_TOKEN2; } - else if (card->reader->atr_info.hist_bytes[0] == 0x80u) { /* compact TLV */ - p = card->reader->atr_info.hist_bytes; - pe = p + card->reader->atr_info.hist_bytes_len; - p++; /* skip 0x80u byte */ - while (p < pe && type == -1) { - j = *p & 0x0fu; /* length */ - if ((*p++ & 0xf0u) == 0xf0u) { /*looking for 15 */ - if ((p + j) <= pe) { - for (k = 0; piv_aids[k].len_long != 0; k++) { - if (j == piv_aids[k].len_long - && !memcmp(p, piv_aids[k].value,j)) { - type = SC_CARD_TYPE_PIV_II_HIST; - break; - } - } + /* look for TLV historic data */ + else if (card->reader->atr_info.hist_bytes_len > 0 && + card->reader->atr_info.hist_bytes[0] == 0x80u) { /* compact TLV */ + size_t datalen; + const u8 *data; + + /* look for card issuer's data: tag 5X where X is datalen */ + if ((data = sc_compacttlv_find_tag(card->reader->atr_info.hist_bytes + 1, + card->reader->atr_info.hist_bytes_len - 1, 0x50, &datalen))) { + if (datalen >= 8 && !(memcmp(data, "Nitrokey", 8))) { /* first 8 are Nitrokey */ + type = SC_CARD_TYPE_PIV_II_NITROKEY; + } else if (datalen == 7 && !(memcmp(data, "YubiKey", 7))) { + type = SC_CARD_TYPE_PIV_II_YUBIKEY4; /* reader says 4 really 5 */ + } + /* Yubikey 5 NFC ATR using ACR122 contactless reader does not match + * https://developers.yubico.com/PIV/Introduction/Yubico_extensions.html + * On Windows 10, using Omnikey 5021, the ATR is correct + * will look at only 6 bytes that do match + */ + else if (datalen == 7 && !(memcmp(data, "YubiKe", 6))) { + type = SC_CARD_TYPE_PIV_II_YUBIKEY4; /* reader says 4 really 5 */ + } + } else if ((data = sc_compacttlv_find_tag(card->reader->atr_info.hist_bytes + 1, + card->reader->atr_info.hist_bytes_len - 1, 0xF0, &datalen))) { + int k; + + for (k = 0; piv_aids[k].len_long != 0; k++) { + if (datalen == piv_aids[k].len_long && + !memcmp(data, piv_aids[k].value, datalen)) { + type = SC_CARD_TYPE_PIV_II_HIST; + break; } } - p += j; } } } - if (type == -1) - type = SC_CARD_TYPE_PIV_II_GENERIC; + + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d type:%d r:%d\n", card->type, type, r); + + if (type == -1) { + /* use known ATRs which changes the type */ + i = _sc_match_atr(card, piv_atrs, &type); + if (i < 0) + type = SC_CARD_TYPE_PIV_II_BASE; /* May be some newer unknown card including CAC or PIV-like card */ + } } - /* Detect by selecting applet */ - i = piv_find_aid(card, &aidfile); + card->type = type; - if (i < 0) - return 0; /* don't match. Does not have a PIV applet. */ + /* we either found via ATR historic bytes or ATR directly */ + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d type:%d r:%d\n", card->type, type, r); + /* allocate and init basic fields */ priv = calloc(1, sizeof(piv_private_data_t)); if (!priv) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); - if (card->type == -1) - card->type = type; - - card->drv_data = priv; - priv->aid_file = sc_file_new(); + card->drv_data = priv; /* will free if no match, or pass on to piv_init */ + /* + * Largest object defined in NIST sp800-73-3 and sp800-73-4 is 12710 bytes + * If for some reason future cards have larger objects, this value needs to + * be increased here. + */ + priv->max_object_size = MAX_FILE_SIZE - 256; /* fix SM apdu resplen issue */ priv->selected_obj = -1; priv->pin_preference = 0x80; /* 800-73-3 part 1, table 3 */ priv->logged_in = SC_PIN_STATE_UNKNOWN; - priv->tries_left = 10; /* will assume OK at start */ + priv->pstate = PIV_STATE_MATCH; + +#ifdef ENABLE_PIV_SM + memset(&card->sm_ctx, 0, sizeof card->sm_ctx); + card->sm_ctx.ops.open = piv_sm_open; + card->sm_ctx.ops.get_sm_apdu = piv_get_sm_apdu; + card->sm_ctx.ops.free_sm_apdu = piv_free_sm_apdu; + card->sm_ctx.ops.close = piv_sm_close; +#endif /* ENABLE_PIV_SM */ + + /* See if contactless or run as virtual card */ + /* PivApplet in .github/test_piv.sh for example */ + if (card->reader->atr.len >= 4 && + card->reader->atr.value[0] == 0x3b && + (card->reader->atr.value[1] & 0xF0) == 0x80 && + card->reader->atr.value[2] == 0x80 && + card->reader->atr.value[3] == 0x01) { + priv->init_flags |= PIV_INIT_CONTACTLESS; + } - /* Some objects will only be present if Histroy object says so */ - for (i=0; i < PIV_OBJ_LAST_ENUM -1; i++) - if(piv_objects[i].flags & PIV_OBJECT_NOT_PRESENT) + for (i = 0; i < PIV_OBJ_LAST_ENUM - 1; i++) + if (piv_objects[i].flags & PIV_OBJECT_NOT_PRESENT) priv->obj_cache[i].flags |= PIV_OBJ_CACHE_NOT_PRESENT; + /* + * Detect if active AID is PIV. NIST 800-73 says only one PIV application per card + * and PIV must be the default application. + * Try to avoid doing a select_aid and losing the login state on some cards. + * We may get interference on some cards by other drivers trying SELECT_AID before + * we get to see if PIV application is still active. Putting PIV driver first might help. + * + * Discovery Object introduced in 800-73-3 so will return OK if found and PIV applet active. + * Will fail with SC_ERROR_FILE_NOT_FOUND if 800-73-3 and no Discovery object. + * But some other card could also return SC_ERROR_FILE_NOT_FOUND. + * Will fail for other reasons if wrong applet is selected or bad PIV implementation. + */ + /* + * if ATR matched or user forced card type + * test if PIV is active applet without using AID If fails try AID + */ - return 1; /* match */ -} - + if (card->type > SC_CARD_TYPE_PIV_II_BASE && + !(priv->init_flags & PIV_INIT_CONTACTLESS)) { + r = piv_find_discovery(card); + if (r < 0) { + piv_obj_cache_free_entry(card, PIV_OBJ_DISCOVERY, 0); /* don't cache on failure */ + r = piv_find_aid(card); + LOG_TEST_GOTO_ERR(card->ctx, r, "Not a PIV card"); + } + } else { + /* piv_find_aid saves al_label from response */ + r = piv_find_aid(card); + LOG_TEST_GOTO_ERR(card->ctx, r, "Not a PIV card"); + } -static int piv_init(sc_card_t *card) -{ - int r; - piv_private_data_t * priv = PIV_DATA(card); - sc_apdu_t apdu; - unsigned long flags; - unsigned long ext_flags; - u8 neo_version_buf[3]; + /* Know to be PIV card but not the type */ - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + /* If card type still unknown, see if Application Label is known and set card->type */ + if (priv->al_label && priv->al_labellen) { + switch (card->type) { + case SC_CARD_TYPE_PIV_II_BASE: + case SC_CARD_TYPE_PIV_II_GENERIC: + for (i = 0; al_map[i].al_label; i++) { + if ((priv->al_labellen >= al_map[i].al_labellen) && + (!memcmp(priv->al_label, al_map[i].al_label, al_map[i].al_labellen))) { + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "AL match i:%d type:%d", i, al_map[i].al_type); + card->type = al_map[i].al_type; + break; + } + } + break; + } + } + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d CI:%08x r:%d AI:%08x\n", + card->type, priv->card_issues, r, priv->alg_ids); - /* can not force the PIV driver to use non-PIV cards as tested in piv_card_match */ - if (!priv || card->type == -1) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_NO_CARD_SUPPORT); + /* + * Assumes all Yubikey/Nitrokey/Token2/PivApplet cards are all "Yubico like" + * via ATR, ATR Historic bytes or Application Label + * Will also check if BASE cards are Yubikey like card and return a version number + * SC_CARD_TYPE_PIV_II_GENERIC can be set by user to not test for Yubico version + */ + switch (card->type) { + case SC_CARD_TYPE_PIV_II_NEO: + case SC_CARD_TYPE_PIV_II_YUBIKEY4: + case SC_CARD_TYPE_PIV_II_NITROKEY: + case SC_CARD_TYPE_PIV_II_TOKEN2: + case SC_CARD_TYPE_PIV_II_PIVAPPLET: + case SC_CARD_TYPE_PIV_II_GENERIC: + case SC_CARD_TYPE_PIV_II_BASE: /* unknown PIV card */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xFD, 0x00, 0x00); + apdu.lc = 0; + apdu.data = NULL; + apdu.datalen = 0; + apdu.resp = yubico_version_buf; + apdu.resplen = sizeof(yubico_version_buf); + apdu.le = apdu.resplen; + r2 = sc_transmit_apdu(card, &apdu); /* if not supported yubico_version == 0 */ + if (apdu.resplen == 3) { + priv->yubico_version = (yubico_version_buf[0] << 16) | (yubico_version_buf[1] << 8) | yubico_version_buf[2]; + sc_log(card->ctx, "Yubikey version test card->type=%d, r=0x%08x version=0x%08x", card->type, r, priv->yubico_version); + } + break; + } - sc_log(card->ctx, - "Max send = %"SC_FORMAT_LEN_SIZE_T"u recv = %"SC_FORMAT_LEN_SIZE_T"u card->type = %d", - card->max_send_size, card->max_recv_size, card->type); - card->cla = 0x00; - if(card->name == NULL) - card->name = "PIV-II card"; + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d r2:%d CI:%08x r:%d AI:%08x\n", + card->type, r2, priv->card_issues, r, priv->alg_ids); + /* We now know PIV AID is active, test CCC object. 800-73-* say CCC is required */ + /* CCC not readable over contactless, unless using VCI. but dont need CCC for SC_CARD_TYPE_PIV_II_800_73_4 */ + switch (card->type) { /* - * Set card_issues based on card type either set by piv_match_card or by opensc.conf + * For cards that may also be CAC, try and read the CCC + * CCC is required and all Dual PIV/CAC will have a CCC + * Currently Dual PIV/CAC are based on NIST 800-73-1 which does not have Discovery or History */ + case SC_CARD_TYPE_PIV_II_BASE: /* i.e. really dont know what this is */ + case SC_CARD_TYPE_PIV_II_GENERIC: + case SC_CARD_TYPE_PIV_II_HIST: + case SC_CARD_TYPE_PIV_II_GI_DE: + case SC_CARD_TYPE_PIV_II_GEMALTO: + case SC_CARD_TYPE_PIV_II_OBERTHUR: + r2 = piv_process_ccc(card); + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d r2:%d CI:%08x r:%d AI:%08x\n", + card->type, r2, priv->card_issues, r, priv->alg_ids); + /* Ignore any error. */ + /* If CCC says it has CAC with PKI on card set to one of the SC_CARD_TYPE_PIV_II_*_DUAL_CAC */ + if (priv->ccc_flags & PIV_CCC_F3_CAC_PKI) { + switch (card->type) { + case SC_CARD_TYPE_PIV_II_BASE: + case SC_CARD_TYPE_PIV_II_GENERIC: + case SC_CARD_TYPE_PIV_II_HIST: + case SC_CARD_TYPE_PIV_II_GI_DE: + card->type = SC_CARD_TYPE_PIV_II_GI_DE_DUAL_CAC; + priv->card_issues |= CI_DISCOVERY_USELESS; + priv->obj_cache[PIV_OBJ_DISCOVERY].flags |= PIV_OBJ_CACHE_NOT_PRESENT; + break; + case SC_CARD_TYPE_PIV_II_GEMALTO: + card->type = SC_CARD_TYPE_PIV_II_GEMALTO_DUAL_CAC; + priv->card_issues |= CI_DISCOVERY_USELESS; + priv->obj_cache[PIV_OBJ_DISCOVERY].flags |= PIV_OBJ_CACHE_NOT_PRESENT; + break; + case SC_CARD_TYPE_PIV_II_OBERTHUR: + card->type = SC_CARD_TYPE_PIV_II_OBERTHUR_DUAL_CAC; + priv->card_issues |= CI_DISCOVERY_USELESS; + priv->obj_cache[PIV_OBJ_DISCOVERY].flags |= PIV_OBJ_CACHE_NOT_PRESENT; + break; + } + } + break; - switch(card->type) { - case SC_CARD_TYPE_PIV_II_NEO: - case SC_CARD_TYPE_PIV_II_YUBIKEY4: - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xFD, 0x00, 0x00); - apdu.lc = 0; - apdu.data = NULL; - apdu.datalen = 0; - apdu.resp = neo_version_buf; - apdu.resplen = sizeof(neo_version_buf); - apdu.le = apdu.resplen; - r = sc_transmit_apdu(card, &apdu); - priv->neo_version = (neo_version_buf[0]<<16) | (neo_version_buf[1] <<8) | neo_version_buf[2]; - sc_log(card->ctx, "NEO card->type=%d, r=0x%08x version=0x%08x", card->type, r, priv->neo_version); - break; + case SC_CARD_TYPE_PIV_II_GI_DE_DUAL_CAC: + case SC_CARD_TYPE_PIV_II_GEMALTO_DUAL_CAC: + case SC_CARD_TYPE_PIV_II_OBERTHUR_DUAL_CAC: + priv->card_issues |= CI_DISCOVERY_USELESS; + priv->obj_cache[PIV_OBJ_DISCOVERY].flags |= PIV_OBJ_CACHE_NOT_PRESENT; + break; + } + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d r2:%d CI:%08x r:%d AI:%08x\n", + card->type, r2, priv->card_issues, r, priv->alg_ids); + + /* If contactless or run as virtual card try to get alg refs from AC entries in response to SELECT AID */ + if (!(priv->init_flags & PIV_INIT_AID_PARSED) && priv->init_flags & PIV_INIT_CONTACTLESS) { + r2 = piv_find_aid(card); + } + + /* Read AID if needed for these cards types */ + if (!(priv->init_flags & PIV_INIT_AID_PARSED)) { + switch (card->type) { + case SC_CARD_TYPE_PIV_II_BASE: + case SC_CARD_TYPE_PIV_II_GENERIC: + case SC_CARD_TYPE_PIV_II_800_73_4: + case SC_CARD_TYPE_PIV_II_NITROKEY: + case SC_CARD_TYPE_PIV_II_TOKEN2: + case SC_CARD_TYPE_PIV_II_PIVAPPLET: + r2 = piv_find_aid(card); + } + } + + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d r2:%d CI:%08x r:%d AI:%08x\n", + card->type, r2, priv->card_issues, r, priv->alg_ids); + + /* if card did not specify any of the NIST basic alg_ids add them */ + if ((priv->alg_ids & AI_NIST) == 0) { + priv->alg_ids |= AI_NIST; + } + + /* If unknown card has 800-73-4 features, it must be based on 800-73-4 or above */ + switch (card->type) { + case SC_CARD_TYPE_PIV_II_BASE: + case SC_CARD_TYPE_PIV_II_GENERIC: + if (priv->init_flags & PIV_INIT_AID_AC_SM) { + card->type = SC_CARD_TYPE_PIV_II_800_73_4; + } + +#ifdef ENABLE_PIV_SM + /* Discovery object has pin policy. 800-74-4 bits, its at least SC_CARD_TYPE_PIV_II_800_73_4 */ + if ((priv->pin_policy & (PIV_PP_OCC | PIV_PP_VCI_IMPL | PIV_PP_VCI_WITHOUT_PC)) != 0) { + card->type = SC_CARD_TYPE_PIV_II_800_73_4; + } +#endif + break; } + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d r2:%d CI:%08x r:%d AI:%08x\n", + card->type, r2, priv->card_issues, r, priv->alg_ids); + /* - * set card_issues flags based card->type and new versions - * YubiKey NEO and Ybuikey 4 have PIV applets, with compliance issues - * with the the NIST 800-73-3 specs The OpenSC developers do not have - * access to the different versions the NEO and 4, so it is a best - * if using a protected object can be use to test verify state. - * TODO use NEO version numbers to set the flags. Allows for finer control - * but needs input from Yubico or users. + * Set card_issues flags based card->type and version numbers if available. + * + * YubiKey NEO, Yubikey 4 and other devices with PIV applets, have compliance + * issues with the NIST 800-73-3 specs. The OpenSC developers do not have + * access to all the different devices or versions of the devices. + * Vendor and user input is welcome on any compliance issues. + * + * For the Yubico devices The assumption is also made that if a bug is + * fixed in a Yubico version that means it is fixed on both NEO and Yubikey 4. + * + * The flags CI_CANT_USE_GETDATA_FOR_STATE and CI_DISCOVERY_USELESS + * may be set earlier or later then in the following code. */ - switch(card->type) { - case SC_CARD_TYPE_PIV_II_NEO: - priv->card_issues = CI_NO_EC384 - | CI_VERIFY_630X - | CI_VERIFY_LC0_FAIL - | CI_OTHER_AID_LOSE_STATE - | CI_LEAKS_FILE_NOT_FOUND - | CI_NFC_EXPOSE_TOO_MUCH; - break; + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d r2:%d CI:%08x r:%d AI:%08x\n", + card->type, r2, priv->card_issues, r, priv->alg_ids); + switch (card->type) { + case SC_CARD_TYPE_PIV_II_NEO: + priv->card_issues |= CI_NO_EC384 | + CI_VERIFY_630X | + CI_OTHER_AID_LOSE_STATE | + CI_LEAKS_FILE_NOT_FOUND | + CI_NFC_EXPOSE_TOO_MUCH; + if (priv->yubico_version < 0x00040302) + priv->card_issues |= CI_VERIFY_LC0_FAIL; + break; - case SC_CARD_TYPE_PIV_II_YUBIKEY4: - priv->card_issues = CI_VERIFY_LC0_FAIL - | CI_OTHER_AID_LOSE_STATE - | CI_LEAKS_FILE_NOT_FOUND; - break; + case SC_CARD_TYPE_PIV_II_YUBIKEY4: + case SC_CARD_TYPE_PIV_II_PIVAPPLET: + case SC_CARD_TYPE_PIV_II_TOKEN2: + priv->card_issues |= CI_OTHER_AID_LOSE_STATE | + CI_LEAKS_FILE_NOT_FOUND; + if (priv->yubico_version < 0x00040302) + priv->card_issues |= CI_VERIFY_LC0_FAIL; + if (priv->yubico_version >= 0x00050700) /* Also used by Token2 */ + priv->alg_ids |= AI_RSA_4096 | AI_25519 | AI_X25519; + break; - case SC_CARD_TYPE_PIV_II_HIST: - priv->card_issues = 0; - break; + case SC_CARD_TYPE_PIV_II_NITROKEY: + priv->card_issues |= CI_OTHER_AID_LOSE_STATE; + if (priv->yubico_version >= 0x00010802) /* use for NitroKey too */ + priv->alg_ids |= AI_RSA_4096; + break; - case SC_CARD_TYPE_PIV_II_GENERIC: - priv->card_issues = CI_VERIFY_LC0_FAIL - | CI_OTHER_AID_LOSE_STATE; - break; + case SC_CARD_TYPE_PIV_II_GI_DE: + case SC_CARD_TYPE_PIV_II_OBERTHUR: + case SC_CARD_TYPE_PIV_II_GEMALTO: + case SC_CARD_TYPE_PIV_II_SWISSBIT: + priv->card_issues |= 0; /* could add others here */ + break; - default: - priv->card_issues = 0; /* opensc.conf may have it wrong, continue anyway */ - sc_log(card->ctx, "Unknown PIV card->type %d", card->type); - card->type = SC_CARD_TYPE_PIV_II_BASE; + case SC_CARD_TYPE_PIV_II_BASE: + case SC_CARD_TYPE_PIV_II_HIST: + case SC_CARD_TYPE_PIV_II_800_73_4: + priv->card_issues |= 0; /* could add others here */ + break; + + case SC_CARD_TYPE_PIV_II_GI_DE_DUAL_CAC: + case SC_CARD_TYPE_PIV_II_GEMALTO_DUAL_CAC: + case SC_CARD_TYPE_PIV_II_OBERTHUR_DUAL_CAC: + priv->card_issues |= CI_VERIFY_LC0_FAIL | + CI_PIV_AID_LOSE_STATE | + CI_NO_RANDOM | + CI_OTHER_AID_LOSE_STATE; + break; + + case SC_CARD_TYPE_PIV_II_GENERIC: + priv->card_issues |= CI_VERIFY_LC0_FAIL | + CI_OTHER_AID_LOSE_STATE; + break; + + case SC_CARD_TYPE_PIV_II_PIVKEY: + priv->card_issues |= CI_VERIFY_LC0_FAIL | + CI_PIV_AID_LOSE_STATE | /* be conservative */ + CI_NO_RANDOM; /* does not have 9B key */ + /* Discovery object returns 6A 82 so is not on card by default */ + break; + + default: + priv->card_issues |= CI_VERIFY_LC0_FAIL | + CI_OTHER_AID_LOSE_STATE; + /* opensc.conf may have it wrong, continue anyway */ + sc_log(card->ctx, "Unknown PIV card->type %d", card->type); + card->type = SC_CARD_TYPE_PIV_II_GENERIC; + } + sc_log(card->ctx, "PIV_MATCH card->type:%d r2:%d CI:%08x r:%d AI:%08x\n", + card->type, r2, priv->card_issues, r, priv->alg_ids); + + if (!(priv->card_issues & CI_DISCOVERY_USELESS) && !(priv->init_flags & PIV_INIT_DISCOVERY_PARSED)) { + /* + * We now know PIV AID is active, test DISCOVERY object again + * Some PIV don't support DISCOVERY and return + * SC_ERROR_INCORRECT_PARAMETERS. Any error + * including SC_ERROR_FILE_NOT_FOUND means we cannot use discovery + * to test for active AID. + */ + r2 = piv_find_discovery(card); + + if (r2 < 0) { + priv->card_issues |= CI_DISCOVERY_USELESS; + piv_obj_cache_free_entry(card, PIV_OBJ_DISCOVERY, PIV_OBJ_CACHE_NOT_PRESENT); + } + } + + sc_log(card->ctx, "PIV_MATCH card->type:%d r2:%d CI:%08x r:%d AI:%08x\n", + card->type, r2, priv->card_issues, r, priv->alg_ids); + /* Matched, caller will use or free priv and sc_lock as needed */ + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + +err: + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "PIV_MATCH failed card->type:%d r2:%d CI:%08x r:%d AI:%08x\n", + card->type, r2, priv->card_issues, r, priv->alg_ids); + /* don't match. Does not have a PIV applet. */ + piv_finish(card); + card->type = saved_type; + LOG_FUNC_RETURN(card->ctx, r); +} + +static int +piv_init(sc_card_t *card) +{ + int r = 0; + piv_private_data_t *priv = PIV_DATA(card); + unsigned long flags; + unsigned long flags_eddsa; + unsigned long flags_xeddsa; + unsigned long ext_flags; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + r = sc_lock(card); /* hold until match or init is complete */ + LOG_TEST_RET(card->ctx, r, "sc_lock failed"); + + /* piv_match_card_continued called from card match should have left card->drv_data */ + if (priv == NULL) { + r = piv_match_card_continued(card); + priv = PIV_DATA(card); + if (r < 0 || !priv) { + sc_log(card->ctx, "piv_match_card_continued failed card->type:%d", card->type); + sc_unlock(card); + piv_finish(card); + /* tell sc_connect_card to try other driver */ + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); + } } - sc_log(card->ctx, "PIV card-type=%d card_issues=0x%08x", card->type, priv->card_issues); + + /* read "card_driver PIV-II" opensc.conf options, and env parameters */ + piv_load_options(card); + + priv->pstate = PIV_STATE_INIT; + + sc_log(card->ctx, + "Max send = %" SC_FORMAT_LEN_SIZE_T "u recv = %" SC_FORMAT_LEN_SIZE_T "u card->type:%d, CI:%08x AI:%08x", + card->max_send_size, card->max_recv_size, card->type, priv->card_issues, priv->alg_ids); + card->cla = 0x00; + if (card->name == NULL) + card->name = card->driver->name; priv->enumtag = piv_aids[0].enumtag; @@ -3101,57 +5959,153 @@ static int piv_init(sc_card_t *card) * piv-tool can still do this, just don't tell PKCS#11 */ - flags = SC_ALGORITHM_RSA_RAW; + flags = SC_ALGORITHM_RSA_RAW; + + if (card->type == SC_CARD_TYPE_PIV_II_SWISSBIT) { + flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; + } + + if (priv->alg_ids & AI_RSA_1024) + _sc_card_add_rsa_alg(card, 1024, flags, 0); /* mandatory */ + + if (priv->alg_ids & AI_RSA_2048) + _sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */ + + if (priv->alg_ids & AI_RSA_3072) + _sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */ - _sc_card_add_rsa_alg(card, 1024, flags, 0); /* manditory */ - _sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */ - _sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */ + if (priv->alg_ids & AI_RSA_4096) + _sc_card_add_rsa_alg(card, 4096, flags, 0); /* non standard */ - flags = SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDH_CDH_RAW | SC_ALGORITHM_ECDSA_HASH_NONE; - ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES; + if (!(priv->card_issues & CI_NO_EC)) { + int i; + flags = SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDH_CDH_RAW | SC_ALGORITHM_ECDSA_HASH_NONE; + ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES; + flags_eddsa = SC_ALGORITHM_EDDSA_RAW; + flags_xeddsa = SC_ALGORITHM_XEDDSA_RAW | SC_ALGORITHM_ECDH_CDH_RAW; - _sc_card_add_ec_alg(card, 256, flags, ext_flags, NULL); - if (!(priv->card_issues & CI_NO_EC384)) - _sc_card_add_ec_alg(card, 384, flags, ext_flags, NULL); + for (i = 0; ec_curves[i].oid.value[0] >= 0; i++) { + if (ec_curves[i].key_type == SC_ALGORITHM_EC) { + if (!(priv->card_issues & CI_NO_EC384 && ec_curves[i].size == 384)) + _sc_card_add_ec_alg(card, ec_curves[i].size, flags, ext_flags, &ec_curves[i].oid); + } + + else if (priv->alg_ids & AI_25519 && ec_curves[i].key_type == SC_ALGORITHM_EDDSA) + _sc_card_add_eddsa_alg(card, ec_curves[i].size, flags_eddsa, ext_flags, &ec_curves[i].oid); + + else if (priv->alg_ids & AI_X25519 && ec_curves[i].key_type == SC_ALGORITHM_XEDDSA) + _sc_card_add_xeddsa_alg(card, ec_curves[i].size, flags_xeddsa, ext_flags, &ec_curves[i].oid); + } + } + + if (!(priv->card_issues & CI_NO_RANDOM)) + card->caps |= SC_CARD_CAP_RNG; - /* TODO may turn off SC_CARD_CAP_ISO7816_PIN_INFO later */ - card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO; + /* May turn off SC_CARD_CAP_ISO7816_PIN_INFO later */ + card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; + + /* + * 800-73-3 cards may have discovery. "piv-like cards may or may not. + * 800-73-4 with VCI must have it as it has the pin policy needed for VCI . + */ +#ifdef ENABLE_PIV_SM /* - * 800-73-3 cards may have a history object and/or a discovery object - * We want to process them now as this has information on what - * keys and certs the card has and how the pin might be used. + * 800-73-4 + * Response of AID says if SM is supported. Look for Cipher Suite */ - r = piv_process_history(card); + if (priv->csID && priv->cs != NULL) { + /* + * Main point in SM and VCI is to allow contactless access + */ + /* Only piv_init and piv_reader_lock_obtained should call piv_sm_open */ - r = piv_process_discovery(card); + /* If user said PIV_SM_FLAGS_NEVER, dont start SM; implies limited contatless access */ + if (priv->sm_flags & PIV_SM_FLAGS_NEVER) { + sc_log(card->ctx, "User has requested PIV_SM_FLAGS_NEVER"); + r = SC_SUCCESS; /* Users choice */ - if (r > 0) - r = 0; - LOG_FUNC_RETURN(card->ctx, r); -} + } else if ((priv->init_flags & PIV_INIT_CONTACTLESS) && + !(priv->pin_policy & PIV_PP_VCI_IMPL)) { + sc_log(card->ctx, "Contactless and no card support for VCI"); + r = SC_SUCCESS; /* User should know VCI is not possible with their card; use like 800-73-3 contactless */ + + } else if ((priv->init_flags & PIV_INIT_CONTACTLESS) && + !(priv->pin_policy & PIV_PP_VCI_WITHOUT_PC) && + (priv->pairing_code[0] == 0x00)) { + sc_log(card->ctx, "Contactless, pairing_code required and no pairing code"); + r = SC_ERROR_PIN_CODE_INCORRECT; /* User should know they need to set pairing code */ + + } else { + priv->sm_flags |= PIV_SM_FLAGS_DEFER_OPEN; /* tell priv_sm_open, OK to open */ + r = piv_sm_open(card); + sc_log(card->ctx, "piv_sm_open returned:%d", r); + } + + /* If failed, and user said PIV_SM_FLAGS_ALWAYS quit */ + if (priv->sm_flags & PIV_SM_FLAGS_ALWAYS && r < 0) { + sc_log(card->ctx, "User has requested PIV_SM_FLAGS_ALWAYS, SM has failed to start, don't use the card"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ALLOWED); + } + + /* user has wrong or no required pairing code */ + if (r == SC_ERROR_PIN_CODE_INCORRECT) + LOG_FUNC_RETURN(card->ctx, r); + + /* If SM did not start, or is not expected to start, continue on without it */ + } +#endif /* ENABLE_PIV_SM */ + /* + * 800-73-3 cards may have a history object + * We want to process it now as this has information on what + * keys and certs. "piv like" cards may or may not have history + */ + piv_process_history(card); + + priv->pstate = PIV_STATE_NORMAL; + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} -static int piv_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) +static int +piv_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); int r; - piv_private_data_t * priv = PIV_DATA(card); +#ifdef ENABLE_PIV_SM + int i; +#endif + piv_private_data_t *priv = PIV_DATA(card); + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - /* may be called before piv_init has allocated priv */ + /* may be called before piv_init has allocated priv */ if (priv) { /* need to save sw1 and sw2 if trying to determine card_state from pin_cmd */ + if (priv->pin_cmd_verify) { priv->pin_cmd_verify_sw1 = sw1; priv->pin_cmd_verify_sw2 = sw2; + } else { + /* a command has completed and it is not verify + * If we are in a context_specific sequence, unlock + * This just decrements the extra lock count + */ + if (priv->context_specific) { + sc_log(card->ctx, "Clearing CONTEXT_SPECIFIC lock"); + priv->context_specific = 0; + sc_unlock(card); + } } + if (priv->card_issues & CI_VERIFY_630X) { - /* Handle the Yubikey NEO or any other PIV card which returns in response to a verify - * 63 0X rather than 63 CX indicate the number of remaining PIN retries. - * Perhaps they misread the spec and thought 0xCX meant "clear" or "don't care", not a literal 0xC! - */ + /* Handle the Yubikey NEO or any other PIV card which returns in response to a verify + * 63 0X rather than 63 CX indicate the number of remaining PIN retries. + * Perhaps they misread the spec and thought 0xCX meant "clear" or "don't care", not a literal 0xC! + */ if (priv->pin_cmd_verify && sw1 == 0x63U) { priv->pin_cmd_verify_sw2 |= 0xC0U; /* make it easier to test in other code */ if ((sw2 & ~0x0fU) == 0x00U) { @@ -3162,19 +6116,30 @@ static int piv_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2 } } } +#ifdef ENABLE_PIV_SM + /* Note 6982 is map to SC_ERROR_SM_NO_SESSION_KEYS but iso maps it to SC_ERROR_SECURITY_STATUS_NOT_SATISFIED */ + /* we do this because 6982 could also mean a verify is not allowed over contactless without VCI */ + /* we stashed the sw1 and sw2 above for verify */ + /* Check specific NIST sp800-73-4 SM errors */ + for (i = 0; piv_sm_errors[i].SWs != 0; i++) { + if (piv_sm_errors[i].SWs == ((sw1 << 8) | sw2)) { + sc_log(card->ctx, "%s", piv_sm_errors[i].errorstr); + return piv_sm_errors[i].errorno; + } + } +#endif r = iso_drv->ops->check_sw(card, sw1, sw2); return r; } - static int piv_check_protected_objects(sc_card_t *card) { int r = 0; int i; - piv_private_data_t * priv = PIV_DATA(card); + piv_private_data_t *priv = PIV_DATA(card); u8 buf[8]; /* tag of 53 with 82 xx xx will fit in 4 */ - u8 * rbuf; + u8 *rbuf; size_t buf_len; static int protected_objects[] = {PIV_OBJ_PI, PIV_OBJ_CHF, PIV_OBJ_IRIS_IMAGE}; @@ -3192,20 +6157,17 @@ piv_check_protected_objects(sc_card_t *card) * If we can't determine the security state from this process, * set card_issues CI_CANT_USE_GETDATA_FOR_STATE * and return SC_ERROR_PIN_CODE_INCORRECT - * The curcomvention is to add a dummy Printed Info object in the card. + * The circumvention is to add a dummy Printed Info object in the card. * so we will have an object to test. * * We save the object's number to use in the future. * */ if (priv->object_test_verify == 0) { - for (i = 0; i < (int)(sizeof(protected_objects)/sizeof(int)); i++) { + for (i = 0; i < (int)(sizeof(protected_objects) / sizeof(int)); i++) { buf_len = sizeof(buf); - priv->pin_cmd_noparse = 1; /* tell piv_general_io dont need to parse at all. */ rbuf = buf; r = piv_get_data(card, protected_objects[i], &rbuf, &buf_len); - priv->pin_cmd_noparse = 0; - /* TODO may need to check sw1 and sw2 to see what really happened */ if (r >= 0 || r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { /* we can use this object next time if needed */ @@ -3224,10 +6186,8 @@ piv_check_protected_objects(sc_card_t *card) } else { /* use the one object we found earlier. Test is security status has changed */ buf_len = sizeof(buf); - priv->pin_cmd_noparse = 1; /* tell piv_general_io dont need to parse at all. */ rbuf = buf; r = piv_get_data(card, priv->object_test_verify, &rbuf, &buf_len); - priv->pin_cmd_noparse = 0; } if (r == SC_ERROR_FILE_NOT_FOUND) r = SC_ERROR_PIN_CODE_INCORRECT; @@ -3236,16 +6196,16 @@ piv_check_protected_objects(sc_card_t *card) else if (r > 0) r = SC_SUCCESS; - sc_log(card->ctx, "object_test_verify=%d, card_issues = 0x%08x", priv->object_test_verify, priv->card_issues); + sc_log(card->ctx, "card->type:%d CI:%08x r:%d AI:%08x\n", + card->type, priv->card_issues, r, priv->alg_ids); LOG_FUNC_RETURN(card->ctx, r); } - static int -piv_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) +piv_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) { int r = 0; - piv_private_data_t * priv = PIV_DATA(card); + piv_private_data_t *priv = PIV_DATA(card); /* Extra validation of (new) PIN during a PIN change request, to * ensure it's not outside the FIPS 201 4.1.6.1 (numeric only) and @@ -3256,11 +6216,11 @@ piv_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_log(card->ctx, "piv_pin_cmd tries_left=%d, logged_in=%d", priv->tries_left, priv->logged_in); if (data->cmd == SC_PIN_CMD_CHANGE) { - int i = 0; + size_t i = 0; if (data->pin2.len < 6) { return SC_ERROR_INVALID_PIN_LENGTH; } - for(i=0; i < data->pin2.len; ++i) { + for (i = 0; i < data->pin2.len; ++i) { if (!isdigit(data->pin2.data[i])) { return SC_ERROR_INVALID_DATA; } @@ -3272,9 +6232,17 @@ piv_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) if (data->cmd == SC_PIN_CMD_GET_INFO) { /* fill in what we think it should be */ data->pin1.logged_in = priv->logged_in; data->pin1.tries_left = priv->tries_left; - if (tries_left) - *tries_left = priv->tries_left; - if (priv->logged_in == SC_PIN_STATE_LOGGED_IN) { + + /* + * If called to check on the login state for a context specific login + * return not logged in. Needed because of logic in e6f7373ef066 + */ + if (data->pin_type == SC_AC_CONTEXT_SPECIFIC) { + data->pin1.logged_in = SC_PIN_STATE_LOGGED_OUT; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + } + + if (priv->logged_in & SC_PIN_STATE_LOGGED_IN) { /* Avoid status requests when the user is logged in to handle NIST * 800-73-4 Part 2: * The PKI cryptographic function (see Table 4b) is protected with @@ -3287,26 +6255,58 @@ piv_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) } } + /* + * If this was for a CKU_CONTEXT_SPECFIC login, lock the card one more time. + * to avoid any interference from other applications. + * Sc_unlock will be called at a later time after the next card command + * that should be a crypto operation. If its not then it is a error by the + * calling application. + */ + if (data->cmd == SC_PIN_CMD_VERIFY && data->pin_type == SC_AC_CONTEXT_SPECIFIC) { + priv->context_specific = 1; + sc_log(card->ctx, "Starting CONTEXT_SPECIFIC verify"); + r = sc_lock(card); + if (r != SC_SUCCESS) { + sc_log(card->ctx, "sc_lock failed"); + return r; + } + } + priv->pin_cmd_verify = 1; /* tell piv_check_sw its a verify to save sw1, sw2 */ - r = iso_drv->ops->pin_cmd(card, data, tries_left); + r = iso_drv->ops->pin_cmd(card, data); priv->pin_cmd_verify = 0; + /* tell user verify not supported on contactless without VCI */ + if (priv->pin_cmd_verify_sw1 == 0x69 && priv->pin_cmd_verify_sw2 == 0x82 && + priv->init_flags & PIV_INIT_CONTACTLESS && + card->type == SC_CARD_TYPE_PIV_II_800_73_4) { + sc_log(card->ctx, "Token does not support pin verify over contacless reader"); + r = SC_ERROR_NOT_SUPPORTED; + } + + /* if verify failed, release the lock */ + if (data->cmd == SC_PIN_CMD_VERIFY && r < 0 && priv->context_specific) { + sc_log(card->ctx, "Clearing CONTEXT_SPECIFIC"); + priv->context_specific = 0; + sc_unlock(card); + } + /* if access to applet is know to be reset by other driver we select_aid and try again */ - if ( priv->card_issues & CI_OTHER_AID_LOSE_STATE && priv->pin_cmd_verify_sw1 == 0x6DU) { + if (priv->card_issues & CI_OTHER_AID_LOSE_STATE && priv->pin_cmd_verify_sw1 == 0x6DU) { sc_log(card->ctx, "AID may be lost doing piv_find_aid and retry pin_cmd"); - r = piv_find_aid(card, priv->aid_file); /* return not tested */ + piv_find_aid(card); priv->pin_cmd_verify = 1; /* tell piv_check_sw its a verify to save sw1, sw2 */ - r = iso_drv->ops->pin_cmd(card, data, tries_left); + r = iso_drv->ops->pin_cmd(card, data); priv->pin_cmd_verify = 0; } - /* If verify worked, we are loged_in */ + /* If verify worked, we are logged_in */ if (data->cmd == SC_PIN_CMD_VERIFY) { - if (r >= 0) - priv->logged_in = SC_PIN_STATE_LOGGED_IN; - else - priv->logged_in = SC_PIN_STATE_LOGGED_OUT; + if (r >= 0) + priv->logged_in = SC_PIN_STATE_LOGGED_IN; + else + priv->logged_in = SC_PIN_STATE_LOGGED_OUT; } /* Some cards never return 90 00 for SC_PIN_CMD_GET_INFO even if the card state is verified */ @@ -3315,18 +6315,17 @@ piv_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) if (data->cmd == SC_PIN_CMD_GET_INFO) { if (priv->card_issues & CI_CANT_USE_GETDATA_FOR_STATE) { sc_log(card->ctx, "CI_CANT_USE_GETDATA_FOR_STATE set, assume logged_in=%d", priv->logged_in); - data->pin1.logged_in = priv->logged_in; /* use what ever we saw last */ - } else if (priv->card_issues & CI_VERIFY_LC0_FAIL - && priv->pin_cmd_verify_sw1 == 0x63U ) { /* can not use modified return codes from iso->drv->pin_cmd */ - /* try another method, looking at a protected object this may require adding one of these to NEO */ - r = piv_check_protected_objects(card); + data->pin1.logged_in = priv->logged_in; /* use what ever we saw last */ + } else if (priv->card_issues & CI_VERIFY_LC0_FAIL && + priv->pin_cmd_verify_sw1 == 0x63U) { /* can not use modified return codes from iso->drv->pin_cmd */ + /* try another method, looking at a protected object this may require adding one of these to NEO */ + r = piv_check_protected_objects(card); if (r == SC_SUCCESS) data->pin1.logged_in = SC_PIN_STATE_LOGGED_IN; - else if (r == SC_ERROR_PIN_CODE_INCORRECT) { + else if (r == SC_ERROR_PIN_CODE_INCORRECT) { if (priv->card_issues & CI_CANT_USE_GETDATA_FOR_STATE) { /* we still can not determine login state */ data->pin1.logged_in = priv->logged_in; /* may have be set from SC_PIN_CMD_VERIFY */ - /* TODO a reset may have logged us out. need to detect resets */ } else { data->pin1.logged_in = SC_PIN_STATE_LOGGED_OUT; } @@ -3337,45 +6336,116 @@ piv_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) priv->tries_left = data->pin1.tries_left; } - sc_log(card->ctx, "piv_pin_cmd tries_left=%d, logged_in=%d",priv->tries_left, priv->logged_in); + sc_log(card->ctx, "piv_pin_cmd tries_left=%d, logged_in=%d", priv->tries_left, priv->logged_in); LOG_FUNC_RETURN(card->ctx, r); } - -static int piv_logout(sc_card_t *card) +static int +piv_logout(sc_card_t *card) { - int r = SC_ERROR_NOT_SUPPORTED; /* TODO Some PIV cards may support a logout */ - /* piv_private_data_t * priv = PIV_DATA(card); */ + int r = SC_ERROR_INTERNAL; + piv_private_data_t *priv = PIV_DATA(card); LOG_FUNC_CALLED(card->ctx); - /* TODO 800-73-3 does not define a logout, 800-73-4 does */ + if (priv) { + /* logout defined since 800-73-4 */ + r = iso7816_logout(card, priv->pin_preference); + if (r == SC_SUCCESS) { + priv->logged_in = SC_PIN_STATE_LOGGED_OUT; + } + } LOG_FUNC_RETURN(card->ctx, r); } +/* + * Called when a sc_lock gets a reader lock and PCSC SCardBeginTransaction + * If SCardBeginTransaction may pass back that a card reset was seen since + * the last transaction completed. + * There may have been one or more resets, by other card drivers in different + * processes, and they may have taken action already + * and changed the AID and or may have sent a VERIFY with PIN + * So test the state of the card. + * this is very similar to what the piv_match routine does, + */ -static int piv_card_reader_lock_obtained(sc_card_t *card, int was_reset) +/* card.c also calls piv_sm_open before this if a reset was done, but + * does not say if a reset was done or not. May need to ignore the call + * the piv_sm_open in this case, but how? may need a open is active flag, + * in case it is the APDU done from open caused triggered the case. + */ +static int +piv_card_reader_lock_obtained(sc_card_t *card, int was_reset) { int r = 0; - u8 temp[2000]; + u8 temp[SC_MAX_APDU_BUFFER_SIZE]; size_t templen = sizeof(temp); - piv_private_data_t * priv = PIV_DATA(card); /* may be null */ + piv_private_data_t *priv = PIV_DATA(card); /* may be null */ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - if (was_reset > 0) { - if (priv) - priv->logged_in = SC_PIN_STATE_UNKNOWN; - r = piv_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, temp, &templen); + /* We have a PCSC transaction and sc_lock */ + if (priv == NULL || priv->pstate == PIV_STATE_MATCH) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + priv ? "PIV_STATE_MATCH" : "priv==NULL"); + r = 0; /* do nothing, piv_match will take care of it */ + goto err; } - LOG_FUNC_RETURN(card->ctx, r); -} + priv->init_flags |= PIV_INIT_IN_READER_LOCK_OBTAINED; + + /* make sure our application is active */ + + /* first see if AID is active AID by reading discovery object '7E' */ + /* If not try selecting AID */ + + /* but if card does not support DISCOVERY object we can not use it */ + if (priv->card_issues & CI_DISCOVERY_USELESS) { + r = SC_ERROR_NO_CARD_SUPPORT; + } else { + r = piv_find_discovery(card); +#ifdef ENABLE_PIV_SM + /* + * All 800-73-4 cards that support SM, also have a discovery object with + * the pin_policy, so can not have CI_DISCOVERY_USELESS + * Discovery object can be read with contact or contactless + * If read with SM and fails with 69 88 SC_ERROR_SM_INVALID_SESSION_KEY + * sm.c will close the SM connectrion, and set defer + */ + if (was_reset == 0 && (r == SC_ERROR_SM_INVALID_SESSION_KEY || priv->sm_flags & PIV_SM_FLAGS_DEFER_OPEN)) { + sc_log(card->ctx, "SC_ERROR_SM_INVALID_SESSION_KEY || PIV_SM_FLAGS_DEFER_OPEN"); + piv_sm_open(card); + r = piv_find_discovery(card); + } +#endif /* ENABLE_PIV_SM */ + } + + if (r < 0) { + if (was_reset > 0 || !(priv->card_issues & CI_PIV_AID_LOSE_STATE)) { + r = iso7816_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, temp, &templen); + sc_debug(card->ctx, SC_LOG_DEBUG_MATCH, "iso7816_select_aid card->type:%d r:%d\n", card->type, r); + } else { + r = 0; /* can't do anything with this card, hope there was no interference */ + } + } + + if (r < 0) /* bad error return will show up in sc_lock as error*/ + goto err; + if (was_reset > 0) + priv->logged_in = SC_PIN_STATE_UNKNOWN; + r = 0; + +err: + if (priv) + priv->init_flags &= ~PIV_INIT_IN_READER_LOCK_OBTAINED; + LOG_FUNC_RETURN(card->ctx, r); +} -static struct sc_card_driver * sc_get_driver(void) +static struct sc_card_driver * +sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); @@ -3384,7 +6454,7 @@ static struct sc_card_driver * sc_get_driver(void) piv_ops.init = piv_init; piv_ops.finish = piv_finish; - piv_ops.select_file = piv_select_file; /* must use get/put, could emulate? */ + piv_ops.select_file = piv_select_file; /* must use get/put, could emulate? */ piv_ops.get_challenge = piv_get_challenge; piv_ops.logout = piv_logout; piv_ops.read_binary = piv_read_binary; @@ -3392,7 +6462,7 @@ static struct sc_card_driver * sc_get_driver(void) piv_ops.set_security_env = piv_set_security_env; piv_ops.restore_security_env = piv_restore_security_env; piv_ops.compute_signature = piv_compute_signature; - piv_ops.decipher = piv_decipher; + piv_ops.decipher = piv_decipher; piv_ops.check_sw = piv_check_sw; piv_ops.card_ctl = piv_card_ctl; piv_ops.pin_cmd = piv_pin_cmd; @@ -3401,11 +6471,8 @@ static struct sc_card_driver * sc_get_driver(void) return &piv_drv; } - -#if 1 -struct sc_card_driver * sc_get_piv_driver(void) +struct sc_card_driver * +sc_get_piv_driver(void) { return sc_get_driver(); } -#endif - diff --git a/src/libopensc/card-rtecp.c b/src/libopensc/card-rtecp.c index a1efe22e8d..70e27153bd 100644 --- a/src/libopensc/card-rtecp.c +++ b/src/libopensc/card-rtecp.c @@ -1,5 +1,5 @@ /* - * card-rtecp.c: Support for Rutoken ECP cards + * card-rtecp.c: Support for Rutoken ECP and Rutoken Lite cards * * Copyright (C) 2009 Aleksey Samsonov * @@ -15,14 +15,13 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif -#include #include #include #include @@ -35,19 +34,38 @@ static const struct sc_card_operations *iso_ops = NULL; static struct sc_card_operations rtecp_ops; static struct sc_card_driver rtecp_drv = { - "Rutoken ECP driver", + "Rutoken ECP and Lite driver", "rutoken_ecp", &rtecp_ops, NULL, 0, NULL }; -static struct sc_atr_table rtecp_atrs[] = { +static const struct sc_atr_table rtecp_atrs[] = { /* Rutoken ECP */ { "3B:8B:01:52:75:74:6F:6B:65:6E:20:45:43:50:A0", - NULL, "Rutoken ECP", SC_CARD_TYPE_GENERIC_BASE, 0, NULL }, + NULL, "Rutoken ECP", SC_CARD_TYPE_RUTOKEN_ECP, 0, NULL }, /* Rutoken ECP (DS) */ { "3B:8B:01:52:75:74:6F:6B:65:6E:20:44:53:20:C1", - NULL, "Rutoken ECP (DS)", SC_CARD_TYPE_GENERIC_BASE, 0, NULL }, + NULL, "Rutoken ECP (DS)", SC_CARD_TYPE_RUTOKEN_ECP, 0, NULL }, + /* Rutoken ECP SC T0 */ + { "3B:9C:96:00:52:75:74:6F:6B:65:6E:45:43:50:73:63", + "00:00:00:00:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF", + "Rutoken ECP SC", SC_CARD_TYPE_RUTOKEN_ECP_SC, 0, NULL }, + /* Rutoken ECP SC T1 */ + { "3B:9C:94:80:11:40:52:75:74:6F:6B:65:6E:45:43:50:73:63:C3", + "00:00:00:00:00:00:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:00", + "Rutoken ECP SC", SC_CARD_TYPE_RUTOKEN_ECP_SC, 0, NULL }, + /* Rutoken ECP SC NFC */ + { "3B:88:80:01:52:74:53:43:77:81:83:20:6A", + "00:00:00:00:FF:FF:FF:FF:00:00:00:00:00", + "Rutoken ECP SC NFC", SC_CARD_TYPE_RUTOKEN_ECP_SC, 0, NULL }, + /* Rutoken Lite */ + { "3B:8B:01:52:75:74:6F:6B:65:6E:6C:69:74:65:C2", + NULL, "Rutoken Lite", SC_CARD_TYPE_RUTOKEN_LITE, 0, NULL }, + /* Rutoken Lite SC*/ + { "3B:9E:96:00:52:75:74:6F:6B:65:6E:4C:69:74:65:53:43:32", + "00:00:00:00:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF", + "Rutoken Lite SC", SC_CARD_TYPE_RUTOKEN_LITE_SC, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; @@ -57,9 +75,9 @@ static int rtecp_match_card(sc_card_t *card) i = _sc_match_atr(card, rtecp_atrs, &card->type); if (i >= 0) { card->name = rtecp_atrs[i].name; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 1); + LOG_FUNC_RETURN(card->ctx, 1); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); + LOG_FUNC_RETURN(card->ctx, 0); } static int rtecp_init(sc_card_t *card) @@ -67,10 +85,17 @@ static int rtecp_init(sc_card_t *card) sc_algorithm_info_t info; unsigned long flags; - assert(card && card->ctx); - card->caps |= SC_CARD_CAP_RNG; + if (!card || !card->ctx) + return SC_ERROR_INVALID_ARGUMENTS; + card->cla = 0; + if (card->type == SC_CARD_TYPE_RUTOKEN_LITE + || card->type == SC_CARD_TYPE_RUTOKEN_LITE_SC) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); + + card->caps |= SC_CARD_CAP_RNG; + flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN | SC_ALGORITHM_RSA_PAD_NONE | SC_ALGORITHM_RSA_HASH_NONE; @@ -82,6 +107,7 @@ static int rtecp_init(sc_card_t *card) _sc_card_add_rsa_alg(card, 1536, flags, 0); _sc_card_add_rsa_alg(card, 1792, flags, 0); _sc_card_add_rsa_alg(card, 2048, flags, 0); + _sc_card_add_rsa_alg(card, 4096, flags, 0); memset(&info, 0, sizeof(info)); info.algorithm = SC_ALGORITHM_GOSTR3410; @@ -90,7 +116,7 @@ static int rtecp_init(sc_card_t *card) | SC_ALGORITHM_GOSTR3410_HASH_NONE; _sc_card_add_algorithm(card, &info); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 0); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static void reverse(unsigned char *buf, size_t len) @@ -98,7 +124,9 @@ static void reverse(unsigned char *buf, size_t len) unsigned char tmp; size_t i; - assert(buf || len == 0); + if (!buf && len != 0) + return; + for (i = 0; i < len / 2; ++i) { tmp = buf[i]; @@ -140,16 +168,19 @@ static void set_acl_from_sec_attr(sc_card_t *card, sc_file_t *file) unsigned int method; unsigned long key_ref; - assert(card && card->ctx && file); - assert(file->sec_attr && file->sec_attr_len == SC_RTECP_SEC_ATTR_SIZE); - assert(1 + 6 < SC_RTECP_SEC_ATTR_SIZE); + if (!card || !card->ctx || !file || !file->sec_attr + || file->sec_attr_len != SC_RTECP_SEC_ATTR_SIZE + || 1 + 6 >= SC_RTECP_SEC_ATTR_SIZE) + { + return; + } sc_file_add_acl_entry(file, SC_AC_OP_SELECT, SC_AC_NONE, SC_AC_KEY_REF_NONE); if (file->sec_attr[0] & 0x40) /* if AccessMode.6 */ { method = sec_attr_to_method(file->sec_attr[1 + 6]); key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 6]); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "SC_AC_OP_DELETE %i %lu\n", (int)method, key_ref); sc_file_add_acl_entry(file, SC_AC_OP_DELETE, method, key_ref); @@ -158,7 +189,7 @@ static void set_acl_from_sec_attr(sc_card_t *card, sc_file_t *file) { method = sec_attr_to_method(file->sec_attr[1 + 0]); key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 0]); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, (file->type == SC_FILE_TYPE_DF) ? "SC_AC_OP_CREATE %i %lu\n" : "SC_AC_OP_READ %i %lu\n", @@ -176,11 +207,11 @@ static void set_acl_from_sec_attr(sc_card_t *card, sc_file_t *file) { method = sec_attr_to_method(file->sec_attr[1 + 1]); key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 1]); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "SC_AC_OP_UPDATE %i %lu\n", (int)method, key_ref); sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, method, key_ref); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "SC_AC_OP_WRITE %i %lu\n", (int)method, key_ref); sc_file_add_acl_entry(file, SC_AC_OP_WRITE, method, key_ref); @@ -193,9 +224,9 @@ static int set_sec_attr_from_acl(sc_card_t *card, sc_file_t *file) u8 sec_attr[SC_RTECP_SEC_ATTR_SIZE] = { 0 }; int r; - assert(card && card->ctx && file); - assert(!file->sec_attr && file->sec_attr_len == 0); - assert(1 + 6 < sizeof(sec_attr)); + if (!card || !card->ctx || !file + || file->sec_attr || file->sec_attr_len != 0) + return SC_ERROR_INVALID_ARGUMENTS; entry = sc_file_get_acl_entry(file, SC_AC_OP_DELETE); if (entry) @@ -252,36 +283,41 @@ static int set_sec_attr_from_acl(sc_card_t *card, sc_file_t *file) static int rtecp_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { - sc_file_t **file_out_copy, *file; - int r; + sc_file_t *file = NULL; + int r = SC_SUCCESS; + + if (!card || !card->ctx || !in_path) + return SC_ERROR_INVALID_ARGUMENTS; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert(card && card->ctx && in_path); switch (in_path->type) { case SC_PATH_TYPE_DF_NAME: case SC_PATH_TYPE_FROM_CURRENT: case SC_PATH_TYPE_PARENT: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); } - assert(iso_ops && iso_ops->select_file); - file_out_copy = file_out; - r = iso_ops->select_file(card, in_path, file_out_copy); - if (r || file_out_copy == NULL) + + // Card Rutoken ECP SC T0 doesn't support SELECT FILE without return a file info. + // So here we request a file and then assign/free it depending on file_out. + r = iso_ops->select_file(card, in_path, &file); + if (r != SC_SUCCESS) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); - assert(file_out_copy); - file = *file_out_copy; - assert(file); + if (file->sec_attr && file->sec_attr_len == SC_RTECP_SEC_ATTR_SIZE) set_acl_from_sec_attr(card, file); - else - r = SC_ERROR_UNKNOWN_DATA_RECEIVED; - if (r) - sc_file_free(file); else { - assert(file_out); - *file_out = file; + sc_file_free(file); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED); } + + if (file_out) + *file_out = file; + else + sc_file_free(file); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } @@ -292,7 +328,10 @@ static int rtecp_verify(sc_card_t *card, unsigned int type, int ref_qualifier, int r, send_logout = 0; (void)type; /* no warning */ - assert(card && card->ctx && data); + + if (!card || !card->ctx || !data) + return SC_ERROR_INVALID_ARGUMENTS; + for (;;) { sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, @@ -301,11 +340,11 @@ static int rtecp_verify(sc_card_t *card, unsigned int type, int ref_qualifier, apdu.data = data; apdu.datalen = data_len; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (send_logout++ == 0 && apdu.sw1 == 0x6F && apdu.sw2 == 0x86) { r = sc_logout(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Logout failed"); + LOG_TEST_RET(card->ctx, r, "Logout failed"); } else break; @@ -315,7 +354,7 @@ static int rtecp_verify(sc_card_t *card, unsigned int type, int ref_qualifier, /* Verification failed */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0, ref_qualifier); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r == SC_ERROR_PIN_CODE_INCORRECT && tries_left) @@ -328,15 +367,30 @@ static int rtecp_logout(sc_card_t *card) sc_apdu_t apdu; int r; - assert(card && card->ctx); + if (!card || !card->ctx) + return SC_ERROR_INVALID_ARGUMENTS; + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x40, 0, 0); apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } +static int rtecp_set_security_env( struct sc_card *card, + const struct sc_security_env *env, + int se_num) +{ + struct sc_security_env se_env; + if(!env) + return SC_ERROR_INVALID_ARGUMENTS; + + se_env= *env; + se_env.flags &= ~SC_SEC_ENV_FILE_REF_PRESENT; + return iso_ops->set_security_env(card, &se_env, se_num); +} + static int rtecp_cipher(sc_card_t *card, const u8 *data, size_t data_len, u8 *out, size_t out_len, int sign) { @@ -345,14 +399,16 @@ static int rtecp_cipher(sc_card_t *card, const u8 *data, size_t data_len, size_t i; int r; - assert(card && card->ctx && data && out); + if (!card || !card->ctx || !data || !out) + return SC_ERROR_INVALID_ARGUMENTS; + buf_out = malloc(out_len + 2); buf = malloc(data_len); if (!buf || !buf_out) { free(buf); free(buf_out); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } for (i = 0; i < data_len; ++i) @@ -372,19 +428,20 @@ static int rtecp_cipher(sc_card_t *card, const u8 *data, size_t data_len, apdu.flags |= SC_APDU_FLAGS_CHAINING; r = sc_transmit_apdu(card, &apdu); if (!sign) - { - assert(buf); sc_mem_clear(buf, data_len); - } - assert(buf); + free(buf); if (r) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed: %s\n", sc_strerror(r)); + sc_log(card->ctx, "APDU transmit failed: %s\n", sc_strerror(r)); else { if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { - assert(buf_out); + if (apdu.resplen > out_len) { + free(buf_out); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, + SC_ERROR_BUFFER_TOO_SMALL); + } for (i = 0; i < apdu.resplen; ++i) out[i] = buf_out[apdu.resplen - 1 - i]; r = (i > 0) ? (int)i : SC_ERROR_INTERNAL; @@ -393,11 +450,8 @@ static int rtecp_cipher(sc_card_t *card, const u8 *data, size_t data_len, r = sc_check_sw(card, apdu.sw1, apdu.sw2); } if (!sign) - { - assert(buf_out); sc_mem_clear(buf_out, out_len + 2); - } - assert(buf_out); + free(buf_out); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); @@ -408,7 +462,13 @@ static int rtecp_decipher(sc_card_t *card, { int r; - assert(card && card->ctx && data && out); + if (!card || !card->ctx || !data || !out) + return SC_ERROR_INVALID_ARGUMENTS; + + if (card->type == SC_CARD_TYPE_RUTOKEN_LITE + || card->type == SC_CARD_TYPE_RUTOKEN_LITE_SC) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); + /* decipher */ r = rtecp_cipher(card, data, data_len, out, out_len, 0); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); @@ -419,7 +479,13 @@ static int rtecp_compute_signature(sc_card_t *card, { int r; - assert(card && card->ctx && data && out); + if (!card || !card->ctx || !data || !out) + return SC_ERROR_INVALID_ARGUMENTS; + + if (card->type == SC_CARD_TYPE_RUTOKEN_LITE + || card->type == SC_CARD_TYPE_RUTOKEN_LITE_SC) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); + /* compute digital signature */ r = rtecp_cipher(card, data, data_len, out, out_len, 1); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); @@ -430,45 +496,56 @@ static int rtecp_change_reference_data(sc_card_t *card, unsigned int type, const u8 *newref, size_t newlen, int *tries_left) { sc_apdu_t apdu; - u8 rsf_length[2], *buf, *buf_end, *p; - size_t val_length, buf_length, max_transmit_length; - int transmits_num, r; + u8 rsf_length[2], *buf, *buf_end, *p; + size_t val_length, buf_length, max_transmit_length, transmits_num; + int r; + + if (!card || !card->ctx || !newref) + return SC_ERROR_INVALID_ARGUMENTS; - assert(card && card->ctx && newref); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "newlen = %"SC_FORMAT_LEN_SIZE_T"u\n", newlen); if (newlen > 0xFFFF) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); if (type == SC_AC_CHV && old && oldlen != 0) { r = sc_verify(card, type, ref_qualifier, old, oldlen, tries_left); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Verify old pin failed"); + LOG_TEST_RET(card->ctx, r, "Verify old pin failed"); } - + max_transmit_length = sc_get_max_send_size(card); - assert(max_transmit_length > 2); + if (max_transmit_length <= 2) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); /* * (2 + sizeof(rsf_length) + newlen) - total length of data we need to transfer, * (max_transmit_length - 2) - amount of useful data we can transfer in one transmit (2 bytes for 0xA5 tag) */ transmits_num = (2 + sizeof(rsf_length) + newlen) / (max_transmit_length - 2) + 1; /* buffer length = size of 0x80 TLV + size of RSF-file + (size of Tag and Length)*(number of APDUs) */ - buf_length = (2 + sizeof(rsf_length)) + newlen + 2*(transmits_num); + buf_length = (2 + sizeof(rsf_length)) + newlen + 2*(transmits_num); p = buf = (u8 *)malloc(buf_length); if (buf == NULL) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); - buf_end = buf + buf_length; + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + buf_end = buf + buf_length; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier); /* put 0x80 TLV */ rsf_length[0] = (newlen >> 8) & 0xFF; rsf_length[1] = newlen & 0xFF; - assert(buf_end - p >= (int)(2 + sizeof(rsf_length))); + + if (buf_end - p < (int)(2 + sizeof(rsf_length))) { + free(buf); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + sc_asn1_put_tag(0x80, rsf_length, sizeof(rsf_length), p, buf_end - p, &p); /* put 0xA5 TLVs (one or more); each transmit must begin with 0xA5 TLV */ while (newlen) { - assert(buf_end - p >= (int)(newlen + 2)); + if (buf_end - p < (int)(newlen + 2)) { + free(buf); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } if ((p - buf) % max_transmit_length + newlen + 2 > max_transmit_length) val_length = max_transmit_length - (p - buf) % max_transmit_length - 2; else @@ -476,7 +553,10 @@ static int rtecp_change_reference_data(sc_card_t *card, unsigned int type, /* not using sc_asn1_put_tag(...) because rtecp do not support asn1 properly (when val_length > 127) */ *p++ = 0xA5; *p++ = (u8)val_length; - assert(val_length <= newlen); + if (val_length > newlen) { + free(buf); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } memcpy(p, newref, val_length); p += val_length; newref += val_length; @@ -491,7 +571,7 @@ static int rtecp_change_reference_data(sc_card_t *card, unsigned int type, r = sc_transmit_apdu(card, &apdu); sc_mem_clear(buf, buf_length); free(buf); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } @@ -504,14 +584,16 @@ static int rtecp_reset_retry_counter(sc_card_t *card, unsigned int type, int r; (void)type, (void)puk, (void)puklen; /* no warning */ - assert(card && card->ctx); + + if (!card || !card->ctx) + return SC_ERROR_INVALID_ARGUMENTS; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2C, 0x03, ref_qualifier); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unblock card failed"); - + LOG_TEST_RET(card->ctx, r, "Unblock card failed"); + if (newref && newlen) { u8 tmp[2], buf[SC_MAX_APDU_BUFFER_SIZE]; u8 *p = buf; @@ -520,7 +602,7 @@ static int rtecp_reset_retry_counter(sc_card_t *card, unsigned int type, tmp[1] = newlen & 0xFF; sc_asn1_put_tag(0x80, tmp, sizeof(tmp), p, sizeof(buf) - (p - buf), &p); r = sc_asn1_put_tag(0xA5, newref, newlen, p, sizeof(buf) - (p - buf), &p); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Invalid new PIN length"); + LOG_TEST_RET(card->ctx, r, "Invalid new PIN length"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier); apdu.lc = p - buf; @@ -528,9 +610,9 @@ static int rtecp_reset_retry_counter(sc_card_t *card, unsigned int type, apdu.datalen = p - buf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Set PIN failed"); + LOG_TEST_RET(card->ctx, r, "Set PIN failed"); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); @@ -540,13 +622,15 @@ static int rtecp_create_file(sc_card_t *card, sc_file_t *file) { int r; - assert(card && card->ctx && file); + if (!card || !card->ctx || !file) + return SC_ERROR_INVALID_ARGUMENTS; + if (file->sec_attr_len == 0) { r = set_sec_attr_from_acl(card, file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Set sec_attr from ACL failed"); + LOG_TEST_RET(card->ctx, r, "Set sec_attr from ACL failed"); } - assert(iso_ops && iso_ops->create_file); + r = iso_ops->create_file(card, file); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } @@ -554,34 +638,36 @@ static int rtecp_create_file(sc_card_t *card, sc_file_t *file) static int rtecp_list_files(sc_card_t *card, u8 *buf, size_t buflen) { sc_apdu_t apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE], previd[2]; + u8 rbuf[SC_MAX_APDU_RESP_SIZE], previd[2]; const u8 *tag; size_t taglen, len = 0; int r; - assert(card && card->ctx && buf); + if (!card || !card->ctx || !buf) + return SC_ERROR_INVALID_ARGUMENTS; + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0, 0); for (;;) { apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); - apdu.le = 256; + apdu.le = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x6A && apdu.sw2 == 0x82) break; /* Next file not found */ r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, ""); + LOG_TEST_RET(card->ctx, r, ""); if (apdu.resplen <= 2) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_WRONG_LENGTH); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH); /* save first file(dir) ID */ tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2, 0x83, &taglen); if (!tag || taglen != sizeof(previd)) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); memcpy(previd, tag, sizeof(previd)); if (len + sizeof(previd) <= buflen) @@ -593,33 +679,41 @@ static int rtecp_list_files(sc_card_t *card, u8 *buf, size_t buflen) tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2, 0x82, &taglen); if (!tag || taglen != 2) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); if (tag[0] == 0x38) { /* Select parent DF of the current DF */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xA4, 0x03, 0); + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0x03, 0); + /* We should set le and resp buf to actually call Get Response for card on T0. */ + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, ""); + LOG_TEST_RET(card->ctx, r, ""); } sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0x02); apdu.lc = sizeof(previd); apdu.data = previd; apdu.datalen = sizeof(previd); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); } static int rtecp_card_ctl(sc_card_t *card, unsigned long request, void *data) { sc_apdu_t apdu; - u8 buf[SC_MAX_APDU_BUFFER_SIZE]; + u8 buf[512]; sc_rtecp_genkey_data_t *genkey_data = data; sc_serial_number_t *serial = data; int r; - assert(card && card->ctx); + const unsigned char rsa_prop[] = {0xA6, 0x06, 0x94, 0x04, 0x01, 0x00, 0x01, 0x00}; + + if (!card || !card->ctx) + return SC_ERROR_INVALID_ARGUMENTS; + switch (request) { case SC_CARDCTL_RTECP_INIT: @@ -632,7 +726,7 @@ static int rtecp_card_ctl(sc_card_t *card, unsigned long request, void *data) break; case SC_CARDCTL_GET_SERIALNR: if (!serial) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0x81); apdu.resp = buf; apdu.resplen = sizeof(buf); @@ -641,25 +735,31 @@ static int rtecp_card_ctl(sc_card_t *card, unsigned long request, void *data) break; case SC_CARDCTL_RTECP_GENERATE_KEY: if (!genkey_data) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x46, 0x80, genkey_data->key_id); apdu.resp = buf; apdu.resplen = sizeof(buf); apdu.le = 256; + if (genkey_data->type == SC_ALGORITHM_RSA) { + apdu.cse = SC_APDU_CASE_4_SHORT; + apdu.data = rsa_prop; + apdu.datalen = sizeof(rsa_prop); + apdu.lc = sizeof(rsa_prop); + } break; case SC_CARDCTL_LIFECYCLE_SET: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n", + sc_log(card->ctx, "%s\n", "SC_CARDCTL_LIFECYCLE_SET not supported"); /* no call sc_debug (SC_FUNC_RETURN) */ return SC_ERROR_NOT_SUPPORTED; default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "request = 0x%lx\n", request); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (!r && request == SC_CARDCTL_RTECP_GENERATE_KEY) { @@ -685,7 +785,7 @@ static int rtecp_card_ctl(sc_card_t *card, unsigned long request, void *data) } else if (!r && request == SC_CARDCTL_GET_SERIALNR) { - if (serial->len >= apdu.resplen) + if (apdu.resplen <= sizeof(serial->value)) { memcpy(serial->value, apdu.resp, apdu.resplen); serial->len = apdu.resplen; @@ -701,8 +801,10 @@ static int rtecp_construct_fci(sc_card_t *card, const sc_file_t *file, { u8 buf[64], *p = out; - assert(card && card->ctx && file && out && outlen); - assert(*outlen >= (size_t)(p - out) + 2); + if (!card || !card->ctx || !file || !out || !outlen + || (*outlen < (size_t)(p - out) + 2)) + return SC_ERROR_INVALID_ARGUMENTS; + *p++ = 0x6F; /* FCI template */ p++; /* for length */ @@ -714,7 +816,8 @@ static int rtecp_construct_fci(sc_card_t *card, const sc_file_t *file, /* 0x82 - File descriptor byte */ if (file->type_attr_len) { - assert(sizeof(buf) >= file->type_attr_len); + if (sizeof(buf) < file->type_attr_len) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); memcpy(buf, file->type_attr, file->type_attr_len); sc_asn1_put_tag(0x82, buf, file->type_attr_len, p, *outlen - (p - out), &p); @@ -731,7 +834,7 @@ static int rtecp_construct_fci(sc_card_t *card, const sc_file_t *file, break; case SC_FILE_TYPE_INTERNAL_EF: default: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } buf[1] = 0; sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p); @@ -743,14 +846,16 @@ static int rtecp_construct_fci(sc_card_t *card, const sc_file_t *file, if (file->prop_attr_len) { - assert(sizeof(buf) >= file->prop_attr_len); + if (sizeof(buf) < file->prop_attr_len) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); memcpy(buf, file->prop_attr, file->prop_attr_len); sc_asn1_put_tag(0x85, buf, file->prop_attr_len, p, *outlen - (p - out), &p); } if (file->sec_attr_len) { - assert(sizeof(buf) >= file->sec_attr_len); + if (sizeof(buf) < file->sec_attr_len) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); memcpy(buf, file->sec_attr, file->sec_attr_len); sc_asn1_put_tag(0x86, buf, file->sec_attr_len, p, *outlen - (p - out), &p); @@ -776,12 +881,12 @@ struct sc_card_driver * sc_get_rtecp_driver(void) rtecp_ops.append_record = NULL; rtecp_ops.update_record = NULL; rtecp_ops.select_file = rtecp_select_file; - rtecp_ops.get_response = NULL; + /* get_response */ /* get_challenge */ rtecp_ops.verify = rtecp_verify; rtecp_ops.logout = rtecp_logout; /* restore_security_env */ - /* set_security_env */ + rtecp_ops.set_security_env = rtecp_set_security_env; rtecp_ops.decipher = rtecp_decipher; rtecp_ops.compute_signature = rtecp_compute_signature; rtecp_ops.change_reference_data = rtecp_change_reference_data; @@ -794,7 +899,5 @@ struct sc_card_driver * sc_get_rtecp_driver(void) /* process_fci */ rtecp_ops.construct_fci = rtecp_construct_fci; rtecp_ops.pin_cmd = NULL; - return &rtecp_drv; } - diff --git a/src/libopensc/card-rutoken.c b/src/libopensc/card-rutoken.c index 6e2276a58b..21bc1c8dad 100644 --- a/src/libopensc/card-rutoken.c +++ b/src/libopensc/card-rutoken.c @@ -16,10 +16,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -84,18 +84,19 @@ static struct sc_card_driver rutoken_drv = { NULL, 0, NULL }; -static struct sc_atr_table rutoken_atrs[] = { - { "3b:6f:00:ff:00:56:72:75:54:6f:6b:6e:73:30:20:00:00:90:00", NULL, NULL, SC_CARD_TYPE_GENERIC_BASE, 0, NULL }, /* Aktiv Rutoken S */ - { "3b:6f:00:ff:00:56:75:61:54:6f:6b:6e:73:30:20:00:00:90:00", NULL, NULL, SC_CARD_TYPE_GENERIC_BASE, 0, NULL }, /* Aktiv uaToken S */ +static const struct sc_atr_table rutoken_atrs[] = { + { "3b:6f:00:ff:00:56:72:75:54:6f:6b:6e:73:30:20:00:00:90:00", NULL, NULL, SC_CARD_TYPE_RUTOKENS, 0, NULL }, /* Aktiv Rutoken S */ + { "3b:6f:00:ff:00:56:75:61:54:6f:6b:6e:73:30:20:00:00:90:00", NULL, NULL, SC_CARD_TYPE_RUTOKENS, 0, NULL }, /* Aktiv uaToken S */ { NULL, NULL, NULL, 0, 0, NULL } }; static int rutoken_finish(sc_card_t *card) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert(card->drv_data); + if (!card->drv_data) + return SC_ERROR_INTERNAL; free(card->drv_data); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int rutoken_match_card(sc_card_t *card) @@ -103,15 +104,15 @@ static int rutoken_match_card(sc_card_t *card) SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (_sc_match_atr(card, rutoken_atrs, &card->type) >= 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "ATR recognized as Rutoken\n"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 1); + sc_log(card->ctx, "ATR recognized as Rutoken\n"); + LOG_FUNC_RETURN(card->ctx, 1); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); + LOG_FUNC_RETURN(card->ctx, 0); } static int token_init(sc_card_t *card, const char *card_name) { - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); card->name = card_name; card->caps |= SC_CARD_CAP_RNG; @@ -132,61 +133,65 @@ static int rutoken_init(sc_card_t *card) ret = token_init(card, "uaToken S card"); else ret = token_init(card, "Rutoken S card"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); + + if (ret != SC_SUCCESS) { + ret = SC_ERROR_INVALID_CARD; + } + LOG_FUNC_RETURN(card->ctx, ret); } static const struct sc_card_error rutoken_errors[] = { - { 0x6300, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63C1, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed. One tries left"}, - { 0x63C2, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed. Two tries left"}, - { 0x63C3, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63C4, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63C5, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63C6, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63C7, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63C8, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63C9, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63CA, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63CB, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63CC, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63CD, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63CE, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x63CF, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x6300, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63C1, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed. One tries left"}, + { 0x63C2, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed. Two tries left"}, + { 0x63C3, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63C4, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63C5, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63C6, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63C7, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63C8, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63C9, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63CA, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63CB, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63CC, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63CD, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63CE, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, + { 0x63CF, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, - { 0x6400, SC_ERROR_CARD_CMD_FAILED, "Aborting"}, + { 0x6400, SC_ERROR_CARD_CMD_FAILED, "Aborting"}, - { 0x6500, SC_ERROR_MEMORY_FAILURE, "Memory failure"}, - { 0x6581, SC_ERROR_MEMORY_FAILURE, "Memory failure"}, + { 0x6500, SC_ERROR_MEMORY_FAILURE, "Memory failure"}, + { 0x6581, SC_ERROR_MEMORY_FAILURE, "Memory failure"}, - { 0x6700, SC_ERROR_WRONG_LENGTH, "Lc or Le invalid"}, + { 0x6700, SC_ERROR_WRONG_LENGTH, "Lc or Le invalid"}, - { 0x6883, SC_ERROR_CARD_CMD_FAILED, "The finishing command of a chain is expected"}, + { 0x6883, SC_ERROR_CARD_CMD_FAILED, "The finishing command of a chain is expected"}, - { 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED, "Required access right not granted"}, - { 0x6983, SC_ERROR_AUTH_METHOD_BLOCKED, "DO blocked"}, - { 0x6985, SC_ERROR_CARD_CMD_FAILED, "Command not allowed (unsuitable conditions)"}, - { 0x6986, SC_ERROR_INCORRECT_PARAMETERS,"No current EF selected"}, + { 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED, "Required access right not granted"}, + { 0x6983, SC_ERROR_AUTH_METHOD_BLOCKED, "DO blocked"}, + { 0x6985, SC_ERROR_CARD_CMD_FAILED, "Command not allowed (unsuitable conditions)"}, + { 0x6986, SC_ERROR_INCORRECT_PARAMETERS,"No current EF selected"}, - { 0x6A80, SC_ERROR_INCORRECT_PARAMETERS,"Invalid parameters in data field"}, - { 0x6A81, SC_ERROR_NOT_SUPPORTED, "Function/mode not supported"}, - { 0x6A82, SC_ERROR_FILE_NOT_FOUND, "File (DO) not found"}, - { 0x6A84, SC_ERROR_CARD_CMD_FAILED, "Not enough memory space in the token"}, - { 0x6A86, SC_ERROR_INCORRECT_PARAMETERS,"P1 or P2 invalid"}, - { 0x6A89, SC_ERROR_FILE_ALREADY_EXISTS, "File (DO) already exists"}, + { 0x6A80, SC_ERROR_INCORRECT_PARAMETERS,"Invalid parameters in data field"}, + { 0x6A81, SC_ERROR_NOT_SUPPORTED, "Function/mode not supported"}, + { 0x6A82, SC_ERROR_FILE_NOT_FOUND, "File (DO) not found"}, + { 0x6A84, SC_ERROR_CARD_CMD_FAILED, "Not enough memory space in the token"}, + { 0x6A86, SC_ERROR_INCORRECT_PARAMETERS,"P1 or P2 invalid"}, + { 0x6A89, SC_ERROR_FILE_ALREADY_EXISTS, "File (DO) already exists"}, - { 0x6B00, SC_ERROR_INCORRECT_PARAMETERS,"Out of maximum file length"}, + { 0x6B00, SC_ERROR_INCORRECT_PARAMETERS,"Out of maximum file length"}, - { 0x6C00, SC_ERROR_WRONG_LENGTH, "Le does not fit the data to be sent"}, + { 0x6C00, SC_ERROR_WRONG_LENGTH, "Le does not fit the data to be sent"}, - { 0x6D00, SC_ERROR_INS_NOT_SUPPORTED, "Ins invalid (not supported)"}, + { 0x6D00, SC_ERROR_INS_NOT_SUPPORTED, "Ins invalid (not supported)"}, /* Own class of an error*/ - { 0x6F01, SC_ERROR_CARD_CMD_FAILED, "Rutoken has the exchange protocol which is not supported by the USB-driver (newer, than in the driver)"}, - { 0x6F83, SC_ERROR_CARD_CMD_FAILED, "Infringement of the exchange protocol with Rutoken is revealed"}, - { 0x6F84, SC_ERROR_CARD_CMD_FAILED, "Rutoken is busy by processing of other command"}, - { 0x6F85, SC_ERROR_CARD_CMD_FAILED, "In the current folder the maximum quantity of file system objects is already created"}, - { 0x6F86, SC_ERROR_CARD_CMD_FAILED, "Invalid access right. Already login"}, + { 0x6F01, SC_ERROR_CARD_CMD_FAILED, "Rutoken has the exchange protocol which is not supported by the USB-driver (newer, than in the driver)"}, + { 0x6F83, SC_ERROR_CARD_CMD_FAILED, "Infringement of the exchange protocol with Rutoken is revealed"}, + { 0x6F84, SC_ERROR_CARD_CMD_FAILED, "Rutoken is busy by processing of other command"}, + { 0x6F85, SC_ERROR_CARD_CMD_FAILED, "In the current folder the maximum quantity of file system objects is already created"}, + { 0x6F86, SC_ERROR_CARD_CMD_FAILED, "Invalid access right. Already login"}, { 0x9000, SC_SUCCESS, NULL} }; @@ -198,12 +203,12 @@ static int rutoken_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) for (i = 0; i < sizeof(rutoken_errors)/sizeof(rutoken_errors[0]); ++i) { if (rutoken_errors[i].SWs == ((sw1 << 8) | sw2)) { if ( rutoken_errors[i].errorstr ) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n", rutoken_errors[i].errorstr); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "sw1 = %x, sw2 = %x", sw1, sw2); + sc_log(card->ctx, "%s\n", rutoken_errors[i].errorstr); + sc_log(card->ctx, "sw1 = %x, sw2 = %x", sw1, sw2); return rutoken_errors[i].errorno; } } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2); + sc_log(card->ctx, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2); return SC_ERROR_CARD_CMD_FAILED; } @@ -242,9 +247,11 @@ static int rutoken_list_files(sc_card_t *card, u8 *buf, size_t buflen) size_t taglen, len = 0; int ret; - assert(card && card->ctx); + if (!card || !card->ctx) + return SC_ERROR_INTERNAL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert(buf); + if (!buf) + return SC_ERROR_INTERNAL; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0, 0); for (;;) @@ -253,21 +260,21 @@ static int rutoken_list_files(sc_card_t *card, u8 *buf, size_t buflen) apdu.resplen = sizeof(rbuf); apdu.le = 256; ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); if (apdu.sw1 == 0x6A && apdu.sw2 == 0x82) break; /* Next file not found */ ret = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, ""); + LOG_TEST_RET(card->ctx, ret, ""); if (apdu.resplen <= 2) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_WRONG_LENGTH); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH); /* save first file(dir) ID */ tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2, 0x83, &taglen); if (!tag || taglen != sizeof(previd)) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); memcpy(previd, tag, sizeof(previd)); if (len + sizeof(previd) <= buflen) @@ -279,7 +286,7 @@ static int rutoken_list_files(sc_card_t *card, u8 *buf, size_t buflen) tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2, 0x82, &taglen); if (!tag || taglen != 2) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); if (tag[0] == 0x38) { /* Select parent DF of the current DF */ @@ -288,16 +295,16 @@ static int rutoken_list_files(sc_card_t *card, u8 *buf, size_t buflen) apdu.resplen = sizeof(rbuf); apdu.le = 256; ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, ""); + LOG_TEST_RET(card->ctx, ret, ""); } sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0x02); apdu.lc = sizeof(previd); apdu.data = previd; apdu.datalen = sizeof(previd); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, len); + LOG_FUNC_RETURN(card->ctx, (int)len); } static void set_acl_from_sec_attr(sc_card_t *card, sc_file_t *file) @@ -308,7 +315,7 @@ static void set_acl_from_sec_attr(sc_card_t *card, sc_file_t *file) SC_AC_NONE, SC_AC_KEY_REF_NONE); if (file->sec_attr[0] & 0x40) /* if AccessMode.6 */ { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SC_AC_OP_DELETE %i %i", + sc_log(card->ctx, "SC_AC_OP_DELETE %i %i", (int)(*(int8_t*)&file->sec_attr[1 +6]), file->sec_attr[1+7 +6*4]); sc_file_add_acl_entry(file, SC_AC_OP_DELETE, @@ -317,7 +324,7 @@ static void set_acl_from_sec_attr(sc_card_t *card, sc_file_t *file) } if (file->sec_attr[0] & 0x01) /* if AccessMode.0 */ { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, (file->type == SC_FILE_TYPE_DF) ? + sc_log(card->ctx, (file->type == SC_FILE_TYPE_DF) ? "SC_AC_OP_CREATE %i %i" : "SC_AC_OP_READ %i %i", (int)(*(int8_t*)&file->sec_attr[1 +0]), file->sec_attr[1+7 +0*4]); @@ -335,13 +342,13 @@ static void set_acl_from_sec_attr(sc_card_t *card, sc_file_t *file) else if (file->sec_attr[0] & 0x02) /* if AccessMode.1 */ { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SC_AC_OP_UPDATE %i %i", + sc_log(card->ctx, "SC_AC_OP_UPDATE %i %i", (int)(*(int8_t*)&file->sec_attr[1 +1]), file->sec_attr[1+7 +1*4]); sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, (int)(*(int8_t*)&file->sec_attr[1 +1]), file->sec_attr[1+7 +1*4]); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SC_AC_OP_WRITE %i %i", + sc_log(card->ctx, "SC_AC_OP_WRITE %i %i", (int)(*(int8_t*)&file->sec_attr[1 +1]), file->sec_attr[1+7 +1*4]); sc_file_add_acl_entry(file, SC_AC_OP_WRITE, @@ -358,13 +365,14 @@ static int rutoken_select_file(sc_card_t *card, u8 buf[SC_MAX_APDU_BUFFER_SIZE], pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; sc_file_t *file = NULL; size_t pathlen; - u8 t0, t1; int ret; - assert(card && card->ctx); + if (!card || !card->ctx) + return SC_ERROR_INTERNAL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert(in_path && sizeof(pathbuf) >= in_path->len); + if (in_path == NULL || sizeof(pathbuf) < in_path->len) + return SC_ERROR_INTERNAL; memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; @@ -374,7 +382,7 @@ static int rutoken_select_file(sc_card_t *card, { case SC_PATH_TYPE_FILE_ID: if (pathlen != 2) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); break; case SC_PATH_TYPE_PATH: if (pathlen >= 2 && memcmp(path, "\x3F\x00", 2) == 0) @@ -389,9 +397,9 @@ static int rutoken_select_file(sc_card_t *card, case SC_PATH_TYPE_DF_NAME: case SC_PATH_TYPE_FROM_CURRENT: case SC_PATH_TYPE_PARENT: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); default: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } swap_pair(path, pathlen); apdu.lc = pathlen; @@ -403,7 +411,7 @@ static int rutoken_select_file(sc_card_t *card, apdu.le = 256; ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); if (file_out == NULL) { if (apdu.sw1 == 0x61) @@ -411,32 +419,23 @@ static int rutoken_select_file(sc_card_t *card, SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } ret = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, ""); + LOG_TEST_RET(card->ctx, ret, ""); if (apdu.resplen > 0 && apdu.resp[0] != 0x62) /* Tag 0x62 - FCP */ - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); file = sc_file_new(); if (file == NULL) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->path = *in_path; if (card->ops->process_fci == NULL) { sc_file_free(file); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } if (apdu.resplen > 1 && apdu.resplen >= (size_t)apdu.resp[1] + 2) { ret = card->ops->process_fci(card, file, apdu.resp+2, apdu.resp[1]); - if (ret == SC_SUCCESS) - { - t0 = file->id & 0xFF; - t1 = (file->id >> 8) & 0xFF; - file->id = (t0 << 8) | t1; - t0 = file->size & 0xFF; - t1 = (file->size >> 8) & 0xFF; - file->size = (t0 << 8) | t1; - } } if (file->sec_attr && file->sec_attr_len == sizeof(sc_SecAttrV2_t)) set_acl_from_sec_attr(card, file); @@ -446,10 +445,36 @@ static int rutoken_select_file(sc_card_t *card, sc_file_free(file); else { - assert(file_out); *file_out = file; } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); + LOG_FUNC_RETURN(card->ctx, ret); +} + +static int rutoken_process_fci(struct sc_card *card, sc_file_t *file, + const unsigned char *buf, size_t buflen) +{ + size_t taglen; + int ret; + const unsigned char *tag; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + ret = iso_ops->process_fci(card, file, buf, buflen); + if (ret == SC_SUCCESS) + { + /* Rutoken S returns buffers in little-endian. */ + /* Set correct file id. */ + file->id = ((file->id & 0xFF) << 8) | ((file->id >> 8) & 0xFF); + sc_log(card->ctx, " file identifier: 0x%04X", file->id); + /* Determine file size. */ + tag = sc_asn1_find_tag(card->ctx, buf, buflen, 0x80, &taglen); + /* Rutoken S always returns 2 bytes. */ + if (tag != NULL && taglen == 2) + { + file->size = (tag[1] << 8) | tag[0]; + sc_log(card->ctx, " bytes in file: %"SC_FORMAT_LEN_SIZE_T"u", file->size); + } + } + LOG_FUNC_RETURN(card->ctx, ret); } static int rutoken_construct_fci(sc_card_t *card, const sc_file_t *file, @@ -457,11 +482,14 @@ static int rutoken_construct_fci(sc_card_t *card, const sc_file_t *file, { u8 buf[64], *p = out; - assert(card && card->ctx); - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + if (!card || !card->ctx) + return SC_ERROR_INTERNAL; + LOG_FUNC_CALLED(card->ctx); - assert(file && out && outlen); - assert(*outlen >= (size_t)(p - out) + 2); + if (!file || !out || !outlen) + return SC_ERROR_INTERNAL; + if (*outlen < (size_t)(p - out) + 2) + return SC_ERROR_INTERNAL; *p++ = 0x62; /* FCP template */ p++; /* for length */ @@ -473,7 +501,8 @@ static int rutoken_construct_fci(sc_card_t *card, const sc_file_t *file, /* 0x82 - File descriptor byte */ if (file->type_attr_len) { - assert(sizeof(buf) >= file->type_attr_len); + if (sizeof(buf) < file->type_attr_len) + return SC_ERROR_INTERNAL; memcpy(buf, file->type_attr, file->type_attr_len); sc_asn1_put_tag(0x82, buf, file->type_attr_len, p, *outlen - (p - out), &p); @@ -490,7 +519,7 @@ static int rutoken_construct_fci(sc_card_t *card, const sc_file_t *file, break; case SC_FILE_TYPE_INTERNAL_EF: default: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } buf[1] = 0; sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p); @@ -502,14 +531,16 @@ static int rutoken_construct_fci(sc_card_t *card, const sc_file_t *file, if (file->prop_attr_len) { - assert(sizeof(buf) >= file->prop_attr_len); + if (sizeof(buf) < file->prop_attr_len) + return SC_ERROR_INTERNAL; memcpy(buf, file->prop_attr, file->prop_attr_len); sc_asn1_put_tag(0x85, buf, file->prop_attr_len, p, *outlen - (p - out), &p); } if (file->sec_attr_len) { - assert(sizeof(buf) >= file->sec_attr_len); + if (sizeof(buf) < file->sec_attr_len) + return SC_ERROR_INTERNAL; memcpy(buf, file->sec_attr, file->sec_attr_len); sc_asn1_put_tag(0x86, buf, file->sec_attr_len, p, *outlen - (p - out), &p); @@ -527,7 +558,7 @@ static int set_sec_attr_from_acl(sc_card_t *card, sc_file_t *file) sc_SecAttrV2_t attr = { 0 }; int ret = SC_SUCCESS; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); if (file->type == SC_FILE_TYPE_DF) { @@ -539,7 +570,7 @@ static int set_sec_attr_from_acl(sc_card_t *card, sc_file_t *file) conv_attr = arr_convert_attr_ef; n_conv_attr = sizeof(arr_convert_attr_ef)/sizeof(arr_convert_attr_ef[0]); } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "file->type = %i", file->type); + sc_log(card->ctx, "file->type = %i", file->type); for (i = 0; i < n_conv_attr; ++i) { @@ -550,20 +581,20 @@ static int set_sec_attr_from_acl(sc_card_t *card, sc_file_t *file) { /* AccessMode.[conv_attr[i].sec_attr_pos] */ attr[0] |= 1 << conv_attr[i].sec_attr_pos; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "AccessMode.%"SC_FORMAT_LEN_SIZE_T"u, attr[0]=0x%x", conv_attr[i].sec_attr_pos, attr[0]); attr[1 + conv_attr[i].sec_attr_pos] = (u8)entry->method; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "method %u", (u8)entry->method); + sc_log(card->ctx, "method %u", (u8)entry->method); if (entry->method == SC_AC_CHV) { attr[1+7 + conv_attr[i].sec_attr_pos*4] = (u8)entry->key_ref; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "key_ref %u", (u8)entry->key_ref); + sc_log(card->ctx, "key_ref %u", (u8)entry->key_ref); } } else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "ACL (%u) not set, set default sec_attr", + sc_log(card->ctx, "ACL (%u) not set, set default sec_attr", conv_attr[i].ac_op); memcpy(attr, default_sec_attr, sizeof(attr)); break; @@ -577,18 +608,21 @@ static int rutoken_create_file(sc_card_t *card, sc_file_t *file) { int ret; - assert(card && card->ctx); + if (!card || !card->ctx) + return SC_ERROR_INTERNAL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - assert(file); + if (!file) + return SC_ERROR_INTERNAL; if (file->sec_attr_len == 0) { ret = set_sec_attr_from_acl(card, file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Set sec_attr from ACL failed"); + LOG_TEST_RET(card->ctx, ret, "Set sec_attr from ACL failed"); } - assert(iso_ops && iso_ops->create_file); + if (!iso_ops || !iso_ops->create_file) + return SC_ERROR_INTERNAL; ret = iso_ops->create_file(card, file); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); + LOG_FUNC_RETURN(card->ctx, ret); } static int rutoken_delete_file(sc_card_t *card, const sc_path_t *path) @@ -597,12 +631,12 @@ static int rutoken_delete_file(sc_card_t *card, const sc_path_t *path) sc_apdu_t apdu; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - if (!path || path->type != SC_PATH_TYPE_FILE_ID || (path->len != 0 && path->len != 2)) + if (!path || path->type != SC_PATH_TYPE_FILE_ID || (path->len != 0 && path->len != 2)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type has to be SC_PATH_TYPE_FILE_ID\n"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + sc_log(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } - if (path->len == sizeof(sbuf)) + if (path->len == sizeof(sbuf)) { sbuf[1] = path->value[0]; sbuf[0] = path->value[1]; @@ -613,8 +647,8 @@ static int rutoken_delete_file(sc_card_t *card, const sc_path_t *path) } else /* No file ID given: means currently selected file */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, 0x00, 0x00); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_transmit_apdu(card, &apdu), "APDU transmit failed"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2)); + LOG_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed"); + LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int rutoken_verify(sc_card_t *card, unsigned int type, int ref_qualifier, @@ -637,9 +671,9 @@ static int rutoken_verify(sc_card_t *card, unsigned int type, int ref_qualifier, sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x40, 0x00, 0x00); apdu.cla = 0x80; ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Reset access rights failed"); + LOG_TEST_RET(card->ctx, ret, "Reset access rights failed"); } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0x00, ref_qualifier); @@ -647,18 +681,18 @@ static int rutoken_verify(sc_card_t *card, unsigned int type, int ref_qualifier, apdu.datalen = data_len; apdu.data = data; ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); if (ret == SC_ERROR_PIN_CODE_INCORRECT && tries_left) { sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, ref_qualifier); ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); if (ret == SC_ERROR_PIN_CODE_INCORRECT) *tries_left = (int)(apdu.sw2 & 0x0f); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); + LOG_FUNC_RETURN(card->ctx, ret); } static int rutoken_logout(sc_card_t *card) @@ -670,14 +704,14 @@ static int rutoken_logout(sc_card_t *card) SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_format_path("3F00", &path); ret = rutoken_select_file(card, &path, NULL); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Select MF failed"); + LOG_TEST_RET(card->ctx, ret, "Select MF failed"); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x40, 0x00, 0x00); apdu.cla = 0x80; ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); + LOG_FUNC_RETURN(card->ctx, ret); } static int rutoken_change_reference_data(sc_card_t *card, unsigned int type, @@ -691,16 +725,16 @@ static int rutoken_change_reference_data(sc_card_t *card, unsigned int type, if (old && oldlen) { ret = rutoken_verify(card, type, ref_qualifier, old, oldlen, tries_left); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Invalid 'old' pass"); + LOG_TEST_RET(card->ctx, ret, "Invalid 'old' pass"); } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier); apdu.lc = newlen; apdu.datalen = newlen; apdu.data = newref; ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); + LOG_FUNC_RETURN(card->ctx, ret); } static int rutoken_reset_retry_counter(sc_card_t *card, unsigned int type, @@ -718,15 +752,15 @@ static int rutoken_reset_retry_counter(sc_card_t *card, unsigned int type, if (puk && puklen) { ret = rutoken_verify(card, type, ref_qualifier, puk, puklen, &left); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Tries left: %i\n", left); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Invalid 'puk' pass"); + sc_log(card->ctx, "Tries left: %i\n", left); + LOG_TEST_RET(card->ctx, ret, "Invalid 'puk' pass"); } #endif sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2c, 0x03, ref_qualifier); ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); + LOG_FUNC_RETURN(card->ctx, ret); } static int rutoken_restore_security_env(sc_card_t *card, int se_num) @@ -737,13 +771,13 @@ static int rutoken_restore_security_env(sc_card_t *card, int se_num) SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 3, se_num); ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); + LOG_FUNC_RETURN(card->ctx, ret); } -static int rutoken_set_security_env(sc_card_t *card, - const sc_security_env_t *env, +static int rutoken_set_security_env(sc_card_t *card, + const sc_security_env_t *env, int se_num) { sc_apdu_t apdu; @@ -753,18 +787,18 @@ static int rutoken_set_security_env(sc_card_t *card, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (!env) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); senv = (auth_senv_t*)card->drv_data; if (!senv) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); if (env->algorithm != SC_ALGORITHM_GOST) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); senv->algorithm = SC_ALGORITHM_GOST; if (env->key_ref_len != 1) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No or invalid key reference\n"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + sc_log(card->ctx, "No or invalid key reference\n"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } data[2] = env->key_ref[0]; /* select component */ @@ -783,20 +817,21 @@ static int rutoken_set_security_env(sc_card_t *card, apdu.p2 = 0xAA; break; default: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } /* set SE */ ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); + LOG_FUNC_RETURN(card->ctx, ret); } static void rutoken_set_do_hdr(u8 *data, size_t *data_len, sc_DOHdrV2_t *hdr) { u8 buf[64], *p = data; - assert(hdr && data && data_len); + if (!hdr || !data || !data_len) + return; /* 0x80 - Number of data bytes in the file, excluding structural information */ buf[1] = (hdr->wDOBodyLen >> 8) & 0xFF; @@ -814,11 +849,12 @@ static void rutoken_set_do_hdr(u8 *data, size_t *data_len, sc_DOHdrV2_t *hdr) buf[2] = hdr->OP.byObjectTry; sc_asn1_put_tag(0x85, buf, 3, p, *data_len - (p - data), &p); - assert(sizeof(buf) >= sizeof(hdr->SA_V2)); + static_assert(sizeof(buf) >= sizeof(hdr->SA_V2), "internal error"); memcpy(buf, hdr->SA_V2, sizeof(hdr->SA_V2)); sc_asn1_put_tag(0x86, buf, sizeof(hdr->SA_V2), p, *data_len - (p - data), &p); - assert(*data_len >= (size_t)(p - data)); + if (*data_len < (size_t)(p - data)) + return; *data_len = p - data; } @@ -829,13 +865,13 @@ static int rutoken_key_gen(sc_card_t *card, sc_DOHdrV2_t *pHdr) sc_apdu_t apdu; int ret; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); if ( (pHdr->wDOBodyLen != SC_RUTOKEN_DEF_LEN_DO_GOST) || (pHdr->OTID.byObjectType != SC_RUTOKEN_TYPE_KEY) || (pHdr->OP.byObjectFlags & SC_RUTOKEN_FLAGS_COMPACT_DO) || (pHdr->OP.byObjectFlags & SC_RUTOKEN_FLAGS_FULL_OPEN_DO) || - (pHdr->OTID.byObjectID < SC_RUTOKEN_DO_ALL_MIN_ID) || + (pHdr->OTID.byObjectID < SC_RUTOKEN_DO_ALL_MIN_ID) || (pHdr->OTID.byObjectID > SC_RUTOKEN_DO_NOCHV_MAX_ID_V2) ) { @@ -849,7 +885,7 @@ static int rutoken_key_gen(sc_card_t *card, sc_DOHdrV2_t *pHdr) apdu.data = data; apdu.datalen = apdu.lc = data_len; ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, ret); @@ -862,16 +898,16 @@ static int rutoken_create_do(sc_card_t *card, sc_DO_V2_t * pDO) sc_apdu_t apdu; int ret; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); if ( ((pDO->HDR.OTID.byObjectType & SC_RUTOKEN_TYPE_CHV) && (pDO->HDR.OTID.byObjectID != SC_RUTOKEN_DEF_ID_GCHV_USER) && (pDO->HDR.OTID.byObjectID != SC_RUTOKEN_DEF_ID_GCHV_ADMIN)) || - ((pDO->HDR.OTID.byObjectType == SC_RUTOKEN_ALLTYPE_GOST) && + ((pDO->HDR.OTID.byObjectType == SC_RUTOKEN_ALLTYPE_GOST) && (pDO->HDR.wDOBodyLen != SC_RUTOKEN_DEF_LEN_DO_GOST)) || ((pDO->HDR.OTID.byObjectType == SC_RUTOKEN_ALLTYPE_SE) && (pDO->HDR.wDOBodyLen != SC_RUTOKEN_DEF_LEN_DO_SE)) || - (pDO->HDR.OTID.byObjectID < SC_RUTOKEN_DO_ALL_MIN_ID) || + (pDO->HDR.OTID.byObjectID < SC_RUTOKEN_DO_ALL_MIN_ID) || (pDO->HDR.OTID.byObjectID > SC_RUTOKEN_DO_NOCHV_MAX_ID_V2) || ((pDO->HDR.OP.byObjectFlags & SC_RUTOKEN_FLAGS_COMPACT_DO) && (pDO->HDR.wDOBodyLen > SC_RUTOKEN_COMPACT_DO_MAX_LEN)) || @@ -883,7 +919,8 @@ static int rutoken_create_do(sc_card_t *card, sc_DO_V2_t * pDO) else { rutoken_set_do_hdr(data, &data_len, &pDO->HDR); - assert(sizeof(data) >= data_len + pDO->HDR.wDOBodyLen + 2); + if (sizeof(data) < data_len + pDO->HDR.wDOBodyLen + 2) + return SC_ERROR_INTERNAL; ret = sc_asn1_put_tag(0xA5, pDO->abyDOBody, pDO->HDR.wDOBodyLen, data + data_len, sizeof(data) - data_len, NULL); if (ret == SC_SUCCESS) @@ -892,7 +929,7 @@ static int rutoken_create_do(sc_card_t *card, sc_DO_V2_t * pDO) apdu.data = data; apdu.datalen = apdu.lc = data_len; ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, ret); @@ -904,9 +941,9 @@ static int rutoken_get_do_info(sc_card_t *card, sc_DO_INFO_t * pInfo) sc_apdu_t apdu; int ret; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); if ((pInfo->SelType != select_first) && - ((pInfo->DoId < SC_RUTOKEN_DO_ALL_MIN_ID) || + ((pInfo->DoId < SC_RUTOKEN_DO_ALL_MIN_ID) || (pInfo->DoId > SC_RUTOKEN_DO_NOCHV_MAX_ID_V2))) { ret = SC_ERROR_INVALID_ARGUMENTS; @@ -925,7 +962,8 @@ static int rutoken_get_do_info(sc_card_t *card, sc_DO_INFO_t * pInfo) apdu.cse = SC_APDU_CASE_2_SHORT; break; case select_next: - apdu.p2 = 0x02; + apdu.p2 = 0x02; + /* fall through */ case select_by_id: data[0] = pInfo->DoId; apdu.data = data; @@ -937,7 +975,7 @@ static int rutoken_get_do_info(sc_card_t *card, sc_DO_INFO_t * pInfo) break; } ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, ret); @@ -949,8 +987,8 @@ static int rutoken_delete_do(sc_card_t *card, u8 *pId) sc_apdu_t apdu; int ret; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); - if ((*pId < SC_RUTOKEN_DO_ALL_MIN_ID) || + LOG_FUNC_CALLED(card->ctx); + if ((*pId < SC_RUTOKEN_DO_ALL_MIN_ID) || (*pId > SC_RUTOKEN_DO_NOCHV_MAX_ID_V2)) { ret = SC_ERROR_INVALID_ARGUMENTS; @@ -963,7 +1001,7 @@ static int rutoken_delete_do(sc_card_t *card, u8 *pId) apdu.datalen = sizeof(data); apdu.lc = sizeof(data); ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, ret); @@ -979,8 +1017,8 @@ static int rutoken_cipher_p(sc_card_t *card, const u8 * crgram, size_t crgram_le int ret; sc_apdu_t apdu; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + LOG_FUNC_CALLED(card->ctx); + sc_log(card->ctx, ": crgram_len %"SC_FORMAT_LEN_SIZE_T"u; outlen %"SC_FORMAT_LEN_SIZE_T"u", crgram_len, outlen); @@ -1005,7 +1043,7 @@ static int rutoken_cipher_p(sc_card_t *card, const u8 * crgram, size_t crgram_le apdu.resp = buf; ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); if (ret == SC_SUCCESS) { @@ -1025,7 +1063,7 @@ static int rutoken_cipher_p(sc_card_t *card, const u8 * crgram, size_t crgram_le } } } while (ret == SC_SUCCESS && crgram_len != 0); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "len out cipher %"SC_FORMAT_LEN_SIZE_T"u\n", outlen - outlen_tail); if (ret == SC_SUCCESS) @@ -1035,16 +1073,16 @@ static int rutoken_cipher_p(sc_card_t *card, const u8 * crgram, size_t crgram_le /* Launcher for cipher */ -static int rutoken_cipher_gost(sc_card_t *card, +static int rutoken_cipher_gost(sc_card_t *card, struct sc_rutoken_decipherinfo *ptr, char is_encipher) { int ret; if (is_encipher) - ret = rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, + ret = rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, ptr->outbuf, ptr->outlen, 0x86, 0x80, 0); else - ret = rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, + ret = rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, ptr->outbuf, ptr->outlen, 0x80, 0x86, 1); if (ret > 0) { @@ -1057,8 +1095,8 @@ static int rutoken_cipher_gost(sc_card_t *card, } -static int rutoken_compute_mac_gost(sc_card_t *card, - const u8 *in, size_t ilen, +static int rutoken_compute_mac_gost(sc_card_t *card, + const u8 *in, size_t ilen, u8 *out, size_t olen) { const size_t signing_chunk = 248; @@ -1066,7 +1104,7 @@ static int rutoken_compute_mac_gost(sc_card_t *card, int ret; sc_apdu_t apdu; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); if (!in || !out || olen != 4 || ilen == 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); do @@ -1091,14 +1129,14 @@ static int rutoken_compute_mac_gost(sc_card_t *card, else apdu.cla = 0x10; ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); } while (ret == SC_SUCCESS && ilen != 0); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, ret); } -static int rutoken_compute_signature(struct sc_card *card, - const u8 * data, size_t datalen, +static int rutoken_compute_signature(struct sc_card *card, + const u8 * data, size_t datalen, u8 * out, size_t outlen) { int ret; @@ -1106,42 +1144,34 @@ static int rutoken_compute_signature(struct sc_card *card, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (!senv) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); if (senv->algorithm == SC_ALGORITHM_GOST) ret = rutoken_compute_mac_gost(card, data, datalen, out, outlen); else ret = SC_ERROR_NOT_SUPPORTED; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); + LOG_FUNC_RETURN(card->ctx, ret); } -static int rutoken_get_challenge(sc_card_t *card, u8 *rnd, size_t count) +static int rutoken_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { - sc_apdu_t apdu; - u8 rbuf[32]; - size_t n; - int ret = SC_SUCCESS; /* if count == 0 */ + unsigned char rbuf[32]; + size_t out_len; + int r; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0x00, 0x00); - apdu.le = sizeof(rbuf); - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); + LOG_FUNC_CALLED(card->ctx); - while (count > 0) - { - ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); - ret = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "Get challenge failed"); - if (apdu.resplen != sizeof(rbuf)) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN); - n = count < sizeof(rbuf) ? count : sizeof(rbuf); - memcpy(rnd, rbuf, n); - count -= n; - rnd += n; + r = iso_ops->get_challenge(card, rbuf, sizeof rbuf); + LOG_TEST_RET(card->ctx, r, "GET CHALLENGE cmd failed"); + + if (len < (size_t) r) { + out_len = len; + } else { + out_len = (size_t) r; } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); + memcpy(rnd, rbuf, out_len); + + LOG_FUNC_RETURN(card->ctx, (int)out_len); } static int rutoken_get_serial(sc_card_t *card, sc_serial_number_t *serial) @@ -1149,13 +1179,13 @@ static int rutoken_get_serial(sc_card_t *card, sc_serial_number_t *serial) sc_apdu_t apdu; int ret; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0x81); apdu.resp = serial->value; apdu.resplen = sizeof(serial->value); apdu.le = 4; ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); serial->len = apdu.resplen; swap_four(serial->value, serial->len); @@ -1168,13 +1198,13 @@ static int rutoken_get_info(sc_card_t *card, void *buff) u8 rbuf[8]; int ret; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0x89); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = sizeof(rbuf); ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); if (ret == SC_SUCCESS) memcpy(buff, apdu.resp, apdu.resplen); @@ -1186,11 +1216,11 @@ static int rutoken_format(sc_card_t *card, int apdu_ins) int ret; sc_apdu_t apdu; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, apdu_ins, 0x00, 0x00); apdu.cla = 0x80; ret = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, ret); } @@ -1207,7 +1237,7 @@ static int rutoken_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) if (ret == SC_SUCCESS) { - switch (cmd) + switch (cmd) { case SC_CARDCTL_RUTOKEN_CREATE_DO: ret = rutoken_create_do(card, ptr); @@ -1245,12 +1275,12 @@ static int rutoken_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) ret = rutoken_format(card, 0x7b); /* APDU: FORMAT END */ break; default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cmd = %lu", cmd); + sc_log(card->ctx, "cmd = %lu", cmd); ret = SC_ERROR_NOT_SUPPORTED; break; } } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, ret); + LOG_FUNC_RETURN(card->ctx, ret); } static struct sc_card_driver* get_rutoken_driver(void) @@ -1285,7 +1315,7 @@ static struct sc_card_driver* get_rutoken_driver(void) rutoken_ops.list_files = rutoken_list_files; rutoken_ops.check_sw = rutoken_check_sw; rutoken_ops.card_ctl = rutoken_card_ctl; - /* process_fci */ + rutoken_ops.process_fci = rutoken_process_fci; rutoken_ops.construct_fci = rutoken_construct_fci; rutoken_ops.pin_cmd = NULL; diff --git a/src/libopensc/card-sc-hsm.c b/src/libopensc/card-sc-hsm.c index cfa084a060..b22ca52174 100644 --- a/src/libopensc/card-sc-hsm.c +++ b/src/libopensc/card-sc-hsm.c @@ -4,6 +4,7 @@ * Driver for the SmartCard-HSM, a light-weight hardware security module * * Copyright (C) 2012 Andreas Schwier, CardContact, Minden, Germany, and others + * Copyright (C) 2018-2019 GSMK - Gesellschaft für Sichere Mobile Kommunikation mbH * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,10 +18,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -35,6 +36,14 @@ #include "card-sc-hsm.h" +#if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) +#include "sm/sm-eac.h" +#include +#include +#include +#include +#endif + /* Static reference to ISO driver */ static const struct sc_card_operations *iso_ops = NULL; @@ -60,70 +69,73 @@ struct sc_aid sc_hsm_aid = { { 0xE8,0x2B,0x06,0x01,0x04,0x01,0x81,0xC3,0x1F,0x02 /* Known ATRs for SmartCard-HSMs */ -static struct sc_atr_table sc_hsm_atrs[] = { +const struct sc_atr_table sc_hsm_atrs[] = { /* standard version */ {"3B:FE:18:00:00:81:31:FE:45:80:31:81:54:48:53:4D:31:73:80:21:40:81:07:FA", NULL, NULL, SC_CARD_TYPE_SC_HSM, 0, NULL}, {"3B:8E:80:01:80:31:81:54:48:53:4D:31:73:80:21:40:81:07:18", NULL, NULL, SC_CARD_TYPE_SC_HSM, 0, NULL}, + {"3B:DE:18:FF:81:91:FE:1F:C3:80:31:81:54:48:53:4D:31:73:80:21:40:81:07:1C", NULL, NULL, SC_CARD_TYPE_SC_HSM, 0, NULL}, + {"3B:DE:96:FF:81:91:FE:1F:C3:80:31:81:54:48:53:4D:31:73:80:21:40:81:07:92", NULL, NULL, SC_CARD_TYPE_SC_HSM, 0, NULL}, + {"3B:80:80:01:01", NULL, NULL, SC_CARD_TYPE_SC_HSM_SOC, 0, NULL}, // SoC Sample Card { "3B:84:80:01:47:6f:49:44:00", "FF:FF:FF:FF:FF:FF:FF:FF:00", - "GoID", SC_CARD_TYPE_SC_HSM_SOC, 0, NULL + "GoID", SC_CARD_TYPE_SC_HSM_GOID, 0, NULL }, { "3B:85:80:01:47:6f:49:44:00:00", "FF:FF:FF:FF:FF:FF:FF:FF:00:00", - "GoID", SC_CARD_TYPE_SC_HSM_SOC, 0, NULL + "GoID", SC_CARD_TYPE_SC_HSM_GOID, 0, NULL }, { "3B:86:80:01:47:6f:49:44:00:00:00", "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00", - "GoID", SC_CARD_TYPE_SC_HSM_SOC, 0, NULL + "GoID", SC_CARD_TYPE_SC_HSM_GOID, 0, NULL }, { "3B:87:80:01:47:6f:49:44:00:00:00:00", "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00", - "GoID", SC_CARD_TYPE_SC_HSM_SOC, 0, NULL + "GoID", SC_CARD_TYPE_SC_HSM_GOID, 0, NULL }, { "3B:88:80:01:47:6f:49:44:00:00:00:00:00", "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00", - "GoID", SC_CARD_TYPE_SC_HSM_SOC, 0, NULL + "GoID", SC_CARD_TYPE_SC_HSM_GOID, 0, NULL }, { "3B:89:80:01:47:6f:49:44:00:00:00:00:00:00", "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00", - "GoID", SC_CARD_TYPE_SC_HSM_SOC, 0, NULL + "GoID", SC_CARD_TYPE_SC_HSM_GOID, 0, NULL }, { "3B:8a:80:01:47:6f:49:44:00:00:00:00:00:00:00", "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00", - "GoID", SC_CARD_TYPE_SC_HSM_SOC, 0, NULL + "GoID", SC_CARD_TYPE_SC_HSM_GOID, 0, NULL }, { "3B:8b:80:01:47:6f:49:44:00:00:00:00:00:00:00:00", "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00:00", - "GoID", SC_CARD_TYPE_SC_HSM_SOC, 0, NULL + "GoID", SC_CARD_TYPE_SC_HSM_GOID, 0, NULL }, { "3B:8c:80:01:47:6f:49:44:00:00:00:00:00:00:00:00:00", "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00:00:00", - "GoID", SC_CARD_TYPE_SC_HSM_SOC, 0, NULL + "GoID", SC_CARD_TYPE_SC_HSM_GOID, 0, NULL }, { "3B:8d:80:01:47:6f:49:44:00:00:00:00:00:00:00:00:00:00", "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00:00:00:00", - "GoID", SC_CARD_TYPE_SC_HSM_SOC, 0, NULL + "GoID", SC_CARD_TYPE_SC_HSM_GOID, 0, NULL }, { "3B:8e:80:01:47:6f:49:44:00:00:00:00:00:00:00:00:00:00:00", "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00:00:00:00:00", - "GoID", SC_CARD_TYPE_SC_HSM_SOC, 0, NULL + "GoID", SC_CARD_TYPE_SC_HSM_GOID, 0, NULL }, { "3B:8f:80:01:47:6f:49:44:00:00:00:00:00:00:00:00:00:00:00:00", "FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00:00:00:00:00:00", - "GoID", SC_CARD_TYPE_SC_HSM_SOC, 0, NULL + "GoID", SC_CARD_TYPE_SC_HSM_GOID, 0, NULL }, {NULL, NULL, NULL, 0, 0, NULL} }; @@ -138,12 +150,12 @@ static int sc_hsm_select_file_ex(sc_card_t *card, sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; sc_file_t *file = NULL; sc_path_t cpath; + size_t card_max_recv_size = card->max_recv_size; + size_t reader_max_recv_size = card->reader->max_recv_size; if (file_out == NULL) { // Versions before 0.16 of the SmartCard-HSM do not support P2='0C' rv = sc_hsm_select_file_ex(card, in_path, forceselect, &file); - if (file != NULL) { - sc_file_free(file); - } + sc_file_free(file); return rv; } @@ -173,13 +185,15 @@ static int sc_hsm_select_file_ex(sc_card_t *card, && in_path->aid.len == sc_hsm_aid.len && !memcmp(in_path->aid.value, sc_hsm_aid.value, sc_hsm_aid.len))) { if (!priv || (priv->dffcp == NULL) || forceselect) { + /* Force use of Le = 0x00 in iso7816_select_file as required by SC-HSM */ + card->max_recv_size = card->reader->max_recv_size = SC_READER_SHORT_APDU_MAX_RECV_SIZE; rv = (*iso_ops->select_file)(card, in_path, file_out); + card->max_recv_size = card_max_recv_size; + card->reader->max_recv_size = reader_max_recv_size; LOG_TEST_RET(card->ctx, rv, "Could not select SmartCard-HSM application"); if (priv) { - if (priv->dffcp != NULL) { - sc_file_free(priv->dffcp); - } + sc_file_free(priv->dffcp); // Cache the FCP returned when selecting the applet sc_file_dup(&priv->dffcp, *file_out); } @@ -190,7 +204,7 @@ static int sc_hsm_select_file_ex(sc_card_t *card, return rv; } - if ((in_path->value[0] == 0x3F) && (in_path->value[1] == 0x00)) { + if ((in_path->len >= 2) && (in_path->value[0] == 0x3F) && (in_path->value[1] == 0x00)) { // The SmartCard-HSM is an applet that is not default selected. Simulate selection of the MF if (in_path->len == 2) { file = sc_file_new(); @@ -204,14 +218,24 @@ static int sc_hsm_select_file_ex(sc_card_t *card, *file_out = file; return SC_SUCCESS; } else { + /* Force use of Le = 0x00 in iso7816_select_file as required by SC-HSM */ + card->max_recv_size = card->reader->max_recv_size = SC_READER_SHORT_APDU_MAX_RECV_SIZE; sc_path_t truncated; memcpy(&truncated, in_path, sizeof truncated); truncated.len = in_path->len - 2; memcpy(truncated.value, in_path->value+2, truncated.len); - return (*iso_ops->select_file)(card, &truncated, file_out); + rv = (*iso_ops->select_file)(card, &truncated, file_out); + card->max_recv_size = card_max_recv_size; + card->reader->max_recv_size = reader_max_recv_size; + return rv; } } - return (*iso_ops->select_file)(card, in_path, file_out); + /* Force use of Le = 0x00 in iso7816_select_file as required by SC-HSM */ + card->max_recv_size = card->reader->max_recv_size = SC_READER_SHORT_APDU_MAX_RECV_SIZE; + rv = (*iso_ops->select_file)(card, in_path, file_out); + card->max_recv_size = card_max_recv_size; + card->reader->max_recv_size = reader_max_recv_size; + return rv; } @@ -225,20 +249,49 @@ static int sc_hsm_select_file(sc_card_t *card, +static int sc_hsm_get_challenge(struct sc_card *card, unsigned char *rnd, size_t len) +{ + LOG_FUNC_CALLED(card->ctx); + + if (len > 1024) { + len = 1024; + } + + LOG_FUNC_RETURN(card->ctx, iso_ops->get_challenge(card, rnd, len)); +} + + + static int sc_hsm_match_card(struct sc_card *card) { sc_path_t path; - int i, r; + int i, r, type = 0; + sc_file_t *file = NULL; - i = _sc_match_atr(card, sc_hsm_atrs, &card->type); - if (i >= 0) + i = _sc_match_atr(card, sc_hsm_atrs, &type); + if (i >= 0 && type != SC_CARD_TYPE_SC_HSM_SOC) { + card->type = type; return 1; + } sc_path_set(&path, SC_PATH_TYPE_DF_NAME, sc_hsm_aid.value, sc_hsm_aid.len, 0, 0); - r = sc_hsm_select_file(card, &path, NULL); + r = sc_hsm_select_file(card, &path, &file); LOG_TEST_RET(card->ctx, r, "Could not select SmartCard-HSM application"); - card->type = SC_CARD_TYPE_SC_HSM; + // Validate that card returns a FCP with a proprietary tag 85 with value longer than 2 byte (Fixes #1377) + if (file != NULL) { + size_t sz = file->prop_attr_len; + sc_file_free(file); + if (sz < 2) { + return 0; + } + } + + if (type == SC_CARD_TYPE_SC_HSM_SOC) { + card->type = SC_CARD_TYPE_SC_HSM_SOC; + } else { + card->type = SC_CARD_TYPE_SC_HSM; + } return 1; } @@ -252,7 +305,7 @@ static int sc_hsm_match_card(struct sc_card *card) static int sc_hsm_encode_sopin(const u8 *sopin, u8 *sopinbin) { int i; - char digit; + unsigned char digit; memset(sopinbin, 0, 8); for (i = 0; i < 16; i++) { @@ -278,66 +331,94 @@ static int sc_hsm_encode_sopin(const u8 *sopin, u8 *sopinbin) static int sc_hsm_soc_select_minbioclient(sc_card_t *card) { - sc_apdu_t apdu; - struct sc_aid minBioClient_aid = { - { 0xFF,'m','i','n','B','i','o','C','l','i','e','n','t',0x01 }, 14 - }; + static const struct sc_aid minBioClient_aid = { + {0xFF, 'm', 'i', 'n', 'B', 'i', 'o', 'C', 'l', 'i', 'e', 'n', 't', 0x01}, + 14 + }; /* Select MinBioClient */ +#ifdef ENABLE_SM sc_sm_stop(card); - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x0C); - apdu.data = minBioClient_aid.value; - apdu.datalen = minBioClient_aid.len; - apdu.lc = minBioClient_aid.len; - LOG_TEST_RET(card->ctx, - sc_transmit_apdu(card, &apdu), - "APDU transmit failed"); - - return sc_check_sw(card, apdu.sw1, apdu.sw2); +#endif + return iso7816_select_aid(card, minBioClient_aid.value, minBioClient_aid.len, NULL, NULL); } -static int sc_hsm_soc_change(sc_card_t *card, struct sc_pin_cmd_data *data, - int *tries_left) +static int sc_hsm_soc_change(sc_card_t *card, struct sc_pin_cmd_data *data) { sc_apdu_t apdu; sc_path_t path; int r; - /* Select MinBioClient */ - r = sc_hsm_soc_select_minbioclient(card); - LOG_TEST_RET(card->ctx, r, "Could not select MinBioClient application"); + if (card->type == SC_CARD_TYPE_SC_HSM_SOC) { + /* Select MinBioClient */ + r = sc_hsm_soc_select_minbioclient(card); + LOG_TEST_RET(card->ctx, r, "Could not select MinBioClient application"); - /* verify PIN */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, 0x80); - r = sc_transmit_apdu(card, &apdu); - LOG_TEST_GOTO_ERR(card->ctx, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_GOTO_ERR(card->ctx, r, "Could not verify PIN"); + /* verify PIN */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, 0x80); + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_GOTO_ERR(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not verify PIN"); - /* change PIN */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x24, 0x01, 0x80); - r = sc_transmit_apdu(card, &apdu); - LOG_TEST_GOTO_ERR(card->ctx, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_GOTO_ERR(card->ctx, r, "Could not change PIN"); + /* change PIN */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x24, 0x01, 0x80); + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_GOTO_ERR(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not change PIN"); + } else { +#ifdef ENABLE_SM + unsigned sm_mode = card->sm_ctx.sm_mode; +#endif + + /* verify PIN */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, 0x85); + apdu.cla = 0x80; + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_GOTO_ERR(card->ctx, r, "APDU transmit failed"); + +#ifdef ENABLE_SM + /* temporary disable SM, change reference data does not reach the applet */ + card->sm_ctx.sm_mode = SM_MODE_NONE; +#endif + + /* change PIN */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x24, 0x01, 0x85); + apdu.cla = 0x80; + r = sc_transmit_apdu(card, &apdu); +#ifdef ENABLE_SM + /* restore SM if possible */ + card->sm_ctx.sm_mode = sm_mode; +#endif + LOG_TEST_GOTO_ERR(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not change PIN"); + } err: - /* Select SC-HSM */ - sc_path_set(&path, SC_PATH_TYPE_DF_NAME, sc_hsm_aid.value, sc_hsm_aid.len, 0, 0); - LOG_TEST_RET(card->ctx, - sc_hsm_select_file_ex(card, &path, 1, NULL), - "Could not select SmartCard-HSM application"); + if (card->type == SC_CARD_TYPE_SC_HSM_SOC) { + /* Select SC-HSM */ + sc_path_set(&path, SC_PATH_TYPE_DF_NAME, + sc_hsm_aid.value, sc_hsm_aid.len, 0, 0); + LOG_TEST_RET(card->ctx, + sc_hsm_select_file_ex(card, &path, 1, NULL), + "Could not select SmartCard-HSM application"); + } return r; } -static int sc_hsm_soc_unblock(sc_card_t *card, struct sc_pin_cmd_data *data, - int *tries_left) +static int sc_hsm_soc_unblock(sc_card_t *card, struct sc_pin_cmd_data *data) { sc_apdu_t apdu; sc_path_t path; int r; + if (card->type == SC_CARD_TYPE_SC_HSM_GOID) { + return SC_ERROR_NOT_SUPPORTED; + } + /* Select MinBioClient */ r = sc_hsm_soc_select_minbioclient(card); LOG_TEST_RET(card->ctx, r, "Could not select MinBioClient application"); @@ -366,26 +447,30 @@ static int sc_hsm_soc_unblock(sc_card_t *card, struct sc_pin_cmd_data *data, return r; } -static int sc_hsm_soc_biomatch(sc_card_t *card, struct sc_pin_cmd_data *data, - int *tries_left) +static int sc_hsm_soc_biomatch(sc_card_t *card, struct sc_pin_cmd_data *data) { sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0x00, 0x85); - apdu.cla = 0x80; - apdu.data = (unsigned char*)"\x7F\x24\x00"; - apdu.datalen = 3; - apdu.lc = 3; - apdu.resplen = 0; + if (card->type == SC_CARD_TYPE_SC_HSM_SOC) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0x00, 0x85); + apdu.cla = 0x80; + apdu.data = (unsigned char*)"\x7F\x24\x00"; + apdu.datalen = 3; + apdu.lc = 3; + apdu.resplen = 0; - r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - /* ignore the actual status bytes */ + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + /* ignore the actual status bytes */ + } /* JCOP's SM accelerator is incapable of using case 1 APDU in SM */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x20, 0x00, 0x81); + if (card->type == SC_CARD_TYPE_SC_HSM_GOID) { + apdu.cla = 0x80; + } apdu.resp = rbuf; apdu.resplen = sizeof rbuf; r = sc_transmit_apdu(card, &apdu); @@ -400,15 +485,7 @@ static int sc_hsm_soc_biomatch(sc_card_t *card, struct sc_pin_cmd_data *data, LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } - - -#ifdef ENABLE_OPENPACE -#include "sm/sm-eac.h" -#include -#include -#include -#include -#include +#if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) static int sc_hsm_perform_chip_authentication(sc_card_t *card) { @@ -445,6 +522,10 @@ static int sc_hsm_perform_chip_authentication(sc_card_t *card) r = sc_read_binary(card, 0, all_certs, all_certs_len, 0); if (r < 0) goto err; + if (r == 0) { + r = SC_ERROR_FILE_NOT_FOUND; + goto err; + } all_certs_len = r; @@ -498,7 +579,11 @@ static int sc_hsm_perform_chip_authentication(sc_card_t *card) goto err; } EVP_PKEY_free(ctx->ca_ctx->ka_ctx->key); - CRYPTO_add(&ctx->ta_ctx->pub_key->references, 1, CRYPTO_LOCK_EVP_PKEY); + if (!EVP_PKEY_up_ref(ctx->ta_ctx->pub_key)) { + sc_log_openssl(card->ctx); + r = SC_ERROR_INTERNAL; + goto err; + } ctx->ca_ctx->ka_ctx->key = ctx->ta_ctx->pub_key; /* generate keys for CA */ @@ -527,13 +612,14 @@ static int sc_hsm_perform_chip_authentication(sc_card_t *card) -static int sc_hsm_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, - int *tries_left) +static int sc_hsm_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) { sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; sc_apdu_t apdu; u8 cmdbuff[16]; +#ifdef ENABLE_SM u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; +#endif int r; int cmd = data->cmd; size_t pin2_len = data->pin2.len; @@ -550,30 +636,46 @@ static int sc_hsm_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, && (data->cmd == SC_PIN_CMD_CHANGE) && (data->pin_reference == 0x81) && (!data->pin1.data || data->pin1.len <= 0)) { - return sc_hsm_soc_change(card, data, tries_left); + return sc_hsm_soc_change(card, data); } else if ((card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH) && (data->cmd == SC_PIN_CMD_UNBLOCK) && (data->pin_reference == 0x81) && (!data->pin1.data || data->pin1.len <= 0)) { - return sc_hsm_soc_unblock(card, data, tries_left); + return sc_hsm_soc_unblock(card, data); } +#ifdef ENABLE_SM /* For contactless cards always establish a secure channel before PIN * verification. Also, Session PIN generation requires SM. */ - if ((card->type == SC_CARD_TYPE_SC_HSM_SOC || card->reader->uid.len - || cmd == SC_PIN_CMD_GET_SESSION_PIN) - && (data->cmd != SC_PIN_CMD_GET_INFO) - && card->sm_ctx.sm_mode != SM_MODE_TRANSMIT) { - LOG_TEST_RET(card->ctx, - sc_hsm_perform_chip_authentication(card), - "Could not perform chip authentication"); + if ((card->type == SC_CARD_TYPE_SC_HSM_SOC + || card->type == SC_CARD_TYPE_SC_HSM_GOID + || card->reader->uid.len || cmd == SC_PIN_CMD_GET_SESSION_PIN) + && (data->cmd != SC_PIN_CMD_GET_INFO)) { + struct sc_pin_cmd_data check_sm_pin_data; + memset(&check_sm_pin_data, 0, sizeof(check_sm_pin_data)); + check_sm_pin_data.cmd = SC_PIN_CMD_GET_INFO; + check_sm_pin_data.pin_type = data->pin_type; + check_sm_pin_data.pin_reference = data->pin_reference; + + r = SC_ERROR_NOT_ALLOWED; + if (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT) { + /* check if the existing SM channel is still valid */ + r = sc_pin_cmd(card, &check_sm_pin_data); + } + if (r == SC_ERROR_ASN1_OBJECT_NOT_FOUND || r == SC_ERROR_NOT_ALLOWED) { + /* need to establish a new SM channel */ + LOG_TEST_RET(card->ctx, + sc_hsm_perform_chip_authentication(card), + "Could not perform chip authentication"); + } } +#endif if ((card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH) && (data->cmd == SC_PIN_CMD_VERIFY) && (data->pin_reference == 0x81) && (!data->pin1.data || data->pin1.len <= 0)) { - r = sc_hsm_soc_biomatch(card, data, tries_left); + r = sc_hsm_soc_biomatch(card, data); } else { if ((data->cmd == SC_PIN_CMD_VERIFY) && (data->pin_reference == 0x88)) { if (data->pin1.len != 16) @@ -604,6 +706,7 @@ static int sc_hsm_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, data->apdu = &apdu; } +#ifdef ENABLE_SM if ((data->cmd == SC_PIN_CMD_GET_INFO) && (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT)) { /* JCOP's SM accelerator is incapable of using case 1 APDU in SM */ @@ -612,13 +715,13 @@ static int sc_hsm_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, apdu.resplen = sizeof rbuf; data->apdu = &apdu; } +#endif data->pin1.offset = 5; - data->pin1.length_offset = 4; data->pin2.offset = 5; - data->pin2.length_offset = 4; - r = (*iso_ops->pin_cmd)(card, data, tries_left); + r = (*iso_ops->pin_cmd)(card, data); + data->apdu = NULL; } LOG_TEST_RET(card->ctx, r, "Verification failed"); @@ -627,11 +730,17 @@ static int sc_hsm_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, data->cmd = SC_PIN_CMD_GET_SESSION_PIN; if (data->pin_reference == 0x81) { u8 recvbuf[SC_MAX_APDU_BUFFER_SIZE]; +#ifdef ENABLE_SM if (card->sm_ctx.sm_mode != SM_MODE_TRANSMIT) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Session PIN generation only supported in SM"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } +#else + sc_log(card->ctx, + "Session PIN generation only supported in SM"); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +#endif sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x5A, 0x01, data->pin_reference); apdu.cla = 0x80; apdu.resp = recvbuf; @@ -639,7 +748,7 @@ static int sc_hsm_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, apdu.le = 0; if (sc_transmit_apdu(card, &apdu) != SC_SUCCESS || sc_check_sw(card, apdu.sw1, apdu.sw2) != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Generating session PIN failed"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -649,12 +758,12 @@ static int sc_hsm_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, apdu.resplen); data->pin2.len = apdu.resplen; } else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Buffer too small for session PIN"); } } } else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Session PIN not supported for this PIN (0x%02X)", data->pin_reference); } @@ -669,7 +778,9 @@ static int sc_hsm_logout(sc_card_t * card) sc_path_t path; sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; memset(priv->sopin, 0, sizeof(priv->sopin)); +#ifdef ENABLE_SM sc_sm_stop(card); +#endif sc_path_set(&path, SC_PATH_TYPE_DF_NAME, sc_hsm_aid.value, sc_hsm_aid.len, 0, 0); @@ -677,10 +788,10 @@ static int sc_hsm_logout(sc_card_t * card) } - +/* NOTE: idx is an offset into the card's file, not into buf */ static int sc_hsm_read_binary(sc_card_t *card, unsigned int idx, u8 *buf, size_t count, - unsigned long flags) + unsigned long *flags) { sc_context_t *ctx = card->ctx; sc_apdu_t apdu; @@ -688,7 +799,7 @@ static int sc_hsm_read_binary(sc_card_t *card, int r; if (idx > 0xffff) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "invalid EF offset: 0x%X > 0xFFFF", idx); + sc_log(ctx, "invalid EF offset: 0x%X > 0xFFFF", idx); return SC_ERROR_OFFSET_TOO_LARGE; } @@ -697,7 +808,8 @@ static int sc_hsm_read_binary(sc_card_t *card, cmdbuff[2] = (idx >> 8) & 0xFF; cmdbuff[3] = idx & 0xFF; - assert(count <= sc_get_max_recv_size(card)); + if (count > sc_get_max_recv_size(card)) + return SC_ERROR_INTERNAL; sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0xB1, 0x00, 0x00); apdu.data = cmdbuff; apdu.datalen = 4; @@ -714,11 +826,11 @@ static int sc_hsm_read_binary(sc_card_t *card, LOG_TEST_RET(ctx, r, "Check SW error"); } - LOG_FUNC_RETURN(ctx, apdu.resplen); + LOG_FUNC_RETURN(ctx, (int)apdu.resplen); } - +/* NOTE: idx is an offset into the card's file, not into buf */ static int sc_hsm_write_ef(sc_card_t *card, int fid, unsigned int idx, const u8 *buf, size_t count) @@ -730,7 +842,7 @@ static int sc_hsm_write_ef(sc_card_t *card, int r; if (idx > 0xffff) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "invalid EF offset: 0x%X > 0xFFFF", idx); + sc_log(ctx, "invalid EF offset: 0x%X > 0xFFFF", idx); return SC_ERROR_OFFSET_TOO_LARGE; } @@ -739,45 +851,60 @@ static int sc_hsm_write_ef(sc_card_t *card, LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } - p = cmdbuff; - *p++ = 0x54; - *p++ = 0x02; - *p++ = (idx >> 8) & 0xFF; - *p++ = idx & 0xFF; - *p++ = 0x53; - if (count < 128) { - *p++ = count; - len = 6; - } else if (count < 256) { - *p++ = 0x81; - *p++ = count; - len = 7; - } else { - *p++ = 0x82; - *p++ = count >> 8; - *p++ = count & 0xFF; - len = 8; - } + size_t bytes_left = count; + // 8 bytes are required for T54(4) and T53(4) + size_t blk_size = card->max_send_size - 8; + size_t to_send = 0; + size_t file_offset = (size_t) idx; + size_t offset = 0; + do { + to_send = bytes_left >= blk_size ? blk_size : bytes_left; + p = cmdbuff; + // ASN1 0x54 offset + *p++ = 0x54; + *p++ = 0x02; + *p++ = (file_offset >> 8) & 0xFF; + *p++ = file_offset & 0xFF; + // ASN1 0x53 to_send + *p++ = 0x53; + if (to_send < 128) { + *p++ = (u8)to_send; + len = 6; + } else if (to_send < 256) { + *p++ = 0x81; + *p++ = (u8)to_send; + len = 7; + } else { + *p++ = 0x82; + *p++ = (to_send >> 8) & 0xFF; + *p++ = to_send & 0xFF; + len = 8; + } - if (buf != NULL) - memcpy(p, buf, count); - len += count; + if (buf != NULL) + memcpy(p, buf+offset, to_send); + len += to_send; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xD7, fid >> 8, fid & 0xFF); - apdu.data = cmdbuff; - apdu.datalen = len; - apdu.lc = len; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xD7, fid >> 8, fid & 0xFF); + apdu.data = cmdbuff; + apdu.datalen = len; + apdu.lc = len; - r = sc_transmit_apdu(card, &apdu); - free(cmdbuff); - LOG_TEST_RET(ctx, r, "APDU transmit failed"); + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_GOTO_ERR(ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_GOTO_ERR(ctx, r, "Check SW error"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(ctx, r, "Check SW error"); + bytes_left -= to_send; + offset += to_send; + file_offset += to_send; + } while (0 < bytes_left); - LOG_FUNC_RETURN(ctx, count); -} +err: + free(cmdbuff); + LOG_FUNC_RETURN(ctx, (int)count); +} static int sc_hsm_update_binary(sc_card_t *card, @@ -815,9 +942,12 @@ static int sc_hsm_list_files(sc_card_t *card, u8 * buf, size_t buflen) } LOG_TEST_RET(card->ctx, r, "ENUMERATE OBJECTS APDU transmit failed"); - memcpy(buf, recvbuf, buflen); + if (buflen < apdu.resplen) + memcpy(buf, recvbuf, buflen); + else + memcpy(buf, recvbuf, apdu.resplen); - LOG_FUNC_RETURN(card->ctx, apdu.resplen); + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); } @@ -842,7 +972,7 @@ static int sc_hsm_delete_file(sc_card_t *card, const sc_path_t *path) int r; if ((path->type != SC_PATH_TYPE_FILE_ID) || (path->len != 2)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type has to be SC_PATH_TYPE_FILE_ID"); + sc_log(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } @@ -879,9 +1009,19 @@ static int sc_hsm_set_security_env(sc_card_t *card, priv->algorithm = ALGO_RSA_PKCS1_SHA1; } else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA256) { priv->algorithm = ALGO_RSA_PKCS1_SHA256; + } else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA384) { + priv->algorithm = ALGO_RSA_PKCS1_SHA384; + } else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA512) { + priv->algorithm = ALGO_RSA_PKCS1_SHA512; } else { priv->algorithm = ALGO_RSA_PKCS1; } + } else if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PSS) { + if ((env->algorithm_flags & SC_ALGORITHM_RSA_HASHES) && + (((env->algorithm_flags & SC_ALGORITHM_MGF1_HASHES) >> 8) != (env->algorithm_flags & SC_ALGORITHM_RSA_HASHES))) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + priv->algorithm = ALGO_RSA_PSS; } else { if (env->operation == SC_SEC_OPERATION_DECIPHER) { priv->algorithm = ALGO_RSA_DECRYPT; @@ -891,7 +1031,9 @@ static int sc_hsm_set_security_env(sc_card_t *card, } break; case SC_ALGORITHM_EC: - if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_NONE) { + if (env->operation == SC_SEC_OPERATION_DERIVE) { + priv->algorithm = ALGO_EC_DH; + } else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_NONE) { priv->algorithm = ALGO_EC_RAW; } else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA1) { priv->algorithm = ALGO_EC_SHA1; @@ -899,12 +1041,12 @@ static int sc_hsm_set_security_env(sc_card_t *card, priv->algorithm = ALGO_EC_SHA224; } else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA256) { priv->algorithm = ALGO_EC_SHA256; + } else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA384) { + priv->algorithm = ALGO_EC_SHA384; + } else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA512) { + priv->algorithm = ALGO_EC_SHA512; } else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_RAW) { - if (env->operation == SC_SEC_OPERATION_DERIVE) { - priv->algorithm = ALGO_EC_DH; - } else { - priv->algorithm = ALGO_EC_RAW; - } + priv->algorithm = ALGO_EC_RAW; } else { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } @@ -920,65 +1062,17 @@ static int sc_hsm_set_security_env(sc_card_t *card, static int sc_hsm_decode_ecdsa_signature(sc_card_t *card, const u8 * data, size_t datalen, - u8 * out, size_t outlen) { - - int i, r; - size_t fieldsizebytes; - const u8 *body, *tag; - size_t bodylen, taglen; - - // Determine field size from length of signature - if (datalen <= 58) { // 192 bit curve = 24 * 2 + 10 byte maximum DER signature - fieldsizebytes = 24; - } else if (datalen <= 66) { // 224 bit curve = 28 * 2 + 10 byte maximum DER signature - fieldsizebytes = 28; - } else if (datalen <= 74) { // 256 bit curve = 32 * 2 + 10 byte maximum DER signature - fieldsizebytes = 32; - } else if (datalen <= 90) { // 320 bit curve = 40 * 2 + 10 byte maximum DER signature - fieldsizebytes = 40; - } else { - fieldsizebytes = 64; - } + u8 * out, size_t outlen, + size_t key_size) { + + int r; + size_t fieldsizebytes = (key_size + 7) >> 3; sc_log(card->ctx, "Field size %"SC_FORMAT_LEN_SIZE_T"u, signature buffer size %"SC_FORMAT_LEN_SIZE_T"u", fieldsizebytes, outlen); - if (outlen < (fieldsizebytes * 2)) { - LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "output too small for EC signature"); - } - memset(out, 0, outlen); - - // Copied from card-piv.c. Thanks - body = sc_asn1_find_tag(card->ctx, data, datalen, 0x30, &bodylen); - - for (i = 0; i<2; i++) { - if (body) { - tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x02, &taglen); - if (tag) { - bodylen -= taglen - (tag - body); - body = tag + taglen; - - if (taglen > fieldsizebytes) { /* drop leading 00 if present */ - if (*tag != 0x00) { - r = SC_ERROR_INVALID_DATA; - goto err; - } - tag++; - taglen--; - } - memcpy(out + fieldsizebytes*i + fieldsizebytes - taglen , tag, taglen); - } else { - r = SC_ERROR_INVALID_DATA; - goto err; - } - } else { - r = SC_ERROR_INVALID_DATA; - goto err; - } - } - r = 2 * fieldsizebytes; -err: + r = sc_asn1_decode_ecdsa_signature(card->ctx, data, datalen, fieldsizebytes, &out, outlen); LOG_FUNC_RETURN(card->ctx, r); } @@ -990,8 +1084,7 @@ static int sc_hsm_compute_signature(sc_card_t *card, { int r; sc_apdu_t apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 rbuf[514]; sc_hsm_private_data_t *priv; if (card == NULL || data == NULL || out == NULL) { @@ -1003,19 +1096,13 @@ static int sc_hsm_compute_signature(sc_card_t *card, LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_FOUND); } - // check if datalen exceeds the buffer size - if (datalen > SC_MAX_APDU_BUFFER_SIZE) { - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); - } - - sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x68, priv->env->key_ref[0], priv->algorithm); + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_EXT, 0x68, priv->env->key_ref[0], priv->algorithm); apdu.cla = 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); - apdu.le = 256; + apdu.le = 512; - memcpy(sbuf, data, datalen); - apdu.data = sbuf; + apdu.data = data; apdu.lc = datalen; apdu.datalen = datalen; r = sc_transmit_apdu(card, &apdu); @@ -1025,12 +1112,12 @@ static int sc_hsm_compute_signature(sc_card_t *card, int len; if ((priv->algorithm & 0xF0) == ALGO_EC_RAW) { - len = sc_hsm_decode_ecdsa_signature(card, apdu.resp, apdu.resplen, out, outlen); + len = sc_hsm_decode_ecdsa_signature(card, apdu.resp, apdu.resplen, out, outlen, priv->env->key_size_bits); if (len < 0) { LOG_FUNC_RETURN(card->ctx, len); } } else { - len = apdu.resplen > outlen ? outlen : apdu.resplen; + len = (int)(apdu.resplen > outlen ? outlen : apdu.resplen); memcpy(out, apdu.resp, len); } LOG_FUNC_RETURN(card->ctx, len); @@ -1045,7 +1132,7 @@ static int sc_hsm_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len int r; size_t len; sc_apdu_t apdu; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 rbuf[514]; sc_hsm_private_data_t *priv; if (card == NULL || crgram == NULL || out == NULL) { @@ -1054,11 +1141,11 @@ static int sc_hsm_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len LOG_FUNC_CALLED(card->ctx); priv = (sc_hsm_private_data_t *) card->drv_data; - sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x62, priv->env->key_ref[0], priv->algorithm); + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_EXT, 0x62, priv->env->key_ref[0], priv->algorithm); apdu.cla = 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); - apdu.le = 256; + apdu.le = 512; apdu.data = (u8 *)crgram; apdu.lc = crgram_len; @@ -1072,14 +1159,15 @@ static int sc_hsm_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len // // The SmartCard-HSM returns the point result of the DH operation // with a leading '04' - assert(apdu.resplen > 0); + if (apdu.resplen <= 0) + return SC_ERROR_INTERNAL; len = apdu.resplen - 1 > outlen ? outlen : apdu.resplen - 1; memcpy(out, apdu.resp + 1, len); - LOG_FUNC_RETURN(card->ctx, len); + LOG_FUNC_RETURN(card->ctx, (int)len); } else { len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); - LOG_FUNC_RETURN(card->ctx, len); + LOG_FUNC_RETURN(card->ctx, (int)len); } } else @@ -1107,6 +1195,16 @@ static int sc_hsm_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) LOG_FUNC_CALLED(card->ctx); + if (!priv->serialno && 0 == strcmp(card->ctx->app_name, "opensc-tool")) { + /* sc-hsm initializes the serial number via its PKCS#15 layer. + * Create and destroy a dummy card to get this initialized. Only do + * this for `opensc-tool --serial` to avoid unnecessary card commands + * in all other cases. */ + sc_pkcs15_card_t *p15card = NULL; + (void)sc_pkcs15_bind(card, NULL, &p15card); + sc_pkcs15_unbind(p15card); + } + if (!priv->serialno) { return SC_ERROR_OBJECT_NOT_FOUND; } @@ -1130,7 +1228,7 @@ static int sc_hsm_initialize(sc_card_t *card, sc_cardctl_sc_hsm_init_param_t *pa int r; size_t tilen; sc_apdu_t apdu; - u8 ibuff[50], *p; + u8 ibuff[68+0xFF], *p; LOG_FUNC_CALLED(card->ctx); @@ -1140,8 +1238,11 @@ static int sc_hsm_initialize(sc_card_t *card, sc_cardctl_sc_hsm_init_param_t *pa memcpy(p, params->options, 2); p += 2; + if (params->user_pin_len > 0xFF) { + return SC_ERROR_INVALID_ARGUMENTS; + } *p++ = 0x81; // User PIN - *p++ = params->user_pin_len; + *p++ = (u8)params->user_pin_len; memcpy(p, params->user_pin, params->user_pin_len); p += params->user_pin_len; @@ -1160,6 +1261,26 @@ static int sc_hsm_initialize(sc_card_t *card, sc_cardctl_sc_hsm_init_param_t *pa *p++ = (u8)params->dkek_shares; } + if (params->num_of_pub_keys > 0) { + *p++ = 0x93; // Use public key authentication + *p++ = 0x02; + *p++ = params->num_of_pub_keys; // Total number of public keys used for public authentication + *p++ = params->required_pub_keys; // Number of public keys required for authentication + } + + if (params->bio1.len) { + *p++ = 0x95; + *p++ = params->bio1.len; + memcpy(p, params->bio1.value, params->bio1.len); + p += params->bio1.len; + } + if (params->bio2.len) { + *p++ = 0x96; + *p++ = params->bio2.len; + memcpy(p, params->bio2.value, params->bio2.len); + p += params->bio2.len; + } + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x50, 0x00, 0x00); apdu.cla = 0x80; apdu.data = ibuff; @@ -1193,7 +1314,7 @@ static int sc_hsm_initialize(sc_card_t *card, sc_cardctl_sc_hsm_init_param_t *pa pincmd.pin1.data = params->user_pin; pincmd.pin1.len = params->user_pin_len; - r = (*iso_ops->pin_cmd)(card, &pincmd, NULL); + r = (*iso_ops->pin_cmd)(card, &pincmd); LOG_TEST_RET(ctx, r, "Could not verify PIN"); r = sc_hsm_write_ef(card, 0x2F03, 0, p, tilen); @@ -1235,7 +1356,8 @@ static int sc_hsm_import_dkek_share(sc_card_t *card, sc_cardctl_sc_hsm_dkek_t *p LOG_TEST_RET(ctx, r, "Check SW error"); - assert(apdu.resplen >= (sizeof(params->key_check_value) + 2)); + if (apdu.resplen < (sizeof(params->key_check_value) + 2)) + return SC_ERROR_INTERNAL; params->dkek_shares = status[0]; params->outstanding_shares = status[1]; @@ -1250,7 +1372,7 @@ static int sc_hsm_wrap_key(sc_card_t *card, sc_cardctl_sc_hsm_wrapped_key_t *par { sc_context_t *ctx = card->ctx; sc_apdu_t apdu; - u8 data[MAX_EXT_APDU_LENGTH]; + u8 data[1500]; int r; LOG_FUNC_CALLED(card->ctx); @@ -1294,19 +1416,182 @@ static int sc_hsm_unwrap_key(sc_card_t *card, sc_cardctl_sc_hsm_wrapped_key_t *p LOG_FUNC_CALLED(card->ctx); - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_EXT, 0x74, params->key_id, 0x93); - apdu.cla = 0x80; - apdu.lc = params->wrapped_key_length; - apdu.data = params->wrapped_key; - apdu.datalen = params->wrapped_key_length; + r = sc_hsm_write_ef(card, 0x2F10, 0, params->wrapped_key, params->wrapped_key_length); + LOG_TEST_RET(card->ctx, r, "Create EF failed"); + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x74, params->key_id, 0x93); + apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, r, "APDU transmit failed"); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, r, "Check SW error"); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int verify_certificate(sc_card_t *card, sc_cvc_t *cvc, + const u8 *cvc_buf, size_t cvc_buf_len) +{ + u8 tag = SC_ASN1_TAG_CONTEXT | SC_ASN1_TAG_BIT_STRING; /* 0x83 */ + size_t pukref_len; + u8 pukref[BUFSIZ]; + sc_apdu_t apdu; + u8 *ptr; + int r; + + LOG_FUNC_CALLED(card->ctx); + + /* check if public key is already known */ + if ((r = sc_asn1_put_tag(tag, (u8 *)cvc->chr, cvc->chrLen, + pukref, sizeof(pukref), &ptr)) < 0) { + sc_log(card->ctx, "Error formatting ASN.1 sequence: %s\n", sc_strerror(r)); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN); + } + pukref_len = ptr - pukref; + + /* MANAGE SECURITY ENVIRONMENT to query public key by chr */ + sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x81, 0xB6, pukref, pukref_len, NULL, 0); + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (!r) { + /* already known */ + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + } + if (apdu.sw1 != 0x6A && apdu.sw2 != 0x88) { + LOG_TEST_RET(card->ctx, SC_ERROR_UNKNOWN, "Check SW error"); + } + + if ((r = sc_asn1_put_tag(tag, (u8 *)cvc->car, cvc->carLen, + pukref, sizeof(pukref), &ptr)) < 0) { + sc_log(card->ctx, "Error formatting ASN.1 sequence: %s\n", sc_strerror(r)); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN); + } + pukref_len = ptr - pukref; + + /* MANAGE SECURITY ENVIRONMENT to set the CAR public key */ + sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x81, 0xB6, pukref, pukref_len, NULL, 0); + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "Check SW error"); + + /* PERFORM SECURITY OPERATION -> VERIFY CERTIFICATE */ + sc_format_apdu_ex(&apdu, 0x00, 0x2A, 0x00, 0xBE, cvc_buf, cvc_buf_len, NULL, 0); + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "Check SW error"); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + + + +static int sc_hsm_register_public_key(sc_card_t *card, + sc_cardctl_sc_hsm_pka_register_t *pka_register) +{ + u8 tag = SC_ASN1_TAG_CONTEXT | SC_ASN1_TAG_BIT_STRING; /* 0x83 */ + u8 recvbuf[4]; + sc_context_t *ctx = card->ctx; + sc_apdu_t apdu; + u8 *ptr; + int r; + sc_pkcs15_card_t p15card; + const u8 *pka_buf; + size_t pka_buf_len; + sc_cvc_pka_t pka; + /* outer CAR in ASN.1 needs a byte for tag and a byte for length */ + u8 asn1_outer_car[sizeof(pka.public_key_req.cvc.outer_car) + 2]; + + LOG_FUNC_CALLED(ctx); + + memset(&pka, 0, sizeof(pka)); + memset(&p15card, 0, sizeof(p15card)); + p15card.card = card; + + pka_buf = pka_register->buf; + pka_buf_len = pka_register->buflen; + r = sc_pkcs15emu_sc_hsm_decode_pka(&p15card, &pka_buf, &pka_buf_len, &pka); + LOG_TEST_GOTO_ERR(ctx, r, "sc_pkcs15emu_sc_hsm_decode_pka failed"); + + /* the DICA CVC must be verified first */ + r = verify_certificate(card, &pka.dica.cvc, pka.dica.ptr, pka.dica.len); + LOG_TEST_GOTO_ERR(ctx, r, "Verify device issuer CA CVC failed"); + /* the device CVC must be verified before registering the public key */ + r = verify_certificate(card, &pka.device.cvc, pka.device.ptr, pka.device.len); + LOG_TEST_GOTO_ERR(ctx, r, "Verify device CVC failed"); + + r = sc_asn1_put_tag(tag, + (u8 *)pka.public_key_req.cvc.outer_car, + pka.public_key_req.cvc.outerCARLen, + asn1_outer_car, sizeof(asn1_outer_car), &ptr); + LOG_TEST_GOTO_ERR(ctx, r, "ASN.1 encode outer CAR failed"); + + /* MANAGE SECURITY ENVIRONMENT with the outer CAR of the public key */ + sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x81, 0xB6, + asn1_outer_car, ptr - asn1_outer_car, NULL, 0); + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_GOTO_ERR(ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_GOTO_ERR(ctx, r, "Check SW error"); + + sc_format_apdu_ex(&apdu, 0x80, 0x54, 0x00, 0x00, + pka.public_key_req.ptr, pka.public_key_req.len, + recvbuf, sizeof(recvbuf)); + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_GOTO_ERR(ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_GOTO_ERR(ctx, r, "Check SW error"); + + pka_register->new_status.num_total = recvbuf[0]; + pka_register->new_status.num_missing = recvbuf[1]; + pka_register->new_status.num_required = recvbuf[2]; + pka_register->new_status.num_authenticated = recvbuf[3]; + + r = 0; + /* fall-through */ + +err: + sc_pkcs15emu_sc_hsm_free_cvc_pka(&pka); + return r; +} + + + +static int sc_hsm_public_key_auth_status(sc_card_t *card, + sc_cardctl_sc_hsm_pka_status_t *status) +{ + u8 recvbuf[4]; + sc_context_t *ctx = card->ctx; + sc_apdu_t apdu; + int r; + + LOG_FUNC_CALLED(card->ctx); + + /* get status */ + sc_format_apdu_ex(&apdu, 0x00, 0x54, 0x00, 0x00, NULL, 0, recvbuf, sizeof recvbuf); + apdu.cla = 0x80; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, r, "Check SW error"); + status->num_total = recvbuf[0]; + status->num_missing = recvbuf[1]; + status->num_required = recvbuf[2]; + status->num_authenticated = recvbuf[3]; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -1421,7 +1706,7 @@ static int sc_hsm_init_pin(sc_card_t *card, sc_cardctl_pkcs11_init_pin_t *params static int sc_hsm_generate_keypair(sc_card_t *card, sc_cardctl_sc_hsm_keygen_info_t *keyinfo) { - u8 rbuf[1024]; + u8 rbuf[1200]; int r; sc_apdu_t apdu; @@ -1476,6 +1761,10 @@ static int sc_hsm_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) return sc_hsm_wrap_key(card, (sc_cardctl_sc_hsm_wrapped_key_t *)ptr); case SC_CARDCTL_SC_HSM_UNWRAP_KEY: return sc_hsm_unwrap_key(card, (sc_cardctl_sc_hsm_wrapped_key_t *)ptr); + case SC_CARDCTL_SC_HSM_REGISTER_PUBLIC_KEY: + return sc_hsm_register_public_key(card, ptr); + case SC_CARDCTL_SC_HSM_PUBLIC_KEY_AUTH_STATUS: + return sc_hsm_public_key_auth_status(card, ptr); } return SC_ERROR_NOT_SUPPORTED; } @@ -1484,29 +1773,26 @@ static int sc_hsm_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) static int sc_hsm_init(struct sc_card *card) { -#ifdef _WIN32 +#if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) && defined(_WIN32) char expanded_val[PATH_MAX]; size_t expanded_len = PATH_MAX; #endif int flags,ext_flags; - sc_file_t *file; + sc_file_t *file = NULL; sc_path_t path; - sc_hsm_private_data_t *priv = card->drv_data; + sc_hsm_private_data_t *priv = NULL; LOG_FUNC_CALLED(card->ctx); - if (!priv) { - priv = calloc(1, sizeof(sc_hsm_private_data_t)); - if (!priv) - LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); - card->drv_data = priv; - } - - flags = SC_ALGORITHM_RSA_RAW|SC_ALGORITHM_ONBOARD_KEY_GEN; + flags = SC_ALGORITHM_RSA_RAW|SC_ALGORITHM_RSA_PAD_PSS|SC_ALGORITHM_ONBOARD_KEY_GEN + |SC_ALGORITHM_RSA_HASH_SHA1|SC_ALGORITHM_RSA_HASH_SHA256|SC_ALGORITHM_RSA_HASH_SHA384|SC_ALGORITHM_RSA_HASH_SHA512 + |SC_ALGORITHM_MGF1_SHA256|SC_ALGORITHM_MGF1_SHA384|SC_ALGORITHM_MGF1_SHA512; _sc_card_add_rsa_alg(card, 1024, flags, 0); _sc_card_add_rsa_alg(card, 1536, flags, 0); _sc_card_add_rsa_alg(card, 2048, flags, 0); + _sc_card_add_rsa_alg(card, 3072, flags, 0); + _sc_card_add_rsa_alg(card, 4096, flags, 0); flags = SC_ALGORITHM_ECDSA_RAW| SC_ALGORITHM_ECDH_CDH_RAW| @@ -1514,6 +1800,8 @@ static int sc_hsm_init(struct sc_card *card) SC_ALGORITHM_ECDSA_HASH_SHA1| SC_ALGORITHM_ECDSA_HASH_SHA224| SC_ALGORITHM_ECDSA_HASH_SHA256| + SC_ALGORITHM_ECDSA_HASH_SHA384| + SC_ALGORITHM_ECDSA_HASH_SHA512| SC_ALGORITHM_ONBOARD_KEY_GEN; ext_flags = SC_ALGORITHM_EXT_EC_F_P| @@ -1525,19 +1813,73 @@ static int sc_hsm_init(struct sc_card *card) _sc_card_add_ec_alg(card, 224, flags, ext_flags, NULL); _sc_card_add_ec_alg(card, 256, flags, ext_flags, NULL); _sc_card_add_ec_alg(card, 320, flags, ext_flags, NULL); + _sc_card_add_ec_alg(card, 384, flags, ext_flags, NULL); + _sc_card_add_ec_alg(card, 512, flags, ext_flags, NULL); + _sc_card_add_ec_alg(card, 521, flags, ext_flags, NULL); card->caps |= SC_CARD_CAP_RNG|SC_CARD_CAP_APDU_EXT|SC_CARD_CAP_ISO7816_PIN_INFO; + // APDU Size limits + // JCOP 2.4.1r3 1462 + // JCOP 2.4.2r3 1454 + // JCOP 3 1232 + // JCOP 4 1454 + // MicroSD with JCOP 3 478 / 506 - handled in reader-pcsc.c + // Reiner SCT 1014 - handled in reader-pcsc.c + // + // Note, that these are limits for the whole APDU, but the semantics of max_send_size + // is the size of the APDU send buffer so to get the right value from them, we need + // to subtract APDU headers (CLA, INS, P1, P2, Lc (3B)), 7 bytes altogether for + // class 3 APDU + // (or 9 bytes for case 4 when we pass in >255 B data and expect return of more than 255 B) + + // Use JCOP 3 (smallest unhandled by reader limitation) card limits for sending + // And make it 9 smaller to make sure we fit the rest of the APDU. + card->max_send_size = 1232 - 9; + // Assume that card supports sending with extended length APDU and without limit + card->max_recv_size = 0; + + if (card->type == SC_CARD_TYPE_SC_HSM_SOC + || card->type == SC_CARD_TYPE_SC_HSM_GOID) { + card->max_recv_size = 0x0630; // SoC Proxy forces this limit + } else { + // Adjust to the limits set by the reader + if (card->reader->max_send_size < card->max_send_size) { + if (18 >= card->reader->max_send_size) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCONSISTENT_CONFIGURATION); + + // 17 byte header and TLV because of odd ins in UPDATE BINARY + card->max_send_size = card->reader->max_send_size - 17; + } + + if (0 < card->reader->max_recv_size) { + if (3 >= card->reader->max_recv_size) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCONSISTENT_CONFIGURATION); + card->max_recv_size = card->reader->max_recv_size - 2; + } + } + + priv = card->drv_data; + if (!priv) { + priv = calloc(1, sizeof(sc_hsm_private_data_t)); + if (!priv) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + card->drv_data = priv; + } + sc_path_set(&path, SC_PATH_TYPE_DF_NAME, sc_hsm_aid.value, sc_hsm_aid.len, 0, 0); if (sc_hsm_select_file_ex(card, &path, 0, &file) == SC_SUCCESS - && file->prop_attr && file->prop_attr_len >= 5) { + && file && file->prop_attr && file->prop_attr_len >= 2) { static char card_name[SC_MAX_APDU_BUFFER_SIZE]; - u8 type = file->prop_attr[2]; - u8 major = file->prop_attr[3]; - u8 minor = file->prop_attr[4]; + u8 type = 0xFF; + u8 major = file->prop_attr[file->prop_attr_len - 2]; + u8 minor = file->prop_attr[file->prop_attr_len - 1]; char p00[] = "SmartCard-HSM Applet for JCOP"; char p01[] = "SmartCard-HSM Demo Applet for JCOP"; char *p = "SmartCard-HSM"; + if (file->prop_attr_len >= 3) { + type = file->prop_attr[file->prop_attr_len - 3]; + } switch (type) { case 0x00: p = p00; @@ -1554,20 +1896,13 @@ static int sc_hsm_init(struct sc_card *card) if (file->prop_attr[1] & 0x04) { card->caps |= SC_CARD_CAP_SESSION_PIN; } - sc_file_free(file); - } - - card->max_send_size = 1431; // 1439 buffer size - 8 byte TLV because of odd ins in UPDATE BINARY - if (card->type == SC_CARD_TYPE_SC_HSM_SOC) { - card->max_recv_size = 0x0630; // SoC Proxy forces this limit - } else { - card->max_recv_size = 0; // Card supports sending with extended length APDU and without limit } + sc_file_free(file); priv->EF_C_DevAut = NULL; priv->EF_C_DevAut_len = 0; -#ifdef ENABLE_OPENPACE +#if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) EAC_init(); #ifdef _WIN32 expanded_len = ExpandEnvironmentStringsA(CVCDIR, expanded_val, sizeof expanded_val); @@ -1586,20 +1921,16 @@ static int sc_hsm_init(struct sc_card *card) static int sc_hsm_finish(sc_card_t * card) { sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; +#ifdef ENABLE_SM sc_sm_stop(card); - if (priv->serialno) { +#endif + if (priv) { free(priv->serialno); - } - if (priv->dffcp) { sc_file_free(priv->dffcp); + free(priv->EF_C_DevAut); } - free(priv->EF_C_DevAut); free(priv); -#ifdef ENABLE_OPENPACE - EAC_cleanup(); -#endif - return SC_SUCCESS; } @@ -1615,6 +1946,7 @@ static struct sc_card_driver * sc_get_driver(void) sc_hsm_ops = *iso_drv->ops; sc_hsm_ops.match_card = sc_hsm_match_card; sc_hsm_ops.select_file = sc_hsm_select_file; + sc_hsm_ops.get_challenge = sc_hsm_get_challenge; sc_hsm_ops.read_binary = sc_hsm_read_binary; sc_hsm_ops.update_binary = sc_hsm_update_binary; sc_hsm_ops.list_files = sc_hsm_list_files; diff --git a/src/libopensc/card-sc-hsm.h b/src/libopensc/card-sc-hsm.h index 0a4fb3b792..3668f960d8 100644 --- a/src/libopensc/card-sc-hsm.h +++ b/src/libopensc/card-sc-hsm.h @@ -15,12 +15,15 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SC_HSM_H_ #define SC_HSM_H_ +#include "pkcs15.h" +#include "internal.h" + #define MAX_EXT_APDU_LENGTH 1014 #define PRKD_PREFIX 0xC4 /* Hi byte in file identifier for PKCS#15 PRKD objects */ @@ -37,14 +40,21 @@ #define ALGO_RSA_PKCS1 0x30 /* RSA signature with DigestInfo input and PKCS#1 V1.5 padding */ #define ALGO_RSA_PKCS1_SHA1 0x31 /* RSA signature with SHA-1 hash and PKCS#1 V1.5 padding */ #define ALGO_RSA_PKCS1_SHA256 0x33 /* RSA signature with SHA-256 hash and PKCS#1 V1.5 padding */ +#define ALGO_RSA_PKCS1_SHA384 0x34 /* RSA signature with SHA-384 hash and PKCS#1 V1.5 padding */ +#define ALGO_RSA_PKCS1_SHA512 0x35 /* RSA signature with SHA-512 hash and PKCS#1 V1.5 padding */ +#define ALGO_RSA_PSS 0x40 /* RSA signature with external hash and PKCS#1 PSS padding*/ #define ALGO_RSA_PSS_SHA1 0x41 /* RSA signature with SHA-1 hash and PKCS#1 PSS padding */ #define ALGO_RSA_PSS_SHA256 0x43 /* RSA signature with SHA-256 hash and PKCS#1 PSS padding */ +#define ALGO_RSA_PSS_SHA384 0x44 /* RSA signature with SHA-384 hash and PKCS#1 PSS padding */ +#define ALGO_RSA_PSS_SHA512 0x45 /* RSA signature with SHA-512 hash and PKCS#1 PSS padding */ #define ALGO_EC_RAW 0x70 /* ECDSA signature with hash input */ #define ALGO_EC_SHA1 0x71 /* ECDSA signature with SHA-1 hash */ #define ALGO_EC_SHA224 0x72 /* ECDSA signature with SHA-224 hash */ #define ALGO_EC_SHA256 0x73 /* ECDSA signature with SHA-256 hash */ +#define ALGO_EC_SHA384 0x74 /* ECDSA signature with SHA-384 hash */ +#define ALGO_EC_SHA512 0x75 /* ECDSA signature with SHA-512 hash */ #define ALGO_EC_DH 0x80 /* ECDH key derivation */ #define ID_USER_PIN 0x81 /* User PIN identifier */ @@ -70,6 +80,7 @@ typedef struct sc_hsm_private_data { struct sc_cvc { int cpi; // Certificate profile indicator (0) char car[17]; // Certification authority reference + size_t carLen; // strlen of car struct sc_object_id pukoid; // Public key algorithm object identifier u8 *primeOrModulus; // Prime for ECC or modulus for RSA @@ -90,11 +101,14 @@ struct sc_cvc { int modulusSize; // Size of RSA modulus in bits char chr[21]; // Certificate holder reference + size_t chrLen; // strlen of chr u8 *signature; // Certificate signature or request self-signed signature size_t signatureLen; char outer_car[17]; // Instance signing the request + size_t outerCARLen; // strlen of outer_car + u8 *outerSignature; // Request authenticating signature size_t outerSignatureLen; }; @@ -112,16 +126,32 @@ struct ec_curve { const struct sc_lv_data coFactor; }; +typedef struct sc_cvc_pka_component { + sc_cvc_t cvc; + const u8 *ptr; /* don't free, this points to the middle of a buffer */ + size_t len; +} sc_cvc_pka_component_t; +typedef struct sc_cvc_pka { + sc_cvc_pka_component_t public_key_req; /* CVC request with public key */ + sc_cvc_pka_component_t device; /* device CVC*/ + sc_cvc_pka_component_t dica; /* device issuer CA CVC */ +} sc_cvc_pka_t; int sc_pkcs15emu_sc_hsm_decode_cvc(sc_pkcs15_card_t * p15card, const u8 ** buf, size_t *buflen, sc_cvc_t *cvc); +int sc_pkcs15emu_sc_hsm_decode_pka(sc_pkcs15_card_t * p15card, + const u8 **buf, size_t *buflen, + sc_cvc_pka_t *pka); int sc_pkcs15emu_sc_hsm_encode_cvc(sc_pkcs15_card_t * p15card, sc_cvc_t *cvc, u8 ** buf, size_t *buflen); void sc_pkcs15emu_sc_hsm_free_cvc(sc_cvc_t *cvc); +void sc_pkcs15emu_sc_hsm_free_cvc_pka(sc_cvc_pka_t *pka); int sc_pkcs15emu_sc_hsm_get_curve(struct ec_curve **curve, u8 *oid, size_t oidlen); int sc_pkcs15emu_sc_hsm_get_public_key(struct sc_context *ctx, sc_cvc_t *cvc, struct sc_pkcs15_pubkey *pubkey); +/* Known ATRs for SmartCard-HSMs */ +extern const struct sc_atr_table sc_hsm_atrs[]; #endif /* SC_HSM_H_ */ diff --git a/src/libopensc/card-setcos.c b/src/libopensc/card-setcos.c index a852180aee..4b1d1773a2 100644 --- a/src/libopensc/card-setcos.c +++ b/src/libopensc/card-setcos.c @@ -17,10 +17,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -31,36 +31,16 @@ #include "asn1.h" #include "cardctl.h" -#define _FINEID_BROKEN_SELECT_FLAG 1 - -static struct sc_atr_table setcos_atrs[] = { - /* some Nokia branded SC */ - { "3B:1F:11:00:67:80:42:46:49:53:45:10:52:66:FF:81:90:00", NULL, NULL, SC_CARD_TYPE_SETCOS_GENERIC, 0, NULL }, - /* RSA SecurID 3100 */ - { "3B:9F:94:40:1E:00:67:16:43:46:49:53:45:10:52:66:FF:81:90:00", NULL, NULL, SC_CARD_TYPE_SETCOS_PKI, 0, NULL }, - - /* FINEID 1016 (SetCOS 4.3.1B3/PKCS#15, VRK) */ - { "3b:9f:94:40:1e:00:67:00:43:46:49:53:45:10:52:66:ff:81:90:00", "ff:ff:ff:ff:ff:ff:ff:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff", NULL, SC_CARD_TYPE_SETCOS_FINEID, SC_CARD_FLAG_RNG, NULL }, - /* FINEID 2032 (EIDApplet/7816-15, VRK test) */ - { "3b:6b:00:ff:80:62:00:a2:56:46:69:6e:45:49:44", "ff:ff:00:ff:ff:ff:00:ff:ff:ff:ff:ff:ff:ff:ff", NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0, NULL }, - /* FINEID 2132 (EIDApplet/7816-15, 3rdparty test) */ - { "3b:64:00:ff:80:62:00:a2", "ff:ff:00:ff:ff:ff:00:ff", NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0, NULL }, - /* FINEID 2064 (EIDApplet/7816-15, VRK) */ - { "3b:7b:00:00:00:80:62:00:51:56:46:69:6e:45:49:44", "ff:ff:00:ff:ff:ff:ff:f0:ff:ff:ff:ff:ff:ff:ff:ff", NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0, NULL }, - /* FINEID 2164 (EIDApplet/7816-15, 3rdparty) */ - { "3b:64:00:00:80:62:00:51", "ff:ff:ff:ff:ff:ff:f0:ff", NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0, NULL }, - /* FINEID 2264 (EIDApplet/7816-15, OPK/EMV/AVANT) */ - { "3b:6e:00:00:00:62:00:00:57:41:56:41:4e:54:10:81:90:00", NULL, NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0, NULL }, - { "3b:7b:94:00:00:80:62:11:51:56:46:69:6e:45:49:44", NULL, NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, 0, NULL }, - /* FINEID cards 1.3.2011 with Samsung chips (round connector) that supports 2048 bit keys. */ - { "3b:7b:94:00:00:80:62:12:51:56:46:69:6e:45:49:44", NULL, NULL, SC_CARD_TYPE_SETCOS_FINEID_V2_2048, 0, NULL }, - /* FINEID card for organisations, chip unknown. */ - { "3b:7b:18:00:00:80:62:01:54:56:46:69:6e:45:49:44", NULL, NULL, SC_CARD_TYPE_SETCOS_FINEID_V2, _FINEID_BROKEN_SELECT_FLAG, NULL }, - /* Swedish NIDEL card */ - { "3b:9f:94:80:1f:c3:00:68:10:44:05:01:46:49:53:45:31:c8:07:90:00:18", NULL, NULL, SC_CARD_TYPE_SETCOS_NIDEL, 0, NULL }, - /* Setcos 4.4.1 */ - { "3b:9f:94:80:1f:c3:00:68:11:44:05:01:46:49:53:45:31:c8:00:00:00:00", "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00", NULL, SC_CARD_TYPE_SETCOS_44, 0, NULL }, - { NULL, NULL, NULL, 0, 0, NULL } +static const struct sc_atr_table setcos_atrs[] = { + /* some Nokia branded SC */ + {"3B:1F:11:00:67:80:42:46:49:53:45:10:52:66:FF:81:90:00", NULL, NULL, SC_CARD_TYPE_SETCOS_GENERIC, 0, NULL}, + /* RSA SecurID 3100 */ + {"3B:9F:94:40:1E:00:67:16:43:46:49:53:45:10:52:66:FF:81:90:00", NULL, NULL, SC_CARD_TYPE_SETCOS_PKI, 0, NULL}, + /* Swedish NIDEL card */ + {"3b:9f:94:80:1f:c3:00:68:10:44:05:01:46:49:53:45:31:c8:07:90:00:18", NULL, NULL, SC_CARD_TYPE_SETCOS_NIDEL, 0, NULL}, + /* Setcos 4.4.1 */ + {"3b:9f:94:80:1f:c3:00:68:11:44:05:01:46:49:53:45:31:c8:00:00:00:00", "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00", NULL, SC_CARD_TYPE_SETCOS_44, 0, NULL}, + {NULL, NULL, NULL, 0, 0, NULL} }; #define SETCOS_IS_EID_APPLET(card) ((card)->type == SC_CARD_TYPE_SETCOS_EID_V2_0 || (card)->type == SC_CARD_TYPE_SETCOS_EID_V2_1) @@ -107,11 +87,6 @@ static int setcos_match_card(sc_card_t *card) i = _sc_match_atr(card, setcos_atrs, &card->type); if (i < 0) { - /* Unknown card, but has the FinEID application for sure */ - if (match_hist_bytes(card, "FinEID", 0)) { - card->type = SC_CARD_TYPE_SETCOS_FINEID_V2_2048; - return 1; - } if (match_hist_bytes(card, "FISE", 0)) { card->type = SC_CARD_TYPE_SETCOS_GENERIC; return 1; @@ -130,7 +105,7 @@ static int setcos_match_card(sc_card_t *card) card->type = SC_CARD_TYPE_SETCOS_EID_V2_1; else { buf[sizeof(buf) - 1] = '\0'; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SetCOS EID applet %s is not supported", (char *) buf); + sc_log(card->ctx, "SetCOS EID applet %s is not supported", (char *) buf); return 0; } return 1; @@ -164,8 +139,6 @@ static int setcos_init(sc_card_t *card) } switch (card->type) { - case SC_CARD_TYPE_SETCOS_FINEID: - case SC_CARD_TYPE_SETCOS_FINEID_V2_2048: case SC_CARD_TYPE_SETCOS_NIDEL: card->cla = 0x00; select_pkcs15_app(card); @@ -189,18 +162,15 @@ static int setcos_init(sc_card_t *card) } switch (card->type) { - case SC_CARD_TYPE_SETCOS_PKI: - case SC_CARD_TYPE_SETCOS_FINEID_V2_2048: - { - unsigned long flags; + case SC_CARD_TYPE_SETCOS_PKI: { + unsigned long flags; - flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1; - flags |= SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_HASH_SHA1; + flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1; + flags |= SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_HASH_SHA1; - _sc_card_add_rsa_alg(card, 1024, flags, 0); - _sc_card_add_rsa_alg(card, 2048, flags, 0); - } - break; + _sc_card_add_rsa_alg(card, 1024, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); + } break; case SC_CARD_TYPE_SETCOS_44: case SC_CARD_TYPE_SETCOS_NIDEL: case SC_CARD_TYPE_SETCOS_EID_V2_0: @@ -237,7 +207,7 @@ static int setcos_construct_fci_44(sc_card_t *card, const sc_file_t *file, u8 *o /* Size (set to 0 for keys/PINs on a Java card) */ if (SETCOS_IS_EID_APPLET(card) && - (file->type == SC_FILE_TYPE_INTERNAL_EF || + (file->type == SC_FILE_TYPE_INTERNAL_EF || (file->type == SC_FILE_TYPE_WORKING_EF && file->ef_structure == 0x22))) buf[0] = buf[1] = 0x00; else { @@ -256,7 +226,7 @@ static int setcos_construct_fci_44(sc_card_t *card, const sc_file_t *file, u8 *o buf[0] = file->shareable ? 0x40 : 0; switch (file->type) { case SC_FILE_TYPE_INTERNAL_EF: /* RSA keyfile */ - buf[0] = 0x11; + buf[0] = 0x11; break; case SC_FILE_TYPE_WORKING_EF: if (file->ef_structure == 0x22) { /* pin-file */ @@ -275,7 +245,7 @@ static int setcos_construct_fci_44(sc_card_t *card, const sc_file_t *file, u8 *o buf[0] |= file->ef_structure & 7; /* set file-type, only for EF, not for DF objects */ } break; - case SC_FILE_TYPE_DF: + case SC_FILE_TYPE_DF: buf[0] = 0x38; break; default: @@ -340,7 +310,7 @@ static int setcos_construct_fci_44(sc_card_t *card, const sc_file_t *file, u8 *o static int setcos_construct_fci(sc_card_t *card, const sc_file_t *file, u8 *out, size_t *outlen) { - if (card->type == SC_CARD_TYPE_SETCOS_44 || + if (card->type == SC_CARD_TYPE_SETCOS_44 || card->type == SC_CARD_TYPE_SETCOS_NIDEL || SETCOS_IS_EID_APPLET(card)) return setcos_construct_fci_44(card, file, out, outlen); @@ -401,11 +371,12 @@ static int setcos_pin_index_44(int *pins, int len, int pin) return i; } } - assert(i != len); /* Too much PINs, shouldn't happen */ + if (i == len) + return SC_ERROR_INTERNAL; return 0; } -/* The ACs are allways for the SETEC_LCSI_ACTIVATED state, even if +/* The ACs are always for the SETEC_LCSI_ACTIVATED state, even if * we have to create the file in the SC_FILE_STATUS_INITIALISATION state. */ static int setcos_create_file_44(sc_card_t *card, sc_file_t *file) { @@ -436,7 +407,7 @@ static int setcos_create_file_44(sc_card_t *card, sc_file_t *file) /* Set file creation status */ sc_file_set_prop_attr(file, &bFileStatus, 1); - /* Build ACI from local structure = get AC for each operation group */ + /* Build ACL from local structure = get AC for each operation group */ if (file->sec_attr_len == 0) { const int* p_idx; int i; @@ -459,6 +430,7 @@ static int setcos_create_file_44(sc_card_t *card, sc_file_t *file) /* Get enabled commands + required Keys/Pins */ memset(bCommands_pin, 0, sizeof(bCommands_pin)); for (i = 7; i >= 0; i--) { /* for each AC Setcos operation */ + int pin_index; bCommands_always <<= 1; bCommands_key <<= 1; @@ -473,10 +445,14 @@ static int setcos_create_file_44(sc_card_t *card, sc_file_t *file) break; case SC_AC_CHV: /* pin */ if ((bNumber & 0x7F) == 0 || (bNumber & 0x7F) > 7) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "SetCOS 4.4 PIN refs can only be 1..7\n"); + sc_log(card->ctx, "SetCOS 4.4 PIN refs can only be 1..7\n"); return SC_ERROR_INVALID_ARGUMENTS; } - bCommands_pin[setcos_pin_index_44(pins, sizeof(pins), (int) bNumber)] |= 1 << i; + pin_index = setcos_pin_index_44(pins, sizeof(pins) / sizeof(pins[0]), (int)bNumber); + if (pin_index < 0) { + return SC_ERROR_INTERNAL; + } + bCommands_pin[pin_index] |= 1 << i; break; case SC_AC_TERM: /* key */ bKeyNumber = bNumber; /* There should be only 1 key */ @@ -485,7 +461,7 @@ static int setcos_create_file_44(sc_card_t *card, sc_file_t *file) } } - /* Add the commands that are allways allowed */ + /* Add the commands that are always allowed */ if (bCommands_always) { bBuf[len++] = 1; bBuf[len++] = bCommands_always; @@ -499,7 +475,7 @@ static int setcos_create_file_44(sc_card_t *card, sc_file_t *file) else bBuf[len++] = pins[i] & 0x07; /* pin ref */ } - /* Add ommands that require the key */ + /* Add commands that require the key */ if (bCommands_key) { bBuf[len++] = 2 | 0x20; /* indicate keyNumber present */ bBuf[len++] = bCommands_key; @@ -569,17 +545,18 @@ static int setcos_set_security_env2(sc_card_t *card, u8 *p; int r, locked = 0; - assert(card != NULL && env != NULL); + if (card == NULL || env == NULL) + return SC_ERROR_INTERNAL; if (card->type == SC_CARD_TYPE_SETCOS_44 || card->type == SC_CARD_TYPE_SETCOS_NIDEL || SETCOS_IS_EID_APPLET(card)) { - if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "asymmetric keyref not supported.\n"); + if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) { + sc_log(card->ctx, "symmetric keyref not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } if (se_num > 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "restore security environment not supported.\n"); + sc_log(card->ctx, "restore security environment not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } } @@ -593,11 +570,11 @@ static int setcos_set_security_env2(sc_card_t *card, break; case SC_SEC_OPERATION_SIGN: /* Should be 0x41 */ - apdu.p1 = ((card->type == SC_CARD_TYPE_SETCOS_FINEID_V2) || - (card->type == SC_CARD_TYPE_SETCOS_FINEID_V2_2048) || - (card->type == SC_CARD_TYPE_SETCOS_44) || - (card->type == SC_CARD_TYPE_SETCOS_NIDEL) || - SETCOS_IS_EID_APPLET(card)) ? 0x41 : 0x81; + apdu.p1 = ((card->type == SC_CARD_TYPE_SETCOS_44) || + (card->type == SC_CARD_TYPE_SETCOS_NIDEL) || + SETCOS_IS_EID_APPLET(card)) + ? 0x41 + : 0x81; apdu.p2 = 0xB6; break; default: @@ -617,9 +594,8 @@ static int setcos_set_security_env2(sc_card_t *card, p += env->file_ref.len; } if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT && - !(card->type == SC_CARD_TYPE_SETCOS_NIDEL || - card->type == SC_CARD_TYPE_SETCOS_FINEID_V2_2048)) { - if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) + !(card->type == SC_CARD_TYPE_SETCOS_NIDEL)) { + if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) *p++ = 0x83; else *p++ = 0x84; @@ -627,26 +603,26 @@ static int setcos_set_security_env2(sc_card_t *card, memcpy(p, env->key_ref, env->key_ref_len); p += env->key_ref_len; } - r = p - sbuf; + r = (int)(p - sbuf); apdu.lc = r; apdu.datalen = r; apdu.data = sbuf; apdu.resplen = 0; if (se_num > 0) { r = sc_lock(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed"); + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); locked = 1; } if (apdu.datalen != 0) { r = sc_transmit_apdu(card, &apdu); if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "%s: APDU transmit failed", sc_strerror(r)); goto err; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "%s: Card returned error", sc_strerror(r)); goto err; } @@ -656,7 +632,7 @@ static int setcos_set_security_env2(sc_card_t *card, sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF2, se_num); r = sc_transmit_apdu(card, &apdu); sc_unlock(card); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); err: if (locked) @@ -674,20 +650,18 @@ static int setcos_set_security_env(sc_card_t *card, tmp.flags &= ~SC_SEC_ENV_ALG_PRESENT; tmp.flags |= SC_SEC_ENV_ALG_REF_PRESENT; if (tmp.algorithm != SC_ALGORITHM_RSA) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Only RSA algorithm supported.\n"); + sc_log(card->ctx, "Only RSA algorithm supported.\n"); return SC_ERROR_NOT_SUPPORTED; } switch (card->type) { case SC_CARD_TYPE_SETCOS_PKI: - case SC_CARD_TYPE_SETCOS_FINEID: - case SC_CARD_TYPE_SETCOS_FINEID_V2_2048: case SC_CARD_TYPE_SETCOS_NIDEL: case SC_CARD_TYPE_SETCOS_44: case SC_CARD_TYPE_SETCOS_EID_V2_0: case SC_CARD_TYPE_SETCOS_EID_V2_1: break; default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Card does not support RSA.\n"); + sc_log(card->ctx, "Card does not support RSA.\n"); return SC_ERROR_NOT_SUPPORTED; break; } @@ -695,7 +669,8 @@ static int setcos_set_security_env(sc_card_t *card, /* potential FIXME: return an error, if an unsupported * pad or hash was requested, although this shouldn't happen. */ - if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) + if ((env->operation == SC_SEC_OPERATION_SIGN && env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01) || + (env->operation == SC_SEC_OPERATION_DECIPHER && env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02)) tmp.algorithm_ref = 0x02; if (tmp.algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) tmp.algorithm_ref |= 0x10; @@ -786,17 +761,18 @@ static void parse_sec_attr_44(sc_file_t *file, const u8 *buf, size_t len) int iOperation; const int* p_idx; - /* Check all sub-AC definitions whitin the total AC */ - while (len > 1) { /* minimum length = 2 */ - int iACLen = buf[iOffset] & 0x0F; + /* Check all sub-AC definitions within the total AC */ + while (len > 1 && (size_t)iOffset < len) { /* minimum length = 2 */ + size_t iACLen = buf[iOffset] & 0x0F; + if (iACLen >= len) + break; - iPinCount = -1; /* default no pin required */ iMethod = SC_AC_NONE; /* default no authentication required */ if (buf[iOffset] & 0X80) { /* AC in adaptive coding */ /* Evaluates only the command-byte, not the optional P1/P2/Option bytes */ - int iParmLen = 1; /* command-byte is always present */ - int iKeyLen = 0; /* Encryption key is optional */ + size_t iParmLen = 1; /* command-byte is always present */ + size_t iKeyLen = 0; /* Encryption key is optional */ if (buf[iOffset] & 0x20) iKeyLen++; if (buf[iOffset+1] & 0x40) iParmLen++; @@ -806,7 +782,10 @@ static void parse_sec_attr_44(sc_file_t *file, const u8 *buf, size_t len) /* Get KeyNumber if available */ if(iKeyLen) { - int iSC = buf[iOffset+iACLen]; + int iSC; + if (len < 1 + iACLen) + break; + iSC = buf[iOffset+iACLen]; switch( (iSC>>5) & 0x03 ){ case 0: @@ -825,11 +804,15 @@ static void parse_sec_attr_44(sc_file_t *file, const u8 *buf, size_t len) /* Get PinNumber if available */ if (iACLen > (1+iParmLen+iKeyLen)) { /* check via total length if pin is present */ + if (len < 1+1+1+(size_t)iParmLen) + break; iKeyRef = buf[iOffset+1+1+iParmLen]; /* PTL + AM-header + parameter-bytes */ iMethod = SC_AC_CHV; } /* Convert SETCOS command to OpenSC command group */ + if (len < 1+2) + break; switch(buf[iOffset+2]){ case 0x2A: /* crypto operation */ iOperation = SC_AC_OP_CRYPTO; @@ -860,10 +843,13 @@ static void parse_sec_attr_44(sc_file_t *file, const u8 *buf, size_t len) } /* Encryption key present ? */ - iPinCount = iACLen - 1; + iPinCount = iACLen > 0 ? (int)iACLen - 1 : 0; if (buf[iOffset] & 0x20) { - int iSC = buf[iOffset + iACLen]; + int iSC; + if (len < 1 + (size_t)iACLen) + break; + iSC = buf[iOffset + iACLen]; switch( (iSC>>5) & 0x03 ) { case 0: @@ -884,6 +870,8 @@ static void parse_sec_attr_44(sc_file_t *file, const u8 *buf, size_t len) /* Pin present ? */ if ( iPinCount > 0 ) { + if (len < 1 + 2) + break; iKeyRef = buf[iOffset + 2]; /* pin ref */ iMethod = SC_AC_CHV; } @@ -908,9 +896,6 @@ static int setcos_select_file(sc_card_t *card, int r; r = iso_ops->select_file(card, in_path, file); - /* Certain FINeID cards for organisations return 6A88 instead of 6A82 for missing files */ - if (card->flags & _FINEID_BROKEN_SELECT_FLAG && r == SC_ERROR_DATA_OBJECT_NOT_FOUND) - return SC_ERROR_FILE_NOT_FOUND; if (r) return r; if (file != NULL) { @@ -930,7 +915,7 @@ static int setcos_list_files(sc_card_t *card, u8 * buf, size_t buflen) int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xAA, 0, 0); - if (card->type == SC_CARD_TYPE_SETCOS_44 || + if (card->type == SC_CARD_TYPE_SETCOS_44 || card->type == SC_CARD_TYPE_SETCOS_NIDEL || SETCOS_IS_EID_APPLET(card)) apdu.cla = 0x80; @@ -938,12 +923,12 @@ static int setcos_list_files(sc_card_t *card, u8 * buf, size_t buflen) apdu.resplen = buflen; apdu.le = buflen > 256 ? 256 : buflen; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (card->type == SC_CARD_TYPE_SETCOS_44 && apdu.sw1 == 0x6A && apdu.sw2 == 0x82) return 0; /* no files found */ if (apdu.resplen == 0) return sc_check_sw(card, apdu.sw1, apdu.sw2); - return apdu.resplen; + return (int)apdu.resplen; } static int setcos_process_fci(sc_card_t *card, sc_file_t *file, @@ -984,12 +969,12 @@ static int setcos_putdata(struct sc_card *card, struct sc_cardctl_setcos_data_ob apdu.data = data_obj->Data; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "PUT_DATA returned error"); + LOG_TEST_RET(card->ctx, r, "PUT_DATA returned error"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } /* Read internal data, e.g. get RSA public key */ @@ -1015,17 +1000,17 @@ static int setcos_getdata(struct sc_card *card, struct sc_cardctl_setcos_data_ob apdu.resplen = data_obj->DataLen; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "GET_DATA returned error"); + LOG_TEST_RET(card->ctx, r, "GET_DATA returned error"); if (apdu.resplen > data_obj->DataLen) r = SC_ERROR_WRONG_LENGTH; else data_obj->DataLen = apdu.resplen; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } /* Generate or store a key */ @@ -1038,30 +1023,30 @@ static int setcos_generate_store_key(sc_card_t *card, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - /* Setup key-generation paramters */ + /* Setup key-generation parameters */ len = 0; if (data->op_type == OP_TYPE_GENERATE) sbuf[len++] = 0x92; /* algo ID: RSA CRT */ else sbuf[len++] = 0x9A; /* algo ID: EXTERNALLY GENERATED RSA CRT */ - sbuf[len++] = 0x00; + sbuf[len++] = 0x00; sbuf[len++] = data->mod_len / 256; /* 2 bytes for modulus bitlength */ sbuf[len++] = data->mod_len % 256; sbuf[len++] = data->pubexp_len / 256; /* 2 bytes for pubexp bitlength */ sbuf[len++] = data->pubexp_len % 256; - memcpy(sbuf + len, data->pubexp, (data->pubexp_len + 7) / 8); - len += (data->pubexp_len + 7) / 8; + memcpy(sbuf + len, data->pubexp, BYTES4BITS(data->pubexp_len)); + len += BYTES4BITS(data->pubexp_len); if (data->op_type == OP_TYPE_STORE) { sbuf[len++] = data->primep_len / 256; sbuf[len++] = data->primep_len % 256; - memcpy(sbuf + len, data->primep, (data->primep_len + 7) / 8); - len += (data->primep_len + 7) / 8; + memcpy(sbuf + len, data->primep, BYTES4BITS(data->primep_len)); + len += BYTES4BITS(data->primep_len); sbuf[len++] = data->primeq_len / 256; sbuf[len++] = data->primeq_len % 256; - memcpy(sbuf + len, data->primeq, (data->primeq_len + 7) / 8); - len += (data->primeq_len + 7) / 8; + memcpy(sbuf + len, data->primeq, BYTES4BITS(data->primeq_len)); + len += BYTES4BITS(data->primeq_len); } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, 0x00); @@ -1071,12 +1056,12 @@ static int setcos_generate_store_key(sc_card_t *card, apdu.lc = len; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "STORE/GENERATE_KEY returned error"); + LOG_TEST_RET(card->ctx, r, "STORE/GENERATE_KEY returned error"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } static int setcos_activate_file(sc_card_t *card) @@ -1089,12 +1074,12 @@ static int setcos_activate_file(sc_card_t *card) apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "ACTIVATE_FILE returned error"); + LOG_TEST_RET(card->ctx, r, "ACTIVATE_FILE returned error"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } static int setcos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) diff --git a/src/libopensc/card-skeid.c b/src/libopensc/card-skeid.c new file mode 100644 index 0000000000..882d53b84b --- /dev/null +++ b/src/libopensc/card-skeid.c @@ -0,0 +1,187 @@ +/* + * card-skeid.c: Support for (CardOS based) cards issued as identity documents in Slovakia + * + * Copyright (C) 2022 Juraj Šarinay + * + * 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 + * + * based on card-cardos.c + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "internal.h" + +#define SKEID_KNOWN_URL_LEN 46 + +static const struct sc_card_operations *iso_ops = NULL; + +static struct sc_card_operations skeid_ops; +static struct sc_card_driver skeid_drv = { + "Slovak eID card", + "skeid", + &skeid_ops, + NULL, 0, NULL +}; + +static const struct sc_atr_table skeid_atrs[] = { + /* Slovak eID v3 - CardOS 5.4 + * + * The ATR was intentionally omitted from minidriver_registration[] within win32/customactions.cpp + * as it is identical to that of CardOS v5.4 and therefore already included. + * Any new ATR may need an entry in minidriver_registration[]. */ + {"3b:d2:18:00:81:31:fe:58:c9:04:11", NULL, NULL, SC_CARD_TYPE_SKEID_V3, 0, NULL}, + {NULL, NULL, NULL, 0, 0, NULL} +}; + +static int skeid_known_url(sc_card_t * card) +{ + const struct sc_aid skeid_aid_eid = {{0xE8, 0x07, 0x04, 0x00, 0x7F, 0x00, 0x07, 0x03, 0x02}, 9}; + const char *known_url = "\x80\x01\x00\x5F\x50\x28http://www.minv.sk/cif/cif-sk-eid-v3.xml"; + u8 buf[SKEID_KNOWN_URL_LEN]; + + sc_path_t url_path; + + int r = SC_ERROR_WRONG_CARD; + + sc_path_set(&url_path, SC_PATH_TYPE_DF_NAME, skeid_aid_eid.value, skeid_aid_eid.len, 0, 0); + + if (sc_select_file(card, &url_path, NULL) == SC_SUCCESS + && sc_get_data(card, 0x7F62, buf, SKEID_KNOWN_URL_LEN) == SKEID_KNOWN_URL_LEN + && !memcmp(buf, known_url, SKEID_KNOWN_URL_LEN)) + r = SC_SUCCESS; + + return r; +} + +static int skeid_match_card(sc_card_t *card) +{ + if (_sc_match_atr(card, skeid_atrs, &card->type) < 0 || skeid_known_url(card) != SC_SUCCESS) + return 0; + + sc_log(card->ctx, "Slovak eID card v3 (CardOS 5.4)"); + + return 1; +} + +static int skeid_get_serialnr(sc_card_t *card) +{ + int r; + sc_apdu_t apdu; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x81); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 256; + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) + return SC_ERROR_INTERNAL; + if (apdu.resplen == 8) { + /* cache serial number */ + memcpy(card->serialnr.value, rbuf, 8); + card->serialnr.len = 8; + } else { + sc_log(card->ctx, "unexpected response to GET DATA serial number"); + return SC_ERROR_INTERNAL; + } + return SC_SUCCESS; +} + +static int skeid_init(sc_card_t *card) +{ + const unsigned long flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE; + const size_t data_field_length = 437; + int r; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + card->name = "Slovak eID (CardOS)"; + card->type = SC_CARD_TYPE_SKEID_V3; + card->cla = 0x00; + + r = skeid_get_serialnr(card); + LOG_TEST_RET(card->ctx, r, "Error reading serial number."); + + card->caps |= SC_CARD_CAP_APDU_EXT | SC_CARD_CAP_ISO7816_PIN_INFO; + + card->max_send_size = data_field_length - 6; +#ifdef _WIN32 + /* see card-cardos.c */ + if (card->reader->max_send_size == 255 && card->reader->max_recv_size == 256) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "resetting reader to use data_field_length"); + card->reader->max_send_size = data_field_length - 6; + card->reader->max_recv_size = data_field_length - 3; + } +#endif + + card->max_send_size = sc_get_max_send_size(card); /* see card-cardos.c */ + card->max_recv_size = data_field_length - 2; + card->max_recv_size = sc_get_max_recv_size(card); + + r = _sc_card_add_rsa_alg(card, 3072, flags, 0); + + LOG_FUNC_RETURN(card->ctx, r); +} + +static int skeid_set_security_env(sc_card_t *card, + const sc_security_env_t *env, + int se_num) +{ + int key_id; + int r; + + if (card == NULL || env == NULL) + return SC_ERROR_INTERNAL; + + if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) { + sc_log(card->ctx, "No or invalid key reference"); + return SC_ERROR_INVALID_ARGUMENTS; + } + + /* here we follow the behaviour of the proprietary driver accompanying the card + * where security operations are preceded by MSE RESTORE rather than MSE SET + */ + key_id = env->key_ref[0]; + r = sc_restore_security_env(card, key_id); + + return r; +} + +static int skeid_logout(sc_card_t *card) +{ + int r; + sc_path_t path; + + sc_format_path("3F00", &path); + r = sc_select_file(card, &path, NULL); + return r; + +} + +struct sc_card_driver * sc_get_skeid_driver(void) +{ + if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; + skeid_ops = *iso_ops; + skeid_ops.match_card = skeid_match_card; + skeid_ops.init = skeid_init; + skeid_ops.set_security_env = skeid_set_security_env; + skeid_ops.logout = skeid_logout; + return &skeid_drv; +} diff --git a/src/libopensc/card-srbeid.c b/src/libopensc/card-srbeid.c new file mode 100644 index 0000000000..22406a21ae --- /dev/null +++ b/src/libopensc/card-srbeid.c @@ -0,0 +1,317 @@ +/* + * card-srbeid.c: Driver for Serbian cards using the CardEdge PKI applet. + * + * Serbian eID, health insurance, and Chamber of Commerce cards use the + * same CardEdge PKCS#15 applet. Cards are matched by ATR whitelist, + * with a PKCS#15 AID select as a confirmation step. + * + * Copyright (C) 2026 LibreSCRS contributors + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "card-srbeid.h" +#include "internal.h" +#include "log.h" + +/* MSE SET algorithm byte (tag 0x80). */ +#define CE_MSE_ALG_RSA_PKCS1 0x02u /* card adds PKCS#1 padding */ +#define CE_MSE_ALG_RSA_RAW 0x00u /* raw RSA; OpenSC pads */ + +static struct sc_card_operations srbeid_ops; +static const struct sc_card_operations *iso_ops; + +static struct sc_card_driver srbeid_drv = { + "Serbian CardEdge driver", + "srbeid", + &srbeid_ops, + NULL, 0, NULL}; + +/* Full ATR list provided by NetSeT (the CardEdge PKI applet vendor). Covers + * citizen eID, foreigner eID, PKS Chamber of Commerce, and RFZO health + * insurance cards across their various underlying card platforms. */ +static const struct sc_atr_table srbeid_atrs[] = { + {"3B:FF:94:00:00:81:31:80:43:80:31:80:65:B0:85:02:01:F3:12:0F:FF:82:90:00:79", NULL, "Serbian eID (Gemalto Multiapp 80K)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {"3B:F8:13:00:00:81:31:FE:45:4A:43:4F:50:76:32:34:31:B7", NULL, "Serbian eID (NXP JCOP v2.4.1 80K)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {"3B:FA:13:00:00:81:31:FE:45:4A:43:4F:50:32:31:56:32:33:31:91", NULL, "Serbian eID (NXP JCOP21 v2.3.1)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {"3B:F4:13:00:00:81:31:FE:45:52:46:5A:4F:ED", NULL, "Serbian health card (NXP JCOP21 v2.4.1)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {"3B:7A:96:00:00:80:65:A2:01:01:02:3D:72:D6:43", NULL, "Serbian eID (Gemalto Multiapp 80K IDCore10)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {"3B:F9:96:00:00:80:31:FE:45:53:43:45:37:20:47:43:4E:33:5E", NULL, "Serbian eID (SmartCafe Expert v7.0)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {"3B:FD:94:00:00:81:31:80:43:80:31:80:65:B1:F3:01:07:0F:83:01:90:00:17", NULL, "Serbian eID (Gemalto Dual-Interface 144K Contact)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {"3B:89:80:01:53:43:45:2E:37:20:20:20:20:44", NULL, "Serbian eID (SmartCafe Expert v7.0 Contactless)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {"3B:F9:96:00:00:80:31:FE:45:53:43:45:37:20:20:00:20:20:07", NULL, "Serbian eID (SmartCafe Expert v7.1)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {"3B:DE:97:00:80:31:FE:45:53:43:45:20:38:2E:30:2D:43:31:56:30:0D:0A:2E", NULL, "Serbian PKS card (SmartCafe Expert v8.0 B)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {"3B:9E:96:80:31:FE:45:53:43:45:20:38:2E:30:2D:43:31:56:30:0D:0A:6F", NULL, "Serbian eID (SmartCafe Expert v8.0)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {"3B:9E:96:80:31:FE:45:53:43:45:20:38:2E:30:2D:43:32:56:30:0D:0A:6C", NULL, "Serbian eID (SmartCafe Expert v8.0 C2)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {"3B:D5:18:FF:81:91:FE:1F:C3:80:73:C8:21:10:0A", NULL, "Serbian eID (NXP JCOP4 P71)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {"3B:DE:97:00:80:31:FE:45:53:43:45:20:38:2E:30:2D:43:32:56:30:0D:0A:2D", NULL, "Serbian PKS card (SmartCafe Expert v8.0 C2 B)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {"3B:9E:97:80:31:FE:45:53:43:45:20:38:2E:30:2D:43:31:56:30:0D:0A:6E", NULL, "Serbian health card (SmartCafe Expert v8.0 S)", SC_CARD_TYPE_SRBEID_BASE, 0, NULL}, + {NULL, NULL, NULL, 0, 0, NULL} +}; + +static int +srbeid_match_card(sc_card_t *card) +{ + int i = _sc_match_atr(card, srbeid_atrs, &card->type); + if (i >= 0 && iso7816_select_aid(card, AID_PKCS15, AID_PKCS15_LEN, NULL, NULL) == SC_SUCCESS) { + card->name = srbeid_atrs[i].name; + return 1; + } + return 0; +} + +static int +srbeid_init(sc_card_t *card) +{ + LOG_FUNC_CALLED(card->ctx); + + card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; + + /* Keep the card's own PKCS#1 signing, and add raw RSA on top: that gives + * CKM_RSA_X_509 and makes 2048-bit decrypt work (OpenSC does the padding). */ + _sc_card_add_rsa_alg(card, 2048, + SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_RAW | + SC_ALGORITHM_RSA_HASH_NONE, + 0); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +/* + * select_file — handle CardEdge's proprietary 10-byte FCI response. + * + * CardEdge FCI layout (10 bytes, big-endian): + * [FID_H FID_L Size_H Size_L ACL*6] + * + * iso7816_select_file() would try to parse this as ISO 7816-4 TLV (tag 0x6F) + * and fail with SC_ERROR_UNKNOWN_DATA_RECEIVED. + * + * DF_NAME (AID) selection is delegated to the ISO layer. + */ +static int +srbeid_select_file(sc_card_t *card, const sc_path_t *in_path, + sc_file_t **file_out) +{ + sc_apdu_t apdu; + u8 fci[16]; + sc_file_t *file; + int r; + + if (in_path->type == SC_PATH_TYPE_DF_NAME) + return iso_ops->select_file(card, in_path, file_out); + + /* AID-only path (path.len==0, path.aid.len>0): PKCS#15 layer wants + * to select the applet before a PIN or key operation. */ + if (in_path->len == 0 && in_path->aid.len > 0) + return iso7816_select_aid(card, in_path->aid.value, + in_path->aid.len, NULL, NULL); + + if (in_path->len != 2) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00); + apdu.data = in_path->value; + apdu.datalen = 2; + apdu.lc = 2; + apdu.resp = fci; + apdu.resplen = sizeof(fci); + apdu.le = 10; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "SELECT FILE failed"); + + if (apdu.resplen < 4) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + + if (file_out) { + file = sc_file_new(); + if (!file) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + + file->id = ((unsigned)in_path->value[0] << 8) | in_path->value[1]; + file->path = *in_path; + file->size = ((size_t)fci[2] << 8) | (size_t)fci[3]; + file->type = SC_FILE_TYPE_WORKING_EF; + *file_out = file; + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +/* + * set_security_env — send MSE SET to the card. + * + * The PKCS#15 layer selects the PKI applet via the AID attached to + * key_info.path before calling this function (see select_key_file() + * in pkcs15-sec.c). + * + * OpenSC populates env->key_ref[0] from key_info.key_reference (low byte). + * The high byte is always 0x60 (CE_KEYS_BASE_FID >> 8) for all CardEdge + * key FIDs, so the full 2-byte FID is reconstructed here. + * + * MSE SET template P2: 0xB6 for signing, 0xB8 for deciphering. + */ +static int +srbeid_set_security_env(sc_card_t *card, + const struct sc_security_env *env, int se_num) +{ + sc_apdu_t apdu; + u8 mse_data[7]; + unsigned key_ref; + u8 p2; + int r; + + LOG_FUNC_CALLED(card->ctx); + (void)se_num; + + /* Extract key FID. */ + if ((env->flags & SC_SEC_ENV_FILE_REF_PRESENT) && env->file_ref.len >= 2) { + key_ref = ((unsigned)env->file_ref.value[0] << 8) | (unsigned)env->file_ref.value[1]; + } else if ((env->flags & SC_SEC_ENV_KEY_REF_PRESENT) && env->key_ref_len >= 1) { + key_ref = CE_KEYS_BASE_FID | (unsigned)env->key_ref[0]; + } else { + sc_log(card->ctx, "srbeid: set_security_env: no key reference"); + return SC_ERROR_INCORRECT_PARAMETERS; + } + + /* Determine MSE SET template from operation type. */ + switch (env->operation) { + case SC_SEC_OPERATION_SIGN: + p2 = 0xB6; + break; + case SC_SEC_OPERATION_DECIPHER: + p2 = 0xB8; + break; + default: + return SC_ERROR_NOT_SUPPORTED; + } + + /* MSE SET: tag 0x80 = algorithm, tag 0x84 = key ref (2 bytes BE). + * The algorithm byte tells the card to pad (PKCS#1) or not (raw RSA). */ + mse_data[0] = 0x80; + mse_data[1] = 0x01; + mse_data[2] = (env->algorithm_flags & SC_ALGORITHM_RSA_RAW) + ? CE_MSE_ALG_RSA_RAW + : CE_MSE_ALG_RSA_PKCS1; + mse_data[3] = 0x84; + mse_data[4] = 0x02; + mse_data[5] = (u8)((key_ref >> 8) & 0xFF); + mse_data[6] = (u8)(key_ref & 0xFF); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, p2); + apdu.data = mse_data; + apdu.datalen = sizeof(mse_data); + apdu.lc = sizeof(mse_data); + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "MSE SET failed"); + + sc_log(card->ctx, "srbeid: set_security_env: key_ref=0x%04x p2=0x%02x algo=0x%02x", + key_ref, p2, mse_data[2]); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +/* + * One PSO (INS=0x2A, given P1). A 256-byte block exceeds the short-APDU Lc, so + * its first byte is carried in P2; a smaller DigestInfo is sent as-is. + */ +static int +srbeid_pso(sc_card_t *card, u8 p1, const u8 *data, size_t datalen, u8 *out, + size_t outlen) +{ + sc_apdu_t apdu; + u8 resp[256]; + u8 p2 = 0x00; + int r; + + LOG_FUNC_CALLED(card->ctx); + + if (datalen == 0 || datalen > sizeof(resp)) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + if (datalen > SC_MAX_APDU_DATA_SIZE) { + p2 = data[0]; + ++data; + --datalen; + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, p1, p2); + apdu.data = data; + apdu.datalen = apdu.lc = datalen; + apdu.resp = resp; + apdu.resplen = sizeof(resp); + apdu.le = sizeof(resp); + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "PSO failed"); + + if (apdu.resplen > outlen) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); + memcpy(out, resp, apdu.resplen); + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); +} + +/* Signing: PSO COMPUTE SIGNATURE (00 2A 9E). */ +static int +srbeid_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, + u8 *out, size_t outlen) +{ + return srbeid_pso(card, 0x9E, data, datalen, out, outlen); +} + +/* + * Decryption: PSO DECIPHER (00 2A 80) so the card is told the operation. The + * applet reads P2 as an ISO template and rejects a cryptogram starting with + * 0x86, so fall back to COMPUTE SIGNATURE (00 2A 9E), which is the same RSA + * operation on this card. + */ +static int +srbeid_decipher(sc_card_t *card, const u8 *crgram, size_t crgram_len, u8 *out, + size_t outlen) +{ + int r = srbeid_pso(card, 0x80, crgram, crgram_len, out, outlen); + + if (r == SC_ERROR_INCORRECT_PARAMETERS) + r = srbeid_pso(card, 0x9E, crgram, crgram_len, out, outlen); + return r; +} + +struct sc_card_driver * +sc_get_srbeid_driver(void) +{ + /* Save ISO ops for delegation, then override what we handle. */ + iso_ops = sc_get_iso7816_driver()->ops; + srbeid_ops = *iso_ops; + srbeid_ops.match_card = srbeid_match_card; + srbeid_ops.init = srbeid_init; + srbeid_ops.select_file = srbeid_select_file; + srbeid_ops.set_security_env = srbeid_set_security_env; + srbeid_ops.compute_signature = srbeid_compute_signature; + srbeid_ops.decipher = srbeid_decipher; + + return &srbeid_drv; +} diff --git a/src/libopensc/card-srbeid.h b/src/libopensc/card-srbeid.h new file mode 100644 index 0000000000..64f3fd0d50 --- /dev/null +++ b/src/libopensc/card-srbeid.h @@ -0,0 +1,35 @@ +/* + * card-srbeid.h: Shared definitions for Serbian CardEdge card and PKCS#15 drivers. + * + * Copyright (C) 2026 LibreSCRS contributors + * + * 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 + */ + +#ifndef CARD_SRBEID_H +#define CARD_SRBEID_H + +#include "types.h" + +/* CardEdge PKI applet AID (A0 00 00 00 63 50 4B 43 53 2D 31 35) */ +static const u8 AID_PKCS15[] = { + 0xA0, 0x00, 0x00, 0x00, 0x63, + 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35}; +#define AID_PKCS15_LEN (sizeof(AID_PKCS15)) + +/* Base address of key files: key FID = CE_KEYS_BASE_FID | container/type bits */ +#define CE_KEYS_BASE_FID 0x6000u + +#endif /* CARD_SRBEID_H */ diff --git a/src/libopensc/card-starcos.c b/src/libopensc/card-starcos.c index 5a46f164c3..429e5237fa 100644 --- a/src/libopensc/card-starcos.c +++ b/src/libopensc/card-starcos.c @@ -1,7 +1,7 @@ /* * card-starcos.c: Support for STARCOS SPK 2.3 cards * - * Copyright (C) 2003 Jörn Zukowski and + * Copyright (C) 2003 Jörn Zukowski and * Nils Larsch , TrustCenter AG * * This library is free software; you can redistribute it and/or @@ -16,10 +16,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -31,26 +31,37 @@ #include "internal.h" #include "iso7816.h" -static struct sc_atr_table starcos_atrs[] = { +// clang-format off +static const struct sc_atr_table starcos_atrs[] = { { "3B:B7:94:00:c0:24:31:fe:65:53:50:4b:32:33:90:00:b4", NULL, NULL, SC_CARD_TYPE_STARCOS_GENERIC, 0, NULL }, { "3B:B7:94:00:81:31:fe:65:53:50:4b:32:33:90:00:d1", NULL, NULL, SC_CARD_TYPE_STARCOS_GENERIC, 0, NULL }, { "3b:b7:18:00:c0:3e:31:fe:65:53:50:4b:32:34:90:00:25", NULL, NULL, SC_CARD_TYPE_STARCOS_GENERIC, 0, NULL }, - /* STARCOS 3.4 */ { "3b:d8:18:ff:81:b1:fe:45:1f:03:80:64:04:1a:b4:03:81:05:61", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_4, 0, NULL }, + { "3b:d3:96:ff:81:b1:fe:45:1f:07:80:81:05:2d", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_4, 0, NULL }, + { "3B:9B:96:C0:0A:31:FE:45:80:67:04:1E:B5:01:00:89:4C:81:05:45", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_5, 0, NULL }, + { "3B:DB:96:FF:81:31:FE:45:80:67:05:34:B5:02:01:C0:A1:81:05:3C", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_5, 0, NULL }, + { "3B:D9:96:FF:81:31:FE:45:80:31:B8:73:86:01:C0:81:05:02", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_5, 0, NULL }, + { "3B:DF:96:FF:81:31:FE:45:80:5B:44:45:2E:42:4E:4F:54:4B:31:31:31:81:05:A0", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_5, 0, NULL }, + { "3B:DF:96:FF:81:31:FE:45:80:5B:44:45:2E:42:4E:4F:54:4B:31:30:30:81:05:A0", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_5, 0, NULL }, + { "3B:D9:96:FF:81:31:FE:45:80:31:B8:73:86:01:E0:81:05:22", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_5, 0, NULL }, + { "3B:D0:97:FF:81:B1:FE:45:1F:07:2B", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_4, 0, NULL }, + { "3B:D0:96:FF:81:B1:FE:45:1F:07:2A", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_4, 0, NULL }, + { "3b:df:96:ff:81:31:fe:45:80:5b:44:45:2e:42:41:5f:53:43:33:35:32:81:05:b5", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_5_ESIGN, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; +// clang-format on static struct sc_card_operations starcos_ops; static struct sc_card_operations *iso_ops = NULL; static struct sc_card_driver starcos_drv = { - "STARCOS SPK 2.3/2.4/3.4", + "STARCOS", "starcos", &starcos_ops, NULL, 0, NULL }; -static const struct sc_card_error starcos_errors[] = +static const struct sc_card_error starcos_errors[] = { { 0x6600, SC_ERROR_INCORRECT_PARAMETERS, "Error setting the security env"}, { 0x66F0, SC_ERROR_INCORRECT_PARAMETERS, "No space left for padding"}, @@ -72,26 +83,42 @@ static const struct sc_card_error starcos_errors[] = typedef struct starcos_ex_data_st { int sec_ops; /* the currently selected security operation, * i.e. SC_SEC_OPERATION_AUTHENTICATE etc. */ - unsigned int fix_digestInfo; + unsigned long fix_digestInfo; + unsigned int pin_encoding; } starcos_ex_data; +/* + This constant allows signing or + decrypting with RSA keys up to 4096 bits. +*/ +#define STARCOS3X_PROBE_APDU_LENGTH 512 + +#define PIN_ENCODING_DETERMINE 0 +#define PIN_ENCODING_DEFAULT SC_PIN_ENCODING_GLP + +// known pin formats for StarCOS 3.x cards +#define PIN_FORMAT_F1 0x11 +#define PIN_FORMAT_F2 0x12 +#define PIN_FORMAT_RSA 0x1230 +#define PIN_FORMAT_BCD 0x13 +#define PIN_FORMAT_ASCII 0x14 +#define PIN_FORMAT_PW_ASCII 0x21 +// default is the Format 2 PIN Block which is GLP in OpenSC +#define PIN_FORMAT_DEFAULT PIN_FORMAT_F2 + #define CHECK_NOT_SUPPORTED_V3_4(card) \ do { \ if ((card)->type == SC_CARD_TYPE_STARCOS_V3_4) { \ - sc_debug((card)->ctx, SC_LOG_DEBUG_NORMAL, \ + sc_log((card)->ctx, \ "not supported for STARCOS 3.4 cards"); \ return SC_ERROR_NOT_SUPPORTED; \ } \ } while (0); -#define CHECK_ONLY_SUPPORTED_V3_4(card) \ - do { \ - if ((card)->type != SC_CARD_TYPE_STARCOS_V3_4) { \ - sc_debug((card)->ctx, SC_LOG_DEBUG_NORMAL, \ - "only supported for STARCOS 3.4 cards"); \ - return SC_ERROR_NOT_SUPPORTED; \ - } \ - } while (0); +/* card type helpers */ +#define IS_V34(card) card->type == SC_CARD_TYPE_STARCOS_V3_4 || card->type == SC_CARD_TYPE_STARCOS_V3_4_ESIGN +#define IS_V35(card) card->type == SC_CARD_TYPE_STARCOS_V3_5 || card->type == SC_CARD_TYPE_STARCOS_V3_5_ESIGN +#define IS_V3x(card) IS_V34(card) || IS_V35(card) /* the starcos part */ static int starcos_match_card(sc_card_t *card) @@ -104,6 +131,230 @@ static int starcos_match_card(sc_card_t *card) return 1; } + +typedef struct starcos_ctrl_ref_template_st { + unsigned int transmission_format; +#if 0 + // not relevant values for now + unsigned int se_reference; + unsigned int ssec_initial_value; +#endif +} starcos_ctrl_ref_template; + +// tags +#define TAG_STARCOS35_PIN_REFERENCE 0x88 +#define TAG_STARCOS3X_SUPPORTED_SEC_MECHANISMS_tag 0x7B +#define TAG_STARCOS3X_CTRL_REF_TEMPLATE 0xA4 +#define TAG_STARCOS3X_TRANSMISSION_FORMAT 0x89 + +static const char * starcos_ef_pwdd = "3F000015"; +static const char * starcos_ef_keyd = "3F000013"; + +/** + * Parses supported security mechanisms record data. + * It returns SC_SUCCESS and the ctrl_ref_template structure data on success + */ +static int starcos_parse_supported_sec_mechanisms(struct sc_card *card, const unsigned char * buf, size_t buflen, starcos_ctrl_ref_template * ctrl_ref_template) +{ + struct sc_context *ctx = card->ctx; + const unsigned char *supported_sec_mechanisms_tag = NULL; + size_t taglen; + + LOG_FUNC_CALLED(ctx); + + supported_sec_mechanisms_tag = sc_asn1_find_tag(ctx, buf, buflen, TAG_STARCOS3X_SUPPORTED_SEC_MECHANISMS_tag, &taglen); + if (supported_sec_mechanisms_tag != NULL && taglen >= 1) { + const unsigned char *tx_fmt_tag = NULL; + const unsigned char *ctrl_ref_template_tag = NULL; + size_t supported_sec_mechanisms_taglen = taglen; + + // control-reference template is either included in the supported security mechanisms tag or it can be the CRT tag itself (EF.PWDD) + ctrl_ref_template_tag = sc_asn1_find_tag(ctx, supported_sec_mechanisms_tag, taglen, TAG_STARCOS3X_CTRL_REF_TEMPLATE, &taglen); + if ( ctrl_ref_template_tag == NULL || taglen == 0 ) { + ctrl_ref_template_tag = supported_sec_mechanisms_tag; + taglen = supported_sec_mechanisms_taglen; + } + + tx_fmt_tag = sc_asn1_find_tag(ctx, ctrl_ref_template_tag, taglen, TAG_STARCOS3X_TRANSMISSION_FORMAT, &taglen); + if ( tx_fmt_tag != NULL && taglen >= 1 ) { + ctrl_ref_template->transmission_format = *(tx_fmt_tag + 0); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + } + } + + LOG_FUNC_RETURN(ctx, SC_ERROR_TEMPLATE_NOT_FOUND); +} + +static int starcos_determine_pin_format34(sc_card_t *card, unsigned int * pin_format) +{ + struct sc_context *ctx = card->ctx; + struct sc_path path; + struct sc_file *file; + unsigned char buf[256]; + int rv; + int retval = SC_SUCCESS; + int rec_no=1; + + LOG_FUNC_CALLED(ctx); + + sc_format_path(starcos_ef_pwdd, &path); + rv = sc_select_file(card, &path, &file); + LOG_TEST_RET(ctx, rv, "Cannot select EF.PWDD file"); + + if ( (rv = sc_read_record(card, rec_no, 0, buf, sizeof(buf), SC_RECORD_BY_REC_NR)) > 0 ) { + starcos_ctrl_ref_template ctrl_ref_template; + memset((void*)&ctrl_ref_template, 0, sizeof(ctrl_ref_template)); + rv = starcos_parse_supported_sec_mechanisms(card, buf, rv, &ctrl_ref_template); + if ( rv == SC_SUCCESS ) { + *pin_format = ctrl_ref_template.transmission_format; + sc_log(ctx, "Determined StarCOS 3.4 PIN format: 0x%x", *pin_format); + } else { + sc_log(ctx, "Failed to parse record %d of EF.PWD, err=%d", rec_no, rv); + retval = rv; + } + } else { + sc_log(ctx, "Failed to read record %d of EF.PWDD, err=%d", rec_no, rv); + retval = rv; + } + + sc_file_free(file); + LOG_FUNC_RETURN(ctx, retval); +} + +static int starcos_determine_pin_format35(sc_card_t *card, unsigned int * pin_format) +{ + struct sc_context *ctx = card->ctx; + struct sc_path path; + struct sc_file *file; + unsigned char buf[256]; + int rv; + int retval = SC_ERROR_RECORD_NOT_FOUND; + int rec_no=1; + starcos_ctrl_ref_template ctrl_ref_template; + + LOG_FUNC_CALLED(ctx); + + sc_format_path(starcos_ef_keyd, &path); + rv = sc_select_file(card, &path, &file); + LOG_TEST_RET(ctx, rv, "Cannot select EF.KEYD file"); + + while ( (rv = sc_read_record(card, rec_no++, 0, buf, sizeof(buf), SC_RECORD_BY_REC_NR)) > 0 ) { + if ( buf[0] != TAG_STARCOS35_PIN_REFERENCE ) continue; + + memset((void*)&ctrl_ref_template, 0, sizeof(ctrl_ref_template)); + rv = starcos_parse_supported_sec_mechanisms(card, buf, rv, &ctrl_ref_template); + if ( rv == SC_SUCCESS ) { + *pin_format = ctrl_ref_template.transmission_format; + sc_log(ctx, "Determined StarCOS 3.5 PIN format: 0x%x", *pin_format); + retval = rv; + // assuming that all PINs and PUKs have the same transmission format + break; + } else { + sc_log(ctx, "Failed to parse record %d of EF.KEYD, err=%d", rec_no-1, rv); + retval = rv; + } + } + + sc_file_free(file); + LOG_FUNC_RETURN(ctx, retval); +} + +/** + * Determine v3.x PIN encoding by parsing either + * EF.PWDD (for v3.4) or EF.KEYD (for v3.5) + * + * It returns an OpenSC PIN encoding, using the default value on failure + */ +static unsigned int starcos_determine_pin_encoding(sc_card_t *card) +{ + unsigned int pin_format = PIN_FORMAT_DEFAULT; + unsigned int encoding = PIN_ENCODING_DETERMINE; + + if ( IS_V34(card) ) { + starcos_determine_pin_format34(card, &pin_format); + } else if ( IS_V35(card) ) { + starcos_determine_pin_format35(card, &pin_format); + } + + switch (pin_format) { + case PIN_FORMAT_PW_ASCII: + case PIN_FORMAT_ASCII: + encoding = SC_PIN_ENCODING_ASCII; + break; + case PIN_FORMAT_BCD: + encoding = SC_PIN_ENCODING_BCD; + break; + case PIN_FORMAT_F1: + case PIN_FORMAT_F2: + encoding = SC_PIN_ENCODING_GLP; + break; + } + + sc_log(card->ctx, "Determined PIN encoding: %d", encoding); + return encoding; +} + +/** + * Returns 1 if an extended APDU can be sent to the card + * with the given card reader. Otherwise returns 0. + */ +static int starcos_probe_reader_for_ext_apdu(sc_card_t * card) { + sc_apdu_t apdu; + int rv; + /* try to read STARCOS3X_PROBE_APDU_LENGTH bytes */ + u8 data[STARCOS3X_PROBE_APDU_LENGTH]; + + /* Get Data: Get Chip Serial Number */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_EXT, 0xCA, 0x9F, 0x6C); + apdu.cla = 0xA0; + apdu.resp = data; + apdu.resplen = sizeof(data); + apdu.le = apdu.resplen; + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, rv, "Failed to send Get Data ext. APDU"); + return (apdu.sw1 == 0x90 && apdu.sw2 == 0x00); +} + +static int starcos_select_mf(sc_card_t * card) { + sc_apdu_t apdu; + const u8 mf_buf[2] = {0x3f, 0x00}; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x00, 0x0C); + apdu.le = 0; + apdu.lc = 2; + apdu.data = mf_buf; + apdu.datalen = 2; + apdu.resplen = 0; + + return sc_transmit_apdu(card, &apdu); +} + +static int starcos_select_aid(sc_card_t *card, + const u8 aid[16], size_t len, + sc_file_t **file_out); + +/* returns 1 if the card has the eSign app with AID A0:00:00:02:45:53:69:67:6E + otherwise returns 0 + */ +static int starcos_has_esign_app(sc_card_t * card) { + static const char * starcos_esign_aid = "A0:00:00:02:45:53:69:67:6E"; + int rv; + + rv = starcos_select_mf(card); + if ( rv == SC_SUCCESS ) { + u8 aid[SC_MAX_PATH_SIZE]; + size_t len = sizeof(aid); + + rv = sc_hex_to_bin(starcos_esign_aid, aid, &len); + LOG_TEST_RET(card->ctx, rv, "Failed to convert eSing AID"); + rv = starcos_select_aid(card, aid, len, NULL); + if ( rv == SC_SUCCESS ) { + starcos_select_mf(card); + } + } + return ( rv == SC_SUCCESS ); +} + static int starcos_init(sc_card_t *card) { unsigned int flags; @@ -113,11 +364,12 @@ static int starcos_init(sc_card_t *card) if (ex_data == NULL) return SC_ERROR_OUT_OF_MEMORY; - card->name = "STARCOS SPK 2.3"; + card->name = "STARCOS"; card->cla = 0x00; card->drv_data = (void *)ex_data; + ex_data->pin_encoding = PIN_ENCODING_DETERMINE; - flags = SC_ALGORITHM_RSA_PAD_PKCS1 + flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_ONBOARD_KEY_GEN | SC_ALGORITHM_RSA_PAD_ISO9796 | SC_ALGORITHM_RSA_HASH_NONE @@ -126,15 +378,16 @@ static int starcos_init(sc_card_t *card) | SC_ALGORITHM_RSA_HASH_RIPEMD160 | SC_ALGORITHM_RSA_HASH_MD5_SHA1; - card->caps = SC_CARD_CAP_RNG; + card->caps = SC_CARD_CAP_RNG; + + if ( IS_V3x(card) ) { - if (card->type == SC_CARD_TYPE_STARCOS_V3_4) { - card->name = "STARCOS SPK 3.4"; flags |= SC_CARD_FLAG_RNG | SC_ALGORITHM_RSA_HASH_SHA224 | SC_ALGORITHM_RSA_HASH_SHA256 | SC_ALGORITHM_RSA_HASH_SHA384 - | SC_ALGORITHM_RSA_HASH_SHA512; + | SC_ALGORITHM_RSA_HASH_SHA512 + | SC_ALGORITHM_RSA_PAD_PSS; _sc_card_add_rsa_alg(card, 512, flags, 0x10001); _sc_card_add_rsa_alg(card, 768, flags, 0x10001); @@ -142,6 +395,15 @@ static int starcos_init(sc_card_t *card) _sc_card_add_rsa_alg(card,1728, flags, 0x10001); _sc_card_add_rsa_alg(card,1976, flags, 0x10001); _sc_card_add_rsa_alg(card,2048, flags, 0x10001); + if ( IS_V34(card) ) { + card->name = "STARCOS 3.4"; + card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; + } else { + card->name = "STARCOS 3.5"; + _sc_card_add_rsa_alg(card,3072, flags, 0x10001); + } + card->max_send_size = 255; + card->max_recv_size = 256; } else { _sc_card_add_rsa_alg(card, 512, flags, 0x10001); _sc_card_add_rsa_alg(card, 768, flags, 0x10001); @@ -153,17 +415,54 @@ static int starcos_init(sc_card_t *card) } if (sc_parse_ef_atr(card) == SC_SUCCESS) { - if (card->ef_atr->card_capabilities & ISO7816_CAP_EXTENDED_LENGTH) { - card->caps |= SC_CARD_CAP_APDU_EXT; + size_t max_recv_size = 0; + size_t max_send_size = 0; + + /* Add max. length values from IAS/ECC specific issuer data */ + if ( card->ef_atr->issuer_data_len >= 4 ) { + max_recv_size = bebytes2ushort(card->ef_atr->issuer_data); + max_send_size = bebytes2ushort(card->ef_atr->issuer_data + 2); } + /* which could be overridden with ISO7816 EF.ATR options, if present */ if (card->ef_atr->max_response_apdu > 0) { - card->max_recv_size = card->ef_atr->max_response_apdu; + max_recv_size = card->ef_atr->max_response_apdu; } if (card->ef_atr->max_command_apdu > 0) { - card->max_send_size = card->ef_atr->max_command_apdu; + max_send_size = card->ef_atr->max_command_apdu; + } + + if ( max_send_size > 256 && max_recv_size > 256 ) { + size_t max_recv_size_prev = card->max_recv_size; + size_t max_send_size_prev = card->max_send_size; + /* allow SC_CARD_CAP_APDU_EXT independent of ef_atr->caps, see IAS/ECC issuer data above */ + card->caps |= SC_CARD_CAP_APDU_EXT; + /* the received data should not exceed max_recv_size including the sw1/sw2 */ + card->max_recv_size = max_recv_size - 2; + /* the sent APDU should not exceed max_send_size including the 4 bytes of the APDU and 2 * 3 bytes Lc/Le */ + card->max_send_size = max_send_size - 10; + /* probe reader for extended APDU support */ + if ( starcos_probe_reader_for_ext_apdu(card) ) { + sc_log(card->ctx, "Successfully probed extended APDU, enabling extended APDU with max send/recv %d/%d", + (int)card->max_send_size, (int)card->max_recv_size); + } else { + card->caps &= ~(SC_CARD_CAP_APDU_EXT); + card->max_recv_size = max_recv_size_prev; + card->max_send_size = max_send_size_prev; + sc_log(card->ctx, "Ext APDU probing failed, the actual reader does not support ext APDU"); + } } } + if ( ex_data->pin_encoding == PIN_ENCODING_DETERMINE ) { + // about to determine PIN encoding + ex_data->pin_encoding = starcos_determine_pin_encoding(card); + } + + if ( card->type == SC_CARD_TYPE_STARCOS_V3_4 && starcos_has_esign_app(card) ) { + card->type = SC_CARD_TYPE_STARCOS_V3_4_ESIGN; + sc_log(card->ctx, "Card has eSign app, card type changed to %d", card->type); + } + return 0; } @@ -185,8 +484,8 @@ static int process_fci(sc_context_t *ctx, sc_file_t *file, size_t taglen, len = buflen; const u8 *tag = NULL, *p; - - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "processing FCI bytes\n"); + + sc_log(ctx, "processing FCI bytes\n"); if (buflen < 2) return SC_ERROR_INTERNAL; @@ -203,16 +502,16 @@ static int process_fci(sc_context_t *ctx, sc_file_t *file, file->shareable = 0; file->record_length = 0; file->size = 0; - + tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, " bytes in file: %d\n", bytes); file->size = bytes; } - tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); + tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); if (tag != NULL) { const char *type = "unknown"; const char *structure = "unknown"; @@ -256,9 +555,9 @@ static int process_fci(sc_context_t *ctx, sc_file_t *file, } } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, " type: %s\n", type); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, " EF structure: %s\n", structure); } file->magic = SC_FILE_MAGIC; @@ -272,7 +571,7 @@ static int process_fci_v3_4(sc_context_t *ctx, sc_file_t *file, size_t taglen, len = buflen; const u8 *tag = NULL, *p; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "processing %"SC_FORMAT_LEN_SIZE_T"u FCI bytes\n", buflen); if (buflen < 2) @@ -296,7 +595,7 @@ static int process_fci_v3_4(sc_context_t *ctx, sc_file_t *file, if (tag != NULL && taglen > 0 && taglen <= 16) { memcpy(file->name, tag, taglen); file->namelen = taglen; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "filename %s", + sc_log(ctx, "filename %s", sc_dump_hex(file->name, file->namelen)); } return SC_SUCCESS; @@ -308,7 +607,7 @@ static int process_fcp_v3_4(sc_context_t *ctx, sc_file_t *file, size_t taglen, len = buflen; const u8 *tag = NULL, *p; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "processing %"SC_FORMAT_LEN_SIZE_T"u FCP bytes\n", buflen); if (buflen < 2) @@ -323,7 +622,7 @@ static int process_fcp_v3_4(sc_context_t *ctx, sc_file_t *file, tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, " bytes in file: %d\n", bytes); file->size = bytes; } @@ -331,7 +630,7 @@ static int process_fcp_v3_4(sc_context_t *ctx, sc_file_t *file, tag = sc_asn1_find_tag(ctx, p, len, 0xc5, &taglen); if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, " bytes in file 2: %d\n", bytes); file->size = bytes; } @@ -385,9 +684,9 @@ static int process_fcp_v3_4(sc_context_t *ctx, sc_file_t *file, break; } } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, " type: %s\n", type); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, " EF structure: %s\n", structure); if (taglen >= 2) { if (tag[1] != 0x41 || taglen != 5) { @@ -396,8 +695,8 @@ static int process_fcp_v3_4(sc_context_t *ctx, sc_file_t *file, /* formatted EF */ file->record_length = (tag[2] << 8) + tag[3]; file->record_count = tag[4]; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - " rec_len: %d rec_cnt: %d\n\n", + sc_log(ctx, + " rec_len: %"SC_FORMAT_LEN_SIZE_T"u rec_cnt: %"SC_FORMAT_LEN_SIZE_T"u\n\n", file->record_length, file->record_count); } } @@ -405,7 +704,7 @@ static int process_fcp_v3_4(sc_context_t *ctx, sc_file_t *file, tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen); if (tag != NULL && taglen >= 2) { file->id = (tag[0] << 8) | tag[1]; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " file identifier: 0x%02X%02X\n", + sc_log(ctx, " file identifier: 0x%02X%02X\n", tag[0], tag[1]); } @@ -413,7 +712,7 @@ static int process_fcp_v3_4(sc_context_t *ctx, sc_file_t *file, if (tag != NULL && taglen > 0 && taglen <= 16) { memcpy(file->name, tag, taglen); file->namelen = taglen; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " filename %s", + sc_log(ctx, " filename %s", sc_dump_hex(file->name, file->namelen)); } @@ -437,7 +736,7 @@ static int process_fcp_v3_4(sc_context_t *ctx, sc_file_t *file, default: break; } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, " file status: %s\n", status); + sc_log(ctx, " file status: %s\n", status); } file->magic = SC_FILE_MAGIC; @@ -445,7 +744,7 @@ static int process_fcp_v3_4(sc_context_t *ctx, sc_file_t *file, } static int starcos_select_aid(sc_card_t *card, - u8 aid[16], size_t len, + const u8 aid[16], size_t len, sc_file_t **file_out) { sc_apdu_t apdu; @@ -459,31 +758,27 @@ static int starcos_select_aid(sc_card_t *card, apdu.resplen = 0; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); /* check return value */ if (!(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) && apdu.sw1 != 0x61 ) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); - - /* update cache */ - card->cache.current_path.type = SC_PATH_TYPE_DF_NAME; - card->cache.current_path.len = len; - memcpy(card->cache.current_path.value, aid, len); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); if (file_out) { sc_file_t *file = sc_file_new(); if (!file) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->type = SC_FILE_TYPE_DF; file->ef_structure = SC_FILE_EF_UNKNOWN; file->path.len = 0; file->size = 0; /* AID */ - for (i = 0; i < len; i++) + for (i = 0; i < len; i++) file->name[i] = aid[i]; file->namelen = len; file->id = 0x0000; file->magic = SC_FILE_MAGIC; + *file_out = file; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); @@ -510,10 +805,14 @@ static int starcos_select_fid(sc_card_t *card, apdu.data = (u8*)data; apdu.datalen = 2; - if (card->type == SC_CARD_TYPE_STARCOS_V3_4) { + if ( IS_V3x(card) ) { if (id_hi == 0x3f && id_lo == 0x0) { apdu.p1 = 0x0; - apdu.p2 = 0x0; + apdu.p2 = 0x0C; + apdu.le = 0; + apdu.resplen = 0; + apdu.resp = NULL; + apdu.cse = SC_APDU_CASE_3_SHORT; isMF = 1; } else if (file_out || is_file) { // last component (i.e. file or path) @@ -527,7 +826,7 @@ static int starcos_select_fid(sc_card_t *card, } r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.p2 == 0x00 && apdu.sw1 == 0x62 && apdu.sw2 == 0x84 ) { /* no FCI => we have a DF (see comment in process_fci()) */ @@ -537,8 +836,9 @@ static int starcos_select_fid(sc_card_t *card, apdu.resplen = 0; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU re-transmit failed"); - } else if (card->type == SC_CARD_TYPE_STARCOS_V3_4 && apdu.p2 == 0x4 && apdu.sw1 == 0x6a && apdu.sw2 == 0x82) { + LOG_TEST_RET(card->ctx, r, "APDU re-transmit failed"); + } else if ((IS_V3x(card)) + && apdu.p2 == 0x4 && apdu.sw1 == 0x6a && apdu.sw2 == 0x82) { /* not a file, could be a path */ bIsDF = 1; apdu.p1 = 0x1; @@ -547,7 +847,7 @@ static int starcos_select_fid(sc_card_t *card, apdu.le = 256; apdu.lc = 2; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU re-transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU re-transmit failed"); } else if (apdu.sw1 == 0x61 || (apdu.sw1 == 0x90 && apdu.sw2 == 0x00 && !isMF)) { /* SELECT returned some data (possible FCI) => * try a READ BINARY to see if a EF is selected */ @@ -559,7 +859,7 @@ static int starcos_select_fid(sc_card_t *card, apdu2.le = 1; apdu2.lc = 0; r = sc_transmit_apdu(card, &apdu2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu2.sw1 == 0x69 && apdu2.sw2 == 0x86) { /* no current EF is selected => we have a DF */ bIsDF = 1; @@ -571,28 +871,13 @@ static int starcos_select_fid(sc_card_t *card, if (apdu.sw1 != 0x61 && (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); - /* update cache */ - if (bIsDF || isMF) { - card->cache.current_path.type = SC_PATH_TYPE_PATH; - card->cache.current_path.value[0] = 0x3f; - card->cache.current_path.value[1] = 0x00; - if (id_hi == 0x3f && id_lo == 0x00) - card->cache.current_path.len = 2; - else { - card->cache.current_path.len = 4; - card->cache.current_path.value[2] = id_hi; - card->cache.current_path.value[3] = id_lo; - } - } - if (file_out) { sc_file_t *file = sc_file_new(); if (!file) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); - file->id = (id_hi << 8) + id_lo; - file->path = card->cache.current_path; + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + file->id = (id_hi << 8) + id_lo; - if (bIsDF) { + if (bIsDF || isMF) { /* we have a DF */ file->type = SC_FILE_TYPE_DF; file->ef_structure = SC_FILE_EF_UNKNOWN; @@ -602,7 +887,7 @@ static int starcos_select_fid(sc_card_t *card, *file_out = file; } else { /* ok, assume we have a EF */ - if (card->type == SC_CARD_TYPE_STARCOS_V3_4) { + if ( IS_V3x(card) ) { if (isFCP) { r = process_fcp_v3_4(card->ctx, file, apdu.resp, apdu.resplen); @@ -631,51 +916,54 @@ static int starcos_select_file(sc_card_t *card, sc_file_t **file_out) { u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; - int r; + int r, pathtype; size_t i, pathlen; - char pbuf[SC_MAX_PATH_STRING_SIZE]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - r = sc_path_print(pbuf, sizeof(pbuf), &card->cache.current_path); - if (r != SC_SUCCESS) - pbuf[0] = '\0'; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "current path (%s, %s): %s (len: %"SC_FORMAT_LEN_SIZE_T"u)\n", - card->cache.current_path.type == SC_PATH_TYPE_DF_NAME ? - "aid" : "path", - card->cache.valid ? "valid" : "invalid", pbuf, - card->cache.current_path.len); - + if ( in_path->len > sizeof(pathbuf) ) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_BUFFER_TOO_SMALL); + } memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; + pathtype = in_path->type; - if (in_path->type == SC_PATH_TYPE_FILE_ID) + if (in_path->aid.len) { + if (!pathlen) { + if ( in_path->aid.len > sizeof(pathbuf) ) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_BUFFER_TOO_SMALL); + } + memcpy(path, in_path->aid.value, in_path->aid.len); + pathlen = in_path->aid.len; + pathtype = SC_PATH_TYPE_DF_NAME; + } else { + r = starcos_select_aid(card, in_path->aid.value, in_path->aid.len, NULL); + LOG_TEST_RET(card->ctx, r, "Could not select AID!"); + + if (pathtype == SC_PATH_TYPE_DF_NAME) { + pathtype = SC_PATH_TYPE_FILE_ID; + } + } + } + + if (pathtype == SC_PATH_TYPE_FILE_ID) { /* SELECT EF/DF with ID */ /* Select with 2byte File-ID */ if (pathlen != 2) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INVALID_ARGUMENTS); - return starcos_select_fid(card, path[0], path[1], file_out, 1); + r = starcos_select_fid(card, path[0], + path[1], path[0] == 0x3F && path[1] == 0x00 ? NULL : file_out, 1); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } - else if (in_path->type == SC_PATH_TYPE_DF_NAME) - { /* SELECT DF with AID */ + else if (pathtype == SC_PATH_TYPE_DF_NAME) + { /* SELECT DF with AID */ /* Select with 1-16byte Application-ID */ - if (card->cache.valid - && card->cache.current_path.type == SC_PATH_TYPE_DF_NAME - && card->cache.current_path.len == pathlen - && memcmp(card->cache.current_path.value, pathbuf, pathlen) == 0 ) - { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cache hit\n"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); - } - else - return starcos_select_aid(card, pathbuf, pathlen, file_out); + r = starcos_select_aid(card, pathbuf, pathlen, file_out); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } - else if (in_path->type == SC_PATH_TYPE_PATH) + else if (pathtype == SC_PATH_TYPE_PATH) { u8 n_pathbuf[SC_MAX_PATH_SIZE]; - int bMatch = -1; /* Select with path (sequence of File-IDs) */ /* Starcos (S 2.1 and SPK 2.3) only supports one @@ -689,102 +977,41 @@ static int starcos_select_file(sc_card_t *card, if (pathlen == 6 && ( path[0] != 0x3f || path[1] != 0x00 )) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); - if (card->type != SC_CARD_TYPE_STARCOS_V3_4 || - (pathlen == 0 && card->cache.current_path.type != SC_PATH_TYPE_DF_NAME)) { + if ( IS_V3x(card) ) { /* unify path (the first FID should be MF) */ if (path[0] != 0x3f || path[1] != 0x00) { n_pathbuf[0] = 0x3f; n_pathbuf[1] = 0x00; - for (i=0; i< pathlen; i++) - n_pathbuf[i+2] = pathbuf[i]; + memcpy(n_pathbuf+2, path, pathlen); path = n_pathbuf; pathlen += 2; } } - - /* check current working directory */ - if (card->cache.valid - && card->cache.current_path.type == SC_PATH_TYPE_PATH - && card->cache.current_path.len >= 2 - && card->cache.current_path.len <= pathlen ) - { - bMatch = 0; - for (i=0; i < card->cache.current_path.len; i+=2) - if (card->cache.current_path.value[i] == path[i] - && card->cache.current_path.value[i+1] == path[i+1] ) - bMatch += 2; - - if (card->type == SC_CARD_TYPE_STARCOS_V3_4 && - bMatch > 0 && - (size_t) bMatch < card->cache.current_path.len) { - /* we're in the wrong folder, start traversing from root */ - bMatch = 0; - card->cache.current_path.len = 0; - } - } - if ( card->cache.valid && bMatch >= 0 ) + for ( i=0; i 2 ) - { - /* two more steps to go */ - sc_path_t new_path; - - /* first step: change directory */ - r = starcos_select_fid(card, path[bMatch], path[bMatch+1], NULL, 0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "SELECT FILE (DF-ID) failed"); - - memset(&new_path, 0, sizeof(sc_path_t)); - new_path.type = SC_PATH_TYPE_PATH; - new_path.len = pathlen - bMatch-2; - memcpy(new_path.value, &(path[bMatch+2]), new_path.len); - /* final step: select file */ - return starcos_select_file(card, &new_path, file_out); - } - else /* if (bMatch - pathlen == 0) */ - { - /* done: we are already in the - * requested directory */ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "cache hit\n"); - /* copy file info (if necessary) */ - if (file_out) { - sc_file_t *file = sc_file_new(); - if (!file) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); - file->id = (path[pathlen-2] << 8) + - path[pathlen-1]; - file->path = card->cache.current_path; - file->type = SC_FILE_TYPE_DF; - file->ef_structure = SC_FILE_EF_UNKNOWN; - file->size = 0; - file->namelen = 0; - file->magic = SC_FILE_MAGIC; - *file_out = file; - } - /* nothing left to do */ - return SC_SUCCESS; - } - } - else - { - /* no usable cache */ - for ( i=0; ictx, SC_LOG_DEBUG_NORMAL, r, "SELECT FILE (DF-ID) failed"); - } - return starcos_select_fid(card, path[pathlen-2], path[pathlen-1], file_out, 1); + r = starcos_select_fid(card, path[i], path[i+1], NULL, 0); + LOG_TEST_RET(card->ctx, r, "SELECT FILE (DF-ID) failed"); } + r = starcos_select_fid(card, path[pathlen-2], path[pathlen-1], file_out, 1); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } else SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } +static int starcos_get_challenge(struct sc_card *card, unsigned char *rnd, size_t len) +{ + LOG_FUNC_CALLED(card->ctx); + + if (len > 8) { + len = 8; + } + + LOG_FUNC_RETURN(card->ctx, iso_ops->get_challenge(card, rnd, len)); +} + #define STARCOS_AC_ALWAYS 0x9f #define STARCOS_AC_NEVER 0x5f #define STARCOS_PINID2STATE(a) ((((a) & 0x0f) == 0x01) ? ((a) & 0x0f) : (0x0f - ((0x0f & (a)) >> 1))) @@ -814,11 +1041,11 @@ static u8 process_acl_entry(sc_file_t *in, unsigned int method, unsigned int in_ * \param card pointer to the sc_card object * \param file pointer to the sc_file object * \param data pointer to a sc_starcos_create_data structure - * \return SC_SUCCESS if no error occured otherwise error code + * \return SC_SUCCESS if no error occurred otherwise error code * - * This function tries to create a somewhat useable Starcos spk 2.3 acl + * This function tries to create a somewhat usable Starcos spk 2.3 acl * from the OpenSC internal acl (storing the result in the supplied - * sc_starcos_create_data structure). + * sc_starcos_create_data structure). */ static int starcos_process_acl(sc_card_t *card, sc_file_t *file, sc_starcos_create_data *data) @@ -845,7 +1072,7 @@ static int starcos_process_acl(sc_card_t *card, sc_file_t *file, *p++ = data->data.mf.header[14]; /* if sm is required use combined mode */ if (file->acl[SC_AC_OP_CREATE] && (sc_file_get_acl_entry(file, SC_AC_OP_CREATE))->method & SC_AC_PRO) - tmp = 0x03; /* combinde mode */ + tmp = 0x03; /* combined mode */ else tmp = 0x00; /* no sm */ *p++ = tmp; /* use the same sm mode for all ops */ @@ -865,7 +1092,7 @@ static int starcos_process_acl(sc_card_t *card, sc_file_t *file, memcpy(p, file->name, (u8)file->namelen); p += 16; } else { - /* (mis)use the fid as aid */ + /* use the fid as aid */ *p++ = 2; memset(p, 0, 16); *p++ = (file->id >> 8) & 0xff; @@ -952,7 +1179,7 @@ static int starcos_process_acl(sc_card_t *card, sc_file_t *file, * \param card pointer to the sc_card structure * \param data pointer to a sc_starcos_create_data object * \return SC_SUCCESS or error code - * + * * This function creates the MF based on the information stored * in the sc_starcos_create_data.mf structure. Note: CREATE END must be * called separately to activate the ACs. @@ -965,7 +1192,7 @@ static int starcos_create_mf(sc_card_t *card, sc_starcos_create_data *data) CHECK_NOT_SUPPORTED_V3_4(card); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "creating MF \n"); + sc_log(ctx, "creating MF \n"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); apdu.cla |= 0x80; apdu.lc = 19; @@ -973,8 +1200,8 @@ static int starcos_create_mf(sc_card_t *card, sc_starcos_create_data *data) apdu.data = (u8 *) data->data.mf.header; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - return sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); + return sc_check_sw(card, apdu.sw1, apdu.sw2); } /** starcos_create_df @@ -996,9 +1223,9 @@ static int starcos_create_df(sc_card_t *card, sc_starcos_create_data *data) CHECK_NOT_SUPPORTED_V3_4(card); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "creating DF\n"); + sc_log(ctx, "creating DF\n"); /* first step: REGISTER DF */ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "calling REGISTER DF\n"); + sc_log(ctx, "calling REGISTER DF\n"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x52, data->data.df.size[0], data->data.df.size[1]); @@ -1009,9 +1236,9 @@ static int starcos_create_df(sc_card_t *card, sc_starcos_create_data *data) apdu.data = data->data.df.header; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); /* second step: CREATE DF */ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "calling CREATE DF\n"); + sc_log(ctx, "calling CREATE DF\n"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x01, 0x00); apdu.cla |= 0x80; @@ -1020,7 +1247,7 @@ static int starcos_create_df(sc_card_t *card, sc_starcos_create_data *data) apdu.data = data->data.df.header; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } @@ -1034,14 +1261,14 @@ static int starcos_create_df(sc_card_t *card, sc_starcos_create_data *data) * the sc_starcos_create_data.ef data structure. */ static int starcos_create_ef(sc_card_t *card, sc_starcos_create_data *data) -{ +{ int r; sc_apdu_t apdu; sc_context_t *ctx = card->ctx; CHECK_NOT_SUPPORTED_V3_4(card); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "creating EF\n"); + sc_log(ctx, "creating EF\n"); sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xE0,0x03,0x00); apdu.cla |= 0x80; @@ -1050,7 +1277,7 @@ static int starcos_create_ef(sc_card_t *card, sc_starcos_create_data *data) apdu.data = (u8 *) data->data.ef.header; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } @@ -1060,7 +1287,7 @@ static int starcos_create_ef(sc_card_t *card, sc_starcos_create_data *data) * \param file pointer to a sc_file object * \return SC_SUCCESS or error code * - * This function finishs the creation of a DF (or MF) and activates + * This function finishes the creation of a DF (or MF) and activates * the ACs. */ static int starcos_create_end(sc_card_t *card, sc_file_t *file) @@ -1082,7 +1309,7 @@ static int starcos_create_end(sc_card_t *card, sc_file_t *file) apdu.datalen = 2; apdu.data = fid; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } @@ -1095,7 +1322,7 @@ static int starcos_create_end(sc_card_t *card, sc_file_t *file) * information in the sc_file structure (using starcos_process_acl). */ static int starcos_create_file(sc_card_t *card, sc_file_t *file) -{ +{ int r; sc_starcos_create_data data; @@ -1138,7 +1365,7 @@ static int starcos_erase_card(sc_card_t *card) { /* restore the delivery state */ int r; u8 sbuf[2]; - sc_apdu_t apdu; + sc_apdu_t apdu = {0}; sbuf[0] = 0x3f; sbuf[1] = 0x00; @@ -1147,11 +1374,9 @@ static int starcos_erase_card(sc_card_t *card) apdu.lc = 2; apdu.datalen = 2; apdu.data = sbuf; - + r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - /* invalidate cache */ - card->cache.valid = 0; + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x69 && apdu.sw2 == 0x85) /* no MF to delete, ignore error */ return SC_SUCCESS; @@ -1193,7 +1418,7 @@ static int starcos_write_key(sc_card_t *card, sc_starcos_wkey_data *data) apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return sc_check_sw(card, apdu.sw1, apdu.sw2); if (data->key == NULL) @@ -1207,28 +1432,27 @@ static int starcos_write_key(sc_card_t *card, sc_starcos_wkey_data *data) tlen = data->key_len; while (tlen != 0) { /* transmit the key in chunks of STARCOS_WKEY_CSIZE bytes */ - u8 clen = tlen < STARCOS_WKEY_CSIZE ? tlen : STARCOS_WKEY_CSIZE; + u8 c_len = tlen < STARCOS_WKEY_CSIZE ? tlen : STARCOS_WKEY_CSIZE; sbuf[0] = 0xc2; - sbuf[1] = 3 + clen; + sbuf[1] = 3 + c_len; sbuf[2] = data->kid; sbuf[3] = (offset >> 8) & 0xff; sbuf[4] = offset & 0xff; - memcpy(sbuf+5, p, clen); - len = 5 + clen; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xf4, - data->mode, 0x00); + memcpy(sbuf+5, p, c_len); + len = 5 + c_len; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xf4, data->mode, 0x00); apdu.cla |= 0x80; apdu.lc = len; apdu.datalen = len; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return sc_check_sw(card, apdu.sw1, apdu.sw2); - offset += clen; - p += clen; - tlen -= clen; + offset += c_len; + p += c_len; + tlen -= c_len; } return SC_SUCCESS; } @@ -1253,7 +1477,7 @@ static int starcos_gen_key(sc_card_t *card, sc_starcos_gen_key_data *data) CHECK_NOT_SUPPORTED_V3_4(card); /* generate key */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, data->key_id); apdu.le = 0; sbuf[0] = (u8)(data->key_length >> 8); @@ -1262,7 +1486,7 @@ static int starcos_gen_key(sc_card_t *card, sc_starcos_gen_key_data *data) apdu.lc = 2; apdu.datalen = 2; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return sc_check_sw(card, apdu.sw1, apdu.sw2); /* read public key via READ PUBLIC KEY */ @@ -1276,7 +1500,7 @@ static int starcos_gen_key(sc_card_t *card, sc_starcos_gen_key_data *data) apdu.resplen = sizeof(rbuf); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return sc_check_sw(card, apdu.sw1, apdu.sw2); @@ -1318,42 +1542,77 @@ static int starcos_set_security_env(sc_card_t *card, p = sbuf; - if (card->type == SC_CARD_TYPE_STARCOS_V3_4) { - if (operation != SC_SEC_OPERATION_SIGN) { - /* we only support signatures for now */ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "not supported for STARCOS 3.4 cards"); - return SC_ERROR_NOT_SUPPORTED; \ - } - - if (!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) || + if ( IS_V3x(card) ) { + u8 algorithm_supported = (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) || + (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PSS); + if (!algorithm_supported || !(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB6); - /* don't know what these mean but doesn't matter as card seems to take - * algorithm / cipher from PKCS#1 padding prefix */ + /* Tag '84' (length 1) denotes key name or key reference */ *p++ = 0x84; *p++ = 0x01; - *p++ = 0x84; + if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) { + *p++ = *env->key_ref | 0x80; + } else { + *p++ = *env->key_ref; + } + + switch (operation) { + case SC_SEC_OPERATION_SIGN: + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB6); + + /* algorithm / cipher selector? */ + /* algorithm: 13.23 PKCS#1 signature with RSA (standard) */ + /* algorithm: 13.33.30 PKCS#1-PSS signature with SHA-256 */ + *p++ = 0x89; + if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PSS) { + *p++ = 0x03; + *p++ = 0x13; + *p++ = 0x33; + *p++ = 0x30; + } else { + // fall back, RSA PKCS1 Padding + *p++ = 0x02; + *p++ = 0x13; + *p++ = 0x23; + } + break; + + case SC_SEC_OPERATION_DECIPHER: + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB8); + + /* algorithm / cipher selector? */ + /* algorithm: 11.3 Encipherment RSA (standard) */ + /* algorithm: 11.31 Encipherment RSA (standard) with PKCS#1 padding */ + /* algorithm: 11.32 Encipherment RSA OAEP padding */ + *p++ = 0x89; + *p++ = 0x02; + *p++ = 0x11; + if ( IS_V34(card) ) + *p++ = 0x30; + else + *p++ = 0x31; + break; - /* algorithm / cipher selector? */ - *p++ = 0x89; - *p++ = 0x02; - *p++ = 0x13; - *p++ = 0x23; + default: + sc_log(card->ctx, + "not supported for STARCOS 3.4 cards"); + return SC_ERROR_NOT_SUPPORTED; + } apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); - if (env->algorithm_flags == SC_ALGORITHM_RSA_PAD_PKCS1) { + if ((operation == SC_SEC_OPERATION_SIGN && env->algorithm_flags == SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01) + || (operation == SC_SEC_OPERATION_DECIPHER && env->algorithm_flags == SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02)) { // input data will be already padded ex_data->fix_digestInfo = 0; } else { @@ -1365,7 +1624,7 @@ static int starcos_set_security_env(sc_card_t *card, /* copy key reference, if present */ if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { - if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) + if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) *p++ = 0x83; else *p++ = 0x84; @@ -1375,7 +1634,7 @@ static int starcos_set_security_env(sc_card_t *card, } pp = p; if (operation == SC_SEC_OPERATION_DECIPHER){ - if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { + if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02) { *p++ = 0x80; *p++ = 0x01; *p++ = 0x02; @@ -1388,14 +1647,14 @@ static int starcos_set_security_env(sc_card_t *card, apdu.lc = p - sbuf; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); return SC_SUCCESS; } /* try COMPUTE SIGNATURE */ if (operation == SC_SEC_OPERATION_SIGN && ( - env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1 || + env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 || env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796)) { if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { *p++ = 0x80; @@ -1406,7 +1665,7 @@ static int starcos_set_security_env(sc_card_t *card, /* set the method to use based on the algorithm_flags */ *p++ = 0x80; *p++ = 0x01; - if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { + if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01) { if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) *p++ = 0x12; else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_RIPEMD160) @@ -1435,10 +1694,10 @@ static int starcos_set_security_env(sc_card_t *card, apdu.datalen = p - sbuf; apdu.lc = p - sbuf; apdu.le = 0; - /* we don't know whether to use + /* we don't know whether to use * COMPUTE SIGNATURE or INTERNAL AUTHENTICATE */ r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { ex_data->fix_digestInfo = 0; ex_data->sec_ops = SC_SEC_OPERATION_SIGN; @@ -1451,7 +1710,7 @@ static int starcos_set_security_env(sc_card_t *card, } try_authenticate: /* try INTERNAL AUTHENTICATE */ - if (operation == SC_SEC_OPERATION_AUTHENTICATE && + if (operation == SC_SEC_OPERATION_AUTHENTICATE && env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { *p++ = 0x80; *p++ = 0x01; @@ -1463,7 +1722,7 @@ static int starcos_set_security_env(sc_card_t *card, apdu.lc = p - sbuf; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); ex_data->fix_digestInfo = env->algorithm_flags; @@ -1489,15 +1748,15 @@ static int starcos_compute_signature(sc_card_t *card, if (ex_data->sec_ops == SC_SEC_OPERATION_SIGN) { /* compute signature with the COMPUTE SIGNATURE command */ - - if (card->type == SC_CARD_TYPE_STARCOS_V3_4) { + + if ( IS_V3x(card) ) { size_t tmp_len; - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, + sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x9E, 0x9A); - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - apdu.le = 0; + apdu.resp = out; + apdu.resplen = outlen; + apdu.le = outlen; if (ex_data->fix_digestInfo) { // need to pad data unsigned int flags = ex_data->fix_digestInfo & SC_ALGORITHM_RSA_HASHES; @@ -1505,8 +1764,12 @@ static int starcos_compute_signature(sc_card_t *card, flags = SC_ALGORITHM_RSA_HASH_NONE; } tmp_len = sizeof(sbuf); - r = sc_pkcs1_encode(card->ctx, flags, data, datalen, sbuf, &tmp_len, sizeof(sbuf)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_pkcs1_encode failed"); + if (ex_data->fix_digestInfo & SC_ALGORITHM_RSA_PAD_PSS) { + r = sc_pkcs1_strip_digest_info_prefix(NULL, data, datalen, sbuf, &tmp_len); + } else { + r = sc_pkcs1_encode(card->ctx, flags, data, datalen, sbuf, &tmp_len, sizeof(sbuf)*8, NULL); + } + LOG_TEST_RET(card->ctx, r, "sc_pkcs1_encode failed"); } else { memcpy(sbuf, data, datalen); tmp_len = datalen; @@ -1515,11 +1778,9 @@ static int starcos_compute_signature(sc_card_t *card, apdu.data = sbuf; apdu.datalen = tmp_len; apdu.lc = tmp_len; - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - apdu.le = 0; + r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); } else { /* set the hash value */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, @@ -1532,7 +1793,7 @@ static int starcos_compute_signature(sc_card_t *card, apdu.lc = datalen; apdu.datalen = datalen; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); @@ -1547,12 +1808,14 @@ static int starcos_compute_signature(sc_card_t *card, apdu.lc = 0; apdu.datalen = 0; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); } if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; - memcpy(out, apdu.resp, len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); + if ( out != apdu.resp ) { + memcpy(out, apdu.resp, len); + } + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); } } else if (ex_data->sec_ops == SC_SEC_OPERATION_AUTHENTICATE) { size_t tmp_len; @@ -1567,7 +1830,7 @@ static int starcos_compute_signature(sc_card_t *card, flags = SC_ALGORITHM_RSA_HASH_NONE; tmp_len = sizeof(sbuf); r = sc_pkcs1_encode(card->ctx, flags, data, datalen, - sbuf, &tmp_len, sizeof(sbuf)); + sbuf, &tmp_len, sizeof(sbuf)*8, NULL); if (r < 0) return r; } else { @@ -1581,12 +1844,12 @@ static int starcos_compute_signature(sc_card_t *card, apdu.resplen = sizeof(rbuf); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); } } else SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); @@ -1598,31 +1861,105 @@ static int starcos_compute_signature(sc_card_t *card, SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } +static int starcos_decipher(struct sc_card *card, + const u8 * crgram, size_t crgram_len, + u8 * out, size_t outlen) +{ + int r; + size_t card_max_send_size = card->max_send_size; + size_t reader_max_send_size = card->reader->max_send_size; + size_t card_max_recv_size = card->max_recv_size; + size_t reader_max_recv_size = card->reader->max_recv_size; + + if (sc_get_max_send_size(card) < crgram_len + 1) { + /* Starcos doesn't support chaining for PSO:DEC, so we just _hope_ + * that both, the reader and the card are able to send enough data. + * (data is prefixed with 1 byte padding content indicator) */ + card->max_send_size = crgram_len + 1; + card->reader->max_send_size = crgram_len + 1; + } + + if (sc_get_max_recv_size(card) < outlen) { + /* Starcos doesn't support get response for PSO:DEC, so we just _hope_ + * that both, the reader and the card are able to receive enough data. + */ + if (0 == (card->caps & SC_CARD_CAP_APDU_EXT) + && outlen > 256) { + card->max_recv_size = 256; + card->reader->max_recv_size = 256; + } else { + card->max_recv_size = outlen; + card->reader->max_recv_size = outlen; + } + } + + if ( IS_V3x(card) ) { + sc_apdu_t apdu; + + u8 *sbuf = malloc(crgram_len + 1); + if (sbuf == NULL) + return SC_ERROR_OUT_OF_MEMORY; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x80, 0x86); + apdu.resp = out; + apdu.resplen = outlen; + apdu.le = outlen; + + sbuf[0] = 0x81; + memcpy(sbuf + 1, crgram, crgram_len); + apdu.data = sbuf; + apdu.lc = crgram_len + 1; + apdu.datalen = crgram_len + 1; + + r = sc_transmit_apdu(card, &apdu); + sc_mem_clear(sbuf, crgram_len + 1); + + free(sbuf); + + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + r = (int)apdu.resplen; + else + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + } else { + r = iso_ops->decipher(card, crgram, crgram_len, out, outlen); + } + + /* reset whatever we've modified above */ + card->max_send_size = card_max_send_size; + card->reader->max_send_size = reader_max_send_size; + card->max_recv_size = card_max_recv_size; + card->reader->max_recv_size = reader_max_recv_size; + + LOG_FUNC_RETURN(card->ctx, r); +} + static int starcos_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) { const int err_count = sizeof(starcos_errors)/sizeof(starcos_errors[0]); int i; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "sw1 = 0x%02x, sw2 = 0x%02x\n", sw1, sw2); - - if (sw1 == 0x90) + + if (sw1 == 0x90 && sw2 == 0x00) return SC_SUCCESS; if (sw1 == 0x63 && (sw2 & ~0x0fU) == 0xc0 ) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Verification failed (remaining tries: %d)\n", + sc_log(card->ctx, "Verification failed (remaining tries: %d)\n", (sw2 & 0x0f)); return SC_ERROR_PIN_CODE_INCORRECT; } - + /* check starcos error messages */ for (i = 0; i < err_count; i++) if (starcos_errors[i].SWs == ((sw1 << 8) | sw2)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n", starcos_errors[i].errorstr); + sc_log(card->ctx, "%s\n", starcos_errors[i].errorstr); return starcos_errors[i].errorno; } - + /* iso error */ return iso_ops->check_sw(card, sw1, sw2); } @@ -1635,29 +1972,41 @@ static int starcos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) if (!serial) return SC_ERROR_INVALID_ARGUMENTS; + /* see if we have cached serial number */ if (card->serialnr.len) { memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } - CHECK_NOT_SUPPORTED_V3_4(card); - /* get serial number via GET CARD DATA */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xf6, 0x00, 0x00); - apdu.cla |= 0x80; - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - apdu.le = 256; - apdu.lc = 0; - apdu.datalen = 0; - r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) - return SC_ERROR_INTERNAL; - /* cache serial number */ - memcpy(card->serialnr.value, apdu.resp, MIN(apdu.resplen, SC_MAX_SERIALNR)); - card->serialnr.len = MIN(apdu.resplen, SC_MAX_SERIALNR); + + if ( IS_V3x(card) ) { + card->serialnr.len = SC_MAX_SERIALNR; + r = sc_parse_ef_gdo(card, card->serialnr.value, &card->serialnr.len, NULL, 0); + if (r < 0) { + card->serialnr.len = 0; + return r; + } + } else { + /* get serial number via GET CARD DATA */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xf6, 0x00, 0x00); + apdu.cla |= 0x80; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 256; + apdu.lc = 0; + apdu.datalen = 0; + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) + return SC_ERROR_INTERNAL; + /* cache serial number */ + memcpy(card->serialnr.value, apdu.resp, MIN(apdu.resplen, SC_MAX_SERIALNR)); + card->serialnr.len = MIN(apdu.resplen, SC_MAX_SERIALNR); + } + /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*serial)); + return SC_SUCCESS; } @@ -1692,21 +2041,30 @@ static int starcos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) } } +static int starcos_logout_v3_x(sc_card_t *card) +{ + return SC_ERROR_NOT_SUPPORTED; +} + static int starcos_logout(sc_card_t *card) { int r; sc_apdu_t apdu; const u8 mf_buf[2] = {0x3f, 0x00}; + if ( IS_V3x(card) ) { + return starcos_logout_v3_x(card); + } + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x00, 0x0C); apdu.le = 0; apdu.lc = 2; apdu.data = mf_buf; apdu.datalen = 2; apdu.resplen = 0; - + r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU re-transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU re-transmit failed"); if (apdu.sw1 == 0x69 && apdu.sw2 == 0x85) /* the only possible reason for this error here is, afaik, @@ -1717,18 +2075,18 @@ static int starcos_logout(sc_card_t *card) return sc_check_sw(card, apdu.sw1, apdu.sw2); } -static int starcos_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, - int *tries_left) +static int starcos_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) { - int ret; - - CHECK_ONLY_SUPPORTED_V3_4(card); + int r; - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); - data->flags |= SC_PIN_CMD_NEED_PADDING; - data->pin1.encoding = SC_PIN_ENCODING_GLP; - ret = iso_ops->pin_cmd(card, data, tries_left); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, ret); + LOG_FUNC_CALLED(card->ctx); + starcos_ex_data * ex_data = (starcos_ex_data*)card->drv_data; + if ( IS_V3x(card) ) { + data->flags |= SC_PIN_CMD_NEED_PADDING; + data->pin1.encoding = ex_data->pin_encoding; + } + r = iso_ops->pin_cmd(card, data); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static struct sc_card_driver * sc_get_driver(void) @@ -1736,21 +2094,23 @@ static struct sc_card_driver * sc_get_driver(void) struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); if (iso_ops == NULL) iso_ops = iso_drv->ops; - + starcos_ops = *iso_drv->ops; starcos_ops.match_card = starcos_match_card; starcos_ops.init = starcos_init; starcos_ops.finish = starcos_finish; starcos_ops.select_file = starcos_select_file; + starcos_ops.get_challenge = starcos_get_challenge; starcos_ops.check_sw = starcos_check_sw; starcos_ops.create_file = starcos_create_file; starcos_ops.delete_file = NULL; starcos_ops.set_security_env = starcos_set_security_env; starcos_ops.compute_signature = starcos_compute_signature; + starcos_ops.decipher = starcos_decipher; starcos_ops.card_ctl = starcos_card_ctl; starcos_ops.logout = starcos_logout; starcos_ops.pin_cmd = starcos_pin_cmd; - + return &starcos_drv; } diff --git a/src/libopensc/card-tcos.c b/src/libopensc/card-tcos.c index c804878438..6eedd99c53 100644 --- a/src/libopensc/card-tcos.c +++ b/src/libopensc/card-tcos.c @@ -17,10 +17,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -33,17 +33,18 @@ #include "asn1.h" #include "cardctl.h" -static struct sc_atr_table tcos_atrs[] = { +static const struct sc_atr_table tcos_atrs[] = { /* Infineon SLE44 */ { "3B:BA:13:00:81:31:86:5D:00:64:05:0A:02:01:31:80:90:00:8B", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL }, /* Infineon SLE66S */ { "3B:BA:14:00:81:31:86:5D:00:64:05:14:02:02:31:80:90:00:91", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL }, /* Infineon SLE66CX320P */ { "3B:BA:96:00:81:31:86:5D:00:64:05:60:02:03:31:80:90:00:66", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL }, - /* Infoneon SLE66CX322P */ + /* Infineon SLE66CX322P */ { "3B:BA:96:00:81:31:86:5D:00:64:05:7B:02:03:31:80:90:00:7D", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL }, /* Philips P5CT072 */ { "3B:BF:96:00:81:31:FE:5D:00:64:04:11:03:01:31:C0:73:F7:01:D0:00:90:00:7D", NULL, NULL, SC_CARD_TYPE_TCOS_V3, 0, NULL }, + { "3B:BF:96:00:81:31:FE:5D:00:64:04:11:04:0F:31:C0:73:F7:01:D0:00:90:00:74", NULL, NULL, SC_CARD_TYPE_TCOS_V3, 0, NULL }, /* Philips P5CT080 */ { "3B:BF:B6:00:81:31:FE:5D:00:64:04:28:03:02:31:C0:73:F7:01:D0:00:90:00:67", NULL, NULL, SC_CARD_TYPE_TCOS_V3, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } @@ -60,7 +61,7 @@ static struct sc_card_driver tcos_drv = { static const struct sc_card_operations *iso_ops = NULL; typedef struct tcos_data_st { - unsigned int pad_flags; + unsigned long pad_flags; unsigned int next_sign; } tcos_data; @@ -85,7 +86,7 @@ static int tcos_match_card(sc_card_t *card) static int tcos_init(sc_card_t *card) { - unsigned long flags; + unsigned long flags; tcos_data *data = malloc(sizeof(tcos_data)); if (!data) return SC_ERROR_OUT_OF_MEMORY; @@ -94,20 +95,20 @@ static int tcos_init(sc_card_t *card) card->drv_data = (void *)data; card->cla = 0x00; - flags = SC_ALGORITHM_RSA_RAW; - flags |= SC_ALGORITHM_RSA_PAD_PKCS1; - flags |= SC_ALGORITHM_RSA_HASH_NONE; + flags = SC_ALGORITHM_RSA_RAW; + flags |= SC_ALGORITHM_RSA_PAD_PKCS1; + flags |= SC_ALGORITHM_RSA_HASH_NONE; - _sc_card_add_rsa_alg(card, 512, flags, 0); - _sc_card_add_rsa_alg(card, 768, flags, 0); - _sc_card_add_rsa_alg(card, 1024, flags, 0); + _sc_card_add_rsa_alg(card, 512, flags, 0); + _sc_card_add_rsa_alg(card, 768, flags, 0); + _sc_card_add_rsa_alg(card, 1024, flags, 0); if (card->type == SC_CARD_TYPE_TCOS_V3) { card->caps |= SC_CARD_CAP_APDU_EXT; _sc_card_add_rsa_alg(card, 1280, flags, 0); _sc_card_add_rsa_alg(card, 1536, flags, 0); _sc_card_add_rsa_alg(card, 1792, flags, 0); - _sc_card_add_rsa_alg(card, 2048, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); } return 0; @@ -123,20 +124,20 @@ static int tcos_construct_fci(const sc_file_t *file, { u8 *p = out; u8 buf[64]; - size_t n; + size_t n; - /* FIXME: possible buffer overflow */ + /* FIXME: possible buffer overflow */ - *p++ = 0x6F; /* FCI */ - p++; + *p++ = 0x6F; /* FCI */ + p++; /* File size */ buf[0] = (file->size >> 8) & 0xFF; buf[1] = file->size & 0xFF; sc_asn1_put_tag(0x81, buf, 2, p, 16, &p); - /* File descriptor */ - n = 0; + /* File descriptor */ + n = 0; buf[n] = file->shareable ? 0x40 : 0; switch (file->type) { case SC_FILE_TYPE_WORKING_EF: @@ -148,63 +149,60 @@ static int tcos_construct_fci(const sc_file_t *file, return SC_ERROR_NOT_SUPPORTED; } buf[n++] |= file->ef_structure & 7; - if ( (file->ef_structure & 7) > 1) { - /* record structured file */ - buf[n++] = 0x41; /* indicate 3rd byte */ - buf[n++] = file->record_length; - } + if ( (file->ef_structure & 7) > 1) { + /* record structured file */ + buf[n++] = 0x41; /* indicate 3rd byte */ + buf[n++] = file->record_length; + } sc_asn1_put_tag(0x82, buf, n, p, 8, &p); - /* File identifier */ + /* File identifier */ buf[0] = (file->id >> 8) & 0xFF; buf[1] = file->id & 0xFF; sc_asn1_put_tag(0x83, buf, 2, p, 16, &p); - /* Directory name */ - if (file->type == SC_FILE_TYPE_DF) { - if (file->namelen) { - sc_asn1_put_tag(0x84, file->name, file->namelen, + /* Directory name */ + if (file->type == SC_FILE_TYPE_DF) { + if (file->namelen) { + sc_asn1_put_tag(0x84, file->name, file->namelen, p, 16, &p); - } - else { - /* TCOS needs one, so we use a faked one */ - snprintf ((char *) buf, sizeof(buf)-1, "foo-%lu", + } else { + /* TCOS needs one, so we use a faked one */ + snprintf ((char *) buf, sizeof(buf)-1, "foo-%lu", (unsigned long) time (NULL)); - sc_asn1_put_tag(0x84, buf, strlen ((char *) buf), p, 16, &p); - } - } + sc_asn1_put_tag(0x84, buf, strlen ((char *) buf), p, 16, &p); + } + } - /* File descriptor extension */ - if (file->prop_attr_len && file->prop_attr) { + /* File descriptor extension */ + if (file->prop_attr_len && file->prop_attr) { n = file->prop_attr_len; memcpy(buf, file->prop_attr, n); - } - else { - n = 0; - buf[n++] = 0x01; /* not invalidated, permanent */ - if (file->type == SC_FILE_TYPE_WORKING_EF) - buf[n++] = 0x00; /* generic data file */ - } - sc_asn1_put_tag(0x85, buf, n, p, 16, &p); - - /* Security attributes */ + } else { + n = 0; + buf[n++] = 0x01; /* not invalidated, permanent */ + if (file->type == SC_FILE_TYPE_WORKING_EF) + buf[n++] = 0x00; /* generic data file */ + } + sc_asn1_put_tag(0x85, buf, n, p, 16, &p); + + /* Security attributes */ if (file->sec_attr_len && file->sec_attr) { memcpy(buf, file->sec_attr, file->sec_attr_len); n = file->sec_attr_len; + } else { + /* no attributes given - fall back to default one */ + memcpy (buf+ 0, "\xa4\x00\x00\x00\xff\xff", 6); /* select */ + memcpy (buf+ 6, "\xb0\x00\x00\x00\xff\xff", 6); /* read bin */ + memcpy (buf+12, "\xd6\x00\x00\x00\xff\xff", 6); /* upd bin */ + memcpy (buf+18, "\x60\x00\x00\x00\xff\xff", 6); /* admin grp*/ + n = 24; } - else { - /* no attributes given - fall back to default one */ - memcpy (buf+ 0, "\xa4\x00\x00\x00\xff\xff", 6); /* select */ - memcpy (buf+ 6, "\xb0\x00\x00\x00\xff\xff", 6); /* read bin */ - memcpy (buf+12, "\xd6\x00\x00\x00\xff\xff", 6); /* upd bin */ - memcpy (buf+18, "\x60\x00\x00\x00\xff\xff", 6); /* admin grp*/ - n = 24; - } - sc_asn1_put_tag(0x86, buf, n, p, sizeof (buf), &p); - - - /* fixup length of FCI */ - out[1] = p - out - 2; + sc_asn1_put_tag(0x86, buf, n, p, sizeof (buf), &p); + + + /* fixup length of FCI */ + out[1] = p - out - 2; *outlen = p - out; return 0; @@ -220,21 +218,21 @@ static int tcos_create_file(sc_card_t *card, sc_file_t *file) len = SC_MAX_APDU_BUFFER_SIZE; r = tcos_construct_fci(file, sbuf, &len); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "tcos_construct_fci() failed"); - + LOG_TEST_RET(card->ctx, r, "tcos_construct_fci() failed"); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); - apdu.cla |= 0x80; /* this is an proprietary extension */ + apdu.cla |= 0x80; /* this is an proprietary extension */ apdu.lc = len; apdu.datalen = len; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } -static unsigned int map_operations (int commandbyte ) +static unsigned int map_operations (int commandbyte) { unsigned int op = (unsigned int)-1; @@ -271,87 +269,86 @@ static unsigned int map_operations (int commandbyte ) static void parse_sec_attr(sc_card_t *card, sc_file_t *file, const u8 *buf, size_t len) { - unsigned int op; - - /* list directory is not covered by ACLs - so always add an entry */ - sc_file_add_acl_entry (file, SC_AC_OP_LIST_FILES, + unsigned int op; + + /* list directory is not covered by ACLs - so always add an entry */ + sc_file_add_acl_entry (file, SC_AC_OP_LIST_FILES, SC_AC_NONE, SC_AC_KEY_REF_NONE); - /* FIXME: check for what LOCK is used */ - sc_file_add_acl_entry (file, SC_AC_OP_LOCK, + /* FIXME: check for what LOCK is used */ + sc_file_add_acl_entry (file, SC_AC_OP_LOCK, SC_AC_NONE, SC_AC_KEY_REF_NONE); - for (; len >= 6; len -= 6, buf += 6) { - /* FIXME: temporary hacks */ - if (!memcmp(buf, "\xa4\x00\x00\x00\xff\xff", 6)) /* select */ - sc_file_add_acl_entry (file, SC_AC_OP_SELECT, + for (; len >= 6; len -= 6, buf += 6) { + /* FIXME: temporary hacks */ + if (!memcmp(buf, "\xa4\x00\x00\x00\xff\xff", 6)) {/* select */ + sc_file_add_acl_entry (file, SC_AC_OP_SELECT, SC_AC_NONE, SC_AC_KEY_REF_NONE); - else if (!memcmp(buf, "\xb0\x00\x00\x00\xff\xff", 6)) /*read*/ - sc_file_add_acl_entry (file, SC_AC_OP_READ, + } else if (!memcmp(buf, "\xb0\x00\x00\x00\xff\xff", 6)) {/*read*/ + sc_file_add_acl_entry (file, SC_AC_OP_READ, SC_AC_NONE, SC_AC_KEY_REF_NONE); - else if (!memcmp(buf, "\xd6\x00\x00\x00\xff\xff", 6)) /*upd*/ - sc_file_add_acl_entry (file, SC_AC_OP_UPDATE, + } else if (!memcmp(buf, "\xd6\x00\x00\x00\xff\xff", 6)) {/*upd*/ + sc_file_add_acl_entry (file, SC_AC_OP_UPDATE, SC_AC_NONE, SC_AC_KEY_REF_NONE); - else if (!memcmp(buf, "\x60\x00\x00\x00\xff\xff", 6)) {/*adm */ - sc_file_add_acl_entry (file, SC_AC_OP_WRITE, + } else if (!memcmp(buf, "\x60\x00\x00\x00\xff\xff", 6)) {/*adm */ + sc_file_add_acl_entry (file, SC_AC_OP_WRITE, SC_AC_NONE, SC_AC_KEY_REF_NONE); - sc_file_add_acl_entry (file, SC_AC_OP_CREATE, + sc_file_add_acl_entry (file, SC_AC_OP_CREATE, SC_AC_NONE, SC_AC_KEY_REF_NONE); - sc_file_add_acl_entry (file, SC_AC_OP_INVALIDATE, + sc_file_add_acl_entry (file, SC_AC_OP_INVALIDATE, SC_AC_NONE, SC_AC_KEY_REF_NONE); - sc_file_add_acl_entry (file, SC_AC_OP_REHABILITATE, + sc_file_add_acl_entry (file, SC_AC_OP_REHABILITATE, SC_AC_NONE, SC_AC_KEY_REF_NONE); - } - else { - /* the first byte tells use the command or the - command group. We have to mask bit 0 - because this one distinguish between AND/OR - combination of PINs*/ - op = map_operations (buf[0]); - if (op == (unsigned int)-1) - { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "Unknown security command byte %02x\n", - buf[0]); - continue; - } - if (!buf[1]) - sc_file_add_acl_entry (file, op, + } else { + /* the first byte tells use the command or the + command group. We have to mask bit 0 + because this one distinguish between AND/OR + combination of PINs*/ + op = map_operations (buf[0]); + if (op == (unsigned int)-1) { + sc_log(card->ctx, + "Unknown security command byte %02x\n", + buf[0]); + continue; + } + if (!buf[1]) + sc_file_add_acl_entry (file, op, SC_AC_NONE, SC_AC_KEY_REF_NONE); - else - sc_file_add_acl_entry (file, op, + else + sc_file_add_acl_entry (file, op, SC_AC_CHV, buf[1]); - if (!buf[2] && !buf[3]) - sc_file_add_acl_entry (file, op, + if (!buf[2] && !buf[3]) + sc_file_add_acl_entry (file, op, SC_AC_NONE, SC_AC_KEY_REF_NONE); - else - sc_file_add_acl_entry (file, op, + else + sc_file_add_acl_entry (file, op, SC_AC_TERM, (buf[2]<<8)|buf[3]); - } - } + } + } } static int tcos_select_file(sc_card_t *card, - const sc_path_t *in_path, - sc_file_t **file_out) + const sc_path_t *in_path, + sc_file_t **file_out) { sc_context_t *ctx; sc_apdu_t apdu; sc_file_t *file=NULL; u8 buf[SC_MAX_APDU_BUFFER_SIZE], pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; - unsigned int i; - int r, pathlen; + int r; + size_t pathlen; - assert(card != NULL && in_path != NULL); + if (card == NULL || in_path == NULL) + return SC_ERROR_INTERNAL; ctx=card->ctx; memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0x04); - + switch (in_path->type) { case SC_PATH_TYPE_FILE_ID: if (pathlen != 2) return SC_ERROR_INVALID_ARGUMENTS; @@ -386,62 +383,27 @@ static int tcos_select_file(sc_card_t *card, apdu.le = 256; } else { apdu.resplen = 0; - apdu.le = 0; - apdu.p2 = 0x0C; + apdu.le = 0; + apdu.p2 = 0x0C; apdu.cse = (pathlen == 0) ? SC_APDU_CASE_1 : SC_APDU_CASE_3_SHORT; } r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r || file_out == NULL) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); - if (apdu.resplen < 1 || apdu.resp[0] != 0x62){ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "received invalid template %02X\n", apdu.resp[0]); + if (apdu.resplen < 1 || apdu.resp[0] != 0x62) { + sc_log(ctx, "received invalid template %02X\n", apdu.resp[0]); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED); } file = sc_file_new(); - if (file == NULL) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + if (file == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); *file_out = file; file->path = *in_path; - for(i=2; i+1size=0; - for(j=0; jsize = (file->size<<8) | d[j]; - break; - case 0x82: - file->shareable = (d[0] & 0x40) ? 1 : 0; - file->ef_structure = d[0] & 7; - switch ((d[0]>>3) & 7) { - case 0: file->type = SC_FILE_TYPE_WORKING_EF; break; - case 7: file->type = SC_FILE_TYPE_DF; break; - default: - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "invalid file type %02X in file descriptor\n", d[0]); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED); - } - break; - case 0x83: - file->id = (d[0]<<8) | d[1]; - break; - case 0x84: - memcpy(file->name, d, len); - file->namelen = len; - break; - case 0x86: - sc_file_set_sec_attr(file, d, len); - break; - default: - if (len>0) sc_file_set_prop_attr(file, d, len); - } - } - file->magic = SC_FILE_MAGIC; + iso_ops->process_fci(card, file, apdu.resp, apdu.resplen); parse_sec_attr(card, file, file->sec_attr, file->sec_attr_len); @@ -456,7 +418,8 @@ static int tcos_list_files(sc_card_t *card, u8 *buf, size_t buflen) u8 rbuf[SC_MAX_APDU_BUFFER_SIZE], p1; int r, count = 0; - assert(card != NULL); + if (card == NULL) + return SC_ERROR_INTERNAL; ctx = card->ctx; for (p1=1; p1<=2; p1++) { @@ -466,14 +429,14 @@ static int tcos_list_files(sc_card_t *card, u8 *buf, size_t buflen) apdu.resplen = sizeof(rbuf); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); if (apdu.sw1==0x6A && (apdu.sw2==0x82 || apdu.sw2==0x88)) continue; r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "List Dir failed"); + LOG_TEST_RET(ctx, r, "List Dir failed"); if (apdu.resplen > buflen) return SC_ERROR_BUFFER_TOO_SMALL; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "got %"SC_FORMAT_LEN_SIZE_T"u %s-FileIDs\n", - apdu.resplen / 2, p1 == 1 ? "DF" : "EF"); + sc_log(ctx, + "got %"SC_FORMAT_LEN_SIZE_T"u %s-FileIDs\n", + apdu.resplen / 2, p1 == 1 ? "DF" : "EF"); memcpy(buf, apdu.resp, apdu.resplen); buf += apdu.resplen; @@ -492,19 +455,19 @@ static int tcos_delete_file(sc_card_t *card, const sc_path_t *path) SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type has to be SC_PATH_TYPE_FILE_ID\n"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + sc_log(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } sbuf[0] = path->value[0]; sbuf[1] = path->value[1]; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); - apdu.cla |= 0x80; + apdu.cla |= 0x80; apdu.lc = 2; apdu.datalen = 2; apdu.data = sbuf; - + r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } @@ -517,35 +480,35 @@ static int tcos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int r, default_key, tcos3; tcos_data *data; - assert(card != NULL && env != NULL); + if (card == NULL || env == NULL) + return SC_ERROR_INTERNAL; ctx = card->ctx; tcos3=(card->type==SC_CARD_TYPE_TCOS_V3); data=(tcos_data *)card->drv_data; - if (se_num || (env->operation!=SC_SEC_OPERATION_DECIPHER && env->operation!=SC_SEC_OPERATION_SIGN)){ - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + if (se_num || (env->operation!=SC_SEC_OPERATION_DECIPHER && env->operation!=SC_SEC_OPERATION_SIGN)) { + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } if(!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT)) - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "No Key-Reference in SecEnvironment\n"); else - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "Key-Reference %02X (len=%"SC_FORMAT_LEN_SIZE_T"u)\n", - env->key_ref[0], env->key_ref_len); + sc_log(ctx, + "Key-Reference %02X (len=%"SC_FORMAT_LEN_SIZE_T"u)\n", + env->key_ref[0], env->key_ref_len); /* Key-Reference 0x80 ?? */ default_key= !(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || (env->key_ref_len==1 && env->key_ref[0]==0x80); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "TCOS3:%d PKCS1:%d\n", tcos3, - !!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)); + sc_log(ctx, "TCOS3:%d PKCS1 type 01:%d PKCS1 type 02: %d\n", tcos3, + !!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01), + !!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02)); data->pad_flags = env->algorithm_flags; data->next_sign = default_key; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, tcos3 ? 0x41 : 0xC1, 0xB8); p = sbuf; - *p++=0x80; *p++=0x01; *p++=tcos3 ? 0x0A : 0x10; if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { - *p++ = (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) ? 0x83 : 0x84; + *p++ = (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) ? 0x83 : 0x84; *p++ = env->key_ref_len; memcpy(p, env->key_ref, env->key_ref_len); p += env->key_ref_len; @@ -555,12 +518,12 @@ static int tcos_set_security_env(sc_card_t *card, const sc_security_env_t *env, r=sc_transmit_apdu(card, &apdu); if (r) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "%s: APDU transmit failed", sc_strerror(r)); return r; } if (apdu.sw1==0x6A && (apdu.sw2==0x81 || apdu.sw2==0x88)) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "Detected Signature-Only key\n"); if (env->operation==SC_SEC_OPERATION_SIGN && default_key) return SC_SUCCESS; } @@ -576,32 +539,46 @@ static int tcos_restore_security_env(sc_card_t *card, int se_num) static int tcos_compute_signature(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { - size_t i, dlen=datalen; + size_t dlen = datalen; sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; int tcos3, r; - assert(card != NULL && data != NULL && out != NULL); - tcos3=(card->type==SC_CARD_TYPE_TCOS_V3); + if (card == NULL || data == NULL || out == NULL) { + return SC_ERROR_INVALID_ARGUMENTS; + } - if (datalen > 255) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + tcos3 = (card->type == SC_CARD_TYPE_TCOS_V3); - if(((tcos_data *)card->drv_data)->next_sign){ - if(datalen>48){ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Data to be signed is too long (TCOS supports max. 48 bytes)\n"); + // We can sign (key length / 8) bytes + if (datalen > 256) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + } + + if (((tcos_data *)card->drv_data)->next_sign) { + if (datalen > 48) { + sc_log(card->ctx, "Data to be signed is too long (TCOS supports max. 48 bytes)\n"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); memcpy(sbuf, data, datalen); - dlen=datalen; + dlen = datalen; } else { - int keylen= tcos3 ? 256 : 128; - sc_format_apdu(card, &apdu, keylen>255 ? SC_APDU_CASE_4_EXT : SC_APDU_CASE_4_SHORT, 0x2A,0x80,0x86); - for(i=0; i keylen) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + } + + sc_format_apdu(card, &apdu, keylen > 255 ? SC_APDU_CASE_4_EXT : SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); + memset(sbuf, 0xff, sizeof(sbuf)); + sbuf[0] = 0x02; + sbuf[1] = 0x00; + sbuf[2] = 0x01; + sbuf[keylen - datalen] = 0x00; + memcpy(sbuf + keylen - datalen + 1, data, datalen); + dlen = keylen + 1; } apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); @@ -610,14 +587,22 @@ static int tcos_compute_signature(sc_card_t *card, const u8 * data, size_t datal apdu.lc = apdu.datalen = dlen; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if (tcos3 && apdu.p1==0x80 && apdu.sw1==0x6A && apdu.sw2==0x87) { - int keylen=128; - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A,0x80,0x86); - for(i=0; ictx, r, "APDU transmit failed"); + if (tcos3 && apdu.p1 == 0x80 && apdu.sw1 == 0x6A && apdu.sw2 == 0x87) { + size_t keylen = 128; + + if (datalen > keylen) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); + memset(sbuf, 0xff, sizeof(sbuf)); + sbuf[0] = 0x02; + sbuf[1] = 0x00; + sbuf[2] = 0x01; + sbuf[keylen - datalen] = 0x00; + memcpy(sbuf + keylen - datalen + 1, data, datalen); + dlen = keylen + 1; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); @@ -625,12 +610,12 @@ static int tcos_compute_signature(sc_card_t *card, const u8 * data, size_t datal apdu.data = sbuf; apdu.lc = apdu.datalen = dlen; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); } - if (apdu.sw1==0x90 && apdu.sw2==0x00) { + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen>outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } @@ -645,40 +630,48 @@ static int tcos_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len, tcos_data *data; int tcos3, r; - assert(card != NULL && crgram != NULL && out != NULL); + if (card == NULL || crgram == NULL || out == NULL) { + return SC_ERROR_INVALID_ARGUMENTS; + } ctx = card->ctx; - tcos3=(card->type==SC_CARD_TYPE_TCOS_V3); - data=(tcos_data *)card->drv_data; + tcos3 = (card->type == SC_CARD_TYPE_TCOS_V3); + data = (tcos_data *)card->drv_data; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "TCOS3:%d PKCS1:%d\n",tcos3, - !!(data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1)); + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "TCOS3:%d PKCS1 type 02:%d\n", tcos3, + !!(data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02)); - sc_format_apdu(card, &apdu, crgram_len>255 ? SC_APDU_CASE_4_EXT : SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); + sc_format_apdu(card, &apdu, crgram_len > 255 ? SC_APDU_CASE_4_EXT : SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = crgram_len; apdu.data = sbuf; - apdu.lc = apdu.datalen = crgram_len+1; - sbuf[0] = tcos3 ? 0x00 : ((data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1) ? 0x81 : 0x02); - memcpy(sbuf+1, crgram, crgram_len); + apdu.lc = apdu.datalen = crgram_len + 1; + sbuf[0] = tcos3 ? 0x00 : ((data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02) ? 0x81 : 0x02); + if (sizeof sbuf - 1 < crgram_len) + return SC_ERROR_INVALID_ARGUMENTS; + memcpy(sbuf + 1, crgram, crgram_len); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - - if (apdu.sw1==0x90 && apdu.sw2==0x00) { - size_t len= (apdu.resplen>outlen) ? outlen : apdu.resplen; - unsigned int offset=0; - if(tcos3 && (data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1) && apdu.resp[0]==0 && apdu.resp[1]==2){ - offset=2; while(offset outlen) ? outlen : apdu.resplen; + unsigned int offset = 0; + + if (tcos3 && (data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02) + && len > 2 && apdu.resp[0] == 0 && apdu.resp[1] == 2) { + offset = 2; + while (offset < len && apdu.resp[offset] != 0) + ++offset; + offset = (offset < len - 1) ? offset + 1 : 0; } - memcpy(out, apdu.resp+offset, len-offset); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len-offset); + if (offset < len) + memcpy(out, apdu.resp + offset, len - offset); + SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, (int)(len - offset)); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); + SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } @@ -692,46 +685,38 @@ static int tcos_setperm(sc_card_t *card, int enable_nullpin) SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xEE, 0x00, 0x00); - apdu.cla |= 0x80; + apdu.cla |= 0x80; apdu.lc = 0; apdu.datalen = 0; apdu.data = NULL; - + r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } static int tcos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { - int r; - u8 buf[64]; - size_t len; - sc_path_t tpath; - sc_file_t *tfile = NULL; + int r; - if (!serial) return SC_ERROR_INVALID_ARGUMENTS; + if (!serial) + return SC_ERROR_INVALID_ARGUMENTS; /* see if we have cached serial number */ if (card->serialnr.len) { memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } - sc_format_path("3F002F02", &tpath); - r = sc_select_file(card, &tpath, &tfile); - if (r < 0) return r; - len = tfile->size; - sc_file_free(tfile); - if (len > sizeof(buf) || len < 12) return SC_ERROR_INTERNAL; - - r = sc_read_binary(card, 0, buf, len, 0); - if (r < 0) return r; - if (buf[0] != 0x5a || buf[1] > len - 2) return SC_ERROR_INTERNAL; + card->serialnr.len = sizeof card->serialnr.value; + r = sc_parse_ef_gdo(card, card->serialnr.value, &card->serialnr.len, NULL, 0); + if (r < 0) { + card->serialnr.len = 0; + return r; + } - card->serialnr.len = buf[1]; - memcpy(card->serialnr.value, buf+2, buf[1]); + /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; @@ -765,11 +750,10 @@ struct sc_card_driver * sc_get_tcos_driver(void) tcos_ops.select_file = tcos_select_file; tcos_ops.list_files = tcos_list_files; tcos_ops.delete_file = tcos_delete_file; - tcos_ops.set_security_env = tcos_set_security_env; tcos_ops.compute_signature = tcos_compute_signature; tcos_ops.decipher = tcos_decipher; tcos_ops.restore_security_env = tcos_restore_security_env; tcos_ops.card_ctl = tcos_card_ctl; - + return &tcos_drv; } diff --git a/src/libopensc/card-westcos.c b/src/libopensc/card-westcos.c deleted file mode 100644 index 3290b6829b..0000000000 --- a/src/libopensc/card-westcos.c +++ /dev/null @@ -1,1293 +0,0 @@ -/* - * card-westcos.c: support for westcos card - * - * Copyright (C) 2009 francois.leblanc@cev-sa.com - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include - -#include "internal.h" -#include "asn1.h" -#include "cardctl.h" - -#ifdef ENABLE_OPENSSL -#include -#include -#include -#include -#include -#include -#endif - -#ifndef min -#define min(a,b) (((a)<(b))?(a):(b)) -#endif - -#define DEFAULT_TRANSPORT_KEY "6f:59:b0:ed:6e:62:46:4a:5d:25:37:68:23:a8:a2:2d" - -#define JAVACARD (0x01) /* westcos applet on javacard */ -#define RSA_CRYPTO_COMPONENT (0x02) /* card component can do crypto */ - -#define WESTCOS_RSA_NO_HASH_NO_PAD (0x20) -#define WESTCOS_RSA_NO_HASH_PAD_PKCS1 (0x21) - -#ifdef ENABLE_OPENSSL -#define DEBUG_SSL -#ifdef DEBUG_SSL -static void print_openssl_error(void) -{ - static int charge = 0; - long r; - - if (!charge) { - ERR_load_crypto_strings(); - charge = 1; - } - while ((r = ERR_get_error()) != 0) - fprintf(stderr, "%s\n", ERR_error_string(r, NULL)); -} -#endif -#endif - -typedef struct { - sc_security_env_t env; - sc_autkey_t default_key; - int flags; - int file_id; -} priv_data_t; - -static const struct sc_card_operations *iso_ops = NULL; -static struct sc_card_operations westcos_ops; - -static struct sc_card_driver westcos_drv = { - "WESTCOS compatible cards", "westcos", &westcos_ops, NULL, 0, NULL -}; - -static int westcos_get_default_key(sc_card_t * card, - struct sc_cardctl_default_key *data) -{ - const char *default_key; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "westcos_get_default_key:data->method=%d, data->key_ref=%d\n", - data->method, data->key_ref); - if (data->method != SC_AC_AUT || data->key_ref != 0) - return SC_ERROR_NO_DEFAULT_KEY; - default_key = - scconf_get_str(card->ctx->conf_blocks[0], "westcos_default_key", - DEFAULT_TRANSPORT_KEY); - return sc_hex_to_bin(default_key, data->key_data, &data->len); -} - -#define CRC_A 1 -#define CRC_B 2 - -static unsigned short westcos_update_crc(unsigned char ch, unsigned short *lpwCrc) -{ - ch = (ch ^ (unsigned char)((*lpwCrc) & 0x00FF)); - ch = (ch ^ (ch << 4)); - *lpwCrc = - (*lpwCrc >> 8) ^ ((unsigned short)ch << 8) ^ ((unsigned short)ch << - 3) ^ ((unsigned short) - ch >> 4); - return (*lpwCrc); -} - -static void westcos_compute_aetb_crc(int CRCType, - unsigned char *Data, - size_t Length, - unsigned char * TransmitFirst, - unsigned char * TransmitSecond) -{ - unsigned char chBlock; - unsigned short wCrc; - switch (CRCType) { - case CRC_A: - wCrc = 0x6363; /* ITU-V.41 */ - break; - case CRC_B: - wCrc = 0xFFFF; /* ISO 3309 */ - break; - default: - return; - } - - do { - chBlock = *Data++; - westcos_update_crc(chBlock, &wCrc); - } while (--Length); - if (CRCType == CRC_B) - wCrc = ~wCrc; /* ISO 3309 */ - *TransmitFirst = (unsigned char) (wCrc & 0xFF); - *TransmitSecond = (unsigned char) ((wCrc >> 8) & 0xFF); - return; -} - -static int westcos_check_sw(sc_card_t * card, unsigned int sw1, - unsigned int sw2) -{ - if ((sw1 == 0x69) && (sw2 == 0x88)) - return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; - assert(iso_ops && iso_ops->check_sw); - return iso_ops->check_sw(card, sw1, sw2); -} - -static struct sc_atr_table westcos_atrs[] = { - /* westcos 2ko */ - { "3F:69:00:00:00:64:01:00:00:00:80:90:00", "ff:ff:ff:ff:ff:ff:ff:00:00:00:f0:ff:ff", NULL, 0x00, 0, NULL }, - /* westcos applet */ - { "3B:95:94:80:1F:C3:80:73:C8:21:13:54", "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff", NULL, JAVACARD, 0, NULL }, - { NULL, NULL, NULL, 0, 0, NULL } -}; - -static int westcos_finish(sc_card_t * card) -{ - if (card->algorithms) - free(card->algorithms); - card->algorithms = NULL; - card->algorithm_count = 0; - if (card->drv_data) - free(card->drv_data); - return 0; -} - -static int westcos_match_card(sc_card_t * card) -{ - int i; - - i = _sc_match_atr(card, westcos_atrs, &card->type); - if (i < 0) - return 0; - - /* JAVACARD, look for westcos applet */ - if (i == 1) { - int r; - sc_apdu_t apdu; - u8 aid[] = { - 0xA0, 0x00, 0xCE, 0x00, 0x07, 0x01 - }; - sc_format_apdu(card, &apdu, - SC_APDU_CASE_3_SHORT, 0xA4, 0x04, - 0); - apdu.cla = 0x00; - apdu.lc = sizeof(aid); - apdu.datalen = sizeof(aid); - apdu.data = aid; - r = sc_transmit_apdu(card, &apdu); - if (r) - return 0; - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r) - return 0; - } - - return 1; -} - -static int westcos_init(sc_card_t * card) -{ - int r; - const char *default_key; - unsigned long exponent, flags; - priv_data_t *priv_data; - - if (card == NULL) - return SC_ERROR_INVALID_ARGUMENTS; - - card->drv_data = malloc(sizeof(priv_data_t)); - if (card->drv_data == NULL) - return SC_ERROR_OUT_OF_MEMORY; - memset(card->drv_data, 0, sizeof(priv_data_t)); - - priv_data = (priv_data_t *) card->drv_data; - - if (card->type & JAVACARD) { - priv_data->flags |= JAVACARD; - } - - /* check for crypto component */ - if(card->atr.value[9] == 0xD0) - { - priv_data->flags |= RSA_CRYPTO_COMPONENT; - } - - card->cla = 0x00; - card->max_send_size = 240; - card->max_recv_size = 240; - exponent = 0; - flags = SC_ALGORITHM_RSA_RAW; - flags |= SC_ALGORITHM_RSA_HASH_NONE; - flags |= SC_ALGORITHM_RSA_PAD_NONE | SC_ALGORITHM_RSA_PAD_PKCS1; - flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; - _sc_card_add_rsa_alg(card, 128, flags, exponent); - _sc_card_add_rsa_alg(card, 256, flags, exponent); - _sc_card_add_rsa_alg(card, 512, flags, exponent); - _sc_card_add_rsa_alg(card, 768, flags, exponent); - _sc_card_add_rsa_alg(card, 1024, flags, exponent); - _sc_card_add_rsa_alg(card, 1100, flags, exponent); - _sc_card_add_rsa_alg(card, 1200, flags, exponent); - _sc_card_add_rsa_alg(card, 1300, flags, exponent); - _sc_card_add_rsa_alg(card, 1400, flags, exponent); - _sc_card_add_rsa_alg(card, 1536, flags, exponent); - _sc_card_add_rsa_alg(card, 2048, flags, exponent); - default_key = - scconf_get_str(card->ctx->conf_blocks[0], "westcos_default_key", - DEFAULT_TRANSPORT_KEY); - if (default_key) { - priv_data = (priv_data_t *) (card->drv_data); - priv_data->default_key.key_reference = 0; - priv_data->default_key.key_len = - sizeof(priv_data->default_key.key_value); - r = sc_hex_to_bin(default_key, priv_data->default_key.key_value, - &(priv_data->default_key.key_len)); - if (r) - return (r); - } - return 0; -} - -static int westcos_select_file(sc_card_t * card, const sc_path_t * in_path, - sc_file_t ** file_out) -{ - priv_data_t *priv_data = (priv_data_t *) card->drv_data; - - assert(iso_ops && iso_ops->select_file); - priv_data->file_id = 0; - return iso_ops->select_file(card, in_path, file_out); -} - -static int _westcos2opensc_ac(u8 flag) -{ - if (flag == 0) - return SC_AC_NEVER; - else if (flag == 1) - return SC_AC_CHV; - else if (flag == 2) - return SC_AC_AUT; - else if (flag == 15) - return SC_AC_NONE; - return SC_AC_UNKNOWN; -} - -static int westcos_process_fci(sc_card_t * card, sc_file_t * file, - const u8 * buf, size_t buflen) -{ - sc_context_t *ctx = card->ctx; - size_t taglen, len = buflen; - const u8 *tag = NULL, *p = buf; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "processing FCI bytes\n"); - tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen); - if (tag != NULL && taglen == 2) { - file->id = (tag[0] << 8) | tag[1]; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - " file identifier: 0x%02X%02X\n", tag[0], tag[1]); - } - tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); - if (tag != NULL && taglen >= 2) { - int bytes = (tag[0] << 8) + tag[1]; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - " bytes in file: %d\n", bytes); - file->size = bytes; - } - if (tag == NULL) { - tag = sc_asn1_find_tag(ctx, p, len, 0x81, &taglen); - if (tag != NULL && taglen >= 2) { - int bytes = (tag[0] << 8) + tag[1]; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - " bytes in file: %d\n", bytes); - file->size = bytes; - } - } - tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); - if (tag != NULL) { - if (taglen > 0) { - unsigned char byte = tag[0]; - const char *type; - file->shareable = 0; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - " shareable: %s\n", - (file->shareable) ? "yes" : "no"); - file->ef_structure = SC_FILE_EF_UNKNOWN; - switch (byte) { - case 0x38: - type = "DF"; - file->type = SC_FILE_TYPE_DF; - break; - case 0x01: - type = "working or internal EF"; - file->type = SC_FILE_TYPE_WORKING_EF; - file->ef_structure = SC_FILE_EF_TRANSPARENT; - break; - case 0x02: - type = "working or internal EF"; - file->type = SC_FILE_TYPE_WORKING_EF; - file->ef_structure = SC_FILE_EF_LINEAR_FIXED; - break; - case 0x06: - type = "working or internal EF"; - file->type = SC_FILE_TYPE_WORKING_EF; - file->ef_structure = SC_FILE_EF_CYCLIC; - break; - default: - type = "unknow"; - } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - " type: %s\n", type); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - " EF structure: %d\n", file->ef_structure); - } - } - tag = sc_asn1_find_tag(ctx, p, len, 0x84, &taglen); - if (tag != NULL && taglen > 0 && taglen <= 16) { - memcpy(file->name, tag, taglen); - file->namelen = taglen; - { - char tbuf[128]; - sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, - file->name, file->namelen, tbuf, sizeof(tbuf)); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - " File name: %s\n", tbuf); - } - } - if (file->type == SC_FILE_TYPE_DF) { - tag = sc_asn1_find_tag(ctx, p, len, 0x85, &taglen); - if (tag != NULL && taglen == 3) { - file->size = tag[1] * 256 + tag[2]; - } else - file->size = 0; - } - tag = sc_asn1_find_tag(ctx, p, len, 0xA5, &taglen); - if (tag != NULL && taglen) { - sc_file_set_prop_attr(file, tag, taglen); - } - tag = sc_asn1_find_tag(ctx, p, len, 0x86, &taglen); - if (tag != NULL && taglen) { - sc_file_set_sec_attr(file, tag, taglen); - - /* FIXME: compact file system only */ - if (file->type == SC_FILE_TYPE_DF) { - sc_file_add_acl_entry(file, SC_AC_OP_SELECT, - _westcos2opensc_ac(tag[0] >> - 4), - tag[0 + 4] >> 4); - sc_file_add_acl_entry(file, SC_AC_OP_CREATE, - _westcos2opensc_ac(tag[0] & - 0x0f), - tag[0 + 4] & 0x0f); - sc_file_add_acl_entry(file, SC_AC_OP_INVALIDATE, - _westcos2opensc_ac(tag[1] >> - 4), - tag[1 + 4] >> 4); - } - - else { - if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { - sc_file_add_acl_entry(file, SC_AC_OP_READ, - _westcos2opensc_ac(tag[0] - >> - 4), - tag[0 + 4] >> 4); - sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, - _westcos2opensc_ac(tag[0] - & - 0x0f), - tag[0 + 4] & 0x0f); - sc_file_add_acl_entry(file, - SC_AC_OP_INVALIDATE, - _westcos2opensc_ac(tag[1] - >> - 4), - tag[1 + 4] >> 4); - sc_file_add_acl_entry(file, SC_AC_OP_ERASE, - _westcos2opensc_ac(tag[1] - & - 0x0f), - tag[1 + 4] & 0x0f); - } - - else { - sc_file_add_acl_entry(file, SC_AC_OP_READ, - _westcos2opensc_ac(tag[0] - >> - 4), - tag[0 + 4] >> 4); - sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, - _westcos2opensc_ac(tag[0] - & - 0x0f), - tag[0 + 4] & 0x0f); - sc_file_add_acl_entry(file, - SC_AC_OP_INVALIDATE, - _westcos2opensc_ac(tag[1] - >> - 4), - tag[1 + 4] >> 4); - } - } - } - return 0; -} - -#define HIGH (0) -#define LOW (1) -static int _convertion_ac_methode(sc_file_t * file, int low, - unsigned int operation, u8 * buf, - u8 * buf_key) -{ - const struct sc_acl_entry *acl; - acl = sc_file_get_acl_entry(file, operation); - if (acl == NULL) { - - /* par defaut always */ - *buf = 0xff; - *buf_key = 0x00; - return 0; - } - switch (acl->method) { - case SC_AC_NONE: - if (low) - *buf |= 0x0f; - - else - *buf |= 0xf0; - break; - case SC_AC_CHV: /* Card Holder Verif. */ - if (low) - *buf |= 0x01; - - else - *buf |= 0x10; - break; - case SC_AC_TERM: /* Terminal auth. */ - return SC_ERROR_NOT_SUPPORTED; - case SC_AC_PRO: /* Secure Messaging */ - return SC_ERROR_NOT_SUPPORTED; - case SC_AC_AUT: /* Key auth. */ - if (low) - *buf |= 0x02; - - else - *buf |= 0x20; - if (acl->key_ref > 15) - return SC_ERROR_NOT_SUPPORTED; - if (low) - *buf_key |= acl->key_ref; - - else - *buf_key |= (acl->key_ref) << 4; - break; - case SC_AC_NEVER: - *buf |= 0; - break; - default: - return SC_ERROR_NOT_SUPPORTED; - } - return 0; -} - -static int westcos_create_file(sc_card_t *card, struct sc_file *file) -{ - int r; - sc_apdu_t apdu; - u8 buf[12], p1 = 0, p2 = 0; - int buflen; - if (card == NULL) - return SC_ERROR_INVALID_ARGUMENTS; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "westcos_create_file\n"); - memset(buf, 0, sizeof(buf)); - - /* transport key */ - r = sc_card_ctl(card, SC_CARDCTL_WESTCOS_AUT_KEY, NULL); - if (r) - return (r); - buflen = sizeof(buf); - switch (file->type) { - case SC_FILE_TYPE_DF: - buf[0] = 0x00; - buf[1] = 0x01; - _convertion_ac_methode(file, HIGH, SC_AC_OP_SELECT, &buf[2], - &buf[2 + 4]); - _convertion_ac_methode(file, LOW, SC_AC_OP_CREATE, &buf[2], - &buf[2 + 4]); - _convertion_ac_methode(file, HIGH, SC_AC_OP_INVALIDATE, - &buf[3], &buf[3 + 4]); - buflen = 10; - break; - case SC_FILE_TYPE_INTERNAL_EF: - buf[0] |= 0x80; - /* fall through */ - case SC_FILE_TYPE_WORKING_EF: - switch (file->ef_structure) { - case SC_FILE_EF_TRANSPARENT: - buf[0] |= 0x20; /* no transaction support */ - buf[1] |= 0; - _convertion_ac_methode(file, HIGH, SC_AC_OP_READ, - &buf[2], &buf[2 + 4]); - _convertion_ac_methode(file, LOW, SC_AC_OP_UPDATE, - &buf[2], &buf[2 + 4]); - _convertion_ac_methode(file, HIGH, SC_AC_OP_INVALIDATE, - &buf[3], &buf[3 + 4]); - _convertion_ac_methode(file, LOW, SC_AC_OP_ERASE, - &buf[3], &buf[3 + 4]); - buf[10] = (u8) ((file->size) / 256); - buf[11] = (u8) ((file->size) % 256); - break; - case SC_FILE_EF_LINEAR_FIXED: - buf[0] |= 0x40; /* no transaction support */ - buf[1] |= 0; - _convertion_ac_methode(file, HIGH, SC_AC_OP_READ, - &buf[2], &buf[2 + 4]); - _convertion_ac_methode(file, LOW, SC_AC_OP_UPDATE, - &buf[2], &buf[2 + 4]); - _convertion_ac_methode(file, HIGH, SC_AC_OP_INVALIDATE, - &buf[3], &buf[3 + 4]); - buf[10] = file->record_count; - buf[11] = file->record_length; - break; - case SC_FILE_EF_CYCLIC: - buf[0] |= 0x60; /* no transaction support */ - buf[1] |= 0; - _convertion_ac_methode(file, HIGH, SC_AC_OP_READ, - &buf[2], &buf[2 + 4]); - _convertion_ac_methode(file, LOW, SC_AC_OP_UPDATE, - &buf[2], &buf[2 + 4]); - _convertion_ac_methode(file, HIGH, SC_AC_OP_INVALIDATE, - &buf[3], &buf[3 + 4]); - buf[10] = file->record_count; - buf[11] = file->record_length; - break; - case SC_FILE_EF_LINEAR_VARIABLE: - case SC_FILE_EF_UNKNOWN: - case SC_FILE_EF_LINEAR_FIXED_TLV: - case SC_FILE_EF_LINEAR_VARIABLE_TLV: - case SC_FILE_EF_CYCLIC_TLV: - default: - return SC_ERROR_NOT_SUPPORTED; - } - break; - default: - return SC_ERROR_NOT_SUPPORTED; - } - if (file->shareable) - buf[0] |= 0x08; - if (file->path.len >= 2) { - p1 = file->path.value[file->path.len - 2]; - p2 = file->path.value[file->path.len - 1]; - } - - else if (file->id) { - p1 = (file->id) / 256; - p2 = (file->id) % 256; - } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "create file %s, id %X size %"SC_FORMAT_LEN_SIZE_T"u\n", - file->path.value, file->id, file->size); - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, p1, p2); - apdu.cla = 0x80; - apdu.lc = buflen; - apdu.datalen = buflen; - apdu.data = buf; - r = sc_transmit_apdu(card, &apdu); - if (r) - return (r); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - return r; -} - -static int westcos_delete_file(sc_card_t * card, const sc_path_t * path_in) -{ - int r; - sc_apdu_t apdu; - if (card == NULL || path_in == NULL || path_in->len < 2) - return SC_ERROR_INVALID_ARGUMENTS; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "westcos_delete_file\n"); - if (path_in->len > 2) { - r = sc_select_file(card, path_in, NULL); - if (r) - return (r); - } - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, - path_in->value[path_in->len - 2], - path_in->value[path_in->len - 1]); - apdu.cla = 0x80; - r = sc_transmit_apdu(card, &apdu); - if (r) - return (r); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r) - return (r); - return 0; -} - -static int westcos_list_files(sc_card_t * card, u8 * buf, size_t buflen) -{ - int r; - sc_apdu_t apdu; - if (card == NULL) - return SC_ERROR_INVALID_ARGUMENTS; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "westcos_list_files\n"); - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x34, 0x00, 0x00); - apdu.cla = 0x80; - apdu.le = buflen; - apdu.resplen = buflen; - apdu.resp = buf; - r = sc_transmit_apdu(card, &apdu); - if (r) - return (r); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r) - return (r); - return apdu.resplen; -} - -static int westcos_get_crypte_challenge(sc_card_t * card, const u8 * key, - u8 * result, size_t * len) -{ - int r; -#ifdef ENABLE_OPENSSL - DES_key_schedule ks1, ks2; -#endif - u8 buf[8]; - if ((*len) < sizeof(buf)) - return SC_ERROR_INVALID_ARGUMENTS; - *len = 8; - r = sc_get_challenge(card, buf, *len); - if (r) - return r; -#ifdef ENABLE_OPENSSL - DES_set_key((const_DES_cblock *) & key[0], &ks1); - DES_set_key((const_DES_cblock *) & key[8], &ks2); - DES_ecb2_encrypt((const_DES_cblock *)buf, (DES_cblock*)result, &ks1, &ks2, DES_ENCRYPT); - return SC_SUCCESS; -#else - return SC_ERROR_NOT_SUPPORTED; -#endif -} - -static int westcos_pin_cmd(sc_card_t * card, struct sc_pin_cmd_data *data, - int *tries_left) -{ - int r; - u8 buf[20]; - sc_apdu_t apdu; - size_t len = 0; - int pad = 0, use_pin_pad = 0, ins, p1 = 0; - if (card == NULL) - return SC_ERROR_INVALID_ARGUMENTS; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "westcos_pin_cmd:data->pin_type=%X, data->cmd=%X\n", - data->pin_type, data->cmd); - if (tries_left) - *tries_left = -1; - switch (data->pin_type) { - case SC_AC_AUT: - len = sizeof(buf); - r = westcos_get_crypte_challenge(card, data->pin1.data, buf, - &len); - if (r) - return (r); - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x82, 0x00, - data->pin_reference); - apdu.lc = len; - apdu.datalen = len; - apdu.data = buf; - r = sc_transmit_apdu(card, &apdu); - if (r) - return (r); - return sc_check_sw(card, apdu.sw1, apdu.sw2); - break; - case SC_AC_CHV: - if (data->flags & SC_PIN_CMD_NEED_PADDING) - pad = 1; - if (data->flags & SC_PIN_CMD_USE_PINPAD) - use_pin_pad = 1; - data->pin1.offset = 0; - data->pin1.encoding = SC_PIN_ENCODING_GLP; - if (data->pin1.min_length == 0) - data->pin1.min_length = 4; - if (data->pin1.max_length == 0) - data->pin1.max_length = 12; - switch (data->cmd) { - case SC_PIN_CMD_VERIFY: - ins = 0x20; - if ((r = - sc_build_pin(buf, sizeof(buf), &data->pin1, - pad)) < 0) - return r; - len = r; - break; - case SC_PIN_CMD_CHANGE: - ins = 0x24; - if (data->pin1.len != 0 || use_pin_pad) { - if ((r = - sc_build_pin(buf, sizeof(buf), - &data->pin1, pad)) < 0) - return r; - len += r; - } else { - - /* implicit test */ - p1 = 1; - } - data->pin2.offset = data->pin1.offset + len; - data->pin2.encoding = SC_PIN_ENCODING_GLP; - if ((r = - sc_build_pin(buf + len, sizeof(buf) - len, - &data->pin2, pad)) < 0) - return r; - len += r; - break; - case SC_PIN_CMD_UNBLOCK: - ins = 0x2C; - if (data->pin1.len != 0 || use_pin_pad) { - if ((r = - sc_build_pin(buf, sizeof(buf), - &data->pin1, pad)) < 0) - return r; - len += r; - } else { - p1 |= 0x02; - } - if (data->pin2.len != 0 || use_pin_pad) { - data->pin2.offset = data->pin1.offset + len; - data->pin2.encoding = SC_PIN_ENCODING_GLP; - if ((r = - sc_build_pin(buf + len, sizeof(buf) - len, - &data->pin2, pad)) < 0) - return r; - len += r; - } else { - p1 |= 0x01; - } - break; - default: - return SC_ERROR_NOT_SUPPORTED; - } - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, ins, p1, - data->pin_reference); - apdu.lc = len; - apdu.datalen = len; - apdu.data = buf; - apdu.resplen = 0; - if (!use_pin_pad) { - - /* Transmit the APDU to the card */ - r = sc_transmit_apdu(card, &apdu); - - /* Clear the buffer - it may contain pins */ - sc_mem_clear(buf, sizeof(buf)); - } else { - data->apdu = &apdu; - if (card->reader - && card->reader->ops - && card->reader->ops->perform_verify) { - r = card->reader->ops->perform_verify(card-> - reader, - data); - } else { - r = SC_ERROR_NOT_SUPPORTED; - } - } - if (r) - return (r); - return sc_check_sw(card, apdu.sw1, apdu.sw2); - default: - return SC_ERROR_NOT_SUPPORTED; - } -} - -static int sc_get_atr(sc_card_t * card) -{ - int r; - sc_apdu_t apdu; - u8 buf[sizeof(card->atr.value)]; - if (card == NULL) - return SC_ERROR_INVALID_ARGUMENTS; - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xEC, 0x00, 0x00); - apdu.cla = 0x80; - apdu.le = 0x0d; - apdu.resplen = 0x0d; - apdu.resp = buf; - r = sc_transmit_apdu(card, &apdu); - if (r) - return (r); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r) - return (r); - memcpy(card->atr.value, buf, sizeof(card->atr.value)); - card->atr.len = apdu.resplen; - return r; -} - -static int sc_lock_phase(sc_card_t * card, u8 phase) -{ - int r; - sc_apdu_t apdu; - if (card == NULL) - return SC_ERROR_INVALID_ARGUMENTS; - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x16, phase, 0x00); - apdu.cla = 0x80; - r = sc_transmit_apdu(card, &apdu); - if (r) - return (r); - return sc_check_sw(card, apdu.sw1, apdu.sw2); -} - -static int westcos_card_ctl(sc_card_t * card, unsigned long cmd, void *ptr) -{ - unsigned int i; - int r; - size_t buflen; - u8 buf[256]; - sc_apdu_t apdu; - struct sc_pin_cmd_data data; - sc_serial_number_t *serialnr; - priv_data_t *priv_data = NULL; - if (card == NULL) - return SC_ERROR_INVALID_ARGUMENTS; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "westcos_card_ctl cmd = %lX\n", cmd); - priv_data = (priv_data_t *) card->drv_data; - switch (cmd) { - case SC_CARDCTL_GET_DEFAULT_KEY: - return westcos_get_default_key(card, - (struct sc_cardctl_default_key - *)ptr); - break; - case SC_CARDCTL_LIFECYCLE_SET: - if (1) { - int mode = *((int *)ptr); - switch (mode) { - case SC_CARDCTRL_LIFECYCLE_ADMIN: - if (priv_data->flags & JAVACARD) { - return 0; - } - if (card->atr.value[10] == 0x80 - || card->atr.value[10] == 0x81) - return 0; - return SC_ERROR_CARD_CMD_FAILED; - case SC_CARDCTRL_LIFECYCLE_USER: - if (card->atr.value[10] == 0x80) { - r = sc_lock_phase(card, 0x02); - if (r) - return (r); - r = sc_get_atr(card); - if (r) - return (r); - r = sc_card_ctl(card, - SC_CARDCTL_WESTCOS_AUT_KEY, - NULL); - if (r) - return (r); - } - if (card->atr.value[10] == 0x81) { - r = sc_lock_phase(card, 0x01); - if (r) - return (r); - r = sc_get_atr(card); - if (r) - return (r); - return 0; - } - return SC_ERROR_CARD_CMD_FAILED; - case SC_CARDCTRL_LIFECYCLE_OTHER: - default: - break; - } - } - break; - case SC_CARDCTL_GET_SERIALNR: - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xEE, 0x00, - 0x00); - apdu.cla = 0xb0; - apdu.le = 8; - apdu.resp = buf; - apdu.resplen = 10; /* include SW's */ - r = sc_transmit_apdu(card, &apdu); - if (r) - return (r); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r) - return (r); - if (SC_MAX_SERIALNR < 8) - return SC_ERROR_NOT_SUPPORTED; - serialnr = (sc_serial_number_t *) ptr; - serialnr->len = 8; - memcpy(serialnr->value, buf, serialnr->len); - return 0; - case SC_CARDCTL_WESTCOS_CREATE_MF: - buf[0] = *((u8 *) ptr); - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x3F, - 0x00); - apdu.cla = 0x80; - apdu.lc = 1; - apdu.datalen = 1; - apdu.data = buf; - apdu.le = 0; - r = sc_transmit_apdu(card, &apdu); - if (r) - return (r); - return sc_check_sw(card, apdu.sw1, apdu.sw2); - case SC_CARDCTL_WESTCOS_COMMIT: - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2C, 0x00, 0x00); - apdu.cla = 0x80; - r = sc_transmit_apdu(card, &apdu); - if (r) - return (r); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r) - return (r); - return r; - case SC_CARDCTL_WESTCOS_ROLLBACK: - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x24, 0x00, 0x00); - apdu.cla = 0x80; - r = sc_transmit_apdu(card, &apdu); - if (r) - return (r); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r) - return (r); - return r; - case SC_CARDCTL_WESTCOS_AUT_KEY: - if (ptr != NULL) - priv_data->default_key = *((sc_autkey_t *) ptr); - memset(&data, 0, sizeof(data)); - data.pin_type = SC_AC_AUT; - data.pin_reference = priv_data->default_key.key_reference; - data.pin1.len = priv_data->default_key.key_len; - data.pin1.data = priv_data->default_key.key_value; - return sc_pin_cmd(card, &data, NULL); - case SC_CARDCTL_WESTCOS_CHANGE_KEY: - if (1) { - int lrc; - u8 temp[7]; - sc_changekey_t *ck = (sc_changekey_t *) ptr; - sc_autkey_t master_key; - if (ck->master_key.key_len != 0) - master_key = ck->master_key; - - else - master_key = priv_data->default_key; - memcpy(temp, ck->key_template, sizeof(temp)); - westcos_compute_aetb_crc(CRC_A, ck->new_key.key_value, - ck->new_key.key_len, &temp[5], &temp[6]); - for (i = 0, temp[4] = 0xAA, lrc = 0; i < sizeof(temp); - i++) - lrc += temp[i]; - temp[4] = (lrc % 256); - buflen = sizeof(buf); - r = westcos_get_crypte_challenge(card, - master_key.key_value, - buf, &buflen); - if (r) - return (r); - memcpy(&buf[buflen], temp, sizeof(temp)); - buflen += sizeof(temp); - memcpy(&buf[buflen], ck->new_key.key_value, - ck->new_key.key_len); - buflen += ck->new_key.key_len; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, - 0xD8, ck->new_key.key_reference, - master_key.key_reference); - apdu.cla = 0x80; - apdu.lc = buflen; - apdu.datalen = buflen; - apdu.data = buf; - r = sc_transmit_apdu(card, &apdu); - if (r) - return (r); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r) - return (r); - return r; - } - case SC_CARDCTL_WESTCOS_SET_DEFAULT_KEY: - priv_data->default_key = *((sc_autkey_t *) ptr); - return 0; - case SC_CARDCTL_WESTCOS_LOAD_DATA: - - /* ptr[0] = 0x01 pour generique appli, 0x81 pour appli avec pme */ - buf[0] = *((u8 *) ptr); - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xB2, 0x80, - 0x14); - apdu.cla = 0xB0; - apdu.lc = 1; - apdu.datalen = 1; - apdu.data = buf; - r = sc_transmit_apdu(card, &apdu); - if (r) - return (r); - return sc_check_sw(card, apdu.sw1, apdu.sw2); - } - return SC_ERROR_NOT_SUPPORTED; -} - -static int westcos_set_security_env(sc_card_t *card, - const struct sc_security_env *env, - int se_num) -{ - int r = 0; - priv_data_t *priv_data = NULL; - if (card == NULL) - return SC_ERROR_INVALID_ARGUMENTS; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "westcos_set_security_env\n"); - priv_data = (priv_data_t *) card->drv_data; - priv_data->env = *env; - - if(priv_data->flags & RSA_CRYPTO_COMPONENT) - { - sc_apdu_t apdu; - unsigned char mode = 0; - u8 buf[128]; - - if ((priv_data->env.flags) & SC_ALGORITHM_RSA_PAD_PKCS1) - mode = WESTCOS_RSA_NO_HASH_PAD_PKCS1; - else if ((priv_data->env.flags) & SC_ALGORITHM_RSA_RAW) - mode = WESTCOS_RSA_NO_HASH_NO_PAD; - - r = sc_path_print((char *)buf, sizeof(buf), &(env->file_ref)); - if(r) - return r; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xf0, mode); - apdu.cla = 0x00; - apdu.lc = strlen((char *)buf); - apdu.datalen = apdu.lc; - apdu.data = buf; - r = sc_transmit_apdu(card, &apdu); - if (r) - return (r); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - } - - return r; -} - -static int westcos_restore_security_env(sc_card_t *card, int se_num) -{ - if (card == NULL) - return SC_ERROR_INVALID_ARGUMENTS; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "westcos_restore_security_env\n"); - return 0; -} - -static int westcos_sign_decipher(int mode, sc_card_t *card, - const u8 * data, size_t data_len, u8 * out, - size_t outlen) -{ - int r; - sc_file_t *keyfile = NULL; -#ifdef ENABLE_OPENSSL - int idx = 0; - u8 buf[180]; - priv_data_t *priv_data = NULL; - int pad; - RSA *rsa = NULL; - BIO *mem = BIO_new(BIO_s_mem()); -#endif - - if (card == NULL) - return SC_ERROR_INVALID_ARGUMENTS; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "westcos_sign_decipher outlen=%"SC_FORMAT_LEN_SIZE_T"u\n", - outlen); - -#ifndef ENABLE_OPENSSL - r = SC_ERROR_NOT_SUPPORTED; -#else - if (mem == NULL || card->drv_data == NULL) { - r = SC_ERROR_OUT_OF_MEMORY; - goto out; - } - - priv_data = (priv_data_t *) card->drv_data; - - if(priv_data->flags & RSA_CRYPTO_COMPONENT) - { - sc_apdu_t apdu; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x00, mode); - apdu.datalen = data_len; - apdu.data = data; - apdu.lc = data_len; - apdu.le = outlen > 240 ? 240 : outlen; - apdu.resp = out; - apdu.resplen = outlen; - - r = sc_transmit_apdu(card, &apdu); - if (r) - goto out2; - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if(r) - goto out2; - - /* correct */ - r = apdu.resplen; - goto out2; - } - if ((priv_data->env.flags) & SC_ALGORITHM_RSA_PAD_PKCS1) - pad = RSA_PKCS1_PADDING; - - else if ((priv_data->env.flags) & SC_ALGORITHM_RSA_RAW) - pad = RSA_NO_PADDING; - - else { - r = SC_ERROR_INVALID_ARGUMENTS; - goto out; - } - r = sc_select_file(card, &(priv_data->env.file_ref), &keyfile); - if (r || !keyfile) - goto out; - - do { - int alire; - alire = min(((keyfile->size) - idx), sizeof(buf)); - if (alire <= 0) - break; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "idx = %d, alire=%d\n", idx, alire); - r = sc_read_binary(card, idx, buf, alire, 0); - if (r < 0) - goto out; - BIO_write(mem, buf, r); - idx += r; - } while (1); - BIO_set_mem_eof_return(mem, -1); - if (!d2i_RSAPrivateKey_bio(mem, &rsa)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "RSA key invalid, %lu\n", ERR_get_error()); - r = SC_ERROR_UNKNOWN; - goto out; - } - - /* pkcs11 reset openssl functions */ - RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); - - if ((size_t)RSA_size(rsa) > outlen) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Buffer too small\n"); - r = SC_ERROR_OUT_OF_MEMORY; - goto out; - } -#if 1 - if (mode) { /* decipher */ - r = RSA_private_decrypt(data_len, data, out, rsa, pad); - if (r == -1) { - -#ifdef DEBUG_SSL - print_openssl_error(); - -#endif - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "Decipher error %lu\n", ERR_get_error()); - r = SC_ERROR_UNKNOWN; - goto out; - } - } - - else { /* sign */ - - r = RSA_private_encrypt(data_len, data, out, rsa, pad); - if (r == -1) { - -#ifdef DEBUG_SSL - print_openssl_error(); - -#endif - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "Signature error %lu\n", ERR_get_error()); - r = SC_ERROR_UNKNOWN; - goto out; - } - } - -#else - if (RSA_sign(nid, data, data_len, out, &outlen, rsa) != 1) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "RSA_sign error %d \n", ERR_get_error()); - r = SC_ERROR_UNKNOWN; - goto out; - } - r = outlen; - -#endif -out: - if (mem) - BIO_free(mem); - if (rsa) - RSA_free(rsa); -out2: -#endif /* ENABLE_OPENSSL */ - sc_file_free(keyfile); - return r; -} - -static int westcos_compute_signature(sc_card_t *card, const u8 * data, - size_t data_len, u8 * out, size_t outlen) -{ - return westcos_sign_decipher(0, card, data, data_len, out, outlen); -} - -static int westcos_decipher(sc_card_t *card, const u8 * crgram, - size_t crgram_len, u8 * out, size_t outlen) -{ - return westcos_sign_decipher(1, card, crgram, crgram_len, out, outlen); -} - -struct sc_card_driver *sc_get_westcos_driver(void) -{ - if (iso_ops == NULL) - iso_ops = sc_get_iso7816_driver()->ops; - westcos_ops = *iso_ops; - - westcos_ops.match_card = westcos_match_card; - westcos_ops.init = westcos_init; - westcos_ops.finish = westcos_finish; - /* read_binary */ - /* write_binary */ - /* update_binary */ - /* read_record */ - /* write_record */ - /* append_record */ - /* update_record */ - westcos_ops.select_file = westcos_select_file; - /* get_response */ - /* get_challenge */ - westcos_ops.restore_security_env = westcos_restore_security_env; - westcos_ops.set_security_env = westcos_set_security_env; - westcos_ops.decipher = westcos_decipher; - westcos_ops.compute_signature = westcos_compute_signature; - westcos_ops.create_file = westcos_create_file; - westcos_ops.delete_file = westcos_delete_file; - westcos_ops.list_files = westcos_list_files; - westcos_ops.check_sw = westcos_check_sw; - westcos_ops.card_ctl = westcos_card_ctl; - westcos_ops.process_fci = westcos_process_fci; - westcos_ops.construct_fci = NULL; - westcos_ops.pin_cmd = westcos_pin_cmd; - - return &westcos_drv; -} - diff --git a/src/libopensc/card.c b/src/libopensc/card.c index 194634bd36..f79989e5d9 100644 --- a/src/libopensc/card.c +++ b/src/libopensc/card.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -28,16 +28,13 @@ #include #endif #include +#include #include "reader-tr03119.h" #include "internal.h" #include "asn1.h" #include "common/compat_strlcpy.h" -/* -#define INVALIDATE_CARD_CACHE_IN_UNLOCK -*/ - #ifdef ENABLE_SM static int sc_card_sm_load(sc_card_t *card, const char *path, const char *module); static int sc_card_sm_unload(sc_card_t *card); @@ -67,6 +64,65 @@ void sc_format_apdu(sc_card_t *card, sc_apdu_t *apdu, apdu->p2 = (u8) p2; } +void sc_format_apdu_cse_lc_le(struct sc_apdu *apdu) +{ + /* TODO calculating the APDU case, Lc and Le should actually only be + * done in sc_apdu2bytes, but to gradually change OpenSC we start here. */ + /* Let sc_detect_apdu_cse set short or extended and test for chaining */ + + if (!apdu) + return; + if (apdu->datalen > SC_MAX_APDU_DATA_SIZE + || apdu->resplen > SC_MAX_APDU_RESP_SIZE) { + /* extended length or data chaining and/or get response */ + if (apdu->datalen <= SC_MAX_EXT_APDU_DATA_SIZE) + apdu->lc = apdu->datalen; + if (apdu->resplen <= SC_MAX_EXT_APDU_RESP_SIZE) + apdu->le = apdu->resplen; + if (apdu->resplen && !apdu->datalen) + apdu->cse = SC_APDU_CASE_2; + if (!apdu->resplen && apdu->datalen) + apdu->cse = SC_APDU_CASE_3; + if (apdu->resplen && apdu->datalen) + apdu->cse = SC_APDU_CASE_4; + } else { + /* short length */ + if (apdu->datalen <= SC_MAX_APDU_DATA_SIZE) + apdu->lc = apdu->datalen; + if (apdu->resplen <= SC_MAX_APDU_RESP_SIZE) + apdu->le = apdu->resplen; + if (!apdu->resplen && !apdu->datalen) + apdu->cse = SC_APDU_CASE_1; + if (apdu->resplen && !apdu->datalen) + apdu->cse = SC_APDU_CASE_2_SHORT; + if (!apdu->resplen && apdu->datalen) + apdu->cse = SC_APDU_CASE_3_SHORT; + if (apdu->resplen && apdu->datalen) + apdu->cse = SC_APDU_CASE_4_SHORT; + } +} + +void sc_format_apdu_ex(struct sc_apdu *apdu, + u8 cla, u8 ins, u8 p1, u8 p2, + const u8 *data, size_t datalen, + u8 *resp, size_t resplen) +{ + if (!apdu) { + return; + } + + memset(apdu, 0, sizeof(*apdu)); + apdu->cla = cla; + apdu->ins = ins; + apdu->p1 = p1; + apdu->p2 = p2; + apdu->resp = resp; + apdu->resplen = resplen; + apdu->data = data; + apdu->datalen = datalen; + sc_format_apdu_cse_lc_le(apdu); +} + static sc_card_t * sc_card_new(sc_context_t *ctx) { sc_card_t *card; @@ -101,19 +157,16 @@ static void sc_card_free(sc_card_t *card) sc_free_apps(card); sc_free_ef_atr(card); - sc_file_free(card->ef_dir); - free(card->ops); if (card->algorithms != NULL) { int i; for (i=0; ialgorithm_count; i++) { struct sc_algorithm_info *info = (card->algorithms + i); - if (info->algorithm == SC_ALGORITHM_EC) { - struct sc_ec_parameters ep = info->u._ec.params; - - free(ep.named_curve); - free(ep.der.value); + if (info->algorithm == SC_ALGORITHM_EC || + info->algorithm == SC_ALGORITHM_EDDSA || + info->algorithm == SC_ALGORITHM_XEDDSA) { + sc_clear_ec_params(&info->u._ec.params); } } free(card->algorithms); @@ -122,9 +175,6 @@ static void sc_card_free(sc_card_t *card) card->algorithm_count = 0; } - sc_file_free(card->cache.current_ef); - sc_file_free(card->cache.current_df); - if (card->mutex != NULL) { int r = sc_mutex_destroy(card->ctx, card->mutex); if (r != SC_SUCCESS) @@ -142,7 +192,7 @@ size_t sc_get_max_recv_size(const sc_card_t *card) } max_recv_size = card->max_recv_size; - /* initialize max_recv_size to a meaningfull value */ + /* initialize max_recv_size to a meaningful value */ if (card->caps & SC_CARD_CAP_APDU_EXT) { if (!max_recv_size) max_recv_size = 65536; @@ -169,7 +219,7 @@ size_t sc_get_max_send_size(const sc_card_t *card) max_send_size = card->max_send_size; - /* initialize max_send_size to a meaningfull value */ + /* initialize max_send_size to a meaningful value */ if (card->caps & SC_CARD_CAP_APDU_EXT && card->reader->active_protocol != SC_PROTO_T0) { if (!max_send_size) @@ -265,8 +315,22 @@ int sc_connect_card(sc_reader_t *reader, sc_card_t **card_out) } } else { + sc_card_t uninitialized = *card; sc_log(ctx, "matching built-in ATRs"); for (i = 0; ctx->card_drivers[i] != NULL; i++) { + /* FIXME If we had a clean API description, we'd probably get a + * cleaner implementation of the driver's match_card and init, + * which should normally *not* modify the card object if + * unsuccessful. However, after years of relentless hacking, reality + * is different: The card object is changed in virtually every card + * driver so in order to prevent unwanted interaction, we reset the + * card object here and hope that the card driver at least doesn't + * allocate any internal resources that need to be freed. If we + * had more time, we should refactor the existing code to not + * modify sc_card_t until complete success (possibly by combining + * `match_card()` and `init()`) */ + *card = uninitialized; + struct sc_card_driver *drv = ctx->card_drivers[i]; const struct sc_card_operations *ops = drv->ops; @@ -307,7 +371,7 @@ int sc_connect_card(sc_reader_t *reader, sc_card_t **card_out) if (card->name == NULL) card->name = card->driver->name; - /* initialize max_send_size/max_recv_size to a meaningfull value */ + /* initialize max_send_size/max_recv_size to a meaningful value */ card->max_recv_size = sc_get_max_recv_size(card); card->max_send_size = sc_get_max_send_size(card); @@ -345,8 +409,6 @@ int sc_disconnect_card(sc_card_t *card) ctx = card->ctx; LOG_FUNC_CALLED(ctx); - if (card->lock_count != 0) - return SC_ERROR_NOT_ALLOWED; if (card->ops->finish) { int r = card->ops->finish(card); if (r) @@ -382,9 +444,6 @@ int sc_reset(sc_card_t *card, int do_cold_reset) return r; r = card->reader->ops->reset(card->reader, do_cold_reset); - /* invalidate cache */ - memset(&card->cache, 0, sizeof(card->cache)); - card->cache.valid = 0; r2 = sc_mutex_unlock(card->ctx, card->mutex); if (r2 != SC_SUCCESS) { @@ -413,9 +472,6 @@ int sc_lock(sc_card_t *card) if (card->reader->ops->lock != NULL) { r = card->reader->ops->lock(card->reader); while (r == SC_ERROR_CARD_RESET || r == SC_ERROR_READER_REATTACHED) { - /* invalidate cache */ - memset(&card->cache, 0, sizeof(card->cache)); - card->cache.valid = 0; if (was_reset++ > 4) /* TODO retry a few times */ break; r = card->reader->ops->lock(card->reader); @@ -423,12 +479,16 @@ int sc_lock(sc_card_t *card) if (r == 0) reader_lock_obtained = 1; } - if (r == 0) - card->cache.valid = 1; } if (r == 0) card->lock_count++; + r2 = sc_mutex_unlock(card->ctx, card->mutex); + if (r2 != SC_SUCCESS) { + sc_log(card->ctx, "unable to release card->mutex lock"); + r = r != SC_SUCCESS ? r : r2; + } + if (r == 0 && was_reset > 0) { #ifdef ENABLE_SM if (card->sm_ctx.ops.open) @@ -436,15 +496,11 @@ int sc_lock(sc_card_t *card) #endif } - r2 = sc_mutex_unlock(card->ctx, card->mutex); - if (r2 != SC_SUCCESS) { - sc_log(card->ctx, "unable to release card->mutex lock"); - r = r != SC_SUCCESS ? r : r2; - } - /* give card driver a chance to do something when reader lock first obtained */ - if (r == 0 && reader_lock_obtained == 1 && card->ops->card_reader_lock_obtained) - r = card->ops->card_reader_lock_obtained(card, was_reset); + if (r == 0 && reader_lock_obtained == 1 && card->ops->card_reader_lock_obtained) { + if (SC_SUCCESS != card->ops->card_reader_lock_obtained(card, was_reset)) + sc_log(card->ctx, "card_reader_lock_obtained failed"); + } LOG_FUNC_RETURN(card->ctx, r); } @@ -460,18 +516,12 @@ int sc_unlock(sc_card_t *card) r = sc_mutex_lock(card->ctx, card->mutex); if (r != SC_SUCCESS) - return r; + LOG_FUNC_RETURN(card->ctx, r); if (card->lock_count < 1) { - return SC_ERROR_INVALID_ARGUMENTS; + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } if (--card->lock_count == 0) { -#ifdef INVALIDATE_CARD_CACHE_IN_UNLOCK - /* invalidate cache */ - memset(&card->cache, 0, sizeof(card->cache)); - card->cache.valid = 0; - sc_log(card->ctx, "cache invalidated"); -#endif /* release reader lock */ if (card->reader->ops->unlock != NULL) r = card->reader->ops->unlock(card->reader); @@ -553,9 +603,10 @@ int sc_delete_file(sc_card_t *card, const sc_path_t *path) } int sc_read_binary(sc_card_t *card, unsigned int idx, - unsigned char *buf, size_t count, unsigned long flags) + unsigned char *buf, size_t count, unsigned long *flags) { size_t max_le = sc_get_max_recv_size(card); + size_t todo = count; int r; if (card == NULL || card->ops == NULL || buf == NULL) { @@ -564,7 +615,7 @@ int sc_read_binary(sc_card_t *card, unsigned int idx, sc_log(card->ctx, "called; %"SC_FORMAT_LEN_SIZE_T"u bytes at index %d", count, idx); if (count == 0) - return 0; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); #ifdef ENABLE_SM if (card->sm_ctx.ops.read_binary) { @@ -573,42 +624,51 @@ int sc_read_binary(sc_card_t *card, unsigned int idx, LOG_FUNC_RETURN(card->ctx, r); } #endif + if (card->ops->read_binary == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); - if (count > max_le) { - int bytes_read = 0; - unsigned char *p = buf; - - r = sc_lock(card); - LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); - while (count > 0) { - size_t n = count > max_le ? max_le : count; - r = sc_read_binary(card, idx, p, n, flags); - if (r < 0) { - sc_unlock(card); - LOG_TEST_RET(card->ctx, r, "sc_read_binary() failed"); - } - p += r; - idx += r; - bytes_read += r; - count -= r; - if (r == 0) { - sc_unlock(card); - LOG_FUNC_RETURN(card->ctx, bytes_read); - } + /* lock the card now to avoid deselection of the file */ + r = sc_lock(card); + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); + + while (todo > 0) { + size_t chunk = MIN(todo, max_le); + + r = card->ops->read_binary(card, idx, buf, chunk, flags); + if (r == 0 || r == SC_ERROR_FILE_END_REACHED) + break; + if (r < 0 && todo != count) { + /* the last command failed, but previous ones succeeded. + * Let's just return what we've successfully read. */ + sc_log(card->ctx, "Subsequent read failed with %d, returning what was read successfully.", r); + break; + } + if (r < 0) { + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, r); + } + if ((idx > SIZE_MAX - (size_t) r) || (size_t) r > todo) { + /* `idx + r` or `todo - r` would overflow */ + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OFFSET_TOO_LARGE); } - sc_unlock(card); - LOG_FUNC_RETURN(card->ctx, bytes_read); + + todo -= (size_t) r; + buf += (size_t) r; + idx += (size_t) r; } - r = card->ops->read_binary(card, idx, buf, count, flags); - LOG_FUNC_RETURN(card->ctx, r); + + sc_unlock(card); + + LOG_FUNC_RETURN(card->ctx, (int)(count - todo)); } int sc_write_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { size_t max_lc = sc_get_max_send_size(card); + size_t todo = count; int r; if (card == NULL || card->ops == NULL || buf == NULL) { @@ -617,44 +677,46 @@ int sc_write_binary(sc_card_t *card, unsigned int idx, sc_log(card->ctx, "called; %"SC_FORMAT_LEN_SIZE_T"u bytes at index %d", count, idx); if (count == 0) - LOG_FUNC_RETURN(card->ctx, 0); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + if (card->ops->write_binary == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); - if (count > max_lc) { - int bytes_written = 0; - const u8 *p = buf; - - r = sc_lock(card); - LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); - while (count > 0) { - size_t n = count > max_lc? max_lc : count; - r = sc_write_binary(card, idx, p, n, flags); - if (r < 0) { - sc_unlock(card); - LOG_TEST_RET(card->ctx, r, "sc_write_binary() failed"); - } - p += r; - idx += r; - bytes_written += r; - count -= r; - if (r == 0) { - sc_unlock(card); - LOG_FUNC_RETURN(card->ctx, bytes_written); - } + /* lock the card now to avoid deselection of the file */ + r = sc_lock(card); + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); + + while (todo > 0) { + size_t chunk = MIN(todo, max_lc); + + r = card->ops->write_binary(card, idx, buf, chunk, flags); + if (r == 0 || r == SC_ERROR_FILE_END_REACHED) + break; + if (r < 0) { + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, r); } - sc_unlock(card); - LOG_FUNC_RETURN(card->ctx, bytes_written); + if ((idx > SIZE_MAX - (size_t) r) || (size_t) r > todo) { + /* `idx + r` or `todo - r` would overflow */ + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OFFSET_TOO_LARGE); + } + + todo -= (size_t) r; + buf += (size_t) r; + idx += (size_t) r; } - r = card->ops->write_binary(card, idx, buf, count, flags); - LOG_FUNC_RETURN(card->ctx, r); + sc_unlock(card); + + LOG_FUNC_RETURN(card->ctx, (int)(count - todo)); } int sc_update_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { size_t max_lc = sc_get_max_send_size(card); + size_t todo = count; int r; if (card == NULL || card->ops == NULL || buf == NULL) { @@ -663,7 +725,7 @@ int sc_update_binary(sc_card_t *card, unsigned int idx, sc_log(card->ctx, "called; %"SC_FORMAT_LEN_SIZE_T"u bytes at index %d", count, idx); if (count == 0) - return 0; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); #ifdef ENABLE_SM if (card->sm_ctx.ops.update_binary) { @@ -676,53 +738,79 @@ int sc_update_binary(sc_card_t *card, unsigned int idx, if (card->ops->update_binary == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); - if (count > max_lc) { - int bytes_written = 0; - const u8 *p = buf; - - r = sc_lock(card); - LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); - while (count > 0) { - size_t n = count > max_lc? max_lc : count; - r = sc_update_binary(card, idx, p, n, flags); - if (r < 0) { - sc_unlock(card); - LOG_TEST_RET(card->ctx, r, "sc_update_binary() failed"); - } - p += r; - idx += r; - bytes_written += r; - count -= r; - if (r == 0) { - sc_unlock(card); - LOG_FUNC_RETURN(card->ctx, bytes_written); - } + /* lock the card now to avoid deselection of the file */ + r = sc_lock(card); + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); + + while (todo > 0) { + size_t chunk = MIN(todo, max_lc); + + r = card->ops->update_binary(card, idx, buf, chunk, flags); + if (r == 0 || r == SC_ERROR_FILE_END_REACHED) + break; + if (r < 0) { + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, r); + } + if ((idx > SIZE_MAX - (size_t) r) || (size_t) r > todo) { + /* `idx + r` or `todo - r` would overflow */ + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OFFSET_TOO_LARGE); } - sc_unlock(card); - LOG_FUNC_RETURN(card->ctx, bytes_written); + + todo -= (size_t) r; + buf += (size_t) r; + idx += (size_t) r; } - r = card->ops->update_binary(card, idx, buf, count, flags); - LOG_FUNC_RETURN(card->ctx, r); + sc_unlock(card); + + LOG_FUNC_RETURN(card->ctx, (int)(count - todo)); } -int sc_erase_binary(struct sc_card *card, unsigned int offs, size_t count, unsigned long flags) +int sc_erase_binary(struct sc_card *card, unsigned int idx, size_t count, unsigned long flags) { int r; + size_t todo = count; if (card == NULL || card->ops == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } sc_log(card->ctx, "called; erase %"SC_FORMAT_LEN_SIZE_T"u bytes from offset %d", - count, offs); + count, idx); + if (count == 0) + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); if (card->ops->erase_binary == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); - r = card->ops->erase_binary(card, offs, count, flags); - LOG_FUNC_RETURN(card->ctx, r); + /* lock the card now to avoid deselection of the file */ + r = sc_lock(card); + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); + + while (todo > 0) { + r = card->ops->erase_binary(card, idx, todo, flags); + if (r == 0 || r == SC_ERROR_FILE_END_REACHED) + break; + if (r < 0) { + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, r); + } + if ((idx > SIZE_MAX - (size_t) r) || (size_t) r > todo) { + /* `idx + r` or `todo - r` would overflow */ + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OFFSET_TOO_LARGE); + } + + todo -= (size_t) r; + idx += (size_t) r; + } + + sc_unlock(card); + + LOG_FUNC_RETURN(card->ctx, (int)(count - todo)); } @@ -813,33 +901,91 @@ int sc_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { int r; - if (card == NULL) { + if (len == 0) + return SC_SUCCESS; + + if (card == NULL || rnd == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } + LOG_FUNC_CALLED(card->ctx); - if (card->ops->get_challenge == NULL) + if (card->ops == NULL || card->ops->get_challenge == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); - r = card->ops->get_challenge(card, rnd, len); - LOG_FUNC_RETURN(card->ctx, r); + r = sc_lock(card); + if (r != SC_SUCCESS) + LOG_FUNC_RETURN(card->ctx, r); + + while (len > 0) { + r = card->ops->get_challenge(card, rnd, len); + if (r == 0) + r = SC_ERROR_INVALID_DATA; + if (r < 0) { + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, r); + } + + rnd += (size_t) r; + len -= (size_t) r; + } + + sc_unlock(card); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } -int sc_read_record(sc_card_t *card, unsigned int rec_nr, u8 *buf, - size_t count, unsigned long flags) +int sc_read_record(sc_card_t *card, unsigned int rec_nr, unsigned int idx, + u8 *buf ,size_t count, unsigned long flags) { + size_t max_le = sc_get_max_recv_size(card); + size_t todo = count; int r; - if (card == NULL) { + if (card == NULL || card->ops == NULL || buf == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } LOG_FUNC_CALLED(card->ctx); + if (count == 0) + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); if (card->ops->read_record == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); - r = card->ops->read_record(card, rec_nr, buf, count, flags); - LOG_FUNC_RETURN(card->ctx, r); + /* lock the card now to avoid deselection of the file */ + r = sc_lock(card); + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); + + while (todo > 0) { + size_t chunk = MIN(todo, max_le); + + r = card->ops->read_record(card, rec_nr, idx, buf, chunk, flags); + if (r == 0 || r == SC_ERROR_FILE_END_REACHED) + break; + if (r < 0 && todo != count) { + /* the last command failed, but previous ones succeeded. + * Let's just return what we've successfully read. */ + sc_log(card->ctx, "Subsequent read failed with %d, returning what was read successfully.", r); + break; + } + if (r < 0) { + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, r); + } + if ((idx > SIZE_MAX - (size_t) r) || (size_t) r > todo) { + /* `idx + r` or `todo - r` would overflow */ + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OFFSET_TOO_LARGE); + } + + todo -= (size_t) r; + buf += (size_t) r; + idx += (size_t) r; + } + + sc_unlock(card); + + LOG_FUNC_RETURN(card->ctx, (int)(count - todo)); } int sc_write_record(sc_card_t *card, unsigned int rec_nr, const u8 * buf, @@ -854,7 +1000,11 @@ int sc_write_record(sc_card_t *card, unsigned int rec_nr, const u8 * buf, if (card->ops->write_record == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + r = card->ops->write_record(card, rec_nr, buf, count, flags); + if (r == SC_SUCCESS) { + r = (int)count; + } LOG_FUNC_RETURN(card->ctx, r); } @@ -871,26 +1021,60 @@ int sc_append_record(sc_card_t *card, const u8 * buf, size_t count, if (card->ops->append_record == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + r = card->ops->append_record(card, buf, count, flags); + if (r == SC_SUCCESS) { + r = (int)count; + } LOG_FUNC_RETURN(card->ctx, r); } -int sc_update_record(sc_card_t *card, unsigned int rec_nr, const u8 * buf, - size_t count, unsigned long flags) +int sc_update_record(sc_card_t *card, unsigned int rec_nr, unsigned int idx, + const u8 * buf, size_t count, unsigned long flags) { + size_t max_lc = sc_get_max_send_size(card); + size_t todo = count; int r; - if (card == NULL) { + if (card == NULL || card->ops == NULL || buf == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } LOG_FUNC_CALLED(card->ctx); + if (count == 0) + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); if (card->ops->update_record == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); - r = card->ops->update_record(card, rec_nr, buf, count, flags); - LOG_FUNC_RETURN(card->ctx, r); + /* lock the card now to avoid deselection of the file */ + r = sc_lock(card); + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); + + while (todo > 0) { + size_t chunk = MIN(todo, max_lc); + + r = card->ops->update_record(card, rec_nr, idx, buf, chunk, flags); + if (r == 0 || r == SC_ERROR_FILE_END_REACHED) + break; + if (r < 0) { + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, r); + } + if ((idx > SIZE_MAX - (size_t) r) || (size_t) r > todo) { + /* `idx + r` or `todo - r` would overflow */ + sc_unlock(card); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OFFSET_TOO_LARGE); + } + + todo -= (size_t) r; + buf += (size_t) r; + idx += (size_t) r; + } + + sc_unlock(card); + + LOG_FUNC_RETURN(card->ctx, (int)(count - todo)); } int sc_delete_record(sc_card_t *card, unsigned int rec_nr) @@ -904,6 +1088,7 @@ int sc_delete_record(sc_card_t *card, unsigned int rec_nr) if (card->ops->delete_record == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + r = card->ops->delete_record(card, rec_nr); LOG_FUNC_RETURN(card->ctx, r); @@ -917,7 +1102,7 @@ sc_card_ctl(sc_card_t *card, unsigned long cmd, void *args) if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } - LOG_FUNC_CALLED(card->ctx); + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "called with cmd=%lu\n", cmd); if (card->ops->card_ctl != NULL) r = card->ops->card_ctl(card, cmd, args); @@ -939,10 +1124,6 @@ int _sc_card_add_algorithm(sc_card_t *card, const sc_algorithm_info_t *info) } p = (sc_algorithm_info_t *) realloc(card->algorithms, (card->algorithm_count + 1) * sizeof(*info)); if (!p) { - if (card->algorithms) - free(card->algorithms); - card->algorithms = NULL; - card->algorithm_count = 0; return SC_ERROR_OUT_OF_MEMORY; } card->algorithms = p; @@ -965,55 +1146,104 @@ int _sc_card_add_symmetric_alg(sc_card_t *card, unsigned int algorithm, return _sc_card_add_algorithm(card, &info); } -int _sc_card_add_ec_alg(sc_card_t *card, unsigned int key_length, +static int +_sc_card_add_ec_alg_int(sc_card_t *card, size_t key_length, unsigned long flags, unsigned long ext_flags, - struct sc_object_id *curve_oid) + struct sc_object_id *curve_oid, + int algorithm) { sc_algorithm_info_t info; + int r; memset(&info, 0, sizeof(info)); sc_init_oid(&info.u._ec.params.id); - info.algorithm = SC_ALGORITHM_EC; + info.algorithm = algorithm; info.key_length = key_length; info.flags = flags; info.u._ec.ext_flags = ext_flags; - if (curve_oid) + if (curve_oid) { info.u._ec.params.id = *curve_oid; + r = sc_encode_oid(card->ctx, &info.u._ec.params.id, &info.u._ec.params.der.value, &info.u._ec.params.der.len); + LOG_TEST_GOTO_ERR(card->ctx, r, "sc_encode_oid failed"); + r = sc_pkcs15_fix_ec_parameters(card->ctx, &info.u._ec.params); + LOG_TEST_GOTO_ERR(card->ctx, r, "sc_pkcs15_fix_ec_parameters failed"); + } - return _sc_card_add_algorithm(card, &info); + r = _sc_card_add_algorithm(card, &info); + return r; +err: + sc_clear_ec_params(&info.u._ec.params); + return r; } -static sc_algorithm_info_t * sc_card_find_alg(sc_card_t *card, - unsigned int algorithm, unsigned int key_length, void *param) +int _sc_card_add_ec_alg(sc_card_t *card, size_t key_length, + unsigned long flags, unsigned long ext_flags, + struct sc_object_id *curve_oid) +{ + return _sc_card_add_ec_alg_int(card, key_length, flags, ext_flags, + curve_oid, SC_ALGORITHM_EC); +} + +int _sc_card_add_eddsa_alg(sc_card_t *card, size_t key_length, + unsigned long flags, unsigned long ext_flags, + struct sc_object_id *curve_oid) +{ + /* For simplicity, share the ec union with the curve information */ + return _sc_card_add_ec_alg_int(card, key_length, flags, ext_flags, + curve_oid, SC_ALGORITHM_EDDSA); +} + +int _sc_card_add_xeddsa_alg(sc_card_t *card, size_t key_length, + unsigned long flags, unsigned long ext_flags, + struct sc_object_id *curve_oid) +{ + /* For simplicity, share the ec union with the curve information */ + return _sc_card_add_ec_alg_int(card, key_length, flags, ext_flags, + curve_oid, SC_ALGORITHM_XEDDSA); +} + +sc_algorithm_info_t *sc_card_find_alg(sc_card_t *card, + unsigned int algorithm, size_t key_length, void *param) { int i; for (i = 0; i < card->algorithm_count; i++) { sc_algorithm_info_t *info = &card->algorithms[i]; - if (info->algorithm != algorithm) - continue; - if (info->key_length != key_length) + if (param && (info->algorithm == SC_ALGORITHM_EC || + info->algorithm == SC_ALGORITHM_EDDSA || + info->algorithm == SC_ALGORITHM_XEDDSA)) { + if (sc_compare_oid((struct sc_object_id *)param, &info->u._ec.params.id)) + return info; + } else if (info->algorithm != algorithm) { continue; - if (param) { - if (info->algorithm == SC_ALGORITHM_EC) - if(!sc_compare_oid((struct sc_object_id *)param, &info->u._ec.params.id)) - continue; - } - return info; + } else if (info->key_length == key_length) + return info; } return NULL; } -sc_algorithm_info_t * sc_card_find_ec_alg(sc_card_t *card, - unsigned int key_length, struct sc_object_id *curve_name) +sc_algorithm_info_t *sc_card_find_ec_alg(sc_card_t *card, + size_t key_length, struct sc_object_id *curve_name) { return sc_card_find_alg(card, SC_ALGORITHM_EC, key_length, curve_name); } -int _sc_card_add_rsa_alg(sc_card_t *card, unsigned int key_length, +sc_algorithm_info_t *sc_card_find_eddsa_alg(sc_card_t *card, + size_t key_length, struct sc_object_id *curve_name) +{ + return sc_card_find_alg(card, SC_ALGORITHM_EDDSA, key_length, curve_name); +} + +sc_algorithm_info_t *sc_card_find_xeddsa_alg(sc_card_t *card, + size_t key_length, struct sc_object_id *curve_name) +{ + return sc_card_find_alg(card, SC_ALGORITHM_XEDDSA, key_length, curve_name); +} + +int _sc_card_add_rsa_alg(sc_card_t *card, size_t key_length, unsigned long flags, unsigned long exponent) { sc_algorithm_info_t info; @@ -1022,24 +1252,29 @@ int _sc_card_add_rsa_alg(sc_card_t *card, unsigned int key_length, info.algorithm = SC_ALGORITHM_RSA; info.key_length = key_length; info.flags = flags; + /* disable particular PKCS1 v1.5 padding type if also RAW is supported on card */ + if ((info.flags & (SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_RAW)) == (SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_RAW)) { + if (card->ctx->disable_hw_pkcs1_padding & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01) + info.flags &= ~SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01; + if (card->ctx->disable_hw_pkcs1_padding & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02) + info.flags &= ~SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02; + } info.u._rsa.exponent = exponent; return _sc_card_add_algorithm(card, &info); } -sc_algorithm_info_t * sc_card_find_rsa_alg(sc_card_t *card, - unsigned int key_length) +sc_algorithm_info_t *sc_card_find_rsa_alg(sc_card_t *card, size_t key_length) { return sc_card_find_alg(card, SC_ALGORITHM_RSA, key_length, NULL); } -sc_algorithm_info_t * sc_card_find_gostr3410_alg(sc_card_t *card, - unsigned int key_length) +sc_algorithm_info_t *sc_card_find_gostr3410_alg(sc_card_t *card, size_t key_length) { return sc_card_find_alg(card, SC_ALGORITHM_GOSTR3410, key_length, NULL); } -static int match_atr_table(sc_context_t *ctx, struct sc_atr_table *table, struct sc_atr *atr) +static int match_atr_table(sc_context_t *ctx, const struct sc_atr_table *table, struct sc_atr *atr) { u8 *card_atr_bin; size_t card_atr_bin_len; @@ -1054,7 +1289,7 @@ static int match_atr_table(sc_context_t *ctx, struct sc_atr_table *table, struct sc_bin_to_hex(card_atr_bin, card_atr_bin_len, card_atr_hex, sizeof(card_atr_hex), ':'); card_atr_hex_len = strlen(card_atr_hex); - sc_log(ctx, "ATR : %s", card_atr_hex); + sc_debug(ctx, SC_LOG_DEBUG_MATCH, "ATR : %s", card_atr_hex); for (i = 0; table[i].atr != NULL; i++) { const char *tatr = table[i].atr; @@ -1065,14 +1300,14 @@ static int match_atr_table(sc_context_t *ctx, struct sc_atr_table *table, struct size_t fix_hex_len = card_atr_hex_len; size_t fix_bin_len = card_atr_bin_len; - sc_log(ctx, "ATR try : %s", tatr); + sc_debug(ctx, SC_LOG_DEBUG_MATCH, "ATR try : %s", tatr); if (tatr_len != fix_hex_len) { - sc_log(ctx, "ignored - wrong length"); + sc_debug(ctx, SC_LOG_DEBUG_MATCH, "ignored - wrong length"); continue; } if (matr != NULL) { - sc_log(ctx, "ATR mask: %s", matr); + sc_debug(ctx, SC_LOG_DEBUG_MATCH, "ATR mask: %s", matr); matr_len = strlen(matr); if (tatr_len != matr_len) @@ -1082,7 +1317,7 @@ static int match_atr_table(sc_context_t *ctx, struct sc_atr_table *table, struct mbin_len = sizeof(mbin); sc_hex_to_bin(matr, mbin, &mbin_len); if (mbin_len != fix_bin_len) { - sc_log(ctx, "length of atr and atr mask do not match - ignored: %s - %s", tatr, matr); + sc_debug(ctx, SC_LOG_DEBUG_MATCH, "length of atr and atr mask do not match - ignored: %s - %s", tatr, matr); continue; } for (s = 0; s < tbin_len; s++) { @@ -1102,7 +1337,7 @@ static int match_atr_table(sc_context_t *ctx, struct sc_atr_table *table, struct return -1; } -int _sc_match_atr(sc_card_t *card, struct sc_atr_table *table, int *type_out) +int _sc_match_atr(sc_card_t *card, const struct sc_atr_table *table, int *type_out) { int res; @@ -1233,28 +1468,15 @@ scconf_block *sc_get_conf_block(sc_context_t *ctx, const char *name1, const char return conf_block; } -void sc_print_cache(struct sc_card *card) { - struct sc_context *ctx = NULL; - - if (card == NULL) - return; - ctx = card->ctx; - - if (!card->cache.valid || (!card->cache.current_ef && !card->cache.current_df)) { - sc_log(ctx, "card cache invalid"); - return; +void +sc_clear_ec_params(struct sc_ec_parameters *ecp) +{ + if (ecp) { + free(ecp->named_curve); + free(ecp->der.value); + memset(ecp, 0, sizeof(struct sc_ec_parameters)); } - - if (card->cache.current_ef) - sc_log(ctx, "current_ef(type=%i) %s", card->cache.current_ef->path.type, - sc_print_path(&card->cache.current_ef->path)); - - if (card->cache.current_df) - sc_log(ctx, - "current_df(type=%i, aid_len=%"SC_FORMAT_LEN_SIZE_T"u) %s", - card->cache.current_df->path.type, - card->cache.current_df->path.aid.len, - sc_print_path(&card->cache.current_df->path)); + return; } int sc_copy_ec_params(struct sc_ec_parameters *dst, struct sc_ec_parameters *src) @@ -1271,13 +1493,16 @@ int sc_copy_ec_params(struct sc_ec_parameters *dst, struct sc_ec_parameters *src dst->id = src->id; if (src->der.value && src->der.len) { dst->der.value = malloc(src->der.len); - if (!dst->der.value) + if (!dst->der.value) { + free(dst->named_curve); return SC_ERROR_OUT_OF_MEMORY; + } memcpy(dst->der.value, src->der.value, src->der.len); dst->der.len = src->der.len; } - src->type = dst->type; - src->field_length = dst->field_length; + dst->type = src->type; + dst->field_length = src->field_length; + dst->key_type = src->key_type; return SC_SUCCESS; } @@ -1309,8 +1534,6 @@ sc_card_sm_load(struct sc_card *card, const char *module_path, const char *in_mo int rv = SC_ERROR_INTERNAL; char *module = NULL; #ifdef _WIN32 - char temp_path[PATH_MAX]; - size_t temp_len; const char path_delim = '\\'; #else const char path_delim = '/'; @@ -1320,22 +1543,13 @@ sc_card_sm_load(struct sc_card *card, const char *module_path, const char *in_mo return SC_ERROR_INVALID_ARGUMENTS; } ctx = card->ctx; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); if (!in_module) return sc_card_sm_unload(card); -#ifdef _WIN32 - if (!module_path) { - temp_len = PATH_MAX; - rv = sc_ctx_win32_get_config_value(NULL, "SmDir", "Software\\OpenSC Project\\OpenSC", - temp_path, &temp_len); - if (rv == SC_SUCCESS) - module_path = temp_path; - } -#endif - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "SM module '%s' located in '%s'", in_module, module_path); - if (module_path) { - int sz = strlen(in_module) + strlen(module_path) + 3; + sc_log(ctx, "SM module '%s' located in '%s'", in_module, module_path); + if (module_path && strlen(module_path) > 0) { + size_t sz = strlen(in_module) + strlen(module_path) + 3; module = malloc(sz); if (module) snprintf(module, sz, "%s%c%s", module_path, path_delim, in_module); @@ -1410,9 +1624,14 @@ sc_card_sm_check(struct sc_card *card) struct sc_context *ctx = card->ctx; scconf_block *atrblock = NULL, *sm_conf_block = NULL; int rv, ii; +#ifdef _WIN32 + char temp_path[PATH_MAX]; + size_t temp_len = PATH_MAX - 1; + char expanded_val[PATH_MAX]; + DWORD expanded_len = PATH_MAX; +#endif - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - sc_log(ctx, "card->sm_ctx.ops.open %p", card->sm_ctx.ops.open); + LOG_FUNC_CALLED(ctx); /* get the name of card specific SM configuration section */ atrblock = _sc_match_atr_block(ctx, card->driver, &card->atr); @@ -1440,8 +1659,20 @@ sc_card_sm_check(struct sc_card *card) LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_CONFIGURATION, "SM configuration block not preset"); /* check if an external SM module has to be used */ - module_path = scconf_get_str(sm_conf_block, "module_path", NULL); - module_name = scconf_get_str(sm_conf_block, "module_name", NULL); +#ifdef _WIN32 + rv = sc_ctx_win32_get_config_value(NULL, "SmDir", "Software\\" OPENSC_VS_FF_COMPANY_NAME "\\OpenSC" OPENSC_ARCH_SUFFIX, + temp_path, &temp_len); + if (rv == SC_SUCCESS) { + temp_path[temp_len] = '\0'; + module_path = temp_path; + } + expanded_len = ExpandEnvironmentStringsA(module_path, expanded_val, expanded_len); + if (0 < expanded_len && expanded_len < sizeof expanded_val) + module_path = expanded_val; +#else + module_path = scconf_get_str(sm_conf_block, "module_path", DEFAULT_SM_MODULE_PATH); +#endif + module_name = scconf_get_str(sm_conf_block, "module_name", DEFAULT_SM_MODULE); sc_log(ctx, "SM module '%s' in '%s'", module_name, module_path); if (!module_name) LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_CONFIGURATION, "Invalid SM configuration: module not defined"); @@ -1453,18 +1684,15 @@ sc_card_sm_check(struct sc_card *card) strlcpy(card->sm_ctx.config_section, sm, sizeof(card->sm_ctx.config_section)); /* allocate resources for the external SM module */ - sc_log(ctx, "'module_init' handler %p", card->sm_ctx.module.ops.module_init); if (card->sm_ctx.module.ops.module_init) { module_data = scconf_get_str(sm_conf_block, "module_data", NULL); - sc_log(ctx, "module_data '%s'", module_data); rv = card->sm_ctx.module.ops.module_init(ctx, module_data); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot initialize SM module"); + LOG_TEST_RET(ctx, rv, "Cannot initialize SM module"); } /* initialize SM session in the case of 'APDU TRANSMIT' SM mode */ sm_mode = scconf_get_str(sm_conf_block, "mode", NULL); - sc_log(ctx, "SM mode '%s'; 'open' handler %p", sm_mode, card->sm_ctx.ops.open); if (sm_mode && !strcasecmp("Transmit", sm_mode)) { if (!card->sm_ctx.ops.open || !card->sm_ctx.ops.get_sm_apdu || !card->sm_ctx.ops.free_sm_apdu) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "'Transmit' SM asked but not supported by card driver"); diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index 9a58427b7a..6b898e41cd 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -2,6 +2,7 @@ * cardctl.h: card_ctl command numbers * * Copyright (C) 2003 Olaf Kirch + * Copyright (C) 2018-2019 GSMK - Gesellschaft für Sichere Mobile Kommunikation mbH * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -15,14 +16,15 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_CARDCTL_H #define _OPENSC_CARDCTL_H -#include +#include "libopensc/opensc.h" #include "libopensc/types.h" +#include #ifdef __cplusplus extern "C" { @@ -40,21 +42,12 @@ enum { SC_CARDCTL_LIFECYCLE_GET, SC_CARDCTL_LIFECYCLE_SET, SC_CARDCTL_GET_SERIALNR, + SC_CARDCTL_GET_CHANGE_COUNTER, SC_CARDCTL_GET_SE_INFO, SC_CARDCTL_GET_CHV_REFERENCE_IN_SE, SC_CARDCTL_PKCS11_INIT_TOKEN, SC_CARDCTL_PKCS11_INIT_PIN, - - /* - * GPK specific calls - */ - SC_CARDCTL_GPK_BASE = _CTL_PREFIX('G', 'P', 'K'), - SC_CARDCTL_GPK_VARIANT, - SC_CARDCTL_GPK_LOCK, - SC_CARDCTL_GPK_PKINIT, - SC_CARDCTL_GPK_PKLOAD, - SC_CARDCTL_GPK_IS_LOCKED, - SC_CARDCTL_GPK_GENERATE_KEY, + SC_CARDCTL_GET_MODEL, /* * Cryptoflex specific calls @@ -62,16 +55,10 @@ enum { SC_CARDCTL_CRYPTOFLEX_BASE = _CTL_PREFIX('C', 'F', 'X'), SC_CARDCTL_CRYPTOFLEX_GENERATE_KEY, - /* - * MioCOS specific calls - */ - SC_CARDCTL_MIOCOS_BASE = _CTL_PREFIX('M', 'I', 'O'), - SC_CARDCTL_MIOCOS_CREATE_AC, - /* * TCOS specific calls */ - SC_CARDCTL_TCOS_BASE = _CTL_PREFIX('T','C','S'), + SC_CARDCTL_TCOS_BASE = _CTL_PREFIX('T', 'C', 'S'), SC_CARDCTL_TCOS_SETPERM, /* @@ -83,6 +70,7 @@ enum { SC_CARDCTL_CARDOS_PUT_DATA_OCI, SC_CARDCTL_CARDOS_PUT_DATA_SECI, SC_CARDCTL_CARDOS_GENERATE_KEY, + SC_CARDCTL_CARDOS_PASS_ALGO_FLAGS, /* * Starcos SPK 2.3 specific calls @@ -93,12 +81,6 @@ enum { SC_CARDCTL_STARCOS_WRITE_KEY, SC_CARDCTL_STARCOS_GENERATE_KEY, - /* - * JCOP specific calls - */ - SC_CARDCTL_JCOP_BASE = _CTL_PREFIX('J', 'C', 'P'), - SC_CARDCTL_JCOP_GENERATE_KEY, - /* * Oberthur specific calls */ @@ -116,21 +98,10 @@ enum { SC_CARDCTL_SETCOS_GENERATE_STORE_KEY, SC_CARDCTL_SETCOS_ACTIVATE_FILE, - /* - * Incrypto34 specific calls - */ - SC_CARDCTL_INCRYPTO34_BASE = _CTL_PREFIX('I', '3', '4'), - SC_CARDCTL_INCRYPTO34_PUT_DATA_FCI, - SC_CARDCTL_INCRYPTO34_PUT_DATA_OCI, - SC_CARDCTL_INCRYPTO34_PUT_DATA_SECI, - SC_CARDCTL_INCRYPTO34_GENERATE_KEY, - SC_CARDCTL_INCRYPTO34_CHANGE_KEY_DATA, - SC_CARDCTL_INCRYPTO34_ERASE_FILES, - /* * Muscle specific calls */ - SC_CARDCTL_MUSCLE_BASE = _CTL_PREFIX('M','S','C'), + SC_CARDCTL_MUSCLE_BASE = _CTL_PREFIX('M', 'S', 'C'), SC_CARDCTL_MUSCLE_GENERATE_KEY, SC_CARDCTL_MUSCLE_EXTRACT_KEY, SC_CARDCTL_MUSCLE_IMPORT_KEY, @@ -139,26 +110,26 @@ enum { /* * ASEPCOS specific calls */ - SC_CARDCTL_ASEPCOS_BASE = _CTL_PREFIX('A','S','E'), + SC_CARDCTL_ASEPCOS_BASE = _CTL_PREFIX('A', 'S', 'E'), SC_CARDCTL_ASEPCOS_CHANGE_KEY, SC_CARDCTL_ASEPCOS_AKN2FILEID, SC_CARDCTL_ASEPCOS_SET_SATTR, SC_CARDCTL_ASEPCOS_ACTIVATE_FILE, - /* + /* * ruToken specific calls */ - SC_CARDCTL_RUTOKEN_BASE = _CTL_PREFIX('R', 'T', 'K'), - /* PUT_DATA */ - SC_CARDCTL_RUTOKEN_CREATE_DO, - SC_CARDCTL_RUTOKEN_CHANGE_DO, - SC_CARDCTL_RUTOKEN_GENERATE_KEY_DO, - SC_CARDCTL_RUTOKEN_DELETE_DO, - SC_CARDCTL_RUTOKEN_GET_INFO, - /* NON STANDART */ - SC_CARDCTL_RUTOKEN_GET_DO_INFO, - SC_CARDCTL_RUTOKEN_GOST_ENCIPHER, - SC_CARDCTL_RUTOKEN_GOST_DECIPHER, + SC_CARDCTL_RUTOKEN_BASE = _CTL_PREFIX('R', 'T', 'K'), + /* PUT_DATA */ + SC_CARDCTL_RUTOKEN_CREATE_DO, + SC_CARDCTL_RUTOKEN_CHANGE_DO, + SC_CARDCTL_RUTOKEN_GENERATE_KEY_DO, + SC_CARDCTL_RUTOKEN_DELETE_DO, + SC_CARDCTL_RUTOKEN_GET_INFO, + /* NON STANDARD */ + SC_CARDCTL_RUTOKEN_GET_DO_INFO, + SC_CARDCTL_RUTOKEN_GOST_ENCIPHER, + SC_CARDCTL_RUTOKEN_GOST_DECIPHER, SC_CARDCTL_RUTOKEN_FORMAT_INIT, SC_CARDCTL_RUTOKEN_FORMAT_END, @@ -181,8 +152,8 @@ enum { SC_CARDCTL_RTECP_GENERATE_KEY, /* - * Westcos specific - */ + * Westcos specific + */ SC_CARDCTL_WESTCOS_FREEZE = _CTL_PREFIX('W', 'T', 'C'), SC_CARDCTL_WESTCOS_CREATE_MF, SC_CARDCTL_WESTCOS_COMMIT, @@ -220,15 +191,16 @@ enum { SC_CARDCTL_CAC_INIT_GET_CERT_OBJECTS, SC_CARDCTL_CAC_GET_NEXT_CERT_OBJECT, SC_CARDCTL_CAC_FINAL_GET_CERT_OBJECTS, + SC_CARDCTL_CAC_GET_ACA_PATH, - /* + /* * AuthentIC v3 */ - SC_CARDCTL_AUTHENTIC_BASE = _CTL_PREFIX('A','V','3'), - SC_CARDCTL_AUTHENTIC_SDO_CREATE, - SC_CARDCTL_AUTHENTIC_SDO_DELETE, - SC_CARDCTL_AUTHENTIC_SDO_STORE, - SC_CARDCTL_AUTHENTIC_SDO_GENERATE, + SC_CARDCTL_AUTHENTIC_BASE = _CTL_PREFIX('A', 'V', '3'), + SC_CARDCTL_AUTHENTIC_SDO_CREATE, + SC_CARDCTL_AUTHENTIC_SDO_DELETE, + SC_CARDCTL_AUTHENTIC_SDO_STORE, + SC_CARDCTL_AUTHENTIC_SDO_GENERATE, /* * Coolkey specific calls @@ -241,13 +213,13 @@ enum { SC_CARDCTL_COOLKEY_GET_TOKEN_INFO, SC_CARDCTL_COOLKEY_FIND_OBJECT, - /* + /* * IAS/ECC */ - SC_CARDCTL_IASECC_BASE = _CTL_PREFIX('E','C','C'), + SC_CARDCTL_IASECC_BASE = _CTL_PREFIX('E', 'C', 'C'), SC_CARDCTL_IASECC_GET_FREE_KEY_REFERENCE, - SC_CARDCTL_IASECC_SDO_MAGIC = _CTL_PREFIX('S','D','O') | 'M', - SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA = _CTL_PREFIX('S','D','O') | 'P', + SC_CARDCTL_IASECC_SDO_MAGIC = _CTL_PREFIX('S', 'D', 'O') | 'M', + SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA = _CTL_PREFIX('S', 'D', 'O') | 'P', SC_CARDCTL_IASECC_SDO_PUT_DATA, SC_CARDCTL_IASECC_SDO_KEY_RSA_PUT_DATA, SC_CARDCTL_IASECC_SDO_GET_DATA, @@ -261,6 +233,7 @@ enum { SC_CARDCTL_OPENPGP_BASE = _CTL_PREFIX('P', 'G', 'P'), SC_CARDCTL_OPENPGP_GENERATE_KEY, SC_CARDCTL_OPENPGP_STORE_KEY, + SC_CARDCTL_OPENPGP_SELECT_DATA, /* * SmartCard-HSM @@ -271,25 +244,27 @@ enum { SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, SC_CARDCTL_SC_HSM_WRAP_KEY, SC_CARDCTL_SC_HSM_UNWRAP_KEY, + SC_CARDCTL_SC_HSM_REGISTER_PUBLIC_KEY, + SC_CARDCTL_SC_HSM_PUBLIC_KEY_AUTH_STATUS, /* * DNIe specific calls */ - SC_CARDCTL_DNIE_BASE = _CTL_PREFIX('D', 'N', 'I'), + SC_CARDCTL_DNIE_BASE = _CTL_PREFIX('D', 'N', 'I'), SC_CARDCTL_DNIE_GENERATE_KEY, SC_CARDCTL_DNIE_GET_INFO, /* * isoApplet Java Card Applet */ - SC_CARDCTL_ISOAPPLET_BASE = _CTL_PREFIX('I','S','O'), + SC_CARDCTL_ISOAPPLET_BASE = _CTL_PREFIX('I', 'S', 'O'), SC_CARDCTL_ISOAPPLET_GENERATE_KEY, SC_CARDCTL_ISOAPPLET_IMPORT_KEY, /* * GIDS cards */ - SC_CARDCTL_GIDS_BASE = _CTL_PREFIX('G','I','D'), + SC_CARDCTL_GIDS_BASE = _CTL_PREFIX('G', 'I', 'D'), SC_CARDCTL_GIDS_GET_ALL_CONTAINERS, SC_CARDCTL_GIDS_GET_CONTAINER_DETAIL, SC_CARDCTL_GIDS_SELECT_KEY_REFERENCE, @@ -302,6 +277,17 @@ enum { SC_CARDCTL_GIDS_INITIALIZE, SC_CARDCTL_GIDS_SET_ADMIN_KEY, SC_CARDCTL_GIDS_AUTHENTICATE_ADMIN, + + /* + * IDPrime specific calls + */ + SC_CARDCTL_IDPRIME_BASE = _CTL_PREFIX('I', 'D', 'P'), + SC_CARDCTL_IDPRIME_INIT_GET_OBJECTS, + SC_CARDCTL_IDPRIME_GET_NEXT_OBJECT, + SC_CARDCTL_IDPRIME_FINAL_GET_OBJECTS, + SC_CARDCTL_IDPRIME_GET_TOKEN_NAME, + SC_CARDCTL_IDPRIME_GET_PIN_ID, + }; enum { @@ -340,58 +326,11 @@ typedef struct sc_cardctl_pkcs11_init_pin { } sc_cardctl_pkcs11_init_pin_t; /* - * GPK lock file. - * Parent DF of file must be selected. - */ -struct sc_cardctl_gpk_lock { - struct sc_file * file; - unsigned int operation; -}; - -/* - * GPK initialize private key file. - * Parent DF must be selected. - */ -struct sc_cardctl_gpk_pkinit { - struct sc_file * file; - unsigned int privlen; -}; - -/* - * GPK load private key portion. - */ -struct sc_cardctl_gpk_pkload { - struct sc_file * file; - u8 * data; - unsigned int len; - unsigned int datalen; -}; - -struct sc_cardctl_gpk_genkey { - unsigned int fid; - unsigned int privlen; - unsigned char * pubkey; - unsigned int pubkey_len; -}; - -enum { - SC_CARDCTL_MIOCOS_AC_PIN, - SC_CARDCTL_MIOCOS_AC_CHAL, - SC_CARDCTL_MIOCOS_AC_LOGICAL, - SC_CARDCTL_MIOCOS_AC_SMARTPIN -}; - -/* - * MioCOS AC info + * Generic cardctl - card driver can examine token info */ -struct sc_cardctl_miocos_ac_info { - int type; - int ref; - int max_tries; - int enable_ac; /* only applicable to PINs */ - u8 key_value[8]; - int max_unblock_tries; /* same here */ - u8 unblock_value[8]; /* and here */ +struct sc_cardctl_parsed_token_info { + unsigned int flags; + struct sc_pkcs15_tokeninfo * tokeninfo; }; /* @@ -404,24 +343,17 @@ struct sc_cardctl_cardos_obj_info { struct sc_cardctl_cardos_genkey_info { unsigned int key_id; - unsigned int key_bits; + size_t key_bits; unsigned short fid; }; -/* - * Incrypto34 PIN info - */ -struct sc_cardctl_incrypto34_obj_info { - u8 * data; - size_t len; - unsigned int key_id; - unsigned int key_class; -}; - -struct sc_cardctl_incrypto34_genkey_info { - unsigned int key_id; - unsigned int key_bits; - unsigned short fid; +struct sc_cardctl_cardos_pass_algo_flags { + unsigned int pass; + unsigned long card_flags; /* from card->flags i.e. user set */ + unsigned long used_flags; /* as set by default */ + unsigned long new_flags; /* set in pkcs15-cardos.c */ + unsigned long ec_flags; /* for EC keys */ + unsigned long ext_flags; /* for EC keys */ }; /* @@ -429,7 +361,7 @@ struct sc_cardctl_incrypto34_genkey_info { */ struct sc_cardctl_cryptoflex_genkey_info { unsigned int key_num; - unsigned int key_bits; + size_t key_bits; unsigned long exponent; unsigned char * pubkey; unsigned int pubkey_len; @@ -472,14 +404,6 @@ typedef struct sc_starcos_gen_key_data_st { u8 *modulus; } sc_starcos_gen_key_data; -struct sc_cardctl_jcop_genkey { - unsigned long exponent; - sc_path_t pub_file_ref; - sc_path_t pri_file_ref; - unsigned char * pubkey; - unsigned int pubkey_len; -}; - /* * Oberthur ex_data stuff */ @@ -489,19 +413,19 @@ enum SC_CARDCTL_OBERTHUR_KEY_TYPE { SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC = 0xA1, SC_CARDCTL_OBERTHUR_KEY_RSA_SFM, SC_CARDCTL_OBERTHUR_KEY_RSA_CRT, - SC_CARDCTL_OBERTHUR_KEY_DSA_PUBLIC, - SC_CARDCTL_OBERTHUR_KEY_DSA_PRIVATE + SC_CARDCTL_OBERTHUR_KEY_EC_CRT, + SC_CARDCTL_OBERTHUR_KEY_EC_PUBLIC }; struct sc_cardctl_oberthur_genkey_info { unsigned int id_prv, id_pub; - unsigned int key_bits; + size_t key_bits; unsigned long exponent; unsigned char * pubkey; - unsigned int pubkey_len; + size_t pubkey_len; int method; /* SC_AC_XXX */ - int key_ref; /* key reference */ + int key_ref; /* key reference */ }; struct sc_cardctl_oberthur_updatekey_info { @@ -511,14 +435,14 @@ struct sc_cardctl_oberthur_updatekey_info { unsigned int data_len; unsigned char id[256]; - unsigned int id_len; + size_t id_len; }; struct sc_cardctl_oberthur_createpin_info { unsigned int type; unsigned int ref; const unsigned char *pin; - unsigned int pin_len; + size_t pin_len; unsigned int pin_tries; const unsigned char *puk; unsigned int puk_len; @@ -538,12 +462,12 @@ struct sc_cardctl_setcos_data_obj { struct sc_cardctl_setcos_gen_store_key_info { int op_type; - unsigned int mod_len; /* in bits */ - unsigned int pubexp_len; /* in bits */ + size_t mod_len; /* in bits */ + size_t pubexp_len; /* in bits */ unsigned char *pubexp; - unsigned int primep_len; /* in bits */ + size_t primep_len; /* in bits */ unsigned char *primep; - unsigned int primeq_len; /* in bits */ + size_t primeq_len; /* in bits */ unsigned char *primeq; }; @@ -552,7 +476,7 @@ struct sc_cardctl_setcos_gen_store_key_info { */ typedef struct sc_cardctl_muscle_gen_key_info { int keyType; - int keySize; + size_t keySize; int privateKeyLocation; int publicKeyLocation; } sc_cardctl_muscle_gen_key_info_t; @@ -561,24 +485,24 @@ typedef struct sc_cardctl_muscle_gen_key_info { typedef struct sc_cardctl_muscle_key_info { int keyType; int keyLocation; - int keySize; + size_t keySize; size_t modLength; u8* modValue; size_t expLength; u8* expValue; - int pLength; + size_t pLength; u8* pValue; - int qLength; + size_t qLength; u8* qValue; - int pqLength; + size_t pqLength; u8* pqValue; - int dp1Length; + size_t dp1Length; u8* dp1Value; - int dq1Length; + size_t dq1Length; u8* dq1Value; - int gLength; + size_t gLength; u8* gValue; - int yLength; + size_t yLength; u8* yValue; } sc_cardctl_muscle_key_info_t; @@ -625,7 +549,7 @@ typedef struct { * RuToken types and constants */ -#define SC_RUTOKEN_DO_PART_BODY_LEN 199 +#define SC_RUTOKEN_DO_PART_BODY_LEN 199 #define SC_RUTOKEN_DO_HDR_LEN 32 /* DO Types */ @@ -652,7 +576,7 @@ typedef struct { /* DO ID */ #define SC_RUTOKEN_ID_CURDF_RESID_FLAG 0x80 /* DO placed in current DF */ - + #define SC_RUTOKEN_DEF_ID_GCHV_ADMIN 0x01 /* ID DO ADMIN */ #define SC_RUTOKEN_DEF_ID_GCHV_USER 0x02 /* ID DO USER */ @@ -734,7 +658,7 @@ struct sc_rutoken_decipherinfo { /* * EnterSafe stuff - * + * */ #define SC_ENTERSAFE_MF_DATA 0x01 @@ -769,7 +693,7 @@ typedef struct sc_entersafe_create_data_st { u8 init_key[16]; } df; struct { - u8 file_id[2]; + u8 file_id[2]; u8 size[2]; u8 attr[2]; u8 name; @@ -838,6 +762,7 @@ typedef struct sc_epass2003_gen_key_data_st { int pukey_id; size_t key_length; u8 *modulus; + size_t modulus_len; } sc_epass2003_gen_key_data; @@ -880,7 +805,8 @@ typedef struct sc_rtecp_genkey_data { SC_CARDCTL_MYEID_KEY_RSA = 0x11, SC_CARDCTL_MYEID_KEY_DES = 0x19, SC_CARDCTL_MYEID_KEY_EC = 0x22, - SC_CARDCTL_MYEID_KEY_AES = 0x29 + SC_CARDCTL_MYEID_KEY_AES = 0x29, + SC_CARDCTL_MYEID_KEY_GENERIC_SECRET = 0x41 }; struct sc_cardctl_myeid_data_obj { @@ -893,26 +819,26 @@ typedef struct sc_rtecp_genkey_data { struct sc_cardctl_myeid_gen_store_key_info { int op_type; - unsigned int key_type; /* value of SC_CARDCTL_MYEID_KEY_TYPE */ - unsigned int key_len_bits; + unsigned int key_type; /* value of SC_CARDCTL_MYEID_KEY_TYPE */ + size_t key_len_bits; unsigned char *mod; - unsigned int pubexp_len; + size_t pubexp_len; unsigned char *pubexp; - unsigned int primep_len; + size_t primep_len; unsigned char *primep; - unsigned int primeq_len; + size_t primeq_len; unsigned char *primeq; - unsigned int dp1_len; + size_t dp1_len; unsigned char *dp1; - unsigned int dq1_len; + size_t dq1_len; unsigned char *dq1; - unsigned int invq_len; + size_t invq_len; unsigned char *invq; /* new for MyEID > 3.6.0 */ unsigned char *d; /* EC private key / Symmetric key */ - unsigned int d_len; /* EC / Symmetric */ + size_t d_len; /* EC / Symmetric */ unsigned char *ecpublic_point; /* EC public key */ - unsigned int ecpublic_point_len; /* EC */ + size_t ecpublic_point_len; /* EC */ }; /* @@ -920,15 +846,16 @@ typedef struct sc_rtecp_genkey_data { */ typedef struct sc_cardctl_piv_genkey_info_st { unsigned int key_num; - unsigned int key_algid; /* RSA 5, 6, 7; EC 11, 14 */ + unsigned int key_algid; /* RSA 5, 6, 7; EC 11, 14 */ unsigned int key_bits; /* RSA */ - unsigned long exponent; /* RSA */ + unsigned char * exponent; /* RSA */ + size_t exponent_len; /* RSA */ unsigned char * pubkey; /* RSA */ - unsigned int pubkey_len; /* RSA */ + size_t pubkey_len; /* RSA */ unsigned char * ecparam; /* EC */ unsigned int ecparam_len; /* EC */ unsigned char * ecpoint; /* EC */ - unsigned int ecpoint_len; /* EC */ + size_t ecpoint_len; /* EC */ } sc_cardctl_piv_genkey_info_t; @@ -939,32 +866,57 @@ typedef struct sc_cardctl_piv_genkey_info_st { #define SC_OPENPGP_KEY_ENCR 2 #define SC_OPENPGP_KEY_AUTH 3 -#define SC_OPENPGP_KEYFORMAT_STD 0 /* See 4.3.3.6 Algorithm Attributes */ -#define SC_OPENPGP_KEYFORMAT_STDN 1 /* OpenPGP card spec v2 */ -#define SC_OPENPGP_KEYFORMAT_CRT 2 -#define SC_OPENPGP_KEYFORMAT_CRTN 3 - -typedef struct sc_cardctl_openpgp_keygen_info { - u8 keytype; /* SC_OPENPGP_KEY_ */ - u8 *modulus; /* New-generated pubkey info responded from the card */ - size_t modulus_len; /* Length of modulus in bit */ - u8 *exponent; - size_t exponent_len; -} sc_cardctl_openpgp_keygen_info_t; - -typedef struct sc_cardctl_openpgp_keystore_info { - u8 keytype; - u8 keyformat; - u8 *e; - size_t e_len; - u8 *p; - size_t p_len; - u8 *q; - size_t q_len; - u8 *n; - size_t n_len; - time_t creationtime; -} sc_cardctl_openpgp_keystore_info_t; +#define SC_OPENPGP_KEYALGO_RSA 0x01 +#define SC_OPENPGP_KEYALGO_ECDH 0x12 +#define SC_OPENPGP_KEYALGO_ECDSA 0x13 +#define SC_OPENPGP_KEYALGO_EDDSA 0x16 + +#define SC_OPENPGP_KEYFORMAT_RSA_STD 0 /* See 4.3.3.6 Algorithm Attributes */ +#define SC_OPENPGP_KEYFORMAT_RSA_STDN 1 /* OpenPGP card spec v2 */ +#define SC_OPENPGP_KEYFORMAT_RSA_CRT 2 +#define SC_OPENPGP_KEYFORMAT_RSA_CRTN 3 + +#define SC_OPENPGP_KEYFORMAT_EC_STD 0 +#define SC_OPENPGP_KEYFORMAT_EC_STDPUB 0xFF + +#define SC_OPENPGP_MAX_EXP_BITS 0x20 /* maximum exponent length supported in bits */ + +typedef struct sc_cardctl_openpgp_key_gen_store_info { + u8 key_id; /* SC_OPENPGP_KEY_... */ + u8 algorithm; /* SC_OPENPGP_KEYALGO_... */ + unsigned long key_type; /* SC_ALGORITHM_... */ + u8 *data; /* algo blob data */ + size_t data_len; /* bytes */ + time_t creationtime; /* when private generated or stored */ + union { + struct { + u8 keyformat; /* SC_OPENPGP_KEYFORMAT_RSA_... */ + u8 *exponent; /* exponent to store or generated */ + size_t exponent_len; /* bit */ + u8 *p; + size_t p_len; + u8 *q; + size_t q_len; + u8 *modulus; /* modulus to store or generated */ + size_t modulus_len; /* bits - field_length */ + } rsa; + struct { + u8 keyformat; /* SC_OPENPGP_KEYFORMAT_EC_... */ + u8 *privateD; /* private key to store */ + size_t privateD_len; /* bytes */ + u8 *ecpointQ; /* public key to store or generated */ + size_t ecpointQ_len; /* bytes */ + struct sc_object_id oid; /* RFC8410 OID */ + /* + * sc_object_id oidv is misused here to contain the bytes + * of the values from TLV of OID DER TLV + */ + struct sc_object_id oidv; + size_t oidv_len; + size_t key_length; /* field_length bits */ + } ec; + } u; +} sc_cardctl_openpgp_key_gen_store_info_t; /* * SmartCard-HSM @@ -983,8 +935,12 @@ typedef struct sc_cardctl_sc_hsm_init_param { u8 *user_pin; /* Initial user PIN */ size_t user_pin_len; /* Length of user PIN */ u8 user_pin_retry_counter; /* Retry counter default value */ + struct sc_aid bio1; /* AID of biometric server for template 1 */ + struct sc_aid bio2; /* AID of biometric server for template 2 */ u8 options[2]; /* Initialization options */ signed char dkek_shares; /* Number of DKEK shares, 0 for card generated, -1 for none */ + signed char num_of_pub_keys; /* Total number of public keys used for public authentication (if > 0) */ + u8 required_pub_keys; /* Number of public keys required for authentication (if public auth. is used) */ char *label; /* Token label to be set in EF.TokenInfo (2F03) */ } sc_cardctl_sc_hsm_init_param_t; @@ -1002,11 +958,25 @@ typedef struct sc_cardctl_sc_hsm_wrapped_key { size_t wrapped_key_length; /* Length of key blob */ } sc_cardctl_sc_hsm_wrapped_key_t; +typedef struct sc_cardctl_sc_hsm_pka_status { + u8 num_total; + u8 num_missing; + u8 num_required; + u8 num_authenticated; +} sc_cardctl_sc_hsm_pka_status_t; + +typedef struct sc_cardctl_sc_hsm_pka_register { + u8 *buf; + size_t buflen; + sc_cardctl_sc_hsm_pka_status_t new_status; +} sc_cardctl_sc_hsm_pka_register_t; + /* * isoApplet */ #define SC_ISOAPPLET_ALG_REF_RSA_GEN_2048 0xF3 +#define SC_ISOAPPLET_ALG_REF_RSA_GEN_4096 0xF5 #define SC_ISOAPPLET_ALG_REF_EC_GEN 0xEC typedef struct sc_cardctl_isoApplet_ec_parameters { @@ -1020,7 +990,7 @@ typedef struct sc_cardctl_isoApplet_ec_parameters { typedef struct sc_cardctl_isoApplet_genkey { u8 algorithm_ref; /* Algorithm reference sent to card */ - unsigned int priv_key_ref; /* Private key refernce sent to card */ + unsigned int priv_key_ref; /* Private key reference sent to card */ union { struct { @@ -1037,7 +1007,7 @@ typedef struct sc_cardctl_isoApplet_genkey { typedef struct sc_cardctl_isoApplet_import_key { u8 algorithm_ref; /* Algorithm reference sent to card */ - unsigned int priv_key_ref; /* Private key refernce sent to card */ + unsigned int priv_key_ref; /* Private key reference sent to card */ union { struct { @@ -1081,7 +1051,7 @@ typedef struct sc_cardctl_coolkey_attribute { typedef struct sc_cardctl_coolkey_find_object { int type; /* in parameter */ unsigned long find_id; /* in parameter */ - sc_cardctl_coolkey_attribute_t *coolkey_template; /* in paramter */ + sc_cardctl_coolkey_attribute_t *coolkey_template; /* in parameter */ int template_count; /* in parameter */ sc_cardctl_coolkey_object_t *obj; /* out parameter */ } sc_cardctl_coolkey_find_object_t; diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index f7c78dd8a1..c897d6f9d9 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_CARDS_H @@ -47,6 +47,7 @@ enum { SC_CARD_TYPE_CARDOS_CIE_V1, /* Italian CIE (eID) v1 */ SC_CARD_TYPE_CARDOS_M4_4, SC_CARD_TYPE_CARDOS_V5_0, + SC_CARD_TYPE_CARDOS_V5_3, /* flex/cyberflex drivers */ SC_CARD_TYPE_FLEX_BASE = 2000, @@ -55,38 +56,15 @@ enum { SC_CARD_TYPE_FLEX_MULTI, SC_CARD_TYPE_FLEX_CYBER, - /* gpk driver */ - SC_CARD_TYPE_GPK_BASE = 3000, - SC_CARD_TYPE_GPK_GENERIC, - SC_CARD_TYPE_GPK_GPK4000_su256 = 3040, - SC_CARD_TYPE_GPK_GPK4000_s, - SC_CARD_TYPE_GPK_GPK4000_sp, - SC_CARD_TYPE_GPK_GPK4000_sdo, - SC_CARD_TYPE_GPK_GPK8000 = 3080, - SC_CARD_TYPE_GPK_GPK8000_8K, - SC_CARD_TYPE_GPK_GPK8000_16K, - SC_CARD_TYPE_GPK_GPK16000 = 3160, - - /* miocos driver */ - SC_CARD_TYPE_MIOCOS_BASE = 4000, - SC_CARD_TYPE_MIOCOS_GENERIC, - /* mcrd driver */ SC_CARD_TYPE_MCRD_BASE = 5000, SC_CARD_TYPE_MCRD_GENERIC, - SC_CARD_TYPE_MCRD_ESTEID_V10, - SC_CARD_TYPE_MCRD_ESTEID_V11, - SC_CARD_TYPE_MCRD_ESTEID_V30, - SC_CARD_TYPE_MCRD_DTRUST, /* setcos driver */ SC_CARD_TYPE_SETCOS_BASE = 6000, SC_CARD_TYPE_SETCOS_GENERIC, SC_CARD_TYPE_SETCOS_PKI, - SC_CARD_TYPE_SETCOS_FINEID, - SC_CARD_TYPE_SETCOS_FINEID_V2, SC_CARD_TYPE_SETCOS_NIDEL, - SC_CARD_TYPE_SETCOS_FINEID_V2_2048, SC_CARD_TYPE_SETCOS_44 = 6100, SC_CARD_TYPE_SETCOS_EID_V2_0, SC_CARD_TYPE_SETCOS_EID_V2_1, @@ -95,6 +73,9 @@ enum { SC_CARD_TYPE_STARCOS_BASE = 7000, SC_CARD_TYPE_STARCOS_GENERIC, SC_CARD_TYPE_STARCOS_V3_4, + SC_CARD_TYPE_STARCOS_V3_5, + SC_CARD_TYPE_STARCOS_V3_4_ESIGN, + SC_CARD_TYPE_STARCOS_V3_5_ESIGN, /* tcos driver */ SC_CARD_TYPE_TCOS_BASE = 8000, @@ -106,12 +87,9 @@ enum { SC_CARD_TYPE_OPENPGP_BASE = 9000, SC_CARD_TYPE_OPENPGP_V1, SC_CARD_TYPE_OPENPGP_V2, + SC_CARD_TYPE_OPENPGP_V3, SC_CARD_TYPE_OPENPGP_GNUK, - /* jcop driver */ - SC_CARD_TYPE_JCOP_BASE = 10000, - SC_CARD_TYPE_JCOP_GENERIC, - /* oberthur driver */ SC_CARD_TYPE_OBERTHUR_BASE = 11000, SC_CARD_TYPE_OBERTHUR_GENERIC, @@ -119,23 +97,31 @@ enum { SC_CARD_TYPE_OBERTHUR_32K_BIO, SC_CARD_TYPE_OBERTHUR_64K, /* Oberthur 'COSMO v7' with applet 'AuthentIC v3.2' */ - SC_CARD_TYPE_OBERTHUR_AUTHENTIC_3_2 = 11100, + SC_CARD_TYPE_OBERTHUR_AUTHENTIC_3_2 = 11100, /* belpic driver */ SC_CARD_TYPE_BELPIC_BASE = 12000, SC_CARD_TYPE_BELPIC_GENERIC, SC_CARD_TYPE_BELPIC_EID, - /* incrypto34 driver */ - SC_CARD_TYPE_INCRYPTO34_BASE = 13000, - SC_CARD_TYPE_INCRYPTO34_GENERIC, - /* PIV-II type cards */ SC_CARD_TYPE_PIV_II_BASE = 14000, SC_CARD_TYPE_PIV_II_GENERIC, SC_CARD_TYPE_PIV_II_HIST, SC_CARD_TYPE_PIV_II_NEO, SC_CARD_TYPE_PIV_II_YUBIKEY4, + SC_CARD_TYPE_PIV_II_GI_DE_DUAL_CAC, + SC_CARD_TYPE_PIV_II_GI_DE, + SC_CARD_TYPE_PIV_II_GEMALTO_DUAL_CAC, + SC_CARD_TYPE_PIV_II_GEMALTO, + SC_CARD_TYPE_PIV_II_OBERTHUR_DUAL_CAC, + SC_CARD_TYPE_PIV_II_OBERTHUR, + SC_CARD_TYPE_PIV_II_PIVKEY, + SC_CARD_TYPE_PIV_II_SWISSBIT, + SC_CARD_TYPE_PIV_II_800_73_4, + SC_CARD_TYPE_PIV_II_NITROKEY, + SC_CARD_TYPE_PIV_II_TOKEN2, + SC_CARD_TYPE_PIV_II_PIVAPPLET, /* MuscleApplet */ SC_CARD_TYPE_MUSCLE_BASE = 15000, @@ -146,19 +132,11 @@ enum { SC_CARD_TYPE_MUSCLE_JCOP241, SC_CARD_TYPE_MUSCLE_JCOP242R2_NO_EXT_APDU, - /* ACOS5 driver */ - SC_CARD_TYPE_ACOS5_BASE = 16000, - SC_CARD_TYPE_ACOS5_GENERIC, - /* Athena APCOS cards */ SC_CARD_TYPE_ASEPCOS_BASE = 17000, SC_CARD_TYPE_ASEPCOS_GENERIC, SC_CARD_TYPE_ASEPCOS_JAVA, - /* TUBITAK UEKAE cards */ - SC_CARD_TYPE_AKIS_BASE = 18000, - SC_CARD_TYPE_AKIS_GENERIC, - /* EnterSafe cards */ SC_CARD_TYPE_ENTERSAFE_BASE = 19000, SC_CARD_TYPE_ENTERSAFE_3K, @@ -175,6 +153,7 @@ enum { /* MyEID cards */ SC_CARD_TYPE_MYEID_BASE = 20000, SC_CARD_TYPE_MYEID_GENERIC, + SC_CARD_TYPE_MYEID_OSEID, /* GemsafeV1 cards */ SC_CARD_TYPE_GEMSAFEV1_BASE = 21000, @@ -186,6 +165,7 @@ enum { SC_CARD_TYPE_ITACNS_BASE = 23000, SC_CARD_TYPE_ITACNS_GENERIC, SC_CARD_TYPE_ITACNS_CNS, + SC_CARD_TYPE_ITACNS_CNS_IDEMIA_2021, SC_CARD_TYPE_ITACNS_CIE_V2, SC_CARD_TYPE_ITACNS_CIE_V1, @@ -200,16 +180,20 @@ enum { SC_CARD_TYPE_IASECC_SAGEM, SC_CARD_TYPE_IASECC_AMOS, SC_CARD_TYPE_IASECC_MI, + SC_CARD_TYPE_IASECC_MI2, + SC_CARD_TYPE_IASECC_CPX, + SC_CARD_TYPE_IASECC_CPXCL, /* SmartCard-HSM */ SC_CARD_TYPE_SC_HSM = 26000, SC_CARD_TYPE_SC_HSM_SOC = 26001, + SC_CARD_TYPE_SC_HSM_GOID = 26002, /* Spanish DNIe card */ SC_CARD_TYPE_DNIE_BASE = 27000, - SC_CARD_TYPE_DNIE_BLANK, /* ATR LC byte: 00 */ - SC_CARD_TYPE_DNIE_ADMIN, /* ATR LC byte: 01 */ - SC_CARD_TYPE_DNIE_USER, /* ATR LC byte: 03 */ + SC_CARD_TYPE_DNIE_BLANK, /* ATR LC byte: 00 */ + SC_CARD_TYPE_DNIE_ADMIN, /* ATR LC byte: 01 */ + SC_CARD_TYPE_DNIE_USER, /* ATR LC byte: 03 */ SC_CARD_TYPE_DNIE_TERMINATED, /* ATR LC byte: 0F */ /* JavaCards with isoApplet */ @@ -229,6 +213,7 @@ enum { /* JPKI cards */ SC_CARD_TYPE_JPKI_BASE = 31000, + /* Coolkey cards */ SC_CARD_TYPE_COOLKEY_BASE = 32000, SC_CARD_TYPE_COOLKEY_GENERIC, @@ -237,39 +222,92 @@ enum { SC_CARD_TYPE_CAC_GENERIC, SC_CARD_TYPE_CAC_I, SC_CARD_TYPE_CAC_II, + SC_CARD_TYPE_CAC_ALT_HID, /* nPA cards */ SC_CARD_TYPE_NPA = 34000, SC_CARD_TYPE_NPA_TEST, SC_CARD_TYPE_NPA_ONLINE, + + /* EstEID cards */ + SC_CARD_TYPE_ESTEID_2018 = 35000, + SC_CARD_TYPE_ESTEID_2018_V2_2025, + SC_CARD_TYPE_LATEID_2018_V2_2025, + SC_CARD_TYPE_ESTEID_2025, + SC_CARD_TYPE_FINEID_2022, + SC_CARD_TYPE_FINEID_2025, + SC_CARD_TYPE_LATEID_2018, + + /* Rutoken cards */ + SC_CARD_TYPE_RUTOKENS = 36000, + SC_CARD_TYPE_RUTOKEN_ECP, + SC_CARD_TYPE_RUTOKEN_ECP_SC, + SC_CARD_TYPE_RUTOKEN_LITE, + SC_CARD_TYPE_RUTOKEN_LITE_SC, + + /* IDPrime cards */ + SC_CARD_TYPE_IDPRIME_BASE = 37000, + SC_CARD_TYPE_IDPRIME_3810, + SC_CARD_TYPE_IDPRIME_830, + SC_CARD_TYPE_IDPRIME_930, + SC_CARD_TYPE_IDPRIME_930_PLUS, + SC_CARD_TYPE_IDPRIME_940, + SC_CARD_TYPE_IDPRIME_840, + SC_CARD_TYPE_IDPRIME_GENERIC, + + /* eDO cards */ + SC_CARD_TYPE_EDO = 38000, + + /* JCOP4 cards with NQ-Applet */ + SC_CARD_TYPE_NQ_APPLET = 39000, + SC_CARD_TYPE_NQ_APPLET_RFID = 39001, + + /* Slovak eID cards */ + SC_CARD_TYPE_SKEID_BASE = 40000, + SC_CARD_TYPE_SKEID_V3, + + /* eOI cards */ + SC_CARD_TYPE_EOI = 41000, + SC_CARD_TYPE_EOI_CONTACTLESS, + + /* D-Trust Signature cards */ + SC_CARD_TYPE_DTRUST_V4_1_STD = 42000, + SC_CARD_TYPE_DTRUST_V4_1_MULTI, + SC_CARD_TYPE_DTRUST_V4_1_M100, + SC_CARD_TYPE_DTRUST_V4_4_STD, + SC_CARD_TYPE_DTRUST_V4_4_MULTI, + SC_CARD_TYPE_DTRUST_V5_1_STD, + SC_CARD_TYPE_DTRUST_V5_1_MULTI, + SC_CARD_TYPE_DTRUST_V5_1_M100, + SC_CARD_TYPE_DTRUST_V5_4_STD, + SC_CARD_TYPE_DTRUST_V5_4_MULTI, + + /* Lithuanian eID cards */ + SC_CARD_TYPE_LTEID = 43000, + + /* Serbian cards (CardEdge PKI applet) */ + SC_CARD_TYPE_SRBEID_BASE = 44000, }; extern sc_card_driver_t *sc_get_default_driver(void); extern sc_card_driver_t *sc_get_cardos_driver(void); extern sc_card_driver_t *sc_get_cryptoflex_driver(void); extern sc_card_driver_t *sc_get_cyberflex_driver(void); -extern sc_card_driver_t *sc_get_gpk_driver(void); extern sc_card_driver_t *sc_get_gemsafeV1_driver(void); -extern sc_card_driver_t *sc_get_miocos_driver(void); extern sc_card_driver_t *sc_get_mcrd_driver(void); extern sc_card_driver_t *sc_get_setcos_driver(void); extern sc_card_driver_t *sc_get_starcos_driver(void); extern sc_card_driver_t *sc_get_tcos_driver(void); extern sc_card_driver_t *sc_get_openpgp_driver(void); -extern sc_card_driver_t *sc_get_jcop_driver(void); extern sc_card_driver_t *sc_get_oberthur_driver(void); extern sc_card_driver_t *sc_get_belpic_driver(void); extern sc_card_driver_t *sc_get_atrust_acos_driver(void); -extern sc_card_driver_t *sc_get_incrypto34_driver(void); extern sc_card_driver_t *sc_get_piv_driver(void); extern sc_card_driver_t *sc_get_muscle_driver(void); -extern sc_card_driver_t *sc_get_acos5_driver(void); extern sc_card_driver_t *sc_get_asepcos_driver(void); -extern sc_card_driver_t *sc_get_akis_driver(void); extern sc_card_driver_t *sc_get_entersafe_driver(void); extern sc_card_driver_t *sc_get_rutoken_driver(void); extern sc_card_driver_t *sc_get_rtecp_driver(void); -extern sc_card_driver_t *sc_get_westcos_driver(void); extern sc_card_driver_t *sc_get_myeid_driver(void); extern sc_card_driver_t *sc_get_sc_hsm_driver(void); extern sc_card_driver_t *sc_get_itacns_driver(void); @@ -283,7 +321,18 @@ extern sc_card_driver_t *sc_get_gids_driver(void); extern sc_card_driver_t *sc_get_jpki_driver(void); extern sc_card_driver_t *sc_get_coolkey_driver(void); extern sc_card_driver_t *sc_get_cac_driver(void); +extern sc_card_driver_t *sc_get_cac1_driver(void); extern sc_card_driver_t *sc_get_npa_driver(void); +extern sc_card_driver_t *sc_get_esteid2018_driver(void); +extern sc_card_driver_t *sc_get_esteid2025_driver(void); +extern sc_card_driver_t *sc_get_idprime_driver(void); +extern sc_card_driver_t *sc_get_edo_driver(void); +extern sc_card_driver_t *sc_get_nqApplet_driver(void); +extern sc_card_driver_t *sc_get_skeid_driver(void); +extern sc_card_driver_t *sc_get_eoi_driver(void); +extern sc_card_driver_t *sc_get_dtrust_driver(void); +extern sc_card_driver_t *sc_get_lteid_driver(void); +extern sc_card_driver_t *sc_get_srbeid_driver(void); #ifdef __cplusplus } diff --git a/src/libopensc/ccid-types.h b/src/libopensc/ccid-types.h index 59d3cf3fc2..fae0ebdc57 100644 --- a/src/libopensc/ccid-types.h +++ b/src/libopensc/ccid-types.h @@ -13,7 +13,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file diff --git a/src/libopensc/compression.c b/src/libopensc/compression.c index dd2ea4eefc..c50a021432 100644 --- a/src/libopensc/compression.c +++ b/src/libopensc/compression.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -38,11 +38,12 @@ static int zerr_to_opensc(int err) { return SC_SUCCESS; case Z_UNKNOWN: return SC_ERROR_UNKNOWN; - case Z_BUF_ERROR: /* XXX: something else than OOM ? */ + case Z_DATA_ERROR: + case Z_BUF_ERROR: + return SC_ERROR_UNKNOWN_DATA_RECEIVED; case Z_MEM_ERROR: return SC_ERROR_OUT_OF_MEMORY; case Z_VERSION_ERROR: - case Z_DATA_ERROR: case Z_STREAM_ERROR: /* case Z_NEED_DICT: */ default: @@ -50,17 +51,31 @@ static int zerr_to_opensc(int err) { } } static int detect_method(const u8* in, size_t inLen) { - if(inLen > 2 && in[0] == 0x1f && in[1] == 0x8b) { /* GZIP */ - return COMPRESSION_GZIP; - } else if(inLen > 1 /*&& (in[0] & 0x10) == Z_DEFLATED*/) { - /* REALLY SIMPLE ZLIB TEST -- - * Check for the compression method to be set to 8... - * many things can spoof this, but this is ok for now - * */ - return COMPRESSION_ZLIB; - } else { - return COMPRESSION_UNKNOWN; + if (in != NULL && inLen > 1) { + if (in[0] == 0x1f && in[1] == 0x8b) + return COMPRESSION_GZIP; + /* + * A zlib stream has the following structure: + * 0 1 + * +---+---+ + * |CMF|FLG| (more-->) + * +---+---+ + * + * FLG (FLaGs) + * This flag byte is divided as follows: + * + * bits 0 to 4 FCHECK (check bits for CMF and FLG) + * bit 5 FDICT (preset dictionary) + * bits 6 to 7 FLEVEL (compression level) + * + * The FCHECK value must be such that CMF and FLG, when viewed as + * a 16-bit unsigned integer stored in MSB order (CMF*256 + FLG), + * is a multiple of 31. + */ + if ((((uint16_t) in[0])*256 + in[1]) % 31 == 0) + return COMPRESSION_ZLIB; } + return COMPRESSION_UNKNOWN; } static int sc_compress_gzip(u8* out, size_t* outLen, const u8* in, size_t inLen) { @@ -71,14 +86,17 @@ static int sc_compress_gzip(u8* out, size_t* outLen, const u8* in, size_t inLen) memset(&gz, 0, sizeof(gz)); gz.next_in = (u8*)in; - gz.avail_in = inLen; + gz.avail_in = (unsigned)inLen; gz.next_out = out; - gz.avail_out = *outLen; + gz.avail_out = (unsigned)*outLen; err = deflateInit2(&gz, Z_BEST_COMPRESSION, Z_DEFLATED, window_size, 9, Z_DEFAULT_STRATEGY); - if(err != Z_OK) return zerr_to_opensc(err); + if (err != Z_OK) { + deflateEnd(&gz); + return zerr_to_opensc(err); + } err = deflate(&gz, Z_FINISH); - if(err != Z_STREAM_END) { + if (err != Z_STREAM_END) { deflateEnd(&gz); return zerr_to_opensc(err); } @@ -96,12 +114,14 @@ static int sc_decompress_gzip(u8* out, size_t* outLen, const u8* in, size_t inLe memset(&gz, 0, sizeof(gz)); gz.next_in = (u8*)in; - gz.avail_in = inLen; + gz.avail_in = (unsigned)inLen; gz.next_out = out; - gz.avail_out = *outLen; + gz.avail_out = (unsigned)*outLen; + + *outLen = 0; err = inflateInit2(&gz, window_size); - if(err != Z_OK) return zerr_to_opensc(err); + if (err != Z_OK) return zerr_to_opensc(err); err = inflate(&gz, Z_FINISH); if(err != Z_STREAM_END) { inflateEnd(&gz); @@ -110,7 +130,10 @@ static int sc_decompress_gzip(u8* out, size_t* outLen, const u8* in, size_t inLe *outLen = gz.total_out; err = inflateEnd(&gz); - return zerr_to_opensc(err); + if (*outLen == 0) { + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + } + return zerr_to_opensc(err); } int sc_compress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method) { @@ -130,19 +153,25 @@ int sc_compress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method) } } -int sc_decompress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method) { +int sc_decompress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method) +{ unsigned long zlib_outlen; int rc; - if(method == COMPRESSION_AUTO) { + if (in == NULL || out == NULL) { + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + } + + if (method == COMPRESSION_AUTO) { method = detect_method(in, inLen); - if(method == COMPRESSION_UNKNOWN) { + if (method == COMPRESSION_UNKNOWN) { + *outLen = 0; return SC_ERROR_UNKNOWN_DATA_RECEIVED; } } - switch(method) { + switch (method) { case COMPRESSION_ZLIB: - zlib_outlen = *outLen; + zlib_outlen = *outLen; rc = zerr_to_opensc(uncompress(out, &zlib_outlen, in, inLen)); *outLen = zlib_outlen; return rc; @@ -158,51 +187,61 @@ static int sc_decompress_zlib_alloc(u8** out, size_t* outLen, const u8* in, size z_stream gz; int err; int window_size = 15; - const int startSize = inLen < 1024 ? 2048 : inLen * 2; - const int blockSize = inLen < 1024 ? 512 : inLen / 2; - int bufferSize = startSize; - if(gzip) + const size_t startSize = inLen < 1024 ? 2048 : inLen * 2; + const size_t blockSize = inLen < 1024 ? 512 : inLen / 2; + size_t bufferSize = startSize; + if (gzip) window_size += 0x20; memset(&gz, 0, sizeof(gz)); - + + if (!out || !outLen) + return SC_ERROR_INVALID_ARGUMENTS; + gz.next_in = (u8*)in; - gz.avail_in = inLen; + gz.avail_in = (unsigned)inLen; err = inflateInit2(&gz, window_size); - if(err != Z_OK) return zerr_to_opensc(err); + if (err != Z_OK) + return zerr_to_opensc(err); *outLen = 0; - while(1) { + while (1) { /* Setup buffer... */ - int num; + size_t num; u8* buf = realloc(*out, bufferSize); - if(!buf) { - if(*out) - free(*out); + if (!buf) { + free(*out); *out = NULL; - return Z_MEM_ERROR; + inflateEnd(&gz); + return SC_ERROR_OUT_OF_MEMORY; } *out = buf; gz.next_out = buf + *outLen; - gz.avail_out = bufferSize - *outLen; + gz.avail_out = (unsigned)(bufferSize - *outLen); err = inflate(&gz, Z_FULL_FLUSH); - if(err != Z_STREAM_END && err != Z_OK) { - if(*out) - free(*out); + if (err != Z_STREAM_END && err != Z_OK) { + free(*out); *out = NULL; break; } - num = bufferSize - *outLen - gz.avail_out; - if(num > 0) { - *outLen += num; - bufferSize += num + blockSize; + num = *outLen + gz.avail_out; + if (bufferSize > num) { + *outLen += bufferSize - num; + bufferSize += bufferSize - num + blockSize; } - if(err == Z_STREAM_END) { - buf = realloc(buf, *outLen); /* Shrink it down, if it fails, just use old data */ - if(buf) { - *out = buf; + if (err == Z_STREAM_END) { + if (*outLen > 0) { + /* Shrink it down, if it fails, just use old data */ + buf = realloc(buf, *outLen); + if (buf) { + *out = buf; + } + } else { + free(*out); + *out = NULL; + err = Z_DATA_ERROR; } break; } @@ -210,14 +249,21 @@ static int sc_decompress_zlib_alloc(u8** out, size_t* outLen, const u8* in, size inflateEnd(&gz); return zerr_to_opensc(err); } -int sc_decompress_alloc(u8** out, size_t* outLen, const u8* in, size_t inLen, int method) { - if(method == COMPRESSION_AUTO) { + +int sc_decompress_alloc(u8** out, size_t* outLen, const u8* in, size_t inLen, int method) +{ + if (in == NULL || out == NULL) { + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + } + + if (method == COMPRESSION_AUTO) { method = detect_method(in, inLen); - if(method == COMPRESSION_UNKNOWN) { + if (method == COMPRESSION_UNKNOWN) { return SC_ERROR_UNKNOWN_DATA_RECEIVED; } } - switch(method) { + + switch (method) { case COMPRESSION_ZLIB: return sc_decompress_zlib_alloc(out, outLen, in, inLen, 0); case COMPRESSION_GZIP: diff --git a/src/libopensc/compression.h b/src/libopensc/compression.h index c27b2d55ab..55885baca2 100644 --- a/src/libopensc/compression.h +++ b/src/libopensc/compression.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef COMPRESSION_H #define COMPRESSION_H diff --git a/src/libopensc/ctbcs.c b/src/libopensc/ctbcs.c index 8b92212242..dddb255536 100644 --- a/src/libopensc/ctbcs.c +++ b/src/libopensc/ctbcs.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -202,7 +202,7 @@ ctbcs_pin_cmd(sc_reader_t *reader, struct sc_pin_cmd_data *data) return r; break; default: - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Unknown PIN command %d", data->cmd); + sc_log(reader->ctx, "Unknown PIN command %d", data->cmd); return SC_ERROR_NOT_SUPPORTED; } @@ -219,10 +219,10 @@ ctbcs_pin_cmd(sc_reader_t *reader, struct sc_pin_cmd_data *data) r = sc_transmit_apdu(card, &apdu); s = sc_mutex_destroy(reader->ctx, card->mutex); if (s != SC_SUCCESS) { - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "unable to destroy mutex\n"); + sc_log(reader->ctx, "unable to destroy mutex\n"); return s; } - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); /* Check CTBCS status word */ switch (((unsigned int) apdu.sw1 << 8) | apdu.sw2) { @@ -245,7 +245,7 @@ ctbcs_pin_cmd(sc_reader_t *reader, struct sc_pin_cmd_data *data) r = SC_ERROR_CARD_CMD_FAILED; break; } - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "PIN command failed"); + LOG_TEST_RET(card->ctx, r, "PIN command failed"); /* Calling Function may expect SW1/SW2 in data-apdu set... */ if (data->apdu) { diff --git a/src/libopensc/ctbcs.h b/src/libopensc/ctbcs.h index adf4fd156f..40c68daf9d 100644 --- a/src/libopensc/ctbcs.h +++ b/src/libopensc/ctbcs.h @@ -17,7 +17,7 @@ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CTBCS_ @@ -107,7 +107,7 @@ #define CTBCS_P2_INPUT_ASTERISKS 0x02 /* Echo input as asterisks */ /* - * Tags for paramaters to input, output et al. + * Tags for parameters to input, output et al. */ #define CTBCS_TAG_PROMPT 0x50 #define CTBCS_TAG_VERIFY_CMD 0x52 @@ -143,9 +143,9 @@ */ #define CTBCS_SW1_RESET_CT_OK 0x90 /* Reset CT successful */ #define CTBCS_SW2_RESET_CT_OK 0x00 -#define CTBCS_SW1_RESET_SYNC_OK 0x90 /* Synchoronous ICC, */ +#define CTBCS_SW1_RESET_SYNC_OK 0x90 /* Synchronous ICC, */ #define CTBCS_SW2_RESET_SYNC_OK 0x00 /* reset successful */ -#define CTBCS_SW1_RESET_ASYNC_OK 0x90 /* Asynchoronous ICC, */ +#define CTBCS_SW1_RESET_ASYNC_OK 0x90 /* Asynchronous ICC, */ #define CTBCS_SW2_RESET_ASYNC_OK 0x01 /* reset successful */ #define CTBCS_SW1_RESET_ERROR 0x64 /* Reset not successful */ #define CTBCS_SW2_RESET_ERROR 0x00 @@ -153,9 +153,9 @@ /* * Return codes for Request ICC */ -#define CTBCS_SW1_REQUEST_SYNC_OK 0x90 /* Synchoronous ICC, */ +#define CTBCS_SW1_REQUEST_SYNC_OK 0x90 /* Synchronous ICC, */ #define CTBCS_SW2_REQUEST_SYNC_OK 0x00 /* reset successful */ -#define CTBCS_SW1_REQUEST_ASYNC_OK 0x90 /* Asynchoronous ICC, */ +#define CTBCS_SW1_REQUEST_ASYNC_OK 0x90 /* Asynchronous ICC, */ #define CTBCS_SW2_REQUEST_ASYNC_OK 0x01 /* reset successful */ #define CTBCS_SW1_REQUEST_NO_CARD 0x62 /* No card present */ #define CTBCS_SW2_REQUEST_NO_CARD 0x00 @@ -169,9 +169,9 @@ /* * Return codes for Eject ICC */ -#define CTBCS_SW1_EJECT_OK 0x90 /* Command succesful, */ +#define CTBCS_SW1_EJECT_OK 0x90 /* Command successful, */ #define CTBCS_SW2_EJECT_OK 0x00 -#define CTBCS_SW1_EJECT_REMOVED 0x90 /* Command succesful, */ +#define CTBCS_SW1_EJECT_REMOVED 0x90 /* Command successful, */ #define CTBCS_SW2_EJECT_REMOVED 0x01 /* Card removed */ #define CTBCS_SW1_EJECT_NOT_REMOVED 0x62 /* Card not removed */ #define CTBCS_SW2_EJECT_NOT_REMOVED 0x00 diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index 21801712d5..e987d001c3 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -34,14 +34,47 @@ #include #include #include +#include +#endif + +#ifdef __APPLE__ +#include #endif #include "common/libscdl.h" +#include "common/compat_strlcpy.h" #include "internal.h" +#ifdef ENABLE_OPENSSL +#include +#include "sc-ossl-compat.h" +#endif + + +static int ignored_reader(sc_context_t *ctx, sc_reader_t *reader) +{ + if (ctx != NULL && reader != NULL && reader->name != NULL) { + size_t i; + const scconf_list *list; + + for (i = 0; ctx->conf_blocks[i]; i++) { + list = scconf_find_list(ctx->conf_blocks[i], "ignored_readers"); + while (list != NULL) { + if (strstr(reader->name, list->data) != NULL) { + sc_log(ctx, "Ignoring reader \'%s\' because of \'%s\'\n", + reader->name, list->data); + return 1; + } + list = list->next; + } + } + } + + return 0; +} int _sc_add_reader(sc_context_t *ctx, sc_reader_t *reader) { - if (reader == NULL) { + if (reader == NULL || ignored_reader(ctx, reader)) { return SC_ERROR_INVALID_ARGUMENTS; } reader->ctx = ctx; @@ -68,28 +101,26 @@ struct _sc_driver_entry { void *(*func)(void); }; +// clang-format off static const struct _sc_driver_entry internal_card_drivers[] = { + /* The card handled by skeid shares the ATR with other cards running CardOS 5.4. + * In order to prevent the cardos driver from matching skeid cards, skeid driver + * precedes cardos and matches no other CardOS 5.4 card. */ + { "skeid", (void *(*)(void)) sc_get_skeid_driver }, + /* The card handled by dtrust shares the ATR with other cards running CardOS 5.4. + * In order to prevent the cardos driver from matching dtrust cards, dtrust driver + * precedes cardos and matches no other CardOS 5.4 card. */ + { "dtrust", (void *(*)(void)) sc_get_dtrust_driver }, { "cardos", (void *(*)(void)) sc_get_cardos_driver }, - { "flex", (void *(*)(void)) sc_get_cryptoflex_driver }, - { "cyberflex", (void *(*)(void)) sc_get_cyberflex_driver }, -#ifdef ENABLE_OPENSSL - { "gpk", (void *(*)(void)) sc_get_gpk_driver }, -#endif { "gemsafeV1", (void *(*)(void)) sc_get_gemsafeV1_driver }, - { "miocos", (void *(*)(void)) sc_get_miocos_driver }, - { "asepcos", (void *(*)(void)) sc_get_asepcos_driver }, { "starcos", (void *(*)(void)) sc_get_starcos_driver }, { "tcos", (void *(*)(void)) sc_get_tcos_driver }, - { "jcop", (void *(*)(void)) sc_get_jcop_driver }, #ifdef ENABLE_OPENSSL { "oberthur", (void *(*)(void)) sc_get_oberthur_driver }, { "authentic", (void *(*)(void)) sc_get_authentic_driver }, { "iasecc", (void *(*)(void)) sc_get_iasecc_driver }, #endif { "belpic", (void *(*)(void)) sc_get_belpic_driver }, - { "incrypto34", (void *(*)(void)) sc_get_incrypto34_driver }, - { "acos5", (void *(*)(void)) sc_get_acos5_driver }, - { "akis", (void *(*)(void)) sc_get_akis_driver }, #ifdef ENABLE_OPENSSL { "entersafe",(void *(*)(void)) sc_get_entersafe_driver }, #ifdef ENABLE_SM @@ -98,20 +129,31 @@ static const struct _sc_driver_entry internal_card_drivers[] = { #endif { "rutoken", (void *(*)(void)) sc_get_rutoken_driver }, { "rutoken_ecp",(void *(*)(void)) sc_get_rtecp_driver }, - { "westcos", (void *(*)(void)) sc_get_westcos_driver }, { "myeid", (void *(*)(void)) sc_get_myeid_driver }, - { "sc-hsm", (void *(*)(void)) sc_get_sc_hsm_driver }, #if defined(ENABLE_OPENSSL) && defined(ENABLE_SM) { "dnie", (void *(*)(void)) sc_get_dnie_driver }, #endif { "masktech", (void *(*)(void)) sc_get_masktech_driver }, + { "idprime", (void *(*)(void)) sc_get_idprime_driver }, +#if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) + { "edo", (void *(*)(void)) sc_get_edo_driver }, + { "lteid", (void *(*)(void)) sc_get_lteid_driver }, +#endif -/* Here should be placed drivers that need some APDU transactions to - * recognise its cards. */ - { "mcrd", (void *(*)(void)) sc_get_mcrd_driver }, - { "setcos", (void *(*)(void)) sc_get_setcos_driver }, +/* Here should be placed drivers that need some APDU transactions in the + * driver's `match_card()` function. */ + { "esteid2018", (void *(*)(void)) sc_get_esteid2018_driver }, + { "esteid2025", (void *(*)(void)) sc_get_esteid2025_driver }, + /* srbeid: Serbian cards with CardEdge PKI applet (eID, health, PKS). + * Uses AID selection in match_card() for cards without a distinct ATR. */ + { "srbeid", (void *(*)(void)) sc_get_srbeid_driver }, + { "coolkey", (void *(*)(void)) sc_get_coolkey_driver }, + /* MUSCLE card applet returns 9000 on whatever AID is selected, see + * https://github.com/JavaCardOS/MuscleCard-Applet/blob/master/musclecard/src/com/musclecard/CardEdge/CardEdge.java#L326 + * put the muscle driver first to cope with this bug. */ { "muscle", (void *(*)(void)) sc_get_muscle_driver }, - { "atrust-acos",(void *(*)(void)) sc_get_atrust_acos_driver }, + { "sc-hsm", (void *(*)(void)) sc_get_sc_hsm_driver }, + { "setcos", (void *(*)(void)) sc_get_setcos_driver }, { "PIV-II", (void *(*)(void)) sc_get_piv_driver }, { "cac", (void *(*)(void)) sc_get_cac_driver }, { "itacns", (void *(*)(void)) sc_get_itacns_driver }, @@ -121,29 +163,40 @@ static const struct _sc_driver_entry internal_card_drivers[] = { #endif { "openpgp", (void *(*)(void)) sc_get_openpgp_driver }, { "jpki", (void *(*)(void)) sc_get_jpki_driver }, - { "coolkey", (void *(*)(void)) sc_get_coolkey_driver }, { "npa", (void *(*)(void)) sc_get_npa_driver }, + { "cac1", (void *(*)(void)) sc_get_cac1_driver }, + { "nqapplet", (void *(*)(void)) sc_get_nqApplet_driver }, +#if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) + { "eOI", (void *(*)(void)) sc_get_eoi_driver }, +#endif /* The default driver should be last, as it handles all the * unrecognized cards. */ { "default", (void *(*)(void)) sc_get_default_driver }, { NULL, NULL } }; +static const struct _sc_driver_entry old_card_drivers[] = { + { "asepcos", (void *(*)(void)) sc_get_asepcos_driver }, + { "atrust-acos",(void *(*)(void)) sc_get_atrust_acos_driver }, + { "cyberflex", (void *(*)(void)) sc_get_cyberflex_driver }, + { "flex", (void *(*)(void)) sc_get_cryptoflex_driver }, + { "mcrd", (void *(*)(void)) sc_get_mcrd_driver }, + { NULL, NULL } +}; +// clang-format on + struct _sc_ctx_options { struct _sc_driver_entry cdrv[SC_MAX_CARD_DRIVERS]; int ccount; - char *forced_card_driver; }; int -sc_ctx_win32_get_config_value(char *name_env, char *name_reg, char *name_key, - char *out, size_t *out_len) +sc_ctx_win32_get_config_value(const char *name_env, + const char *name_reg, const char *name_key, + void *out, size_t *out_len) { #ifdef _WIN32 - char temp[PATH_MAX + 1]; - char *value = NULL; - DWORD temp_len = PATH_MAX; long rc; HKEY hKey; @@ -151,49 +204,44 @@ sc_ctx_win32_get_config_value(char *name_env, char *name_reg, char *name_key, return SC_ERROR_INVALID_ARGUMENTS; if (name_env) { - value = getenv(name_env); - if (value) - goto done; + char *value = getenv(name_env); + if (value) { + if (strlen(value) > *out_len) + return SC_ERROR_NOT_ENOUGH_MEMORY; + memcpy(out, value, strlen(value)); + *out_len = strlen(value); + return SC_SUCCESS; + } } if (!name_reg) return SC_ERROR_INVALID_ARGUMENTS; if (!name_key) - name_key = "Software\\OpenSC Project\\OpenSC"; + name_key = "Software\\" OPENSC_VS_FF_COMPANY_NAME "\\OpenSC" OPENSC_ARCH_SUFFIX; rc = RegOpenKeyExA(HKEY_CURRENT_USER, name_key, 0, KEY_QUERY_VALUE, &hKey); if (rc == ERROR_SUCCESS) { - temp_len = PATH_MAX; - rc = RegQueryValueEx( hKey, name_reg, NULL, NULL, (LPBYTE) temp, &temp_len); - if ((rc == ERROR_SUCCESS) && (temp_len < PATH_MAX)) - value = temp; + DWORD len = *out_len; + rc = RegQueryValueEx(hKey, name_reg, NULL, NULL, out, &len); RegCloseKey(hKey); - } - - if (!value) { - rc = RegOpenKeyExA( HKEY_LOCAL_MACHINE, name_key, 0, KEY_QUERY_VALUE, &hKey ); if (rc == ERROR_SUCCESS) { - temp_len = PATH_MAX; - rc = RegQueryValueEx( hKey, name_reg, NULL, NULL, (LPBYTE) temp, &temp_len); - if ((rc == ERROR_SUCCESS) && (temp_len < PATH_MAX)) - value = temp; - RegCloseKey(hKey); + *out_len = len; + return SC_SUCCESS; } } -done: - if (value) { - if (strlen(value) >= *out_len) - return SC_ERROR_BUFFER_TOO_SMALL; - strcpy(out, value); - *out_len = strlen(out); - return SC_SUCCESS; + rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, name_key, 0, KEY_QUERY_VALUE, &hKey); + if (rc == ERROR_SUCCESS) { + DWORD len = *out_len; + rc = RegQueryValueEx(hKey, name_reg, NULL, NULL, out, &len); + RegCloseKey(hKey); + if (rc == ERROR_SUCCESS) { + *out_len = len; + return SC_SUCCESS; + } } - memset(out, 0, *out_len); - *out_len = 0; - return SC_ERROR_OBJECT_NOT_FOUND; #else return SC_ERROR_NOT_SUPPORTED; @@ -256,6 +304,19 @@ static void add_internal_drvs(struct _sc_ctx_options *opts) } } +static void add_old_drvs(struct _sc_ctx_options *opts) +{ + const struct _sc_driver_entry *lst; + int i; + + lst = old_card_drivers; + i = 0; + while (lst[i].name != NULL) { + add_drv(opts, lst[i].name); + i++; + } +} + static void set_defaults(sc_context_t *ctx, struct _sc_ctx_options *opts) { ctx->debug = 0; @@ -263,13 +324,6 @@ static void set_defaults(sc_context_t *ctx, struct _sc_ctx_options *opts) fclose(ctx->debug_file); ctx->debug_file = stderr; ctx->flags = 0; - -#ifdef __APPLE__ - /* Override the default debug log for OpenSC.tokend to be different from PKCS#11. - * TODO: Could be moved to OpenSC.tokend */ - if (!strcmp(ctx->app_name, "tokend")) - ctx->debug_file = fopen("/tmp/opensc-tokend.log", "a"); -#endif ctx->forced_driver = NULL; add_internal_drvs(opts); } @@ -286,12 +340,10 @@ int sc_ctx_log_to_file(sc_context_t *ctx, const char* filename) ctx->debug_file = NULL; } - if (ctx->reopen_log_file) { - if (!ctx->debug_filename) { - if (!filename) - filename = "stderr"; - ctx->debug_filename = strdup(filename); - } + if (!ctx->debug_filename) { + if (!filename) + filename = "stderr"; + ctx->debug_filename = strdup(filename); } if (!filename) @@ -310,25 +362,36 @@ int sc_ctx_log_to_file(sc_context_t *ctx, const char* filename) return SC_SUCCESS; } +static void +set_drivers(struct _sc_ctx_options *opts, const scconf_list *list) +{ + const char *s_internal = "internal", *s_old = "old"; + if (list != NULL) + del_drvs(opts); + while (list != NULL) { + if (strcmp(list->data, s_internal) == 0) + add_internal_drvs(opts); + else if (strcmp(list->data, s_old) == 0) + add_old_drvs(opts); + else + add_drv(opts, list->data); + list = list->next; + } +} static int load_parameters(sc_context_t *ctx, scconf_block *block, struct _sc_ctx_options *opts) { int err = 0; const scconf_list *list; - const char *val, *s_internal = "internal"; + const char *val; int debug; + const char *disable_hw_pkcs1_padding; #ifdef _WIN32 char expanded_val[PATH_MAX]; DWORD expanded_len; #endif -#ifdef _WIN32 - ctx->reopen_log_file = 1; -#else - ctx->reopen_log_file = scconf_get_bool(block, "reopen_debug_file", 0); -#endif - debug = scconf_get_int(block, "debug", ctx->debug); if (debug > ctx->debug) ctx->debug = debug; @@ -347,35 +410,41 @@ load_parameters(sc_context_t *ctx, scconf_block *block, struct _sc_ctx_options * sc_ctx_log_to_file(ctx, NULL); } - if (scconf_get_bool (block, "paranoid-memory", - ctx->flags & SC_CTX_FLAG_PARANOID_MEMORY)) - ctx->flags |= SC_CTX_FLAG_PARANOID_MEMORY; - if (scconf_get_bool (block, "disable_popups", ctx->flags & SC_CTX_FLAG_DISABLE_POPUPS)) ctx->flags |= SC_CTX_FLAG_DISABLE_POPUPS; + if (scconf_get_bool (block, "disable_colors", + ctx->flags & SC_CTX_FLAG_DISABLE_COLORS)) + ctx->flags |= SC_CTX_FLAG_DISABLE_COLORS; + if (scconf_get_bool (block, "enable_default_driver", ctx->flags & SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER)) ctx->flags |= SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER; - val = scconf_get_str(block, "force_card_driver", NULL); - if (val) { - if (opts->forced_card_driver) - free(opts->forced_card_driver); - opts->forced_card_driver = strdup(val); + list = scconf_find_list(block, "card_drivers"); + set_drivers(opts, list); + + /* Disable PKCS#1 v1.5 type 2 (for decryption) depadding on card by default */ + disable_hw_pkcs1_padding = "decipher"; + ctx->disable_hw_pkcs1_padding = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02; + disable_hw_pkcs1_padding = scconf_get_str(block, "disable_hw_pkcs1_padding", disable_hw_pkcs1_padding); + if (0 == strcmp(disable_hw_pkcs1_padding, "no")) { + ctx->disable_hw_pkcs1_padding = 0; + } else if (0 == strcmp(disable_hw_pkcs1_padding, "sign")) { + ctx->disable_hw_pkcs1_padding = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01; + } else if (0 == strcmp(disable_hw_pkcs1_padding, "decipher")) { + ctx->disable_hw_pkcs1_padding = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02; + } else if (0 == strcmp(disable_hw_pkcs1_padding, "both")) { + ctx->disable_hw_pkcs1_padding = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02; } - list = scconf_find_list(block, "card_drivers"); - if (list != NULL) - del_drvs(opts); - while (list != NULL) { - if (strcmp(list->data, s_internal) == 0) - add_internal_drvs(opts); - else - add_drv(opts, list->data); - list = list->next; +#ifdef USE_OPENSSL3_LIBCTX + val = scconf_get_str(block, "openssl_config", NULL); + if (val != NULL) { + ctx->openssl_config = strdup(val); } +#endif return err; } @@ -429,7 +498,11 @@ static void *load_dynamic_driver(sc_context_t *ctx, void **dll, const char *name const char *(*modversion)(void) = NULL; const char *(**tmodv)(void) = &modversion; - if (name == NULL) { /* should not occurr, but... */ + if (dll == NULL) { + sc_log(ctx, "No dll parameter specified"); + return NULL; + } + if (name == NULL) { /* should not occur, but... */ sc_log(ctx, "No module specified"); return NULL; } @@ -458,8 +531,8 @@ static void *load_dynamic_driver(sc_context_t *ctx, void **dll, const char *name sc_dlclose(handle); return NULL; } - if (dll) - *dll = handle; + + *dll = handle; sc_log(ctx, "successfully loaded card driver '%s'", name); return modinit(name); } @@ -507,11 +580,20 @@ static int load_card_drivers(sc_context_t *ctx, struct _sc_ctx_options *opts) } ent = &opts->cdrv[i]; - for (j = 0; internal_card_drivers[j].name != NULL; j++) + for (j = 0; internal_card_drivers[j].name != NULL; j++) { if (strcmp(ent->name, internal_card_drivers[j].name) == 0) { func = (struct sc_card_driver *(*)(void)) internal_card_drivers[j].func; break; } + } + if (func == NULL) { + for (j = 0; old_card_drivers[j].name != NULL; j++) { + if (strcmp(ent->name, old_card_drivers[j].name) == 0) { + func = (struct sc_card_driver *(*)(void)) old_card_drivers[j].func; + break; + } + } + } /* if not initialized assume external module */ if (func == NULL) *(void **)(tfunc) = load_dynamic_driver(ctx, &dll, ent->name); @@ -603,6 +685,8 @@ static int load_card_atrs(sc_context_t *ctx) if (!strcmp(list->data, "rng")) flags = SC_CARD_FLAG_RNG; + else if (!strcmp(list->data, "keep_alive")) + flags = SC_CARD_FLAG_KEEP_ALIVE; else if (sscanf(list->data, "%x", &flags) != 1) flags = 0; @@ -635,13 +719,14 @@ static void process_config_file(sc_context_t *ctx, struct _sc_ctx_options *opts) memset(ctx->conf_blocks, 0, sizeof(ctx->conf_blocks)); #ifdef _WIN32 - temp_len = PATH_MAX; - r = sc_ctx_win32_get_config_value("OPENSC_CONF", "ConfigFile", "Software\\OpenSC Project\\OpenSC", - temp_path, &temp_len); + temp_len = PATH_MAX-1; + r = sc_ctx_win32_get_config_value("OPENSC_CONF", "ConfigFile", "Software\\" OPENSC_VS_FF_COMPANY_NAME "\\OpenSC" OPENSC_ARCH_SUFFIX, + temp_path, &temp_len); if (r) { sc_log(ctx, "process_config_file doesn't find opensc config file. Please set the registry key."); return; } + temp_path[temp_len] = '\0'; conf_path = temp_path; #else conf_path = getenv("OPENSC_CONF"); @@ -669,6 +754,12 @@ static void process_config_file(sc_context_t *ctx, struct _sc_ctx_options *opts) ctx->conf = NULL; return; } + /* needs to be after the log file is known */ + sc_log(ctx, "Used configuration file '%s'", conf_path); + blocks = scconf_find_blocks(ctx->conf, NULL, "app", ctx->exe_path); + if (blocks && blocks[0]) + ctx->conf_blocks[count++] = blocks[0]; + free(blocks); blocks = scconf_find_blocks(ctx->conf, NULL, "app", ctx->app_name); if (blocks && blocks[0]) ctx->conf_blocks[count++] = blocks[0]; @@ -679,7 +770,7 @@ static void process_config_file(sc_context_t *ctx, struct _sc_ctx_options *opts) ctx->conf_blocks[count] = blocks[0]; free(blocks); } - /* Above we add 2 blocks at most, but conf_blocks has 3 elements, + /* Above we add 3 blocks at most, but conf_blocks has 4 elements, * so at least one is NULL */ for (i = 0; ctx->conf_blocks[i]; i++) load_parameters(ctx, ctx->conf_blocks[i], opts); @@ -748,11 +839,113 @@ int sc_context_repair(sc_context_t **ctx_out) return SC_SUCCESS; } +#ifdef USE_OPENSSL3_LIBCTX +static int sc_openssl3_init(sc_context_t *ctx) +{ + ctx->ossl3ctx = calloc(1, sizeof(ossl3ctx_t)); + if (ctx->ossl3ctx == NULL) { + return SC_ERROR_OUT_OF_MEMORY; + } + + ctx->ossl3ctx->libctx = OSSL_LIB_CTX_new(); + if (ctx->ossl3ctx->libctx == NULL) { + return SC_ERROR_INTERNAL; + } + + if (ctx->openssl_config != NULL) { + if (access(ctx->openssl_config, R_OK) != 0) { + sc_log(ctx, "Warning: provided OpenSSL configuration file '%s' is not readable", + ctx->openssl_config); + } else { + /* + * Load OpenSC specific openssl config file to configure FIPS module + * (or whatever else the user needs). + * We assume that this config file will automatically activate the FIPS + * and base providers so we don't need to explicitly load them here. + */ + if (!OSSL_LIB_CTX_load_config(ctx->ossl3ctx->libctx, ctx->openssl_config)) { + return SC_ERROR_INTERNAL; + } + } + } else { + /* We do not have configuration file specified: load the default + * and legacy providers */ + const char *defprov_name = "default"; + if (FIPS_mode()) { + defprov_name = "fips"; + } + ctx->ossl3ctx->defprov = OSSL_PROVIDER_load(ctx->ossl3ctx->libctx, defprov_name); + if (ctx->ossl3ctx->defprov == NULL) { + OSSL_LIB_CTX_free(ctx->ossl3ctx->libctx); + free(ctx->ossl3ctx); + ctx->ossl3ctx = NULL; + return SC_ERROR_INTERNAL; + } + /* yes, legacy -- smart cards depend on several legacy algorithms */ + ctx->ossl3ctx->legacyprov = OSSL_PROVIDER_load(ctx->ossl3ctx->libctx, "legacy"); + if (ctx->ossl3ctx->legacyprov == NULL) { + sc_log(ctx, "Failed to load OpenSSL Legacy provider"); + } + } + return SC_SUCCESS; +} + +static void sc_openssl3_deinit(sc_context_t *ctx) +{ + if (ctx->ossl3ctx == NULL) + return; + if (ctx->ossl3ctx->legacyprov) + OSSL_PROVIDER_unload(ctx->ossl3ctx->legacyprov); + if (ctx->ossl3ctx->defprov) + OSSL_PROVIDER_unload(ctx->ossl3ctx->defprov); + if (ctx->ossl3ctx->libctx) + OSSL_LIB_CTX_free(ctx->ossl3ctx->libctx); + free(ctx->ossl3ctx); + ctx->ossl3ctx = NULL; +} +#endif + +static char *get_exe_path() +{ + /* Find the executable's path which runs this code. + * See https://github.com/gpakosz/whereami/ for + * potentially more platforms */ + char exe_path[PATH_MAX] = "unknown executable path"; + int path_found = 0; + +#if defined(_WIN32) + if (0 < GetModuleFileNameA(NULL, exe_path, sizeof exe_path)) + path_found = 1; +#elif defined(__APPLE__) + if (0 < proc_pidpath(getpid(), exe_path, sizeof exe_path)) + path_found = 1; +#elif defined(__linux__) || defined(__CYGWIN__) + if (NULL != realpath("/proc/self/exe", exe_path)) + path_found = 1; +#endif + +#if defined(HAVE_GETPROGNAME) + if (!path_found) { + /* getprogname is unreliable and typically only returns the basename. + * However, this should be enough for our purposes */ + const char *prog = getprogname(); + if (prog) + strlcpy(exe_path, prog, sizeof exe_path); + } +#else + /* avoid warning "set but not used" */ + (void) path_found; +#endif + + return strdup(exe_path); +} + int sc_context_create(sc_context_t **ctx_out, const sc_context_param_t *parm) { sc_context_t *ctx; struct _sc_ctx_options opts; int r; + char *driver; if (ctx_out == NULL || parm == NULL) return SC_ERROR_INVALID_ARGUMENTS; @@ -772,10 +965,18 @@ int sc_context_create(sc_context_t **ctx_out, const sc_context_param_t *parm) return SC_ERROR_OUT_OF_MEMORY; } + ctx->exe_path = get_exe_path(); + if (ctx->exe_path == NULL) { + sc_release_context(ctx); + return SC_ERROR_OUT_OF_MEMORY; + } + ctx->flags = parm->flags; set_defaults(ctx, &opts); if (0 != list_init(&ctx->readers)) { + del_drvs(&opts); + sc_release_context(ctx); return SC_ERROR_OUT_OF_MEMORY; } list_attributes_seeker(&ctx->readers, reader_list_seeker); @@ -784,13 +985,41 @@ int sc_context_create(sc_context_t **ctx_out, const sc_context_param_t *parm) ctx->thread_ctx = parm->thread_ctx; r = sc_mutex_create(ctx, &ctx->mutex); if (r != SC_SUCCESS) { + del_drvs(&opts); sc_release_context(ctx); return r; } +#if defined(ENABLE_OPENSSL) && defined(OPENSSL_SECURE_MALLOC_SIZE) && !defined(LIBRESSL_VERSION_NUMBER) + if (!CRYPTO_secure_malloc_initialized()) { + CRYPTO_secure_malloc_init(OPENSSL_SECURE_MALLOC_SIZE, OPENSSL_SECURE_MALLOC_SIZE/8); + } +#endif + process_config_file(ctx, &opts); + + /* overwrite with caller's parameters if explicitly given */ + if (parm->debug) { + ctx->debug = parm->debug; + } + if (parm->debug_file) { + if (ctx->debug_file && (ctx->debug_file != stderr && ctx->debug_file != stdout)) + fclose(ctx->debug_file); + ctx->debug_file = parm->debug_file; + } + sc_log(ctx, "==================================="); /* first thing in the log */ - sc_log(ctx, "opensc version: %s", sc_get_version()); + sc_log(ctx, "OpenSC version: %s", sc_get_version()); + sc_log(ctx, "Configured for %s (%s)", ctx->app_name, ctx->exe_path); + +#ifdef USE_OPENSSL3_LIBCTX + r = sc_openssl3_init(ctx); + if (r != SC_SUCCESS) { + del_drvs(&opts); + sc_release_context(ctx); + return r; + } +#endif #ifdef ENABLE_PCSC ctx->reader_driver = sc_get_pcsc_driver(); @@ -800,28 +1029,28 @@ int sc_context_create(sc_context_t **ctx_out, const sc_context_param_t *parm) ctx->reader_driver = sc_get_ctapi_driver(); #elif defined(ENABLE_OPENCT) ctx->reader_driver = sc_get_openct_driver(); +#else + ctx->reader_driver = sc_get_empty_driver(); #endif r = ctx->reader_driver->ops->init(ctx); if (r != SC_SUCCESS) { + del_drvs(&opts); sc_release_context(ctx); return r; } + driver = getenv("OPENSC_DRIVER"); + if (driver) { + scconf_list *list = NULL; + scconf_list_add(&list, driver); + set_drivers(&opts, list); + scconf_list_destroy(list); + } + load_card_drivers(ctx, &opts); load_card_atrs(ctx); - if (!opts.forced_card_driver) { - char *driver = getenv("OPENSC_DRIVER"); - if(driver) { - opts.forced_card_driver = strdup(driver); - } - } - if (opts.forced_card_driver) { - if (SC_SUCCESS != sc_set_card_driver(ctx, opts.forced_card_driver)) - sc_log(ctx, "Warning: Could not load %s.", opts.forced_card_driver); - free(opts.forced_card_driver); - } del_drvs(&opts); sc_ctx_detect_readers(ctx); *ctx_out = ctx; @@ -832,7 +1061,7 @@ int sc_context_create(sc_context_t **ctx_out, const sc_context_param_t *parm) /* Used by minidriver to pass in provided handles to reader-pcsc */ int sc_ctx_use_reader(sc_context_t *ctx, void *pcsc_context_handle, void *pcsc_card_handle) { - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); if (ctx->reader_driver->ops->use_reader != NULL) return ctx->reader_driver->ops->use_reader(ctx, pcsc_context_handle, pcsc_card_handle); @@ -842,7 +1071,7 @@ int sc_ctx_use_reader(sc_context_t *ctx, void *pcsc_context_handle, void *pcsc_c /* Following two are only implemented with internal PC/SC and don't consume a reader object */ int sc_cancel(sc_context_t *ctx) { - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); if (ctx->reader_driver->ops->cancel != NULL) return ctx->reader_driver->ops->cancel(ctx); @@ -852,14 +1081,13 @@ int sc_cancel(sc_context_t *ctx) int sc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_reader_t **event_reader, unsigned int *event, int timeout, void **reader_states) { - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); if (ctx->reader_driver->ops->wait_for_event != NULL) return ctx->reader_driver->ops->wait_for_event(ctx, event_mask, event_reader, event, timeout, reader_states); return SC_ERROR_NOT_SUPPORTED; } - int sc_release_context(sc_context_t *ctx) { unsigned int i; @@ -873,7 +1101,7 @@ int sc_release_context(sc_context_t *ctx) _sc_delete_reader(ctx, rdr); } - if (ctx->reader_driver->ops->finish != NULL) + if (ctx->reader_driver != NULL && ctx->reader_driver->ops->finish != NULL) ctx->reader_driver->ops->finish(ctx); for (i = 0; ctx->card_drivers[i]; i++) { @@ -884,6 +1112,9 @@ int sc_release_context(sc_context_t *ctx) if (drv->dll) sc_dlclose(drv->dll); } +#ifdef USE_OPENSSL3_LIBCTX + sc_openssl3_deinit(ctx); +#endif if (ctx->preferred_language != NULL) free(ctx->preferred_language); if (ctx->mutex != NULL) { @@ -897,10 +1128,9 @@ int sc_release_context(sc_context_t *ctx) scconf_free(ctx->conf); if (ctx->debug_file && (ctx->debug_file != stdout && ctx->debug_file != stderr)) fclose(ctx->debug_file); - if (ctx->debug_filename != NULL) - free(ctx->debug_filename); - if (ctx->app_name != NULL) - free(ctx->app_name); + free(ctx->debug_filename); + free(ctx->app_name); + free(ctx->exe_path); list_destroy(&ctx->readers); sc_mem_clear(ctx, sizeof(*ctx)); free(ctx); @@ -942,14 +1172,21 @@ int sc_get_cache_dir(sc_context_t *ctx, char *buf, size_t bufsize) conf_block = sc_get_conf_block(ctx, "framework", "pkcs15", 1); cache_dir = scconf_get_str(conf_block, "file_cache_dir", NULL); if (cache_dir != NULL) { - if (bufsize <= strlen(cache_dir)) - return SC_ERROR_BUFFER_TOO_SMALL; - strcpy(buf, cache_dir); + strlcpy(buf, cache_dir, bufsize); return SC_SUCCESS; } #ifndef _WIN32 - cache_dir = ".eid/cache"; +#ifdef __APPLE__ + cache_dir = getenv("Caches"); +#else + cache_dir = getenv("XDG_CACHE_HOME"); +#endif + if (cache_dir != NULL && cache_dir[0] != '\0') { + snprintf(buf, bufsize, "%s/%s", cache_dir, "opensc"); + return SC_SUCCESS; + } + cache_dir = ".cache/opensc"; homedir = getenv("HOME"); #else cache_dir = "eid-cache"; @@ -961,7 +1198,7 @@ int sc_get_cache_dir(sc_context_t *ctx, char *buf, size_t bufsize) homedir = temp_path; } #endif - if (homedir == NULL) + if (homedir == NULL || homedir[0] == '\0') return SC_ERROR_INTERNAL; if (snprintf(buf, bufsize, "%s/%s", homedir, cache_dir) < 0) return SC_ERROR_BUFFER_TOO_SMALL; diff --git a/src/libopensc/cwa-dnie.c b/src/libopensc/cwa-dnie.c index be2154c823..6020921dbc 100644 --- a/src/libopensc/cwa-dnie.c +++ b/src/libopensc/cwa-dnie.c @@ -19,7 +19,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __SM_DNIE_C__ @@ -44,15 +44,19 @@ #include #include #include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# include +# include +#endif #define MAX_RESP_BUFFER_SIZE 2048 /********************* Keys and certificates as published by DGP ********/ /** - * Modulo de la clave pública de la Root CA del DNIe electronico + * Public Key modulus for the ROOT CA for DNIe (pk-RCAicc->n) */ -static u8 icc_root_ca_modulus[] = { +static u8 icc_root_ca_modulus_0[] = { 0xEA, 0xDE, 0xDA, 0x45, 0x53, 0x32, 0x94, 0x50, 0x39, 0xDA, 0xA4, 0x04, 0xC8, 0xEB, 0xC4, 0xD3, 0xB7, 0xF5, 0xDC, 0x86, 0x92, 0x83, 0xCD, 0xEA, 0x2F, 0x10, 0x1E, 0x2A, 0xB5, 0x4F, 0xB0, 0xD0, 0xB0, 0x3D, 0x8F, 0x03, @@ -66,17 +70,31 @@ static u8 icc_root_ca_modulus[] = { 0x91, 0xDB, 0x64, 0xF8, 0x0B, 0x5E, 0x92, 0xCD }; +static u8 icc_root_ca_modulus_1[] = { + 0xb9, 0x72, 0x34, 0x5e, 0x35, 0xbc, 0xdd, 0x12, 0xdc, 0x2c, 0x8e, 0x85, + 0xf6, 0x22, 0x97, 0x97, 0x9f, 0x12, 0x2b, 0xb7, 0xc9, 0xc3, 0xed, 0x13, + 0xa0, 0xc4, 0xeb, 0x59, 0x34, 0xe7, 0x0c, 0xd6, 0xd0, 0x0c, 0x54, 0x06, + 0x18, 0x38, 0x6e, 0x42, 0xf2, 0xba, 0x00, 0x89, 0xc0, 0xdd, 0x80, 0x0e, + 0xba, 0x78, 0x3b, 0xdc, 0x9d, 0x93, 0xd9, 0xfb, 0xfc, 0x3c, 0x16, 0x9f, + 0x9a, 0xf6, 0x4e, 0x80, 0x10, 0x0f, 0xc6, 0x87, 0xcc, 0xa5, 0x62, 0xe7, + 0xfc, 0x84, 0xd1, 0x12, 0x92, 0xc2, 0x40, 0x4c, 0x59, 0xb8, 0xa8, 0x60, + 0xd3, 0x9e, 0x2d, 0x66, 0x54, 0x7d, 0xc7, 0xb2, 0xd4, 0x8c, 0xa7, 0x89, + 0x81, 0x4f, 0x43, 0x06, 0x26, 0x34, 0xe3, 0xe0, 0xc0, 0xd6, 0xbf, 0x5f, + 0x54, 0xba, 0x1d, 0x9c, 0x46, 0x64, 0x45, 0x83, 0x1d, 0xcd, 0xea, 0xb0, + 0x87, 0x08, 0xf3, 0xf6, 0x22, 0x0e, 0x07, 0x75 +}; + /** - * Exponente de la clave publica de la Root CA del DNI electronico + * Exponente de la clave publica de la Root CA del DNI electronico (pk-RCAicc->e) */ static u8 icc_root_ca_public_exponent[] = { 0x01, 0x00, 0x01 }; /** - * Terminal (IFD) key modulus for SM channel creation + * Terminal (IFD) key modulus for SM channel creation (dnieRealParam->sk-IFD-AUT->n) */ -static u8 ifd_modulus[] = { +static u8 ifd_modulus_0[] = { 0xdb, 0x2c, 0xb4, 0x1e, 0x11, 0x2b, 0xac, 0xfa, 0x2b, 0xd7, 0xc3, 0xd3, 0xd7, 0x96, 0x7e, 0x84, 0xfb, 0x94, 0x34, 0xfc, 0x26, 0x1f, 0x9d, 0x09, 0x0a, 0x89, 0x83, 0x94, 0x7d, 0xaf, 0x84, 0x88, 0xd3, 0xdf, 0x8f, 0xbd, @@ -90,10 +108,24 @@ static u8 ifd_modulus[] = { 0x6a, 0xe2, 0x36, 0x59, 0x00, 0x16, 0xba, 0x69 }; +static u8 ifd_modulus_1[] = { + 0xbd, 0xef, 0xdb, 0x84, 0xec, 0xe6, 0x98, 0xb8, 0x28, 0x7f, 0x7f, 0xe6, + 0x29, 0x6d, 0x80, 0x72, 0x98, 0x3a, 0x1b, 0x3d, 0x3b, 0x9f, 0x57, 0xad, + 0x98, 0x4f, 0xba, 0x78, 0x58, 0x1f, 0xff, 0x52, 0xe9, 0x3d, 0x89, 0x6b, + 0xf5, 0x62, 0x25, 0xe9, 0xf8, 0x2e, 0x96, 0x95, 0x14, 0x00, 0x69, 0x98, + 0x2e, 0x5b, 0x5b, 0xce, 0x37, 0xad, 0x73, 0x16, 0x45, 0x02, 0xd8, 0xac, + 0xbd, 0x60, 0x5f, 0x69, 0x12, 0x4a, 0x3c, 0xf5, 0xaf, 0xe4, 0xb0, 0x18, + 0x60, 0x2d, 0xd4, 0xba, 0x04, 0xdb, 0xc9, 0x85, 0x88, 0x45, 0xe6, 0xa9, + 0xc4, 0x05, 0x5b, 0xc5, 0xbf, 0xa0, 0xed, 0xdb, 0x86, 0x67, 0x89, 0xf0, + 0xec, 0x6a, 0x80, 0xfc, 0xe5, 0x3c, 0x66, 0x08, 0xdf, 0xdc, 0x9b, 0x9f, + 0xe2, 0xed, 0x56, 0x75, 0x2c, 0xc6, 0x05, 0x51, 0x3b, 0xa3, 0xf1, 0x75, + 0x9c, 0xdd, 0x95, 0x22, 0x75, 0x3f, 0x18, 0xd7 +}; + /** - * Terminal (IFD) key modulus for SM channel creation for PIN channel DNIe 3.0 + * Terminal (IFD) key modulus for SM channel creation for PIN channel DNIe 3.0 (dnie30RealParamPIN->sk-IFD-AUT->n) */ -static u8 ifd_pin_modulus[] = { +static u8 ifd_pin_modulus_0[] = { 0xF4, 0x27, 0x97, 0x8D, 0xA1, 0x59, 0xBA, 0x02, 0x79, 0x30, 0x8A, 0x6C, 0x6A, 0x89, 0x50, 0x5A, 0xDA, 0x5A, 0x67, 0xC3, 0xDA, 0x26, 0x79, 0xEA, 0xF4, 0xA1, 0xB0, 0x11, 0x9E, 0xDD, 0x4D, 0xF4, 0x6E, 0x78, 0x04, 0x24, @@ -107,6 +139,20 @@ static u8 ifd_pin_modulus[] = { 0x15, 0x96, 0x6A, 0x5A, 0x89, 0xAD, 0x58, 0xA5 }; +static u8 ifd_pin_modulus_1[] = { + 0xdf, 0x03, 0x93, 0x0d, 0x4f, 0x1d, 0x97, 0x15, 0xeb, 0xb0, 0x0f, 0xbd, + 0xae, 0x48, 0xaf, 0x9c, 0x9d, 0xbf, 0xd6, 0x99, 0xca, 0xb0, 0xbd, 0xbe, + 0x5c, 0xdb, 0x01, 0x34, 0x00, 0x0e, 0x46, 0x2e, 0x71, 0x3a, 0xe9, 0x7a, + 0x2f, 0x7e, 0x20, 0xaf, 0xbf, 0x84, 0xd3, 0xce, 0x73, 0x4f, 0xe2, 0x15, + 0x75, 0x7a, 0xaf, 0xa1, 0xe8, 0x9e, 0x64, 0x57, 0xea, 0xe2, 0xe8, 0x08, + 0x11, 0x03, 0x73, 0xe2, 0x56, 0x56, 0x34, 0x94, 0xfb, 0x5d, 0x10, 0x4f, + 0x0d, 0xcc, 0x88, 0x8d, 0x47, 0x96, 0x54, 0x3f, 0x03, 0x25, 0x4f, 0x4e, + 0x2c, 0xdf, 0x98, 0xb1, 0xe1, 0x26, 0x11, 0xe3, 0x98, 0x1f, 0x53, 0x33, + 0xdf, 0x98, 0xc8, 0x86, 0x01, 0x93, 0x75, 0x84, 0x0f, 0xac, 0x61, 0xdb, + 0x8f, 0x1b, 0xa3, 0xb5, 0x43, 0xdc, 0xea, 0x3d, 0x05, 0x9e, 0x6a, 0x41, + 0x4f, 0x6d, 0xd2, 0x9f, 0xc7, 0xc9, 0x9d, 0x8b +}; + /** * Terminal (IFD) public exponent for SM channel creation */ @@ -122,9 +168,9 @@ static u8 ifd_pin_public_exponent[] = { }; /** - * Terminal (IFD) private exponent for SM channel establishment + * Terminal (IFD) private exponent for SM channel establishment (dnieRealParam->sk-IFD-AUT->d) */ -static u8 ifd_private_exponent[] = { +static u8 ifd_private_exponent_0[] = { 0x18, 0xb4, 0x4a, 0x3d, 0x15, 0x5c, 0x61, 0xeb, 0xf4, 0xe3, 0x26, 0x1c, 0x8b, 0xb1, 0x57, 0xe3, 0x6f, 0x63, 0xfe, 0x30, 0xe9, 0xaf, 0x28, 0x89, 0x2b, 0x59, 0xe2, 0xad, 0xeb, 0x18, 0xcc, 0x8c, 0x8b, 0xad, 0x28, 0x4b, @@ -138,10 +184,24 @@ static u8 ifd_private_exponent[] = { 0xbd, 0x9b, 0x00, 0x31, 0x3c, 0x0f, 0x46, 0xed }; +static u8 ifd_private_exponent_1[] = { + 0xa0, 0x51, 0x55, 0x93, 0xd4, 0x36, 0x2b, 0x8f, 0xbd, 0xb7, 0x28, 0xa8, + 0x88, 0x2d, 0x42, 0x2e, 0xf7, 0xa8, 0x8c, 0x17, 0x5a, 0x3c, 0xfb, 0xcf, + 0xad, 0xf1, 0x15, 0xee, 0xc0, 0x4b, 0x79, 0xc2, 0x6c, 0xd6, 0xa1, 0x28, + 0xbb, 0xbd, 0x35, 0x4d, 0x50, 0x4b, 0x5a, 0x94, 0xc8, 0x86, 0x34, 0x9a, + 0xdb, 0xfe, 0x06, 0xf6, 0x7f, 0xee, 0x6a, 0x66, 0xd0, 0xa7, 0x3f, 0x66, + 0x46, 0x8e, 0x92, 0xd8, 0x73, 0xb6, 0x8e, 0xe2, 0xcb, 0x47, 0xb1, 0xa1, + 0x5a, 0x2a, 0xa7, 0xd8, 0xc6, 0xce, 0x8f, 0x3f, 0x14, 0x93, 0x0d, 0x56, + 0xb6, 0x32, 0x7f, 0x56, 0xcb, 0x21, 0x54, 0x69, 0xa5, 0x7a, 0x1e, 0xe0, + 0x18, 0x8f, 0xd6, 0xd2, 0x6d, 0x83, 0xa3, 0x80, 0xa6, 0xab, 0xd3, 0xa8, + 0x9f, 0x1b, 0x63, 0xc4, 0x99, 0x81, 0x90, 0x46, 0x53, 0x69, 0x35, 0xad, + 0xb2, 0xdb, 0x3c, 0x17, 0xcc, 0xbd, 0xaa, 0x51 +}; + /** - * Terminal (IFD) private exponent for SM channel establishment for PIN channel DNIe 3.0 + * Terminal (IFD) private exponent for SM channel establishment for PIN channel DNIe 3.0 (dnie30RealParamDataPIN->sk-IFD-AUT->d) */ -static u8 ifd_pin_private_exponent[] = { +static u8 ifd_pin_private_exponent_0[] = { 0xD2, 0x7A, 0x03, 0x23, 0x7C, 0x72, 0x2E, 0x71, 0x8D, 0x69, 0xF4, 0x1A, 0xEC, 0x68, 0xBD, 0x95, 0xE4, 0xE0, 0xC4, 0xCD, 0x49, 0x15, 0x9C, 0x4A, 0x99, 0x63, 0x7D, 0xB6, 0x62, 0xFE, 0xA3, 0x02, 0x51, 0xED, 0x32, 0x9C, @@ -155,10 +215,24 @@ static u8 ifd_pin_private_exponent[] = { 0xC6, 0x3B, 0x35, 0x8B, 0x7C, 0x11, 0x5A, 0xA1 }; +static u8 ifd_pin_private_exponent_1[] = { + 0x86, 0x6f, 0x0f, 0x2c, 0x0c, 0xaf, 0x17, 0xae, 0x7d, 0x1e, 0xea, 0xbe, + 0x3a, 0xdb, 0x52, 0x11, 0x24, 0xfe, 0xc9, 0x8c, 0x77, 0xa4, 0xc7, 0x1c, + 0x83, 0xb8, 0xf9, 0x26, 0xb1, 0x89, 0xe9, 0x40, 0x81, 0xbd, 0x33, 0x95, + 0x16, 0x1f, 0xff, 0xf0, 0x31, 0x91, 0x0e, 0x64, 0xfb, 0x1a, 0x02, 0x7d, + 0x51, 0x0e, 0x1d, 0xe5, 0x89, 0xe6, 0x41, 0x32, 0xc6, 0x42, 0xf6, 0x00, + 0x36, 0xd1, 0x4f, 0xfe, 0xd5, 0xd0, 0xce, 0x1f, 0x45, 0xe7, 0x11, 0x6f, + 0x13, 0xc4, 0xe6, 0x38, 0x8e, 0x25, 0xdd, 0x43, 0x83, 0x57, 0x78, 0x05, + 0x85, 0x73, 0xdc, 0x29, 0xad, 0x6a, 0x37, 0x32, 0x71, 0x6d, 0x08, 0x11, + 0x24, 0xb7, 0x52, 0x51, 0x40, 0xb1, 0xdd, 0xab, 0xe2, 0x51, 0xa4, 0x98, + 0x0c, 0xc5, 0xc0, 0x3a, 0x86, 0xa8, 0x2d, 0x17, 0x4f, 0xb7, 0xa8, 0x1d, + 0x24, 0x8d, 0x7c, 0xaa, 0x2b, 0x3d, 0x61, 0xd1 +}; + /** - * Intermediate CA certificate in CVC format (Card verifiable certificate) + * Intermediate CA certificate in CVC format (Card verifiable certificate) (c-CV-CA-CS-AUT) */ -static u8 C_CV_CA_CS_AUT_cert[] = { +static u8 C_CV_CA_CS_AUT_cert_0[] = { 0x7f, 0x21, 0x81, 0xce, 0x5f, 0x37, 0x81, 0x80, 0x3c, 0xba, 0xdc, 0x36, 0x84, 0xbe, 0xf3, 0x20, 0x41, 0xad, 0x15, 0x50, 0x89, 0x25, 0x8d, 0xfd, 0x20, 0xc6, 0x91, 0x15, 0xd7, 0x2f, 0x9c, 0x38, 0xaa, 0x99, 0xad, 0x6c, @@ -179,10 +253,31 @@ static u8 C_CV_CA_CS_AUT_cert[] = { 0x52, 0x44, 0x49, 0x60, 0x00, 0x06 }; +static u8 C_CV_CA_CS_AUT_cert_1[] = { + 0x7f, 0x21, 0x81, 0xce, 0x5f, 0x37, 0x81, 0x80, 0x7a, 0xa0, 0x6c, 0x96, + 0x5e, 0x8f, 0xb2, 0x19, 0x61, 0xcf, 0xd4, 0x49, 0xd0, 0x9b, 0x9d, 0xaf, + 0x03, 0x04, 0x73, 0x01, 0x15, 0x69, 0x70, 0xb7, 0x73, 0xf1, 0x9c, 0x40, + 0xf1, 0x27, 0xd3, 0x38, 0xe3, 0xc1, 0x35, 0xeb, 0x21, 0x20, 0x56, 0x6d, + 0xc6, 0xf9, 0xf7, 0x45, 0xff, 0xb8, 0xf8, 0xe2, 0xb6, 0x1e, 0xe8, 0x16, + 0x6f, 0xfd, 0x06, 0xd2, 0x8c, 0xb4, 0x8c, 0x15, 0x2a, 0x1f, 0xa4, 0xf7, + 0xe9, 0xf6, 0x09, 0xd7, 0x52, 0x76, 0x33, 0x1c, 0xb7, 0x00, 0xb8, 0x4e, + 0x36, 0xac, 0x8a, 0x0a, 0x77, 0x74, 0x46, 0x8c, 0x3c, 0xf3, 0xd1, 0x47, + 0xa4, 0x9c, 0x97, 0x6e, 0x17, 0xab, 0x02, 0xda, 0x03, 0xea, 0x4a, 0xc1, + 0x51, 0x77, 0x7e, 0xdf, 0xbc, 0x35, 0xc2, 0x7d, 0x56, 0xfb, 0xa6, 0x85, + 0x75, 0x6e, 0xd6, 0x52, 0x85, 0x1d, 0xfd, 0xe7, 0x01, 0xbf, 0x87, 0x49, + 0x92, 0xdd, 0x4d, 0xe8, 0x5f, 0x38, 0x3d, 0x33, 0xe3, 0xd5, 0x2a, 0x4b, + 0x09, 0x40, 0xe3, 0x90, 0xcd, 0x1a, 0x64, 0x1f, 0xea, 0x2e, 0x9c, 0xdd, + 0x79, 0xd3, 0x87, 0x2d, 0xd6, 0xc5, 0x08, 0xd5, 0xef, 0x23, 0x9c, 0xb0, + 0x7e, 0xb5, 0x55, 0x68, 0xce, 0x18, 0x8b, 0x65, 0x13, 0xac, 0xb8, 0x84, + 0x14, 0xc9, 0xad, 0xf7, 0xa6, 0x4e, 0x2c, 0xc0, 0xb3, 0x14, 0xd1, 0x27, + 0x54, 0xae, 0xee, 0x67, 0x00, 0x01, 0x00, 0x01, 0x42, 0x08, 0x65, 0x73, + 0x52, 0x44, 0x49, 0x62, 0x00, 0x18 +}; + /** - * Terminal (IFD) certificate in CVC format (PK.IFD.AUT) + * Terminal (IFD) certificate in CVC format (PK.IFD.AUT) (dnieRealParamData->c-CV-IFD-AUT) */ -static u8 C_CV_IFDUser_AUT_cert[] = { +static u8 C_CV_IFDUser_AUT_cert_0[] = { 0x7f, 0x21, 0x81, 0xcd, 0x5f, 0x37, 0x81, 0x80, 0x82, 0x5b, 0x69, 0xc6, 0x45, 0x1e, 0x5f, 0x51, 0x70, 0x74, 0x38, 0x5f, 0x2f, 0x17, 0xd6, 0x4d, 0xfe, 0x2e, 0x68, 0x56, 0x75, 0x67, 0x09, 0x4b, 0x57, 0xf3, 0xc5, 0x78, @@ -203,10 +298,31 @@ static u8 C_CV_IFDUser_AUT_cert[] = { 0x44, 0x49, 0x60, 0x00, 0x06 }; +static u8 C_CV_IFDUser_AUT_cert_1[] = { + 0x7f, 0x21, 0x81, 0xcd, 0x5f, 0x37, 0x81, 0x80, 0x5d, 0xa9, 0x4b, 0x6b, + 0x4e, 0xb8, 0x61, 0xec, 0xa6, 0x36, 0xd2, 0x67, 0x39, 0x74, 0x71, 0x1f, + 0x55, 0x63, 0x0f, 0x5b, 0x89, 0x03, 0x8c, 0x57, 0xd0, 0xbb, 0xbb, 0xc1, + 0xd2, 0xc6, 0x8c, 0xc3, 0xeb, 0x56, 0xd5, 0x30, 0x38, 0x00, 0xf5, 0xa9, + 0xf5, 0xe2, 0x96, 0x7f, 0xdf, 0x28, 0x91, 0x7b, 0xaf, 0xc8, 0x87, 0x63, + 0xb8, 0xec, 0x2c, 0x0e, 0xbe, 0x7a, 0xcb, 0x0b, 0xa4, 0xaf, 0xbf, 0xe6, + 0x6d, 0xb2, 0xa1, 0xed, 0xa1, 0x3e, 0x45, 0x64, 0xf7, 0x8e, 0x65, 0x58, + 0x6e, 0x51, 0x01, 0x76, 0xf1, 0x1c, 0x4c, 0x99, 0x36, 0x4a, 0xaf, 0x18, + 0x97, 0xd1, 0x1b, 0xf9, 0x8e, 0x9d, 0x1d, 0x0a, 0x12, 0xd0, 0x6a, 0xab, + 0x75, 0x76, 0x4a, 0xa8, 0xdc, 0x85, 0x8d, 0xf0, 0xf0, 0x03, 0xeb, 0x8b, + 0x4b, 0x3b, 0x56, 0xf5, 0xf9, 0x5f, 0xa6, 0x37, 0x53, 0x75, 0x19, 0xe4, + 0xc6, 0x55, 0x10, 0xf7, 0x5f, 0x38, 0x3c, 0x60, 0x2d, 0xd4, 0xba, 0x04, + 0xdb, 0xc9, 0x85, 0x88, 0x45, 0xe6, 0xa9, 0xc4, 0x05, 0x5b, 0xc5, 0xbf, + 0xa0, 0xed, 0xdb, 0x86, 0x67, 0x89, 0xf0, 0xec, 0x6a, 0x80, 0xfc, 0xe5, + 0x3c, 0x66, 0x08, 0xdf, 0xdc, 0x9b, 0x9f, 0xe2, 0xed, 0x56, 0x75, 0x2c, + 0xc6, 0x05, 0x51, 0x3b, 0xa3, 0xf1, 0x75, 0x9c, 0xdd, 0x95, 0x22, 0x75, + 0x3f, 0x18, 0xd7, 0x00, 0x01, 0x00, 0x01, 0x42, 0x08, 0x65, 0x73, 0x53, + 0x44, 0x49, 0x62, 0x00, 0x18 +}; + /** - * Terminal (IFD) certificate in CVC format (PK.IFD.AUT) for the PIN channel in DNIe 3.0 + * Terminal (IFD) certificate in CVC format (PK.IFD.AUT) for the PIN channel in DNIe 3.0 (dnie30RealParamDataPIN->c-CV-IFD-AUT) */ -static u8 C_CV_IFDUser_AUT_pin_cert[] = { +static u8 C_CV_IFDUser_AUT_pin_cert_0[] = { 0x7f, 0x21, 0x81, 0xcd, 0x5f, 0x37, 0x81, 0x80, 0x69, 0xc4, 0xe4, 0x94, 0xf0, 0x08, 0xe2, 0x42, 0x14, 0xb1, 0xc1, 0x31, 0xb6, 0x1f, 0xce, 0x9c, 0x15, 0xfa, 0x3c, 0xb0, 0x61, 0xdd, 0x6f, 0x02, 0xd8, 0xa2, 0xcd, 0x30, @@ -227,44 +343,136 @@ static u8 C_CV_IFDUser_AUT_pin_cert[] = { 0x44, 0x49, 0x60, 0x00, 0x06 }; +static u8 C_CV_IFDUser_AUT_pin_cert_1[] = { + 0x7f, 0x21, 0x81, 0xcd, 0x5f, 0x37, 0x81, 0x80, 0x0a, 0x3d, 0xb4, 0xd1, + 0x57, 0x98, 0xf2, 0x34, 0xf6, 0x31, 0xfd, 0x94, 0xc9, 0x1d, 0x2a, 0x63, + 0x63, 0xd0, 0xe1, 0x8e, 0x1b, 0x56, 0xda, 0xbd, 0xe6, 0x22, 0xbc, 0x20, + 0x1f, 0xd7, 0xc7, 0xff, 0x59, 0xff, 0x66, 0xda, 0x6e, 0x43, 0x4f, 0xe2, + 0xf7, 0xf4, 0x6e, 0x42, 0xe4, 0xa6, 0x06, 0xea, 0x82, 0x39, 0xac, 0x1a, + 0xc3, 0x0c, 0x7d, 0xad, 0xe2, 0x29, 0x65, 0xdf, 0x60, 0x6d, 0x11, 0x5e, + 0x04, 0xc8, 0xef, 0xfc, 0x77, 0x2b, 0x8f, 0x5d, 0x48, 0x77, 0x3e, 0x34, + 0x95, 0x5f, 0x33, 0xf4, 0x64, 0xed, 0x85, 0xcc, 0x0e, 0xb1, 0xbc, 0x57, + 0x2a, 0xfa, 0xba, 0x47, 0x25, 0xfb, 0xf5, 0xbd, 0xcf, 0x1d, 0x8c, 0x38, + 0xc9, 0xfe, 0x9c, 0xd8, 0x53, 0x6f, 0x34, 0x0b, 0xce, 0x14, 0x1d, 0xf5, + 0x18, 0x7f, 0xa2, 0xe2, 0x37, 0x2d, 0x73, 0xbc, 0x7f, 0x89, 0x48, 0x35, + 0x0c, 0xba, 0xde, 0xf2, 0x5f, 0x38, 0x3c, 0x0d, 0xcc, 0x88, 0x8d, 0x47, + 0x96, 0x54, 0x3f, 0x03, 0x25, 0x4f, 0x4e, 0x2c, 0xdf, 0x98, 0xb1, 0xe1, + 0x26, 0x11, 0xe3, 0x98, 0x1f, 0x53, 0x33, 0xdf, 0x98, 0xc8, 0x86, 0x01, + 0x93, 0x75, 0x84, 0x0f, 0xac, 0x61, 0xdb, 0x8f, 0x1b, 0xa3, 0xb5, 0x43, + 0xdc, 0xea, 0x3d, 0x05, 0x9e, 0x6a, 0x41, 0x4f, 0x6d, 0xd2, 0x9f, 0xc7, + 0xc9, 0x9d, 0x8b, 0x00, 0x01, 0x00, 0x01, 0x42, 0x08, 0x65, 0x73, 0x53, + 0x44, 0x49, 0x62, 0x00, 0x18 +}; + /** - * Root CA card key reference + * Root CA card key reference (pk-RCA-AUT-keyRef) */ static u8 root_ca_keyref[] = { 0x02, 0x0f }; /** - * ICC card private key reference + * ICC card private key reference (sk-ICC-AUT-keyRef) */ static u8 icc_priv_keyref[] = { 0x02, 0x1f }; /** - * Intermediate CA card key reference + * Intermediate CA card key reference (ifd-keyRef) */ -static u8 cvc_intca_keyref[] = - { 0x65, 0x73, 0x53, 0x44, 0x49, 0x60, 0x00, 0x06 }; +static u8 cvc_intca_keyref_0[] = { 0x65, 0x73, 0x53, 0x44, 0x49, 0x60, 0x00, 0x06 }; +static u8 cvc_intca_keyref_1[] = { 0x65, 0x73, 0x53, 0x44, 0x49, 0x62, 0x00, 0x18 }; /** - * In memory key reference for selecting IFD sent certificate + * In memory key reference for selecting IFD sent certificate (dnieRealParamData->pk-IFD-AUT-keyRef) */ -static u8 cvc_ifd_keyref[] = - { 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; +static u8 cvc_ifd_keyref_0[] = { 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; +static u8 cvc_ifd_keyref_1[] = { 0x00, 0x00, 0x00, 0x00, 0xd0, 0x02, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x02 }; /** - * In memory key reference for selecting IFD sent certificate in PIN channel DNIe 3.0 + * In memory key reference for selecting IFD sent certificate in PIN channel DNIe 3.0 (dnie30RealParamDataPIN->pk-IFD-AUT-keyRef) */ -static u8 cvc_ifd_keyref_pin[] = - { 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; +static u8 cvc_ifd_keyref_pin_0[] = { 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; +static u8 cvc_ifd_keyref_pin_1[] = { 0x00, 0x00, 0x00, 0x00, 0xd0, 0x02, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x04 }; /** - * Serial number for IFD Terminal application + * Serial number for IFD Terminal application (dnieRealParamData->sn-IFD) */ -static u8 sn_ifd[] = { 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; +static u8 sn_ifd_0[] = { 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; +static u8 sn_ifd_1[] = { 0xd0, 0x02, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x02 }; /** - * Serial number for IFD Terminal application in PIN channel DNIe 3.0 + * Serial number for IFD Terminal application in PIN channel DNIe 3.0 (dnie30RealParamDataPIN->sn-IFD) */ -static u8 sn_ifd_pin[] = { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; +static u8 sn_ifd_pin_0[] = { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; +static u8 sn_ifd_pin_1[] = { 0xd0, 0x02, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x04 }; + +#define AC_RAIZ_COMPONENTES_OLD_IDX 0 +#define AC_RAIZ_COMPONENTES_ISSUER "/C=ES/O=DIRECCION GENERAL DE LA POLICIA/OU=DNIE/OU=AC RAIZ COMPONENTES/CN=000000006573524449600006" +#define AC_RAIZ_COMPONENTES_2_NEW_IDX 1 +#define AC_RAIZ_COMPONENTES_2_ISSUER "/C=ES/O=DIRECCION GENERAL DE LA POLICIA/OU=DNIE/organizationIdentifier=VATES-S2816015H/OU=AC RAIZ COMPONENTES 2/CN=000000006573524449620018" +#define AC_RAIZ_COMPONENTES_2_ISSUER_OU "/OU=AC RAIZ COMPONENTES 2/" + +/** + * The DNIe secure channel uses some static configuration. + * Since DNIe 'BMP100001' it seems that the old values were + * replaced by new certs and keys. So an array of configuration + * values is going to be added that will be set to the card + * private data. For the moment the issuer of the icc intermediate + * CA cert will be used to assign one or the other array element. + */ +static dnie_channel_data_t channel_data[] = { + { /* AC_RAIZ_COMPONENTES_OLD_IDX: Channel data configuration for DNIe before BMP100001 */ + .icc_root_ca = { + .modulus = { icc_root_ca_modulus_0, sizeof(icc_root_ca_modulus_0) }, + .exponent = { icc_root_ca_public_exponent, sizeof(icc_root_ca_public_exponent) } + }, + .ifd = { + .modulus = { ifd_modulus_0, sizeof(ifd_modulus_0) }, + .exponent = { ifd_public_exponent, sizeof(ifd_public_exponent) }, + .private = { ifd_private_exponent_0, sizeof(ifd_private_exponent_0) } + }, + .ifd_pin = { + .modulus = { .value = ifd_pin_modulus_0, sizeof(ifd_pin_modulus_0) }, + .exponent = { .value = ifd_pin_public_exponent, sizeof(ifd_pin_public_exponent) }, + .private = { .value = ifd_pin_private_exponent_0, sizeof(ifd_pin_private_exponent_0) } + }, + .C_CV_CA_CS_AUT_cert = { .value = C_CV_CA_CS_AUT_cert_0, sizeof(C_CV_CA_CS_AUT_cert_0) }, + .C_CV_IFDUser_AUT_cert = { .value = C_CV_IFDUser_AUT_cert_0, sizeof(C_CV_IFDUser_AUT_cert_0) }, + .C_CV_IFDUser_AUT_pin_cert = { .value = C_CV_IFDUser_AUT_pin_cert_0, sizeof(C_CV_IFDUser_AUT_pin_cert_0) }, + .root_ca_keyref = { root_ca_keyref, sizeof(root_ca_keyref) }, + .icc_priv_keyref = { icc_priv_keyref, sizeof(icc_priv_keyref) }, + .cvc_intca_keyref = { cvc_intca_keyref_0, sizeof(cvc_intca_keyref_0) }, + .cvc_ifd_keyref = { cvc_ifd_keyref_0, sizeof(cvc_ifd_keyref_0) }, + .cvc_ifd_keyref_pin = { cvc_ifd_keyref_pin_0, sizeof(cvc_ifd_keyref_pin_0) }, + .sn_ifd = { sn_ifd_0, sizeof(sn_ifd_0) }, + .sn_ifd_pin = { sn_ifd_pin_0, sizeof(sn_ifd_pin_0) } + }, + { /* AC_RAIZ_COMPONENTES_2_NEW_IDX: Channel data configuration for DNIe BMP100001 and newer */ + .icc_root_ca = { + .modulus = { icc_root_ca_modulus_1, sizeof(icc_root_ca_modulus_1) }, + .exponent = { icc_root_ca_public_exponent, sizeof(icc_root_ca_public_exponent) } + }, + .ifd = { + .modulus = { ifd_modulus_1, sizeof(ifd_modulus_1) }, + .exponent = { ifd_public_exponent, sizeof(ifd_public_exponent) }, + .private = { ifd_private_exponent_1, sizeof(ifd_private_exponent_1) } + }, + .ifd_pin = { + .modulus = { .value = ifd_pin_modulus_1, sizeof(ifd_pin_modulus_1) }, + .exponent = { .value = ifd_pin_public_exponent, sizeof(ifd_pin_public_exponent) }, + .private = { .value = ifd_pin_private_exponent_1, sizeof(ifd_pin_private_exponent_1) } + }, + .C_CV_CA_CS_AUT_cert = { .value = C_CV_CA_CS_AUT_cert_1, sizeof(C_CV_CA_CS_AUT_cert_1) }, + .C_CV_IFDUser_AUT_cert = { .value = C_CV_IFDUser_AUT_cert_1, sizeof(C_CV_IFDUser_AUT_cert_1) }, + .C_CV_IFDUser_AUT_pin_cert = { .value = C_CV_IFDUser_AUT_pin_cert_1, sizeof(C_CV_IFDUser_AUT_pin_cert_1) }, + .root_ca_keyref = { root_ca_keyref, sizeof(root_ca_keyref) }, + .icc_priv_keyref = { icc_priv_keyref, sizeof(icc_priv_keyref) }, + .cvc_intca_keyref = { cvc_intca_keyref_1, sizeof(cvc_intca_keyref_1) }, + .cvc_ifd_keyref = { cvc_ifd_keyref_1, sizeof(cvc_ifd_keyref_1) }, + .cvc_ifd_keyref_pin = { cvc_ifd_keyref_pin_1, sizeof(cvc_ifd_keyref_pin_1) }, + .sn_ifd = { sn_ifd_1, sizeof(sn_ifd_1) }, + .sn_ifd_pin = { sn_ifd_pin_1, sizeof(sn_ifd_pin_1) } + } +}; /************ internal functions **********************************/ @@ -314,7 +522,7 @@ int dnie_read_file(sc_card_t * card, goto dnie_read_file_end; } fsize = (*file)->size; - /* reserve enought space to read data from card */ + /* reserve enough space to read data from card */ if (fsize <= 0) { res = SC_ERROR_FILE_TOO_SMALL; msg = "provided buffer size is too small"; @@ -330,7 +538,7 @@ int dnie_read_file(sc_card_t * card, sc_log(ctx, "read_binary(): expected '%"SC_FORMAT_LEN_SIZE_T"u' bytes", fsize); res = sc_read_binary(card, 0, data, fsize, 0L); - if (res < 0) { /* read_binary returns number of bytes readed */ + if (res < 0) { /* read_binary returns number of bytes read */ res = SC_ERROR_CARD_CMD_FAILED; msg = "read_binary() failed"; goto dnie_read_file_err; @@ -361,7 +569,7 @@ int dnie_read_file(sc_card_t * card, * No validation is done except that received data is effectively a certificate * @param card Pointer to card driver structure * @param certpat path to requested certificate - * @param cert where to store resultig data + * @param cert where to store resulting data * @return SC_SUCCESS if ok, else error code */ static int dnie_read_certificate(sc_card_t * card, char *certpath, X509 ** cert) @@ -383,6 +591,7 @@ static int dnie_read_certificate(sc_card_t * card, char *certpath, X509 ** cert) buffer2 = buffer; *cert = d2i_X509(NULL, (const unsigned char **)&buffer2, bufferlen); if (*cert == NULL) { /* received data is not a certificate */ + sc_log_openssl(card->ctx); res = SC_ERROR_OBJECT_NOT_VALID; msg = "Read data is not a certificate"; goto read_cert_end; @@ -402,6 +611,64 @@ static int dnie_read_certificate(sc_card_t * card, char *certpath, X509 ** cert) LOG_FUNC_RETURN(card->ctx, res); } +/** + * Method that sets the configuration channel data to use. + * The configuration data is already set to the card private data. + * Just created in case this will be modified. + * + * @param card Pointer to card driver structure + * @param data The data for the channel will be assigned here + * @return SC_SUCCESS if ok; else error code + */ +static int dnie_get_channel_data(sc_card_t * card, dnie_channel_data_t ** data) { + dnie_private_data_t *priv_data = GET_DNIE_PRIV_DATA(card); + LOG_FUNC_CALLED(card->ctx); + if (!priv_data->channel_data) { + sc_log(card->ctx, "Data channel configuration was not initialized"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + *data = priv_data->channel_data; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +/** + * Method to assign into the private data the secure channel + * configuration to use. Right now the icc_intermediate_ca_cert + * issuer is used. If it is the new one the new data is assigned + * else the old data is set. + * + * @param card Pointer to card driver structure + * @param icc_intermediate_ca_cert Pointer to the X509 icc intermediate CA certificate + * @return SC_SUCCESS if ok; else error code + */ +static int dnie_set_channel_data(sc_card_t * card, X509 * icc_intermediate_ca_cert) { + char *buf = NULL; + dnie_private_data_t *priv_data = GET_DNIE_PRIV_DATA(card); + LOG_FUNC_CALLED(card->ctx); + + const X509_NAME *issuer = X509_get_issuer_name(icc_intermediate_ca_cert); + if (issuer) { + buf = X509_NAME_oneline(issuer, buf, 0); + if (!buf) { + sc_log_openssl(card->ctx); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + sc_log(card->ctx, "icc_intermediate_ca_cert issuer %s", buf); + } + + if (buf && strstr(buf, AC_RAIZ_COMPONENTES_2_ISSUER_OU)) { + sc_log(card->ctx, "assigning new data channel configuration"); + priv_data->channel_data = &channel_data[AC_RAIZ_COMPONENTES_2_NEW_IDX]; + } else { + sc_log(card->ctx, "assigning old data channel configuration"); + priv_data->channel_data = &channel_data[AC_RAIZ_COMPONENTES_OLD_IDX]; + } + if (buf) { + OPENSSL_free(buf); + } + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + /************ implementation of cwa provider methods **************/ /** @@ -414,39 +681,96 @@ static int dnie_read_certificate(sc_card_t * card, char *certpath, X509 ** cert) */ static int dnie_get_root_ca_pubkey(sc_card_t * card, EVP_PKEY ** root_ca_key) { - int res=SC_SUCCESS; - RSA *root_ca_rsa=NULL; - BIGNUM *root_ca_rsa_n, *root_ca_rsa_e; - LOG_FUNC_CALLED(card->ctx); - - /* compose root_ca_public key with data provided by Dnie Manual */ - *root_ca_key = EVP_PKEY_new(); + int res = SC_SUCCESS; + BIGNUM *root_ca_rsa_n = NULL, *root_ca_rsa_e = NULL; + dnie_channel_data_t *data; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA *root_ca_rsa = NULL; root_ca_rsa = RSA_new(); - if (!*root_ca_key || !root_ca_rsa) { + *root_ca_key = EVP_PKEY_new(); + if (!root_ca_rsa || !*root_ca_key) { + if (root_ca_rsa) + RSA_free(root_ca_rsa); + if (*root_ca_key) + EVP_PKEY_free(*root_ca_key); +#else + EVP_PKEY_CTX *ctx = NULL; + OSSL_PARAM_BLD *bld = NULL; + OSSL_PARAM *params = NULL; + + ctx = EVP_PKEY_CTX_new_from_name(card->ctx->ossl3ctx->libctx, "RSA", NULL); + if (!ctx) { +#endif + sc_log_openssl(card->ctx); sc_log(card->ctx, "Cannot create data for root CA public key"); return SC_ERROR_OUT_OF_MEMORY; } - root_ca_rsa_n = BN_bin2bn(icc_root_ca_modulus, sizeof(icc_root_ca_modulus), NULL); - root_ca_rsa_e = BN_bin2bn(icc_root_ca_public_exponent, sizeof(icc_root_ca_public_exponent), NULL); + LOG_FUNC_CALLED(card->ctx); + + /* obtain the data channel info for the card */ + res = dnie_get_channel_data(card, &data); + if (res < 0) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA_free(root_ca_rsa); + EVP_PKEY_free(*root_ca_key); +#else + EVP_PKEY_CTX_free(ctx); +#endif + } + LOG_TEST_RET(card->ctx, res, "Error getting the card channel data"); + + /* compose root_ca_public key with data provided by Dnie Manual */ + root_ca_rsa_n = BN_bin2bn(data->icc_root_ca.modulus.value, (int)data->icc_root_ca.modulus.len, NULL); + root_ca_rsa_e = BN_bin2bn(data->icc_root_ca.exponent.value, (int)data->icc_root_ca.exponent.len, NULL); + +#if OPENSSL_VERSION_NUMBER < 0x30000000L if (RSA_set0_key(root_ca_rsa, root_ca_rsa_n, root_ca_rsa_e, NULL) != 1) { + sc_log_openssl(card->ctx); BN_free(root_ca_rsa_n); BN_free(root_ca_rsa_e); - if (*root_ca_key) - EVP_PKEY_free(*root_ca_key); - if (root_ca_rsa) - RSA_free(root_ca_rsa); - sc_log(card->ctx, "Cannot set RSA valuses for CA public key"); + EVP_PKEY_free(*root_ca_key); + RSA_free(root_ca_rsa); + sc_log(card->ctx, "Cannot set RSA values for CA public key"); return SC_ERROR_INTERNAL; } - res = EVP_PKEY_assign_RSA(*root_ca_key, root_ca_rsa); if (!res) { - if (*root_ca_key) - EVP_PKEY_free(*root_ca_key); /*implies root_ca_rsa free() */ + sc_log_openssl(card->ctx); + RSA_free(root_ca_rsa); +#else + if (!(bld = OSSL_PARAM_BLD_new()) || + OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, root_ca_rsa_n) != 1 || + OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, root_ca_rsa_e) != 1 || + !(params = OSSL_PARAM_BLD_to_param(bld))) { + sc_log_openssl(card->ctx); + OSSL_PARAM_BLD_free(bld); + OSSL_PARAM_free(params); + EVP_PKEY_CTX_free(ctx); + sc_log(card->ctx, "Cannot set RSA values for CA public key"); + return SC_ERROR_INTERNAL; + } + OSSL_PARAM_BLD_free(bld); + + if (EVP_PKEY_fromdata_init(ctx) != 1 || + EVP_PKEY_fromdata(ctx, root_ca_key, EVP_PKEY_PUBLIC_KEY, params) != 1) { + sc_log_openssl(card->ctx); + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); +#endif + BN_free(root_ca_rsa_n); + BN_free(root_ca_rsa_e); + EVP_PKEY_free(*root_ca_key); sc_log(card->ctx, "Cannot compose root CA public key"); return SC_ERROR_INTERNAL; } + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + BN_free(root_ca_rsa_n); + BN_free(root_ca_rsa_e); +#endif LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -457,7 +781,7 @@ static int dnie_get_root_ca_pubkey(sc_card_t * card, EVP_PKEY ** root_ca_key) * (in CardVerifiable Certificate format) to be sent to the * card in External Authentication process * As this is local provider, just points to provided static data, - * and allways return success + * and always return success * * @param card Pointer to card driver Certificate * @param cert Where to store resulting byte array @@ -466,10 +790,17 @@ static int dnie_get_root_ca_pubkey(sc_card_t * card, EVP_PKEY ** root_ca_key) */ static int dnie_get_cvc_ca_cert(sc_card_t * card, u8 ** cert, size_t * length) { + int res; + dnie_channel_data_t *data; LOG_FUNC_CALLED(card->ctx); - *cert = C_CV_CA_CS_AUT_cert; - *length = sizeof(C_CV_CA_CS_AUT_cert); - LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + + /* obtain the data channel info for the card */ + res = dnie_get_channel_data(card, &data); + LOG_TEST_RET(card->ctx, res, "Error getting the card channel data"); + + *cert = data->C_CV_CA_CS_AUT_cert.value; + *length = data->C_CV_CA_CS_AUT_cert.len; + LOG_FUNC_RETURN(card->ctx, res); } /** @@ -479,7 +810,7 @@ static int dnie_get_cvc_ca_cert(sc_card_t * card, u8 ** cert, size_t * length) * (in CardVerifiable Certificate format) to be sent to the * card in External Authentication process * As this is local provider, just points to provided static data, - * and allways return success + * and always return success * * @param card Pointer to card driver Certificate * @param cert Where to store resulting byte array @@ -488,10 +819,17 @@ static int dnie_get_cvc_ca_cert(sc_card_t * card, u8 ** cert, size_t * length) */ static int dnie_get_cvc_ifd_cert(sc_card_t * card, u8 ** cert, size_t * length) { + int res; + dnie_channel_data_t *data; LOG_FUNC_CALLED(card->ctx); - *cert = C_CV_IFDUser_AUT_cert; - *length = sizeof(C_CV_IFDUser_AUT_cert); - LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + + /* obtain the data channel info for the card */ + res = dnie_get_channel_data(card, &data); + LOG_TEST_RET(card->ctx, res, "Error getting the card channel data"); + + *cert = data->C_CV_IFDUser_AUT_cert.value; + *length = data->C_CV_IFDUser_AUT_cert.len; + LOG_FUNC_RETURN(card->ctx, res); } /** @@ -502,7 +840,7 @@ static int dnie_get_cvc_ifd_cert(sc_card_t * card, u8 ** cert, size_t * length) * (in CardVerifiable Certificate format) to be sent to the * card in External Authentication process * As this is local provider, just points to provided static data, - * and allways return success + * and always return success * * @param card Pointer to card driver Certificate * @param cert Where to store resulting byte array @@ -511,10 +849,17 @@ static int dnie_get_cvc_ifd_cert(sc_card_t * card, u8 ** cert, size_t * length) */ static int dnie_get_cvc_ifd_cert_pin(sc_card_t * card, u8 ** cert, size_t * length) { + int res; + dnie_channel_data_t *data; LOG_FUNC_CALLED(card->ctx); - *cert = C_CV_IFDUser_AUT_pin_cert; - *length = sizeof(C_CV_IFDUser_AUT_pin_cert); - LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + + /* obtain the data channel info for the card */ + res = dnie_get_channel_data(card, &data); + LOG_TEST_RET(card->ctx, res, "Error getting the card channel data"); + + *cert = data->C_CV_IFDUser_AUT_pin_cert.value; + *length = data->C_CV_IFDUser_AUT_pin_cert.len; + LOG_FUNC_RETURN(card->ctx, res); } /** @@ -533,26 +878,47 @@ static int dnie_get_cvc_ifd_cert_pin(sc_card_t * card, u8 ** cert, size_t * leng */ static int dnie_get_privkey(sc_card_t * card, EVP_PKEY ** ifd_privkey, u8 * modulus, int modulus_len, - u8 * public_exponent, int public_exponent_len, - u8 * private_exponent, int private_exponent_len) + u8 * public_exponent, size_t public_exponent_len, + u8 * private_exponent, size_t private_exponent_len) { - RSA *ifd_rsa=NULL; - BIGNUM *ifd_rsa_n, *ifd_rsa_e, *ifd_rsa_d = NULL; - int res=SC_SUCCESS; + BIGNUM *ifd_rsa_n = NULL, *ifd_rsa_e = NULL, *ifd_rsa_d = NULL; - LOG_FUNC_CALLED(card->ctx); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + int res = SC_ERROR_INTERNAL; + RSA *ifd_rsa = NULL; - /* compose ifd_private key with data provided in Annex 3 of DNIe Manual */ - *ifd_privkey = EVP_PKEY_new(); + LOG_FUNC_CALLED(card->ctx); ifd_rsa = RSA_new(); - if (!*ifd_privkey || !ifd_rsa) { + *ifd_privkey = EVP_PKEY_new(); + + if (!ifd_rsa || !*ifd_privkey) { + if (ifd_rsa) + RSA_free(ifd_rsa); + if (*ifd_privkey) + EVP_PKEY_free(*ifd_privkey); +#else + OSSL_PARAM_BLD *bld = NULL; + OSSL_PARAM *params = NULL; + EVP_PKEY_CTX *ctx = NULL; + + LOG_FUNC_CALLED(card->ctx); + ctx = EVP_PKEY_CTX_new_from_name(card->ctx->ossl3ctx->libctx, "RSA", NULL); + + if (!ctx) { +#endif + sc_log_openssl(card->ctx); sc_log(card->ctx, "Cannot create data for IFD private key"); return SC_ERROR_OUT_OF_MEMORY; } + + /* compose ifd_private key with data provided in Annex 3 of DNIe Manual */ ifd_rsa_n = BN_bin2bn(modulus, modulus_len, NULL); - ifd_rsa_e = BN_bin2bn(public_exponent, public_exponent_len, NULL); - ifd_rsa_d = BN_bin2bn(private_exponent, private_exponent_len, NULL); + ifd_rsa_e = BN_bin2bn(public_exponent, (int)public_exponent_len, NULL); + ifd_rsa_d = BN_bin2bn(private_exponent, (int)private_exponent_len, NULL); + +#if OPENSSL_VERSION_NUMBER < 0x30000000L if (RSA_set0_key(ifd_rsa, ifd_rsa_n, ifd_rsa_e, ifd_rsa_d) != 1) { + sc_log_openssl(card->ctx); BN_free(ifd_rsa_n); BN_free(ifd_rsa_e); BN_free(ifd_rsa_d); @@ -564,11 +930,46 @@ static int dnie_get_privkey(sc_card_t * card, EVP_PKEY ** ifd_privkey, res = EVP_PKEY_assign_RSA(*ifd_privkey, ifd_rsa); if (!res) { + RSA_free(ifd_rsa); + sc_log_openssl(card->ctx); +#else + if (!(bld = OSSL_PARAM_BLD_new()) || + OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, ifd_rsa_n) != 1 || + OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, ifd_rsa_e) != 1 || + OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, ifd_rsa_d) != 1 || + !(params = OSSL_PARAM_BLD_to_param(bld))) { + sc_log_openssl(card->ctx); + OSSL_PARAM_BLD_free(bld); + OSSL_PARAM_free(params); + EVP_PKEY_CTX_free(ctx); + BN_free(ifd_rsa_n); + BN_free(ifd_rsa_e); + BN_free(ifd_rsa_d); + sc_log(card->ctx, "Cannot set RSA values for CA public key"); + return SC_ERROR_INTERNAL; + } + OSSL_PARAM_BLD_free(bld); + + if (EVP_PKEY_fromdata_init(ctx) != 1 || + EVP_PKEY_fromdata(ctx, ifd_privkey, EVP_PKEY_KEYPAIR, params) != 1) { + sc_log_openssl(card->ctx); + EVP_PKEY_CTX_free(ctx); +#endif + BN_free(ifd_rsa_n); + BN_free(ifd_rsa_e); + BN_free(ifd_rsa_d); if (*ifd_privkey) EVP_PKEY_free(*ifd_privkey); /* implies ifd_rsa free() */ sc_log(card->ctx, "Cannot compose IFD private key"); return SC_ERROR_INTERNAL; } +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + OSSL_PARAM_free(params); + EVP_PKEY_CTX_free(ctx); + BN_free(ifd_rsa_n); + BN_free(ifd_rsa_e); + BN_free(ifd_rsa_d); +#endif LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -584,9 +985,16 @@ static int dnie_get_privkey(sc_card_t * card, EVP_PKEY ** ifd_privkey, */ static int dnie_get_ifd_privkey(sc_card_t * card, EVP_PKEY ** ifd_privkey) { - return dnie_get_privkey(card, ifd_privkey, ifd_modulus, sizeof(ifd_modulus), - ifd_public_exponent, sizeof(ifd_public_exponent), - ifd_private_exponent, sizeof(ifd_private_exponent)); + int res; + dnie_channel_data_t *data; + + /* obtain the data channel info for the card */ + res = dnie_get_channel_data(card, &data); + LOG_TEST_RET(card->ctx, res, "Error getting the card channel data"); + + return dnie_get_privkey(card, ifd_privkey, data->ifd.modulus.value, (int)data->ifd.modulus.len, + data->ifd.exponent.value, data->ifd.exponent.len, + data->ifd.private.value, data->ifd.private.len); } /** @@ -601,9 +1009,16 @@ static int dnie_get_ifd_privkey(sc_card_t * card, EVP_PKEY ** ifd_privkey) */ static int dnie_get_ifd_privkey_pin(sc_card_t * card, EVP_PKEY ** ifd_privkey) { - return dnie_get_privkey(card, ifd_privkey, ifd_pin_modulus, sizeof(ifd_pin_modulus), - ifd_pin_public_exponent, sizeof(ifd_pin_public_exponent), - ifd_pin_private_exponent, sizeof(ifd_pin_private_exponent)); + int res; + dnie_channel_data_t *data; + + /* obtain the data channel info for the card */ + res = dnie_get_channel_data(card, &data); + LOG_TEST_RET(card->ctx, res, "Error getting the card channel data"); + + return dnie_get_privkey(card, ifd_privkey, data->ifd_pin.modulus.value, (int)data->ifd_pin.modulus.len, + data->ifd_pin.exponent.value, data->ifd_pin.exponent.len, + data->ifd_pin.private.value, data->ifd_pin.private.len); } /** @@ -615,7 +1030,14 @@ static int dnie_get_ifd_privkey_pin(sc_card_t * card, EVP_PKEY ** ifd_privkey) */ static int dnie_get_icc_intermediate_ca_cert(sc_card_t * card, X509 ** cert) { - return dnie_read_certificate(card, "3F006020", cert); + dnie_private_data_t *priv_data = GET_DNIE_PRIV_DATA(card); + + int res = dnie_read_certificate(card, "3F006020", cert); + if (res == SC_SUCCESS && !priv_data->channel_data) { + /* initialize the secure channel data using the issuer cert */ + res = dnie_set_channel_data(card, *cert); + } + return res; } /** @@ -642,16 +1064,23 @@ static int dnie_get_icc_cert(sc_card_t * card, X509 ** cert) static int dnie_get_root_ca_pubkey_ref(sc_card_t * card, u8 ** buf, size_t * len) { - *buf = root_ca_keyref; - *len = sizeof(root_ca_keyref); - return SC_SUCCESS; + int res; + dnie_channel_data_t *data; + + /* obtain the data channel info for the card */ + res = dnie_get_channel_data(card, &data); + LOG_TEST_RET(card->ctx, res, "Error getting the card channel data"); + + *buf = data->root_ca_keyref.value; + *len = data->root_ca_keyref.len; + return res; } /** * Retrieve public key reference for intermediate CA to validate IFD cert. * * This is required in the process of On card external authenticate - * As this driver is for local SM authentication SC_SUCCESS is allways returned + * As this driver is for local SM authentication SC_SUCCESS is always returned * * @param card Pointer to card driver structure * @param buf where to store resulting key reference @@ -661,9 +1090,16 @@ static int dnie_get_root_ca_pubkey_ref(sc_card_t * card, u8 ** buf, static int dnie_get_intermediate_ca_pubkey_ref(sc_card_t * card, u8 ** buf, size_t * len) { - *buf = cvc_intca_keyref; - *len = sizeof(cvc_intca_keyref); - return SC_SUCCESS; + int res; + dnie_channel_data_t *data; + + /* obtain the data channel info for the card */ + res = dnie_get_channel_data(card, &data); + LOG_TEST_RET(card->ctx, res, "Error getting the card channel data"); + + *buf = data->cvc_intca_keyref.value; + *len = data->cvc_intca_keyref.len; + return res; } /** @@ -671,7 +1107,7 @@ static int dnie_get_intermediate_ca_pubkey_ref(sc_card_t * card, u8 ** buf, * * This tells the card with in memory key reference is to be used * when CVC cert is sent for external auth procedure - * As this driver is for local SM authentication SC_SUCCESS is allways returned + * As this driver is for local SM authentication SC_SUCCESS is always returned * * @param card pointer to card driver structure * @param buf where to store data to be sent @@ -680,9 +1116,16 @@ static int dnie_get_intermediate_ca_pubkey_ref(sc_card_t * card, u8 ** buf, */ static int dnie_get_ifd_pubkey_ref(sc_card_t * card, u8 ** buf, size_t * len) { - *buf = cvc_ifd_keyref; - *len = sizeof(cvc_ifd_keyref); - return SC_SUCCESS; + int res; + dnie_channel_data_t *data; + + /* obtain the data channel info for the card */ + res = dnie_get_channel_data(card, &data); + LOG_TEST_RET(card->ctx, res, "Error getting the card channel data"); + + *buf = data->cvc_ifd_keyref.value; + *len = data->cvc_ifd_keyref.len; + return res; } /** @@ -690,7 +1133,7 @@ static int dnie_get_ifd_pubkey_ref(sc_card_t * card, u8 ** buf, size_t * len) * * This tells the card with in memory key reference is to be used * when CVC cert is sent for external auth procedure - * As this driver is for local SM authentication SC_SUCCESS is allways returned + * As this driver is for local SM authentication SC_SUCCESS is always returned * * @param card pointer to card driver structure * @param buf where to store data to be sent @@ -699,16 +1142,23 @@ static int dnie_get_ifd_pubkey_ref(sc_card_t * card, u8 ** buf, size_t * len) */ static int dnie_get_ifd_pubkey_ref_pin(sc_card_t * card, u8 ** buf, size_t * len) { + int res; + dnie_channel_data_t *data; LOG_FUNC_CALLED(card->ctx); - *buf = cvc_ifd_keyref_pin; - *len = sizeof(cvc_ifd_keyref_pin); - return SC_SUCCESS; + + /* obtain the data channel info for the card */ + res = dnie_get_channel_data(card, &data); + LOG_TEST_RET(card->ctx, res, "Error getting the card channel data"); + + *buf = data->cvc_ifd_keyref_pin.value; + *len = data->cvc_ifd_keyref_pin.len; + return res; } /** * Retrieve key reference for ICC privkey. * - * In local SM stablishment, just retrieve key reference from static + * In local SM establishment, just retrieve key reference from static * data tables and just return success * * @param card pointer to card driver structure @@ -718,9 +1168,16 @@ static int dnie_get_ifd_pubkey_ref_pin(sc_card_t * card, u8 ** buf, size_t * len */ static int dnie_get_icc_privkey_ref(sc_card_t * card, u8 ** buf, size_t * len) { - *buf = icc_priv_keyref; - *len = sizeof(icc_priv_keyref); - return SC_SUCCESS; + int res; + dnie_channel_data_t *data; + + /* obtain the data channel info for the card */ + res = dnie_get_channel_data(card, &data); + LOG_TEST_RET(card->ctx, res, "Error getting the card channel data"); + + *buf = data->icc_priv_keyref.value; + *len = data->icc_priv_keyref.len; + return res; } /** @@ -735,9 +1192,16 @@ static int dnie_get_icc_privkey_ref(sc_card_t * card, u8 ** buf, size_t * len) */ static int dnie_get_sn_ifd(sc_card_t * card) { + int res; + dnie_channel_data_t *data; struct sm_cwa_session * sm = &card->sm_ctx.info.session.cwa; - memcpy(sm->ifd.sn, sn_ifd, sizeof(sm->ifd.sn)); - return SC_SUCCESS; + + /* obtain the data channel info for the card */ + res = dnie_get_channel_data(card, &data); + LOG_TEST_RET(card->ctx, res, "Error getting the card channel data"); + + memcpy(sm->ifd.sn, data->sn_ifd.value, data->sn_ifd.len); + return res; } /** @@ -752,9 +1216,16 @@ static int dnie_get_sn_ifd(sc_card_t * card) */ static int dnie_get_sn_ifd_pin(sc_card_t * card) { + int res; + dnie_channel_data_t *data; struct sm_cwa_session * sm = &card->sm_ctx.info.session.cwa; - memcpy(sm->ifd.sn, sn_ifd_pin, sizeof(sm->ifd.sn)); - return SC_SUCCESS; + + /* obtain the data channel info for the card */ + res = dnie_get_channel_data(card, &data); + LOG_TEST_RET(card->ctx, res, "Error getting the card channel data"); + + memcpy(sm->ifd.sn, data->sn_ifd_pin.value, data->sn_ifd_pin.len); + return res; } /* Retrieve SN.ICC (8 bytes left padded with zeroes if needed). @@ -772,7 +1243,7 @@ static int dnie_get_sn_icc(sc_card_t * card) struct sm_cwa_session * sm = &card->sm_ctx.info.session.cwa; res = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); - LOG_TEST_RET(card->ctx, res, "Error in gettting serial number"); + LOG_TEST_RET(card->ctx, res, "Error in getting serial number"); /* copy into sn_icc buffer.Remember that dnie sn has 7 bytes length */ memset(sm->icc.sn, 0, sizeof(sm->icc.sn)); memcpy(&sm->icc.sn[1], serial.value, 7); @@ -896,7 +1367,7 @@ void dnie_change_cwa_provider_to_pin(sc_card_t * card) } void dnie_format_apdu(sc_card_t *card, sc_apdu_t *apdu, - int cse, int ins, int p1, int p2, int le, int lc, + int cse, int ins, int p1, int p2, size_t le, size_t lc, unsigned char * resp, size_t resplen, const unsigned char * data, size_t datalen) { diff --git a/src/libopensc/cwa-dnie.h b/src/libopensc/cwa-dnie.h index 91b0135dac..cd906c010d 100644 --- a/src/libopensc/cwa-dnie.h +++ b/src/libopensc/cwa-dnie.h @@ -2,7 +2,7 @@ * cwa-dnie.h: CWA specifics for DNIe * * This work is derived from many sources at OpenSC Project site, - * (see references), and the information made public for Spanish + * (see references), and the information made public for Spanish * Direccion General de la Policia y de la Guardia Civil * * This library is free software; you can redistribute it and/or @@ -17,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __CWADNIE_H__ @@ -39,6 +39,41 @@ typedef struct ui_context { struct cwa_provider_st; +/** + * Structs for the channel configuration data. + */ +typedef struct dnie_buffer { + u8 *value; + size_t len; +} dnie_buffer_t; + +typedef struct dnie_public_key { + dnie_buffer_t modulus; + dnie_buffer_t exponent; +} dnie_public_key_t; + +typedef struct dnie_private_key { + dnie_buffer_t modulus; + dnie_buffer_t exponent; + dnie_buffer_t private; +} dnie_private_key_t; + +typedef struct dnie_channel_data { + dnie_public_key_t icc_root_ca; + dnie_private_key_t ifd; + dnie_private_key_t ifd_pin; + dnie_buffer_t C_CV_CA_CS_AUT_cert; + dnie_buffer_t C_CV_IFDUser_AUT_cert; + dnie_buffer_t C_CV_IFDUser_AUT_pin_cert; + dnie_buffer_t root_ca_keyref; + dnie_buffer_t icc_priv_keyref; + dnie_buffer_t cvc_intca_keyref; + dnie_buffer_t cvc_ifd_keyref; + dnie_buffer_t cvc_ifd_keyref_pin; + dnie_buffer_t sn_ifd; + dnie_buffer_t sn_ifd_pin; +} dnie_channel_data_t; + /** * OpenDNIe private data declaration * @@ -53,8 +88,9 @@ struct cwa_provider_st; #ifdef ENABLE_DNIE_UI struct ui_context ui_ctx; #endif + dnie_channel_data_t *channel_data; /* Configuration data for the secure channel */ } dnie_private_data_t; - + /** * DNIe Card Driver private data */ @@ -71,7 +107,7 @@ void dnie_change_cwa_provider_to_pin(sc_card_t * card); void dnie_change_cwa_provider_to_secure(sc_card_t * card); void dnie_format_apdu(sc_card_t *card, sc_apdu_t *apdu, - int cse, int ins, int p1, int p2, int le, int lc, + int cse, int ins, int p1, int p2, size_t le, size_t lc, unsigned char * resp, size_t resplen, const unsigned char * data, size_t datalen); diff --git a/src/libopensc/cwa14890.c b/src/libopensc/cwa14890.c index f60d094bc5..0c1c491170 100644 --- a/src/libopensc/cwa14890.c +++ b/src/libopensc/cwa14890.c @@ -1,10 +1,10 @@ /** * cwa14890.c: Implementation of Secure Messaging according CWA-14890-1 and CWA-14890-2 standards. - * + * * Copyright (C) 2010 Juan Antonio Martinez * * This work is derived from many sources at OpenSC Project site, - * (see references) and the information made public by Spanish + * (see references) and the information made public by Spanish * Direccion General de la Policia y de la Guardia Civil * * This library is free software; you can redistribute it and/or @@ -19,7 +19,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __CWA14890_C__ @@ -33,6 +33,7 @@ #include #include +#include "asn1.h" #include "opensc.h" #include "cardctl.h" #include "internal.h" @@ -43,6 +44,11 @@ #include #include "cwa14890.h" #include "cwa-dnie.h" +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# include +# include +# include +#endif #define MAX_RESP_BUFFER_SIZE 2048 @@ -51,13 +57,13 @@ * according to iso7816-4 sect 5.2.2. * * Notice that current implementation does not handle properly - * multibyte tag id. Just asume that tag is 1-byte length + * multibyte tag id. Just assume that tag is 1-byte length * Also, encodings for data length longer than 0x01000000 bytes * are not supported (tag 0x84) */ typedef struct cwa_tlv_st { u8 *buf; /** local copy of TLV byte array */ - size_t buflen; /** lengt of buffer */ + size_t buflen; /** length of buffer */ unsigned int tag; /** tag ID */ size_t len; /** length of data field */ u8 *data; /** pointer to start of data in buf buffer */ @@ -83,7 +89,7 @@ static void cwa_trace_apdu(sc_card_t * card, sc_apdu_t * apdu, int flag) return; if (flag == 0) { /* apdu command */ if (apdu->datalen > 0) { /* apdu data to show */ - sc_hex_dump(card->ctx, SC_LOG_DEBUG_NORMAL, apdu->data, apdu->datalen, buf, sizeof(buf)); + sc_hex_dump(apdu->data, apdu->datalen, buf, sizeof(buf)); sc_log(card->ctx, "\nAPDU before encode: ==================================================\nCLA: %02X INS: %02X P1: %02X P2: %02X Lc: %02"SC_FORMAT_LEN_SIZE_T"X Le: %02"SC_FORMAT_LEN_SIZE_T"X DATA: [%5"SC_FORMAT_LEN_SIZE_T"u bytes]\n%s======================================================================\n", apdu->cla, apdu->ins, apdu->p1, apdu->p2, @@ -95,7 +101,7 @@ static void cwa_trace_apdu(sc_card_t * card, sc_apdu_t * apdu, int flag) apdu->lc, apdu->le); } } else { /* apdu response */ - sc_hex_dump(card->ctx, SC_LOG_DEBUG_NORMAL, apdu->resp, apdu->resplen, buf, sizeof(buf)); + sc_hex_dump(apdu->resp, apdu->resplen, buf, sizeof(buf)); sc_log(card->ctx, "\nAPDU response after decode: ==========================================\nSW1: %02X SW2: %02X RESP: [%5"SC_FORMAT_LEN_SIZE_T"u bytes]\n%s======================================================================\n", apdu->sw1, apdu->sw2, apdu->resplen, buf); @@ -108,7 +114,7 @@ static void cwa_trace_apdu(sc_card_t * card, sc_apdu_t * apdu, int flag) * @param card smart card info structure * @return SC_SUCCESS if ok; else error code * - * TODO: to further study: what about using bignum arithmetics? + * TODO: to further study: what about using bignum arithmetic? */ static int cwa_increase_ssc(sc_card_t * card) { @@ -133,9 +139,9 @@ static int cwa_increase_ssc(sc_card_t * card) /** * ISO 7816 padding. * - * Adds an 0x80 at the end of buffer and as many zeroes to get len + * Adds an 0x80 at the end of buffer and as many zeroes to get len * multiple of 8 - * Buffer must be long enougth to store additional bytes + * Buffer must be long enough to store additional bytes * * @param buffer where to compose data * @param len pointer to buffer length @@ -151,10 +157,10 @@ static void cwa_iso7816_padding(u8 * buf, size_t * buflen) /** * compose a BER-TLV data in provided buffer. * - * Multybyte tag id are not supported + * Multibyte tag id are not supported * Also multibyte id 0x84 is unhandled * - * Notice that TLV is composed starting at offset lenght from + * Notice that TLV is composed starting at offset length from * the buffer. Consecutive calls to cwa_add_tlv, appends a new * TLV at the end of the buffer * @@ -176,7 +182,7 @@ static int cwa_compose_tlv(sc_card_t * card, /* preliminary checks */ if (!card || !card->ctx || !out || !outlen) return SC_ERROR_INVALID_ARGUMENTS; - /* comodity vars */ + /* commodity vars */ ctx = card->ctx; LOG_FUNC_CALLED(ctx); @@ -215,7 +221,7 @@ static int cwa_compose_tlv(sc_card_t * card, * Parse and APDU Response and extract specific BER-TLV data. * * NOTICE that iso7816 sect 5.2.2 states that Tag length may be 1 to n bytes - * length. In this code we'll assume allways tag lenght = 1 byte + * length. In this code we'll assume always tag length = 1 byte * * FIXME use `sc_asn1_read_tag` or similar instead * @@ -230,24 +236,38 @@ static int cwa_parse_tlv(sc_card_t * card, cwa_tlv_t tlv_array[] ) { - size_t n = 0; - size_t next = 0; + const u8 *p = buffer; + size_t left = datalen; sc_context_t *ctx = NULL; /* preliminary checks */ if (!card || !card->ctx) return SC_ERROR_INVALID_ARGUMENTS; - /* comodity vars */ + /* commodity vars */ ctx = card->ctx; LOG_FUNC_CALLED(ctx); if (!tlv_array) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - for (n = 0; n < datalen; n += next) { - cwa_tlv_t *tlv = NULL; /* pointer to TLV structure to store info */ - size_t j = 2; /* TLV has at least two bytes */ - switch (*(buffer + n)) { + while (left > 0) { + unsigned int cla = 0, tag_val = 0; + size_t tag_len = 0, header_len; + const u8 *tlv_start = p; + cwa_tlv_t *tlv = NULL; + u8 raw_tag; + + int r = sc_asn1_read_tag(&p, left, &cla, &tag_val, &tag_len); + if (r != SC_SUCCESS) { + sc_log(ctx, "Failed to parse ASN.1 tag"); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + } + + header_len = (p - tlv_start); + left -= header_len; + + raw_tag = (cla | tag_val); + switch (raw_tag) { case CWA_SM_PLAIN_TAG: tlv = &tlv_array[0]; break; /* 0x81 Plain */ @@ -261,50 +281,19 @@ static int cwa_parse_tlv(sc_card_t * card, tlv = &tlv_array[3]; break; /* 0x99 Status */ default: /* CWA_SM_LE_TAG (0x97) is not valid here */ - sc_log(ctx, "Invalid TLV Tag type: '0x%02X'", - *(buffer + n)); + sc_log(ctx, "Invalid TLV Tag type: '0x%02X'", raw_tag); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); } - tlv->buf = buffer + n; - tlv->tag = 0xff & *(buffer + n); - tlv->len = 0; /* temporary */ - /* evaluate len and start of data */ - switch (0xff & *(buffer + n + 1)) { - case 0x84: - tlv->len = (0xff & *(buffer + n + j++)); - /* fall through */ - case 0x83: - tlv->len = - (tlv->len << 8) + (0xff & *(buffer + n + j++)); - /* fall through */ - case 0x82: - tlv->len = - (tlv->len << 8) + (0xff & *(buffer + n + j++)); - /* fall through */ - case 0x81: - tlv->len = - (tlv->len << 8) + (0xff & *(buffer + n + j++)); - break; - /* case 0x80 is not standard, but official code uses it */ - case 0x80: - tlv->len = - (tlv->len << 8) + (0xff & *(buffer + n + j++)); - break; - default: - if ((*(buffer + n + 1) & 0xff) < 0x80) { - tlv->len = 0xff & *(buffer + n + 1); - } else { - sc_log(ctx, "Invalid tag length indicator: %d", - *(buffer + n + 1)); - LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_LENGTH); - } - } - tlv->data = buffer + n + j; - tlv->buflen = j + tlv->len;; + tlv->buf = (u8 *)tlv_start; + tlv->tag = raw_tag; + tlv->len = tag_len; + tlv->data = (u8 *)p; + tlv->buflen = header_len + tag_len; sc_log(ctx, "Found Tag: '0x%02X': Length: '%"SC_FORMAT_LEN_SIZE_T"u' Value:\n%s", tlv->tag, tlv->len, sc_dump_hex(tlv->data, tlv->len)); /* set index to next Tag to jump to */ - next = tlv->buflen; + p += tag_len; + left -= tag_len; } LOG_FUNC_RETURN(ctx, SC_SUCCESS); /* mark no error */ } @@ -316,11 +305,11 @@ static int cwa_parse_tlv(sc_card_t * card, * * This routine uses Root CA public key data From Annex III of manual * to verify intermediate CA icc certificate provided by card - * if verify sucess, then extract public keys from intermediate CA + * if verify success, then extract public keys from intermediate CA * and verify icc certificate * * @param card pointer to sc_card_contex - * @param sub_ca_cert icc intermediate CA certificate readed from card + * @param sub_ca_cert icc intermediate CA certificate read from card * @param icc_ca icc certificate from card * @return SC_SUCCESS if verification is ok; else error code */ @@ -328,7 +317,7 @@ static int cwa_verify_icc_certificates(sc_card_t * card, cwa_provider_t * provider, X509 * sub_ca_cert, X509 * icc_cert) { - char *msg; + char *msg = NULL; int res = SC_SUCCESS; EVP_PKEY *root_ca_key = NULL; EVP_PKEY *sub_ca_key = NULL; @@ -353,17 +342,24 @@ static int cwa_verify_icc_certificates(sc_card_t * card, /* verify sub_ca_cert against root_ca_key */ res = X509_verify(sub_ca_cert, root_ca_key); if (!res) { + sc_log_openssl(ctx); msg = "Cannot verify icc Sub-CA certificate"; res = SC_ERROR_SM_AUTHENTICATION_FAILED; goto verify_icc_certificates_end; } /* extract sub_ca_key from sub_ca_cert */ - sub_ca_key = X509_get_pubkey(sub_ca_cert); + if (!(sub_ca_key = X509_get_pubkey(sub_ca_cert))) { + sc_log_openssl(ctx); + msg = "Cannot extract public key icc Sub-CA certificate"; + res = SC_ERROR_INTERNAL; + goto verify_icc_certificates_end; + } /* verify icc_cert against sub_ca_key */ res = X509_verify(icc_cert, sub_ca_key); if (!res) { + sc_log_openssl(ctx); msg = "Cannot verify icc certificate"; res = SC_ERROR_SM_AUTHENTICATION_FAILED; goto verify_icc_certificates_end; @@ -376,15 +372,16 @@ static int cwa_verify_icc_certificates(sc_card_t * card, EVP_PKEY_free(root_ca_key); if (sub_ca_key) EVP_PKEY_free(sub_ca_key); - if (res != SC_SUCCESS) + if (res != SC_SUCCESS) { sc_log(ctx, "%s", msg); + } LOG_FUNC_RETURN(ctx, res); } /** * Verify CVC certificates in SM establishment process. * - * This is done by mean of 00 2A 00 AE + * This is done by mean of 00 2A 00 AE * (Perform Security Operation: Verify Certificate ) * * @param card pointer to card data @@ -421,11 +418,11 @@ static int cwa_verify_cvc_certificate(sc_card_t * card, /** * Alternate implementation for set_security environment. * - * Used to handle raw apdu data in set_security_env() on SM stblishment - * Standard set_securiy_env() method has sc_security_env->buffer limited + * Used to handle raw apdu data in set_security_env() on SM establishment + * Standard set_security_env() method has sc_security_env->buffer limited * to 8 bytes; so cannot send some of required SM commands. * - * @param card pointer to card data + * @param card pointer to card data * @param p1 apdu P1 parameter * @param p2 apdu P2 parameter * @param buffer raw data to be inserted in apdu @@ -463,7 +460,7 @@ static int cwa_set_security_env(sc_card_t * card, * * Internal (Card) authentication (let the card verify sent ifd certs) * - * @param card pointer to card data + * @param card pointer to card data * @param sig signature buffer * @param dig_len signature buffer length * @param data data to be sent in apdu @@ -504,7 +501,7 @@ static int cwa_internal_auth(sc_card_t * card, u8 * sig, size_t sig_len, u8 * da /** * Compose signature data for external auth according CWA-14890. - * + * * This code prepares data to be sent to ICC for external * authentication procedure * @@ -512,15 +509,15 @@ static int cwa_internal_auth(sc_card_t * card, u8 * sig, size_t sig_len, u8 * da * * @param card pointer to st_card_t card data information * @param icc_pubkey public key of card - * @param ifd_privkey private RSA key of ifd + * @param ifd_privkey private key of ifd * @param sn_icc card serial number * @param sig signature buffer * @param sig_len signature buffer length * @return SC_SUCCESS if ok; else errorcode */ static int cwa_prepare_external_auth(sc_card_t * card, - RSA * icc_pubkey, - RSA * ifd_privkey, + EVP_PKEY *icc_pubkey, + EVP_PKEY *ifd_privkey, u8 * sig, size_t sig_len) { @@ -532,31 +529,43 @@ static int cwa_prepare_external_auth(sc_card_t * card, PRND2 || - (74 bytes) random data to make buffer 128 bytes length Kifd || - (32 bytes)- ifd random generated key sha1_hash( - PRND2 || - Kifd || + PRND2 || + Kifd || RND.ICC || - (8 bytes) response to get_challenge() cmd SN.ICC - (8 bytes) serial number from get_serialnr() cmd - ) || + ) || 0xBC - iso 9796-2 padding ) - total: 128 bytes then, we should encrypt with our private key and then with icc pub key returning resulting data */ - char *msg; /* to store error messages */ + char *msg = NULL; /* to store error messages */ int res = SC_SUCCESS; - u8 *buf1; /* where to encrypt with icc pub key */ - u8 *buf2; /* where to encrypt with ifd pub key */ - u8 *buf3; /* where to compose message to be encrypted */ - int len1, len2, len3; - u8 *sha_buf; /* to compose message to be sha'd */ - u8 *sha_data; /* sha signature data */ + u8 *buf1 = NULL; /* where to encrypt with icc pub key */ + u8 *buf2 = NULL; /* where to encrypt with ifd pub key */ + u8 *buf3 = NULL; /* where to compose message to be encrypted */ + size_t len1 = 128, len2 = 128, len3 = 128; + u8 *sha_buf = NULL; /* to compose message to be sha'd */ + u8 *sha_data = NULL; /* sha signature data */ BIGNUM *bn = NULL; BIGNUM *bnsub = NULL; BIGNUM *bnres = NULL; sc_context_t *ctx = NULL; - const BIGNUM *ifd_privkey_n, *ifd_privkey_e, *ifd_privkey_d; struct sm_cwa_session * sm = &card->sm_ctx.info.session.cwa; + EVP_PKEY_CTX *pctx = NULL; + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const BIGNUM *ifd_privkey_n = NULL; + const RSA *rsa_ifd_privkey = EVP_PKEY_get0_RSA(ifd_privkey); + if (!rsa_ifd_privkey) { + res = SC_ERROR_INTERNAL; + msg = "Can not extract RSA object ifd priv"; + goto prepare_external_auth_end; + } +#else + BIGNUM *ifd_privkey_n = NULL; +#endif /* safety check */ if (!card || !card->ctx) @@ -580,8 +589,13 @@ static int cwa_prepare_external_auth(sc_card_t * card, /* compose buffer data */ buf3[0] = 0x6A; /* iso padding */ - RAND_bytes(buf3 + 1, 74); /* pRND */ - RAND_bytes(sm->ifd.k, 32); /* Kifd */ + if (RAND_bytes(buf3 + 1, 74) != 1 || /* pRND */ + RAND_bytes(sm->ifd.k, 32) != 1) { /* Kifd */ + sc_log_openssl(ctx); + msg = "prepare external auth: random data error"; + res = SC_ERROR_INTERNAL; + goto prepare_external_auth_end; + } memcpy(buf3 + 1 + 74, sm->ifd.k, 32); /* copy Kifd into buffer */ /* prepare data to be hashed */ memcpy(sha_buf, buf3 + 1, 74); /* copy pRND into sha_buf */ @@ -593,82 +607,109 @@ static int cwa_prepare_external_auth(sc_card_t * card, memcpy(buf3 + 1 + 74 + 32, sha_data, SHA_DIGEST_LENGTH); buf3[127] = 0xBC; /* iso padding */ - /* encrypt with ifd private key */ - len2 = RSA_private_decrypt(128, buf3, buf2, ifd_privkey, RSA_NO_PADDING); - if (len2 < 0) { - msg = "Prepare external auth: ifd_privk encrypt failed"; + /* decrypt with ifd private key */ + pctx = EVP_PKEY_CTX_new(ifd_privkey, NULL); + if (!pctx || + EVP_PKEY_decrypt_init(pctx) != 1 || + EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_NO_PADDING) != 1 || + EVP_PKEY_decrypt(pctx, buf2, &len2, buf3, 128) != 1) { + sc_log_openssl(ctx); + msg = "Prepare external auth: ifd_privk decrypt failed"; res = SC_ERROR_SM_ENCRYPT_FAILED; + EVP_PKEY_CTX_free(pctx); goto prepare_external_auth_end; } + EVP_PKEY_CTX_free(pctx); + pctx = NULL; /* evaluate value of minsig and store into buf3 */ - bn = BN_bin2bn(buf2, len2, NULL); + bn = BN_bin2bn(buf2, (int)len2, NULL); bnsub = BN_new(); if (!bn || !bnsub) { + sc_log_openssl(ctx); msg = "Prepare external auth: BN creation failed"; res = SC_ERROR_INTERNAL; goto prepare_external_auth_end; } - RSA_get0_key(ifd_privkey, &ifd_privkey_n, &ifd_privkey_e, &ifd_privkey_d); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA_get0_key(rsa_ifd_privkey, &ifd_privkey_n, NULL, NULL); +#else + if (EVP_PKEY_get_bn_param(ifd_privkey, OSSL_PKEY_PARAM_RSA_N, &ifd_privkey_n) != 1) { + sc_log_openssl(ctx); + msg = "Prepare external auth: BN get param failed"; + res = SC_ERROR_INTERNAL; + goto prepare_external_auth_end; + } +#endif + res = BN_sub(bnsub, ifd_privkey_n, bn); /* eval N.IFD-SIG */ if (res == 0) { /* 1:success 0 fail */ + sc_log_openssl(ctx); msg = "Prepare external auth: BN sigmin evaluation failed"; res = SC_ERROR_INTERNAL; goto prepare_external_auth_end; } bnres = (BN_cmp(bn, bnsub) < 0) ? bn : bnsub; /* choose min(SIG,N.IFD-SIG) */ if (BN_num_bytes(bnres) > 128) { + sc_log_openssl(ctx); msg = "Prepare external auth: BN sigmin result is too big"; res = SC_ERROR_INTERNAL; goto prepare_external_auth_end; } len3 = BN_bn2bin(bnres, buf3); /* convert result back into buf3 */ if (len3 <= 0) { + sc_log_openssl(ctx); msg = "Prepare external auth: BN to buffer conversion failed"; res = SC_ERROR_INTERNAL; goto prepare_external_auth_end; } /* re-encrypt result with icc public key */ - len1 = RSA_public_encrypt(len3, buf3, buf1, icc_pubkey, RSA_NO_PADDING); - if (len1 <= 0 || (size_t) len1 != sig_len) { + pctx = EVP_PKEY_CTX_new(icc_pubkey, NULL); + if (!pctx || + EVP_PKEY_encrypt_init(pctx) != 1 || + EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_NO_PADDING) != 1 || + EVP_PKEY_encrypt(pctx, buf1, &len1, buf3, 128) != 1 || + (size_t)len1 != sig_len) { + sc_log_openssl(ctx); msg = "Prepare external auth: icc_pubk encrypt failed"; res = SC_ERROR_SM_ENCRYPT_FAILED; + EVP_PKEY_CTX_free(pctx); goto prepare_external_auth_end; } + EVP_PKEY_CTX_free(pctx); /* process done: copy result into cwa_internal buffer and return success */ memcpy(sig, buf1, len1); res = SC_SUCCESS; prepare_external_auth_end: - if (bn) - BN_free(bn); - if (bnsub) - BN_free(bnsub); + BN_free(bn); + BN_free(bnsub); if (buf1) { - memset(buf1, 0, 128); + sc_mem_clear(buf1, 128); free(buf1); } if (buf2) { - memset(buf2, 0, 128); + sc_mem_clear(buf2, 128); free(buf2); } if (buf3) { - memset(buf3, 0, 128); + sc_mem_clear(buf3, 128); free(buf3); } if (sha_buf) { - memset(sha_buf, 0, 74 + 32 + 8 + 1 + 7); + sc_mem_clear(sha_buf, 74 + 32 + 8 + 1 + 7); free(sha_buf); } - if (sha_data) { - memset(sha_data, 0, SHA_DIGEST_LENGTH); - free(sha_data); - } + free(sha_data); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + BN_clear_free(ifd_privkey_n); +#endif - if (res != SC_SUCCESS) + if (res != SC_SUCCESS) { sc_log(ctx, "%s", msg); + } LOG_FUNC_RETURN(ctx, res); } @@ -677,7 +718,7 @@ static int cwa_prepare_external_auth(sc_card_t * card, * * Perform external (IFD) authenticate procedure (8.4.1.2) * - * @param card pointer to card data + * @param card pointer to card data * @param sig signature buffer * @param sig signature buffer length * @return SC_SUCCESS if OK: else error code @@ -770,17 +811,14 @@ static int cwa_compute_session_keys(sc_card_t * card) compute_session_keys_end: if (kseed) { - memset(kseed, 0, 32); + sc_mem_clear(kseed, 32); free(kseed); } if (data) { - memset(data, 0, 32 + 4); + sc_mem_clear(data, 32 + 4); free(data); } - if (sha_data) { - memset(sha_data, 0, SHA_DIGEST_LENGTH); - free(sha_data); - } + free(sha_data); if (res != SC_SUCCESS) sc_log(ctx, "%s", msg); else { @@ -821,14 +859,12 @@ static int cwa_compare_signature(u8 * data, size_t dlen, u8 * ifd_data) if (memcmp(data + 127 - SHA_DIGEST_LENGTH, sha, SHA_DIGEST_LENGTH) == 0) res = SC_SUCCESS; compare_signature_end: - if (buf) - free(buf); - if (sha) - free(sha); + free(buf); + free(sha); return res; } -/** +/** * check the result of internal_authenticate operation. * * Checks icc received data from internal auth procedure against @@ -844,8 +880,8 @@ static int cwa_compare_signature(u8 * data, size_t dlen, u8 * ifd_data) * @return SC_SUCCESS if ok; else error code */ static int cwa_verify_internal_auth(sc_card_t * card, - RSA * icc_pubkey, - RSA * ifd_privkey, + EVP_PKEY *icc_pubkey, + EVP_PKEY *ifd_privkey, u8 * ifdbuf, size_t ifdlen, u8 * sig, @@ -856,14 +892,24 @@ static int cwa_verify_internal_auth(sc_card_t * card, u8 *buf1 = NULL; /* to decrypt with our private key */ u8 *buf2 = NULL; /* to try SIGNUM==SIG */ u8 *buf3 = NULL; /* to try SIGNUM==N.ICC-SIG */ - int len1 = 0; - int len2 = 0; - int len3 = 0; + size_t len1 = 128, len2 = 128, len3 = 128; BIGNUM *bn = NULL; BIGNUM *sigbn = NULL; sc_context_t *ctx = NULL; - const BIGNUM *icc_pubkey_n, *icc_pubkey_e, *icc_pubkey_d; struct sm_cwa_session * sm = &card->sm_ctx.info.session.cwa; + EVP_PKEY_CTX *pctx = NULL; + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const BIGNUM *icc_pubkey_n = NULL; + const RSA *rsa_icc_pubkey = EVP_PKEY_get0_RSA(icc_pubkey); + if (!rsa_icc_pubkey) { + res = SC_ERROR_INTERNAL; + msg = "Can not extract RSA object icc pub"; + goto verify_internal_done; + } +#else + BIGNUM *icc_pubkey_n = NULL; +#endif if (!card || !card->ctx) return SC_ERROR_INVALID_ARGUMENTS; @@ -888,7 +934,7 @@ static int cwa_verify_internal_auth(sc_card_t * card, goto verify_internal_done; } - /* + /* We have received data with this format: sigbuf = E[PK.IFD.AUT](SIGMIN) SIGMIN = min ( SIG, N.ICC-SIG ) @@ -897,62 +943,98 @@ static int cwa_verify_internal_auth(sc_card_t * card, PRND1 || Kicc || sha1_hash(PRND1 || Kicc || RND.IFD || SN.IFD) || - 0xBC + 0xBC ) So we should reverse the process and try to get valid results */ /* decrypt data with our ifd priv key */ - len1 = RSA_private_decrypt(sig_len, sig, buf1, ifd_privkey, RSA_NO_PADDING); - if (len1 <= 0) { + pctx = EVP_PKEY_CTX_new(ifd_privkey, NULL); + if (!pctx || + EVP_PKEY_decrypt_init(pctx) != 1 || + EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_NO_PADDING) != 1 || + EVP_PKEY_decrypt(pctx, buf1, &len1, sig, sig_len) != 1) { + sc_log_openssl(ctx); msg = "Verify Signature: decrypt with ifd privk failed"; res = SC_ERROR_SM_ENCRYPT_FAILED; + EVP_PKEY_CTX_free(pctx); goto verify_internal_done; } + EVP_PKEY_CTX_free(pctx); + pctx = NULL; /* OK: now we have SIGMIN in buf1 */ /* check if SIGMIN data matches SIG or N.ICC-SIG */ /* evaluate DS[SK.ICC.AUTH](SIG) trying to decrypt with icc pubk */ - len3 = RSA_public_encrypt(len1, buf1, buf3, icc_pubkey, RSA_NO_PADDING); - if (len3 <= 0) + pctx = EVP_PKEY_CTX_new(icc_pubkey, NULL); + if (!pctx || + EVP_PKEY_encrypt_init(pctx) != 1 || + EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_NO_PADDING) != 1 || + EVP_PKEY_encrypt(pctx, buf3, &len3, buf1, len1) != 1) { + EVP_PKEY_CTX_free(pctx); + sc_log_openssl(ctx); goto verify_nicc_sig; /* evaluate N.ICC-SIG and retry */ + } + + EVP_PKEY_CTX_free(pctx); + res = cwa_compare_signature(buf3, len3, ifdbuf); if (res == SC_SUCCESS) goto verify_internal_ok; verify_nicc_sig: - /* - * Arriving here means need to evaluate N.ICC-SIG + /* + * Arriving here means need to evaluate N.ICC-SIG * So convert buffers to bignums to operate */ - bn = BN_bin2bn(buf1, len1, NULL); /* create BN data */ + bn = BN_bin2bn(buf1, (int)len1, NULL); /* create BN data */ sigbn = BN_new(); if (!bn || !sigbn) { + sc_log_openssl(ctx); msg = "Verify Signature: cannot bignums creation error"; res = SC_ERROR_OUT_OF_MEMORY; goto verify_internal_done; } - RSA_get0_key(icc_pubkey, &icc_pubkey_n, &icc_pubkey_e, &icc_pubkey_d); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA_get0_key(rsa_icc_pubkey, &icc_pubkey_n, NULL, NULL); +#else + if (EVP_PKEY_get_bn_param(icc_pubkey, OSSL_PKEY_PARAM_RSA_N, &icc_pubkey_n) != 1) { + sc_log_openssl(ctx); + msg = "Verify Signature: BN get param failed"; + res = SC_ERROR_INTERNAL; + goto verify_internal_done; + } +#endif res = BN_sub(sigbn, icc_pubkey_n, bn); /* eval N.ICC-SIG */ if (!res) { + sc_log_openssl(ctx); msg = "Verify Signature: evaluation of N.ICC-SIG failed"; res = SC_ERROR_INTERNAL; goto verify_internal_done; } len2 = BN_bn2bin(sigbn, buf2); /* copy result to buffer */ if (len2 <= 0) { - msg = "Verify Signature: cannot conver bignum to buffer"; + sc_log_openssl(ctx); + msg = "Verify Signature: cannot convert bignum to buffer"; res = SC_ERROR_INTERNAL; goto verify_internal_done; } /* ok: check again with new data */ /* evaluate DS[SK.ICC.AUTH](I.ICC-SIG) trying to decrypt with icc pubk */ - len3 = RSA_public_encrypt(len2, buf2, buf3, icc_pubkey, RSA_NO_PADDING); - if (len3 <= 0) { + pctx = EVP_PKEY_CTX_new(icc_pubkey, NULL); + if (!pctx || + EVP_PKEY_encrypt_init(pctx) != 1 || + EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_NO_PADDING) != 1 || + EVP_PKEY_encrypt(pctx, buf3, &len3, buf2, len2) != 1) { + sc_log_openssl(ctx); msg = "Verify Signature: cannot get valid SIG data"; res = SC_ERROR_INVALID_DATA; + EVP_PKEY_CTX_free(pctx); goto verify_internal_done; } + EVP_PKEY_CTX_free(pctx); + pctx = NULL; + res = cwa_compare_signature(buf3, len3, ifdbuf); if (res != SC_SUCCESS) { msg = "Verify Signature: cannot get valid SIG data"; @@ -964,29 +1046,28 @@ static int cwa_verify_internal_auth(sc_card_t * card, memcpy(sm->icc.k, buf3 + 1 + 74, 32); /* extract Kicc from buf3 */ res = SC_SUCCESS; verify_internal_done: - if (buf1) - free(buf1); - if (buf2) - free(buf2); - if (buf3) - free(buf3); - if (bn) - BN_free(bn); - if (sigbn) - BN_free(sigbn); - if (res != SC_SUCCESS) + free(buf1); + free(buf2); + free(buf3); + BN_free(bn); + BN_free(sigbn); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + BN_clear_free(icc_pubkey_n); +#endif + if (res != SC_SUCCESS) { sc_log(ctx, "%s", msg); + } LOG_FUNC_RETURN(ctx, res); } /** * Create Secure Messaging channel. * - * This is the main entry point for CWA14890 SM chanel creation. + * This is the main entry point for CWA14890 SM channel creation. * It closely follows cwa standard, with a minor modification: - * - ICC serial number is taken at the begining of SM creation + * - ICC serial number is taken at the beginning of SM creation * - ICC and IFD certificate agreement process is reversed, to allow - * card to retain key references on further proccess (this behavior + * card to retain key references on further process (this behavior * is also defined in standard) * * Based on Several documents: @@ -1029,7 +1110,7 @@ int cwa_create_secure_channel(sc_card_t * card, return SC_ERROR_INVALID_ARGUMENTS; if (!provider) return SC_ERROR_SM_NOT_INITIALIZED; - /* comodity vars */ + /* commodity vars */ ctx = card->ctx; LOG_FUNC_CALLED(ctx); @@ -1039,7 +1120,11 @@ int cwa_create_secure_channel(sc_card_t * card, case CWA_SM_OFF: /* disable SM */ card->sm_ctx.sm_mode = SM_MODE_NONE; sc_log(ctx, "Setting CWA SM status to none"); - LOG_FUNC_RETURN(ctx, SC_SUCCESS); + /* request the Master File to provoke an SM error and close the channel */ + res = sc_select_file(card, sc_get_mf_path(), NULL); + if (res == SC_ERROR_SM) + res = SC_SUCCESS; + LOG_FUNC_RETURN(ctx, res); case CWA_SM_ON: /* force sm initialization process */ sc_log(ctx, "CWA SM initialization requested"); break; @@ -1077,7 +1162,7 @@ int cwa_create_secure_channel(sc_card_t * card, goto csc_end; } - /* + /* * Notice that this code inverts ICC and IFD certificate standard * checking sequence. */ @@ -1085,7 +1170,7 @@ int cwa_create_secure_channel(sc_card_t * card, /* Read Intermediate CA from card */ if (!provider->cwa_get_icc_intermediate_ca_cert) { sc_log(ctx, - "Step 8.4.1.6: Skip Retrieveing ICC intermediate CA"); + "Step 8.4.1.6: Skip Retrieving ICC intermediate CA"); ca_cert = NULL; } else { sc_log(ctx, "Step 8.4.1.7: Retrieving ICC intermediate CA"); @@ -1124,7 +1209,12 @@ int cwa_create_secure_channel(sc_card_t * card, } /* Extract public key from ICC certificate */ - icc_pubkey = X509_get_pubkey(icc_cert); + if (!(icc_pubkey = X509_get_pubkey(icc_cert))) { + res = SC_ERROR_INTERNAL; + sc_log_openssl(ctx); + msg = "Cannot extract public key from ICC certificate"; + goto csc_end; + } /* Select Root CA in card for ifd certificate verification */ sc_log(ctx, @@ -1187,7 +1277,7 @@ int cwa_create_secure_channel(sc_card_t * card, res = cwa_compose_tlv(card, 0x83, bufferlen, buffer, &tlv, &tlvlen); if (res != SC_SUCCESS) { msg = - "Cannot compose tlv for setting intermeditate CA key reference"; + "Cannot compose tlv for setting intermediate CA key reference"; goto csc_end; } res = cwa_set_security_env(card, 0x81, 0xB6, tlv, tlvlen); @@ -1196,7 +1286,7 @@ int cwa_create_secure_channel(sc_card_t * card, goto csc_end; } - /* Send IFD certiticate in CVC format C_CV_IFD */ + /* Send IFD certificate in CVC format C_CV_IFD */ sc_log(ctx, "Step 8.4.1.5: Send CVC IFD Certificate for ICC verification"); res = provider->cwa_get_cvc_ifd_cert(card, &cert, &certlen); @@ -1251,7 +1341,7 @@ int cwa_create_secure_channel(sc_card_t * card, goto csc_end; } - /* Internal (Card) authentication (let the card verify sent ifd certs) + /* Internal (Card) authentication (let the card verify sent ifd certs) SN.IFD equals 8 lsb bytes of ifd.pubk ref according cwa14890 sec 8.4.1 */ sc_log(ctx, "Step 8.4.1.10: Perform Internal authentication"); res = provider->cwa_get_sn_ifd(card); @@ -1259,7 +1349,12 @@ int cwa_create_secure_channel(sc_card_t * card, msg = "Cannot get ifd serial number from provider"; goto csc_end; } - RAND_bytes(sm->ifd.rnd, 8); /* generate 8 random bytes */ + /* generate 8 random bytes */ + if (RAND_bytes(sm->ifd.rnd, 8) != 1) { + msg = "Cannot generate random data"; + res = SC_ERROR_INTERNAL; + goto csc_end; + } memcpy(rndbuf, sm->ifd.rnd, 8); /* insert RND.IFD into rndbuf */ memcpy(rndbuf + 8, sm->ifd.sn, 8); /* insert SN.IFD into rndbuf */ res = cwa_internal_auth(card, sig, 128, rndbuf, 16); @@ -1278,8 +1373,8 @@ int cwa_create_secure_channel(sc_card_t * card, /* verify received signature */ sc_log(ctx, "Verify Internal Auth command response"); - res = cwa_verify_internal_auth(card, EVP_PKEY_get0_RSA(icc_pubkey), /* evaluated icc public key */ - EVP_PKEY_get0_RSA(ifd_privkey), /* evaluated from DGP's Manual Annex 3 Data */ + res = cwa_verify_internal_auth(card, icc_pubkey, /* evaluated icc public key */ + ifd_privkey, /* evaluated from DGP's Manual Annex 3 Data */ rndbuf, /* RND.IFD || SN.IFD */ 16, /* rndbuf length; should be 16 */ sig, 128 @@ -1291,16 +1386,14 @@ int cwa_create_secure_channel(sc_card_t * card, /* get challenge: retrieve 8 random bytes from card */ sc_log(ctx, "Step 8.4.1.11: Prepare External Auth: Get Challenge"); - res = card->ops->get_challenge(card, sm->icc.rnd, sizeof(sm->icc.rnd)); + res = sc_get_challenge(card, sm->icc.rnd, sizeof(sm->icc.rnd)); if (res != SC_SUCCESS) { msg = "Get Challenge failed"; goto csc_end; } /* compose signature data for external auth */ - res = cwa_prepare_external_auth(card, - EVP_PKEY_get0_RSA(icc_pubkey), - EVP_PKEY_get0_RSA(ifd_privkey), sig, 128); + res = cwa_prepare_external_auth(card, icc_pubkey, ifd_privkey, sig, 128); if (res != SC_SUCCESS) { msg = "Prepare external auth failed"; goto csc_end; @@ -1336,14 +1429,10 @@ int cwa_create_secure_channel(sc_card_t * card, res = SC_SUCCESS; csc_end: free(tlv); - if (icc_cert) - X509_free(icc_cert); - if (ca_cert) - X509_free(ca_cert); - if (icc_pubkey) - EVP_PKEY_free(icc_pubkey); - if (ifd_privkey) - EVP_PKEY_free(ifd_privkey); + X509_free(icc_cert); + X509_free(ca_cert); + EVP_PKEY_free(icc_pubkey); + EVP_PKEY_free(ifd_privkey); /* setup SM state according result */ if (res != SC_SUCCESS) { sc_log(ctx, "%s", msg); @@ -1378,8 +1467,6 @@ int cwa_encode_apdu(sc_card_t * card, u8 *ccbuf = NULL; /* where to store data to eval cryptographic checksum CC */ size_t cclen = 0; u8 macbuf[8]; /* to store and compute CC */ - DES_key_schedule k1; - DES_key_schedule k2; char *msg = NULL; size_t i, j; /* for xor loops */ @@ -1389,6 +1476,11 @@ int cwa_encode_apdu(sc_card_t * card, u8 *msgbuf = NULL; /* to encrypt apdu data */ u8 *cryptbuf = NULL; + EVP_CIPHER_CTX *cctx = NULL; + EVP_CIPHER *alg = NULL; + unsigned char *key = NULL; + int tmplen = 0; + /* mandatory check */ if (!card || !card->ctx || !provider) return SC_ERROR_INVALID_ARGUMENTS; @@ -1431,11 +1523,9 @@ int cwa_encode_apdu(sc_card_t * card, sizeof(u8)); ccbuf = calloc(MAX(SC_MAX_APDU_BUFFER_SIZE, 20 + from->datalen), sizeof(u8)); - if (!to->resp) { - /* if no response create a buffer for the encoded response */ - to->resp = calloc(MAX_RESP_BUFFER_SIZE, sizeof(u8)); - to->resplen = MAX_RESP_BUFFER_SIZE; - } + /* always create a new buffer for the encoded response */ + to->resp = calloc(MAX_RESP_BUFFER_SIZE, sizeof(u8)); + to->resplen = MAX_RESP_BUFFER_SIZE; if (!apdubuf || !ccbuf || (!from->resp && !to->resp)) { res = SC_ERROR_OUT_OF_MEMORY; goto err; @@ -1458,53 +1548,68 @@ int cwa_encode_apdu(sc_card_t * card, *(ccbuf + cclen++) = to->p2; cwa_iso7816_padding(ccbuf, &cclen); /* pad header (4 bytes pad) */ + if (!(cctx = EVP_CIPHER_CTX_new())) { + res = SC_ERROR_INTERNAL; + goto err; + } + /* if no data, skip data encryption step */ if (from->lc != 0) { - size_t dlen = from->lc; - - /* prepare keys */ - DES_cblock iv = { 0, 0, 0, 0, 0, 0, 0, 0 }; - DES_set_key_unchecked((const_DES_cblock *) & (sm_session->session_enc[0]), - &k1); - DES_set_key_unchecked((const_DES_cblock *) & (sm_session->session_enc[8]), - &k2); + unsigned char iv[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int dlen = (int)from->lc; + size_t len = dlen; /* pad message */ memcpy(msgbuf, from->data, dlen); - cwa_iso7816_padding(msgbuf, &dlen); + cwa_iso7816_padding(msgbuf, &len); + dlen = (int)len; /* start kriptbuff with iso padding indicator */ *cryptbuf = 0x01; - /* aply TDES + CBC with kenc and iv=(0,..,0) */ - DES_ede3_cbc_encrypt(msgbuf, cryptbuf + 1, dlen, &k1, &k2, &k1, - &iv, DES_ENCRYPT); + key = sm_session->session_enc; + + alg = sc_evp_cipher(card->ctx, "DES-EDE-CBC"); + + if (!alg || + EVP_EncryptInit_ex(cctx, alg, NULL, key, iv) != 1 || + EVP_CIPHER_CTX_set_padding(cctx, 0) != 1 || + EVP_EncryptUpdate(cctx, cryptbuf + 1, &dlen, msgbuf, dlen) != 1 || + EVP_EncryptFinal_ex(cctx, cryptbuf + 1 + dlen, &tmplen) != 1) { + msg = "Error in encrypting APDU"; + res = SC_ERROR_INTERNAL; + goto encode_end; + } + dlen += tmplen; + /* compose data TLV and add to result buffer */ - res = - cwa_compose_tlv(card, 0x87, dlen + 1, cryptbuf, &ccbuf, - &cclen); + res = cwa_compose_tlv(card, 0x87, dlen + 1, cryptbuf, &ccbuf, &cclen); if (res != SC_SUCCESS) { msg = "Error in compose tag 8x87 TLV"; goto encode_end; } } else if ((0xff & from->le) > 0) { - - /* if le byte is declared, compose and add Le TLV */ - /* FIXME: For DNIe we must not send the le bytes - when le == 256 but this goes against the standard - and might break other cards reusing this code */ - /* NOTE: In FNMT MultiPKCS11 code this is an if, i.e., - the le is only sent if no data (lc) is set. - In DNIe 3.0 pin verification sending both TLV return - 69 88 "SM Data Object incorrect". For the moment it is - fixed sendind le=0 in pin verification apdu */ - u8 le = 0xff & from->le; - res = cwa_compose_tlv(card, 0x97, 1, &le, &ccbuf, &cclen); - if (res != SC_SUCCESS) { - msg = "Encode APDU compose_tlv(0x97) failed"; - goto encode_end; - } + /* if le byte is declared, compose and add Le TLV */ + /* FIXME: For DNIe we must not send the le bytes + when le == 256 but this goes against the standard + and might break other cards reusing this code */ + /* NOTE: In FNMT MultiPKCS11 code this is an if, i.e., + the le is only sent if no data (lc) is set. + In DNIe 3.0 pin verification sending both TLV return + 69 88 "SM Data Object incorrect". For the moment it is + fixed sendind le=0 in pin verification apdu */ + u8 le = 0xff & from->le; + res = cwa_compose_tlv(card, 0x97, 1, &le, &ccbuf, &cclen); + if (res != SC_SUCCESS) { + msg = "Encode APDU compose_tlv(0x97) failed"; + goto encode_end; + } } /* copy current data to apdu buffer (skip header and header padding) */ + if (cclen < 8) { + res = SC_ERROR_INTERNAL; + msg = "Incorrect checksum length"; + goto encode_end; + } memcpy(apdubuf, ccbuf + 8, cclen - 8); apdulen = cclen - 8; /* pad again ccbuffer to compute CC */ @@ -1517,33 +1622,63 @@ int cwa_encode_apdu(sc_card_t * card, msg = "Error in computing SSC"; goto encode_end; } - /* set up keys for mac computing */ - DES_set_key_unchecked((const_DES_cblock *) & (sm_session->session_mac[0]),&k1); - DES_set_key_unchecked((const_DES_cblock *) & (sm_session->session_mac[8]),&k2); memcpy(macbuf, sm_session->ssc, 8); /* start with computed SSC */ + + tmplen = 0; + key = sm_session->session_mac; + + sc_evp_cipher_free(alg); + alg = sc_evp_cipher(card->ctx, "DES-ECB"); + if (!alg || + EVP_EncryptInit_ex(cctx, alg, NULL, key, NULL) != 1 || + EVP_CIPHER_CTX_set_padding(cctx, 0) != 1) { + msg = "Error in DES ECB encryption"; + res = SC_ERROR_INTERNAL; + goto encode_end; + } + for (i = 0; i < cclen; i += 8) { /* divide data in 8 byte blocks */ /* compute DES */ - DES_ecb_encrypt((const_DES_cblock *) macbuf, - (DES_cblock *) macbuf, &k1, DES_ENCRYPT); + if (EVP_EncryptUpdate(cctx, macbuf, &tmplen, macbuf , 8) != 1) { + msg = "Error in DES ECB encryption"; + res = SC_ERROR_INTERNAL; + goto encode_end; + } /* XOR with next data and repeat */ for (j = 0; j < 8; j++) macbuf[j] ^= ccbuf[i + j]; } + if (EVP_EncryptFinal_ex(cctx, macbuf + tmplen, &tmplen) != 1) { + msg = "Error in DES ECB encryption"; + res = SC_ERROR_INTERNAL; + goto encode_end; + } + /* and apply 3DES to result */ - DES_ecb2_encrypt((const_DES_cblock *) macbuf, (DES_cblock *) macbuf, - &k1, &k2, DES_ENCRYPT); + sc_evp_cipher_free(alg); + alg = sc_evp_cipher(card->ctx, "DES-EDE-ECB"); + + if (!alg || + EVP_EncryptInit_ex(cctx, alg, NULL, key, NULL) != 1 || + EVP_CIPHER_CTX_set_padding(cctx, 0) != 1 || + EVP_EncryptUpdate(cctx, macbuf, &tmplen, macbuf, 8) != 1 || + EVP_EncryptFinal_ex(cctx, macbuf + tmplen, &tmplen) != 1) { + msg = "Error in 3DEC ECB encryption"; + res = SC_ERROR_INTERNAL; + goto encode_end; + } /* compose and add computed MAC TLV to result buffer */ tlv_len = (card->atr.value[15] >= DNIE_30_VERSION)? 8 : 4; - sc_log(ctx, "Using TLV lenght: %"SC_FORMAT_LEN_SIZE_T"u", tlv_len); + sc_log(ctx, "Using TLV length: %"SC_FORMAT_LEN_SIZE_T"u", tlv_len); res = cwa_compose_tlv(card, 0x8E, tlv_len, macbuf, &apdubuf, &apdulen); if (res != SC_SUCCESS) { msg = "Encode APDU compose_tlv(0x87) failed"; goto encode_end; } - /* rewrite resulting header */ + /* rewrite resulting APDU */ to->lc = apdulen; to->data = apdubuf; to->datalen = apdulen; @@ -1554,13 +1689,18 @@ int cwa_encode_apdu(sc_card_t * card, err: encode_end: - if (apdubuf) - free(apdubuf); - if (from->resp != to->resp) + free(apdubuf); + if (from->resp != to->resp) { free(to->resp); + to->resp = NULL; + } encode_end_apdu_valid: - if (msg) + sc_evp_cipher_free(alg); + EVP_CIPHER_CTX_free(cctx); + if (msg) { + sc_log_openssl(ctx); sc_log(ctx, "%s", msg); + } free(msgbuf); free(cryptbuf); free(ccbuf); @@ -1596,13 +1736,21 @@ int cwa_decode_response(sc_card_t * card, size_t cclen = 0; /* ccbuf len */ u8 macbuf[8]; /* where to calculate mac */ size_t resplen = 0; /* respbuf length */ - DES_key_schedule k1; - DES_key_schedule k2; int res = SC_SUCCESS; char *msg = NULL; /* to store error messages */ sc_context_t *ctx = NULL; struct sm_cwa_session * sm_session = &card->sm_ctx.info.session.cwa; + EVP_CIPHER_CTX *cctx = NULL; + EVP_CIPHER *alg = NULL; + unsigned char *key = NULL; + int tmplen = 0; + + if ((cctx = EVP_CIPHER_CTX_new()) == NULL) { + sc_log_openssl(ctx); + return SC_ERROR_INTERNAL; + } + /* mandatory check */ if (!card || !card->ctx || !provider) return SC_ERROR_INVALID_ARGUMENTS; @@ -1722,22 +1870,53 @@ int cwa_decode_response(sc_card_t * card, msg = "Error in computing SSC"; goto response_decode_end; } - /* set up keys for mac computing */ - DES_set_key_unchecked((const_DES_cblock *) & (sm_session->session_mac[0]), &k1); - DES_set_key_unchecked((const_DES_cblock *) & (sm_session->session_mac[8]), &k2); + /* set up key for mac computing */ + key = sm_session->session_mac; + + alg = sc_evp_cipher(card->ctx, "DES-ECB"); + if (!alg || + EVP_EncryptInit_ex(cctx, alg, NULL, key, NULL) != 1 || + EVP_CIPHER_CTX_set_padding(cctx, 0) != 1) { + sc_log_openssl(ctx); + msg = "Error in DES ECB encryption"; + res = SC_ERROR_INTERNAL; + goto response_decode_end; + } memcpy(macbuf, sm_session->ssc, 8); /* start with computed SSC */ for (i = 0; i < cclen; i += 8) { /* divide data in 8 byte blocks */ /* compute DES */ - DES_ecb_encrypt((const_DES_cblock *) macbuf, - (DES_cblock *) macbuf, &k1, DES_ENCRYPT); + if (EVP_EncryptUpdate(cctx, macbuf, &tmplen, macbuf, 8) != 1) { + sc_log_openssl(ctx); + msg = "Error in DES ECB encryption"; + res = SC_ERROR_INTERNAL; + goto response_decode_end; + } /* XOR with data and repeat */ for (j = 0; j < 8; j++) macbuf[j] ^= ccbuf[i + j]; } + if (EVP_EncryptFinal_ex(cctx, macbuf + tmplen, &tmplen) != 1) { + sc_log_openssl(ctx); + msg = "Error in DES ECB encryption"; + res = SC_ERROR_INTERNAL; + goto response_decode_end; + } + /* finally apply 3DES to result */ - DES_ecb2_encrypt((const_DES_cblock *) macbuf, (DES_cblock *) macbuf, - &k1, &k2, DES_ENCRYPT); + sc_evp_cipher_free(alg); + alg = sc_evp_cipher(card->ctx, "DES-EDE-ECB"); + + if (!alg || + EVP_EncryptInit_ex(cctx, alg, NULL, key, NULL) != 1 || + EVP_CIPHER_CTX_set_padding(cctx, 0) != 1 || + EVP_EncryptUpdate(cctx, macbuf, &tmplen, macbuf, 8) != 1 || + EVP_EncryptFinal_ex(cctx, macbuf + tmplen, &tmplen) != 1) { + sc_log_openssl(ctx); + msg = "Error in 3DEC ECB encryption"; + res = SC_ERROR_INTERNAL; + goto response_decode_end; + } /* check evaluated mac with provided by apdu response */ @@ -1767,7 +1946,8 @@ int cwa_decode_response(sc_card_t * card, /* if encoded data, decode and store into apdu response */ else if (e_tlv->buf) { /* encoded data */ - DES_cblock iv = { 0, 0, 0, 0, 0, 0, 0, 0 }; + unsigned char iv[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int dlen = (int)apdu->resplen; /* check data len */ if ((e_tlv->len < 9) || ((e_tlv->len - 1) % 8) != 0) { msg = "Invalid length for Encoded data TLV"; @@ -1781,21 +1961,30 @@ int cwa_decode_response(sc_card_t * card, goto response_decode_end; } /* prepare keys to decode */ - DES_set_key_unchecked((const_DES_cblock *) & (sm_session->session_enc[0]), - &k1); - DES_set_key_unchecked((const_DES_cblock *) & (sm_session->session_enc[8]), - &k2); + key = sm_session->session_enc; + /* decrypt into response buffer * by using 3DES CBC by mean of kenc and iv={0,...0} */ - DES_ede3_cbc_encrypt(&e_tlv->data[1], apdu->resp, e_tlv->len - 1, - &k1, &k2, &k1, &iv, DES_DECRYPT); - apdu->resplen = e_tlv->len - 1; + sc_evp_cipher_free(alg); + alg = sc_evp_cipher(card->ctx, "DES-EDE-CBC"); + + if (!alg || + EVP_DecryptInit_ex(cctx, alg, NULL, key, iv) != 1 || + EVP_CIPHER_CTX_set_padding(cctx, 0) != 1 || + EVP_DecryptUpdate(cctx, apdu->resp, &dlen, &e_tlv->data[1], (int)(e_tlv->len - 1)) != 1 || + EVP_DecryptFinal_ex(cctx, apdu->resp + dlen, &tmplen) != 1) { + sc_log_openssl(ctx); + res = SC_ERROR_INTERNAL; + msg = "Can not decrypt 3DES CBC"; + goto response_decode_end; + } + apdu->resplen = dlen + tmplen; + /* remove iso padding from response length */ for (; (apdu->resplen > 0) && *(apdu->resp + apdu->resplen - 1) == 0x00; apdu->resplen--) ; /* empty loop */ if (*(apdu->resp + apdu->resplen - 1) != 0x80) { /* check padding byte */ - msg = - "Decrypted TLV has no 0x80 iso padding indicator!"; + msg = "Decrypted TLV has no 0x80 iso padding indicator!"; res = SC_ERROR_INVALID_DATA; goto response_decode_end; } @@ -1810,10 +1999,10 @@ int cwa_decode_response(sc_card_t * card, res = SC_SUCCESS; response_decode_end: - if (buffer) - free(buffer); - if (ccbuf) - free(ccbuf); + sc_evp_cipher_free(alg); + EVP_CIPHER_CTX_free(cctx); + free(buffer); + free(ccbuf); if (msg) { sc_log(ctx, "%s", msg); } else { diff --git a/src/libopensc/cwa14890.h b/src/libopensc/cwa14890.h index ca74cda441..013f4aa883 100644 --- a/src/libopensc/cwa14890.h +++ b/src/libopensc/cwa14890.h @@ -19,7 +19,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __CWA14890_H__ @@ -60,9 +60,9 @@ typedef struct cwa_provider_st { * * This code is called before any operation required in * standard cwa14890 SM stablisment process. It's usually - * used for adquiring/initialize data to be used in the + * used for acquiring/initialize data to be used in the * process (i.e: retrieve card serial number), to make sure - * that no extra apdu is sent during the SM stablishment procedure + * that no extra apdu is sent during the SM establishment procedure * * @param card pointer to card driver structure * @param provider pointer to SM data provider for DNIe @@ -74,8 +74,8 @@ typedef struct cwa_provider_st { /** * CWA-14890 SM stablisment post-operations. * - * This code is called after sucessfull SM chanel stablishment - * procedure, and before returning from create_sm_chanel() function + * This code is called after successful SM channel establishment + * procedure, and before returning from create_sm_channel() function * May be use for store data, trace, logs and so * * @param card pointer to card driver structure @@ -115,7 +115,7 @@ typedef struct cwa_provider_st { /** * Get RSA IFD (Terminal) private key data. * - * Notice that resulting data should be keept in memory as little + * Notice that resulting data should be kept in memory as little * as possible Erasing them once used * * @param card pointer to card driver structure @@ -131,7 +131,7 @@ typedef struct cwa_provider_st { * * But to do this, an special OpenSSL with PACE extensions is * needed. In the meantime, let's use binary buffers to get - * CVC and key references, until an CV_CERT hancling API + * CVC and key references, until an CV_CERT handling API * become available in standard OpenSSL * *@see http://openpace.sourceforge.net @@ -150,7 +150,7 @@ typedef struct cwa_provider_st { * @return SC_SUCCESS if ok; else error code */ int (*cwa_get_cvc_ca_cert) (sc_card_t * card, u8 ** cert, - size_t * lenght); + size_t * length); /** * Retrieve IFD (application) CVC certificate and length. @@ -165,7 +165,7 @@ typedef struct cwa_provider_st { * @return SC_SUCCESS if ok; else error code */ int (*cwa_get_cvc_ifd_cert) (sc_card_t * card, u8 ** cert, - size_t * lenght); + size_t * length); /** * Retrieve public key reference for Root CA to validate CVC intermediate CA certs. diff --git a/src/libopensc/dir.c b/src/libopensc/dir.c index 59e988d77c..25db151dd3 100644 --- a/src/libopensc/dir.c +++ b/src/libopensc/dir.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -39,7 +39,11 @@ static const struct app_entry apps[] = { { (const u8 *) "\xA0\x00\x00\x00\x63PKCS-15", 12, "PKCS #15" }, { (const u8 *) "\xA0\x00\x00\x01\x77PKCS-15", 12, "Belgian eID" }, { (const u8 *) "\x44\x46\x20\x69\x73\x73\x75\x65\x72", 9, "Portugal eID" }, - { (const u8 *) "\xE8\x28\xBD\x08\x0F\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E", 15, "ESIGN"} + { (const u8 *) "\xE8\x28\xBD\x08\x0F\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E", 15, "ESIGN"}, + { (const u8 *) "\xE8\x28\xBD\x08\x0F\x80\x25\x00\x00\x01\xFF\x00\x10", 13, "CPx IAS"}, + { (const u8 *) "\xE8\x28\xBD\x08\x0F\x80\x25\x00\x00\x01\xFF\x00\x20", 13, "CPx IAS CL"}, + { (const u8 *) "\xE8\x28\xBD\x08\x0F\xD2\x50\x45\x43\x43\x2D\x65\x49\x44", 14, "ECC eID"}, + { (const u8 *) "\xE8\x28\xBD\x08\x0F\xD2\x50\x47\x65\x6E\x65\x72\x69\x63", 14, "ECC Generic PKI"}, }; static const struct sc_asn1_entry c_asn1_dirrecord[] = { @@ -65,12 +69,13 @@ parse_dir_record(sc_card_t *card, u8 ** buf, size_t *buflen, int rec_nr) sc_app_info_t *app = NULL; struct sc_aid aid; u8 label[128], path[128], ddo[128]; - size_t label_len = sizeof(label), path_len = sizeof(path), ddo_len = sizeof(ddo); + size_t label_len = sizeof(label) - 1, path_len = sizeof(path), ddo_len = sizeof(ddo); int r; LOG_FUNC_CALLED(ctx); aid.len = sizeof(aid.value); + memset(label, 0, sizeof(label)); sc_copy_asn1_entry(c_asn1_dirrecord, asn1_dirrecord); sc_copy_asn1_entry(c_asn1_dir, asn1_dir); sc_format_asn1_entry(asn1_dir + 0, asn1_dirrecord, NULL, 0); @@ -93,8 +98,8 @@ parse_dir_record(sc_card_t *card, u8 ** buf, size_t *buflen, int rec_nr) sc_bin_to_hex(aid.value, aid.len, aid_str, sizeof(aid_str), 0); blocks = scconf_find_blocks(card->ctx->conf, conf_block, "application", aid_str); if (blocks) { - ignore_app = (blocks[0] && scconf_get_str(blocks[0], "disable", 0)); - free(blocks); + ignore_app = (blocks[0] && scconf_get_bool(blocks[0], "disable", 0)); + free(blocks); } if (ignore_app) { @@ -117,6 +122,7 @@ parse_dir_record(sc_card_t *card, u8 ** buf, size_t *buflen, int rec_nr) if (asn1_dirrecord[2].flags & SC_ASN1_PRESENT && path_len > 0) { /* application path present: ignore AID */ if (path_len > SC_MAX_PATH_SIZE) { + free(app->label); free(app); LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "Application path is too long."); } @@ -134,6 +140,7 @@ parse_dir_record(sc_card_t *card, u8 ** buf, size_t *buflen, int rec_nr) if (asn1_dirrecord[3].flags & SC_ASN1_PRESENT) { app->ddo.value = malloc(ddo_len); if (app->ddo.value == NULL) { + free(app->label); free(app); LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate DDO value"); } @@ -159,31 +166,35 @@ int sc_enum_apps(sc_card_t *card) int ef_structure; size_t file_size, jj; int r, ii, idx; + struct sc_file *ef_dir = NULL; LOG_FUNC_CALLED(ctx); - if (card->app_count < 0) - card->app_count = 0; + + sc_free_apps(card); + card->app_count = 0; sc_format_path("3F002F00", &path); - sc_file_free(card->ef_dir); - card->ef_dir = NULL; - r = sc_select_file(card, &path, &card->ef_dir); + r = sc_select_file(card, &path, &ef_dir); + if (r < 0) + sc_file_free(ef_dir); LOG_TEST_RET(ctx, r, "Cannot select EF.DIR file"); - if (card->ef_dir->type != SC_FILE_TYPE_WORKING_EF) { - sc_file_free(card->ef_dir); - card->ef_dir = NULL; + if (ef_dir->type != SC_FILE_TYPE_WORKING_EF) { + sc_file_free(ef_dir); LOG_TEST_RET(ctx, SC_ERROR_INVALID_CARD, "EF(DIR) is not a working EF."); } - ef_structure = card->ef_dir->ef_structure; + ef_structure = ef_dir->ef_structure; + file_size = ef_dir->size; + sc_file_free(ef_dir); if (ef_structure == SC_FILE_EF_TRANSPARENT) { u8 *buf = NULL, *p; size_t bufsize; - file_size = card->ef_dir->size; if (file_size == 0) LOG_FUNC_RETURN(ctx, 0); + if (file_size > MAX_FILE_SIZE) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); buf = malloc(file_size); if (buf == NULL) @@ -214,9 +225,9 @@ int sc_enum_apps(sc_card_t *card) size_t rec_size; /* Arbitrary set '16' as maximal number of records to check out: - * to avoid endless loop because of some uncomplete cards/drivers */ + * to avoid endless loop because of some incomplete cards/drivers */ for (rec_nr = 1; rec_nr < 16; rec_nr++) { - r = sc_read_record(card, rec_nr, buf, sizeof(buf), SC_RECORD_BY_REC_NR); + r = sc_read_record(card, rec_nr, 0, buf, sizeof(buf), SC_RECORD_BY_REC_NR); if (r == SC_ERROR_RECORD_NOT_FOUND) break; LOG_TEST_RET(ctx, r, "read_record() failed"); @@ -259,10 +270,8 @@ void sc_free_apps(sc_card_t *card) int i; for (i = 0; i < card->app_count; i++) { - if (card->app[i]->label) - free(card->app[i]->label); - if (card->app[i]->ddo.value) - free(card->app[i]->ddo.value); + free(card->app[i]->label); + free(card->app[i]->ddo.value); free(card->app[i]); } card->app_count = -1; @@ -309,6 +318,8 @@ static int update_transparent(sc_card_t *card, sc_file_t *file) free(buf); return r; } + if (!rec_size) + continue; tmp = (u8 *) realloc(buf, buf_size + rec_size); if (!tmp) { if (rec) @@ -350,7 +361,7 @@ static int update_single_record(sc_card_t *card, sc_app_info_t *app) if (r) return r; if (app->rec_nr > 0) - r = sc_update_record(card, (unsigned int)app->rec_nr, rec, rec_size, SC_RECORD_BY_REC_NR); + r = sc_update_record(card, (unsigned int)app->rec_nr, 0, rec, rec_size, SC_RECORD_BY_REC_NR); else if (app->rec_nr == 0) { /* create new record entry */ r = sc_append_record(card, rec, rec_size, 0); @@ -364,7 +375,7 @@ static int update_single_record(sc_card_t *card, sc_app_info_t *app) if (card->app[i]->rec_nr > rec_nr) rec_nr = card->app[i]->rec_nr; rec_nr++; - r = sc_update_record(card, (unsigned int)rec_nr, rec, rec_size, SC_RECORD_BY_REC_NR); + r = sc_update_record(card, (unsigned int)rec_nr, 0, rec, rec_size, SC_RECORD_BY_REC_NR); } } else { sc_log(card->ctx, "invalid record number\n"); diff --git a/src/libopensc/ef-atr.c b/src/libopensc/ef-atr.c index c03473d33e..8265188165 100644 --- a/src/libopensc/ef-atr.c +++ b/src/libopensc/ef-atr.c @@ -17,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include @@ -139,16 +139,17 @@ int sc_parse_ef_atr(struct sc_card *card) { struct sc_context *ctx = card->ctx; struct sc_path path; - struct sc_file *file; + struct sc_file *file = NULL; int rv; unsigned char *buf = NULL; size_t size; + size_t off = 0; LOG_FUNC_CALLED(ctx); sc_format_path("3F002F01", &path); rv = sc_select_file(card, &path, &file); - LOG_TEST_RET(ctx, rv, "Cannot select EF(ATR) file"); + LOG_TEST_GOTO_ERR(ctx, rv, "Cannot select EF(ATR) file"); if (file->size) { size = file->size; @@ -156,18 +157,30 @@ int sc_parse_ef_atr(struct sc_card *card) size = 1024; } buf = malloc(size); - if (!buf) - LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Memory allocation error"); + if (!buf) { + rv = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, rv, "Memory allocation error"); + } rv = sc_read_binary(card, 0, buf, size, 0); - LOG_TEST_RET(ctx, rv, "Cannot read EF(ATR) file"); - - rv = sc_parse_ef_atr_content(card, buf, rv); - LOG_TEST_RET(ctx, rv, "EF(ATR) parse error"); + LOG_TEST_GOTO_ERR(ctx, rv, "Cannot read EF(ATR) file"); + + /* Workaround: Some cards seem to have a buggy storage of the EF.ATR */ + if ((card->type == SC_CARD_TYPE_IASECC_CPX) || + (card->type == SC_CARD_TYPE_IASECC_CPXCL)) { + /* Let's keep the first byte */ + if ((rv > 1) && + (buf[0] == ISO7816_II_CATEGORY_TLV)) + off++; + } + rv = sc_parse_ef_atr_content(card, buf + off, rv - off); + LOG_TEST_GOTO_ERR(ctx, rv, "EF(ATR) parse error"); - free(buf); - sc_file_free(file); + rv = SC_SUCCESS; - LOG_FUNC_RETURN(ctx, SC_SUCCESS); +err: + sc_file_free(file); + free(buf); + LOG_FUNC_RETURN(ctx, rv); } void sc_free_ef_atr(sc_card_t *card) diff --git a/src/libopensc/ef-gdo.c b/src/libopensc/ef-gdo.c new file mode 100644 index 0000000000..c7392c09af --- /dev/null +++ b/src/libopensc/ef-gdo.c @@ -0,0 +1,132 @@ +/* + * ef-atr.c: Stuff for handling EF(GDO) + * + * Copyright (C) 2017 Frank Morgner + * + * 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 + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "asn1.h" +#include "internal.h" +#include +#include + +static int +sc_parse_ef_gdo_content(const unsigned char *gdo, size_t gdo_len, + unsigned char *iccsn, size_t *iccsn_len, + unsigned char *chn, size_t *chn_len) +{ + int r = SC_SUCCESS, iccsn_found = 0, chn_found = 0; + const unsigned char *p = gdo; + size_t left = gdo_len; + + while (left >= 2) { + unsigned int cla, tag; + size_t tag_len; + + r = sc_asn1_read_tag(&p, left, &cla, &tag, &tag_len); + if (r != SC_SUCCESS) { + if (r == SC_ERROR_ASN1_END_OF_CONTENTS) { + /* not enough data */ + r = SC_SUCCESS; + } + break; + } + if (p == NULL) { + /* done parsing */ + break; + } + + if (cla == SC_ASN1_TAG_APPLICATION) { + switch (tag) { + case 0x1A: + iccsn_found = 1; + if (iccsn && iccsn_len) { + memcpy(iccsn, p, MIN(tag_len, *iccsn_len)); + *iccsn_len = MIN(tag_len, *iccsn_len); + } + break; + case 0x1F20: + chn_found = 1; + if (chn && chn_len) { + memcpy(chn, p, MIN(tag_len, *chn_len)); + *chn_len = MIN(tag_len, *chn_len); + } + break; + } + } + + p += tag_len; + left = gdo_len - (p - gdo); + } + + if (!iccsn_found && iccsn_len) + *iccsn_len = 0; + if (!chn_found && chn_len) + *chn_len = 0; + + return r; +} + + + +int +sc_parse_ef_gdo(struct sc_card *card, + unsigned char *iccsn, size_t *iccsn_len, + unsigned char *chn, size_t *chn_len) +{ + struct sc_context *ctx; + struct sc_path path; + struct sc_file *file; + unsigned char *gdo = NULL; + size_t gdo_len = 0; + int r; + + if (!card) + return SC_ERROR_INVALID_ARGUMENTS; + + ctx = card->ctx; + + LOG_FUNC_CALLED(ctx); + + sc_format_path("3F002F02", &path); + r = sc_select_file(card, &path, &file); + LOG_TEST_GOTO_ERR(ctx, r, "Cannot select EF(GDO) file"); + + if (file->size) { + gdo_len = file->size; + } else { + gdo_len = 64; + } + gdo = malloc(gdo_len); + if (!gdo) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + + r = sc_read_binary(card, 0, gdo, gdo_len, 0); + LOG_TEST_GOTO_ERR(ctx, r, "Cannot read EF(GDO) file"); + + r = sc_parse_ef_gdo_content(gdo, r, iccsn, iccsn_len, chn, chn_len); + +err: + sc_file_free(file); + free(gdo); + + LOG_FUNC_RETURN(ctx, r); +} diff --git a/src/libopensc/errors.c b/src/libopensc/errors.c index b6c6eb4810..58a28c5e18 100644 --- a/src/libopensc/errors.c +++ b/src/libopensc/errors.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -30,6 +30,7 @@ const char *sc_strerror(int error) { + unsigned int error_index = 0; const char *rdr_errors[] = { "Generic reader error", "No readers found", @@ -45,11 +46,11 @@ const char *sc_strerror(int error) "Message too long (keypad)", "Timeout while waiting for event from card reader", "Unresponsive card (correctly inserted?)", - "Reader detached (hotplug device?)", - "Reader reattached (hotplug device?)", + "Reader detached", + "Reader reattached", "Reader in use by another application" }; - const int rdr_base = -SC_ERROR_READER; + const unsigned int rdr_base = -SC_ERROR_READER; const char *card_errors[] = { "Card command failed", @@ -74,7 +75,7 @@ const char *sc_strerror(int error) "End of file/record reached before reading Le bytes", "Reference data not usable" }; - const int card_base = -SC_ERROR_CARD_CMD_FAILED; + const unsigned int card_base = -SC_ERROR_CARD_CMD_FAILED; const char *arg_errors[] = { "Invalid arguments", @@ -84,7 +85,7 @@ const char *sc_strerror(int error) "Invalid PIN length", "Invalid data", }; - const int arg_base = -SC_ERROR_INVALID_ARGUMENTS; + const unsigned int arg_base = -SC_ERROR_INVALID_ARGUMENTS; const char *int_errors[] = { "Internal error", @@ -103,9 +104,11 @@ const char *sc_strerror(int error) "Unsupported card", "Unable to load external module", "EF offset too large", - "Not implemented" + "Not implemented", + "Invalid Simple TLV object", + "Premature end of Simple TLV stream", }; - const int int_base = -SC_ERROR_INTERNAL; + const unsigned int int_base = -SC_ERROR_INTERNAL; const char *p15i_errors[] = { "Generic PKCS#15 initialization error", @@ -120,7 +123,7 @@ const char *sc_strerror(int error) "Invalid PIN reference", "File too small", }; - const int p15i_base = -SC_ERROR_PKCS15INIT; + const unsigned int p15i_base = -SC_ERROR_PKCS15INIT; const char *sm_errors[] = { "Generic Secure Messaging error", @@ -137,54 +140,53 @@ const char *sc_strerror(int error) "SM session already active", "Invalid checksum" }; - const int sm_base = -SC_ERROR_SM; + const unsigned int sm_base = -SC_ERROR_SM; const char *misc_errors[] = { "Unknown error", "PKCS#15 compatible smart card not found", }; - const int misc_base = -SC_ERROR_UNKNOWN; + const unsigned int misc_base = -SC_ERROR_UNKNOWN; const char *no_errors = "Success"; const char **errors = NULL; - int count = 0, err_base = 0; + unsigned int count = 0, err_base = 0; if (!error) return no_errors; - if (error < 0) - error = -error; + error_index = error < 0 ? (unsigned)(-(long long int)error) : (unsigned)error; - if (error >= misc_base) { + if (error_index >= misc_base) { errors = misc_errors; count = DIM(misc_errors); err_base = misc_base; - } else if (error >= sm_base) { + } else if (error_index >= sm_base) { errors = sm_errors; count = DIM(sm_errors); err_base = sm_base; - } else if (error >= p15i_base) { + } else if (error_index >= p15i_base) { errors = p15i_errors; count = DIM(p15i_errors); err_base = p15i_base; - } else if (error >= int_base) { + } else if (error_index >= int_base) { errors = int_errors; count = DIM(int_errors); err_base = int_base; - } else if (error >= arg_base) { + } else if (error_index >= arg_base) { errors = arg_errors; count = DIM(arg_errors); err_base = arg_base; - } else if (error >= card_base) { + } else if (error_index >= card_base) { errors = card_errors; count = DIM(card_errors); err_base = card_base; - } else if (error >= rdr_base) { + } else if (error_index >= rdr_base) { errors = rdr_errors; count = DIM(rdr_errors); err_base = rdr_base; } - error -= err_base; - if (error >= count || count == 0) + error_index -= err_base; + if (error_index >= count) return misc_errors[0]; - return errors[error]; + return errors[error_index]; } diff --git a/src/libopensc/errors.h b/src/libopensc/errors.h index 183b0758d3..0f770871e9 100644 --- a/src/libopensc/errors.h +++ b/src/libopensc/errors.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_ERRORS_H @@ -95,6 +95,8 @@ extern "C" { #define SC_ERROR_CANNOT_LOAD_MODULE -1414 #define SC_ERROR_OFFSET_TOO_LARGE -1415 #define SC_ERROR_NOT_IMPLEMENTED -1416 +#define SC_ERROR_INVALID_TLV_OBJECT -1417 +#define SC_ERROR_TLV_END_OF_CONTENTS -1418 /* Relating to PKCS #15 init stuff */ #define SC_ERROR_PKCS15INIT -1500 diff --git a/src/libopensc/esteid.h b/src/libopensc/esteid.h deleted file mode 100644 index 2fbfaeed5c..0000000000 --- a/src/libopensc/esteid.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef _OPENSC_ESTEID_H -#define _OPENSC_ESTEID_H - -/* personal data file record numbers */ -enum { - SC_ESTEID_PD_SURNAME = 1, - SC_ESTEID_PD_GIVEN_NAMES1, - SC_ESTEID_PD_GIVEN_NAMES2, - SC_ESTEID_PD_SEX, - SC_ESTEID_PD_CITIZENSHIP, - SC_ESTEID_PD_DATE_OF_BIRTH, - SC_ESTEID_PD_PERSONAL_ID, - SC_ESTEID_PD_DOCUMENT_NR, - SC_ESTEID_PD_EXPIRY_DATE, - SC_ESTEID_PD_PLACE_OF_BIRTH, - SC_ESTEID_PD_ISSUING_DATE, - SC_ESTEID_PD_PERMIT_TYPE, - SC_ESTEID_PD_REMARK1, - SC_ESTEID_PD_REMARK2, - SC_ESTEID_PD_REMARK3, - SC_ESTEID_PD_REMARK4 -}; - -enum { - SC_ESTEID_AUTH = 1, - SC_ESTEID_SIGN -}; - -#define SC_ESTEID_KEYREF_FILE_RECLEN 21 - -int select_esteid_df(sc_card_t * card); -int is_esteid_card(sc_card_t *card); -#endif diff --git a/src/libopensc/gp.c b/src/libopensc/gp.c new file mode 100644 index 0000000000..32e399782d --- /dev/null +++ b/src/libopensc/gp.c @@ -0,0 +1,108 @@ +/* + * gp.c: Global Platform Related functions + * + * Copyright (C) 2018 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "internal.h" +#include "gp.h" + +/* ISO 7816 CLA values */ +#define GLOBAL_PLATFORM_CLASS 0x80 + +/* ISO 71816 INS values */ +#define ISO7816_INS_GET_DATA 0xca + +/* The AID of the Card Manager defined by Open Platform 2.0.1 specification */ +static const struct sc_aid gp_card_manager = { + {0xA0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00}, 7 +}; + +/* The AID of the Issuer Security Domain defined by GlobalPlatform 2.3.1 specification. */ +static const struct sc_aid gp_isd_rid = { + {0xA0, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00}, 7 +}; + + +/* Select AID */ +int +gp_select_aid(struct sc_card *card, const struct sc_aid *aid) +{ + int rv; + LOG_FUNC_CALLED(card->ctx); + rv = iso7816_select_aid(card, aid->value, aid->len, NULL, NULL); + LOG_FUNC_RETURN(card->ctx, rv); +} + +/* Select the Open Platform Card Manager */ +int +gp_select_card_manager(struct sc_card *card) +{ + int rv; + + LOG_FUNC_CALLED(card->ctx); + rv = gp_select_aid(card, &gp_card_manager); + LOG_FUNC_RETURN(card->ctx, rv); +} + +/* Select Global Platform Card Manager */ +int +gp_select_isd_rid(struct sc_card *card) +{ + int rv; + + LOG_FUNC_CALLED(card->ctx); + rv = gp_select_aid(card, &gp_isd_rid); + LOG_FUNC_RETURN(card->ctx, rv); +} + +/* Get Card Production Life-Cycle information */ +int +gp_get_cplc_data(struct sc_card *card, global_platform_cplc_data_t *cplc_data) +{ + size_t len = sizeof(global_platform_cplc_data_t); + u8 *receive_buf = (u8 *)cplc_data; + struct sc_apdu apdu; + int rc; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, ISO7816_INS_GET_DATA, 0x9f, 0x7f); + apdu.cla = GLOBAL_PLATFORM_CLASS; + apdu.resp = receive_buf; + apdu.resplen = len; + apdu.le = len; + + rc = sc_transmit_apdu(card, &apdu); + if (rc < 0) + LOG_FUNC_RETURN(card->ctx, rc); + + rc = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (rc < 0) + LOG_FUNC_RETURN(card->ctx, rc); + + /* We expect this will fill the whole structure in the argument. + * If we got something else, report error */ + if ((size_t)apdu.resplen < sizeof(global_platform_cplc_data_t)) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_CORRUPTED_DATA); + } + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); +} diff --git a/src/libopensc/gp.h b/src/libopensc/gp.h new file mode 100644 index 0000000000..e6c91a1fd4 --- /dev/null +++ b/src/libopensc/gp.h @@ -0,0 +1,82 @@ +/* + * gp.h: Global Platform Related functions + * + * Copyright (C) 2018 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 + */ + +#ifndef _LIBOPENSC_GP_H +#define _LIBOPENSC_GP_H + +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#define PACKED +#pragma pack(push,1) +#elif defined(__GNUC__) +#define PACKED __attribute__ ((__packed__)) +#endif + +/* returned by the iso get status apdu with the global platform cplc data parameters */ +typedef struct global_platform_cplc_data { + u8 tag[2]; + u8 length; + u8 ic_fabricator[2]; + u8 ic_type[2]; + u8 os_id[2]; + u8 os_date[2]; + u8 os_level[2]; + u8 fabrication_data[2]; + u8 ic_serial_number[4]; + u8 ic_batch[2]; + u8 module_fabricator[2]; + u8 packing_data[2]; + u8 icc_manufacturer[2]; + u8 ic_embedding_data[2]; + u8 pre_personalizer[2]; + u8 ic_pre_personalization_data[2]; + u8 ic_pre_personalization_id[4]; + u8 ic_personalizaer[2]; + u8 ic_personalization_data[2]; + u8 ic_personalization_id[4]; +} PACKED global_platform_cplc_data_t; + +int gp_select_aid(struct sc_card *card, const struct sc_aid *aid); +int gp_select_card_manager(struct sc_card *card); +int gp_select_isd_rid(struct sc_card *card); +int gp_get_cplc_data(struct sc_card *card, global_platform_cplc_data_t *cplc_data); + +#ifdef _MSC_VER +#undef PACKED +#pragma pack(pop) +#elif defined(__GNUC__) +#undef PACKED +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libopensc/iasecc-sdo.c b/src/libopensc/iasecc-sdo.c index 6054259ac1..4d17b84c9a 100644 --- a/src/libopensc/iasecc-sdo.c +++ b/src/libopensc/iasecc-sdo.c @@ -17,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H @@ -36,7 +36,7 @@ #include "iasecc.h" #include "iasecc-sdo.h" -static int iasecc_parse_size(unsigned char *data, size_t *out); +static int iasecc_parse_size(unsigned char *data, size_t data_len, size_t *out); static int @@ -44,7 +44,8 @@ iasecc_parse_acls(struct sc_card *card, struct iasecc_sdo_docp *docp, int flags) { struct sc_context *ctx = card->ctx; struct iasecc_extended_tlv *acls = &docp->acls_contact; - int ii, offs; + int ii; + size_t offs; unsigned char mask = 0x40; if (flags) @@ -56,8 +57,12 @@ iasecc_parse_acls(struct sc_card *card, struct iasecc_sdo_docp *docp, int flags) docp->amb = *(acls->value + 0); memset(docp->scbs, 0xFF, sizeof(docp->scbs)); for (ii=0, offs = 1; ii<7; ii++, mask >>= 1) - if (mask & docp->amb) + if (mask & docp->amb) { + if (offs >= acls->size) { + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + } docp->scbs[ii] = *(acls->value + offs++); + } sc_log(ctx, "iasecc_parse_docp() SCBs %02X:%02X:%02X:%02X:%02X:%02X:%02X", docp->scbs[0],docp->scbs[1],docp->scbs[2],docp->scbs[3], @@ -83,7 +88,7 @@ iasecc_sdo_convert_acl(struct sc_card *card, struct iasecc_sdo *sdo, {SC_AC_OP_READ, IASECC_ACL_GET_DATA}, {0x00, 0x00} }; - unsigned char mask = 0x80, op_mask; + unsigned char mask = 0x80, op_mask = 0; int ii; LOG_FUNC_CALLED(ctx); @@ -94,10 +99,10 @@ iasecc_sdo_convert_acl(struct sc_card *card, struct iasecc_sdo *sdo, break; } } - if (ops[ii].mask == 0) + if (op_mask == 0) LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); - sc_log(ctx, "OP:%i, mask:0x%X", op, ops[ii].mask); + sc_log(ctx, "OP:%i, mask:0x%X", op, op_mask); sc_log(ctx, "AMB:%X, scbs:%s", sdo->docp.amb, sc_dump_hex(sdo->docp.scbs, IASECC_MAX_SCBS)); sc_log(ctx, "docp.acls_contact:%s", sc_dump_hex(sdo->docp.acls_contact.value, sdo->docp.acls_contact.size)); @@ -143,55 +148,42 @@ iasecc_sdo_convert_acl(struct sc_card *card, struct iasecc_sdo *sdo, void iasecc_sdo_free_fields(struct sc_card *card, struct iasecc_sdo *sdo) { - if (sdo->docp.tries_remaining.value) - free(sdo->docp.tries_remaining.value); - if (sdo->docp.usage_remaining.value) - free(sdo->docp.usage_remaining.value); - if (sdo->docp.non_repudiation.value) - free(sdo->docp.non_repudiation.value); - if (sdo->docp.acls_contact.value) - free(sdo->docp.acls_contact.value); - if (sdo->docp.size.value) - free(sdo->docp.size.value); - if (sdo->docp.name.value) - free(sdo->docp.name.value); - if (sdo->docp.issuer_data.value) - free(sdo->docp.issuer_data.value); + if (sdo == NULL) { + return; + } + + free(sdo->docp.tries_maximum.value); + free(sdo->docp.tries_remaining.value); + free(sdo->docp.usage_remaining.value); + free(sdo->docp.non_repudiation.value); + free(sdo->docp.acls_contact.value); + free(sdo->docp.acls_contactless.value); + free(sdo->docp.size.value); + free(sdo->docp.name.value); + free(sdo->docp.issuer_data.value); if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PUBLIC) { - if (sdo->data.pub_key.n.value) - free(sdo->data.pub_key.n.value); - if (sdo->data.pub_key.e.value) - free(sdo->data.pub_key.e.value); - if (sdo->data.pub_key.compulsory.value) - free(sdo->data.pub_key.compulsory.value); - if (sdo->data.pub_key.chr.value) - free(sdo->data.pub_key.chr.value); - if (sdo->data.pub_key.cha.value) - free(sdo->data.pub_key.cha.value); + free(sdo->data.pub_key.n.value); + free(sdo->data.pub_key.e.value); + free(sdo->data.pub_key.compulsory.value); + free(sdo->data.pub_key.chr.value); + free(sdo->data.pub_key.cha.value); } else if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PRIVATE) { - if (sdo->data.prv_key.p.value) - free(sdo->data.prv_key.p.value); - if (sdo->data.prv_key.q.value) - free(sdo->data.prv_key.q.value); - if (sdo->data.prv_key.iqmp.value) - free(sdo->data.prv_key.iqmp.value); - if (sdo->data.prv_key.dmp1.value) - free(sdo->data.prv_key.dmp1.value); - if (sdo->data.prv_key.dmq1.value) - free(sdo->data.prv_key.dmq1.value); - if (sdo->data.prv_key.compulsory.value) - free(sdo->data.prv_key.compulsory.value); + free(sdo->data.prv_key.p.value); + free(sdo->data.prv_key.q.value); + free(sdo->data.prv_key.iqmp.value); + free(sdo->data.prv_key.dmp1.value); + free(sdo->data.prv_key.dmq1.value); + free(sdo->data.prv_key.compulsory.value); } else if (sdo->sdo_class == IASECC_SDO_CLASS_CHV) { - if (sdo->data.chv.size_max.value) - free(sdo->data.chv.size_max.value); - if (sdo->data.chv.size_min.value) - free(sdo->data.chv.size_min.value); - if (sdo->data.chv.value.value) - free(sdo->data.chv.value.value); + free(sdo->data.chv.size_max.value); + free(sdo->data.chv.size_min.value); + free(sdo->data.chv.value.value); } + /* invalidate all the other members too */ + memset(sdo, 0, sizeof(struct iasecc_sdo)); } @@ -204,7 +196,7 @@ iasecc_sdo_free(struct sc_card *card, struct iasecc_sdo *sdo) static int -iasecc_crt_parse(struct sc_card *card, unsigned char *data, struct iasecc_se_info *se) +iasecc_crt_parse(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_se_info *se) { struct sc_context *ctx = card->ctx; struct sc_crt crt; @@ -212,11 +204,16 @@ iasecc_crt_parse(struct sc_card *card, unsigned char *data, struct iasecc_se_inf sc_log(ctx, "iasecc_crt_parse(0x%X) called", *data); + if (data_len < 2) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + memset(&crt, 0, sizeof(crt)); crt.tag = *(data + 0); len = *(data + 1); for(offs = 2; offs < len + 2; offs += 3) { + if ((size_t) offs + 2 >= data_len) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); sc_log(ctx, "iasecc_crt_parse(0x%X) CRT %X -> %X", *data, *(data + offs), *(data + offs + 2)); if (*(data + offs) == IASECC_CRT_TAG_USAGE) { crt.usage = *(data + offs + 2); @@ -319,37 +316,48 @@ int iasecc_se_parse(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_se_info *se) { struct sc_context *ctx = card->ctx; - size_t size, offs, size_size; + size_t size, offs; + int size_size; int rv; LOG_FUNC_CALLED(ctx); + sc_log(ctx, "data_len %"SC_FORMAT_LEN_SIZE_T"u", data_len); + + if (data_len < 1) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); if (*data == IASECC_SDO_TEMPLATE_TAG) { - size_size = iasecc_parse_size(data + 1, &size); + size_size = iasecc_parse_size(data + 1, data_len - 1, &size); LOG_TEST_RET(ctx, size_size, "parse error: invalid size data of IASECC_SDO_TEMPLATE"); + if (data_len < size + size_size + 1) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + data += size_size + 1; data_len = size; sc_log(ctx, - "IASECC_SDO_TEMPLATE: size %"SC_FORMAT_LEN_SIZE_T"u, size_size %"SC_FORMAT_LEN_SIZE_T"u", + "IASECC_SDO_TEMPLATE: size %"SC_FORMAT_LEN_SIZE_T"u, size_size %d", size, size_size); + if (data_len < 3) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + if (*data != IASECC_SDO_TAG_HEADER) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); if ((*(data + 1) & 0x7F) != IASECC_SDO_CLASS_SE) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); - size_size = iasecc_parse_size(data + 3, &size); + size_size = iasecc_parse_size(data + 3, data_len - 3, &size); LOG_TEST_RET(ctx, size_size, "parse error: invalid SDO SE data size"); if (data_len != size + size_size + 3) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalide SDO SE data size"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalid SDO SE data size"); data += 3 + size_size; data_len = size; sc_log(ctx, - "IASECC_SDO_TEMPLATE SE: size %"SC_FORMAT_LEN_SIZE_T"u, size_size %"SC_FORMAT_LEN_SIZE_T"u", + "IASECC_SDO_TEMPLATE SE: size %"SC_FORMAT_LEN_SIZE_T"u, size_size %d", size, size_size); } @@ -360,39 +368,39 @@ iasecc_se_parse(struct sc_card *card, unsigned char *data, size_t data_len, stru LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } - size_size = iasecc_parse_size(data + 1, &size); + size_size = iasecc_parse_size(data + 1, data_len - 1, &size); LOG_TEST_RET(ctx, size_size, "parse error: invalid size data"); if (data_len != size + size_size + 1) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalide SE data size"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalid SE data size"); offs = 1 + size_size; for (; offs < data_len;) { - rv = iasecc_crt_parse(card, data + offs, se); + rv = iasecc_crt_parse(card, data + offs, data_len - offs, se); LOG_TEST_RET(ctx, rv, "parse error: invalid SE data"); offs += rv; } if (offs != data_len) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totaly parsed"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totally parsed"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int -iasecc_parse_size(unsigned char *data, size_t *out) +iasecc_parse_size(unsigned char *data, size_t data_len, size_t *out) { - if (*data < 0x80) { + if (data_len > 0 && *data < 0x80) { *out = *data; return 1; } - else if (*data == 0x81) { + else if (data_len > 1 && *data == 0x81) { *out = *(data + 1); return 2; } - else if (*data == 0x82) { + else if (data_len > 2 && *data == 0x82) { *out = *(data + 1) * 0x100 + *(data + 2); return 3; } @@ -402,14 +410,18 @@ iasecc_parse_size(unsigned char *data, size_t *out) static int -iasecc_parse_get_tlv(struct sc_card *card, unsigned char *data, struct iasecc_extended_tlv *tlv) +iasecc_parse_get_tlv(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_extended_tlv *tlv) { struct sc_context *ctx = card->ctx; - size_t size_len, tag_len; + int size_len, tag_len; memset(tlv, 0, sizeof(*tlv)); sc_log(ctx, "iasecc_parse_get_tlv() called for tag 0x%X", *data); + if (data_len < 1) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); if ((*data == 0x7F) || (*data == 0x5F)) { + if (data_len < 2) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); tlv->tag = *data * 0x100 + *(data + 1); tag_len = 2; } @@ -419,8 +431,11 @@ iasecc_parse_get_tlv(struct sc_card *card, unsigned char *data, struct iasecc_ex } sc_log(ctx, "iasecc_parse_get_tlv() tlv->tag 0x%X", tlv->tag); - size_len = iasecc_parse_size(data + tag_len, &tlv->size); + size_len = iasecc_parse_size(data + tag_len, data_len - tag_len, &tlv->size); LOG_TEST_RET(ctx, size_len, "parse error: invalid size data"); + if (tag_len + size_len + tlv->size > data_len) { + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + } tlv->value = calloc(1, tlv->size); if (!tlv->value) @@ -432,7 +447,7 @@ iasecc_parse_get_tlv(struct sc_card *card, unsigned char *data, struct iasecc_ex sc_log(ctx, "iasecc_parse_get_tlv() parsed %"SC_FORMAT_LEN_SIZE_T"u bytes", tag_len + size_len + tlv->size); - return tag_len + size_len + tlv->size; + return (int)(tag_len + size_len + tlv->size); } @@ -447,21 +462,26 @@ iasecc_parse_chv(struct sc_card *card, unsigned char *data, size_t data_len, str while(offs < data_len) { struct iasecc_extended_tlv tlv; - rv = iasecc_parse_get_tlv(card, data + offs, &tlv); + rv = iasecc_parse_get_tlv(card, data + offs, data_len - offs, &tlv); LOG_TEST_RET(ctx, rv, "iasecc_parse_chv() get and parse TLV error"); sc_log(ctx, "iasecc_parse_chv() get and parse TLV returned %i; tag %X; size %"SC_FORMAT_LEN_SIZE_T"u", rv, tlv.tag, tlv.size); - if (tlv.tag == IASECC_SDO_CHV_TAG_SIZE_MAX) + if (tlv.tag == IASECC_SDO_CHV_TAG_SIZE_MAX) { + free(chv->size_max.value); chv->size_max = tlv; - else if (tlv.tag == IASECC_SDO_CHV_TAG_SIZE_MIN) + } else if (tlv.tag == IASECC_SDO_CHV_TAG_SIZE_MIN) { + free(chv->size_min.value); chv->size_min = tlv; - else if (tlv.tag == IASECC_SDO_CHV_TAG_VALUE) + } else if (tlv.tag == IASECC_SDO_CHV_TAG_VALUE) { + free(chv->value.value); chv->value = tlv; - else + } else { + free(tlv.value); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non CHV SDO tag"); + } offs += rv; } @@ -481,17 +501,20 @@ iasecc_parse_prvkey(struct sc_card *card, unsigned char *data, size_t data_len, while(offs < data_len) { struct iasecc_extended_tlv tlv; - rv = iasecc_parse_get_tlv(card, data + offs, &tlv); + rv = iasecc_parse_get_tlv(card, data + offs, data_len - offs, &tlv); LOG_TEST_RET(ctx, rv, "iasecc_parse_prvkey() get and parse TLV error"); sc_log(ctx, "iasecc_parse_prvkey() get and parse TLV returned %i; tag %X; size %"SC_FORMAT_LEN_SIZE_T"u", rv, tlv.tag, tlv.size); - if (tlv.tag == IASECC_SDO_PRVKEY_TAG_COMPULSORY) + if (tlv.tag == IASECC_SDO_PRVKEY_TAG_COMPULSORY) { + free(prvkey->compulsory.value); prvkey->compulsory = tlv; - else + } else { + free(tlv.value); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non PrvKey SDO tag"); + } offs += rv; } @@ -511,25 +534,32 @@ iasecc_parse_pubkey(struct sc_card *card, unsigned char *data, size_t data_len, while(offs < data_len) { struct iasecc_extended_tlv tlv; - rv = iasecc_parse_get_tlv(card, data + offs, &tlv); + rv = iasecc_parse_get_tlv(card, data + offs, data_len - offs, &tlv); LOG_TEST_RET(ctx, rv, "iasecc_parse_pubkey() get and parse TLV error"); sc_log(ctx, "iasecc_parse_pubkey() get and parse TLV returned %i; tag %X; size %"SC_FORMAT_LEN_SIZE_T"u", rv, tlv.tag, tlv.size); - if (tlv.tag == IASECC_SDO_PUBKEY_TAG_N) + if (tlv.tag == IASECC_SDO_PUBKEY_TAG_N) { + free(pubkey->n.value); pubkey->n = tlv; - else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_E) + } else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_E) { + free(pubkey->e.value); pubkey->e = tlv; - else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_CHR) + } else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_CHR) { + free(pubkey->chr.value); pubkey->chr = tlv; - else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_CHA) + } else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_CHA) { + free(pubkey->cha.value); pubkey->cha = tlv; - else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_COMPULSORY) + } else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_COMPULSORY) { + free(pubkey->compulsory.value); pubkey->compulsory = tlv; - else + } else { + free(tlv.value); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non PubKey SDO tag"); + } offs += rv; } @@ -549,17 +579,20 @@ iasecc_parse_keyset(struct sc_card *card, unsigned char *data, size_t data_len, while(offs < data_len) { struct iasecc_extended_tlv tlv; - rv = iasecc_parse_get_tlv(card, data + offs, &tlv); + rv = iasecc_parse_get_tlv(card, data + offs, data_len - offs, &tlv); LOG_TEST_RET(ctx, rv, "iasecc_parse_keyset() get and parse TLV error"); sc_log(ctx, "iasecc_parse_prvkey() get and parse TLV returned %i; tag %X; size %"SC_FORMAT_LEN_SIZE_T"u", rv, tlv.tag, tlv.size); - if (tlv.tag == IASECC_SDO_KEYSET_TAG_COMPULSORY) + if (tlv.tag == IASECC_SDO_KEYSET_TAG_COMPULSORY) { + free(keyset->compulsory.value); keyset->compulsory = tlv; - else + } else { + free(tlv.value); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non KeySet SDO tag"); + } offs += rv; } @@ -579,11 +612,11 @@ iasecc_parse_docp(struct sc_card *card, unsigned char *data, size_t data_len, st while(offs < data_len) { struct iasecc_extended_tlv tlv; - rv = iasecc_parse_get_tlv(card, data + offs, &tlv); + rv = iasecc_parse_get_tlv(card, data + offs, data_len - offs, &tlv); LOG_TEST_RET(ctx, rv, "iasecc_parse_get_tlv() get and parse TLV error"); sc_log(ctx, - "iasecc_parse_docp() parse_get_tlv retuned %i; tag %X; size %"SC_FORMAT_LEN_SIZE_T"u", + "iasecc_parse_docp() parse_get_tlv returned %i; tag %X; size %"SC_FORMAT_LEN_SIZE_T"u", rv, tlv.tag, tlv.size); if (tlv.tag == IASECC_DOCP_TAG_ACLS) { @@ -592,33 +625,43 @@ iasecc_parse_docp(struct sc_card *card, unsigned char *data, size_t data_len, st LOG_TEST_RET(ctx, _rv, "parse error: cannot parse DOCP"); } else if (tlv.tag == IASECC_DOCP_TAG_ACLS_CONTACT) { + free(sdo->docp.acls_contact.value); sdo->docp.acls_contact = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_ACLS_CONTACTLESS) { + free(sdo->docp.acls_contactless.value); sdo->docp.acls_contactless = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_SIZE) { + free(sdo->docp.size.value); sdo->docp.size = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_NAME) { + free(sdo->docp.name.value); sdo->docp.name = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_ISSUER_DATA) { + free(sdo->docp.issuer_data.value); sdo->docp.issuer_data = tlv; } - else if (tlv.tag == IASECC_DOCP_TAG_NON_REPUDATION) { + else if (tlv.tag == IASECC_DOCP_TAG_NON_REPUDIATION) { + free(sdo->docp.non_repudiation.value); sdo->docp.non_repudiation = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_USAGE_REMAINING) { + free(sdo->docp.usage_remaining.value); sdo->docp.usage_remaining = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_TRIES_MAXIMUM) { + free(sdo->docp.tries_maximum.value); sdo->docp.tries_maximum = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_TRIES_REMAINING) { + free(sdo->docp.tries_remaining.value); sdo->docp.tries_remaining = tlv; } else { + free(tlv.value); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_parse_get_tlv() parse error: non DOCP tag"); } @@ -633,7 +676,7 @@ iasecc_parse_docp(struct sc_card *card, unsigned char *data, size_t data_len, st static int -iasecc_sdo_parse_data(struct sc_card *card, unsigned char *data, struct iasecc_sdo *sdo) +iasecc_sdo_parse_data(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo *sdo) { struct sc_context *ctx = card->ctx; struct iasecc_extended_tlv tlv; @@ -642,7 +685,7 @@ iasecc_sdo_parse_data(struct sc_card *card, unsigned char *data, struct iasecc_s LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_sdo_parse_data() class %X; ref %X", sdo->sdo_class, sdo->sdo_ref); - tlv_size = iasecc_parse_get_tlv(card, data, &tlv); + tlv_size = iasecc_parse_get_tlv(card, data, data_len, &tlv); LOG_TEST_RET(ctx, tlv_size, "parse error: get TLV"); sc_log(ctx, "iasecc_sdo_parse_data() tlv.tag 0x%X", tlv.tag); @@ -655,56 +698,65 @@ iasecc_sdo_parse_data(struct sc_card *card, unsigned char *data, struct iasecc_s free(tlv.value); LOG_TEST_RET(ctx, rv, "parse error: cannot parse DOCP"); } - else if (tlv.tag == IASECC_DOCP_TAG_NON_REPUDATION) { + else if (tlv.tag == IASECC_DOCP_TAG_NON_REPUDIATION) { + free(sdo->docp.non_repudiation.value); sdo->docp.non_repudiation = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_USAGE_REMAINING) { + free(sdo->docp.usage_remaining.value); sdo->docp.usage_remaining = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_TRIES_MAXIMUM) { + free(sdo->docp.tries_maximum.value); sdo->docp.tries_maximum = tlv; } else if (tlv.tag == IASECC_DOCP_TAG_TRIES_REMAINING) { + free(sdo->docp.tries_remaining.value); sdo->docp.tries_remaining = tlv; } else if (tlv.tag == IASECC_SDO_CHV_TAG) { - if (sdo->sdo_class != IASECC_SDO_CLASS_CHV) + if (sdo->sdo_class != IASECC_SDO_CLASS_CHV) { + free(tlv.value); LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: IASECC_SDO_CHV_TAG tag in non User CHV SDO"); + } rv = iasecc_parse_chv(card, tlv.value, tlv.size, &sdo->data.chv); - LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO CHV data"); - free(tlv.value); + LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO CHV data"); } else if (tlv.tag == IASECC_SDO_PUBKEY_TAG) { - if (sdo->sdo_class != IASECC_SDO_CLASS_RSA_PUBLIC) + if (sdo->sdo_class != IASECC_SDO_CLASS_RSA_PUBLIC) { + free(tlv.value); LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: SDO_PUBLIC_KEY tag in non PUBLIC_KEY SDO"); + } rv = iasecc_parse_pubkey(card, tlv.value, tlv.size, &sdo->data.pub_key); - LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO PUBLIC KEY data"); - free(tlv.value); + LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO PUBLIC KEY data"); } else if (tlv.tag == IASECC_SDO_PRVKEY_TAG) { - if (sdo->sdo_class != IASECC_SDO_CLASS_RSA_PRIVATE) + if (sdo->sdo_class != IASECC_SDO_CLASS_RSA_PRIVATE) { + free(tlv.value); LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: SDO_PRIVATE_KEY tag in non PRIVATE_KEY SDO"); + } rv = iasecc_parse_prvkey(card, tlv.value, tlv.size, &sdo->data.prv_key); - LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO PRIVATE KEY data"); - free(tlv.value); + LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO PRIVATE KEY data"); } else if (tlv.tag == IASECC_SDO_KEYSET_TAG) { - if (sdo->sdo_class != IASECC_SDO_CLASS_KEYSET) + if (sdo->sdo_class != IASECC_SDO_CLASS_KEYSET) { + free(tlv.value); LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: SDO_KEYSET tag in non KEYSET SDO"); + } rv = iasecc_parse_keyset(card, tlv.value, tlv.size, &sdo->data.keyset); - LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO KEYSET data"); - free(tlv.value); + LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO KEYSET data"); } else { sc_log(ctx, "iasecc_sdo_parse_data() non supported tag 0x%X", tlv.tag); + free(tlv.value); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } @@ -716,23 +768,34 @@ int iasecc_sdo_parse(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo *sdo) { struct sc_context *ctx = card->ctx; - size_t size, offs, size_size; + size_t size, offs; + int size_size; int rv; LOG_FUNC_CALLED(ctx); + if (data == NULL || data_len < 2) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + if (*data == IASECC_SDO_TEMPLATE_TAG) { - size_size = iasecc_parse_size(data + 1, &size); + size_size = iasecc_parse_size(data + 1, data_len - 1, &size); LOG_TEST_RET(ctx, size_size, "parse error: invalid size data of IASECC_SDO_TEMPLATE"); + if (data_len < (size_t)(size_size + 1)) { + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + } data += size_size + 1; + data_len -= (size_size + 1); + if (size > data_len) { + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + } data_len = size; sc_log(ctx, - "IASECC_SDO_TEMPLATE: size %"SC_FORMAT_LEN_SIZE_T"u, size_size %"SC_FORMAT_LEN_SIZE_T"u", + "IASECC_SDO_TEMPLATE: size %"SC_FORMAT_LEN_SIZE_T"u, size_size %d", size, size_size); } - if (*data != IASECC_SDO_TAG_HEADER) + if (data_len < 4 || *data != IASECC_SDO_TAG_HEADER) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); if (sdo->sdo_class != (*(data + 1) & 0x7F)) @@ -741,26 +804,29 @@ iasecc_sdo_parse(struct sc_card *card, unsigned char *data, size_t data_len, str if (sdo->sdo_ref != (*(data + 2) & 0x3F)) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); - size_size = iasecc_parse_size(data + 3, &size); + size_size = iasecc_parse_size(data + 3, data_len - 3, &size); LOG_TEST_RET(ctx, size_size, "parse error: invalid size data"); if (data_len != size + size_size + 3) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalide SDO data size"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalid SDO data size"); sc_log(ctx, - "sz %"SC_FORMAT_LEN_SIZE_T"u, sz_size %"SC_FORMAT_LEN_SIZE_T"u", + "sz %"SC_FORMAT_LEN_SIZE_T"u, sz_size %d", size, size_size); offs = 3 + size_size; for (; offs < data_len;) { - rv = iasecc_sdo_parse_data(card, data + offs, sdo); - LOG_TEST_RET(ctx, rv, "parse error: invalid SDO data"); + rv = iasecc_sdo_parse_data(card, data + offs, data_len - offs, sdo); + if (rv != SC_SUCCESS) { + iasecc_sdo_free_fields(card, sdo); + LOG_TEST_RET(ctx, rv, "parse error: invalid SDO data"); + } offs += rv; } if (offs != data_len) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totaly parsed"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totally parsed"); sc_log(ctx, "docp.acls_contact.size %"SC_FORMAT_LEN_SIZE_T"u, docp.size.size %"SC_FORMAT_LEN_SIZE_T"u", @@ -776,15 +842,13 @@ iasecc_sdo_allocate_and_parse(struct sc_card *card, unsigned char *data, size_t { struct sc_context *ctx = card->ctx; struct iasecc_sdo *sdo = NULL; - size_t size, offs, size_size; + size_t size, offs; + int size_size; int rv; LOG_FUNC_CALLED(ctx); - if (*data != IASECC_SDO_TAG_HEADER) - LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); - - if (data_len < 3) + if (data_len < 3 || !data || *data != IASECC_SDO_TAG_HEADER) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); sdo = calloc(1, sizeof(struct iasecc_sdo)); @@ -799,26 +863,26 @@ iasecc_sdo_allocate_and_parse(struct sc_card *card, unsigned char *data, size_t if (data_len == 3) LOG_FUNC_RETURN(ctx, SC_SUCCESS); - size_size = iasecc_parse_size(data + 3, &size); + size_size = iasecc_parse_size(data + 3, data_len - 3, &size); LOG_TEST_RET(ctx, size_size, "parse error: invalid size data"); if (data_len != size + size_size + 3) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalide SDO data size"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalid SDO data size"); sc_log(ctx, - "sz %"SC_FORMAT_LEN_SIZE_T"u, sz_size %"SC_FORMAT_LEN_SIZE_T"u", + "sz %"SC_FORMAT_LEN_SIZE_T"u, sz_size %d", size, size_size); offs = 3 + size_size; for (; offs < data_len;) { - rv = iasecc_sdo_parse_data(card, data + offs, sdo); + rv = iasecc_sdo_parse_data(card, data + offs, data_len - offs, sdo); LOG_TEST_RET(ctx, rv, "parse error: invalid SDO data"); offs += rv; } if (offs != data_len) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totaly parsed"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totally parsed"); sc_log(ctx, "docp.acls_contact.size %"SC_FORMAT_LEN_SIZE_T"u; docp.size.size %"SC_FORMAT_LEN_SIZE_T"u", @@ -833,7 +897,7 @@ iasecc_update_blob(struct sc_context *ctx, struct iasecc_extended_tlv *tlv, unsigned char **blob, size_t *blob_size) { unsigned char *pp = NULL; - int offs = 0, sz = tlv->size + 2; + size_t offs = 0, sz; if (tlv->size == 0) LOG_FUNC_RETURN(ctx, SC_SUCCESS); @@ -878,7 +942,7 @@ static int iasecc_encode_docp(struct sc_context *ctx, struct iasecc_sdo_docp *docp, unsigned char **out, size_t *out_len) { struct iasecc_extended_tlv tlv, tlv_st; - unsigned char *st_blob, *tmp_blob, *docp_blob; + unsigned char *st_blob = NULL, *tmp_blob = NULL, *docp_blob = NULL; size_t blob_size; int rv; @@ -889,63 +953,64 @@ iasecc_encode_docp(struct sc_context *ctx, struct iasecc_sdo_docp *docp, unsigne memset(&tlv, 0, sizeof(tlv)); memset(&tlv_st, 0, sizeof(tlv_st)); - st_blob = NULL; blob_size = 0; rv = iasecc_update_blob(ctx, &docp->acls_contact, &st_blob, &blob_size); - LOG_TEST_RET(ctx, rv, "ECC: cannot add contact ACLs to blob"); + LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add contact ACLs to blob"); rv = iasecc_update_blob(ctx, &docp->acls_contactless, &st_blob, &blob_size); - LOG_TEST_RET(ctx, rv, "ECC: cannot add contactless ACLs to blob"); + LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add contactless ACLs to blob"); tlv.tag = IASECC_DOCP_TAG_ACLS; tlv.size = blob_size; tlv.value = st_blob; - tmp_blob = NULL; blob_size = 0; rv = iasecc_update_blob(ctx, &tlv, &tmp_blob, &blob_size); - LOG_TEST_RET(ctx, rv, "ECC: cannot add ACLs template to blob"); + LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add ACLs template to blob"); rv = iasecc_update_blob(ctx, &docp->name, &tmp_blob, &blob_size); - LOG_TEST_RET(ctx, rv, "ECC: cannot add NAME to blob"); + LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add NAME to blob"); rv = iasecc_update_blob(ctx, &docp->tries_maximum, &tmp_blob, &blob_size); - LOG_TEST_RET(ctx, rv, "ECC: cannot add TRIES MAXIMUM to blob"); + LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add TRIES MAXIMUM to blob"); rv = iasecc_update_blob(ctx, &docp->tries_remaining, &tmp_blob, &blob_size); - LOG_TEST_RET(ctx, rv, "ECC: cannot add TRIES REMAINING to blob"); + LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add TRIES REMAINING to blob"); rv = iasecc_update_blob(ctx, &docp->usage_maximum, &tmp_blob, &blob_size); - LOG_TEST_RET(ctx, rv, "ECC: cannot add USAGE MAXIMUM to blob"); + LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add USAGE MAXIMUM to blob"); rv = iasecc_update_blob(ctx, &docp->usage_remaining, &tmp_blob, &blob_size); - LOG_TEST_RET(ctx, rv, "ECC: cannot add USAGE REMAINING to blob"); + LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add USAGE REMAINING to blob"); rv = iasecc_update_blob(ctx, &docp->non_repudiation, &tmp_blob, &blob_size); - LOG_TEST_RET(ctx, rv, "ECC: cannot add NON REPUDATION to blob"); + LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add NON REPUDIATION to blob"); rv = iasecc_update_blob(ctx, &docp->size, &tmp_blob, &blob_size); - LOG_TEST_RET(ctx, rv, "ECC: cannot add SIZE to blob"); + LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add SIZE to blob"); rv = iasecc_update_blob(ctx, &docp->issuer_data, &tmp_blob, &blob_size); - LOG_TEST_RET(ctx, rv, "ECC: cannot add IDATA to blob"); + LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add IDATA to blob"); tlv.tag = IASECC_DOCP_TAG; tlv.size = blob_size; tlv.value = tmp_blob; - docp_blob = NULL; blob_size = 0; rv = iasecc_update_blob(ctx, &tlv, &docp_blob, &blob_size); - LOG_TEST_RET(ctx, rv, "ECC: cannot add ACLs to blob"); - - free(tmp_blob); + LOG_TEST_GOTO_ERR(ctx, rv, "ECC: cannot add ACLs to blob"); if (out && out_len) { *out = docp_blob; *out_len = blob_size; + docp_blob = NULL; } +err: + free(docp_blob); + free(tmp_blob); + free(st_blob); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -1015,7 +1080,7 @@ iasecc_sdo_encode_create(struct sc_context *ctx, struct iasecc_sdo *sdo, unsigne if (out) sc_debug(ctx, SC_LOG_DEBUG_ASN1,"Create data: %s", sc_dump_hex(*out, out_len)); - LOG_FUNC_RETURN(ctx, out_len); + LOG_FUNC_RETURN(ctx, (int)out_len); } @@ -1068,7 +1133,7 @@ iasecc_sdo_encode_update_field(struct sc_context *ctx, unsigned char sdo_class, sc_debug(ctx, SC_LOG_DEBUG_ASN1,"Data: %s", sc_dump_hex(tlv->value, tlv->size)); sc_debug(ctx, SC_LOG_DEBUG_ASN1,"Encoded: %s", sc_dump_hex(*out, out_len)); - LOG_FUNC_RETURN(ctx, out_len); + LOG_FUNC_RETURN(ctx, (int)out_len); } @@ -1081,7 +1146,7 @@ iasecc_sdo_encode_rsa_update(struct sc_context *ctx, struct iasecc_sdo *sdo, str sc_log(ctx, "iasecc_sdo_encode_rsa_update() SDO class %X", sdo->sdo_class); memset(sdo_update, 0, sizeof(*sdo_update)); if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PRIVATE) { - int indx = 0; + int index = 0; sc_log(ctx, "iasecc_sdo_encode_rsa_update(IASECC_SDO_CLASS_RSA_PRIVATE)"); if (!rsa->p.len || !rsa->q.len || !rsa->iqmp.len || !rsa->dmp1.len || !rsa->dmq1.len) @@ -1092,35 +1157,35 @@ iasecc_sdo_encode_rsa_update(struct sc_context *ctx, struct iasecc_sdo *sdo, str sdo_update->sdo_class = IASECC_SDO_CLASS_RSA_PRIVATE; - sdo_update->fields[indx].parent_tag = IASECC_SDO_PRVKEY_TAG; - sdo_update->fields[indx].tag = IASECC_SDO_PRVKEY_TAG_P; - sdo_update->fields[indx].value = rsa->p.data; - sdo_update->fields[indx].size = rsa->p.len; - indx++; - - sdo_update->fields[indx].parent_tag = IASECC_SDO_PRVKEY_TAG; - sdo_update->fields[indx].tag = IASECC_SDO_PRVKEY_TAG_Q; - sdo_update->fields[indx].value = rsa->q.data; - sdo_update->fields[indx].size = rsa->q.len; - indx++; - - sdo_update->fields[indx].parent_tag = IASECC_SDO_PRVKEY_TAG; - sdo_update->fields[indx].tag = IASECC_SDO_PRVKEY_TAG_IQMP; - sdo_update->fields[indx].value = rsa->iqmp.data; - sdo_update->fields[indx].size = rsa->iqmp.len; - indx++; - - sdo_update->fields[indx].parent_tag = IASECC_SDO_PRVKEY_TAG; - sdo_update->fields[indx].tag = IASECC_SDO_PRVKEY_TAG_DMP1; - sdo_update->fields[indx].value = rsa->dmp1.data; - sdo_update->fields[indx].size = rsa->dmp1.len; - indx++; - - sdo_update->fields[indx].parent_tag = IASECC_SDO_PRVKEY_TAG; - sdo_update->fields[indx].tag = IASECC_SDO_PRVKEY_TAG_DMQ1; - sdo_update->fields[indx].value = rsa->dmq1.data; - sdo_update->fields[indx].size = rsa->dmq1.len; - indx++; + sdo_update->fields[index].parent_tag = IASECC_SDO_PRVKEY_TAG; + sdo_update->fields[index].tag = IASECC_SDO_PRVKEY_TAG_P; + sdo_update->fields[index].value = rsa->p.data; + sdo_update->fields[index].size = rsa->p.len; + index++; + + sdo_update->fields[index].parent_tag = IASECC_SDO_PRVKEY_TAG; + sdo_update->fields[index].tag = IASECC_SDO_PRVKEY_TAG_Q; + sdo_update->fields[index].value = rsa->q.data; + sdo_update->fields[index].size = rsa->q.len; + index++; + + sdo_update->fields[index].parent_tag = IASECC_SDO_PRVKEY_TAG; + sdo_update->fields[index].tag = IASECC_SDO_PRVKEY_TAG_IQMP; + sdo_update->fields[index].value = rsa->iqmp.data; + sdo_update->fields[index].size = rsa->iqmp.len; + index++; + + sdo_update->fields[index].parent_tag = IASECC_SDO_PRVKEY_TAG; + sdo_update->fields[index].tag = IASECC_SDO_PRVKEY_TAG_DMP1; + sdo_update->fields[index].value = rsa->dmp1.data; + sdo_update->fields[index].size = rsa->dmp1.len; + index++; + + sdo_update->fields[index].parent_tag = IASECC_SDO_PRVKEY_TAG; + sdo_update->fields[index].tag = IASECC_SDO_PRVKEY_TAG_DMQ1; + sdo_update->fields[index].value = rsa->dmq1.data; + sdo_update->fields[index].size = rsa->dmq1.len; + index++; sc_log(ctx, "prv_key.compulsory.on_card %i", sdo->data.prv_key.compulsory.on_card); if (!sdo->data.prv_key.compulsory.on_card) { @@ -1128,16 +1193,16 @@ iasecc_sdo_encode_rsa_update(struct sc_context *ctx, struct iasecc_sdo *sdo, str sc_log(ctx, "sdo_prvkey->data.prv_key.compulsory.size %"SC_FORMAT_LEN_SIZE_T"u", sdo->data.prv_key.compulsory.size); - sdo_update->fields[indx].parent_tag = IASECC_SDO_PRVKEY_TAG; - sdo_update->fields[indx].tag = IASECC_SDO_PRVKEY_TAG_COMPULSORY; - sdo_update->fields[indx].value = sdo->data.prv_key.compulsory.value; - sdo_update->fields[indx].size = sdo->data.prv_key.compulsory.size; - indx++; + sdo_update->fields[index].parent_tag = IASECC_SDO_PRVKEY_TAG; + sdo_update->fields[index].tag = IASECC_SDO_PRVKEY_TAG_COMPULSORY; + sdo_update->fields[index].value = sdo->data.prv_key.compulsory.value; + sdo_update->fields[index].size = sdo->data.prv_key.compulsory.size; + index++; } } } else if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PUBLIC) { - int indx = 0; + int index = 0; sc_log(ctx, "iasecc_sdo_encode_rsa_update(IASECC_SDO_CLASS_RSA_PUBLIC)"); sdo_update->magic = SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA; @@ -1145,45 +1210,45 @@ iasecc_sdo_encode_rsa_update(struct sc_context *ctx, struct iasecc_sdo *sdo, str sdo_update->sdo_class = sdo->sdo_class; if (rsa->exponent.len) { - sdo_update->fields[indx].parent_tag = IASECC_SDO_PUBKEY_TAG; - sdo_update->fields[indx].tag = IASECC_SDO_PUBKEY_TAG_E; - sdo_update->fields[indx].value = rsa->exponent.data; - sdo_update->fields[indx].size = rsa->exponent.len; - indx++; + sdo_update->fields[index].parent_tag = IASECC_SDO_PUBKEY_TAG; + sdo_update->fields[index].tag = IASECC_SDO_PUBKEY_TAG_E; + sdo_update->fields[index].value = rsa->exponent.data; + sdo_update->fields[index].size = rsa->exponent.len; + index++; } if (rsa->modulus.len) { - sdo_update->fields[indx].parent_tag = IASECC_SDO_PUBKEY_TAG; - sdo_update->fields[indx].tag = IASECC_SDO_PUBKEY_TAG_N; - sdo_update->fields[indx].value = rsa->modulus.data; - sdo_update->fields[indx].size = rsa->modulus.len; - indx++; + sdo_update->fields[index].parent_tag = IASECC_SDO_PUBKEY_TAG; + sdo_update->fields[index].tag = IASECC_SDO_PUBKEY_TAG_N; + sdo_update->fields[index].value = rsa->modulus.data; + sdo_update->fields[index].size = rsa->modulus.len; + index++; } if (sdo->data.pub_key.cha.value) { - sdo_update->fields[indx].parent_tag = IASECC_SDO_PUBKEY_TAG; - sdo_update->fields[indx].tag = IASECC_SDO_PUBKEY_TAG_CHA; - sdo_update->fields[indx].value = sdo->data.pub_key.cha.value; - sdo_update->fields[indx].size = sdo->data.pub_key.cha.size; - indx++; + sdo_update->fields[index].parent_tag = IASECC_SDO_PUBKEY_TAG; + sdo_update->fields[index].tag = IASECC_SDO_PUBKEY_TAG_CHA; + sdo_update->fields[index].value = sdo->data.pub_key.cha.value; + sdo_update->fields[index].size = sdo->data.pub_key.cha.size; + index++; } if (sdo->data.pub_key.chr.value) { - sdo_update->fields[indx].parent_tag = IASECC_SDO_PUBKEY_TAG; - sdo_update->fields[indx].tag = IASECC_SDO_PUBKEY_TAG_CHR; - sdo_update->fields[indx].value = sdo->data.pub_key.chr.value; - sdo_update->fields[indx].size = sdo->data.pub_key.chr.size; - indx++; + sdo_update->fields[index].parent_tag = IASECC_SDO_PUBKEY_TAG; + sdo_update->fields[index].tag = IASECC_SDO_PUBKEY_TAG_CHR; + sdo_update->fields[index].value = sdo->data.pub_key.chr.value; + sdo_update->fields[index].size = sdo->data.pub_key.chr.size; + index++; } /* For ECC card 'compulsory' flag should be already here */ if (!sdo->data.pub_key.compulsory.on_card) { if (sdo->data.pub_key.compulsory.value) { - sdo_update->fields[indx].parent_tag = IASECC_SDO_PUBKEY_TAG; - sdo_update->fields[indx].tag = IASECC_SDO_PUBKEY_TAG_COMPULSORY; - sdo_update->fields[indx].value = sdo->data.pub_key.compulsory.value; - sdo_update->fields[indx].size = sdo->data.pub_key.compulsory.size; - indx++; + sdo_update->fields[index].parent_tag = IASECC_SDO_PUBKEY_TAG; + sdo_update->fields[index].tag = IASECC_SDO_PUBKEY_TAG_COMPULSORY; + sdo_update->fields[index].value = sdo->data.pub_key.compulsory.value; + sdo_update->fields[index].size = sdo->data.pub_key.compulsory.size; + index++; } } } @@ -1199,7 +1264,7 @@ int iasecc_sdo_parse_card_answer(struct sc_context *ctx, unsigned char *data, size_t data_len, struct iasecc_sm_card_answer *out) { - int have_mac = 0, have_status = 0; + int rc, have_mac = 0, have_status = 0; size_t size = 0, size_size, offs; LOG_FUNC_CALLED(ctx); @@ -1207,19 +1272,26 @@ iasecc_sdo_parse_card_answer(struct sc_context *ctx, unsigned char *data, size_t LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); memset(out, 0, sizeof(*out)); - for (offs=0; offs data_len) { + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + } if (*(data + offs) == IASECC_CARD_ANSWER_TAG_DATA ) { if (size > sizeof(out->data)) - LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "iasecc_sm_decode_answer() unbelivable !!!"); + LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "iasecc_sm_decode_answer() unbelievable !!!"); memcpy(out->data, data + offs + size_size + 1, size); out->data_len = size; offs += 1 + size_size + size; } else if (*(data + offs) == IASECC_CARD_ANSWER_TAG_SW ) { - if (*(data + offs + 1) != 2) + if (size_size != 1 || size != 2) LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_sm_decode_answer() SW length not 2"); out->sw = *(data + offs + 2) * 0x100 + *(data + offs + 3); @@ -1229,7 +1301,7 @@ iasecc_sdo_parse_card_answer(struct sc_context *ctx, unsigned char *data, size_t have_status = 1; } else if (*(data + offs) == IASECC_CARD_ANSWER_TAG_MAC ) { - if (*(data + offs + 1) != 8) + if (size_size != 1 || size != 8) LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_sm_decode_answer() MAC length not 8"); memcpy(out->mac, data + offs + 2, 8); @@ -1279,7 +1351,7 @@ iasecc_docp_copy(struct sc_context *ctx, struct iasecc_sdo_docp *in, struct iase int rv; LOG_FUNC_CALLED(ctx); - if (!in || !out) + if (!in || !out) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); memset(out, 0, sizeof(struct iasecc_sdo_docp)); diff --git a/src/libopensc/iasecc-sdo.h b/src/libopensc/iasecc-sdo.h index 941ec5b3ae..1d9f0d27df 100644 --- a/src/libopensc/iasecc-sdo.h +++ b/src/libopensc/iasecc-sdo.h @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SC_IASECC_SDO_H @@ -34,7 +34,7 @@ #define IASECC_DOCP_TAG_TRIES_REMAINING 0x9B #define IASECC_DOCP_TAG_USAGE_MAXIMUM 0x9C #define IASECC_DOCP_TAG_USAGE_REMAINING 0x9D -#define IASECC_DOCP_TAG_NON_REPUDATION 0x9E +#define IASECC_DOCP_TAG_NON_REPUDIATION 0x9E #define IASECC_DOCP_TAG_SIZE 0x80 #define IASECC_DOCP_TAG_ACLS 0xA1 #define IASECC_DOCP_TAG_ACLS_CONTACT 0x8C @@ -322,7 +322,7 @@ int iasecc_docp_copy(struct sc_context *, struct iasecc_sdo_docp *, struct iasec int iasecc_se_get_info(struct sc_card *card, struct iasecc_se_info *se); int iasecc_sm_external_authentication(struct sc_card *card, unsigned skey_ref, int *tries_left); -int iasecc_sm_pin_verify(struct sc_card *card, unsigned se_num, struct sc_pin_cmd_data *data, int *tries_left); +int iasecc_sm_pin_verify(struct sc_card *card, unsigned se_num, struct sc_pin_cmd_data *data); int iasecc_sm_pin_reset(struct sc_card *card, unsigned se_num, struct sc_pin_cmd_data *data); int iasecc_sm_update_binary(struct sc_card *card, unsigned se_num, size_t offs, const unsigned char *buff, size_t count); int iasecc_sm_read_binary(struct sc_card *card, unsigned se_num, size_t offs, unsigned char *buff, size_t count); diff --git a/src/libopensc/iasecc-sm.c b/src/libopensc/iasecc-sm.c index 681a767762..e589e8d7ae 100644 --- a/src/libopensc/iasecc-sm.c +++ b/src/libopensc/iasecc-sm.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include @@ -38,14 +38,16 @@ static int sm_save_sc_context (struct sc_card *card, struct sm_info *sm_info) { + struct iasecc_private_data *prv; struct sc_context *ctx; struct sc_card_cache *cache; if (!card || !sm_info) return SC_ERROR_INVALID_ARGUMENTS; + prv = (struct iasecc_private_data *)card->drv_data; ctx = card->ctx; - cache = &card->cache; + cache = &prv->cache; sc_log(ctx, "SM save context: cache(valid:%i,current_df:%p)", cache->valid, cache->current_df); if (cache->valid && cache->current_df) { @@ -91,7 +93,8 @@ iasecc_sm_transmit_apdus(struct sc_card *card, struct sc_remote_data *rdata, { struct sc_context *ctx = card->ctx; struct sc_remote_apdu *rapdu = rdata->data; - int rv = SC_SUCCESS, offs = 0; + int rv = SC_SUCCESS; + size_t offs = 0; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_sm_transmit_apdus() rdata-length %i", rdata->length); @@ -105,7 +108,7 @@ iasecc_sm_transmit_apdus(struct sc_card *card, struct sc_remote_data *rdata, LOG_TEST_RET(ctx, rv, "iasecc_sm_transmit_apdus() fatal error %i"); if (out && out_len && (rapdu->flags & SC_REMOTE_APDU_FLAG_RETURN_ANSWER)) { - int len = rapdu->apdu.resplen > (*out_len - offs) ? (*out_len - offs) : rapdu->apdu.resplen; + size_t len = rapdu->apdu.resplen > (*out_len - offs) ? (*out_len - offs) : rapdu->apdu.resplen; memcpy(out + offs, rapdu->apdu.resp, len); offs += len; @@ -235,9 +238,8 @@ iasecc_sm_se_mutual_authentication(struct sc_card *card, unsigned se_num) LOG_TEST_RET(ctx, rv, "Get SE info error"); rv = iasecc_se_get_crt(card, &se, crt); - LOG_TEST_RET(ctx, rv, "Cannot get authentication CRT"); - sc_file_free(se.df); + LOG_TEST_RET(ctx, rv, "Cannot get authentication CRT"); /* MSE SET Mutual Authentication SK scheme */ offs = 0; @@ -260,31 +262,6 @@ iasecc_sm_se_mutual_authentication(struct sc_card *card, unsigned se_num) LOG_FUNC_RETURN(ctx, rv); } - - -static int -iasecc_sm_get_challenge(struct sc_card *card, unsigned char *out, size_t len) -{ - struct sc_context *ctx = card->ctx; - struct sc_apdu apdu; - unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; - int rv; - - sc_log(ctx, "SM get challenge: length %"SC_FORMAT_LEN_SIZE_T"u", len); - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0, 0); - apdu.le = len; - apdu.resplen = len; - apdu.resp = rbuf; - - rv = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(ctx, rv, "APDU transmit failed"); - rv = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(ctx, rv, "Command failed"); - - memcpy(out, rbuf, apdu.resplen); - - LOG_FUNC_RETURN(ctx, apdu.resplen); -} #endif @@ -309,7 +286,7 @@ iasecc_sm_initialize(struct sc_card *card, unsigned se_num, unsigned cmd) rv = iasecc_sm_se_mutual_authentication(card, se_num); LOG_TEST_RET(ctx, rv, "iasecc_sm_initialize() MUTUAL AUTHENTICATION failed"); - rv = iasecc_sm_get_challenge(card, cwa_session->card_challenge, SM_SMALL_CHALLENGE_LEN); + rv = sc_get_challenge(card, cwa_session->card_challenge, SM_SMALL_CHALLENGE_LEN); LOG_TEST_RET(ctx, rv, "iasecc_sm_initialize() GET CHALLENGE failed"); sc_remote_data_init(&rdata); @@ -335,7 +312,7 @@ iasecc_sm_initialize(struct sc_card *card, unsigned se_num, unsigned cmd) rv = iasecc_sm_transmit_apdus (card, &rdata, cwa_session->mdata, &cwa_session->mdata_len); if (rv == SC_ERROR_PIN_CODE_INCORRECT) sc_log(ctx, "SM initialization failed, %i tries left", (rdata.data + rdata.length - 1)->apdu.sw2 & 0x0F); - LOG_TEST_RET(ctx, rv, "iasecc_sm_initialize() trasmit APDUs failed"); + LOG_TEST_RET(ctx, rv, "iasecc_sm_initialize() transmit APDUs failed"); rdata.free(&rdata); @@ -470,7 +447,7 @@ iasecc_sm_rsa_update(struct sc_card *card, unsigned se_num, struct iasecc_sdo_rs int -iasecc_sm_pin_verify(struct sc_card *card, unsigned se_num, struct sc_pin_cmd_data *data, int *tries_left) +iasecc_sm_pin_verify(struct sc_card *card, unsigned se_num, struct sc_pin_cmd_data *data) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM @@ -479,7 +456,7 @@ iasecc_sm_pin_verify(struct sc_card *card, unsigned se_num, struct sc_pin_cmd_da int rv; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "iasecc_sm_pin_verify() SE#%i, PIN(ref:%i,len:%i)", se_num, data->pin_reference, data->pin1.len); + sc_log(ctx, "iasecc_sm_pin_verify() SE#%i, PIN(ref:%i,len:%zu)", se_num, data->pin_reference, data->pin1.len); rv = iasecc_sm_initialize(card, se_num, SM_CMD_PIN_VERIFY); LOG_TEST_RET(ctx, rv, "iasecc_sm_pin_verify() SM INITIALIZE failed"); @@ -488,9 +465,9 @@ iasecc_sm_pin_verify(struct sc_card *card, unsigned se_num, struct sc_pin_cmd_da sc_remote_data_init(&rdata); rv = iasecc_sm_cmd(card, &rdata); - if (rv && rdata.length && tries_left) + if (rv && rdata.length) if (rdata.data->apdu.sw1 == 0x63 && (rdata.data->apdu.sw2 & 0xF0) == 0xC0) - *tries_left = rdata.data->apdu.sw2 & 0x0F; + data->pin1.tries_left = rdata.data->apdu.sw2 & 0x0F; LOG_TEST_RET(ctx, rv, "iasecc_sm_pin_verify() SM 'PIN VERIFY' failed"); @@ -551,7 +528,7 @@ iasecc_sm_pin_reset(struct sc_card *card, unsigned se_num, struct sc_pin_cmd_dat int rv; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "iasecc_sm_pin_reset() SE#%i, PIN(ref:%i,len:%i)", se_num, data->pin_reference, data->pin2.len); + sc_log(ctx, "iasecc_sm_pin_reset() SE#%i, PIN(ref:%i,len:%zu)", se_num, data->pin_reference, data->pin2.len); rv = iasecc_sm_initialize(card, se_num, SM_CMD_PIN_RESET); LOG_TEST_RET(ctx, rv, "iasecc_sm_pin_reset() SM INITIALIZE failed"); @@ -683,7 +660,7 @@ iasecc_sm_update_binary(struct sc_card *card, unsigned se_num, size_t offs, LOG_TEST_RET(ctx, rv, "iasecc_sm_update_binary() SM release failed"); rdata.free(&rdata); - LOG_FUNC_RETURN(ctx, count); + LOG_FUNC_RETURN(ctx, (int)count); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of Secure-Messaging"); return SC_ERROR_NOT_SUPPORTED; diff --git a/src/libopensc/iasecc.h b/src/libopensc/iasecc.h index 53ba14e977..2e5408e981 100644 --- a/src/libopensc/iasecc.h +++ b/src/libopensc/iasecc.h @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_IASECC_H @@ -27,7 +27,6 @@ #include "libopensc/iasecc-sdo.h" #define ISO7812_PAN_SN_TAG 0x5A -#define ISO7812_PAN_LENGTH 0x0C #ifndef SHA256_DIGEST_LENGTH #define SHA_DIGEST_LENGTH 20 @@ -124,6 +123,15 @@ struct iasecc_io_buffer_sizes { size_t recv_sc; }; +struct sc_card_cache { + struct sc_path current_path; + + struct sc_file *current_ef; + struct sc_file *current_df; + + int valid; +}; + struct iasecc_private_data { struct iasecc_version version; struct iasecc_io_buffer_sizes max_sizes; @@ -133,5 +141,8 @@ struct iasecc_private_data { unsigned op_method, op_ref; struct iasecc_se_info *se_info; + + struct sc_card_cache cache; }; + #endif diff --git a/src/libopensc/internal-winscard.h b/src/libopensc/internal-winscard.h index 82a643921b..2aa250b857 100644 --- a/src/libopensc/internal-winscard.h +++ b/src/libopensc/internal-winscard.h @@ -93,6 +93,7 @@ typedef unsigned __int8 uint8_t; #define SCARD_S_SUCCESS 0x00000000 /**< No error was encountered. */ #define SCARD_E_CANCELLED 0x80100002 /**< The action was cancelled by an SCardCancel request. */ #define SCARD_E_INVALID_HANDLE 0x80100003 /**< The supplied handle was invalid. */ +#define SCARD_E_UNKNOWN_READER 0x80100009 /**< The specified reader name is not recognized. */ #define SCARD_E_TIMEOUT 0x8010000A /**< The user-specified timeout value has expired. */ #define SCARD_E_SHARING_VIOLATION 0x8010000B /**< The smart card cannot be accessed because of other connections outstanding. */ #define SCARD_E_NO_SMARTCARD 0x8010000C /**< The operation requires a smart card, but no smart card is currently in the device. */ @@ -100,6 +101,7 @@ typedef unsigned __int8 uint8_t; #define SCARD_E_NOT_TRANSACTED 0x80100016 /**< An attempt was made to end a non-existent transaction. */ #define SCARD_E_READER_UNAVAILABLE 0x80100017 /**< The specified reader is not currently available for use. */ #define SCARD_E_NO_SERVICE 0x8010001D /**< The Smart card resource manager is not running. */ +#define SCARD_E_SERVICE_STOPPED 0x8010001E /**< The smart card resource manager has shut down. */ #define SCARD_E_NO_READERS_AVAILABLE 0x8010002E /**< Cannot find a smart card reader. */ #define SCARD_W_UNRESPONSIVE_CARD 0x80100066 /**< The smart card is not responding to a reset. */ #define SCARD_W_UNPOWERED_CARD 0x80100067 /**< Power has been removed from the smart card, so that further communication is not possible. */ @@ -138,13 +140,20 @@ typedef const SCARD_IO_REQUEST *LPCSCARD_IO_REQUEST; #endif /* HAVE_SCARD_H */ +#ifndef PCSC_API #if defined(_WIN32) #define PCSC_API WINAPI -#elif defined(USE_CYGWIN) +#elif defined(__CYGWIN__) #define PCSC_API __stdcall #else #define PCSC_API #endif +#endif + +#ifdef __APPLE__ +#define extern +#define __attribute__(a) +#endif typedef LONG (PCSC_API *SCardEstablishContext_t)(DWORD dwScope, LPCVOID pvReserved1, LPCVOID pvReserved2, LPSCARDCONTEXT phContext); @@ -174,6 +183,11 @@ typedef LONG (PCSC_API *SCardListReaders_t)(SCARDCONTEXT hContext, LPCSTR mszGro typedef LONG (PCSC_API *SCardGetAttrib_t)(SCARDHANDLE hCard, DWORD dwAttrId,\ LPBYTE pbAttr, LPDWORD pcbAttrLen); +#ifdef __APPLE__ +#undef extern +#undef __attribute__ +#endif + /* Copied from pcsc-lite reader.h */ #ifndef SCARD_CTL_CODE @@ -237,7 +251,7 @@ typedef LONG (PCSC_API *SCardGetAttrib_t)(SCARDHANDLE hCard, DWORD dwAttrId,\ /* structures used (but not defined) in PCSC Part 10: * "IFDs with Secure Pin Entry Capabilities" */ -/* Set structure elements aligment on bytes +/* Set structure elements alignment on bytes * http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html */ #if defined(__APPLE__) || defined(sun) #pragma pack(1) diff --git a/src/libopensc/internal.h b/src/libopensc/internal.h index 33f64cfeda..2b65b9984f 100644 --- a/src/libopensc/internal.h +++ b/src/libopensc/internal.h @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SC_INTERNAL_H @@ -33,12 +33,22 @@ extern "C" { #include #ifdef _WIN32 #include + +# ifdef _MSC_VER +# ifndef _SSIZE_T_DEFINED +# undef ssize_t +# include + typedef _W64 SSIZE_T ssize_t; +# define _SSIZE_T_DEFINED +# endif /* _SSIZE_T_DEFINED */ +# endif /* _MSC_VER */ #endif #include "common/simclist.h" #include "libopensc/opensc.h" #include "libopensc/log.h" #include "libopensc/cards.h" +#include "scconf/scconf.h" #ifdef ENABLE_OPENSSL #include "libopensc/sc-ossl-compat.h" @@ -117,6 +127,23 @@ unsigned short bebytes2ushort(const u8 *buf); * @return the converted value */ unsigned short lebytes2ushort(const u8 *buf); +/** + * Convert 4 bytes in little endian order into an unsigned long + * @param buf the byte array of 4 bytes + * @return the converted value + */ +unsigned long lebytes2ulong(const u8 *buf); + +/* Usable for setting string elements of token info, which + * are either initialized to NULL or we need to clean + * previous value. + * + * @param strp The pointer where to store string + * @param value The string to store (is strdupped) + */ +void set_string(char **strp, const char *value); + +#define BYTES4BITS(num) (((num) + 7) / 8) /* number of bytes necessary to hold 'num' bits */ /* Returns an scconf_block entry with matching ATR/ATRmask to the ATR specified, * NULL otherwise. Additionally, if card driver is not specified, search through @@ -125,14 +152,20 @@ scconf_block *_sc_match_atr_block(sc_context_t *ctx, struct sc_card_driver *driv /* Returns an index number if a match was found, -1 otherwise. table has to * be null terminated. */ -int _sc_match_atr(struct sc_card *card, struct sc_atr_table *table, int *type_out); +int _sc_match_atr(struct sc_card *card, const struct sc_atr_table *table, int *type_out); int _sc_card_add_algorithm(struct sc_card *card, const struct sc_algorithm_info *info); int _sc_card_add_symmetric_alg(sc_card_t *card, unsigned int algorithm, unsigned int key_length, unsigned long flags); -int _sc_card_add_rsa_alg(struct sc_card *card, unsigned int key_length, +int _sc_card_add_rsa_alg(struct sc_card *card, size_t key_length, unsigned long flags, unsigned long exponent); -int _sc_card_add_ec_alg(struct sc_card *card, unsigned int key_length, +int _sc_card_add_ec_alg(struct sc_card *card, size_t key_length, + unsigned long flags, unsigned long ext_flags, + struct sc_object_id *curve_oid); +int _sc_card_add_eddsa_alg(struct sc_card *card, size_t key_length, + unsigned long flags, unsigned long ext_flags, + struct sc_object_id *curve_oid); +int _sc_card_add_xeddsa_alg(struct sc_card *card, size_t key_length, unsigned long flags, unsigned long ext_flags, struct sc_object_id *curve_oid); @@ -142,10 +175,14 @@ int _sc_card_add_ec_alg(struct sc_card *card, unsigned int key_length, int sc_pkcs1_strip_01_padding(struct sc_context *ctx, const u8 *in_dat, size_t in_len, u8 *out_dat, size_t *out_len); -int sc_pkcs1_strip_02_padding(struct sc_context *ctx, const u8 *data, size_t len, - u8 *out_dat, size_t *out_len); +int sc_pkcs1_strip_02_padding_constant_time(sc_context_t *ctx, unsigned int n, const u8 *data, + unsigned int data_len, u8 *out, unsigned int *out_len); int sc_pkcs1_strip_digest_info_prefix(unsigned int *algorithm, const u8 *in_dat, size_t in_len, u8 *out_dat, size_t *out_len); +#ifdef ENABLE_OPENSSL +int sc_pkcs1_strip_oaep_padding(sc_context_t *ctx, u8 *data, size_t len, + unsigned long flags, uint8_t *param, size_t paramlen); +#endif /** * PKCS1 encodes the given data. @@ -155,11 +192,11 @@ int sc_pkcs1_strip_digest_info_prefix(unsigned int *algorithm, * @param inlen IN length of the input * @param out OUT output buffer (in == out is allowed) * @param outlen OUT length of the output buffer - * @param modlen IN length of the modulus in bytes + * @param mod_bits IN length of the modulus in bits * @return SC_SUCCESS on success and an error code otherwise */ int sc_pkcs1_encode(sc_context_t *ctx, unsigned long flags, - const u8 *in, size_t inlen, u8 *out, size_t *outlen, size_t modlen); + const u8 *in, size_t inlen, u8 *out, size_t *outlen, size_t mod_bits, void *pMechanism); /** * Get the necessary padding and sec. env. flags. * @param ctx IN sc_contex_t object @@ -187,7 +224,7 @@ int sc_get_encoding_flags(sc_context_t *ctx, int sc_mutex_create(const sc_context_t *ctx, void **mutex); /** * Tries to acquire a lock for a sc_mutex object. Note: Unless - * sc_mutex_set_mutex_funcs() has been called before this + * sc_mutex_set_mutex_funcs() has been called before this * function does nothing and always returns SUCCESS. * @param ctx sc_context_t object with the thread context * @param mutex mutex object to lock @@ -247,18 +284,18 @@ int sc_apdu_set_resp(sc_context_t *ctx, sc_apdu_t *apdu, const u8 *buf, /** * Logs APDU * @param ctx sc_context_t object - * @param level log if ctx->debug >= level * @param buf buffer with the APDU data * @param len length of the APDU * @param is_outgoing != 0 if the data is send to the card */ -#define sc_apdu_log(ctx, level, data, len, is_outgoing) \ - sc_debug_hex(ctx, level, is_outgoing != 0 ? "Outgoing APDU" : "Incoming APDU", data, len) +#define sc_apdu_log(ctx, data, len, is_outgoing) \ + sc_debug_hex(ctx, SC_LOG_DEBUG_NORMAL, is_outgoing != 0 ? "Outgoing APDU" : "Incoming APDU", data, len) extern struct sc_reader_driver *sc_get_pcsc_driver(void); extern struct sc_reader_driver *sc_get_ctapi_driver(void); extern struct sc_reader_driver *sc_get_openct_driver(void); extern struct sc_reader_driver *sc_get_cryptotokenkit_driver(void); +extern struct sc_reader_driver *sc_get_empty_driver(void); #ifdef __cplusplus } diff --git a/src/libopensc/iso7816.c b/src/libopensc/iso7816.c index 72fa6c41c7..3097eeadd6 100644 --- a/src/libopensc/iso7816.c +++ b/src/libopensc/iso7816.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -30,8 +30,8 @@ #include "iso7816.h" #include "sm/sm-iso.h" - -static void fixup_transceive_length(const struct sc_card *card, +void +iso7816_fixup_transceive_length(const struct sc_card *card, struct sc_apdu *apdu) { if (card == NULL || apdu == NULL) { @@ -57,10 +57,15 @@ static const struct sc_card_error iso7816_errors[] = { { 0x6282, SC_ERROR_FILE_END_REACHED, "End of file/record reached before reading Le bytes" }, { 0x6283, SC_ERROR_CARD_CMD_FAILED, "Selected file invalidated" }, { 0x6284, SC_ERROR_CARD_CMD_FAILED, "FCI not formatted according to ISO 7816-4" }, + { 0x6285, SC_ERROR_CARD_CMD_FAILED, "Selected file in termination state" }, + { 0x6286, SC_ERROR_CARD_CMD_FAILED, "No input data available from a sensor on the card" }, { 0x6300, SC_ERROR_CARD_CMD_FAILED, "Warning: no information given, non-volatile memory has changed" }, { 0x6381, SC_ERROR_CARD_CMD_FAILED, "Warning: file filled up by last write" }, + { 0x6400, SC_ERROR_CARD_CMD_FAILED, "Execution error" }, + { 0x6401, SC_ERROR_CARD_CMD_FAILED, "Immediate response required by the card" }, + { 0x6581, SC_ERROR_MEMORY_FAILURE, "Memory failure" }, { 0x6700, SC_ERROR_WRONG_LENGTH, "Wrong length" }, @@ -68,37 +73,36 @@ static const struct sc_card_error iso7816_errors[] = { { 0x6800, SC_ERROR_NO_CARD_SUPPORT, "Functions in CLA not supported" }, { 0x6881, SC_ERROR_NO_CARD_SUPPORT, "Logical channel not supported" }, { 0x6882, SC_ERROR_NO_CARD_SUPPORT, "Secure messaging not supported" }, + { 0x6883, SC_ERROR_CARD_CMD_FAILED, "Last command of the chain expected" }, + { 0x6884, SC_ERROR_NO_CARD_SUPPORT, "Command chaining not supported" }, { 0x6900, SC_ERROR_NOT_ALLOWED, "Command not allowed" }, { 0x6981, SC_ERROR_CARD_CMD_FAILED, "Command incompatible with file structure" }, - { 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED, "Security status not satisfied" }, + { 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"Security status not satisfied" }, { 0x6983, SC_ERROR_AUTH_METHOD_BLOCKED, "Authentication method blocked" }, { 0x6984, SC_ERROR_REF_DATA_NOT_USABLE, "Referenced data not usable" }, { 0x6985, SC_ERROR_NOT_ALLOWED, "Conditions of use not satisfied" }, { 0x6986, SC_ERROR_NOT_ALLOWED, "Command not allowed (no current EF)" }, { 0x6987, SC_ERROR_INCORRECT_PARAMETERS,"Expected SM data objects missing" }, - { 0x6988, SC_ERROR_INCORRECT_PARAMETERS,"SM data objects incorrect" }, + { 0x6988, SC_ERROR_INCORRECT_PARAMETERS,"Incorrect SM data objects" }, { 0x6A00, SC_ERROR_INCORRECT_PARAMETERS,"Wrong parameter(s) P1-P2" }, { 0x6A80, SC_ERROR_INCORRECT_PARAMETERS,"Incorrect parameters in the data field" }, { 0x6A81, SC_ERROR_NO_CARD_SUPPORT, "Function not supported" }, - { 0x6A82, SC_ERROR_FILE_NOT_FOUND, "File not found" }, + { 0x6A82, SC_ERROR_FILE_NOT_FOUND, "File or application not found" }, { 0x6A83, SC_ERROR_RECORD_NOT_FOUND, "Record not found" }, { 0x6A84, SC_ERROR_NOT_ENOUGH_MEMORY, "Not enough memory space in the file" }, { 0x6A85, SC_ERROR_INCORRECT_PARAMETERS,"Lc inconsistent with TLV structure" }, { 0x6A86, SC_ERROR_INCORRECT_PARAMETERS,"Incorrect parameters P1-P2" }, { 0x6A87, SC_ERROR_INCORRECT_PARAMETERS,"Lc inconsistent with P1-P2" }, { 0x6A88, SC_ERROR_DATA_OBJECT_NOT_FOUND,"Referenced data not found" }, - { 0x6A89, SC_ERROR_FILE_ALREADY_EXISTS, "File already exists"}, - { 0x6A8A, SC_ERROR_FILE_ALREADY_EXISTS, "DF name already exists"}, + { 0x6A89, SC_ERROR_FILE_ALREADY_EXISTS, "File already exists"}, + { 0x6A8A, SC_ERROR_FILE_ALREADY_EXISTS, "DF name already exists"}, { 0x6B00, SC_ERROR_INCORRECT_PARAMETERS,"Wrong parameter(s) P1-P2" }, { 0x6D00, SC_ERROR_INS_NOT_SUPPORTED, "Instruction code not supported or invalid" }, { 0x6E00, SC_ERROR_CLASS_NOT_SUPPORTED, "Class not supported" }, { 0x6F00, SC_ERROR_CARD_CMD_FAILED, "No precise diagnosis" }, - - - }; @@ -113,12 +117,12 @@ iso7816_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) sc_log(card->ctx, "Wrong length; correct length is %d", sw2); return SC_ERROR_WRONG_LENGTH; } - if (sw1 == 0x90) + if (sw1 == 0x90 && sw2 == 0x00) return SC_SUCCESS; - if (sw1 == 0x63U && (sw2 & ~0x0fU) == 0xc0U ) { - sc_log(card->ctx, "Verification failed (remaining tries: %d)", (sw2 & 0x0f)); - return SC_ERROR_PIN_CODE_INCORRECT; - } + if (sw1 == 0x63U && (sw2 & ~0x0fU) == 0xc0U) { + sc_log(card->ctx, "PIN not verified (remaining tries: %d)", (sw2 & 0x0f)); + return SC_ERROR_PIN_CODE_INCORRECT; + } for (i = 0; i < err_count; i++) { if (iso7816_errors[i].SWs == ((sw1 << 8) | sw2)) { sc_log(card->ctx, "%s", iso7816_errors[i].errorstr); @@ -132,13 +136,13 @@ iso7816_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) static int -iso7816_read_binary(struct sc_card *card, unsigned int idx, u8 *buf, size_t count, unsigned long flags) +iso7816_read_binary(struct sc_card *card, unsigned int idx, u8 *buf, size_t count, unsigned long *flags) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; int r; - if (idx > 0x7fff) { + if (idx > 0x7FFF) { sc_log(ctx, "invalid EF offset: 0x%X > 0x7FFF", idx); return SC_ERROR_OFFSET_TOO_LARGE; } @@ -148,53 +152,131 @@ iso7816_read_binary(struct sc_card *card, unsigned int idx, u8 *buf, size_t coun apdu.resplen = count; apdu.resp = buf; - fixup_transceive_length(card, &apdu); + iso7816_fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, r, "APDU transmit failed"); - if (apdu.resplen == 0) - LOG_FUNC_RETURN(ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r == SC_ERROR_FILE_END_REACHED) - LOG_FUNC_RETURN(ctx, apdu.resplen); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r == SC_ERROR_FILE_END_REACHED || r == SC_ERROR_INCORRECT_PARAMETERS) + LOG_FUNC_RETURN(ctx, (int)apdu.resplen); LOG_TEST_RET(ctx, r, "Check SW error"); - if (apdu.resplen < count) { - r = iso7816_read_binary(card, idx + apdu.resplen, buf + apdu.resplen, count - apdu.resplen, flags); - /* Ignore all but 'corrupted data' errors */ - if (r == SC_ERROR_CORRUPTED_DATA) - LOG_FUNC_RETURN(ctx, SC_ERROR_CORRUPTED_DATA); - else if (r > 0) - apdu.resplen += r; + LOG_FUNC_RETURN(ctx, (int)apdu.resplen); +} + + +const struct sc_asn1_entry c_asn1_do_data[] = { + { "Offset Data Object", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x14, SC_ASN1_OPTIONAL, NULL, NULL }, + { "Discretionary Data Object", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x13, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED|SC_ASN1_OPTIONAL, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } +}; + +int encode_do_data(struct sc_context *ctx, + unsigned int idx, const unsigned char *data, size_t data_len, + u8 **out, size_t *outlen) +{ + unsigned char offset_buffer[2]; + size_t offset_buffer_len = sizeof offset_buffer; + struct sc_asn1_entry asn1_do_data[sizeof c_asn1_do_data / sizeof *c_asn1_do_data]; + sc_copy_asn1_entry(c_asn1_do_data, asn1_do_data); + + if (idx > 0xFFFF) + LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "Offset beyond 0xFFFF not supported"); + offset_buffer[0] = (u8) (idx >> 8); + offset_buffer[1] = (u8) (idx & 0x00FF); + sc_format_asn1_entry(asn1_do_data + 0, offset_buffer, &offset_buffer_len, 1); + + if (data && data_len) { + sc_format_asn1_entry(asn1_do_data + 1, (void *) &data, &data_len, 1); + } else { + sc_format_asn1_entry(asn1_do_data + 1, NULL, NULL, 0); } - LOG_FUNC_RETURN(ctx, apdu.resplen); + LOG_TEST_RET(ctx, + sc_asn1_encode(ctx, asn1_do_data, out, outlen), + "sc_asn1_encode() failed"); + + return SC_SUCCESS; } +int decode_do_data(struct sc_context *ctx, + const unsigned char *encoded_data, size_t encoded_data_len, + u8 **out, size_t *outlen) +{ + struct sc_asn1_entry asn1_do_data[sizeof c_asn1_do_data / sizeof *c_asn1_do_data]; + sc_copy_asn1_entry(c_asn1_do_data, asn1_do_data); + + sc_format_asn1_entry(asn1_do_data + 0, NULL, NULL, 0); + sc_format_asn1_entry(asn1_do_data + 1, out, outlen, 0); + + LOG_TEST_RET(ctx, + sc_asn1_decode(ctx, asn1_do_data, encoded_data, encoded_data_len, NULL, NULL), + "sc_asn1_decode() failed"); + + if (!(asn1_do_data[1].flags & SC_ASN1_PRESENT)) + return SC_ERROR_INVALID_ASN1_OBJECT; + + return SC_SUCCESS; +} static int -iso7816_read_record(struct sc_card *card, - unsigned int rec_nr, u8 *buf, size_t count, unsigned long flags) +iso7816_read_record(struct sc_card *card, unsigned int rec_nr, unsigned int idx, + u8 *buf, size_t count, unsigned long flags) { struct sc_apdu apdu; int r; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0xB2, rec_nr, 0); + /* XXX maybe use some bigger buffer */ + unsigned char resp[SC_MAX_APDU_RESP_SIZE]; + unsigned char *encoded_data = NULL, *decoded_data = NULL; + size_t encoded_data_len = 0, decoded_data_len = 0; + + if (rec_nr > 0xFF) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + if (idx == 0) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0xB2, rec_nr, 0); + apdu.le = count; + apdu.resplen = count; + apdu.resp = buf; + } else { + r = encode_do_data(card->ctx, idx, NULL, 0, &encoded_data, &encoded_data_len); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not encode data objects"); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0xB3, rec_nr, 0); + apdu.lc = encoded_data_len; + apdu.datalen = encoded_data_len; + apdu.data = encoded_data; + apdu.le = sizeof resp; + apdu.resplen = sizeof resp; + apdu.resp = resp; + } apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3; if (flags & SC_RECORD_BY_REC_NR) apdu.p2 |= 0x04; - apdu.le = count; - apdu.resplen = count; - apdu.resp = buf; - - fixup_transceive_length(card, &apdu); + iso7816_fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - if (apdu.resplen == 0) - LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); + LOG_TEST_GOTO_ERR(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_GOTO_ERR(card->ctx, r, "Card returned error"); + + if (idx == 0) { + r = (int)apdu.resplen; + } else { + r = decode_do_data(card->ctx, apdu.resp, apdu.resplen, + &decoded_data, &decoded_data_len); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not decode data objects"); + if (decoded_data_len <= count) { + count = decoded_data_len; + } + memcpy(buf, decoded_data, count); + r = (int)count; + } - LOG_FUNC_RETURN(card->ctx, apdu.resplen); +err: + free(encoded_data); + free(decoded_data); + LOG_FUNC_RETURN(card->ctx, r); } @@ -206,21 +288,20 @@ iso7816_write_record(struct sc_card *card, unsigned int rec_nr, int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xD2, rec_nr, 0); - apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3; - if (flags & SC_RECORD_BY_REC_NR) - apdu.p2 |= 0x04; - apdu.lc = count; apdu.datalen = count; apdu.data = buf; + apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3; + if (flags & SC_RECORD_BY_REC_NR) + apdu.p2 |= 0x04; - fixup_transceive_length(card, &apdu); + iso7816_fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); - LOG_FUNC_RETURN(card->ctx, count); + LOG_FUNC_RETURN(card->ctx, (int)count); } @@ -232,48 +313,63 @@ iso7816_append_record(struct sc_card *card, int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xE2, 0, 0); - apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3; - apdu.lc = count; apdu.datalen = count; apdu.data = buf; + apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3; - fixup_transceive_length(card, &apdu); + iso7816_fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); - LOG_FUNC_RETURN(card->ctx, count); + LOG_FUNC_RETURN(card->ctx, (int)count); } static int -iso7816_update_record(struct sc_card *card, unsigned int rec_nr, +iso7816_update_record(struct sc_card *card, unsigned int rec_nr, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { struct sc_apdu apdu; int r; + unsigned char *encoded_data = NULL; + size_t encoded_data_len = 0; + + if (rec_nr > 0xFF) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + if (idx == 0) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDC, rec_nr, 0); + apdu.lc = count; + apdu.datalen = count; + apdu.data = buf; + } else { + r = encode_do_data(card->ctx, idx, buf, count, &encoded_data, &encoded_data_len); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not encode data objects"); - sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDC, rec_nr, 0); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDD, rec_nr, 0); + apdu.lc = encoded_data_len; + apdu.datalen = encoded_data_len; + apdu.data = encoded_data; + } apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3; if (flags & SC_RECORD_BY_REC_NR) apdu.p2 |= 0x04; - apdu.lc = count; - apdu.datalen = count; - apdu.data = buf; - - fixup_transceive_length(card, &apdu); + iso7816_fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_GOTO_ERR(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - LOG_TEST_RET(card->ctx, r, "Card returned error"); + LOG_TEST_GOTO_ERR(card->ctx, r, "Card returned error"); + r = (int)count; - LOG_FUNC_RETURN(card->ctx, count); +err: + free(encoded_data); + LOG_FUNC_RETURN(card->ctx, r); } - static int iso7816_write_binary(struct sc_card *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) @@ -292,13 +388,13 @@ iso7816_write_binary(struct sc_card *card, apdu.datalen = count; apdu.data = buf; - fixup_transceive_length(card, &apdu); + iso7816_fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); - LOG_FUNC_RETURN(card->ctx, count); + LOG_FUNC_RETURN(card->ctx, (int)count); } @@ -319,13 +415,13 @@ iso7816_update_binary(struct sc_card *card, apdu.datalen = count; apdu.data = buf; - fixup_transceive_length(card, &apdu); + iso7816_fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); - LOG_FUNC_RETURN(card->ctx, count); + LOG_FUNC_RETURN(card->ctx, (int)count); } @@ -334,115 +430,198 @@ iso7816_process_fci(struct sc_card *card, struct sc_file *file, const unsigned char *buf, size_t buflen) { struct sc_context *ctx = card->ctx; - size_t taglen, len = buflen; - int i; - const unsigned char *tag = NULL, *p = buf; + const unsigned char *p, *end; + unsigned int cla = 0, tag = 0; + size_t length; - sc_log(ctx, "processing FCI bytes"); - tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen); - if (tag != NULL && taglen == 2) { - file->id = (tag[0] << 8) | tag[1]; - sc_log(ctx, " file identifier: 0x%02X%02X", tag[0], tag[1]); - } + file->status = SC_FILE_STATUS_UNKNOWN; - /* determine the file size */ - /* try the tag 0x80 then the tag 0x81 */ - file->size = 0; - for (i = 0x80; i <= 0x81; i++) { - int size = 0; - len = buflen; - tag = sc_asn1_find_tag(ctx, p, len, i, &taglen); - if (tag == NULL) - continue; - if (taglen == 0) - continue; - if (sc_asn1_decode_integer(tag, taglen, &size) < 0) - continue; - if (size <0) - continue; - - file->size = size; - sc_log(ctx, " bytes in file: %"SC_FORMAT_LEN_SIZE_T"u", - file->size); - break; - } + for (p = buf, length = buflen, end = buf + buflen; + p < end; + p += length, length = end - p) { - tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); - if (tag != NULL) { - if (taglen > 0) { - unsigned char byte = tag[0]; - const char *type; - - file->shareable = byte & 0x40 ? 1 : 0; - sc_log(ctx, " shareable: %s", (byte & 0x40) ? "yes" : "no"); - file->ef_structure = byte & 0x07; - switch ((byte >> 3) & 7) { - case 0: - type = "working EF"; - file->type = SC_FILE_TYPE_WORKING_EF; + if (SC_SUCCESS != sc_asn1_read_tag(&p, length, &cla, &tag, &length) + || p == NULL) { + break; + } + switch (cla | tag) { + case 0x81: + if (file->size != 0) { + /* don't overwrite existing file size excluding structural information */ + break; + } + /* fall through */ + case 0x80: + /* determine the file size */ + file->size = 0; + if (p && length <= sizeof(size_t)) { + size_t size = 0, i; + for (i = 0; i < length; i++) { + size <<= 8; + size |= p[i]; + } + if (size > MAX_FILE_SIZE) { + file->size = MAX_FILE_SIZE; + sc_log(ctx, " file size truncated, encoded length: %"SC_FORMAT_LEN_SIZE_T"u", size); + } else { + file->size = size; + } + } + + sc_log(ctx, " bytes in file: %"SC_FORMAT_LEN_SIZE_T"u", file->size); break; - case 1: - type = "internal EF"; - file->type = SC_FILE_TYPE_INTERNAL_EF; + + case 0x82: + if (length > 0) { + unsigned char byte = p[0]; + const char *type; + + file->shareable = byte & 0x40 ? 1 : 0; + sc_log(ctx, " shareable: %s", (byte & 0x40) ? "yes" : "no"); + file->ef_structure = byte & 0x07; + switch ((byte >> 3) & 7) { + case 0: + type = "working EF"; + file->type = SC_FILE_TYPE_WORKING_EF; + break; + case 1: + type = "internal EF"; + file->type = SC_FILE_TYPE_INTERNAL_EF; + break; + case 7: + type = "DF"; + file->type = SC_FILE_TYPE_DF; + break; + default: + file->type = SC_FILE_TYPE_UNKNOWN; + type = "unknown"; + break; + } + sc_log(ctx, " type: %s", type); + sc_log(ctx, " EF structure: %d", byte & 0x07); + sc_log(ctx, " tag 0x82: 0x%02x", byte); + + /* if possible, get additional information for non-DFs */ + if (file->type != SC_FILE_TYPE_DF) { + /* max. record length for fixed- & variable-sized records */ + if (length > 2 && byte & 0x06) { + file->record_length = (length > 3) + ? bebytes2ushort(p+2) + : p[2]; + sc_log(ctx, " record length: %"SC_FORMAT_LEN_SIZE_T"u", + file->record_length); + } + + /* number of records */ + if (length > 4) { + file->record_count = (length > 5) + ? bebytes2ushort(p+4) + : p[4]; + sc_log(ctx, " records: %"SC_FORMAT_LEN_SIZE_T"u", + file->record_count); + } + } + + if (SC_SUCCESS != sc_file_set_type_attr(file, p, length)) + sc_log(ctx, "Warning: Could not set file attributes"); + } break; - case 7: - type = "DF"; - file->type = SC_FILE_TYPE_DF; + + case 0x83: + if (length == 2) { + file->id = (p[0] << 8) | p[1]; + sc_log(ctx, " file identifier: 0x%02X%02X", p[0], p[1]); + } break; - default: - type = "unknown"; + + case 0x84: + if (length > 0 && length <= 16) { + memcpy(file->name, p, length); + file->namelen = length; + + sc_log_hex(ctx, " File name:", file->name, file->namelen); + if (!file->type) + file->type = SC_FILE_TYPE_DF; + } + break; + + case 0x85: + case 0xA5: + if (SC_SUCCESS != sc_file_set_prop_attr(file, p, length)) { + sc_log(ctx, "Warning: Could not set proprietary file properties"); + } break; - } - sc_log(ctx, " type: %s", type); - sc_log(ctx, " EF structure: %d", byte & 0x07); - sc_log(ctx, " tag 0x82: 0x%02x", byte); - if (SC_SUCCESS != sc_file_set_type_attr(file, &byte, 1)) - sc_log(ctx, "Warning: Could not set file attributes"); - } - } - tag = sc_asn1_find_tag(ctx, p, len, 0x84, &taglen); - if (tag != NULL && taglen > 0 && taglen <= 16) { - char tbuf[128]; + case 0x86: + if (SC_SUCCESS != sc_file_set_sec_attr(file, p, length)) { + sc_log(ctx, "Warning: Could not set file security properties"); + } + break; - memcpy(file->name, tag, taglen); - file->namelen = taglen; + case 0x88: + if (length == 1) { + file->sid = *p; + sc_log(ctx, " short file identifier: 0x%02X", *p); + } + break; - sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, file->name, file->namelen, tbuf, sizeof(tbuf)); - sc_log(ctx, " File name: %s", tbuf); - if (!file->type) - file->type = SC_FILE_TYPE_DF; - } + case 0x8A: + if (length == 1) { + switch (p[0]) { + case 0: + file->status =SC_FILE_STATUS_NO_INFO; + break; + case 1: + file->status = SC_FILE_STATUS_CREATION; + break; + case 3: + file->status = SC_FILE_STATUS_INITIALISATION; + break; + case 4: + case 6: + file->status = SC_FILE_STATUS_INVALIDATED; + break; + case 5: + case 7: + file->status = SC_FILE_STATUS_ACTIVATED; + break; + case 12: + case 13: + case 14: + case 15: + file->status = SC_FILE_STATUS_TERMINATION; + break; + case 2: + file->status = SC_FILE_STATUS_RFU_2; + break; + case 8: + file->status = SC_FILE_STATUS_RFU_8; + break; + case 9: + file->status = SC_FILE_STATUS_RFU_9; + break; + case 10: + file->status = SC_FILE_STATUS_RFU_10; + break; + case 11: + file->status = SC_FILE_STATUS_RFU_11; + break; + default: + file->status = SC_FILE_STATUS_PROPRIETARY; + } + } + break; - tag = sc_asn1_find_tag(ctx, p, len, 0x85, &taglen); - if (tag != NULL && taglen) - sc_file_set_prop_attr(file, tag, taglen); - else - file->prop_attr_len = 0; - - tag = sc_asn1_find_tag(ctx, p, len, 0xA5, &taglen); - if (tag != NULL && taglen) - sc_file_set_prop_attr(file, tag, taglen); - - tag = sc_asn1_find_tag(ctx, p, len, 0x86, &taglen); - if (tag != NULL && taglen) - sc_file_set_sec_attr(file, tag, taglen); - - tag = sc_asn1_find_tag(ctx, p, len, 0x88, &taglen); - if (tag != NULL && taglen == 1) - file->sid = *tag; - - tag = sc_asn1_find_tag(ctx, p, len, 0x8A, &taglen); - if (tag != NULL && taglen==1) { - if (tag[0] == 0x01) - file->status = SC_FILE_STATUS_CREATION; - else if (tag[0] == 0x07 || tag[0] == 0x05) - file->status = SC_FILE_STATUS_ACTIVATED; - else if (tag[0] == 0x06 || tag[0] == 0x04) - file->status = SC_FILE_STATUS_INVALIDATED; + case 0x62: + case 0x64: + case 0x6F: + /* allow nested FCP/FMD/FCI templates */ + iso7816_process_fci(card, file, p, length); + } } file->magic = SC_FILE_MAGIC; + return SC_SUCCESS; } @@ -454,7 +633,8 @@ iso7816_select_file(struct sc_card *card, const struct sc_path *in_path, struct struct sc_apdu apdu; unsigned char buf[SC_MAX_APDU_BUFFER_SIZE]; unsigned char pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; - int r, pathlen, pathtype; + int r, pathtype; + size_t pathlen; int select_mf = 0; struct sc_file *file = NULL; const u8 *buffer; @@ -608,28 +788,23 @@ iso7816_get_challenge(struct sc_card *card, u8 *rnd, size_t len) { int r; struct sc_apdu apdu; - u8 buf[10]; - if (!rnd && len) - return SC_ERROR_INVALID_ARGUMENTS; + sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x84, 0x00, 0x00); + apdu.le = len; + apdu.resp = rnd; + apdu.resplen = len; - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0x00, 0x00); - apdu.le = 8; - apdu.resp = buf; - apdu.resplen = 8; /* include SW's */ + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - while (len > 0) { - size_t n = len > 8 ? 8 : len; + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "GET CHALLENGE failed"); - r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - if (apdu.resplen != 8) - return sc_check_sw(card, apdu.sw1, apdu.sw2); - memcpy(rnd, apdu.resp, n); - len -= n; - rnd += n; + if (len < apdu.resplen) { + return (int) len; } - return 0; + + return (int) apdu.resplen; } @@ -650,7 +825,8 @@ iso7816_construct_fci(struct sc_card *card, const sc_file_t *file, sc_asn1_put_tag(0x81, buf, 2, p, *outlen - (p - out), &p); if (file->type_attr_len) { - assert(sizeof(buf) >= file->type_attr_len); + if (sizeof(buf) < file->type_attr_len) + return SC_ERROR_INTERNAL; memcpy(buf, file->type_attr, file->type_attr_len); sc_asn1_put_tag(0x82, buf, file->type_attr_len, p, *outlen - (p - out), &p); @@ -676,13 +852,15 @@ iso7816_construct_fci(struct sc_card *card, const sc_file_t *file, sc_asn1_put_tag(0x83, buf, 2, p, *outlen - (p - out), &p); /* 0x84 = DF name */ if (file->prop_attr_len) { - assert(sizeof(buf) >= file->prop_attr_len); + if (sizeof(buf) < file->prop_attr_len) + return SC_ERROR_INTERNAL; memcpy(buf, file->prop_attr, file->prop_attr_len); sc_asn1_put_tag(0x85, buf, file->prop_attr_len, p, *outlen - (p - out), &p); } if (file->sec_attr_len) { - assert(sizeof(buf) >= file->sec_attr_len); + if (sizeof(buf) < file->sec_attr_len) + return SC_ERROR_INTERNAL; memcpy(buf, file->sec_attr, file->sec_attr_len); sc_asn1_put_tag(0x86, buf, file->sec_attr_len, p, *outlen - (p - out), &p); @@ -726,7 +904,7 @@ iso7816_create_file(struct sc_card *card, sc_file_t *file) static int iso7816_get_response(struct sc_card *card, size_t *count, u8 *buf) { - struct sc_apdu apdu; + struct sc_apdu apdu = {0}; int r; size_t rlen; @@ -745,11 +923,12 @@ iso7816_get_response(struct sc_card *card, size_t *count, u8 *buf) r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - if (apdu.resplen == 0) - LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); *count = apdu.resplen; + if (apdu.resplen == 0) { + LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); + } if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) r = 0; /* no more data to read */ else if (apdu.sw1 == 0x61) @@ -773,7 +952,7 @@ iso7816_delete_file(struct sc_card *card, const sc_path_t *path) SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (path->type != SC_PATH_TYPE_FILE_ID || (path->len != 0 && path->len != 2)) { sc_log(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID"); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } if (path->len == 2) { @@ -812,7 +991,11 @@ iso7816_set_security_env(struct sc_card *card, } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0); switch (env->operation) { + case SC_SEC_OPERATION_AUTHENTICATE: + apdu.p2 = 0xA4; + break; case SC_SEC_OPERATION_DECIPHER: + case SC_SEC_OPERATION_DERIVE: apdu.p2 = 0xB8; break; case SC_SEC_OPERATION_SIGN: @@ -828,23 +1011,31 @@ iso7816_set_security_env(struct sc_card *card, *p++ = env->algorithm_ref & 0xFF; } if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) { + if (env->file_ref.len > SC_MAX_PATH_SIZE) + return SC_ERROR_INVALID_ARGUMENTS; + if (sizeof(sbuf) - (p - sbuf) < env->file_ref.len + 2) + return SC_ERROR_OFFSET_TOO_LARGE; + *p++ = 0x81; - *p++ = env->file_ref.len; - assert(sizeof(sbuf) - (p - sbuf) >= env->file_ref.len); + *p++ = (u8) env->file_ref.len; memcpy(p, env->file_ref.value, env->file_ref.len); p += env->file_ref.len; } if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { - if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) + if (sizeof(sbuf) - (p - sbuf) < env->key_ref_len + 2) + return SC_ERROR_OFFSET_TOO_LARGE; + + if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) *p++ = 0x83; else *p++ = 0x84; - *p++ = env->key_ref_len; - assert(sizeof(sbuf) - (p - sbuf) >= env->key_ref_len); + if (env->key_ref_len > SC_MAX_KEYREF_SIZE) + return SC_ERROR_INVALID_ARGUMENTS; + *p++ = env->key_ref_len & 0xFF; memcpy(p, env->key_ref, env->key_ref_len); p += env->key_ref_len; } - r = p - sbuf; + r = (int)(p - sbuf); apdu.lc = r; apdu.datalen = r; apdu.data = sbuf; @@ -865,8 +1056,10 @@ iso7816_set_security_env(struct sc_card *card, goto err; } } - if (se_num <= 0) - return 0; + if (se_num <= 0) { + r = SC_SUCCESS; + goto err; + } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF2, se_num); r = sc_transmit_apdu(card, &apdu); sc_unlock(card); @@ -929,11 +1122,11 @@ iso7816_compute_signature(struct sc_card *card, apdu.lc = datalen; apdu.datalen = datalen; - fixup_transceive_length(card, &apdu); + iso7816_fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) - LOG_FUNC_RETURN(card->ctx, apdu.resplen); + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); @@ -977,20 +1170,20 @@ iso7816_decipher(struct sc_card *card, apdu.lc = crgram_len + 1; apdu.datalen = crgram_len + 1; - fixup_transceive_length(card, &apdu); + iso7816_fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); sc_mem_clear(sbuf, crgram_len + 1); free(sbuf); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) - LOG_FUNC_RETURN(card->ctx, apdu.resplen); + LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); else LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); } -static int +int iso7816_build_pin_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_pin_cmd_data *data, u8 *buf, size_t buf_len) { @@ -1001,6 +1194,7 @@ iso7816_build_pin_apdu(struct sc_card *card, struct sc_apdu *apdu, case SC_AC_CHV: /* fall through */ case SC_AC_SESSION: + case SC_AC_CONTEXT_SPECIFIC: break; default: return SC_ERROR_INVALID_ARGUMENTS; @@ -1016,6 +1210,9 @@ iso7816_build_pin_apdu(struct sc_card *card, struct sc_apdu *apdu, switch (data->cmd) { case SC_PIN_CMD_VERIFY: ins = 0x20; + /* detect overloaded APDU with SC_PIN_CMD_GET_INFO */ + if (data->pin1.len == 0 && !use_pin_pad) + return SC_ERROR_INVALID_PIN_LENGTH; if ((r = sc_build_pin(buf, buf_len, &data->pin1, pad)) < 0) return r; len = r; @@ -1062,6 +1259,10 @@ iso7816_build_pin_apdu(struct sc_card *card, struct sc_apdu *apdu, } else { p1 |= 0x01; } + if (p1 == 0x03) { + /* No data to send or to receive */ + cse = SC_APDU_CASE_1; + } break; case SC_PIN_CMD_GET_INFO: ins = 0x20; @@ -1083,21 +1284,18 @@ iso7816_build_pin_apdu(struct sc_card *card, struct sc_apdu *apdu, static int -iso7816_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +iso7816_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data) { struct sc_apdu local_apdu, *apdu; int r; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; data->pin1.tries_left = -1; - if (tries_left != NULL) { - *tries_left = data->pin1.tries_left; - } /* Many cards do support PIN status queries, but some cards don't and * mistakenly count the command as a failed PIN attempt, so for now we - * whitelist cards with this flag. In future this may be reduced to a - * blacklist, subject to testing more cards. */ + * allow cards with this flag. In future this may be reduced to a + * blocklist, subject to testing more cards. */ if (data->cmd == SC_PIN_CMD_GET_INFO && !(card->caps & SC_CARD_CAP_ISO7816_PIN_INFO)) { sc_log(card->ctx, "Card does not support PIN status queries"); @@ -1162,9 +1360,6 @@ iso7816_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_l if (data->cmd == SC_PIN_CMD_GET_INFO) r = SC_SUCCESS; } - if (tries_left != NULL) { - *tries_left = data->pin1.tries_left; - } return r; } @@ -1187,20 +1382,45 @@ static int iso7816_get_data(struct sc_card *card, unsigned int tag, u8 *buf, si apdu.resp = buf; apdu.resplen = len; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "GET_DATA returned error"); + LOG_TEST_RET(card->ctx, r, "GET_DATA returned error"); if (apdu.resplen > len) r = SC_ERROR_WRONG_LENGTH; else - r = apdu.resplen; + r = (int)apdu.resplen; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } +int +iso7816_select_aid(struct sc_card *card, const u8 *req, + size_t reqlen, u8 *resp, size_t *resplen) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + int rv; + + SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); + + sc_format_apdu(card, &apdu, resp == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, 0xA4, 0x04, resp == NULL ? 0x0C : 0x00); + apdu.lc = reqlen; + apdu.data = req; + apdu.datalen = reqlen; + apdu.resp = resp; + apdu.resplen = resp == NULL ? 0 : *resplen; + apdu.le = resp == NULL ? 0 : 256; + + rv = sc_transmit_apdu(card, &apdu); + if (resplen) + *resplen = apdu.resplen; + LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_FUNC_RETURN(ctx, rv); +} static int iso7816_init(struct sc_card *card) @@ -1253,7 +1473,11 @@ static struct sc_card_operations iso_ops = { NULL, /* put_data */ NULL, /* delete_record */ NULL, /* read_public_key */ - NULL /* card_reader_lock_obtained */ + NULL, /* card_reader_lock_obtained */ + NULL, /* wrap */ + NULL, /* unwrap */ + NULL, /* encrypt_sym */ + NULL /* decrypt_sym */ }; static struct sc_card_driver iso_driver = { @@ -1274,7 +1498,7 @@ int iso7816_read_binary_sfid(sc_card_t *card, unsigned char sfid, u8 **ef, size_t *ef_len) { int r; - size_t read = MAX_SM_APDU_RESP_SIZE; + size_t read; sc_apdu_t apdu; u8 *p; @@ -1284,13 +1508,9 @@ int iso7816_read_binary_sfid(sc_card_t *card, unsigned char sfid, } *ef_len = 0; -#if MAX_SM_APDU_RESP_SIZE > (0xff+1) - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_EXT, - ISO_READ_BINARY, ISO_P1_FLAG_SFID|sfid, 0); -#else - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, + read = card->max_recv_size; + sc_format_apdu(card, &apdu, SC_APDU_CASE_2, ISO_READ_BINARY, ISO_P1_FLAG_SFID|sfid, 0); -#endif p = realloc(*ef, read); if (!p) { r = SC_ERROR_OUT_OF_MEMORY; @@ -1302,18 +1522,22 @@ int iso7816_read_binary_sfid(sc_card_t *card, unsigned char sfid, apdu.le = read; r = sc_transmit_apdu(card, &apdu); - /* emulate the behaviour of sc_read_binary */ - if (r >= 0) - r = apdu.resplen; + if (r < 0) + goto err; + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r < 0 && r != SC_ERROR_FILE_END_REACHED) + goto err; + /* emulate the behaviour of iso7816_read_binary */ + r = (int)apdu.resplen; - while(1) { - if (r >= 0 && ((size_t) r) != read) { - *ef_len += r; - break; - } - if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not read EF."); - goto err; + while (1) { + if (r <= 0) { + if (*ef_len > 0) + break; + else { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not read EF."); + goto err; + } } *ef_len += r; @@ -1324,11 +1548,10 @@ int iso7816_read_binary_sfid(sc_card_t *card, unsigned char sfid, } *ef = p; - r = sc_read_binary(card, *ef_len, - *ef + *ef_len, read, 0); + r = iso7816_read_binary(card, (unsigned)*ef_len, *ef + *ef_len, read, 0); } - r = SC_SUCCESS; + r = (int)*ef_len; err: return r; @@ -1337,6 +1560,60 @@ int iso7816_read_binary_sfid(sc_card_t *card, unsigned char sfid, #define ISO_WRITE_BINARY 0xD0 int iso7816_write_binary_sfid(sc_card_t *card, unsigned char sfid, u8 *ef, size_t ef_len) +{ + int r; + size_t write, wrote = 0; + sc_apdu_t apdu; + + if (!card) { + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } + + write = card->max_send_size; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3, + ISO_WRITE_BINARY, ISO_P1_FLAG_SFID|sfid, 0); + + if (write > ef_len) { + apdu.datalen = ef_len; + apdu.lc = ef_len; + } else { + apdu.datalen = write; + apdu.lc = write; + } + apdu.data = ef; + + + r = sc_transmit_apdu(card, &apdu); + /* emulate the behaviour of sc_write_binary */ + if (r >= 0) + r = (int)apdu.datalen; + + while (1) { + if (r < 0 || ((size_t) r) > ef_len) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not write EF."); + goto err; + } + if (r == 0 || r == SC_ERROR_FILE_END_REACHED) + break; + wrote += r; + apdu.data += r; + if (wrote >= ef_len) + break; + + r = sc_write_binary(card, (unsigned)wrote, ef, write, 0); + } + + r = (int)wrote; + +err: + return r; +} + +#define ISO_UPDATE_BINARY 0xD6 +int iso7816_update_binary_sfid(sc_card_t *card, unsigned char sfid, + u8 *ef, size_t ef_len) { int r; size_t write = MAX_SM_APDU_DATA_SIZE, wrote = 0; @@ -1362,11 +1639,11 @@ int iso7816_write_binary_sfid(sc_card_t *card, unsigned char sfid, -18) / iso_sm_ctx->block_length) * iso_sm_ctx->block_length - 1))) sc_format_apdu(card, &apdu, SC_APDU_CASE_3_EXT, - ISO_WRITE_BINARY, ISO_P1_FLAG_SFID|sfid, 0); + ISO_UPDATE_BINARY, ISO_P1_FLAG_SFID|sfid, 0); else #endif sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, - ISO_WRITE_BINARY, ISO_P1_FLAG_SFID|sfid, 0); + ISO_UPDATE_BINARY, ISO_P1_FLAG_SFID|sfid, 0); if (write > ef_len) { apdu.datalen = ef_len; @@ -1381,23 +1658,41 @@ int iso7816_write_binary_sfid(sc_card_t *card, unsigned char sfid, r = sc_transmit_apdu(card, &apdu); /* emulate the behaviour of sc_write_binary */ if (r >= 0) - r = apdu.datalen; + r = (int)apdu.datalen; while (1) { if (r < 0 || ((size_t) r) > ef_len) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not write EF."); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not update EF."); goto err; } + if (r == 0 || r == SC_ERROR_FILE_END_REACHED) + break; wrote += r; apdu.data += r; if (wrote >= ef_len) break; - r = sc_write_binary(card, wrote, ef, write, 0); + r = sc_update_binary(card, (unsigned)wrote, ef, write, 0); } - r = SC_SUCCESS; + r = (int)wrote; err: return r; } + +int iso7816_logout(sc_card_t *card, unsigned char pin_reference) +{ + int r; + sc_apdu_t apdu; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0xFF, pin_reference); + + r = sc_transmit_apdu(card, &apdu); + if (r < 0) + return r; + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + + return r; +} diff --git a/src/libopensc/itacns.h b/src/libopensc/itacns.h index 742627b45b..8c5aed0edb 100644 --- a/src/libopensc/itacns.h +++ b/src/libopensc/itacns.h @@ -4,11 +4,11 @@ typedef struct { u8 ic_manufacturer_code; u8 mask_manufacturer_code; - u8 os_version_h; - u8 os_version_l; - u8 cns_version; } itacns_drv_data_t; +#define ITACNS_ICMAN_INFINEON 0x05 + +#define ITACNS_MASKMAN_IDEMIA 0x05 #define ITACNS_MASKMAN_SIEMENS 0x08 #define ITACNS_MASKMAN_STINCARD 0x09 diff --git a/src/libopensc/jpki.h b/src/libopensc/jpki.h index 7d9c22c071..011bf0f883 100644 --- a/src/libopensc/jpki.h +++ b/src/libopensc/jpki.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_JPKI_H diff --git a/src/libopensc/log.c b/src/libopensc/log.c index f26eadf5ae..61689126f7 100644 --- a/src/libopensc/log.c +++ b/src/libopensc/log.c @@ -16,10 +16,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -41,30 +41,87 @@ #ifdef HAVE_PTHREAD #include #endif +#ifdef _WIN32 +#include +#endif +#ifdef ENABLE_OPENSSL +#include +#endif /* ENABLE_OPENSSL */ +#include "common/compat_strlcat.h" #include "internal.h" -static void sc_do_log_va(sc_context_t *ctx, int level, const char *file, int line, const char *func, const char *format, va_list args); +static void sc_do_log_va(sc_context_t *ctx, int level, const char *file, int line, const char *func, int color, const char *format, va_list args); +static int sc_color_fprintf_va(int colors, struct sc_context *ctx, FILE * stream, const char *format, va_list args); void sc_do_log(sc_context_t *ctx, int level, const char *file, int line, const char *func, const char *format, ...) { va_list ap; va_start(ap, format); - sc_do_log_va(ctx, level, file, line, func, format, ap); + sc_do_log_va(ctx, level, file, line, func, 0, format, ap); va_end(ap); } +void sc_do_log_color(sc_context_t *ctx, int level, const char *file, int line, const char *func, int color, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + sc_do_log_va(ctx, level, file, line, func, color, format, ap); + va_end(ap); +} + +#ifdef ENABLE_OPENSSL +void sc_do_log_openssl(sc_context_t *ctx, int level, const char *file, int line, const char *func) +{ + BIO *bio = NULL; + int length, rc; + char *buffer = NULL; + + if ((bio = BIO_new(BIO_s_mem())) == NULL) { + sc_do_log(ctx, level, file, line, func, "Cannot log OpenSSL error"); + goto end; + } + ERR_print_errors(bio); + + length = BIO_pending(bio); + if (length <= 0) { + /* no error? */ + goto end; + } + /* trailing null byte */ + buffer = malloc(length + 1); + if (buffer == NULL) { + sc_do_log(ctx, level, file, line, func, "No memory!"); + goto end; + } + rc = BIO_read(bio, buffer, length); + buffer[length] = '\0'; + if (rc <= 0) { + sc_do_log(ctx, level, file, line, func, "Cannot read OpenSSL error"); + goto end; + } + + sc_do_log(ctx, level, file, line, func, "OpenSSL error\n%s", buffer); +end: + free(buffer); + BIO_free(bio); +} +#else +void sc_do_log_openssl(sc_context_t *ctx, int level, const char *file, int line, const char *func) +{ + sc_do_log(ctx, level, file, line, func, "OpenSSL not enabled"); +} +#endif + void sc_do_log_noframe(sc_context_t *ctx, int level, const char *format, va_list args) { - sc_do_log_va(ctx, level, NULL, 0, NULL, format, args); + sc_do_log_va(ctx, level, NULL, 0, NULL, 0, format, args); } -static void sc_do_log_va(sc_context_t *ctx, int level, const char *file, int line, const char *func, const char *format, va_list args) +static void sc_do_log_va(sc_context_t *ctx, int level, const char *file, int line, const char *func, int color, const char *format, va_list args) { - char buf[4096], *p; - int r; - size_t left; #ifdef _WIN32 SYSTEMTIME st; #else @@ -72,69 +129,76 @@ static void sc_do_log_va(sc_context_t *ctx, int level, const char *file, int lin struct timeval tv; char time_string[40]; #endif - FILE *outf = NULL; - int n; if (!ctx || ctx->debug < level) return; - p = buf; - left = sizeof(buf); +#ifdef _WIN32 + /* In Windows, file handles can not be shared between DLL-s, each DLL has a + * separate file handle table. Make sure we always have a valid file + * descriptor. */ + if (sc_ctx_log_to_file(ctx, ctx->debug_filename) < 0) + return; +#endif + if (ctx->debug_file == NULL) + return; #ifdef _WIN32 GetLocalTime(&st); - r = snprintf(p, left, - "%i-%02i-%02i %02i:%02i:%02i.%03i ", + sc_color_fprintf(SC_COLOR_FG_GREEN|SC_COLOR_BOLD, + ctx, ctx->debug_file, + "P:%lu; T:%lu", + (unsigned long)GetCurrentProcessId(), + (unsigned long)GetCurrentThreadId()); + sc_color_fprintf(SC_COLOR_FG_GREEN, + ctx, ctx->debug_file, + " %i-%02i-%02i %02i:%02i:%02i.%03i", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); #else + sc_color_fprintf(SC_COLOR_FG_GREEN|SC_COLOR_BOLD, + ctx, ctx->debug_file, + "P:%lu; T:0x%lu", + (unsigned long)getpid(), + (unsigned long)pthread_self()); gettimeofday (&tv, NULL); tm = localtime (&tv.tv_sec); strftime (time_string, sizeof(time_string), "%H:%M:%S", tm); - r = snprintf(p, left, "0x%lx %s.%03ld ", (unsigned long)pthread_self(), time_string, (long)tv.tv_usec / 1000); + sc_color_fprintf(SC_COLOR_FG_GREEN, + ctx, ctx->debug_file, + " %s.%03ld", + time_string, + (long)tv.tv_usec / 1000); #endif - p += r; - left -= r; - if (file != NULL) { - r = snprintf(p, left, "[%s] %s:%d:%s: ", - ctx->app_name, file, line, func ? func : ""); - if (r < 0 || (unsigned int)r > sizeof(buf)) - return; - } - else { - r = 0; - } - p += r; - left -= r; - - r = vsnprintf(p, left, format, args); - if (r < 0) - return; + sc_color_fprintf(SC_COLOR_FG_YELLOW, + ctx, ctx->debug_file, + " ["); + sc_color_fprintf(SC_COLOR_FG_YELLOW|SC_COLOR_BOLD, + ctx, ctx->debug_file, + "%s", + ctx->app_name); + sc_color_fprintf(SC_COLOR_FG_YELLOW, + ctx, ctx->debug_file, + "] "); - if (ctx->reopen_log_file) { - r = sc_ctx_log_to_file(ctx, ctx->debug_filename); - if (r < 0) - return; + if (file != NULL) { + sc_color_fprintf(SC_COLOR_FG_YELLOW, + ctx, ctx->debug_file, + "%s:%d:%s: ", + file, line, func ? func : ""); } - outf = ctx->debug_file; - if (outf == NULL) - return; - - fprintf(outf, "%s", buf); - n = strlen(buf); - if (n == 0 || buf[n-1] != '\n') - fprintf(outf, "\n"); - fflush(outf); - - if (ctx->reopen_log_file) { - if (ctx->debug_file && (ctx->debug_file != stderr && ctx->debug_file != stdout)) - fclose(ctx->debug_file); - ctx->debug_file = NULL; - } + sc_color_fprintf_va(color, ctx, ctx->debug_file, format, args); + if (strlen(format) == 0 || format[strlen(format) - 1] != '\n') + sc_color_fprintf(color, ctx, ctx->debug_file, "\n"); + fflush(ctx->debug_file); - return; +#ifdef _WIN32 + if (ctx->debug_file && (ctx->debug_file != stderr && ctx->debug_file != stdout)) + fclose(ctx->debug_file); + ctx->debug_file = NULL; +#endif } void _sc_debug(struct sc_context *ctx, int level, const char *format, ...) @@ -142,7 +206,7 @@ void _sc_debug(struct sc_context *ctx, int level, const char *format, ...) va_list ap; va_start(ap, format); - sc_do_log_va(ctx, level, NULL, 0, NULL, format, ap); + sc_do_log_va(ctx, level, NULL, 0, NULL, 0, format, ap); va_end(ap); } @@ -151,8 +215,127 @@ void _sc_log(struct sc_context *ctx, const char *format, ...) va_list ap; va_start(ap, format); - sc_do_log_va(ctx, SC_LOG_DEBUG_NORMAL, NULL, 0, NULL, format, ap); + sc_do_log_va(ctx, SC_LOG_DEBUG_NORMAL, NULL, 0, NULL, 0, format, ap); + va_end(ap); +} + +void _sc_log_openssl(struct sc_context *ctx) +{ + sc_do_log_openssl(ctx, SC_LOG_DEBUG_DEPS, NULL, 0, NULL); +} + +static int is_a_tty(FILE *fp) +{ + if (fp != NULL) { + int fd = fileno(fp); + if (fd >= 0) { +#ifdef _WIN32 + HANDLE h = (HANDLE)_get_osfhandle(fd); + if (h != INVALID_HANDLE_VALUE) { + return GetFileType(h) == FILE_TYPE_CHAR; + } +#else + return isatty(fd); +#endif + } + } + return 0; +} + +#ifdef _WIN32 +#define set_color(sc_color, win_color, vt100_color) \ + do { if (colors & sc_color) { attr |= win_color; } } while (0) +#else +#define set_color(sc_color, win_color, vt100_color) \ + do { if (colors & sc_color) { fprintf(stream, vt100_color); } } while (0) +#endif + +int sc_color_fprintf(int colors, struct sc_context *ctx, FILE * stream, const char * format, ...) +{ + int r; + va_list ap; + + va_start(ap, format); + r = sc_color_fprintf_va(colors, ctx, stream, format, ap); va_end(ap); + + return r; +} + +int sc_color_fprintf_va(int colors, struct sc_context *ctx, FILE * stream, const char *format, va_list args) +{ + int r; +#ifdef _WIN32 + WORD old_attr = 0; + int fd = stream ? fileno(stream) : -1; + HANDLE handle = fd >= 0 ? (HANDLE) _get_osfhandle(fd) : INVALID_HANDLE_VALUE; +#endif + + if (!is_a_tty(stream)) + colors = 0; + + if (colors && (!ctx || (!(ctx->flags & SC_CTX_FLAG_DISABLE_COLORS)))) { +#ifdef _WIN32 + WORD attr = 0; + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(handle, &csbi); + old_attr = csbi.wAttributes; +#endif + set_color(SC_COLOR_FG_RED, + FOREGROUND_RED, + "\x1b[31m"); + set_color(SC_COLOR_FG_GREEN, + FOREGROUND_GREEN, + "\x1b[32m"); + set_color(SC_COLOR_FG_YELLOW, + FOREGROUND_GREEN|FOREGROUND_RED, + "\x1b[33m"); + set_color(SC_COLOR_FG_BLUE, + FOREGROUND_BLUE, + "\x1b[34m"); + set_color(SC_COLOR_FG_MAGENTA, + FOREGROUND_BLUE|FOREGROUND_RED, + "\x1b[35m"); + set_color(SC_COLOR_FG_CYAN, + FOREGROUND_BLUE|FOREGROUND_GREEN, + "\x1b[36m"); + set_color(SC_COLOR_BG_RED, + FOREGROUND_RED, + "\x1b[41m"); + set_color(SC_COLOR_BG_GREEN, + BACKGROUND_GREEN, + "\x1b[42m"); + set_color(SC_COLOR_BG_YELLOW, + BACKGROUND_GREEN|BACKGROUND_RED, + "\x1b[43m"); + set_color(SC_COLOR_BG_BLUE, + BACKGROUND_BLUE, + "\x1b[44m"); + set_color(SC_COLOR_BG_MAGENTA, + BACKGROUND_BLUE|BACKGROUND_RED, + "\x1b[45m"); + set_color(SC_COLOR_BG_CYAN, + BACKGROUND_BLUE|BACKGROUND_GREEN, + "\x1b[46m"); + set_color(SC_COLOR_BOLD, + FOREGROUND_INTENSITY, + "\x1b[1m"); +#ifdef _WIN32 + SetConsoleTextAttribute(handle, attr); +#endif + } + + r = vfprintf(stream, format, args); + + if (colors && (!ctx || (!(ctx->flags & SC_CTX_FLAG_DISABLE_COLORS)))) { +#ifdef _WIN32 + SetConsoleTextAttribute(handle, old_attr); +#else + fprintf(stream, "\x1b[0m"); +#endif + } + + return r; } void _sc_debug_hex(sc_context_t *ctx, int type, const char *file, int line, @@ -163,29 +346,26 @@ void _sc_debug_hex(sc_context_t *ctx, int type, const char *file, int line, if (buf == NULL) return; - sc_hex_dump(ctx, type, data, len, buf, blen); + sc_hex_dump(data, len, buf, blen); if (label) sc_do_log(ctx, type, file, line, func, - "\n%s (%u byte%s):\n%s", - label, (unsigned int) len, len==1?"":"s", buf); + "\n%s (%"SC_FORMAT_LEN_SIZE_T"u byte%s):\n%s", + label, len, len==1?"":"s", buf); else sc_do_log(ctx, type, file, line, func, - "%u byte%s:\n%s", - (unsigned int) len, len==1?"":"s", buf); + "%"SC_FORMAT_LEN_SIZE_T"u byte%s:\n%s", + len, len==1?"":"s", buf); free(buf); } -/* Although not used, we need this for consistent exports */ -void sc_hex_dump(struct sc_context *ctx, int level, const u8 * in, size_t count, char *buf, size_t len) +void sc_hex_dump(const u8 * in, size_t count, char *buf, size_t len) { char *p = buf; + size_t p_len = len; int lines = 0; - if (!ctx || ctx->debug < level) - return; - if (buf == NULL || (in == NULL && count != 0)) { return; } @@ -203,23 +383,24 @@ void sc_hex_dump(struct sc_context *ctx, int level, const u8 * in, size_t count, else ascbuf[i] = '.'; p += 3; + p_len -= 3; in++; } count -= i; ascbuf[i] = 0; for (; i < 16 && lines; i++) { - strcat(p, " "); + strlcat(p, " ", p_len); p += 3; + p_len -= 3; } - strcat(p, ascbuf); - p += strlen(p); - sprintf(p, "\n"); - p++; + snprintf(p, p_len, "%s\n", ascbuf); + p += strlen(ascbuf) + 1; + p_len -= strlen(ascbuf) - 1; lines++; } } -char * +const char * sc_dump_hex(const u8 * in, size_t count) { static char dump_buf[0x1000]; @@ -252,7 +433,7 @@ sc_dump_hex(const u8 * in, size_t count) return dump_buf; } -char * +const char * sc_dump_oid(const struct sc_object_id *oid) { static char dump_buf[SC_MAX_OBJECT_ID_OCTETS * 20]; diff --git a/src/libopensc/log.h b/src/libopensc/log.h index d0c6c2809b..49f53213b5 100644 --- a/src/libopensc/log.h +++ b/src/libopensc/log.h @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_LOG_H @@ -34,22 +34,46 @@ enum { SC_LOG_DEBUG_VERBOSE, /* helps users */ SC_LOG_DEBUG_NORMAL, /* helps developers */ SC_LOG_DEBUG_RFU1, /* RFU */ - SC_LOG_DEBUG_RFU2, /* RFU */ - SC_LOG_DEBUG_ASN1, /* asn1.c only */ - SC_LOG_DEBUG_MATCH, /* card matching only */ + SC_LOG_DEBUG_SM, /* secure messaging */ + SC_LOG_DEBUG_ASN1, /* asn1.c */ + SC_LOG_DEBUG_MATCH, /* card matching */ + SC_LOG_DEBUG_DEPS, /* debugging of dependencies, e.g. OpenSSL */ + SC_LOG_DEBUG_PIN, /* PIN commands */ }; +#define SC_COLOR_FG_RED 0x0001 +#define SC_COLOR_FG_GREEN 0x0002 +#define SC_COLOR_FG_YELLOW 0x0004 +#define SC_COLOR_FG_BLUE 0x0008 +#define SC_COLOR_FG_MAGENTA 0x0010 +#define SC_COLOR_FG_CYAN 0x0020 +#define SC_COLOR_BG_RED 0x0100 +#define SC_COLOR_BG_GREEN 0x0200 +#define SC_COLOR_BG_YELLOW 0x0400 +#define SC_COLOR_BG_BLUE 0x0800 +#define SC_COLOR_BG_MAGENTA 0x1000 +#define SC_COLOR_BG_CYAN 0x2000 +#define SC_COLOR_BOLD 0x8080 + /* You can't do #ifndef __FUNCTION__ */ #if !defined(__GNUC__) && !defined(__IBMC__) && !(defined(_MSC_VER) && (_MSC_VER >= 1300)) #define __FUNCTION__ NULL #endif +#ifdef __FILE_NAME__ +#define FILENAME __FILE_NAME__ +#else +#define FILENAME __FILE__ +#endif + #if defined(__GNUC__) -#define sc_debug(ctx, level, format, args...) sc_do_log(ctx, level, __FILE__, __LINE__, __FUNCTION__, format , ## args) -#define sc_log(ctx, format, args...) sc_do_log(ctx, SC_LOG_DEBUG_NORMAL, __FILE__, __LINE__, __FUNCTION__, format , ## args) +#define sc_debug(ctx, level, format, args...) sc_do_log(ctx, level, FILENAME, __LINE__, __FUNCTION__, format , ## args) +#define sc_log(ctx, format, args...) sc_do_log(ctx, SC_LOG_DEBUG_NORMAL, FILENAME, __LINE__, __FUNCTION__, format , ## args) +#define sc_log_openssl(ctx) sc_do_log_openssl(ctx, SC_LOG_DEBUG_DEPS, FILENAME, __LINE__, __FUNCTION__) #else #define sc_debug _sc_debug #define sc_log _sc_log +#define sc_log_openssl _sc_log_openssl #endif #if defined(__GNUC__) @@ -63,19 +87,33 @@ enum { void sc_do_log(struct sc_context *ctx, int level, const char *file, int line, const char *func, const char *format, ...) __attribute__ ((format (SC_PRINTF_FORMAT, 6, 7))); +void sc_do_log_color(struct sc_context *ctx, int level, const char *file, int line, + const char *func, int color, const char *format, ...) + __attribute__ ((format (SC_PRINTF_FORMAT, 7, 8))); void sc_do_log_noframe(sc_context_t *ctx, int level, const char *format, va_list args) __attribute__ ((format (SC_PRINTF_FORMAT, 3, 0))); void _sc_debug(struct sc_context *ctx, int level, const char *format, ...) __attribute__ ((format (SC_PRINTF_FORMAT, 3, 4))); void _sc_log(struct sc_context *ctx, const char *format, ...) __attribute__ ((format (SC_PRINTF_FORMAT, 2, 3))); +void _sc_log_openssl(struct sc_context *ctx); +int sc_color_fprintf(int colors, struct sc_context *ctx, FILE * stream, const char * format, ...) + __attribute__ ((format (SC_PRINTF_FORMAT, 4, 5))); #else void sc_do_log(struct sc_context *ctx, int level, const char *file, int line, const char *func, const char *format, ...); +void sc_do_log_color(struct sc_context *ctx, int level, const char *file, int line, const char *func, int color, + const char *format, ...); void sc_do_log_noframe(sc_context_t *ctx, int level, const char *format, va_list args); void _sc_debug(struct sc_context *ctx, int level, const char *format, ...); void _sc_log(struct sc_context *ctx, const char *format, ...); +void _sc_log_openssl(struct sc_context *ctx); +int sc_color_fprintf(int colors, struct sc_context *ctx, FILE * stream, const char * format, ...); #endif + +void sc_do_log_openssl(struct sc_context *ctx, int level, const char *file, int line, + const char *func); + /** * @brief Log binary data to a sc context * @@ -86,12 +124,14 @@ void _sc_log(struct sc_context *ctx, const char *format, ...); * @param[in] len Length of \a data */ #define sc_debug_hex(ctx, level, label, data, len) \ - _sc_debug_hex(ctx, level, __FILE__, __LINE__, __FUNCTION__, label, data, len) + _sc_debug_hex(ctx, level, FILENAME, __LINE__, __FUNCTION__, label, data, len) +#define sc_log_hex(ctx, label, data, len) \ + sc_debug_hex(ctx, SC_LOG_DEBUG_NORMAL, label, data, len) /** * @brief Log binary data * * @param[in] ctx Context for logging - * @param[in] type Debug level + * @param[in] level Debug level * @param[in] file File name to be prepended * @param[in] line Line to be prepended * @param[in] func Function to be prepended @@ -102,21 +142,21 @@ void _sc_log(struct sc_context *ctx, const char *format, ...); void _sc_debug_hex(struct sc_context *ctx, int level, const char *file, int line, const char *func, const char *label, const u8 *data, size_t len); -void sc_hex_dump(struct sc_context *ctx, int level, const u8 * buf, size_t len, char *out, size_t outlen); -char * sc_dump_hex(const u8 * in, size_t count); -char * sc_dump_oid(const struct sc_object_id *oid); +void sc_hex_dump(const u8 *buf, size_t len, char *out, size_t outlen); +const char * sc_dump_hex(const u8 * in, size_t count); +const char * sc_dump_oid(const struct sc_object_id *oid); #define SC_FUNC_CALLED(ctx, level) do { \ - sc_do_log(ctx, level, __FILE__, __LINE__, __FUNCTION__, "called\n"); \ + sc_do_log(ctx, level, FILENAME, __LINE__, __FUNCTION__, "called\n"); \ } while (0) #define LOG_FUNC_CALLED(ctx) SC_FUNC_CALLED((ctx), SC_LOG_DEBUG_NORMAL) #define SC_FUNC_RETURN(ctx, level, r) do { \ int _ret = r; \ if (_ret <= 0) { \ - sc_do_log(ctx, level, __FILE__, __LINE__, __FUNCTION__, \ + sc_do_log_color(ctx, level, FILENAME, __LINE__, __FUNCTION__, _ret ? SC_COLOR_FG_RED : 0, \ "returning with: %d (%s)\n", _ret, sc_strerror(_ret)); \ } else { \ - sc_do_log(ctx, level, __FILE__, __LINE__, __FUNCTION__, \ + sc_do_log(ctx, level, FILENAME, __LINE__, __FUNCTION__, \ "returning with: %d\n", _ret); \ } \ return _ret; \ @@ -126,7 +166,7 @@ char * sc_dump_oid(const struct sc_object_id *oid); #define SC_TEST_RET(ctx, level, r, text) do { \ int _ret = (r); \ if (_ret < 0) { \ - sc_do_log(ctx, level, __FILE__, __LINE__, __FUNCTION__, \ + sc_do_log_color(ctx, level, FILENAME, __LINE__, __FUNCTION__, SC_COLOR_FG_RED, \ "%s: %d (%s)\n", (text), _ret, sc_strerror(_ret)); \ return _ret; \ } \ @@ -136,7 +176,7 @@ char * sc_dump_oid(const struct sc_object_id *oid); #define SC_TEST_GOTO_ERR(ctx, level, r, text) do { \ int _ret = (r); \ if (_ret < 0) { \ - sc_do_log(ctx, level, __FILE__, __LINE__, __FUNCTION__, \ + sc_do_log_color(ctx, level, FILENAME, __LINE__, __FUNCTION__, SC_COLOR_FG_RED, \ "%s: %d (%s)\n", (text), _ret, sc_strerror(_ret)); \ goto err; \ } \ diff --git a/src/libopensc/muscle-filesystem.c b/src/libopensc/muscle-filesystem.c index 602c40dda7..99a5fe162f 100644 --- a/src/libopensc/muscle-filesystem.c +++ b/src/libopensc/muscle-filesystem.c @@ -1,5 +1,5 @@ /* - * muscle-filesystem.c: Support for MuscleCard Applet from musclecard.com + * muscle-filesystem.c: Support for MuscleCard Applet from musclecard.com * * Copyright (C) 2006, Identity Alliance, Thomas Harning * @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -53,6 +53,7 @@ mscfs_t *mscfs_new(void) { void mscfs_free(mscfs_t *fs) { mscfs_clear_cache(fs); + free(fs); } void mscfs_clear_cache(mscfs_t* fs) { @@ -77,9 +78,12 @@ static int mscfs_is_ignored(mscfs_t* fs, msc_id objectId) return ignored; } +#define MAX_FILES 10000 int mscfs_push_file(mscfs_t* fs, mscfs_file_t *file) { mscfs_cache_t *cache = &fs->cache; + if (cache->size >= MAX_FILES) + return SC_ERROR_INTERNAL; if(!cache->array || cache->size == cache->totalSize) { int length = cache->totalSize + MSCFS_CACHE_INCREMENT; mscfs_file_t *oldArray; @@ -95,7 +99,7 @@ int mscfs_push_file(mscfs_t* fs, mscfs_file_t *file) } cache->array[cache->size] = *file; cache->size++; - return 0; + return SC_SUCCESS; } int mscfs_update_cache(mscfs_t* fs) { @@ -120,8 +124,10 @@ int mscfs_update_cache(mscfs_t* fs) { } else { file.ef = 1; /* File is a working elementary file */ } - - mscfs_push_file(fs, &file); + + r = mscfs_push_file(fs, &file); + if (r != SC_SUCCESS) + return r; } r = fs->listFile(&file, 0, fs->udata); if(r == 0) @@ -132,22 +138,24 @@ int mscfs_update_cache(mscfs_t* fs) { return fs->cache.size; } -void mscfs_check_cache(mscfs_t* fs) +int mscfs_check_cache(mscfs_t* fs) { + int r = SC_SUCCESS; if(!fs->cache.array) { - mscfs_update_cache(fs); + r = mscfs_update_cache(fs); } + return r; } -int mscfs_lookup_path(mscfs_t* fs, const u8 *path, int pathlen, msc_id* objectId, int isDirectory) +int mscfs_lookup_path(mscfs_t* fs, const u8 *path, size_t pathlen, msc_id* objectId, int isDirectory) { u8* oid = objectId->id; - if ((pathlen & 1) != 0) /* not divisble by 2 */ + if ((pathlen & 1) != 0) /* not divisible by 2 */ return MSCFS_INVALID_ARGS; if(isDirectory) { /* Directory must be right next to root */ - if((0 == memcmp(path, "\x3F\x00", 2) && pathlen == 4) - || (0 == memcmp(fs->currentPath, "\x3F\x00", 2) && pathlen == 2)) { + if ((pathlen == 4 && 0 == memcmp(path, "\x3F\x00", 2)) || + (pathlen == 2 && 0 == memcmp(fs->currentPath, "\x3F\x00", 2))) { oid[0] = path[pathlen - 2]; oid[1] = path[pathlen - 1]; oid[2] = oid[3] = 0; @@ -168,7 +176,7 @@ int mscfs_lookup_path(mscfs_t* fs, const u8 *path, int pathlen, msc_id* objectId if(pathlen > 4) return MSCFS_INVALID_ARGS; /* Reset to root */ - if(0 == memcmp(path, "\x3F\x00", 2) && pathlen == 2) { + if(pathlen == 2 && 0 == memcmp(path, "\x3F\x00", 2)) { oid[0] = oid[2] = path[0]; oid[1] = oid[3] = path[1]; } else if(pathlen == 2) { /* Path preserved for current-path */ @@ -180,7 +188,7 @@ int mscfs_lookup_path(mscfs_t* fs, const u8 *path, int pathlen, msc_id* objectId oid[2] = path[2]; oid[3] = path[3]; } - + return 0; } @@ -204,42 +212,51 @@ int mscfs_check_selection(mscfs_t *fs, int requiredItem) return 0; } -int mscfs_loadFileInfo(mscfs_t* fs, const u8 *path, int pathlen, mscfs_file_t **file_data, int* idx) +int mscfs_loadFileInfo(mscfs_t* fs, const u8 *path, size_t pathlen, mscfs_file_t **file_data, int* idx) { - msc_id fullPath; - int x; - assert(fs != NULL && path != NULL && file_data != NULL); - mscfs_lookup_path(fs, path, pathlen, &fullPath, 0); - + msc_id fullPath = {{0, 0, 0, 0}}; + int x, rc; + if (fs == NULL || path == NULL || file_data == NULL) + return SC_ERROR_INTERNAL; + rc = mscfs_lookup_path(fs, path, pathlen, &fullPath, 0); + if (rc != SC_SUCCESS) { + return rc; + } + /* Obtain file information while checking if it exists */ - mscfs_check_cache(fs); + rc = mscfs_check_cache(fs); + if (rc < 0) + return rc; if(idx) *idx = -1; for(x = 0; x < fs->cache.size; x++) { - msc_id objectId; *file_data = &fs->cache.array[x]; - objectId = (*file_data)->objectId; - if(0 == memcmp(objectId.id, fullPath.id, 4)) { - if(idx) *idx = x; - break; + if (*file_data) { + msc_id objectId; + objectId = (*file_data)->objectId; + if(0 == memcmp(objectId.id, fullPath.id, 4)) { + if (idx) + *idx = x; + break; + } + *file_data = NULL; } - *file_data = NULL; } - if(*file_data == NULL && (0 == memcmp("\x3F\x00\x00\x00", fullPath.id, 4) || 0 == memcmp("\x3F\x00\x3F\x00", fullPath.id, 4 ))) { + if(*file_data == NULL && (0 == memcmp("\x3F\x00\x00\x00", fullPath.id, 4) || 0 == memcmp("\x3F\x00\x50\x15", fullPath.id, 4 ) || 0 == memcmp("\x3F\x00\x3F\x00", fullPath.id, 4))) { static mscfs_file_t ROOT_FILE; ROOT_FILE.ef = 0; ROOT_FILE.size = 0; /* Faked Root ID */ ROOT_FILE.objectId = rootId; - + ROOT_FILE.read = 0; ROOT_FILE.write = 0x02; /* User Pin access */ ROOT_FILE.delete = 0x02; - + *file_data = &ROOT_FILE; if(idx) *idx = -2; } else if(*file_data == NULL) { return MSCFS_FILE_NOT_FOUND; } - + return 0; } diff --git a/src/libopensc/muscle-filesystem.h b/src/libopensc/muscle-filesystem.h index 3978843b41..b764a75d97 100644 --- a/src/libopensc/muscle-filesystem.h +++ b/src/libopensc/muscle-filesystem.h @@ -1,5 +1,5 @@ /* - * muscle-filesystem.h: Support for MuscleCard Applet from musclecard.com + * muscle-filesystem.h: Support for MuscleCard Applet from musclecard.com * * Copyright (C) 2006, Identity Alliance, Thomas Harning * @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MUSCLE_FILESYSTEM_H @@ -34,6 +34,7 @@ typedef struct mscfs_file { size_t size; unsigned short read, write, delete; int ef; + int deleteFile; } mscfs_file_t; typedef struct mscfs_cache { @@ -57,14 +58,14 @@ void mscfs_clear_cache(mscfs_t* fs); int mscfs_push_file(mscfs_t* fs, mscfs_file_t *file); int mscfs_update_cache(mscfs_t* fs); -void mscfs_check_cache(mscfs_t* fs); +int mscfs_check_cache(mscfs_t* fs); -int mscfs_lookup_path(mscfs_t* fs, const u8 *path, int pathlen, msc_id* objectId, int isDirectory); +int mscfs_lookup_path(mscfs_t* fs, const u8 *path, size_t pathlen, msc_id* objectId, int isDirectory); int mscfs_lookup_local(mscfs_t* fs, const int id, msc_id* objectId); /* -1 any, 0 DF, 1 EF */ int mscfs_check_selection(mscfs_t *fs, int requiredItem); -int mscfs_loadFileInfo(mscfs_t* fs, const u8 *path, int pathlen, mscfs_file_t **file_data, int* index); +int mscfs_loadFileInfo(mscfs_t* fs, const u8 *path, size_t pathlen, mscfs_file_t **file_data, int* index); #endif diff --git a/src/libopensc/muscle.c b/src/libopensc/muscle.c index 78f06fbae1..d2ae6419ec 100644 --- a/src/libopensc/muscle.c +++ b/src/libopensc/muscle.c @@ -1,5 +1,5 @@ /* - * muscle.c: Support for MuscleCard Applet from musclecard.com + * muscle.c: Support for MuscleCard Applet from musclecard.com * * Copyright (C) 2006, Identity Alliance, Thomas Harning * @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -30,8 +30,6 @@ #define MSC_RSA_PUBLIC 0x01 #define MSC_RSA_PRIVATE 0x02 #define MSC_RSA_PRIVATE_CRT 0x03 -#define MSC_DSA_PUBLIC 0x04 -#define MSC_DSA_PRIVATE 0x05 static msc_id inputId = { { 0xFF, 0xFF, 0xFF, 0xFF } }; static msc_id outputId = { { 0xFF, 0xFF, 0xFF, 0xFE } }; @@ -48,7 +46,7 @@ int msc_list_objects(sc_card_t* card, u8 next, mscfs_file_t* file) { r = sc_transmit_apdu(card, &apdu); if (r) return r; - + if(apdu.sw1 == 0x9C && apdu.sw2 == 0x12) { return 0; } @@ -58,7 +56,7 @@ int msc_list_objects(sc_card_t* card, u8 next, mscfs_file_t* file) { if(apdu.resplen == 0) /* No more left */ return 0; if (apdu.resplen != 14) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "expected 14 bytes, got %"SC_FORMAT_LEN_SIZE_T"u.\n", apdu.resplen); return SC_ERROR_UNKNOWN_DATA_RECEIVED; @@ -77,10 +75,10 @@ int msc_partial_read_object(sc_card_t *card, msc_id objectId, int offset, u8 *da u8 buffer[9]; sc_apdu_t apdu; int r; - + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x56, 0x00, 0x00); - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + + sc_log(card->ctx, "READ: Offset: %x\tLength: %"SC_FORMAT_LEN_SIZE_T"u\n", offset, dataLength); memcpy(buffer, objectId.id, 4); @@ -91,50 +89,51 @@ int msc_partial_read_object(sc_card_t *card, msc_id objectId, int offset, u8 *da apdu.lc = 9; apdu.le = dataLength; apdu.resplen = dataLength; - apdu.resp = data; + apdu.resp = data; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) - return dataLength; - if(apdu.sw1 == 0x9C) { - if(apdu.sw2 == 0x07) { + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00 && dataLength <= apdu.resplen) + return (int)dataLength; + if (apdu.sw1 == 0x9C) { + if (apdu.sw2 == 0x07) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_FILE_NOT_FOUND); - } else if(apdu.sw2 == 0x06) { + } else if (apdu.sw2 == 0x06) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_ALLOWED); - } else if(apdu.sw2 == 0x0F) { + } else if (apdu.sw2 == 0x0F) { /* GUESSED */ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); - return dataLength; - + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED); } int msc_read_object(sc_card_t *card, msc_id objectId, int offset, u8 *data, size_t dataLength) { - int r; - size_t i; + int r = 0; + unsigned int i; size_t max_read_unit = MSC_MAX_READ; - for(i = 0; i < dataLength; i += max_read_unit) { + for (i = 0; i < dataLength; i += r) { r = msc_partial_read_object(card, objectId, offset + i, data + i, MIN(dataLength - i, max_read_unit)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Error in partial object read"); + LOG_TEST_RET(card->ctx, r, "Error in partial object read"); + if (r == 0) + break; } - return dataLength; + return (int)dataLength; } int msc_zero_object(sc_card_t *card, msc_id objectId, size_t dataLength) { u8 zeroBuffer[MSC_MAX_APDU]; size_t i; - size_t max_write_unit = MSC_MAX_SEND - 9; /* - 9 for object ID+length */ + size_t max_write_unit = MIN(MSC_MAX_APDU, MSC_MAX_SEND - 9); /* - 9 for object ID+length */ memset(zeroBuffer, 0, max_write_unit); for(i = 0; i < dataLength; i += max_write_unit) { int r = msc_partial_update_object(card, objectId, i, zeroBuffer, MIN(dataLength - i, max_write_unit)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Error in zeroing file update"); + LOG_TEST_RET(card->ctx, r, "Error in zeroing file update"); } return 0; } @@ -149,16 +148,16 @@ int msc_create_object(sc_card_t *card, msc_id objectId, size_t objectSize, unsig apdu.lc = 14; apdu.data = buffer, apdu.datalen = 14; - + memcpy(buffer, objectId.id, 4); ulong2bebytes(buffer + 4, objectSize); ushort2bebytes(buffer + 8, readAcl); ushort2bebytes(buffer + 10, writeAcl); ushort2bebytes(buffer + 12, deleteAcl); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) - return objectSize; + return (int)objectSize; if(apdu.sw1 == 0x9C) { if(apdu.sw2 == 0x01) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_MEMORY_FAILURE); @@ -169,27 +168,30 @@ int msc_create_object(sc_card_t *card, msc_id objectId, size_t objectSize, unsig } } if (card->ctx->debug >= 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", + sc_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } msc_zero_object(card, objectId, objectSize); - return objectSize; + return (int)objectSize; } /* Update up to MSC_MAX_READ - 9 bytes */ -int msc_partial_update_object(sc_card_t *card, msc_id objectId, int offset, const u8 *data, size_t dataLength) +int msc_partial_update_object(sc_card_t *card, msc_id objectId, size_t offset, const u8 *data, size_t dataLength) { u8 buffer[MSC_MAX_APDU]; sc_apdu_t apdu; int r; + if (dataLength + 9 > MSC_MAX_APDU) + return SC_ERROR_INVALID_ARGUMENTS; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x54, 0x00, 0x00); apdu.lc = dataLength + 9; if (card->ctx->debug >= 2) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "WRITE: Offset: %x\tLength: %"SC_FORMAT_LEN_SIZE_T"u\n", + sc_log(card->ctx, + "WRITE: Offset: %zx\tLength: %"SC_FORMAT_LEN_SIZE_T"u\n", offset, dataLength); - + memcpy(buffer, objectId.id, 4); ulong2bebytes(buffer + 4, offset); buffer[8] = (u8)dataLength; @@ -197,9 +199,9 @@ int msc_partial_update_object(sc_card_t *card, msc_id objectId, int offset, cons apdu.data = buffer; apdu.datalen = apdu.lc; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) - return dataLength; + return (int)dataLength; if(apdu.sw1 == 0x9C) { if(apdu.sw2 == 0x07) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_FILE_NOT_FOUND); @@ -211,10 +213,10 @@ int msc_partial_update_object(sc_card_t *card, msc_id objectId, int offset, cons } } if (card->ctx->debug >= 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", + sc_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } - return dataLength; + return (int)dataLength; } int msc_update_object(sc_card_t *card, msc_id objectId, int offset, const u8 *data, size_t dataLength) @@ -224,9 +226,9 @@ int msc_update_object(sc_card_t *card, msc_id objectId, int offset, const u8 *da size_t max_write_unit = MSC_MAX_SEND - 9; for(i = 0; i < dataLength; i += max_write_unit) { r = msc_partial_update_object(card, objectId, offset + i, data + i, MIN(dataLength - i, max_write_unit)); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Error in partial object update"); + LOG_TEST_RET(card->ctx, r, "Error in partial object update"); } - return dataLength; + return (int)dataLength; } int msc_delete_object(sc_card_t *card, msc_id objectId, int zero) @@ -239,7 +241,7 @@ int msc_delete_object(sc_card_t *card, msc_id objectId, int zero) apdu.data = objectId.id; apdu.datalen = 4; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) return 0; if(apdu.sw1 == 0x9C) { @@ -250,7 +252,7 @@ int msc_delete_object(sc_card_t *card, msc_id objectId, int zero) } } if (card->ctx->debug >= 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", + sc_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } return 0; @@ -267,17 +269,17 @@ int msc_select_applet(sc_card_t *card, u8 *appletId, size_t appletIdLength) apdu.datalen = appletIdLength; apdu.resplen = 0; apdu.le = 0; - + r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) return 1; - + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_CARD_CMD_FAILED); } /* Truncate the nulls at the end of a PIN, useful in padding is unnecessarily added */ -static void truncatePinNulls(const u8* pin, int *pinLength) { +static void truncatePinNulls(const u8* pin, size_t *pinLength) { for(; *pinLength > 0; (*pinLength)--) { if(pin[*pinLength - 1]) break; } @@ -290,34 +292,37 @@ int msc_verify_pin(sc_card_t *card, int pinNumber, const u8 *pinValue, int pinLe const int bufferLength = MSC_MAX_PIN_LENGTH; u8 buffer[MSC_MAX_PIN_LENGTH]; - assert(pinLength <= MSC_MAX_PIN_LENGTH); - msc_verify_pin_apdu(card, &apdu, buffer, bufferLength, pinNumber, pinValue, pinLength); + if (pinLength > MSC_MAX_PIN_LENGTH) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + r = msc_verify_pin_apdu(card, &apdu, buffer, bufferLength, pinNumber, pinValue, pinLength); + LOG_TEST_RET(card->ctx, r, "APDU verification failed"); + if(tries) *tries = -1; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { return 0; } else if(apdu.sw1 == 0x63) { /* Invalid auth */ if(tries) *tries = apdu.sw2 & 0x0F; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x9C && apdu.sw2 == 0x02) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x83) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_AUTH_METHOD_BLOCKED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_AUTH_METHOD_BLOCKED); } - + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_PIN_CODE_INCORRECT); } /* USE ISO_VERIFY due to tries return */ -void msc_verify_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pinValue, int pinLength) +int msc_verify_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pinValue, size_t pinLength) { - assert(buffer); - assert(bufferLength >= (size_t)pinLength); - assert(pinLength <= MSC_MAX_PIN_LENGTH); + if (!buffer || bufferLength < (size_t)pinLength || pinLength > MSC_MAX_PIN_LENGTH) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); truncatePinNulls(pinValue, &pinLength); @@ -326,6 +331,7 @@ void msc_verify_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bu apdu->lc = pinLength; apdu->data = buffer; apdu->datalen = pinLength; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } int msc_unblock_pin(sc_card_t *card, int pinNumber, const u8 *pukValue, int pukLength, int *tries) @@ -335,33 +341,35 @@ int msc_unblock_pin(sc_card_t *card, int pinNumber, const u8 *pukValue, int pukL const int bufferLength = MSC_MAX_PIN_LENGTH; u8 buffer[MSC_MAX_PIN_LENGTH]; - assert(pukLength <= MSC_MAX_PIN_LENGTH); + if (pukLength > MSC_MAX_PIN_LENGTH) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + + r = msc_unblock_pin_apdu(card, &apdu, buffer, bufferLength, pinNumber, pukValue, pukLength); + LOG_TEST_RET(card->ctx, r, "APDU unblock failed"); - msc_unblock_pin_apdu(card, &apdu, buffer, bufferLength, pinNumber, pukValue, pukLength); if(tries) *tries = -1; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { return 0; } else if(apdu.sw1 == 0x63) { /* Invalid auth */ if(tries) *tries = apdu.sw2 & 0x0F; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x9C && apdu.sw2 == 0x02) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x83) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_AUTH_METHOD_BLOCKED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_AUTH_METHOD_BLOCKED); } - + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_PIN_CODE_INCORRECT); } -void msc_unblock_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pukValue, int pukLength) +int msc_unblock_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pukValue, size_t pukLength) { - assert(buffer); - assert(bufferLength >= (size_t)pukLength); - assert(pukLength <= MSC_MAX_PIN_LENGTH); + if (!buffer || bufferLength < (size_t)pukLength || pukLength > MSC_MAX_PIN_LENGTH) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); truncatePinNulls(pukValue, &pukLength); @@ -370,6 +378,7 @@ void msc_unblock_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t b apdu->lc = pukLength; apdu->data = buffer; apdu->datalen = pukLength; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } int msc_change_pin(sc_card_t *card, int pinNumber, const u8 *pinValue, int pinLength, const u8 *newPin, int newPinLength, int *tries) @@ -379,34 +388,34 @@ int msc_change_pin(sc_card_t *card, int pinNumber, const u8 *pinValue, int pinLe const int bufferLength = (MSC_MAX_PIN_LENGTH + 1) * 2; u8 buffer[(MSC_MAX_PIN_LENGTH + 1) * 2]; - msc_change_pin_apdu(card, &apdu, buffer, bufferLength, pinNumber, pinValue, pinLength, newPin, newPinLength); + r = msc_change_pin_apdu(card, &apdu, buffer, bufferLength, pinNumber, pinValue, pinLength, newPin, newPinLength); + LOG_TEST_RET(card->ctx, r, "APDU change failed"); if(tries) *tries = -1; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { return 0; } else if(apdu.sw1 == 0x63) { /* Invalid auth */ if(tries) *tries = apdu.sw2 & 0x0F; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x9C && apdu.sw2 == 0x02) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PIN_CODE_INCORRECT); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x83) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_AUTH_METHOD_BLOCKED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_AUTH_METHOD_BLOCKED); } - + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_PIN_CODE_INCORRECT); } /* USE ISO_VERIFY due to tries return */ -void msc_change_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pinValue, int pinLength, const u8 *newPin, int newPinLength) +int msc_change_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pinValue, size_t pinLength, const u8 *newPin, size_t newPinLength) { u8 *ptr; - assert(pinLength <= MSC_MAX_PIN_LENGTH); - assert(newPinLength <= MSC_MAX_PIN_LENGTH); - assert(buffer); - assert(bufferLength >= pinLength + newPinLength + 2UL); + if (pinLength > MSC_MAX_PIN_LENGTH || newPinLength > MSC_MAX_PIN_LENGTH + || !buffer || bufferLength < pinLength + newPinLength + 2UL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); truncatePinNulls(pinValue, &pinLength); truncatePinNulls(newPin, &newPinLength); @@ -424,6 +433,7 @@ void msc_change_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bu apdu->lc = pinLength + newPinLength + 2; apdu->datalen = apdu->lc; apdu->data = buffer; + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } int msc_get_challenge(sc_card_t *card, unsigned short dataLength, unsigned short seedLength, u8 *seedData, u8 *outputData) @@ -432,16 +442,16 @@ int msc_get_challenge(sc_card_t *card, unsigned short dataLength, unsigned short int r, location, cse; size_t len; u8 *buffer, *ptr; - + location = (dataLength < MSC_MAX_READ) ? 1 : 2; /* 1 == APDU, 2 == (seed in 0xFFFFFFFE, out in 0xFFFFFFFF) */ cse = (location == 1) ? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_3_SHORT; len = seedLength + 4; - - assert(seedLength < MSC_MAX_SEND - 4); - assert(dataLength < MSC_MAX_READ - 9); /* Output buffer doesn't seem to operate as desired.... nobody can read/delete */ - + + if (seedLength >= MSC_MAX_SEND - 4 || dataLength >= MSC_MAX_READ - 9)/* Output buffer doesn't seem to operate as desired.... nobody can read/delete */ + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + buffer = malloc(len); - if(!buffer) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + if(!buffer) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); ptr = buffer; ushort2bebytes(ptr, dataLength); ptr+=2; @@ -454,10 +464,13 @@ int msc_get_challenge(sc_card_t *card, unsigned short dataLength, unsigned short apdu.data = buffer; apdu.datalen = len; apdu.lc = len; - + if(location == 1) { u8* outputBuffer = malloc(dataLength + 2); - if(outputBuffer == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + if(outputBuffer == NULL) { + free(buffer); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + }; apdu.le = dataLength + 2; apdu.resp = outputBuffer; apdu.resplen = dataLength + 2; @@ -468,7 +481,7 @@ int msc_get_challenge(sc_card_t *card, unsigned short dataLength, unsigned short free(apdu.resp); } free(buffer); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(location == 1) { if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { return SC_SUCCESS; @@ -476,88 +489,89 @@ int msc_get_challenge(sc_card_t *card, unsigned short dataLength, unsigned short r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", + sc_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CARD_CMD_FAILED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); } } else { if(apdu.sw1 != 0x90 || apdu.sw2 != 0x00) { r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", + sc_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CARD_CMD_FAILED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); } r = msc_read_object(card, inputId, 2, outputData, dataLength); if(r < 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); msc_delete_object(card, inputId,0); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } } -int msc_generate_keypair(sc_card_t *card, int privateKey, int publicKey, int algorithm, int keySize, int options) +int msc_generate_keypair(sc_card_t *card, int privateKey, int publicKey, int algorithm, size_t keySize, int options) { sc_apdu_t apdu; - u8 buffer[16]; /* Keypair payload length */ + u8 buffer[16]; /* Key pair payload length */ u8 *ptr = buffer; int r; unsigned short prRead = 0xFFFF, prWrite = 0x0002, prCompute = 0x0002, puRead = 0x0000, puWrite = 0x0002, puCompute = 0x0000; - assert(privateKey <= 0x0F && publicKey <= 0x0F); - + if (privateKey > 0x0F || publicKey > 0x0F) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x30, privateKey, publicKey); *ptr = algorithm; ptr++; - + ushort2bebytes(ptr, keySize); ptr+=2; - + ushort2bebytes(ptr, prRead); ptr+=2; ushort2bebytes(ptr, prWrite); ptr+=2; ushort2bebytes(ptr, prCompute); ptr+=2; - + ushort2bebytes(ptr, puRead); ptr+=2; ushort2bebytes(ptr, puWrite); ptr+=2; ushort2bebytes(ptr, puCompute); ptr+=2; - + *ptr = 0; /* options; -- no options for now, they need extra data */ - + apdu.data = buffer; apdu.datalen = 16; apdu.lc = 16; - + r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { return 0; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", + sc_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CARD_CMD_FAILED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); } -int msc_extract_key(sc_card_t *card, +int msc_extract_key(sc_card_t *card, int keyLocation) { sc_apdu_t apdu; @@ -569,24 +583,24 @@ int msc_extract_key(sc_card_t *card, apdu.datalen = 1; apdu.lc = 1; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { return 0; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got strange SWs: 0x%02X 0x%02X\n", + sc_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CARD_CMD_FAILED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); } -int msc_extract_rsa_public_key(sc_card_t *card, +int msc_extract_rsa_public_key(sc_card_t *card, int keyLocation, - size_t* modLength, + size_t* modLength, u8** modulus, size_t* expLength, u8** exponent) @@ -596,36 +610,40 @@ int msc_extract_rsa_public_key(sc_card_t *card, int fileLocation = 1; r = msc_extract_key(card, keyLocation); - if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); - + if(r < 0) LOG_FUNC_RETURN(card->ctx, r); + /* Read keyType, keySize, and what should be the modulus size */ r = msc_read_object(card, inputId, fileLocation, buffer, 5); fileLocation += 5; - if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); - - if(buffer[0] != MSC_RSA_PUBLIC) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_UNKNOWN_DATA_RECEIVED); + if(r < 0) LOG_FUNC_RETURN(card->ctx, r); + + if(buffer[0] != MSC_RSA_PUBLIC) LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); *modLength = (buffer[3] << 8) | buffer[4]; /* Read the modulus and the exponent length */ if (*modLength + 2 > sizeof buffer) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); r = msc_read_object(card, inputId, fileLocation, buffer, *modLength + 2); fileLocation += *modLength + 2; - if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); - + if(r < 0) LOG_FUNC_RETURN(card->ctx, r); + *modulus = malloc(*modLength); - if(!*modulus) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + if(!*modulus) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(*modulus, buffer, *modLength); *expLength = (buffer[*modLength] << 8) | buffer[*modLength + 1]; + if (*expLength > sizeof buffer) { + free(*modulus); *modulus = NULL; + return SC_ERROR_OUT_OF_MEMORY; + } r = msc_read_object(card, inputId, fileLocation, buffer, *expLength); if(r < 0) { free(*modulus); *modulus = NULL; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } *exponent = malloc(*expLength); if(!*exponent) { free(*modulus); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(*exponent, buffer, *expLength); return 0; @@ -633,9 +651,9 @@ int msc_extract_rsa_public_key(sc_card_t *card, -/* For the moment, only support streaming data to the card +/* For the moment, only support streaming data to the card in blocks, not through file IO */ -int msc_compute_crypt_init(sc_card_t *card, +int msc_compute_crypt_init(sc_card_t *card, int keyLocation, int cipherMode, int cipherDirection, @@ -668,28 +686,33 @@ int msc_compute_crypt_init(sc_card_t *card, memcpy(ptr, initData, dataLength); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { - short receivedData = outputBuffer[0] << 8 | outputBuffer[1]; + size_t receivedData = outputBuffer[0] << 8 | outputBuffer[1]; + if (receivedData > MSC_MAX_APDU) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); + if (receivedData > *outputDataLength) { + *outputDataLength = receivedData; + LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); + } *outputDataLength = receivedData; - assert(receivedData <= MSC_MAX_APDU); memcpy(outputData, outputBuffer + 2, receivedData); return 0; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "init: got strange SWs: 0x%02X 0x%02X\n", + sc_log(card->ctx, "init: got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CARD_CMD_FAILED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); } int msc_compute_crypt_final( - sc_card_t *card, + sc_card_t *card, int keyLocation, const u8* inputData, u8* outputData, @@ -703,11 +726,11 @@ int msc_compute_crypt_final( int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x36, keyLocation, 0x03); /* Final */ - + apdu.data = buffer; apdu.datalen = dataLength + 3; apdu.lc = dataLength + 3; - + memset(outputBuffer, 0, sizeof(outputBuffer)); apdu.resp = outputBuffer; apdu.resplen = dataLength + 2; @@ -717,30 +740,36 @@ int msc_compute_crypt_final( *ptr = (dataLength >> 8) & 0xFF; ptr++; *ptr = dataLength & 0xFF; ptr++; memcpy(ptr, inputData, dataLength); - + r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { - short receivedData = outputBuffer[0] << 8 | outputBuffer[1]; + size_t receivedData = outputBuffer[0] << 8 | outputBuffer[1]; + if (receivedData > MSC_MAX_APDU) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); + if (receivedData > *outputDataLength) { + *outputDataLength = receivedData; + LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); + } *outputDataLength = receivedData; - assert(receivedData <= MSC_MAX_APDU); + memcpy(outputData, outputBuffer + 2, receivedData); return 0; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "final: got strange SWs: 0x%02X 0x%02X\n", + sc_log(card->ctx, "final: got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CARD_CMD_FAILED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); } /* Stream data to the card through file IO */ static int msc_compute_crypt_final_object( - sc_card_t *card, + sc_card_t *card, int keyLocation, const u8* inputData, u8* outputData, @@ -753,11 +782,11 @@ static int msc_compute_crypt_final_object( int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x36, keyLocation, 0x03); /* Final */ - + apdu.data = buffer; apdu.datalen = 1; apdu.lc = 1; - + ptr = buffer; *ptr = 0x02; ptr++; /* DATA LOCATION: OBJECT */ @@ -768,7 +797,7 @@ static int msc_compute_crypt_final_object( memcpy(ptr, inputData, dataLength); r = msc_create_object(card, outputId, dataLength + 2, 0x02, 0x02, 0x02); - if(r < 0) { + if(r < 0) { if(r == SC_ERROR_FILE_ALREADY_EXISTS) { r = msc_delete_object(card, outputId, 0); if(r < 0) { @@ -782,33 +811,34 @@ static int msc_compute_crypt_final_object( } r = msc_update_object(card, outputId, 0, buffer + 1, dataLength + 2); - if(r < 0) return r; - + if(r < 0) return r; + r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { r = msc_read_object(card, inputId, 2, outputData, dataLength); - *outputDataLength = dataLength; + if (r >= 0) + *outputDataLength = r; msc_delete_object(card, outputId, 0); msc_delete_object(card, inputId, 0); - return 0; + return r; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "final: got strange SWs: 0x%02X 0x%02X\n", + sc_log(card->ctx, "final: got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } } else { r = SC_ERROR_CARD_CMD_FAILED; } - /* this is last ditch cleanup */ + /* this is last ditch cleanup */ msc_delete_object(card, outputId, 0); - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + + LOG_FUNC_RETURN(card->ctx, r); } -int msc_compute_crypt(sc_card_t *card, +int msc_compute_crypt(sc_card_t *card, int keyLocation, int cipherMode, int cipherDirection, @@ -817,31 +847,34 @@ int msc_compute_crypt(sc_card_t *card, size_t dataLength, size_t outputDataLength) { - size_t left = dataLength; + size_t left = dataLength, outLeft = outputDataLength; const u8* inPtr = data; u8* outPtr = outputData; int toSend; int r; - size_t received = 0; - assert(outputDataLength >= dataLength); - + size_t received = outputDataLength; + + if (outputDataLength < dataLength) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + /* Don't send data during init... apparently current version does not support it */ toSend = 0; - r = msc_compute_crypt_init(card, - keyLocation, - cipherMode, - cipherDirection, - inPtr, - outPtr, + r = msc_compute_crypt_init(card, + keyLocation, + cipherMode, + cipherDirection, + inPtr, + outPtr, toSend, &received); - if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + if(r < 0) LOG_FUNC_RETURN(card->ctx, r); left -= toSend; inPtr += toSend; + outLeft -= received; outPtr += received; - toSend = MIN(left, MSC_MAX_APDU - 5); + toSend = MIN((int)left, MSC_MAX_APDU - 5); /* If the card supports extended APDUs, or the data fits in one normal APDU, use it for the data exchange */ if (left < (MSC_MAX_SEND - 4) || (card->caps & SC_CARD_CAP_APDU_EXT) != 0) { @@ -850,20 +883,20 @@ int msc_compute_crypt(sc_card_t *card, inPtr, outPtr, toSend, - &received); - if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + &outLeft); + if(r < 0) LOG_FUNC_RETURN(card->ctx, r); } else { /* Data is too big: use objects */ r = msc_compute_crypt_final_object(card, keyLocation, inPtr, outPtr, toSend, - &received); - if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); - } - outPtr += received; + &outLeft); + if(r < 0) LOG_FUNC_RETURN(card->ctx, r); + } + outPtr += outLeft; - return outPtr - outputData; /* Amt received */ + return (int)(outPtr - outputData); /* Amt received */ } /* USED IN KEY ITEM WRITING */ @@ -879,28 +912,30 @@ int msc_import_key(sc_card_t *card, writeAcl = 0x0002, use = 0x0002, keySize = data->keySize; - int bufferSize = 0; + size_t bufferSize = 0; u8 *buffer, *p; u8 apduBuffer[6]; sc_apdu_t apdu; int r; - assert(data->keyType == 0x02 || data->keyType == 0x03); + if (data->keyType != 0x02 && data->keyType != 0x03) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + if(data->keyType == 0x02) { if( (data->pLength == 0 || !data->pValue) || (data->modLength == 0 || !data->modValue)) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } else if(data->keyType == 0x03) { if( (data->pLength == 0 || !data->pValue) || (data->qLength == 0 || !data->qValue) || (data->pqLength == 0 || !data->pqValue) || (data->dp1Length == 0 || !data->dp1Value) || (data->dq1Length == 0 || !data->dq1Value)) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } else { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } - + if(data->keyType == 0x02) { bufferSize = 4 + 4 + data->pLength + data->modLength; } else if(data->keyType == 0x03) { @@ -909,12 +944,12 @@ int msc_import_key(sc_card_t *card, + data->dp1Length + data->dq1Length; } buffer = malloc(bufferSize); - if(!buffer) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + if(!buffer) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); p = buffer; *p = 0x00; p++; /* Encoding plain */ *p = data->keyType; p++; /* RSA_PRIVATE */ ushort2bebytes(p, keySize); p+=2; /* key size */ - + if(data->keyType == 0x02) { CPYVAL(mod); CPYVAL(p); @@ -925,9 +960,9 @@ int msc_import_key(sc_card_t *card, CPYVAL(dp1); CPYVAL(dq1); } - + r = msc_create_object(card, outputId, bufferSize, 0x02, 0x02, 0x02); - if(r < 0) { + if(r < 0) { if(r == SC_ERROR_FILE_ALREADY_EXISTS) { r = msc_delete_object(card, outputId, 0); if(r < 0) { @@ -941,12 +976,12 @@ int msc_import_key(sc_card_t *card, } } } - + r = msc_update_object(card, outputId, 0, buffer, bufferSize); free(buffer); if(r < 0) return r; - - + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x32, keyLocation, 0x00); apdu.lc = 6; apdu.data = apduBuffer; @@ -954,9 +989,9 @@ int msc_import_key(sc_card_t *card, p = apduBuffer; ushort2bebytes(p, readAcl); p+=2; ushort2bebytes(p, writeAcl); p+=2; - ushort2bebytes(p, use); + ushort2bebytes(p, use); r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { msc_delete_object(card, outputId, 0); return 0; @@ -964,16 +999,16 @@ int msc_import_key(sc_card_t *card, r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "keyimport: got strange SWs: 0x%02X 0x%02X\n", + sc_log(card->ctx, "keyimport: got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } /* this is last ditch cleanup */ msc_delete_object(card, outputId, 0); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } /* this is last ditch cleanup */ msc_delete_object(card, outputId, 0); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CARD_CMD_FAILED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); } #undef CPYVAL diff --git a/src/libopensc/muscle.h b/src/libopensc/muscle.h index 7f8f4fdd54..7b48417292 100644 --- a/src/libopensc/muscle.h +++ b/src/libopensc/muscle.h @@ -1,5 +1,5 @@ /* - * muscle.h: Support for MuscleCard Applet from musclecard.com + * muscle.h: Support for MuscleCard Applet from musclecard.com * * Copyright (C) 2006, Identity Alliance, Thomas Harning * @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MUSCLE_H_ #define MUSCLE_H_ @@ -39,7 +39,7 @@ int msc_list_objects(sc_card_t* card, u8 next, mscfs_file_t* file); int msc_partial_read_object(sc_card_t *card, msc_id objectId, int offset, u8 *data, size_t dataLength); int msc_read_object(sc_card_t *card, msc_id objectId, int offset, u8 *data, size_t dataLength); int msc_create_object(sc_card_t *card, msc_id objectId, size_t objectSize, unsigned short read, unsigned short write, unsigned short deletion); -int msc_partial_update_object(sc_card_t *card, msc_id objectId, int offset, const u8 *data, size_t dataLength); +int msc_partial_update_object(sc_card_t *card, msc_id objectId, size_t offset, const u8 *data, size_t dataLength); int msc_update_object(sc_card_t *card, msc_id objectId, int offset, const u8 *data, size_t dataLength); int msc_zero_object(sc_card_t *card, msc_id objectId, size_t dataLength); @@ -47,24 +47,24 @@ int msc_delete_object(sc_card_t *card, msc_id objectId, int zero); int msc_select_applet(sc_card_t *card, u8 *appletId, size_t appletIdLength); int msc_verify_pin(sc_card_t *card, int pinNumber, const u8 *pinValue, int pinLength, int *tries); -void msc_verify_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pinValue, int pinLength); +int msc_verify_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pinValue, size_t pinLength); int msc_unblock_pin(sc_card_t *card, int pinNumber, const u8 *pukValue, int pukLength, int *tries); -void msc_unblock_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pukValue, int pukLength); +int msc_unblock_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pukValue, size_t pukLength); int msc_change_pin(sc_card_t *card, int pinNumber, const u8 *pinValue, int pinLength, const u8 *newPin, int newPinLength, int *tries); -void msc_change_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pinValue, int pinLength, const u8 *newPin, int newPinLength); +int msc_change_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, u8* buffer, size_t bufferLength, int pinNumber, const u8 *pinValue, size_t pinLength, const u8 *newPin, size_t newPinLength); int msc_get_challenge(sc_card_t *card, unsigned short dataLength, unsigned short seedLength, u8 *seedData, u8 *outputData); -int msc_generate_keypair(sc_card_t *card, int privateKey, int publicKey, int algorithm, int keySize, int options); -int msc_extract_rsa_public_key(sc_card_t *card, +int msc_generate_keypair(sc_card_t *card, int privateKey, int publicKey, int algorithm, size_t keySize, int options); +int msc_extract_rsa_public_key(sc_card_t *card, int keyLocation, - size_t* modLength, + size_t* modLength, u8** modulus, size_t* expLength, u8** exponent); -int msc_extract_key(sc_card_t *card, +int msc_extract_key(sc_card_t *card, int keyLocation); -int msc_compute_crypt_init(sc_card_t *card, +int msc_compute_crypt_init(sc_card_t *card, int keyLocation, int cipherMode, int cipherDirection, @@ -73,20 +73,20 @@ int msc_compute_crypt_init(sc_card_t *card, size_t dataLength, size_t* outputDataLength); int msc_compute_crypt_process( - sc_card_t *card, + sc_card_t *card, int keyLocation, const u8* inputData, u8* outputData, size_t dataLength, size_t* outputDataLength); int msc_compute_crypt_final( - sc_card_t *card, + sc_card_t *card, int keyLocation, const u8* inputData, u8* outputData, size_t dataLength, size_t* outputDataLength); -int msc_compute_crypt(sc_card_t *card, +int msc_compute_crypt(sc_card_t *card, int keyLocation, int cipherMode, int cipherDirection, diff --git a/src/libopensc/libopensc.exports b/src/libopensc/opensc.exports similarity index 92% rename from src/libopensc/libopensc.exports rename to src/libopensc/opensc.exports index 7931a2686b..c9a0b35fcc 100644 --- a/src/libopensc/libopensc.exports +++ b/src/libopensc/opensc.exports @@ -64,6 +64,7 @@ sc_cancel sc_card_ctl sc_change_reference_data sc_check_sw +sc_clear_ec_params sc_compare_oid sc_compare_path sc_compare_path_prefix @@ -83,16 +84,20 @@ sc_ctx_use_reader sc_ctx_win32_get_config_value _sc_delete_reader sc_decipher +sc_decrypt_sym sc_delete_file sc_delete_record sc_der_copy sc_detect_card_presence sc_disconnect_card sc_do_log +sc_do_log_color sc_do_log_noframe +sc_do_log_openssl _sc_debug _sc_debug_hex sc_enum_apps +sc_encrypt_sym sc_encode_oid sc_parse_ef_atr sc_establish_context @@ -108,11 +113,11 @@ sc_file_set_type_attr sc_file_set_content sc_file_valid sc_format_apdu +sc_format_apdu_ex sc_bytes2apdu sc_format_asn1_entry sc_format_oid sc_init_oid -sc_compare_oid sc_valid_oid sc_format_path sc_free_apps @@ -130,8 +135,9 @@ sc_list_files sc_lock sc_logout sc_make_cache_dir -sc_mem_alloc_secure sc_mem_clear +sc_mem_secure_alloc +sc_mem_secure_free sc_mem_reverse sc_match_atr_block sc_path_print @@ -156,7 +162,6 @@ sc_pkcs15_decode_cdf_entry sc_pkcs15_decode_dodf_entry sc_pkcs15_decode_prkdf_entry sc_pkcs15_decode_pubkey -sc_pkcs15_decode_pubkey_dsa sc_pkcs15_decode_pubkey_rsa sc_pkcs15_decode_pubkey_ec sc_pkcs15_decode_pubkey_gostr3410 @@ -170,15 +175,19 @@ sc_pkcs15_encode_dodf_entry sc_pkcs15_encode_odf sc_pkcs15_encode_prkdf_entry sc_pkcs15_encode_pubkey -sc_pkcs15_encode_pubkey_dsa sc_pkcs15_encode_pubkey_rsa sc_pkcs15_encode_pubkey_ec +sc_pkcs15_encode_pubkey_eddsa sc_pkcs15_encode_pubkey_gostr3410 -sc_pkcs15_encode_pubkey_as_spki +sc_pkcs15_encode_pubkey_as_spki sc_pkcs15_encode_pukdf_entry +sc_pkcs15_encode_skdf_entry sc_pkcs15_encode_tokeninfo sc_pkcs15_encode_unusedspace +sc_pkcs15_encrypt_sym sc_pkcs15_erase_pubkey +sc_pkcs15_erase_prkey +sc_pkcs15_decrypt_sym sc_pkcs15_dup_pubkey sc_pkcs15_find_cert_by_id sc_pkcs15_find_data_object_by_app_oid @@ -207,6 +216,8 @@ sc_pkcs15_free_prkey sc_pkcs15_free_prkey_info sc_pkcs15_free_pubkey sc_pkcs15_free_pubkey_info +sc_pkcs15_free_tokeninfo +sc_pkcs15_free_skey_info sc_pkcs15_get_application_by_type sc_pkcs15_get_name_from_dn sc_pkcs15_get_object_guid @@ -234,8 +245,11 @@ sc_pkcs15_pubkey_from_cert sc_pkcs15_remove_object sc_pkcs15_remove_unusedspace sc_pkcs15_search_objects +sc_pkcs15_tokeninfo_new sc_pkcs15_unbind sc_pkcs15_unblock_pin +sc_pkcs15_unwrap +sc_pkcs15_wrap sc_pkcs15_verify_pin sc_pkcs15_get_pin_info sc_pkcs15_verify_pin_with_session_pin @@ -262,11 +276,13 @@ sc_set_security_env sc_strerror sc_transmit_apdu sc_unlock +sc_unwrap sc_update_binary sc_update_dir sc_update_record sc_verify sc_wait_for_event +sc_wrap sc_write_binary sc_write_record sc_erase_binary @@ -290,12 +306,8 @@ sc_pkcs15init_get_asepcos_ops sc_pkcs15init_get_cardos_ops sc_pkcs15init_get_cryptoflex_ops sc_pkcs15init_get_cyberflex_ops -sc_pkcs15init_get_gpk_ops sc_pkcs15init_get_gids_ops -sc_pkcs15init_get_incrypto34_ops -sc_pkcs15init_get_jcop_ops sc_pkcs15init_get_manufacturer -sc_pkcs15init_get_miocos_ops sc_pkcs15init_get_muscle_ops sc_pkcs15init_get_oberthur_ops sc_pkcs15init_get_pin_info @@ -323,8 +335,8 @@ sc_pkcs15init_verify_secret sc_pkcs15init_sanity_check sc_pkcs15init_finalize_profile sc_card_find_rsa_alg +sc_card_find_ec_alg sc_check_apdu -sc_print_cache sc_find_app sc_remote_data_init sc_crc32 @@ -348,18 +360,28 @@ iasecc_sdo_encode_update_field _sc_card_add_ec_alg _sc_card_add_rsa_alg _sc_match_atr +_sc_match_atr_block _sc_log -npa_secret_name +_sc_log_openssl +eac_secret_name get_pace_capabilities perform_pace perform_terminal_authentication perform_chip_authentication -npa_default_flags +eac_default_flags +eac_pace_get_tries_left npa_reset_retry_counter -npa_pace_get_tries_left escape_pace_input_to_buf escape_buf_to_pace_input escape_pace_output_to_buf escape_buf_to_pace_output escape_pace_capabilities_to_buf escape_buf_to_pace_capabilities +ui_get_str +sc_notify_init +sc_notify_close +sc_notify +sc_notify_id +sc_color_fprintf +iso7816_update_binary_sfid +sc_free diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index f81f27e3ae..144e8012c0 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** @@ -57,26 +57,36 @@ extern "C" { #define SC_SEC_OPERATION_SIGN 0x0002 #define SC_SEC_OPERATION_AUTHENTICATE 0x0003 #define SC_SEC_OPERATION_DERIVE 0x0004 - +#define SC_SEC_OPERATION_WRAP 0x0005 +#define SC_SEC_OPERATION_UNWRAP 0x0006 +#define SC_SEC_OPERATION_ENCRYPT_SYM 0x0007 +#define SC_SEC_OPERATION_DECRYPT_SYM 0x0008 /* sc_security_env flags */ #define SC_SEC_ENV_ALG_REF_PRESENT 0x0001 #define SC_SEC_ENV_FILE_REF_PRESENT 0x0002 #define SC_SEC_ENV_KEY_REF_PRESENT 0x0004 -/* FIXME: the flag below is misleading */ -#define SC_SEC_ENV_KEY_REF_ASYMMETRIC 0x0008 +#define SC_SEC_ENV_KEY_REF_SYMMETRIC 0x0008 #define SC_SEC_ENV_ALG_PRESENT 0x0010 +#define SC_SEC_ENV_TARGET_FILE_REF_PRESENT 0x0020 + +/* sc_security_env additional parameters */ +#define SC_SEC_ENV_MAX_PARAMS 10 +#define SC_SEC_ENV_PARAM_IV 1 +#define SC_SEC_ENV_PARAM_TARGET_FILE 2 /* PK algorithms */ #define SC_ALGORITHM_RSA 0 -#define SC_ALGORITHM_DSA 1 -#define SC_ALGORITHM_EC 2 +#define SC_ALGORITHM_EC 2 #define SC_ALGORITHM_GOSTR3410 3 +#define SC_ALGORITHM_EDDSA 4 +#define SC_ALGORITHM_XEDDSA 5 /* Symmetric algorithms */ #define SC_ALGORITHM_DES 64 #define SC_ALGORITHM_3DES 65 #define SC_ALGORITHM_GOST 66 #define SC_ALGORITHM_AES 67 +#define SC_ALGORITHM_UNDEFINED 68 /* used with CKK_GENERIC_SECRET type keys */ /* Hash algorithms */ #define SC_ALGORITHM_MD5 128 @@ -86,49 +96,91 @@ extern "C" { /* Key derivation algorithms */ #define SC_ALGORITHM_PBKDF2 192 -/* Key encryption algoprithms */ +/* Key encryption algorithms */ #define SC_ALGORITHM_PBES2 256 #define SC_ALGORITHM_ONBOARD_KEY_GEN 0x80000000 /* need usage = either sign or decrypt. keys with both? decrypt, emulate sign */ #define SC_ALGORITHM_NEED_USAGE 0x40000000 -#define SC_ALGORITHM_SPECIFIC_FLAGS 0x0001FFFF +#define SC_ALGORITHM_SPECIFIC_FLAGS 0x001FFFFF -#define SC_ALGORITHM_RSA_RAW 0x00000001 /* If the card is willing to produce a cryptogram padded with the following - * methods, set these flags accordingly. */ -#define SC_ALGORITHM_RSA_PADS 0x0000000E -#define SC_ALGORITHM_RSA_PAD_NONE 0x00000000 -#define SC_ALGORITHM_RSA_PAD_PKCS1 0x00000002 + * methods, set these flags accordingly. These flags are exclusive: an RSA card + * must support at least one of them, and exactly one of them must be selected + * for a given operation. */ +#define SC_ALGORITHM_RSA_RAW 0x00000001 +#define SC_ALGORITHM_RSA_PADS 0x000000FF +#define SC_ALGORITHM_RSA_PAD_NONE 0x00000001 #define SC_ALGORITHM_RSA_PAD_ANSI 0x00000004 #define SC_ALGORITHM_RSA_PAD_ISO9796 0x00000008 +#define SC_ALGORITHM_RSA_PAD_PSS 0x00000010 /* PKCS#1 v2.0 PSS */ +#define SC_ALGORITHM_RSA_PAD_OAEP 0x00000020 /* PKCS#1 v2.0 OAEP */ +#define SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 0x00000040 /* PKCS#1 v1.5 padding type 1 */ +#define SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02 0x00000080 /* PKCS#1 v1.5 padding type 2 */ +#define SC_ALGORITHM_RSA_PAD_PKCS1 (SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02) /* PKCS#1 v1.5 (type 1 or 2) */ /* If the card is willing to produce a cryptogram with the following - * hash values, set these flags accordingly. */ -#define SC_ALGORITHM_RSA_HASH_NONE 0x00000010 -#define SC_ALGORITHM_RSA_HASH_SHA1 0x00000020 -#define SC_ALGORITHM_RSA_HASH_MD5 0x00000040 -#define SC_ALGORITHM_RSA_HASH_MD5_SHA1 0x00000080 -#define SC_ALGORITHM_RSA_HASH_RIPEMD160 0x00000100 -#define SC_ALGORITHM_RSA_HASH_SHA256 0x00000200 -#define SC_ALGORITHM_RSA_HASH_SHA384 0x00000400 -#define SC_ALGORITHM_RSA_HASH_SHA512 0x00000800 -#define SC_ALGORITHM_RSA_HASH_SHA224 0x00001000 -#define SC_ALGORITHM_RSA_HASHES 0x00001FE0 - -#define SC_ALGORITHM_GOSTR3410_RAW 0x00002000 -#define SC_ALGORITHM_GOSTR3410_HASH_NONE 0x00004000 -#define SC_ALGORITHM_GOSTR3410_HASH_GOSTR3411 0x00008000 -#define SC_ALGORITHM_GOSTR3410_HASHES 0x00008000 -/*TODO: -DEE Should the above be 0x0000E000 */ -/* Or should the HASH_NONE be 0x00000010 and HASHES be 0x00008010 */ - + * hash values, set these flags accordingly. The interpretation of the hash + * flags depends on the algorithm and padding chosen: for RSA, the hash flags + * determine how the padding is constructed and do not describe the first + * hash applied to the document before padding begins. + * + * - For PAD_NONE, ANSI X9.31, (and ISO9796?), the hash value is therefore + * ignored. For ANSI X9.31, the input data must already have the hash + * identifier byte appended (eg 0x33 for SHA-1). + * - For PKCS1 (v1.5) the hash is recorded in the padding, and HASH_NONE is a + * valid value, meaning that the hash's DigestInfo has already been + * prepended to the data, otherwise the hash id is put on the front. + * - For PSS (PKCS#1 v2.0) the hash is used to derive the padding from the + * already-hashed message. + * + * In no case is the hash actually applied to the entire document. + * + * It's possible that the card may support different hashes for PKCS1 and PSS + * signatures; in this case the card driver has to pick the lowest-denominator + * when it sets these flags to indicate its capabilities. */ +#define SC_ALGORITHM_RSA_HASH_NONE 0x00000100 /* only applies to PKCS1 padding */ +#define SC_ALGORITHM_RSA_HASH_SHA1 0x00000200 +#define SC_ALGORITHM_RSA_HASH_MD5 0x00000400 +#define SC_ALGORITHM_RSA_HASH_MD5_SHA1 0x00000800 +#define SC_ALGORITHM_RSA_HASH_RIPEMD160 0x00001000 +#define SC_ALGORITHM_RSA_HASH_SHA256 0x00002000 +#define SC_ALGORITHM_RSA_HASH_SHA384 0x00004000 +#define SC_ALGORITHM_RSA_HASH_SHA512 0x00008000 +#define SC_ALGORITHM_RSA_HASH_SHA224 0x00010000 +#define SC_ALGORITHM_RSA_HASHES 0x0001FF00 + +/* This defines the hashes to be used with MGF1 in PSS padding */ +#define SC_ALGORITHM_MGF1_SHA1 0x00100000 +#define SC_ALGORITHM_MGF1_SHA256 0x00200000 +#define SC_ALGORITHM_MGF1_SHA384 0x00400000 +#define SC_ALGORITHM_MGF1_SHA512 0x00800000 +#define SC_ALGORITHM_MGF1_SHA224 0x01000000 +#define SC_ALGORITHM_MGF1_HASHES 0x01F00000 + +/* These flags are exclusive: a GOST R34.10 card must support at least one or the + * other of the methods, and exactly one of them applies to any given operation. + * Note that the GOST R34.11 hash is actually applied to the data (ie if this + * algorithm is chosen the entire unhashed document is passed in). */ +#define SC_ALGORITHM_GOSTR3410_RAW 0x00020000 +#define SC_ALGORITHM_GOSTR3410_HASH_NONE SC_ALGORITHM_GOSTR3410_RAW /*XXX*/ +#define SC_ALGORITHM_GOSTR3410_HASH_GOSTR3411 0x00080000 +#define SC_ALGORITHM_GOSTR3410_HASHES 0x000A0000 +/*TODO: -DEE Should the above be 0x000E0000 */ +/* Or should the HASH_NONE be 0x00000100 and HASHES be 0x00080010 */ + +/* The ECDSA flags are exclusive, and exactly one of them applies to any given + * operation. If ECDSA with a hash is specified, then the data passed in is + * the entire document, unhashed, and the hash is applied once to it before + * truncating and signing. These flags are distinct from the RSA hash flags, + * which determine the hash ids the card is willing to put in RSA message + * padding. */ /* May need more bits if card can do more hashes */ /* TODO: -DEE Will overload RSA_HASHES with EC_HASHES */ /* Not clear if these need their own bits or not */ /* The PIV card does not support and hashes */ -#define SC_ALGORITHM_ECDSA_RAW 0x00010000 -#define SC_ALGORITHM_ECDH_CDH_RAW 0x00020000 +#define SC_ALGORITHM_ECDH_CDH_RAW 0x00200000 +#define SC_ALGORITHM_ECDSA_RAW 0x00100000 #define SC_ALGORITHM_ECDSA_HASH_NONE SC_ALGORITHM_RSA_HASH_NONE #define SC_ALGORITHM_ECDSA_HASH_SHA1 SC_ALGORITHM_RSA_HASH_SHA1 #define SC_ALGORITHM_ECDSA_HASH_SHA224 SC_ALGORITHM_RSA_HASH_SHA224 @@ -141,10 +193,17 @@ extern "C" { SC_ALGORITHM_ECDSA_HASH_SHA384 | \ SC_ALGORITHM_ECDSA_HASH_SHA512) +/* EdDSA algorithms */ +#define SC_ALGORITHM_EDDSA_RAW 0x00400000 +#define SC_ALGORITHM_XEDDSA_RAW 0x00800000 + /* define mask of all algorithms that can do raw */ -#define SC_ALGORITHM_RAW_MASK (SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_GOSTR3410_RAW | SC_ALGORITHM_ECDSA_RAW) +#define SC_ALGORITHM_RAW_MASK (SC_ALGORITHM_RSA_RAW | \ + SC_ALGORITHM_GOSTR3410_RAW | \ + SC_ALGORITHM_ECDH_CDH_RAW | \ + SC_ALGORITHM_ECDSA_RAW) -/* extened algorithm bits for selected mechs */ +/* extended algorithm bits for selected mechs */ #define SC_ALGORITHM_EXT_EC_F_P 0x00000001 #define SC_ALGORITHM_EXT_EC_F_2M 0x00000002 #define SC_ALGORITHM_EXT_EC_ECPARAMETERS 0x00000004 @@ -152,37 +211,60 @@ extern "C" { #define SC_ALGORITHM_EXT_EC_UNCOMPRESES 0x00000010 #define SC_ALGORITHM_EXT_EC_COMPRESS 0x00000020 +/* symmetric algorithm flags. More algorithms to be added when implemented. */ +#define SC_ALGORITHM_AES_ECB 0x01000000 +#define SC_ALGORITHM_AES_CBC 0x02000000 +#define SC_ALGORITHM_AES_CBC_PAD 0x04000000 +#define SC_ALGORITHM_AES_FLAGS 0x0F000000 + + /* Event masks for sc_wait_for_event() */ #define SC_EVENT_CARD_INSERTED 0x0001 #define SC_EVENT_CARD_REMOVED 0x0002 -#define SC_EVENT_CARD_EVENTS SC_EVENT_CARD_INSERTED|SC_EVENT_CARD_REMOVED +#define SC_EVENT_CARD_EVENTS (SC_EVENT_CARD_INSERTED|SC_EVENT_CARD_REMOVED) #define SC_EVENT_READER_ATTACHED 0x0004 #define SC_EVENT_READER_DETACHED 0x0008 -#define SC_EVENT_READER_EVENTS SC_EVENT_READER_ATTACHED|SC_EVENT_READER_DETACHED +#define SC_EVENT_READER_EVENTS (SC_EVENT_READER_ATTACHED|SC_EVENT_READER_DETACHED) + +#define MAX_FILE_SIZE 65535 + +#define ED448_KEY_SIZE_BYTES 57U struct sc_supported_algo_info { unsigned int reference; unsigned int mechanism; + struct sc_object_id parameters; /* OID for ECC */ unsigned int operations; struct sc_object_id algo_id; unsigned int algo_ref; }; +typedef struct sc_sec_env_param { + unsigned int param_type; + void* value; + size_t value_len; +} sc_sec_env_param_t; + + typedef struct sc_security_env { unsigned long flags; int operation; - unsigned int algorithm, algorithm_flags; + unsigned long algorithm, algorithm_flags; + size_t key_size_bits; - unsigned int algorithm_ref; + unsigned long algorithm_ref; struct sc_path file_ref; - unsigned char key_ref[8]; + unsigned char key_ref[SC_MAX_KEYREF_SIZE]; size_t key_ref_len; + struct sc_path target_file_ref; /* target key file in unwrap operation */ struct sc_supported_algo_info supported_algos[SC_MAX_SUPPORTED_ALGORITHMS]; + /* optional parameters */ + struct sc_sec_env_param params[SC_SEC_ENV_MAX_PARAMS]; } sc_security_env_t; struct sc_algorithm_id { - unsigned int algorithm; + unsigned long algorithm; struct sc_object_id oid; void *params; }; @@ -201,14 +283,20 @@ struct sc_pbes2_params { }; /* + * PKCS11 2.3 Elliptic Curve lists mechanisms that use CKA_EC_PARAMS + * which implies the type of key and size needed in the OID * The ecParameters can be presented as * - name of curve; * - OID of named curve; * - implicit parameters. + * - printable string for non standard OIDS - added in pkcs11 3.0 * * type - type(choice) of 'EC domain parameters' as it present in CKA_EC_PARAMS (PKCS#11). - Recommended value '1' -- namedCurve. + * Recommended value '1' -- namedCurve. * field_length - EC key size in bits. + * key_type - 0 implies SC_ALGORITHM_EC, SC_ALGORITHM_EDDSA or SC_ALGORITHM_XEDDSA + * Not actually part of CKA_EC_PARAMS - used in OpenSC to differentiate key types that use ec_params + * will be set by sc_pkcs15_fix_ec_parameters */ struct sc_ec_parameters { char *named_curve; @@ -217,19 +305,20 @@ struct sc_ec_parameters { int type; size_t field_length; + unsigned int key_type; }; typedef struct sc_algorithm_info { unsigned int algorithm; - unsigned int key_length; - unsigned int flags; + size_t key_length; + unsigned long flags; union { struct sc_rsa_info { unsigned long exponent; } _rsa; struct sc_ec_info { - unsigned ext_flags; + unsigned long ext_flags; struct sc_ec_parameters params; } _ec; } u; @@ -267,15 +356,6 @@ struct sc_ef_atr { unsigned status; }; -struct sc_card_cache { - struct sc_path current_path; - - struct sc_file *current_ef; - struct sc_file *current_df; - - int valid; -}; - #define SC_PROTO_T0 0x00000001 #define SC_PROTO_T1 0x00000002 #define SC_PROTO_RAW 0x00001000 @@ -323,7 +403,7 @@ typedef struct sc_reader { unsigned long flags, capabilities; unsigned int supported_protocols, active_protocol; size_t max_send_size; /* Max Lc supported by the reader layer */ - size_t max_recv_size; /* Mac Le supported by the reader layer */ + size_t max_recv_size; /* Max Le supported by the reader layer */ struct sc_atr atr; struct sc_uid uid; @@ -353,42 +433,51 @@ typedef struct sc_reader { #define SC_PIN_ENCODING_BCD 1 #define SC_PIN_ENCODING_GLP 2 /* Global Platform - Card Specification v2.0.1 */ -/** Values for sc_pin_cmd_pin.logged_in */ -#define SC_PIN_STATE_UNKNOWN -1 -#define SC_PIN_STATE_LOGGED_OUT 0 -#define SC_PIN_STATE_LOGGED_IN 1 +/** Values for sc_pin_cmd_pin.logged_in, can be bitmapped together */ +#define SC_PIN_STATE_UNKNOWN 0 +#define SC_PIN_STATE_LOGGED_OUT 1 +#define SC_PIN_STATE_LOGGED_IN 2 +#define SC_PIN_STATE_NEEDS_CHANGE 4 +/* A card driver receives the sc_pin_cmd_data and sc_pin_cmd_pin structures filled in by the + * caller, with the exception of the fields returned by the driver for SC_PIN_CMD_GET_INFO. + * It may use and update any of the fields before passing the structure to the ISO 7816 layer for + * processing. + */ struct sc_pin_cmd_pin { const char *prompt; /* Prompt to display */ - const unsigned char *data; /* PIN, if given by the appliction */ - int len; /* set to -1 to get pin from pin pad */ + const unsigned char *data; /* PIN, set to NULL when using pin pad */ + size_t len; /* set to 0 when using pin pad */ size_t min_length; /* min length of PIN */ size_t max_length; /* max length of PIN */ - size_t stored_length; /* stored length of PIN */ unsigned int encoding; /* ASCII-numeric, BCD, etc */ - size_t pad_length; /* filled in by the card driver */ + size_t pad_length; /* PIN padding options, used with SC_PIN_CMD_NEED_PADDING */ unsigned char pad_char; - size_t offset; /* PIN offset in the APDU */ - size_t length_offset; /* Effective PIN length offset in the APDU */ - - int max_tries; /* Used for signaling back from SC_PIN_CMD_GET_INFO */ - int tries_left; /* Used for signaling back from SC_PIN_CMD_GET_INFO */ - int logged_in; /* Used for signaling back from SC_PIN_CMD_GET_INFO */ + size_t offset; /* PIN offset in the APDU when using pin pad */ - struct sc_acl_entry acls[SC_MAX_SDO_ACLS]; + int max_tries; /* Used for signaling back from SC_PIN_CMD_GET_INFO */ + int tries_left; /* Used for signaling back from SC_PIN_CMD_GET_INFO */ + int logged_in; /* Used for signaling back from SC_PIN_CMD_GET_INFO */ }; +/* A NULL in apdu means that the APDU is prepared by the ISO 7816 layer, which also handles PIN + * padding and setting offset fields for the PINs (for PIN-pad use). A non-NULL in APDU means that + * the card driver has prepared the APDU (including padding) and set the PIN offset fields. + * + * Note that flags apply to both PINs for multi-PIN operations. + */ struct sc_pin_cmd_data { unsigned int cmd; unsigned int flags; unsigned int pin_type; /* usually SC_AC_CHV */ int pin_reference; + int puk_reference; /* non-zero means that reference is available */ struct sc_pin_cmd_pin pin1, pin2; @@ -452,6 +541,7 @@ struct sc_reader_operations { /* Hint SC_CARD_CAP_RNG */ #define SC_CARD_FLAG_RNG 0x00000002 +#define SC_CARD_FLAG_KEEP_ALIVE 0x00000004 /* * Card capabilities @@ -473,16 +563,23 @@ struct sc_reader_operations { * instead of relying on the ACL info in the profile files. */ #define SC_CARD_CAP_USE_FCI_AC 0x00000010 -/* D-TRUST CardOS cards special flags */ -#define SC_CARD_CAP_ONLY_RAW_HASH 0x00000040 -#define SC_CARD_CAP_ONLY_RAW_HASH_STRIPPED 0x00000080 - /* Card (or card driver) supports an protected authentication mechanism */ #define SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH 0x00000100 /* Card (or card driver) supports generating a session PIN */ #define SC_CARD_CAP_SESSION_PIN 0x00000200 +/* Card and driver supports handling on card session objects. + * If a driver has this capability, the driver handles storage and operations + * with objects that CKA_TOKEN set to FALSE. If a driver doesn't support this, + * OpenSC handles them as in memory objects.*/ +#define SC_CARD_CAP_ONCARD_SESSION_OBJECTS 0x00000400 + +/* Card (or card driver) supports key wrapping operations */ +#define SC_CARD_CAP_WRAP_KEY 0x00000800 +/* Card (or card driver) supports key unwrapping operations */ +#define SC_CARD_CAP_UNWRAP_KEY 0x00001000 + typedef struct sc_card { struct sc_context *ctx; struct sc_reader *reader; @@ -498,7 +595,6 @@ typedef struct sc_card { struct sc_app_info *app[SC_MAX_CARD_APPS]; int app_count; - struct sc_file *ef_dir; struct sc_ef_atr *ef_atr; @@ -513,8 +609,6 @@ typedef struct sc_card { void *drv_data; int max_pin_len; - struct sc_card_cache cache; - struct sc_serial_number serialnr; struct sc_version version; @@ -527,38 +621,100 @@ typedef struct sc_card { } sc_card_t; struct sc_card_operations { - /* Called in sc_connect_card(). Must return 1, if the current + /** @brief Match a card with the given card driver. + * + * Called in sc_connect_card(). Must return 1, if the current * card can be handled with this driver, or 0 otherwise. ATR * field of the sc_card struct is filled in before calling - * this function. */ + * this function. It is recommended not to modify `card` during this call. + * */ int (*match_card)(struct sc_card *card); - /* Called when ATR of the inserted card matches an entry in ATR + /** @brief Initialize a card. + * + * Called when ATR of the inserted card matches an entry in ATR * table. May return SC_ERROR_INVALID_CARD to indicate that - * the card cannot be handled with this driver. */ + * the card cannot be handled with this driver. drv_data may be used to + * store card driver's (allocated) private data. */ int (*init)(struct sc_card *card); - /* Called when the card object is being freed. finish() has to + /** @brief Deinitialize a card. + * + * Called when the `card` object is being freed. finish() has to * deallocate all possible private data. */ int (*finish)(struct sc_card *card); /* ISO 7816-4 functions */ + /** + * @brief Read data from a binary EF with a single command + * + * Implementation of this call back is optional and may be NULL. + * + * @param card struct sc_card object on which to issue the command + * @param idx index within the file with the data to read + * @param buf buffer to the read data + * @param count number of bytes to read + * @param flags flags for the READ BINARY command (optional) + * @return number of bytes read or an error code + * + * @see sc_read_binary() + */ int (*read_binary)(struct sc_card *card, unsigned int idx, - u8 * buf, size_t count, unsigned long flags); + u8 * buf, size_t count, unsigned long *flags); + /** + * @brief Write data to a binary EF with a single command + * + * Implementation of this call back is optional and may be NULL. + * + * @param card struct sc_card object on which to issue the command + * @param idx index within the file for the data to be written + * @param buf buffer with the data + * @param count number of bytes to write + * @param flags flags for the WRITE BINARY command (currently not used) + * @return number of bytes written or an error code + * + * @see sc_write_binary() + */ int (*write_binary)(struct sc_card *card, unsigned int idx, const u8 * buf, size_t count, unsigned long flags); + /** @brief Updates the content of a binary EF + * + * Implementation of this call back is optional and may be NULL. + * + * @param card struct sc_card object on which to issue the command + * @param idx index within the file for the data to be updated + * @param buf buffer with the new data + * @param count number of bytes to update + * @param flags flags for the UPDATE BINARY command (currently not used) + * @return number of bytes written or an error code + * + * @see sc_update_binary() + */ int (*update_binary)(struct sc_card *card, unsigned int idx, const u8 * buf, size_t count, unsigned long flags); + /** + * @brief Sets (part of) the content of an EF to its logical erased state + * + * Implementation of this call back is optional and may be NULL. + * + * @param card struct sc_card object on which to issue the command + * @param idx index within the file for the data to be erased + * @param count number of bytes to erase + * @param flags flags for the ERASE BINARY command (currently not used) + * @return number of bytes erased or an error code + * + * @see sc_erase_binary() + */ int (*erase_binary)(struct sc_card *card, unsigned int idx, size_t count, unsigned long flags); - int (*read_record)(struct sc_card *card, unsigned int rec_nr, + int (*read_record)(struct sc_card *card, unsigned int rec_nr, unsigned int idx, u8 * buf, size_t count, unsigned long flags); int (*write_record)(struct sc_card *card, unsigned int rec_nr, const u8 * buf, size_t count, unsigned long flags); int (*append_record)(struct sc_card *card, const u8 * buf, size_t count, unsigned long flags); - int (*update_record)(struct sc_card *card, unsigned int rec_nr, + int (*update_record)(struct sc_card *card, unsigned int rec_nr, unsigned int idx, const u8 * buf, size_t count, unsigned long flags); /* select_file: Does the equivalent of SELECT FILE command specified @@ -567,6 +723,16 @@ struct sc_card_operations { int (*select_file)(struct sc_card *card, const struct sc_path *path, struct sc_file **file_out); int (*get_response)(struct sc_card *card, size_t *count, u8 *buf); + /** + * Get random data from the card + * + * Implementation of this call back is optional and may be NULL. + * + * @param card struct sc_card object on which to issue the command + * @param buf buffer to be filled with random data + * @param count number of random bytes to initialize + * @return number of random bytes successfully initialized (i.e. `count` or less bytes) or an error code + */ int (*get_challenge)(struct sc_card *card, u8 * buf, size_t count); /* @@ -600,7 +766,7 @@ struct sc_card_operations { int (*decipher)(struct sc_card *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen); - /* compute_signature: Generates a digital signature on the card. Similiar + /* compute_signature: Generates a digital signature on the card. Similar * to the function decipher. */ int (*compute_signature)(struct sc_card *card, const u8 * data, size_t data_len, u8 * out, size_t outlen); @@ -634,8 +800,7 @@ struct sc_card_operations { /* pin_cmd: verify/change/unblock command; optionally using the * card's pin pad if supported. */ - int (*pin_cmd)(struct sc_card *, struct sc_pin_cmd_data *, - int *tries_left); + int (*pin_cmd)(struct sc_card *, struct sc_pin_cmd_data *); int (*get_data)(struct sc_card *, unsigned int, u8 *, size_t); int (*put_data)(struct sc_card *, unsigned int, const u8 *, size_t); @@ -647,6 +812,15 @@ struct sc_card_operations { unsigned char **, size_t *); int (*card_reader_lock_obtained)(struct sc_card *, int was_reset); + + int (*wrap)(struct sc_card *card, u8 *out, size_t outlen); + + int (*unwrap)(struct sc_card *card, const u8 *crgram, size_t crgram_len); + + int (*encrypt_sym)(struct sc_card *card, const u8 *plaintext, size_t plaintext_len, + u8 *out, size_t *outlen); + int (*decrypt_sym)(struct sc_card *card, const u8 *EncryptedData, size_t EncryptedDataLen, + u8 *out, size_t *outlen); }; typedef struct sc_card_driver { @@ -678,7 +852,7 @@ typedef struct { unsigned long (*thread_id)(void); } sc_thread_context_t; -/** Stop modifing or using external resources +/** Stop modifying or using external resources * * Currently this is used to avoid freeing duplicated external resources for a * process that has been forked. For example, a child process may want to leave @@ -687,17 +861,21 @@ typedef struct { * calling sc_disconnect_card. */ #define SC_CTX_FLAG_TERMINATE 0x00000001 +/** removed in 0.18.0 and later */ #define SC_CTX_FLAG_PARANOID_MEMORY 0x00000002 #define SC_CTX_FLAG_DEBUG_MEMORY 0x00000004 #define SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER 0x00000008 #define SC_CTX_FLAG_DISABLE_POPUPS 0x00000010 +#define SC_CTX_FLAG_DISABLE_COLORS 0x00000020 + +typedef struct ossl3ctx ossl3ctx_t; typedef struct sc_context { scconf_context *conf; - scconf_block *conf_blocks[3]; + scconf_block *conf_blocks[4]; char *app_name; + char *exe_path; int debug; - int reopen_log_file; unsigned long flags; FILE *debug_file; @@ -715,7 +893,13 @@ typedef struct sc_context { sc_thread_context_t *thread_ctx; void *mutex; +#ifdef ENABLE_OPENSSL + char *openssl_config; + ossl3ctx_t *ossl3ctx; +#endif + unsigned int magic; + int disable_hw_pkcs1_padding; } sc_context_t; /* APDU handling functions */ @@ -723,11 +907,23 @@ typedef struct sc_context { /** Sends a APDU to the card * @param card struct sc_card object to which the APDU should be send * @param apdu sc_apdu_t object of the APDU to be send - * @return SC_SUCCESS on succcess and an error code otherwise + * @return SC_SUCCESS on success and an error code otherwise */ -int sc_transmit_apdu(struct sc_card *, struct sc_apdu *); +int sc_transmit_apdu(struct sc_card *card, struct sc_apdu *apdu); -void sc_format_apdu(struct sc_card *, struct sc_apdu *, int, int, int, int); +void sc_format_apdu(struct sc_card *card, struct sc_apdu *apdu, + int cse, int ins, int p1, int p2); + +/** Format an APDU based on the data to be sent and received. + * + * Calls \a sc_transmit_apdu() by determining the APDU case based on \a datalen + * and \a resplen. As result, no chaining or GET RESPONSE will be performed in + * sc_format_apdu(). + */ +void sc_format_apdu_ex(struct sc_apdu *apdu, + u8 cla, u8 ins, u8 p1, u8 p2, + const u8 *data, size_t datalen, + u8 *resp, size_t resplen); int sc_check_apdu(struct sc_card *, const struct sc_apdu *); @@ -745,6 +941,24 @@ int sc_check_apdu(struct sc_card *, const struct sc_apdu *); */ int sc_bytes2apdu(sc_context_t *ctx, const u8 *buf, size_t len, sc_apdu_t *apdu); +/** Encodes a APDU as an octet string + * @param ctx sc_context_t object (used for logging) + * @param apdu APDU to be encoded as an octet string + * @param proto protocol version to be used + * @param out output buffer of size outlen. + * @param outlen size of the output buffer + * @return SC_SUCCESS on success and an error code otherwise + */ +int sc_apdu2bytes(sc_context_t *ctx, const sc_apdu_t *apdu, + unsigned int proto, u8 *out, size_t outlen); + +/** Calculates the length of the encoded APDU in octets. + * @param apdu the APDU + * @param proto the desired protocol + * @return length of the encoded APDU + */ +size_t sc_apdu_get_length(const sc_apdu_t *apdu, unsigned int proto); + int sc_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2); /********************************************************************/ @@ -769,13 +983,15 @@ typedef struct { /** version number of this structure (0 for this version) */ unsigned int ver; /** name of the application (used for finding application - * dependend configuration data). If NULL the name "default" + * dependent configuration data). If NULL the name "default" * will be used. */ const char *app_name; /** context flags */ unsigned long flags; /** mutex functions to use (optional) */ sc_thread_context_t *thread_ctx; + int debug; + FILE *debug_file; } sc_context_param_t; /** @@ -818,8 +1034,9 @@ int sc_ctx_detect_readers(sc_context_t *ctx); * @param key path of register key * @return SC_SUCCESS on success and an error code otherwise. */ -int sc_ctx_win32_get_config_value(char *env, char *reg, char *key, char *out, - size_t *out_size); +int sc_ctx_win32_get_config_value(const char *env, + const char *reg, const char *key, + void *out, size_t *out_size); /** * Returns a pointer to the specified sc_reader_t object @@ -837,10 +1054,21 @@ sc_reader_t *sc_ctx_get_reader(sc_context_t *ctx, unsigned int i); * @param ctx pointer to a sc_context_t * @param pcsc_context_handle pointer to the new context_handle to use * @param pcsc_card_handle pointer to the new card_handle to use - * @return SC_SUCCESS on success and an error code otherwise. + * @return SC_SUCCESS or 1 on success and an error code otherwise. + * a return of 1 indicates to call reinit_card_for, as + * the reader has changed. */ int sc_ctx_use_reader(sc_context_t *ctx, void * pcsc_context_handle, void * pcsc_card_handle); +/** + * detect if the given handles are referencing `reader` + * + * 0 -> handles also point to `reader` + * 1 -> handles don't point to `reader`, but to a different reader + */ +int +pcsc_check_reader_handles(sc_context_t *ctx, sc_reader_t *reader, void * pcsc_context_handle, void * pcsc_card_handle); + /** * Returns a pointer to the specified sc_reader_t object * @param ctx OpenSC context @@ -900,7 +1128,7 @@ int sc_disconnect_card(struct sc_card *card); /** * Checks if a card is present in a reader * @param reader Reader structure - * @retval If an error occured, the return value is a (negative) + * @retval If an error occurred, the return value is a (negative) * OpenSC error code. If no card is present, 0 is returned. * Otherwise, a positive value is returned, which is a * combination of flags. The flag SC_READER_CARD_PRESENT is @@ -910,22 +1138,29 @@ int sc_disconnect_card(struct sc_card *card); int sc_detect_card_presence(sc_reader_t *reader); /** - * Waits for an event on readers. Note: only the event is detected, - * there is no update of any card or other info. - * NOTE: Only PC/SC backend implements this. - * @param ctx pointer to a Context structure - * @param event_mask The types of events to wait for; this should - * be ORed from one of the following - * SC_EVENT_CARD_REMOVED - * SC_EVENT_CARD_INSERTED - * SC_EVENT_READER_ATTACHED - * @param event_reader (OUT) the reader on which the event was detected, or NULL if new reader + * Waits for an event on readers. + * + * In case of a reader event (attached/detached), the list of reader is + * adjusted accordingly. This means that a subsequent call to + * `sc_ctx_detect_readers()` is not needed. + * + * @note Only PC/SC backend implements this. An infinite timeout on macOS does + * not detect reader events (use a limited timeout instead if needed). + * + * @param ctx (IN) pointer to a Context structure + * @param event_mask (IN) The types of events to wait for; this should + * be ORed from one of the following: + * - SC_EVENT_CARD_REMOVED + * - SC_EVENT_CARD_INSERTED + * - SC_EVENT_READER_ATTACHED + * - SC_EVENT_READER_DETACHED + * @param event_reader (OUT) the reader on which the event was detected * @param event (OUT) the events that occurred. This is also ORed - * from the SC_EVENT_CARD_* constants listed above. + * from the constants listed above. * @param timeout Amount of millisecs to wait; -1 means forever - * @retval < 0 if an error occured + * @retval < 0 if an error occurred * @retval = 0 if a an event happened - * @retval = 1 if the timeout occured + * @retval = 1 if the timeout occurred */ int sc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_reader_t **event_reader, unsigned int *event, @@ -979,7 +1214,7 @@ size_t sc_get_max_recv_size(const sc_card_t *card); * Takes card limitations into account such as extended length support as well * as the reader's limitation for data transfer. * - * @param card + * @param card card * * @return maximum Nc */ @@ -1009,7 +1244,10 @@ int sc_select_file(struct sc_card *card, const sc_path_t *path, */ int sc_list_files(struct sc_card *card, u8 *buf, size_t buflen); /** - * Read data from a binary EF + * @brief Read data from a binary EF + * + * If `count` exceeds the card's transmission limits, multiple commands are issued. + * * @param card struct sc_card object on which to issue the command * @param idx index within the file with the data to read * @param buf buffer to the read data @@ -1018,37 +1256,43 @@ int sc_list_files(struct sc_card *card, u8 *buf, size_t buflen); * @return number of bytes read or an error code */ int sc_read_binary(struct sc_card *card, unsigned int idx, u8 * buf, - size_t count, unsigned long flags); + size_t count, unsigned long *flags); /** - * Write data to a binary EF + * @brief Write data to a binary EF + * + * If `count` exceeds the card's transmission limits, multiple commands are issued. + * * @param card struct sc_card object on which to issue the command * @param idx index within the file for the data to be written * @param buf buffer with the data * @param count number of bytes to write * @param flags flags for the WRITE BINARY command (currently not used) - * @return number of bytes writen or an error code + * @return number of bytes written or an error code */ int sc_write_binary(struct sc_card *card, unsigned int idx, const u8 * buf, size_t count, unsigned long flags); /** - * Updates the content of a binary EF + * @brief Updates the content of a binary EF + * + * If `count` exceeds the card's transmission limits, multiple commands are issued. + * * @param card struct sc_card object on which to issue the command * @param idx index within the file for the data to be updated * @param buf buffer with the new data * @param count number of bytes to update * @param flags flags for the UPDATE BINARY command (currently not used) - * @return number of bytes writen or an error code + * @return number of bytes written or an error code */ int sc_update_binary(struct sc_card *card, unsigned int idx, const u8 * buf, size_t count, unsigned long flags); /** - * Sets (part of) the content fo an EF to its logical erased state + * Sets (part of) the content of an EF to its logical erased state * @param card struct sc_card object on which to issue the command * @param idx index within the file for the data to be erased * @param count number of bytes to erase * @param flags flags for the ERASE BINARY command (currently not used) - * @return number of bytes writen or an error code + * @return number of bytes erased or an error code */ int sc_erase_binary(struct sc_card *card, unsigned int idx, size_t count, unsigned long flags); @@ -1066,21 +1310,22 @@ int sc_erase_binary(struct sc_card *card, unsigned int idx, * Reads a record from the current (i.e. selected) file. * @param card struct sc_card object on which to issue the command * @param rec_nr SC_READ_RECORD_CURRENT or a record number starting from 1 + * @param idx index within the record with the data to read * @param buf Pointer to a buffer for storing the data * @param count Number of bytes to read * @param flags flags (may contain a short file id of a file to select) * @retval number of bytes read or an error value */ -int sc_read_record(struct sc_card *card, unsigned int rec_nr, u8 * buf, - size_t count, unsigned long flags); +int sc_read_record(struct sc_card *card, unsigned int rec_nr, unsigned int idx, + u8 * buf, size_t count, unsigned long flags); /** * Writes data to a record from the current (i.e. selected) file. * @param card struct sc_card object on which to issue the command * @param rec_nr SC_READ_RECORD_CURRENT or a record number starting from 1 - * @param buf buffer with to the data to be writen + * @param buf buffer with to the data to be written * @param count number of bytes to write * @param flags flags (may contain a short file id of a file to select) - * @retval number of bytes writen or an error value + * @retval number of bytes written or an error value */ int sc_write_record(struct sc_card *card, unsigned int rec_nr, const u8 * buf, size_t count, unsigned long flags); @@ -1090,7 +1335,7 @@ int sc_write_record(struct sc_card *card, unsigned int rec_nr, const u8 * buf, * @param buf buffer with to the data for the new record * @param count length of the data * @param flags flags (may contain a short file id of a file to select) - * @retval number of bytes writen or an error value + * @retval number of bytes written or an error value */ int sc_append_record(struct sc_card *card, const u8 * buf, size_t count, unsigned long flags); @@ -1098,13 +1343,14 @@ int sc_append_record(struct sc_card *card, const u8 * buf, size_t count, * Updates the data of a record from the current (i.e. selected) file. * @param card struct sc_card object on which to issue the command * @param rec_nr SC_READ_RECORD_CURRENT or a record number starting from 1 - * @param buf buffer with to the new data to be writen + * @param idx index within the record with the data to read + * @param buf buffer with to the new data to be written * @param count number of bytes to update * @param flags flags (may contain a short file id of a file to select) - * @retval number of bytes writen or an error value + * @retval number of bytes written or an error value */ -int sc_update_record(struct sc_card *card, unsigned int rec_nr, const u8 * buf, - size_t count, unsigned long flags); +int sc_update_record(struct sc_card *card, unsigned int rec_nr, unsigned int idx, + const u8 * buf, size_t count, unsigned long flags); int sc_delete_record(struct sc_card *card, unsigned int rec_nr); /* get/put data functions */ @@ -1114,7 +1360,7 @@ int sc_put_data(struct sc_card *, unsigned int, const u8 *, size_t); /** * Gets challenge from the card (normally random data). * @param card struct sc_card object on which to issue the command - * @param rndout buffer for the returned random challenge + * @param rndout buffer for the returned random challenge. Note that the buffer may be only partially initialized on error. * @param len length of the challenge * @return SC_SUCCESS on success and an error code otherwise */ @@ -1131,8 +1377,8 @@ int sc_decipher(struct sc_card *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen); int sc_compute_signature(struct sc_card *card, const u8 * data, size_t data_len, u8 * out, size_t outlen); -int sc_verify(struct sc_card *card, unsigned int type, int ref, const u8 *buf, - size_t buflen, int *tries_left); +int sc_verify(struct sc_card *card, unsigned int type, int ref, const u8 *pin, + size_t pinlen, int *tries_left); /** * Resets the security status of the card (i.e. withdraw all granted * access rights). Note: not all card operating systems support a logout @@ -1142,7 +1388,7 @@ int sc_verify(struct sc_card *card, unsigned int type, int ref, const u8 *buf, * doesn't support a logout command and an error code otherwise */ int sc_logout(struct sc_card *card); -int sc_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *, int *tries_left); +int sc_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *); int sc_change_reference_data(struct sc_card *card, unsigned int type, int ref, const u8 *old, size_t oldlen, const u8 *newref, size_t newlen, @@ -1152,6 +1398,11 @@ int sc_reset_retry_counter(struct sc_card *card, unsigned int type, const u8 *newref, size_t newlen); int sc_build_pin(u8 *buf, size_t buflen, struct sc_pin_cmd_pin *pin, int pad); +int sc_encrypt_sym(struct sc_card *card, const u8 *Data, size_t DataLen, + u8 *out, size_t *outlen); + +int sc_decrypt_sym(struct sc_card *card, const u8 *EncryptedData, size_t EncryptedDataLen, + u8 *out, size_t *outlen); /********************************************************************/ /* ISO 7816-9 related functions */ @@ -1183,6 +1434,13 @@ int sc_file_set_type_attr(sc_file_t *file, const u8 *type_attr, int sc_file_set_content(sc_file_t *file, const u8 *content, size_t content_len); +/********************************************************************/ +/* Key wrapping and unwrapping */ +/********************************************************************/ +int sc_unwrap(struct sc_card *card, const u8 * data, + size_t data_len, u8 * out, size_t outlen); +int sc_wrap(struct sc_card *card, const u8 * data, + size_t data_len, u8 * out, size_t outlen); /********************************************************************/ /* sc_path_t handling functions */ @@ -1264,6 +1522,25 @@ const sc_path_t *sc_get_mf_path(void); /********************************************************************/ int sc_hex_to_bin(const char *in, u8 *out, size_t *outlen); +/** + * Converts an u8 array to a string representing the input as hexadecimal, + * human-readable/printable form. It's the inverse function of sc_hex_to_bin. + * + * @param in The u8 array input to be interpreted, may be NULL iff in_len==0 + * @param in_len Less or equal to the amount of bytes available from in + * @param out output buffer offered for the string representation, *MUST NOT* + * be NULL and *MUST* be sufficiently sized, see out_len + * @param out_len *MUST* be at least 1 and state the maximum of bytes available + * within out to be written, including the \0 termination byte + * that will be written unconditionally + * @param separator The character to be used to separate the u8 string + * representations. `0` will suppress separation. + * + * Example: input [0x3f], in_len=1, requiring an out_len>=3, will write to out: + * [0x33, 0x66, 0x00] which reads as "3f" + * Example: input [0x3f, 0x01], in_len=2, separator=':', req. an out_len>=6, + * writes to out: [0x33, 0x66, 0x3A, 0x30, 0x31, 0x00] which reads as "3f:01" + */ int sc_bin_to_hex(const u8 *, size_t, char *, size_t, int separator); size_t sc_right_trim(u8 *buf, size_t len); scconf_block *sc_get_conf_block(sc_context_t *ctx, const char *name1, const char *name2, int priority); @@ -1305,7 +1582,12 @@ int sc_base64_decode(const char *in, u8 *out, size_t outlen); * @param len length of the memory buffer */ void sc_mem_clear(void *ptr, size_t len); -void *sc_mem_alloc_secure(sc_context_t *ctx, size_t len); +void *sc_mem_secure_alloc(size_t len); +void sc_mem_secure_free(void *ptr, size_t len); +#define sc_mem_secure_clear_free(ptr, len) do { \ + sc_mem_clear(ptr, len); \ + sc_mem_secure_free(ptr, len); \ +} while (0); int sc_mem_reverse(unsigned char *buf, size_t len); int sc_get_cache_dir(sc_context_t *ctx, char *buf, size_t bufsize); @@ -1316,16 +1598,23 @@ struct sc_app_info *sc_find_app(struct sc_card *card, struct sc_aid *aid); void sc_free_apps(struct sc_card *card); int sc_parse_ef_atr(struct sc_card *card); void sc_free_ef_atr(struct sc_card *card); +int sc_parse_ef_gdo(struct sc_card *card, + unsigned char *iccsn, size_t *iccsn_len, + unsigned char *chn, size_t *chn_len); int sc_update_dir(struct sc_card *card, sc_app_info_t *app); -void sc_print_cache(struct sc_card *card); - struct sc_algorithm_info * sc_card_find_rsa_alg(struct sc_card *card, - unsigned int key_length); + size_t key_length); struct sc_algorithm_info * sc_card_find_ec_alg(struct sc_card *card, - unsigned int field_length, struct sc_object_id *curve_oid); + size_t field_length, struct sc_object_id *curve_oid); +struct sc_algorithm_info * sc_card_find_eddsa_alg(struct sc_card *card, + size_t field_length, struct sc_object_id *curve_oid); +struct sc_algorithm_info * sc_card_find_xeddsa_alg(struct sc_card *card, + size_t field_length, struct sc_object_id *curve_oid); struct sc_algorithm_info * sc_card_find_gostr3410_alg(struct sc_card *card, - unsigned int key_length); + size_t key_length); +struct sc_algorithm_info * sc_card_find_alg(sc_card_t *card, + unsigned int algorithm, size_t key_length, void *param); scconf_block *sc_match_atr_block(sc_context_t *ctx, struct sc_card_driver *driver, struct sc_atr *atr); /** @@ -1333,7 +1622,19 @@ scconf_block *sc_match_atr_block(sc_context_t *ctx, struct sc_card_driver *drive * @param value pointer to data used for CRC calculation * @param len length of data used for CRC calculation */ -unsigned sc_crc32(unsigned char *value, size_t len); +unsigned sc_crc32(const unsigned char *value, size_t len); + +/** + * Find a given tag in a compact TLV structure + * @param[in] buf input buffer holding the compact TLV structure + * @param[in] len length of the input buffer @buf in bytes + * @param[in] tag compact tag to search for - high nibble: plain tag, low nibble: length. + * If length is 0, only the plain tag is used for searching, + * in any other case, the length must also match. + * @param[out] outlen pointer where the size of the buffer returned is to be stored + * @return pointer to the tag value found within @buf, or NULL if not found/on error + */ +const u8 *sc_compacttlv_find_tag(const u8 *buf, size_t len, u8 tag, size_t *outlen); /** * Used to initialize the @c sc_remote_data structure -- @@ -1342,6 +1643,11 @@ unsigned sc_crc32(unsigned char *value, size_t len); */ void sc_remote_data_init(struct sc_remote_data *rdata); +/** + * Clear ec_params + * @ecp + */ +void sc_clear_ec_params(struct sc_ec_parameters *); /** * Copy and allocate if needed EC parameters data @@ -1368,10 +1674,22 @@ extern const char *sc_get_version(void); extern sc_card_driver_t *sc_get_iso7816_driver(void); -/** +/* + * @brief Request command chaining if needed. + * + * @param[in] card card + * @param[in,out] apdu apdu structure to update + * + * @note Checks if the command payload or the expected response fits into the + * card transceive buffer. It requests command chaining from the lower levels + * if the data length exceeds the buffer size. + */ +void iso7816_fixup_transceive_length(const struct sc_card *card, struct sc_apdu *apdu); + +/** * @brief Read a complete EF by short file identifier. * - * @param[in] card + * @param[in] card card * @param[in] sfid Short file identifier * @param[in,out] ef Where to safe the file. the buffer will be allocated * using \c realloc() and should be set to NULL, if @@ -1386,9 +1704,9 @@ int iso7816_read_binary_sfid(sc_card_t *card, unsigned char sfid, /** * @brief Write a complete EF by short file identifier. * - * @param[in] card + * @param[in] card card * @param[in] sfid Short file identifier - * @param[in] ef Date to write + * @param[in] ef Data to write * @param[in] ef_len Length of \a ef * * @note The appropriate directory must be selected before calling this function. @@ -1396,6 +1714,69 @@ int iso7816_read_binary_sfid(sc_card_t *card, unsigned char sfid, int iso7816_write_binary_sfid(sc_card_t *card, unsigned char sfid, u8 *ef, size_t ef_len); +/** + * @brief Update a EF by short file identifier. + * + * @param[in] card card + * @param[in] sfid Short file identifier + * @param[in] ef Data to write + * @param[in] ef_len Length of \a ef + * + * @note The appropriate directory must be selected before calling this function. + * */ +int iso7816_update_binary_sfid(sc_card_t *card, unsigned char sfid, + u8 *ef, size_t ef_len); + +/** + * @brief Set verification status of a specific PIN to “not verified†+ * + * @param[in] card card + * @param[in] pin_reference PIN reference written to P2 + * + * @note The appropriate directory must be selected before calling this function. + * */ +int iso7816_logout(sc_card_t *card, unsigned char pin_reference); + +/* + * @brief Format PIN APDU for modifiction by card driver + * + * @param[in] card card + * @param[in] apdu apdu structure to update with PIN APDU + * @param[in] data pin command data to set into the APDU + * @param[in] buf buffer for APDU data field + * @param[in] buf_len maximum buffer length + */ +int +iso7816_build_pin_apdu(struct sc_card *card, struct sc_apdu *apdu, + struct sc_pin_cmd_data *data, u8 *buf, size_t buf_len); + +/* + * @brief Send a SELECT AID APDU to the smart card. + * + * This function constructs and transmits a SELECT command using the provided + * AID (Application Identifier) to select an application on the card. + * + * @param[in] card Pointer to the smart card context. + * @param[in] req Pointer to the AID to be selected. + * @param[in] reqlen Length of the AID. + * @param[out] resp Optional. Buffer to receive the response from the card. + * May be NULL if the response is not needed. + * @param[in,out] resplen Optional. On input, the maximum length of the response buffer. + * On output, the actual length of the response. May be NULL + * if resp is NULL or response length is not needed. + */ +int iso7816_select_aid(struct sc_card *card, const u8 *req, + size_t reqlen, u8 *resp, size_t *resplen); + +/** + * Free a buffer returned by OpenSC. + * Use this instead your C libraries free() to free memory allocated by OpenSC. + * For more details see + * + * @param[in] p the buffer + */ +void sc_free(void *p); + #ifdef __cplusplus } #endif diff --git a/src/libopensc/p15card-helper.c b/src/libopensc/p15card-helper.c deleted file mode 100644 index 94f9fe2834..0000000000 --- a/src/libopensc/p15card-helper.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * p15card-helper.c: Utility library to assist in PKCS#15 emulation on Non-filesystem cards - * - * Copyright (C) 2006, Identity Alliance, Thomas Harning - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef ENABLE_OPENSSL /* empty file without openssl */ -#include -#include -#include -#include -#include -#include - -#include "internal.h" -#include "p15card-helper.h" -#include "opensc.h" -#include "types.h" -#include "log.h" -#include "pkcs15.h" - -int sc_pkcs15emu_initialize_objects(sc_pkcs15_card_t *p15card, p15data_items *items) { - sc_card_t* card = p15card->card; - const objdata* objects = items->objects; - int i, r; - if(!objects) return SC_SUCCESS; - for (i = 0; objects[i].label; i++) { - struct sc_pkcs15_data_info obj_info; - struct sc_pkcs15_object obj_obj; - - memset(&obj_info, 0, sizeof(obj_info)); - memset(&obj_obj, 0, sizeof(obj_obj)); - sc_pkcs15_format_id(objects[i].id, &obj_info.id); - sc_format_path(objects[i].path, &obj_info.path); - strncpy(obj_info.app_label, objects[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); - r = sc_format_oid(&obj_info.app_oid, objects[i].aoid); - if (r != SC_SUCCESS) - return r; - - strncpy(obj_obj.label, objects[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); - obj_obj.flags = objects[i].obj_flags; - - r = sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_DATA_OBJECT, - &obj_obj, &obj_info); - if (r < 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); - } - return SC_SUCCESS; -} - -static const prdata* get_prkey_by_cert(p15data_items* items, const cdata* cert) { - const prdata* keys; - if(!items->private_keys) - return NULL; - for(keys = items->private_keys; keys->id; keys++) { - if(0 == strcmp(cert->id, keys->id)) - return keys; - } - return NULL; -} - -static int add_private_key(sc_pkcs15_card_t *p15card, const prdata* key, int usage, int modulus_length) { - struct sc_pkcs15_prkey_info prkey_info; - struct sc_pkcs15_object prkey_obj; - - memset(&prkey_info, 0, sizeof(prkey_info)); - memset(&prkey_obj, 0, sizeof(prkey_obj)); - - sc_pkcs15_format_id(key->id, &prkey_info.id); - - prkey_info.native = 1; - prkey_info.key_reference = key->ref; - - if(!modulus_length) modulus_length = key->modulus_len; - prkey_info.modulus_length= modulus_length; - - sc_format_path(key->path, &prkey_info.path); - - strncpy(prkey_obj.label, key->label, SC_PKCS15_MAX_LABEL_SIZE - 1); - - prkey_obj.flags = key->obj_flags; - - /* Setup key usage */ - if(!usage) usage = key->usage; - prkey_info.usage = usage; - - if (key->auth_id) - sc_pkcs15_format_id(key->auth_id, &prkey_obj.auth_id); - - return sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); -} - -static int add_public_key(sc_pkcs15_card_t *p15card, const pubdata *key, int usage, int modulus_length) { - struct sc_pkcs15_pubkey_info pubkey_info; - struct sc_pkcs15_object pubkey_obj; - - memset(&pubkey_info, 0, sizeof(pubkey_info)); - memset(&pubkey_obj, 0, sizeof(pubkey_obj)); - - sc_pkcs15_format_id(key->id, &pubkey_info.id); - if(!usage) usage = key->usage; - pubkey_info.usage = usage; - pubkey_info.native = 1; - pubkey_info.key_reference = key->ref; - if(!modulus_length) modulus_length = key->modulus_len; - pubkey_info.modulus_length= modulus_length; - /* we really don't know how many bits or module length, - * we will assume 1024 for now - */ - sc_format_path(key->path, &pubkey_info.path); - - strncpy(pubkey_obj.label, key->label, SC_PKCS15_MAX_LABEL_SIZE - 1); - - pubkey_obj.flags = key->obj_flags; - - if (key->auth_id) - sc_pkcs15_format_id(key->auth_id, &pubkey_obj.auth_id); - - return sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); -} - -/* int default_cert_handle(sc_pkcs15_card_t *p15card, p15data_items* items, cdata* cert, u8* data, size_t length) { */ -CERT_HANDLE_FUNCTION(default_cert_handle) { - /* Certificate data exists, parse it */ - int r; - X509 *cert_data = NULL; - EVP_PKEY *pkey = NULL; - RSA * rsa = NULL; - int certtype = 0; - int modulus_len = 0; - const prdata* key = get_prkey_by_cert(items, cert); - if(!key) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Error: No key for this certificate"); - return SC_ERROR_INTERNAL; - } - - if(!d2i_X509(&cert_data, (const u8**)&data, length)) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Error converting certificate"); - return SC_ERROR_INTERNAL; - } - - pkey = X509_get_pubkey(cert_data); - - if(pkey == NULL) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Error: no public key associated with the certificate"); - r = SC_ERROR_INTERNAL; - goto err; - } - - certtype = X509_certificate_type(cert_data, pkey); - if(! (EVP_PK_RSA & certtype)) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Error: certificate is not for an RSA key"); - r = SC_ERROR_INTERNAL; - goto err; - } - rsa = EVP_PKEY_get0_RSA(pkey); - if( rsa == NULL) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Error: no modulus associated with the certificate"); - r = SC_ERROR_INTERNAL; - goto err; - } - - modulus_len = RSA_bits(rsa); - - /* printf("Key Size: %d bits\n\n", modulus_len); */ - /* cached_cert->modulusLength = modulus_len; */ - - if(key->label) { - int usage = 0; - if (certtype & EVP_PKT_SIGN) { - usage |= SC_PKCS15_PRKEY_USAGE_SIGN; - usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; - } - - if (certtype & EVP_PKT_ENC) { - usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT; - usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT; - } - if (certtype & EVP_PKT_EXCH) { - usage |= SC_PKCS15_PRKEY_USAGE_WRAP; - usage |= SC_PKCS15_PRKEY_USAGE_UNWRAP; - } - r = add_private_key(p15card, key, usage, modulus_len); - if (r < 0) - goto err; - } - r = SC_SUCCESS; -err: - if(pkey) { - EVP_PKEY_free(pkey); - pkey = NULL; - } - if(cert_data) { - X509_free(cert_data); - cert_data = NULL; - } - SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - -int sc_pkcs15emu_initialize_certificates(sc_pkcs15_card_t *p15card, p15data_items* items) { - /* set certs */ - sc_card_t* card = p15card->card; - const cdata* certs = items->certs; - int onFailResume = items->cert_continue; - int i, r; - if(!certs) return SC_SUCCESS; - for (i = 0; certs[i].label; i++) { - struct sc_pkcs15_cert_info cert_info; - struct sc_pkcs15_object cert_obj; - - memset(&cert_info, 0, sizeof(cert_info)); - memset(&cert_obj, 0, sizeof(cert_obj)); - - sc_pkcs15_format_id(certs[i].id, &cert_info.id); - cert_info.authority = certs[i].authority; - sc_format_path(certs[i].path, &cert_info.path); - - strncpy(cert_obj.label, certs[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); - cert_obj.flags = certs[i].obj_flags; - - if(items->cert_load) { - u8* cert_buffer = NULL; - size_t cert_length = 0; - int should_free = 0; - if(SC_SUCCESS != sc_select_file(card, &cert_info.path, NULL)) { - if(onFailResume) - continue; - else - break; - } - if(SC_SUCCESS != (r = items->cert_load(card, &cert_buffer, &cert_length, &should_free))) { - if(onFailResume) - continue; - else - break; - } - /* Handle cert */ - /* If no cert handler, add.. if cert handler succeeds.. add */ - if(!items->cert_handle || SC_SUCCESS == (r = items->cert_handle(p15card, items, &certs[i], cert_buffer, cert_length))) { - r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); - } - if(should_free) - free(cert_buffer); - if(SC_SUCCESS != r) { - if(onFailResume) - continue; - else - break; - } - } else { /* Automatically add */ - if(SC_SUCCESS != sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info)) { - if(onFailResume) - continue; - else - break; - } - } - } - return SC_SUCCESS; -} - -int sc_pkcs15emu_initialize_pins(sc_pkcs15_card_t *p15card, p15data_items* items) { - /* set pins */ - int i,r; - const pindata* pins = items->pins; - if(!pins) return SC_SUCCESS; - for (i = 0; pins[i].label; i++) { - struct sc_pkcs15_auth_info pin_info; - struct sc_pkcs15_object pin_obj; - - memset(&pin_info, 0, sizeof(pin_info)); - memset(&pin_obj, 0, sizeof(pin_obj)); - - pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; - sc_pkcs15_format_id(pins[i].id, &pin_info.auth_id); - - pin_info.attrs.pin.reference = pins[i].ref; - pin_info.attrs.pin.flags = pins[i].flags; - pin_info.attrs.pin.type = pins[i].type; - pin_info.attrs.pin.min_length = pins[i].minlen; - pin_info.attrs.pin.stored_length = pins[i].storedlen; - pin_info.attrs.pin.max_length = pins[i].maxlen; - pin_info.attrs.pin.pad_char = pins[i].pad_char; - - sc_format_path(pins[i].path, &pin_info.path); - pin_info.tries_left = -1; - - strncpy(pin_obj.label, pins[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); - pin_obj.flags = pins[i].obj_flags; - - if(0 > (r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info))) - SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); - } - return SC_SUCCESS; -} - -int sc_pkcs15emu_initialize_private_keys(sc_pkcs15_card_t *p15card, p15data_items* items) { - const prdata *prkeys = items->private_keys; - int i, r; - if(!prkeys) return SC_SUCCESS; - /* set private keys */ - for (i = 0; prkeys[i].label; i++) { - r = add_private_key(p15card, &prkeys[i], 0, 0); - if (r < 0) - SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); - } - return SC_SUCCESS; -} - -int sc_pkcs15emu_initialize_public_keys(sc_pkcs15_card_t *p15card, p15data_items *items) { - const pubdata *keys = items->public_keys; - int i, r; - if(!keys) return SC_SUCCESS; - /* set public keys */ - for (i = 0; keys[i].label; i++) { - r = add_public_key(p15card, &keys[i], 0, 0); - if (r < 0) - SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); - } - return SC_SUCCESS; - -} - -int sc_pkcs15emu_initialize_all(sc_pkcs15_card_t *p15card, p15data_items* items) { - int r; - if(SC_SUCCESS != (r = sc_pkcs15emu_initialize_objects(p15card, items))) - return r; - if(SC_SUCCESS != (r = sc_pkcs15emu_initialize_certificates(p15card, items))) - return r; - if(SC_SUCCESS != (r = sc_pkcs15emu_initialize_pins(p15card, items))) - return r; - - if(items->forced_private && (SC_SUCCESS != (r = sc_pkcs15emu_initialize_private_keys(p15card, items)))) - return r; - if(items->forced_public && (SC_SUCCESS != (r = sc_pkcs15emu_initialize_public_keys(p15card, items)))) - return r; - return SC_SUCCESS; -} - -#endif /* ENABLE_OPENSSL */ diff --git a/src/libopensc/p15card-helper.h b/src/libopensc/p15card-helper.h deleted file mode 100644 index ce3ea7291c..0000000000 --- a/src/libopensc/p15card-helper.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * p15card-helper.h: Utility library to assist in PKCS#15 emulation on Non-filesystem cards - * - * Copyright (C) 2006, Identity Alliance, Thomas Harning - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef P15CARD_HELPER_H -#define P15CARD_HELPER_H - -#include "libopensc/pkcs15.h" - - -#define USAGE_NONREP SC_PKCS15_PRKEY_USAGE_NONREPUDIATION | \ - SC_PKCS15_PRKEY_USAGE_SIGN -#define USAGE_DS SC_PKCS15_PRKEY_USAGE_SIGN -#define USAGE_CRYPTO SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ - SC_PKCS15_PRKEY_USAGE_DECRYPT | \ - SC_PKCS15_PRKEY_USAGE_WRAP | \ - SC_PKCS15_PRKEY_USAGE_UNWRAP -#define USAGE_KE SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ - SC_PKCS15_PRKEY_USAGE_DECRYPT | \ - SC_PKCS15_PRKEY_USAGE_WRAP | \ - SC_PKCS15_PRKEY_USAGE_UNWRAP -#define USAGE_AUT SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ - SC_PKCS15_PRKEY_USAGE_DECRYPT | \ - SC_PKCS15_PRKEY_USAGE_WRAP | \ - SC_PKCS15_PRKEY_USAGE_UNWRAP | \ - SC_PKCS15_PRKEY_USAGE_SIGN - - -typedef struct objdata_st { - const char *id; - const char *label; - const char *aoid; - int authority; - const char *path; - int obj_flags; -} objdata; - -typedef struct cdata_st { - const char *id; - const char *label; - int authority; - const char *path; - int obj_flags; -} cdata; - -typedef struct pdata_st { - const char *id; - const char *label; - const char *path; - int ref; - int type; - unsigned int maxlen; - unsigned int minlen; - unsigned int storedlen; - int flags; - int tries_left; - const char pad_char; - int obj_flags; -} pindata; - -typedef struct pubdata_st { - const char *id; - const char *label; - unsigned int modulus_len; - int usage; - const char *path; - int ref; - const char *auth_id; - int obj_flags; -} pubdata; - -typedef struct prdata_st { - const char *id; - const char *label; - unsigned int modulus_len; - int usage; - const char *path; - int ref; - const char *auth_id; - int obj_flags; -} prdata; - -typedef struct keyinfo_st { - int fileid; - sc_pkcs15_id_t id; - unsigned int modulus_len; - u8 modulus[1024/8]; -} keyinfo; - -typedef struct p15data_items p15data_items; - -typedef int (*cert_load_function)(sc_card_t *card, u8** data, size_t* length, int* shouldFree); -#define CERT_LOAD_FUNCTION(x) int x(sc_card_t *card, u8** data, size_t*length, int *shouldFree) -typedef int (*cert_handle_function)(sc_pkcs15_card_t *p15card, p15data_items* items, const cdata* cert, u8* data, size_t length); -#define CERT_HANDLE_FUNCTION(x) int x(sc_pkcs15_card_t *p15card, p15data_items* items, const cdata* cert, u8* data, size_t length) - -struct p15data_items { - const objdata* objects; - const cdata* certs; - const pindata* pins; - const pubdata* public_keys; - const prdata* private_keys; - - cert_load_function cert_load; - cert_handle_function cert_handle; - int cert_continue; /* Continue after cert failure */ - int forced_private; /* Should add all private keys w/o cert-management */ - int forced_public; /* Should add public keys (generally not needed..) */ -}; - -CERT_HANDLE_FUNCTION(default_cert_handle); - -int sc_pkcs15emu_initialize_objects(sc_pkcs15_card_t *p15card, p15data_items* items); -int sc_pkcs15emu_initialize_certificates(sc_pkcs15_card_t *p15card, p15data_items* items); -int sc_pkcs15emu_initialize_pins(sc_pkcs15_card_t *p15card, p15data_items *items); -int sc_pkcs15emu_initialize_private_keys(sc_pkcs15_card_t *p15card, p15data_items *items); -int sc_pkcs15emu_initialize_public_keys(sc_pkcs15_card_t *p15card, p15data_items *items); -int sc_pkcs15emu_initialize_all(sc_pkcs15_card_t *p15card, p15data_items *items); - -#endif - diff --git a/src/libopensc/pace.h b/src/libopensc/pace.h index bec5022837..032d5b1152 100644 --- a/src/libopensc/pace.h +++ b/src/libopensc/pace.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PACE_H diff --git a/src/libopensc/padding.c b/src/libopensc/padding.c index 25ee300696..e56ec99fcb 100644 --- a/src/libopensc/padding.c +++ b/src/libopensc/padding.c @@ -16,20 +16,29 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif +#ifdef ENABLE_OPENSSL +#include +#include +#include +#endif + #include #include +#include "common/constant-time.h" #include "internal.h" - +#include "pkcs11/pkcs11.h" /* TODO doxygen comments */ +#define SC_PKCS1_PADDING_MIN_SIZE 11 + /* * Prefixes for pkcs-v1 signatures */ @@ -96,7 +105,7 @@ static int sc_pkcs1_add_01_padding(const u8 *in, size_t in_len, memmove(out + i, in, in_len); *out++ = 0x00; *out++ = 0x01; - + memset(out, 0xFF, i - 3); out += i - 3; *out = 0x00; @@ -137,46 +146,234 @@ sc_pkcs1_strip_01_padding(struct sc_context *ctx, const u8 *in_dat, size_t in_le return SC_SUCCESS; } - -/* remove pkcs1 BT02 padding (adding BT02 padding is currently not - * needed/implemented) */ +/* Remove pkcs1 BT02 padding (adding BT02 padding is currently not + * needed/implemented) in constant-time. + * Original source: https://github.com/openssl/openssl/blob/9890cc42daff5e2d0cad01ac4bf78c391f599a6e/crypto/rsa/rsa_pk1.c#L171 */ int -sc_pkcs1_strip_02_padding(sc_context_t *ctx, const u8 *data, size_t len, u8 *out, size_t *out_len) +sc_pkcs1_strip_02_padding_constant_time(sc_context_t *ctx, unsigned int n, const u8 *data, unsigned int data_len, u8 *out, unsigned int *out_len) { - unsigned int n = 0; + unsigned int i = 0; + u8 *msg, *msg_orig = NULL; + unsigned int good, found_zero_byte, mask, tmp_outlen; + unsigned int zero_index = 0, msg_index, mlen = -1, len = 0; + LOG_FUNC_CALLED(ctx); + + if (data == NULL || data_len <= 0 || data_len > n || + n < SC_PKCS1_PADDING_MIN_SIZE || out_len == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); + + tmp_outlen = *out_len; + msg = msg_orig = calloc(n, sizeof(u8)); + if (msg == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); + + /* + * We can not check length of input data straight away and still we need to read + * from input even when the input is not as long as needed to keep the time constant. + * If data has wrong size, it is padded by zeroes from left and the following checks + * do not pass. + */ + len = data_len; + for (data += len, msg += n, i = 0; i < n; i++) { + mask = ~constant_time_is_zero(len); + len -= 1 & mask; + data -= 1 & mask; + *--msg = *data & mask; + } + // check first byte to be 0x00 + good = constant_time_is_zero(msg[0]); + // check second byte to be 0x02 + good &= constant_time_eq(msg[1], 2); + + // find zero byte after random data in padding + found_zero_byte = 0; + for (i = 2; i < n; i++) { + unsigned int equals0 = constant_time_is_zero(msg[i]); + zero_index = constant_time_select(~found_zero_byte & equals0, i, zero_index); + found_zero_byte |= equals0; + } + + // zero_index stands for index of last found zero + good &= constant_time_ge(zero_index, 2 + 8); + + // start of the actual message in data + msg_index = zero_index + 1; + + // length of message + mlen = data_len - msg_index; + + // check that message fits into out buffer + good &= constant_time_ge(tmp_outlen, mlen); + + // move the result in-place by |num|-SC_PKCS1_PADDING_MIN_SIZE-|mlen| bytes to the left. + tmp_outlen = constant_time_select(constant_time_lt(n - SC_PKCS1_PADDING_MIN_SIZE, tmp_outlen), + n - SC_PKCS1_PADDING_MIN_SIZE, tmp_outlen); + for (msg_index = 1; msg_index < n - SC_PKCS1_PADDING_MIN_SIZE; msg_index <<= 1) { + mask = ~constant_time_eq(msg_index & (n - SC_PKCS1_PADDING_MIN_SIZE - mlen), 0); + for (i = SC_PKCS1_PADDING_MIN_SIZE; i < n - msg_index; i++) + msg[i] = constant_time_select_8(mask, msg[i + msg_index], msg[i]); + } + // move message into out buffer, if good + for (i = 0; i < tmp_outlen; i++) { + unsigned int msg_index; + // when out is longer than message in data, use some bogus index in msg + mask = good & constant_time_lt(i, mlen); + msg_index = constant_time_select(mask, i + SC_PKCS1_PADDING_MIN_SIZE, 0); // to now overflow msg buffer + out[i] = constant_time_select_8(mask, msg[msg_index], out[i]); + } + + *out_len = constant_time_select(good, mlen, *out_len); + free(msg_orig); + return constant_time_select(good, mlen, SC_ERROR_WRONG_PADDING); +} + +#ifdef ENABLE_OPENSSL +static int mgf1(u8 *mask, size_t len, u8 *seed, size_t seedLen, const EVP_MD *dgst) +{ + int i; + size_t outlen = 0; + u8 cnt[4]; + EVP_MD_CTX *md_ctx = NULL; + int mdlen; + u8 md[EVP_MAX_MD_SIZE]; + int rv = 1; + + if (!(md_ctx = EVP_MD_CTX_new())) + goto out; + + mdlen = EVP_MD_size(dgst); + if (mdlen < 0) + goto out; + + for (i = 0; outlen < len; i++) { + cnt[0] = (u8) ((i >> 24) & 255); + cnt[1] = (u8) ((i >> 16) & 255); + cnt[2] = (u8) ((i >> 8) & 255); + cnt[3] = (u8) ((i >> 0) & 255); + if (!EVP_DigestInit_ex(md_ctx, dgst, NULL) + || !EVP_DigestUpdate(md_ctx, seed, seedLen) + || !EVP_DigestUpdate(md_ctx, cnt, 4)) + goto out; + if (outlen + mdlen <= len) { + if (!EVP_DigestFinal_ex(md_ctx, mask + outlen, NULL)) + goto out; + outlen += mdlen; + } else { + if (!EVP_DigestFinal_ex(md_ctx, md, NULL)) + goto out; + memcpy(mask + outlen, md, len - outlen); + outlen = len; + } + } + rv = 0; + out: + OPENSSL_cleanse(md, sizeof(md)); + if (md_ctx) + EVP_MD_CTX_free(md_ctx); + return rv; +} + +/* forward declarations */ +static EVP_MD *mgf1_flag2md(sc_context_t *ctx, unsigned long mgf1); +static EVP_MD *hash_flag2md(sc_context_t *ctx, unsigned long hash); + +/* check/remove OAEP - RFC 8017 padding */ +int sc_pkcs1_strip_oaep_padding(sc_context_t *ctx, u8 *data, size_t len, unsigned long flags, uint8_t *param, size_t paramlen) +{ + size_t i,j; + size_t mdlen, dblen; + u8 seed[EVP_MAX_MD_SIZE]; + EVP_MD *mgf1_md = NULL, *hash_md = NULL; + u8 db[512]; /* up to RSA 4096 */ + u8 label[EVP_MAX_MD_SIZE]; + EVP_MD_CTX *md_ctx; + unsigned int hash_len = 0; LOG_FUNC_CALLED(ctx); - if (data == NULL || len < 3) + if (data == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); - /* skip leading zero byte */ - if (*data == 0) { - data++; - len--; + /* https://www.rfc-editor.org/rfc/pdfrfc/rfc8017.txt.pdf, page 26, 3.a. */ + hash_md = hash_flag2md(ctx, flags); + if (!hash_md) + return SC_ERROR_NOT_SUPPORTED; + + memset(label, 0, sizeof(label)); + if ((md_ctx = EVP_MD_CTX_new())) { + if (!EVP_DigestInit_ex(md_ctx, hash_md, NULL) + || !EVP_DigestUpdate(md_ctx, param, paramlen) + || !EVP_DigestFinal_ex(md_ctx, label, &hash_len)) { + sc_log_openssl(ctx); + hash_len = 0; + } + EVP_MD_CTX_free(md_ctx); + } + sc_evp_md_free(hash_md); + hash_md = NULL; + if (!hash_len) { + LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); } - if (data[0] != 0x02) + + mgf1_md = mgf1_flag2md(ctx, flags); + if (!mgf1_md) + return SC_ERROR_NOT_SUPPORTED; + + mdlen = EVP_MD_size(mgf1_md); + + if (len < 2 * mdlen + 2) { + sc_evp_md_free(mgf1_md); LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_PADDING); - /* skip over padding bytes */ - for (n = 1; n < len && data[n]; n++) - ; - /* Must be at least 8 pad bytes */ - if (n >= len || n < 9) + } + + if (*data != 0) { + sc_evp_md_free(mgf1_md); LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_PADDING); - n++; - if (out == NULL) - /* just check the padding */ - LOG_FUNC_RETURN(ctx, SC_SUCCESS); + } - /* Now move decrypted contents to head of buffer */ - if (*out_len < len - n) + dblen = len - 1 - mdlen; + if (dblen > sizeof(db)) { + sc_evp_md_free(mgf1_md); LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); - *out_len = len - n; - memmove(out, data + n, *out_len); + } + + if (mgf1(seed, mdlen, data + mdlen + 1, dblen, mgf1_md)) { + sc_log_openssl(ctx); + sc_evp_md_free(mgf1_md); + LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); + } + for (i = 0; i < mdlen; i++) + seed[i] ^= data[i + 1]; - sc_log(ctx, "stripped output(%"SC_FORMAT_LEN_SIZE_T"u): %s", len - n, - sc_dump_hex(out, len - n)); - LOG_FUNC_RETURN(ctx, len - n); + if (mgf1(db, dblen, seed, mdlen, mgf1_md)) { + sc_log_openssl(ctx); + sc_evp_md_free(mgf1_md); + LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); + } + sc_evp_md_free(mgf1_md); + for (i = 0; i < dblen; i++) { + db[i] ^= data[i + mdlen + 1]; + /* clear lHash' if same as lHash */ + if (i < hash_len) + db[i] ^= label[i]; + } + /* if the padding is correct, it is a concatenation: + * 00...00 || 01 || plaintext + * check padding but do not leak information about error: + */ + for (j = 0, i = 0; i < dblen;) { + j += db[i++] + 1; + if (i > mdlen) { + if (j == i + 1) { + /* OK correct padding found */ + len = dblen - i; + memcpy(data, db + i, len); + LOG_FUNC_RETURN(ctx, (int)len); + } + } + } + LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_PADDING); } +#endif /* add/remove DigestInfo prefix */ static int sc_pkcs1_add_digest_info_prefix(unsigned int algorithm, @@ -213,7 +410,7 @@ int sc_pkcs1_strip_digest_info_prefix(unsigned int *algorithm, size_t hdr_len = digest_info_prefix[i].hdr_len, hash_len = digest_info_prefix[i].hash_len; const u8 *hdr = digest_info_prefix[i].hdr; - + if (in_len == (hdr_len + hash_len) && !memcmp(in_dat, hdr, hdr_len)) { if (algorithm) @@ -231,22 +428,209 @@ int sc_pkcs1_strip_digest_info_prefix(unsigned int *algorithm, return SC_ERROR_INTERNAL; } +#ifdef ENABLE_OPENSSL + +static EVP_MD* hash_flag2md(sc_context_t *ctx, unsigned long hash) +{ + switch (hash & SC_ALGORITHM_RSA_HASHES) { + case SC_ALGORITHM_RSA_HASH_SHA1: + return sc_evp_md(ctx, "SHA1"); + case SC_ALGORITHM_RSA_HASH_SHA224: + return sc_evp_md(ctx, "SHA224"); + case SC_ALGORITHM_RSA_HASH_SHA256: + return sc_evp_md(ctx, "SHA256"); + case SC_ALGORITHM_RSA_HASH_SHA384: + return sc_evp_md(ctx, "SHA384"); + case SC_ALGORITHM_RSA_HASH_SHA512: + return sc_evp_md(ctx, "SHA512"); + default: + return NULL; + } +} + +static EVP_MD* mgf1_flag2md(sc_context_t *ctx, unsigned long mgf1) +{ + switch (mgf1 & SC_ALGORITHM_MGF1_HASHES) { + case SC_ALGORITHM_MGF1_SHA1: + return sc_evp_md(ctx, "SHA1"); + case SC_ALGORITHM_MGF1_SHA224: + return sc_evp_md(ctx, "SHA224"); + case SC_ALGORITHM_MGF1_SHA256: + return sc_evp_md(ctx, "SHA256"); + case SC_ALGORITHM_MGF1_SHA384: + return sc_evp_md(ctx, "SHA384"); + case SC_ALGORITHM_MGF1_SHA512: + return sc_evp_md(ctx, "SHA512"); + default: + return NULL; + } +} + +/* large enough up to RSA 4096 */ +#define PSS_MAX_SALT_SIZE 512 +/* add PKCS#1 v2.0 PSS padding */ +static int sc_pkcs1_add_pss_padding(sc_context_t *scctx, unsigned int hash, unsigned int mgf1_hash, + const u8 *in, size_t in_len, u8 *out, size_t *out_len, size_t mod_bits, size_t sLen) +{ + /* hLen = sLen in our case */ + int rv = SC_ERROR_INTERNAL, j, hlen; + size_t dblen, plen, round, mgf_rounds, i; + int mgf1_hlen; + EVP_MD* md = NULL, *mgf1_md = NULL; + EVP_MD_CTX* ctx = NULL; + u8 buf[8]; + u8 salt[PSS_MAX_SALT_SIZE], mask[EVP_MAX_MD_SIZE]; + size_t mod_length = BYTES4BITS(mod_bits); + + if (*out_len < mod_length) + return SC_ERROR_BUFFER_TOO_SMALL; + + md = hash_flag2md(scctx, hash); + if (md == NULL) { + sc_log_openssl(scctx); + return SC_ERROR_NOT_SUPPORTED; + } + hlen = EVP_MD_size(md); + dblen = mod_length - hlen - 1; /* emLen - hLen - 1 */ + plen = mod_length - sLen - hlen - 1; + if (in_len != (unsigned)hlen) { + sc_evp_md_free(md); + return SC_ERROR_INVALID_ARGUMENTS; + } + if (sLen + (unsigned)hlen + 2 > mod_length) { + /* RSA key too small for chosen hash (1296 bits or higher needed for + * signing SHA-512 hashes) */ + sc_evp_md_free(md); + return SC_ERROR_NOT_SUPPORTED; + } + if (sLen > PSS_MAX_SALT_SIZE) { + sc_evp_md_free(md); + return SC_ERROR_INVALID_ARGUMENTS; + } + if (RAND_bytes(salt, (unsigned)sLen) != 1) { + sc_log_openssl(scctx); + sc_evp_md_free(md); + return SC_ERROR_INTERNAL; + } + + /* Hash M' to create H */ + if (!(ctx = EVP_MD_CTX_create())) + goto done; + memset(buf, 0x00, 8); + if (EVP_DigestInit_ex(ctx, md, NULL) != 1 || + EVP_DigestUpdate(ctx, buf, 8) != 1 || + EVP_DigestUpdate(ctx, in, hlen) != 1 || /* mHash */ + EVP_DigestUpdate(ctx, salt, sLen) != 1) { + sc_log_openssl(scctx); + goto done; + } + + /* Construct padding2, salt, H, and BC in the output block */ + /* DB = PS || 0x01 || salt */ + memset(out, 0x00, plen - 1); /* emLen - sLen - hLen - 2 */ + out[plen - 1] = 0x01; + memcpy(out + plen, salt, sLen); + if (EVP_DigestFinal_ex(ctx, out + dblen, NULL) != 1) { /* H */ + sc_log_openssl(scctx); + goto done; + } + out[dblen + hlen] = 0xBC; + /* EM = DB* || H || 0xbc + * *the first part is masked later */ + + /* Construct the DB mask block by block and XOR it in. */ + mgf1_md = mgf1_flag2md(scctx, mgf1_hash); + if (mgf1_md == NULL) { + sc_log_openssl(scctx); + goto done; + } + mgf1_hlen = EVP_MD_size(mgf1_md); + + mgf_rounds = (dblen + mgf1_hlen - 1) / mgf1_hlen; /* round up */ + for (round = 0; round < mgf_rounds; ++round) { + buf[0] = (round&0xFF000000U) >> 24; + buf[1] = (round&0x00FF0000U) >> 16; + buf[2] = (round&0x0000FF00U) >> 8; + buf[3] = (round&0x000000FFU); + if (EVP_DigestInit_ex(ctx, mgf1_md, NULL) != 1 || + EVP_DigestUpdate(ctx, out + dblen, hlen) != 1 || /* H (Z parameter of MGF1) */ + EVP_DigestUpdate(ctx, buf, 4) != 1 || /* C */ + EVP_DigestFinal_ex(ctx, mask, NULL) != 1) { + sc_log_openssl(scctx); + goto done; + } + /* this is no longer part of the MGF1, but actually + * XORing mask with DB to create maskedDB inplace */ + for (i = round * mgf1_hlen, j = 0; i < dblen && j < mgf1_hlen; ++i, ++j) { + out[i] ^= mask[j]; + } + } + + /* Set leftmost N bits in leftmost octet in maskedDB to zero + * to make sure the result is smaller than the modulus ( +1) + */ + out[0] &= (0xff >> (8 * mod_length - mod_bits + 1)); + + *out_len = mod_length; + rv = SC_SUCCESS; + +done: + OPENSSL_cleanse(salt, sizeof(salt)); + OPENSSL_cleanse(mask, sizeof(mask)); + sc_evp_md_free(md); + sc_evp_md_free(mgf1_md); + if (ctx) { + EVP_MD_CTX_destroy(ctx); + } + return rv; +} + +static int hash_len2algo(size_t hash_len) +{ + switch (hash_len) { + case SHA_DIGEST_LENGTH: + return SC_ALGORITHM_RSA_HASH_SHA1; + case SHA224_DIGEST_LENGTH: + return SC_ALGORITHM_RSA_HASH_SHA224; + case SHA256_DIGEST_LENGTH: + return SC_ALGORITHM_RSA_HASH_SHA256; + case SHA384_DIGEST_LENGTH: + return SC_ALGORITHM_RSA_HASH_SHA384; + case SHA512_DIGEST_LENGTH: + return SC_ALGORITHM_RSA_HASH_SHA512; + } + /* Should never happen -- the mechanism and data should be already + * verified to match one of the above. If not, we will fail later + */ + return SC_ALGORITHM_RSA_HASH_NONE; +} +#endif + /* general PKCS#1 encoding function */ int sc_pkcs1_encode(sc_context_t *ctx, unsigned long flags, - const u8 *in, size_t in_len, u8 *out, size_t *out_len, size_t mod_len) + const u8 *in, size_t in_len, u8 *out, size_t *out_len, size_t mod_bits, void *pMechanism) { int rv, i; size_t tmp_len = *out_len; const u8 *tmp = in; unsigned int hash_algo, pad_algo; + size_t mod_len = BYTES4BITS(mod_bits); +#ifdef ENABLE_OPENSSL + size_t sLen; + EVP_MD* md = NULL; + unsigned int mgf1_hash; +#endif LOG_FUNC_CALLED(ctx); - hash_algo = flags & (SC_ALGORITHM_RSA_HASHES | SC_ALGORITHM_RSA_HASH_NONE); + hash_algo = flags & SC_ALGORITHM_RSA_HASHES; pad_algo = flags & SC_ALGORITHM_RSA_PADS; + if (pad_algo == 0) + pad_algo = SC_ALGORITHM_RSA_PAD_NONE; sc_log(ctx, "hash algorithm 0x%X, pad algorithm 0x%X", hash_algo, pad_algo); - if (hash_algo != SC_ALGORITHM_RSA_HASH_NONE) { + if ((pad_algo == SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 || pad_algo == SC_ALGORITHM_RSA_PAD_NONE) && + hash_algo != SC_ALGORITHM_RSA_HASH_NONE) { i = sc_pkcs1_add_digest_info_prefix(hash_algo, in, in_len, out, &tmp_len); if (i != SC_SUCCESS) { sc_log(ctx, "Unable to add digest info 0x%x", hash_algo); @@ -264,14 +648,49 @@ int sc_pkcs1_encode(sc_context_t *ctx, unsigned long flags, memcpy(out, tmp, tmp_len); *out_len = tmp_len; LOG_FUNC_RETURN(ctx, SC_SUCCESS); - case SC_ALGORITHM_RSA_PAD_PKCS1: + case SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01: /* add pkcs1 bt01 padding */ rv = sc_pkcs1_add_01_padding(tmp, tmp_len, out, out_len, mod_len); LOG_FUNC_RETURN(ctx, rv); + case SC_ALGORITHM_RSA_PAD_PSS: + /* add PSS padding */ +#ifdef ENABLE_OPENSSL + mgf1_hash = flags & SC_ALGORITHM_MGF1_HASHES; + if (hash_algo == SC_ALGORITHM_RSA_HASH_NONE) { + /* this is generic RSA_PKCS1_PSS mechanism with hash + * already done outside of the module. The parameters + * were already checked so we need to adjust the hash + * algorithm to do the padding with the correct hash + * function. + */ + hash_algo = hash_len2algo(tmp_len); + } + /* sLen is by default same as hash length */ + if (!(md = hash_flag2md(ctx, hash_algo))) { + sc_log_openssl(ctx); + return SC_ERROR_NOT_SUPPORTED; + } + sLen = EVP_MD_size(md); + sc_evp_md_free(md); + /* if application provide sLen, use it */ + if (pMechanism != NULL) { + CK_MECHANISM *mech = (CK_MECHANISM *)pMechanism; + CK_RSA_PKCS_PSS_PARAMS *pss_params; + if (mech->pParameter && sizeof(CK_RSA_PKCS_PSS_PARAMS) == mech->ulParameterLen) { + pss_params = mech->pParameter; + sLen = pss_params->sLen; + } + } + rv = sc_pkcs1_add_pss_padding(ctx, hash_algo, mgf1_hash, + tmp, tmp_len, out, out_len, mod_bits, sLen); +#else + rv = SC_ERROR_NOT_SUPPORTED; +#endif + LOG_FUNC_RETURN(ctx, rv); default: - /* currently only pkcs1 padding is supported */ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Unsupported padding algorithm 0x%x", pad_algo); - LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + /* We shouldn't be called with an unexpected padding type, we've already + * returned SC_ERROR_NOT_SUPPORTED if the card can't be used. */ + LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); } } @@ -279,37 +698,67 @@ int sc_get_encoding_flags(sc_context_t *ctx, unsigned long iflags, unsigned long caps, unsigned long *pflags, unsigned long *sflags) { - size_t i; - LOG_FUNC_CALLED(ctx); if (pflags == NULL || sflags == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); sc_log(ctx, "iFlags 0x%lX, card capabilities 0x%lX", iflags, caps); - for (i = 0; digest_info_prefix[i].algorithm != 0; i++) { - if (iflags & digest_info_prefix[i].algorithm) { - if (digest_info_prefix[i].algorithm != SC_ALGORITHM_RSA_HASH_NONE && - caps & digest_info_prefix[i].algorithm) - *sflags |= digest_info_prefix[i].algorithm; - else - *pflags |= digest_info_prefix[i].algorithm; - break; - } - } - if (iflags & SC_ALGORITHM_RSA_PAD_PKCS1) { - if (caps & SC_ALGORITHM_RSA_PAD_PKCS1) - *sflags |= SC_ALGORITHM_RSA_PAD_PKCS1; - else - *pflags |= SC_ALGORITHM_RSA_PAD_PKCS1; - } else if ((iflags & SC_ALGORITHM_RSA_PADS) == SC_ALGORITHM_RSA_PAD_NONE) { - - /* Work with RSA, EC and maybe GOSTR? */ - if (!(caps & SC_ALGORITHM_RAW_MASK)) - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "raw encryption is not supported"); - - *sflags |= (caps & SC_ALGORITHM_RAW_MASK); /* adds in the one raw type */ + /* For ECDSA and GOSTR, we don't do any padding or hashing ourselves, the + * card has to support the requested operation. Similarly, for RSA with + * raw padding (raw RSA) and ISO9796, we require the card to do it for us. + * Finally, for PKCS1 (v1.5 and PSS) and ASNI X9.31 we can apply the padding + * ourselves if the card supports raw RSA. */ + + /* TODO: Could convert GOSTR3410_HASH_GOSTR3411 -> GOSTR3410_RAW and + * ECDSA_HASH_ -> ECDSA_RAW using OpenSSL (not much benefit though). */ + + if ((caps & iflags) == iflags) { + /* Card supports the signature operation we want to do, great, let's + * go with it then. */ + *sflags = iflags; + *pflags = 0; + + } else if ((caps & SC_ALGORITHM_RSA_PAD_PSS) && + (iflags & SC_ALGORITHM_RSA_PAD_PSS)) { + *sflags |= SC_ALGORITHM_RSA_PAD_PSS; + *sflags |= iflags & SC_ALGORITHM_MGF1_HASHES; + *pflags = iflags & ~(iflags & (SC_ALGORITHM_MGF1_HASHES | SC_ALGORITHM_RSA_PAD_PSS)); + + } else if ((caps & SC_ALGORITHM_RSA_RAW) && + (iflags & SC_ALGORITHM_RSA_PAD_PKCS1 + || iflags & SC_ALGORITHM_RSA_PAD_PSS +#ifdef ENABLE_OPENSSL + || iflags & SC_ALGORITHM_RSA_PAD_OAEP +#endif + || iflags & SC_ALGORITHM_RSA_PAD_NONE)) { + /* Use the card's raw RSA capability on the padded input */ + *sflags = SC_ALGORITHM_RSA_PAD_NONE; + *pflags = iflags; + + } else if ((caps & (SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_HASH_NONE)) && + (iflags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01)) { + /* A corner case - the card can partially do PKCS1, if we prepend the + * DigestInfo bit it will do the rest. */ + *sflags = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_HASH_NONE; + *pflags = iflags & SC_ALGORITHM_RSA_HASHES; + + } else if ((caps & (SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02 | SC_ALGORITHM_RSA_HASH_NONE)) && + (iflags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02)) { + *sflags = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02 | SC_ALGORITHM_RSA_HASH_NONE; + *pflags = iflags & SC_ALGORITHM_RSA_HASHES; + + } else if ((iflags & SC_ALGORITHM_AES) == SC_ALGORITHM_AES) { /* TODO: seems like this constant does not belong to the same set of flags used form asymmetric algos. Fix this! */ + *sflags = 0; *pflags = 0; + + } else if ((iflags & SC_ALGORITHM_AES_FLAGS) > 0) { + *sflags = iflags & SC_ALGORITHM_AES_FLAGS; + if (iflags & SC_ALGORITHM_AES_CBC_PAD) + *pflags = SC_ALGORITHM_AES_CBC_PAD; + else + *pflags = 0; + } else { LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "unsupported algorithm"); } diff --git a/src/libopensc/pkcs15-actalis.c b/src/libopensc/pkcs15-actalis.c index 24983921aa..540602460d 100644 --- a/src/libopensc/pkcs15-actalis.c +++ b/src/libopensc/pkcs15-actalis.c @@ -19,10 +19,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -36,8 +36,7 @@ #include "common/compat_strlcpy.h" #include "libopensc/pkcs15.h" #include "libopensc/log.h" - -int sc_pkcs15emu_actalis_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); +#include "libopensc/internal.h" static int (*set_security_env) (sc_card_t *, const sc_security_env_t *, int); @@ -48,7 +47,7 @@ static int set_sec_env(sc_card_t * card, const sc_security_env_t *env, sc_security_env_t tenv = *env; if (tenv.operation == SC_SEC_OPERATION_SIGN) tenv.operation = SC_SEC_OPERATION_DECIPHER; - + if ((r = card->ops->restore_security_env(card, 0x40)) == SC_SUCCESS) return set_security_env(card, &tenv, se_num); @@ -62,13 +61,6 @@ static int do_sign(sc_card_t * card, const u8 * in, size_t inlen, u8 * out, return card->ops->decipher(card, in, inlen, out, outlen); } -static void set_string(char **strp, const char *value) -{ - if (*strp) - free(*strp); - *strp = value ? strdup(value) : NULL; -} - #if 1 /* XXX: temporary copy of the old pkcs15emu functions, * to be removed */ @@ -79,33 +71,33 @@ static int sc_pkcs15emu_add_pin(sc_pkcs15_card_t *p15card, unsigned int max_length, int flags, int tries_left, const char pad_char, int obj_flags) { - sc_pkcs15_auth_info_t info; + sc_pkcs15_auth_info_t info; sc_pkcs15_object_t obj; memset(&info, 0, sizeof(info)); memset(&obj, 0, sizeof(obj)); - info.auth_id = *id; + info.auth_id = *id; info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; - info.attrs.pin.min_length = min_length; - info.attrs.pin.max_length = max_length; - info.attrs.pin.stored_length = max_length; - info.attrs.pin.type = type; - info.attrs.pin.reference = ref; - info.attrs.pin.flags = flags; - info.attrs.pin.pad_char = pad_char; - info.tries_left = tries_left; + info.attrs.pin.min_length = min_length; + info.attrs.pin.max_length = max_length; + info.attrs.pin.stored_length = max_length; + info.attrs.pin.type = type; + info.attrs.pin.reference = ref; + info.attrs.pin.flags = flags; + info.attrs.pin.pad_char = pad_char; + info.tries_left = tries_left; info.logged_in = SC_PIN_STATE_UNKNOWN; - if (path) - info.path = *path; - if (type == SC_PKCS15_PIN_TYPE_BCD) - info.attrs.pin.stored_length /= 2; + if (path) + info.path = *path; + if (type == SC_PKCS15_PIN_TYPE_BCD) + info.attrs.pin.stored_length /= 2; strlcpy(obj.label, label, sizeof(obj.label)); obj.flags = obj_flags; - return sc_pkcs15emu_add_pin_obj(p15card, &obj, &info); + return sc_pkcs15emu_add_pin_obj(p15card, &obj, &info); } static int sc_pkcs15emu_add_prkey(sc_pkcs15_card_t *p15card, @@ -149,32 +141,34 @@ static int sc_pkcs15emu_actalis_init(sc_pkcs15_card_t * p15card) int r; #ifdef ENABLE_ZLIB + int use_file_cache_backup = p15card->opts.use_file_cache; + int i = 0, j = 0; const char *certLabel[] = { "User Non-repudiation Certificate", /* "User Non-repudiation Certificate" */ "TSA Certificate", "CA Certificate" - }; + }; const char *certPath[] = { "3F00300060006002", "3F00300060006003", "3F00300060006004" }; #endif const char *keyPath = "3F00300040000008"; const char *pinDfName = "05040200"; - + /* const int prkey_usage = SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; */ const int authprkey_usage = SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT; - + const char *authPIN = "Authentication PIN"; /* const char *nonrepPIN = "Non-repudiation PIN"; */ const char *authPRKEY = "Authentication Key"; /* const char *nonrepPRKEY = "Non repudiation Key"; */ - - p15card->opts.use_file_cache = 1; + + p15card->opts.use_file_cache = SC_PKCS15_OPTS_CACHE_ALL_FILES; /* Get Serial number */ sc_format_path("3F0030000001", &path); @@ -182,7 +176,9 @@ static int sc_pkcs15emu_actalis_init(sc_pkcs15_card_t * p15card) if (r != SC_SUCCESS) return SC_ERROR_WRONG_CARD; - sc_read_binary(card, 0xC3, serial_buf, 12, 0); + r = sc_read_binary(card, 0xC3, serial_buf, 12, 0); + if (r != SC_SUCCESS) + return SC_ERROR_WRONG_CARD; serial = serial_buf; /* @@ -199,9 +195,9 @@ static int sc_pkcs15emu_actalis_init(sc_pkcs15_card_t * p15card) serial[8] = '\0'; /* Controllo che il serial number inizi per "H" */ - if( serial[0] != 'H' ) + if( serial[0] != 'H' ) return SC_ERROR_WRONG_CARD; - + set_string(&p15card->tokeninfo->label, "Actalis"); set_string(&p15card->tokeninfo->manufacturer_id, "Actalis"); set_string(&p15card->tokeninfo->serial_number, (char *)serial); @@ -220,7 +216,8 @@ static int sc_pkcs15emu_actalis_init(sc_pkcs15_card_t * p15card) memset(&cert_info, 0, sizeof(cert_info)); memset(&cert_obj, 0, sizeof(cert_obj)); - sc_read_binary(card, 2, size, 2, 0); + if (SC_SUCCESS != sc_read_binary(card, 2, size, 2, 0)) + continue; compLen = (size[0] << 8) + size[1]; compCert = malloc(compLen * sizeof(unsigned char)); len = 3 * compLen; /*Approximation of the uncompressed size */ @@ -228,16 +225,19 @@ static int sc_pkcs15emu_actalis_init(sc_pkcs15_card_t * p15card) if (!cert || !compCert) { free(cert); free(compCert); + sc_pkcs15_card_clear(p15card); + p15card->opts.use_file_cache = use_file_cache_backup; return SC_ERROR_OUT_OF_MEMORY; } - sc_read_binary(card, 4, compCert, compLen, 0); - - if (uncompress(cert, &len, - compCert, compLen) != Z_OK) - return SC_ERROR_INTERNAL; + if (sc_read_binary(card, 4, compCert, compLen, 0) != SC_SUCCESS + || uncompress(cert, &len, compCert, compLen) != Z_OK) { + free(cert); + free(compCert); + continue; + } cpath.index = 0; - cpath.count = len; + cpath.count = (int)len; sc_pkcs15_cache_file(p15card, &cpath, cert, len); id.value[0] = j + 1; @@ -250,19 +250,28 @@ static int sc_pkcs15emu_actalis_init(sc_pkcs15_card_t * p15card) j++; cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; - sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); + r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); + if (r < 0) { + sc_log(card->ctx, "Failed to add cert obj r=%d", r); + free(cert); + free(compCert); + continue; + } + + free(cert); + free(compCert); } } #endif - + /* adding PINs & private keys */ flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED | - SC_PKCS15_PIN_FLAG_NEEDS_PADDING; - + SC_PKCS15_PIN_FLAG_NEEDS_PADDING; + sc_format_path(pinDfName, &path); path.type = SC_PATH_TYPE_DF_NAME; - + id.value[0] = 1; id.len = 1; sc_pkcs15emu_add_pin(p15card, &id, @@ -271,7 +280,7 @@ static int sc_pkcs15emu_actalis_init(sc_pkcs15_card_t * p15card) 5, 8, flags, 3, 0, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE); - + sc_format_path(keyPath, &path); id.value[0] = 1; id.len = 1; @@ -284,7 +293,7 @@ static int sc_pkcs15emu_actalis_init(sc_pkcs15_card_t * p15card) &path, 0x08, &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); - + /* return to MF */ sc_format_path("3F00", &path); sc_select_file(card, &path, NULL); @@ -295,7 +304,7 @@ static int sc_pkcs15emu_actalis_init(sc_pkcs15_card_t * p15card) card->ops->set_security_env = set_sec_env; card->ops->compute_signature = do_sign; } - + return SC_SUCCESS; } @@ -307,20 +316,14 @@ static int actalis_detect_card(sc_pkcs15_card_t * p15card) /* check if we have the correct card OS */ if (strcmp(card->name, "CardOS M4")) return SC_ERROR_WRONG_CARD; - + return SC_SUCCESS; } int sc_pkcs15emu_actalis_init_ex(sc_pkcs15_card_t * p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t * opts) + struct sc_aid *aid) { - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - return sc_pkcs15emu_actalis_init(p15card); - else { - int r = actalis_detect_card(p15card); - if (r) - return SC_ERROR_WRONG_CARD; - return sc_pkcs15emu_actalis_init(p15card); - } + if (actalis_detect_card(p15card)) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_actalis_init(p15card); } diff --git a/src/libopensc/pkcs15-algo.c b/src/libopensc/pkcs15-algo.c index c383f2d50e..335bde8c18 100644 --- a/src/libopensc/pkcs15-algo.c +++ b/src/libopensc/pkcs15-algo.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -45,7 +45,8 @@ asn1_decode_des_params(sc_context_t *ctx, void **paramp, { struct sc_asn1_entry asn1_des_iv[2]; u8 iv[8]; - int ivlen = 8, r; + size_t ivlen = 8; + int r; sc_copy_asn1_entry(c_asn1_des_iv, asn1_des_iv); sc_format_asn1_entry(asn1_des_iv + 0, iv, &ivlen, 0); @@ -267,10 +268,6 @@ asn1_decode_ec_params(sc_context_t *ctx, void **paramp, struct sc_asn1_entry asn1_ec_params[4]; struct sc_ec_parameters *ecp; - sc_debug(ctx, SC_LOG_DEBUG_ASN1, - "DEE - asn1_decode_ec_params %p:%"SC_FORMAT_LEN_SIZE_T"u %d", - buf, buflen, depth); - memset(&curve, 0, sizeof(curve)); /* We only want to copy the parms if they are a namedCurve @@ -289,7 +286,7 @@ asn1_decode_ec_params(sc_context_t *ctx, void **paramp, if (r < 0) return r; - ecp = calloc(sizeof(struct sc_ec_parameters), 1); + ecp = calloc(1, sizeof(struct sc_ec_parameters)); if (ecp == NULL) return SC_ERROR_OUT_OF_MEMORY; @@ -350,110 +347,137 @@ asn1_free_ec_params(void *params) } } - static struct sc_asn1_pkcs15_algorithm_info algorithm_table[] = { #ifdef SC_ALGORITHM_SHA1 - /* hmacWithSHA1 */ - { SC_ALGORITHM_SHA1, {{ 1, 2, 840, 113549, 2, 7, -1}}, NULL, NULL, NULL }, - { SC_ALGORITHM_SHA1, {{ 1, 3, 6, 1, 5, 5, 8, 1, 2, -1}}, NULL, NULL, NULL }, - /* SHA1 */ - { SC_ALGORITHM_SHA1, {{ 1, 3, 14, 3, 2, 26, -1}}, NULL, NULL, NULL }, + /* hmacWithSHA1 */ + {SC_ALGORITHM_SHA1, {{1, 2, 840, 113549, 2, 7, -1}}, NULL, NULL, NULL}, + {SC_ALGORITHM_SHA1, {{1, 3, 6, 1, 5, 5, 8, 1, 2, -1}}, NULL, NULL, NULL}, + /* SHA1 */ + {SC_ALGORITHM_SHA1, {{1, 3, 14, 3, 2, 26, -1}}, NULL, NULL, NULL}, #endif #ifdef SC_ALGORITHM_MD5 - { SC_ALGORITHM_MD5, {{ 1, 2, 840, 113549, 2, 5, -1}}, NULL, NULL, NULL }, + {SC_ALGORITHM_MD5, {{1, 2, 840, 113549, 2, 5, -1}}, NULL, NULL, NULL}, #endif -#ifdef SC_ALGORITHM_DSA - { SC_ALGORITHM_DSA, {{ 1, 2, 840, 10040, 4, 3, -1}}, NULL, NULL, NULL }, -#endif -#ifdef SC_ALGORITHM_RSA /* really rsaEncryption */ - { SC_ALGORITHM_RSA, {{ 1, 2, 840, 113549, 1, 1, 1, -1}}, NULL, NULL, NULL }, +#ifdef SC_ALGORITHM_RSA /* really rsaEncryption and id-RSASSA-PSS */ + {SC_ALGORITHM_RSA, {{1, 2, 840, 113549, 1, 1, 1, -1}}, NULL, NULL, NULL}, + {SC_ALGORITHM_RSA, {{1, 2, 840, 113549, 1, 1, 10, -1}}, NULL, NULL, NULL}, #endif #ifdef SC_ALGORITHM_DH - { SC_ALGORITHM_DH, {{ 1, 2, 840, 10046, 2, 1, -1}}, NULL, NULL, NULL }, + {SC_ALGORITHM_DH, {{1, 2, 840, 10046, 2, 1, -1}}, NULL, NULL, NULL}, #endif #ifdef SC_ALGORITHM_RC2_WRAP /* from CMS */ - { SC_ALGORITHM_RC2_WRAP, {{ 1, 2, 840, 113549, 1, 9, 16, 3, 7, -1}}, NULL, NULL, NULL }, + {SC_ALGORITHM_RC2_WRAP, {{1, 2, 840, 113549, 1, 9, 16, 3, 7, -1}}, NULL, NULL, NULL}, #endif #ifdef SC_ALGORITHM_RC2 /* CBC mode */ - { SC_ALGORITHM_RC2, {{ 1, 2, 840, 113549, 3, 2, -1}}, + {SC_ALGORITHM_RC2, {{1, 2, 840, 113549, 3, 2, -1}}, asn1_decode_rc2_params, - asn1_encode_rc2_params }, + asn1_encode_rc2_params}, #endif #ifdef SC_ALGORITHM_DES /* CBC mode */ - { SC_ALGORITHM_DES, {{ 1, 3, 14, 3, 2, 7, -1}}, + {SC_ALGORITHM_DES, {{1, 3, 14, 3, 2, 7, -1}}, asn1_decode_des_params, asn1_encode_des_params, - free }, + free}, #endif #ifdef SC_ALGORITHM_3DES_WRAP /* from CMS */ - { SC_ALGORITHM_3DES_WRAP, {{ 1, 2, 840, 113549, 1, 9, 16, 3, 6, -1}}, NULL, NULL, NULL }, + {SC_ALGORITHM_3DES_WRAP, {{1, 2, 840, 113549, 1, 9, 16, 3, 6, -1}}, NULL, NULL, NULL}, #endif #ifdef SC_ALGORITHM_3DES /* EDE CBC mode */ - { SC_ALGORITHM_3DES, {{ 1, 2, 840, 113549, 3, 7, -1}}, + {SC_ALGORITHM_3DES, {{1, 2, 840, 113549, 3, 7, -1}}, asn1_decode_des_params, asn1_encode_des_params, - free }, + free}, #endif #ifdef SC_ALGORITHM_GOST /* EDE CBC mode */ - { SC_ALGORITHM_GOST, {{ 1, 2, 4434, 66565, 3, 7, -1}}, NULL, NULL, NULL }, + {SC_ALGORITHM_GOST, {{1, 2, 4434, 66565, 3, 7, -1}}, NULL, NULL, NULL}, #endif #ifdef SC_ALGORITHM_GOSTR3410 - { SC_ALGORITHM_GOSTR3410, {{ 1, 2, 643, 2, 2, 19, -1}}, + {SC_ALGORITHM_GOSTR3410, {{1, 2, 643, 2, 2, 19, -1}}, asn1_decode_gostr3410_params, asn1_encode_gostr3410_params, - NULL }, + NULL}, #endif /* We do not support PBES1 because the encryption is weak */ #ifdef SC_ALGORITHM_PBKDF2 - { SC_ALGORITHM_PBKDF2, {{ 1, 2, 840, 113549, 1, 5, 12, -1}}, + {SC_ALGORITHM_PBKDF2, {{1, 2, 840, 113549, 1, 5, 12, -1}}, asn1_decode_pbkdf2_params, asn1_encode_pbkdf2_params, - free }, + free}, #endif #ifdef SC_ALGORITHM_PBES2 - { SC_ALGORITHM_PBES2, {{ 1, 2, 840, 113549, 1, 5, 13, -1}}, + {SC_ALGORITHM_PBES2, {{1, 2, 840, 113549, 1, 5, 13, -1}}, asn1_decode_pbes2_params, asn1_encode_pbes2_params, - asn1_free_pbes2_params }, + asn1_free_pbes2_params}, #endif - #ifdef SC_ALGORITHM_EC - { SC_ALGORITHM_EC, {{ 1, 2, 840, 10045, 2, 1, -1}}, + {SC_ALGORITHM_EC, {{1, 2, 840, 10045, 2, 1, -1}}, asn1_decode_ec_params, asn1_encode_ec_params, - asn1_free_ec_params }, + asn1_free_ec_params}, #endif -/* TODO: -DEE Not clear if we need the next five or not */ #ifdef SC_ALGORITHM_ECDSA_SHA1 - /* Note RFC 3279 says no ecParameters */ - { SC_ALGORITHM_ECDSA_SHA1, {{ 1, 2, 840, 10045, 4, 1, -1}}, NULL, NULL, NULL}, + /* Note RFC 3279 says no ecParameters */ + {SC_ALGORITHM_ECDSA_SHA1, {{1, 2, 840, 10045, 4, 1, -1}}, NULL, NULL, NULL}, #endif #ifdef SC_ALGORITHM_ECDSA_SHA224 -/* These next 4 are defined in RFC 5758 */ - { SC_ALGORITHM_ECDSA_SHA224, {{ 1, 2, 840, 10045, 4, 3, 1, -1}}, + /* These next 4 are defined in RFC 5758 */ + {SC_ALGORITHM_ECDSA_SHA224, {{1, 2, 840, 10045, 4, 3, 1, -1}}, asn1_decode_ec_params, asn1_encode_ec_params, - asn1_free_ec_params }, + asn1_free_ec_params}, #endif #ifdef SC_ALGORITHM_ECDSA_SHA256 - { SC_ALGORITHM_ECDSA_SHA256, {{ 1, 2, 840, 10045, 4, 3, 2, -1}}, + {SC_ALGORITHM_ECDSA_SHA256, {{1, 2, 840, 10045, 4, 3, 2, -1}}, asn1_decode_ec_params, asn1_encode_ec_params, - asn1_free_ec_params }, + asn1_free_ec_params}, #endif #ifdef SC_ALGORITHM_ECDSA_SHA384 - { SC_ALGORITHM_ECDSA_SHA384, {{ 1, 2, 840, 10045, 4, 3, 3, -1}}, + {SC_ALGORITHM_ECDSA_SHA384, {{1, 2, 840, 10045, 4, 3, 3, -1}}, asn1_decode_ec_params, asn1_encode_ec_params, - asn1_free_ec_params }, + asn1_free_ec_params}, #endif #ifdef SC_ALGORITHM_ECDSA_SHA512 - { SC_ALGORITHM_ECDSA_SHA512, {{ 1, 2, 840, 10045, 4, 3, 4, -1}}, + {SC_ALGORITHM_ECDSA_SHA512, {{1, 2, 840, 10045, 4, 3, 4, -1}}, + asn1_decode_ec_params, + asn1_encode_ec_params, + asn1_free_ec_params}, +#endif +#ifdef SC_ALGORITHM_EDDSA + /* aka Ed25519 */ + /* RFC 8410, needed to parse/create X509 certs/pubkeys */ + {SC_ALGORITHM_EDDSA, {{1, 3, 101, 112, -1}}, + asn1_decode_ec_params, + asn1_encode_ec_params, + asn1_free_ec_params}, /* Ed25119 */ + {SC_ALGORITHM_EDDSA, {{1, 3, 6, 1, 4, 1, 11591, 15, 1, -1}}, + asn1_decode_ec_params, + asn1_encode_ec_params, + asn1_free_ec_params}, /* Ed25519 OID used by OpenPGP */ + {SC_ALGORITHM_EDDSA, {{1, 3, 101, 113, -1}}, + asn1_decode_ec_params, + asn1_encode_ec_params, + asn1_free_ec_params}, /* Ed448 */ +#endif +#ifdef SC_ALGORITHM_XEDDSA + /* aka curve25519 */ + /* RFC 8410, needed to parse/create X509 certs/pubkeys ec_parms*/ + {SC_ALGORITHM_XEDDSA, {{1, 3, 101, 110, -1}}, + asn1_decode_ec_params, + asn1_encode_ec_params, + asn1_free_ec_params}, /* X25519 */ + {SC_ALGORITHM_XEDDSA, {{1, 3, 6, 1, 4, 1, 3029, 1, 5, 1 - 1}}, + asn1_decode_ec_params, + asn1_encode_ec_params, + asn1_free_ec_params}, /* X25519 OID used by OpenPGP */ + {SC_ALGORITHM_XEDDSA, {{1, 3, 101, 111, -1}}, asn1_decode_ec_params, asn1_encode_ec_params, - asn1_free_ec_params }, + asn1_free_ec_params}, /* X448 */ #endif - { -1, {{ -1 }}, NULL, NULL, NULL } + {-1, {{-1}}, NULL, NULL, NULL} }; @@ -530,10 +554,10 @@ sc_asn1_encode_algorithm_id(struct sc_context *ctx, u8 **buf, size_t *len, u8 *tmp; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "type of algorithm to encode: %i", id->algorithm); + sc_log(ctx, "type of algorithm to encode: %lu", id->algorithm); alg_info = sc_asn1_get_algorithm_info(id); if (alg_info == NULL) { - sc_log(ctx, "Cannot encode unknown algorithm %u", id->algorithm); + sc_log(ctx, "Cannot encode unknown algorithm %lu", id->algorithm); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } @@ -549,7 +573,12 @@ sc_asn1_encode_algorithm_id(struct sc_context *ctx, u8 **buf, size_t *len, sc_format_asn1_entry(asn1_alg_id + 0, (void *) &id->oid, NULL, 1); /* no parameters, write NULL tag */ - if (!id->params || !alg_info->encode) + /* If it's EDDSA/XEDDSA, according to RFC8410, params + * MUST be absent */ + /* PKCS11 3.0 list them under ec_params */ + if (id->algorithm != SC_ALGORITHM_EDDSA && + id->algorithm != SC_ALGORITHM_XEDDSA && + (!id->params || !alg_info->encode)) asn1_alg_id[1].flags |= SC_ASN1_PRESENT; r = _sc_asn1_encode(ctx, asn1_alg_id, buf, len, depth + 1); diff --git a/src/libopensc/pkcs15-atrust-acos.c b/src/libopensc/pkcs15-atrust-acos.c index 2d6ba4a9e7..a4d4217a1c 100644 --- a/src/libopensc/pkcs15-atrust-acos.c +++ b/src/libopensc/pkcs15-atrust-acos.c @@ -16,10 +16,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -27,6 +27,7 @@ #include #include +#include "internal.h" #include "common/compat_strlcpy.h" #include "libopensc/pkcs15.h" #include "libopensc/cardctl.h" @@ -34,8 +35,6 @@ #define MANU_ID "A-Trust" #define CARD_LABEL "a.sign Premium a" -int sc_pkcs15emu_atrust_acos_init_ex(sc_pkcs15_card_t *, struct sc_aid *aid, sc_pkcs15emu_opt_t *); - typedef struct cdata_st { const char *label; int authority; @@ -53,11 +52,11 @@ typedef struct pdata_st { unsigned int maxlen; unsigned int minlen; unsigned int storedlen; - int flags; + int flags; int tries_left; const char pad_char; int obj_flags; -} pindata; +} pindata; typedef struct prdata_st { const char *id; @@ -79,14 +78,14 @@ static int get_cert_len(sc_card_t *card, sc_path_t *path) if (r < 0) return 0; r = sc_read_binary(card, 0, buf, sizeof(buf), 0); - if (r < 0) + if (r < 4) return 0; if (buf[0] != 0x30 || buf[1] != 0x82) return 0; path->index = 0; - path->count = ((((size_t) buf[2]) << 8) | buf[3]) + 4; + path->count = ((((int) buf[2]) << 8) | buf[3]) + 4; return 1; -} +} static int acos_detect_card(sc_pkcs15_card_t *p15card) { @@ -123,7 +122,7 @@ static int sc_pkcs15emu_atrust_acos_init(sc_pkcs15_card_t *p15card) 4, 4, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_LOCAL, -1, 0x00, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE }, - { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0} + { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; const prdata prkeys[] = { @@ -154,28 +153,20 @@ static int sc_pkcs15emu_atrust_acos_init(sc_pkcs15_card_t *p15card) r = sc_bin_to_hex(buf, 8, buf2, sizeof(buf2), 0); if (r != SC_SUCCESS) return SC_ERROR_INTERNAL; - if (p15card->tokeninfo->serial_number) - free(p15card->tokeninfo->serial_number); - p15card->tokeninfo->serial_number = malloc(strlen(buf2) + 1); + + set_string(&p15card->tokeninfo->serial_number, buf2); if (!p15card->tokeninfo->serial_number) return SC_ERROR_INTERNAL; - strcpy(p15card->tokeninfo->serial_number, buf2); /* manufacturer ID */ - if (p15card->tokeninfo->manufacturer_id) - free(p15card->tokeninfo->manufacturer_id); - p15card->tokeninfo->manufacturer_id = malloc(strlen(MANU_ID) + 1); + set_string(&p15card->tokeninfo->manufacturer_id, MANU_ID); if (!p15card->tokeninfo->manufacturer_id) - return SC_ERROR_INTERNAL; - strcpy(p15card->tokeninfo->manufacturer_id, MANU_ID); + goto err; /* card label */ - if (p15card->tokeninfo->label) - free(p15card->tokeninfo->label); - p15card->tokeninfo->label = malloc(strlen(CARD_LABEL) + 1); + set_string(&p15card->tokeninfo->label, CARD_LABEL); if (!p15card->tokeninfo->label) - return SC_ERROR_INTERNAL; - strcpy(p15card->tokeninfo->label, CARD_LABEL); + goto err; /* set certs */ for (i = 0; certs[i].label; i++) { @@ -197,7 +188,7 @@ static int sc_pkcs15emu_atrust_acos_init(sc_pkcs15_card_t *p15card) r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) - return SC_ERROR_INTERNAL; + goto err; } /* set pins */ for (i = 0; pins[i].label; i++) { @@ -225,7 +216,7 @@ static int sc_pkcs15emu_atrust_acos_init(sc_pkcs15_card_t *p15card) r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if (r < 0) - return SC_ERROR_INTERNAL; + goto err; } /* set private keys */ for (i = 0; prkeys[i].label; i++) { @@ -249,33 +240,29 @@ static int sc_pkcs15emu_atrust_acos_init(sc_pkcs15_card_t *p15card) r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); if (r < 0) - return SC_ERROR_INTERNAL; + goto err; } - + /* select the application DF */ sc_format_path("DF71", &path); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS || !file) - return SC_ERROR_INTERNAL; + goto err; /* set the application DF */ - if (p15card->file_app) - free(p15card->file_app); + sc_file_free(p15card->file_app); p15card->file_app = file; return SC_SUCCESS; + +err: + sc_pkcs15_card_clear(p15card); + return SC_ERROR_INTERNAL; } int sc_pkcs15emu_atrust_acos_init_ex(sc_pkcs15_card_t *p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t *opts) + struct sc_aid *aid) { - - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - return sc_pkcs15emu_atrust_acos_init(p15card); - else { - int r = acos_detect_card(p15card); - if (r) - return SC_ERROR_WRONG_CARD; - return sc_pkcs15emu_atrust_acos_init(p15card); - } + if (acos_detect_card(p15card)) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_atrust_acos_init(p15card); } diff --git a/src/libopensc/pkcs15-cac.c b/src/libopensc/pkcs15-cac.c index 45e5988f8e..6a65d5fc30 100644 --- a/src/libopensc/pkcs15-cac.c +++ b/src/libopensc/pkcs15-cac.c @@ -24,10 +24,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -45,9 +45,6 @@ /* probably should get manufacturer ID from cuid */ #define MANU_ID "Common Access Card" -int sc_pkcs15emu_cac_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); - - typedef struct pdata_st { const char *id; @@ -82,98 +79,16 @@ static const char * cac_get_name(int type) switch (type) { case SC_CARD_TYPE_CAC_I: return ("CAC I"); case SC_CARD_TYPE_CAC_II: return ("CAC II"); + case SC_CARD_TYPE_CAC_ALT_HID: return ("CAC ALT HID"); default: break; } return ("CAC"); } -/* - * These could move to a helper file for other cards that wish to use usage as a way of getting flags - */ - -/* Only certain usages are valid for a given algorithm, return all the usages that the algorithm supports so we - * can use it as a filter for all the public and private key usages */ -static unsigned int -cac_alg_flags_from_algorithm(int algorithm) -{ - switch (algorithm) { - case SC_ALGORITHM_RSA: - return SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP | - SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER | - SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP | - SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | - SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; - case SC_ALGORITHM_DSA: - return SC_PKCS15_PRKEY_USAGE_VERIFY| SC_PKCS15_PRKEY_USAGE_SIGN | - SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; -#ifdef SC_ALGORITHM_DH - case SC_ALGORITHM_DH: - return SC_PKCS15_PRKEY_USAGE_DERIVE ; -#endif - case SC_ALGORITHM_EC: - return SC_PKCS15_PRKEY_USAGE_DERIVE | SC_PKCS15_PRKEY_USAGE_VERIFY| - SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; - case SC_ALGORITHM_GOSTR3410: - return SC_PKCS15_PRKEY_USAGE_DERIVE | SC_PKCS15_PRKEY_USAGE_VERIFY| - SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; - } - return 0; -} - -/* These are the cert key usage bits that map to various PKCS #11 (and thus PKCS #15) flags */ -#define CAC_X509_USAGE_SIGNATURE \ - (SC_X509_DIGITAL_SIGNATURE | \ - SC_X509_NON_REPUDIATION | \ - SC_X509_KEY_CERT_SIGN | \ - SC_X509_CRL_SIGN) -#define CAC_X509_USAGE_DERIVE \ - SC_X509_KEY_AGREEMENT -#define CAC_X509_USAGE_UNWRAP \ - (SC_X509_KEY_ENCIPHERMENT | \ - SC_X509_KEY_AGREEMENT) -#define CAC_X509_USAGE_DECRYPT \ - (SC_X509_DATA_ENCIPHERMENT | \ - SC_X509_ENCIPHER_ONLY) -#define CAC_X509_USAGE_NONREPUDIATION \ - SC_X509_NON_REPUDIATION - -/* map a cert usage and algorithm to public and private key usages */ -static int -cac_map_usage(unsigned long long cert_usage, int algorithm, unsigned int *pub_usage_ptr, unsigned int *pr_usage_ptr, int allow_nonrepudiation) -{ - unsigned int pub_usage = 0, pr_usage = 0; - unsigned int alg_flags = cac_alg_flags_from_algorithm(algorithm); - - if (cert_usage & CAC_X509_USAGE_SIGNATURE) { - pub_usage |= SC_PKCS15_PRKEY_USAGE_VERIFY|SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER; - pr_usage |= SC_PKCS15_PRKEY_USAGE_SIGN|SC_PKCS15_PRKEY_USAGE_SIGNRECOVER; - } - if (cert_usage & CAC_X509_USAGE_DERIVE) { - pub_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE; - pr_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE; - } - if (cert_usage & (CAC_X509_USAGE_DECRYPT|CAC_X509_USAGE_UNWRAP)) { - pub_usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT; - pr_usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT; - } - if (allow_nonrepudiation && (cert_usage & CAC_X509_USAGE_NONREPUDIATION)) { - pub_usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; - pr_usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; - } - /* filter usages algorithm */ - if (pub_usage_ptr) { - *pub_usage_ptr = pub_usage & alg_flags; - } - if (pr_usage_ptr) { - *pr_usage_ptr = pr_usage & alg_flags; - } - return SC_SUCCESS; -} - static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card) { static const pindata pins[] = { - { "1", NULL, "", 0x00, + { "1", "PIN", "", 0x00, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 8, 4, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | @@ -184,7 +99,7 @@ static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card) }; /* oid for key usage */ static const struct sc_object_id usage_type = {{ 2, 5, 29, 15, -1 }}; - unsigned long long usage; + unsigned int usage; /* @@ -208,23 +123,26 @@ static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card) /* could read this off card if needed */ - p15card->tokeninfo->label = strdup(cac_get_name(card->type)); - p15card->tokeninfo->manufacturer_id = strdup(MANU_ID); + set_string(&p15card->tokeninfo->label, cac_get_name(card->type)); + set_string(&p15card->tokeninfo->manufacturer_id, MANU_ID); /* * get serial number */ r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"sc_card_ctl rc=%d",r); - p15card->tokeninfo->serial_number = strdup("00000000"); + sc_log(card->ctx, "sc_card_ctl rc=%d",r); + set_string(&p15card->tokeninfo->serial_number, "00000000"); } else { sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0); - p15card->tokeninfo->serial_number = strdup(buf); + set_string(&p15card->tokeninfo->serial_number, buf); } /* set pins */ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "CAC adding pins..."); + /* TODO we should not create PIN objects if it is not initialized + * (opensc-tool -s 0020000000 returns 0x6A 0x88) + */ + sc_log(card->ctx, "CAC adding pins..."); for (i = 0; pins[i].id; i++) { struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; @@ -245,34 +163,38 @@ static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card) sc_format_path(pins[i].path, &pin_info.path); pin_info.tries_left = -1; - label = pins[i].label? pins[i].label : cac_get_name(card->type); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "CAC Adding pin %d label=%s",i, label); + label = pins[i].label; + sc_log(card->ctx, "CAC Adding pin %d label=%s",i, label); strncpy(pin_obj.label, label, SC_PKCS15_MAX_LABEL_SIZE - 1); pin_obj.flags = pins[i].obj_flags; + /* get the ACA path in case it needs to be selected before PIN verify */ + r = sc_card_ctl(card, SC_CARDCTL_CAC_GET_ACA_PATH, &pin_info.path); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not get ACA path."); + r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); - if (r < 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not add pin object."); } /* set other objects */ r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_INIT_GET_GENERIC_OBJECTS, &count); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not initiate generic objects."); + for (i = 0; i < count; i++) { struct sc_pkcs15_data_info obj_info; struct sc_pkcs15_object obj_obj; r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_GET_NEXT_GENERIC_OBJECT, &obj_info); - if (r < 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not get next generic object."); memset(&obj_obj, 0, sizeof(obj_obj)); memcpy(obj_obj.label, obj_info.app_label, sizeof(obj_obj.label)); r = sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_DATA_OBJECT, &obj_obj, &obj_info); - if (r < 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not finalize generic object."); } r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_FINAL_GET_GENERIC_OBJECTS, &count); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not finalize generic objects."); /* * certs, pubkeys and priv keys are related and we assume @@ -280,8 +202,10 @@ static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card) * We need to read the cert, get modulus and keylen * We use those for the pubkey, and priv key objects. */ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "CAC adding certs, pub and priv keys..."); + sc_log(card->ctx, "CAC adding certs, pub and priv keys..."); r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_INIT_GET_CERT_OBJECTS, &count); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not initiate cert objects."); + for (i = 0; i < count; i++) { struct sc_pkcs15_data_info obj_info; struct sc_pkcs15_cert_info cert_info; @@ -294,6 +218,7 @@ static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card) sc_pkcs15_cert_t *cert_out = NULL; r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_GET_NEXT_CERT_OBJECT, &obj_info); + LOG_TEST_RET(card->ctx, r, "Can not get next object"); memset(&cert_info, 0, sizeof(cert_info)); memset(&pubkey_info, 0, sizeof(pubkey_info)); @@ -317,8 +242,8 @@ static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card) prkey_info.path.len += 2; } pubkey_info.native = 1; - pubkey_info.key_reference = ((int)obj_info.id.value[0]) << 8 || obj_info.id.value[1]; - prkey_info.key_reference = ((int)obj_info.id.value[0]) << 8 || obj_info.id.value[1]; + pubkey_info.key_reference = ((int)obj_info.id.value[0]) << 8 | obj_info.id.value[1]; + prkey_info.key_reference = ((int)obj_info.id.value[0]) << 8 | obj_info.id.value[1]; prkey_info.native = 1; memcpy(cert_obj.label, obj_info.app_label, sizeof(obj_info.app_label)); @@ -327,18 +252,18 @@ static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card) prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; sc_pkcs15_format_id(pins[0].id, &prkey_obj.auth_id); - r = sc_pkcs15_read_file(p15card, &cert_info.path, &cert_der.value, &cert_der.len); + r = sc_pkcs15_read_file(p15card, &cert_info.path, &cert_der.value, &cert_der.len, 0); if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No cert found,i=%d", i); + sc_log(card->ctx, "No cert found,i=%d", i); continue; } - cert_info.path.count = cert_der.len; + cert_info.path.count = (int)cert_der.len; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "cert len=%"SC_FORMAT_LEN_SIZE_T"u, cert_info.path.count=%d r=%d\n", cert_der.len, cert_info.path.count, r); - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "cert", cert_der.value, cert_der.len); + sc_log_hex(card->ctx, "cert", cert_der.value, cert_der.len); /* cache it using the PKCS15 emulation objects */ /* as it does not change */ @@ -349,18 +274,20 @@ static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card) } /* following will find the cached cert in cert_info */ - r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out); + r = sc_pkcs15_read_certificate(p15card, &cert_info, 0, &cert_out); if (r < 0 || cert_out->key == NULL) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to read/parse the certificate r=%d",r); + sc_log(card->ctx, "Failed to read/parse the certificate r=%d",r); if (cert_out != NULL) sc_pkcs15_free_certificate(cert_out); + free(cert_info.value.value); continue; } r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " Failed to add cert obj r=%d",r); + sc_log(card->ctx, " Failed to add cert obj r=%d",r); sc_pkcs15_free_certificate(cert_out); + free(cert_info.value.value); continue; } /* set the token name to the name of the CN of the first certificate */ @@ -373,6 +300,7 @@ static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card) if (r == SC_SUCCESS) { token_name = malloc (cn_len+1); if (!token_name) { + free(cn_name); r = SC_ERROR_OUT_OF_MEMORY; goto fail; } @@ -392,53 +320,62 @@ static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card) r = sc_pkcs15_get_bitstring_extension(card->ctx, cert_out, &usage_type, &usage, NULL); if (r < 0) { - usage = 0xd9ULL; /* basic default usage */ + usage = SC_X509_DATA_ENCIPHERMENT|SC_X509_DIGITAL_SIGNATURE; /* basic default usage */ } - cac_map_usage(usage, cert_out->key->algorithm, &pubkey_info.usage, &prkey_info.usage, 1); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cert %s: cert_usage=0x%llx, pub_usage=0x%x priv_usage=0x%x\n", + sc_pkcs15_map_usage(usage, cert_out->key->algorithm, &pubkey_info.usage, &prkey_info.usage, 1); + sc_log(card->ctx, "cert %s: cert_usage=0x%x, pub_usage=0x%x priv_usage=0x%x\n", sc_dump_hex(cert_info.id.value, cert_info.id.len), usage, pubkey_info.usage, prkey_info.usage); if (cert_out->key->algorithm != SC_ALGORITHM_RSA) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"unsupported key.algorithm %d", cert_out->key->algorithm); + sc_log(card->ctx, "unsupported key.algorithm %lu", cert_out->key->algorithm); sc_pkcs15_free_certificate(cert_out); + free(pubkey_info.direct.spki.value); continue; } else { pubkey_info.modulus_length = cert_out->key->u.rsa.modulus.len * 8; prkey_info.modulus_length = cert_out->key->u.rsa.modulus.len * 8; r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "adding rsa public key r=%d usage=%x",r, pubkey_info.usage); - if (r < 0) + sc_log(card->ctx, "adding rsa public key r=%d usage=%x",r, pubkey_info.usage); + if (r < 0) { + free(pubkey_info.direct.spki.value); goto fail; + } + pubkey_info.direct.spki.value = NULL; /* moved to the pubkey object on p15card */ + pubkey_info.direct.spki.len = 0; r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "adding rsa private key r=%d usage=%x",r, prkey_info.usage); + if (r < 0) + goto fail; + sc_log(card->ctx, "adding rsa private key r=%d usage=%x",r, prkey_info.usage); } cert_out->key = NULL; fail: sc_pkcs15_free_certificate(cert_out); - if (r < 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); /* should not fail */ + if (r < 0) { + (card->ops->card_ctl)(card, SC_CARDCTL_CAC_FINAL_GET_CERT_OBJECTS, &count); + LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to add object."); + } } r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_FINAL_GET_CERT_OBJECTS, &count); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not finalize cert objects."); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + +err: + sc_pkcs15_card_clear(p15card); + LOG_FUNC_RETURN(card->ctx, r); } int sc_pkcs15emu_cac_init_ex(sc_pkcs15_card_t *p15card, - struct sc_aid *aid, sc_pkcs15emu_opt_t *opts) + struct sc_aid *aid) { sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; LOG_FUNC_CALLED(ctx); - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - return sc_pkcs15emu_cac_init(p15card); - else { - int r = cac_detect_card(p15card); - if (r) - return SC_ERROR_WRONG_CARD; - return sc_pkcs15emu_cac_init(p15card); - } + if (cac_detect_card(p15card)) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_cac_init(p15card); } diff --git a/src/libopensc/pkcs15-cache.c b/src/libopensc/pkcs15-cache.c index 16593931b4..394be867cd 100644 --- a/src/libopensc/pkcs15-cache.c +++ b/src/libopensc/pkcs15-cache.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -35,6 +35,7 @@ #include "internal.h" #include "pkcs15.h" +#include "common/compat_strlcpy.h" #define RANDOM_UID_INDICATOR 0x08 static int generate_cache_filename(struct sc_pkcs15_card *p15card, @@ -45,13 +46,15 @@ static int generate_cache_filename(struct sc_pkcs15_card *p15card, char *last_update = NULL; int r; unsigned u; + size_t change_counter; if (p15card->tokeninfo->serial_number == NULL && (p15card->card->uid.len == 0 || p15card->card->uid.value[0] == RANDOM_UID_INDICATOR)) return SC_ERROR_INVALID_ARGUMENTS; - assert(path->len <= SC_MAX_PATH_SIZE); + if (path->len > SC_MAX_PATH_SIZE) + return SC_ERROR_INTERNAL; r = sc_get_cache_dir(p15card->card->ctx, dir, sizeof(dir)); if (r) return r; @@ -72,6 +75,9 @@ static int generate_cache_filename(struct sc_pkcs15_card *p15card, p15card->card->uid.len), last_update); } + if (SC_SUCCESS == sc_card_ctl(p15card->card, SC_CARDCTL_GET_CHANGE_COUNTER, &change_counter)) + snprintf(dir + strlen(dir), sizeof(dir) - strlen(dir), "_%" SC_FORMAT_LEN_SIZE_T "u", change_counter); + if (path->aid.len && (path->type == SC_PATH_TYPE_FILE_ID || path->type == SC_PATH_TYPE_PATH)) { snprintf(dir + strlen(dir), sizeof(dir) - strlen(dir), "_"); @@ -94,9 +100,9 @@ static int generate_cache_filename(struct sc_pkcs15_card *p15card, "%02X", path->value[u + offs]); } - if (!buf || bufsize < strlen(dir)) + if (!buf) return SC_ERROR_BUFFER_TOO_SMALL; - strcpy(buf, dir); + strlcpy(buf, dir, bufsize); return SC_SUCCESS; } @@ -190,6 +196,7 @@ int sc_pkcs15_cache_file(struct sc_pkcs15_card *p15card, { char fname[PATH_MAX]; int r; + long len; FILE *f; size_t c; @@ -197,22 +204,38 @@ int sc_pkcs15_cache_file(struct sc_pkcs15_card *p15card, if (r != 0) return r; - f = fopen(fname, "wb"); + f = fopen(fname, "ab"); /* If the open failed because the cache directory does * not exist, create it and a re-try the fopen() call. */ if (f == NULL && errno == ENOENT) { if ((r = sc_make_cache_dir(p15card->card->ctx)) < 0) return r; - f = fopen(fname, "wb"); + f = fopen(fname, "ab"); } if (f == NULL) return 0; + /* we opened the file for appending so we should be at the end of file. + * The ftell() will give use the length of the file */ + len = ftell(f); + if (len > path->index) { + /* override previous cache records on this location */ + r = fseek(f, path->index, SEEK_SET); + if (r != 0) { + fclose(f); + return 0; + } + } else if (path->index > len) { + /* We miss some bytes so we will not cache this chunk */ + fclose(f); + return 0; + } + c = fwrite(buf, 1, bufsize, f); fclose(f); if (c != bufsize) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(p15card->card->ctx, "fwrite() wrote only %"SC_FORMAT_LEN_SIZE_T"u bytes", c); unlink(fname); diff --git a/src/libopensc/pkcs15-cardos.c b/src/libopensc/pkcs15-cardos.c new file mode 100644 index 0000000000..59f3b1e1be --- /dev/null +++ b/src/libopensc/pkcs15-cardos.c @@ -0,0 +1,177 @@ +/* + * PKCS15 emulation layer for CardOS cards + * Adapted from PKCS15 emulation layer for IAS/ECC card. + * + * Copyright (C) 2020, Douglas E. Engert + * Copyright (C) 2016, Viktor Tarasov + * Copyright (C) 2004, Bud P. Bruegger + * Copyright (C) 2004, Antonino Iacono + * Copyright (C) 2003, Olaf Kirch + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "internal.h" +#include "pkcs15.h" + + +/* + * Called after sc_pkcs15_bind_internal + * Create new flags based on supported_algos. + */ +static int cardos_fix_token_info(sc_pkcs15_card_t *p15card) +{ + sc_card_t *card; + struct sc_supported_algo_info (*saa)[SC_MAX_SUPPORTED_ALGORITHMS]; + struct sc_supported_algo_info *sa; + struct sc_cardctl_cardos_pass_algo_flags *passed = NULL; + int r = 0; + int i; + + card = p15card->card; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + passed = calloc(1, sizeof(struct sc_cardctl_cardos_pass_algo_flags)); + if (!passed) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); + + passed->pass = 1; /* get used_flags and card_flags from card */ + r = sc_card_ctl(p15card->card, SC_CARDCTL_CARDOS_PASS_ALGO_FLAGS, passed); + if (r < 0) { + free(passed); + LOG_FUNC_RETURN(card->ctx, r); + } + + saa = &(p15card->tokeninfo->supported_algos); + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Original Flags: 0x%8.8lx card->flags:0x%8.8lx", passed->used_flags, passed->card_flags); + + if (passed->card_flags) { /* user forced the flags, use them */ + passed->new_flags = passed->card_flags; /* from card_atr flags */ + } else { + + for (i = 0, sa = saa[0]; i < SC_MAX_SUPPORTED_ALGORITHMS; i++, sa++) { + + if (sa->reference == 0 && sa->mechanism == 0 + && sa->operations == 0 && sa->algo_ref == 0) + break; + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "supported_algos[%d] mechanism:0x%8.8x", i, sa->mechanism); + switch(sa->mechanism) { + case 0x01 : + /* + * Card appears to use lower 4 bits of reference as key, and upper + * 4 bits as mech for card. + * Also has a bug if mechanism = 1 (CKM_RSA_PKCS1) and reference 0x10 + * bit is set mechanism should be 3 (CKM_RSA_X_509) + * correct the mechanism in tokenInfo + */ + if (sa->reference & 0x10) { + sc_log(card->ctx, "Changing mechanism to CKM_RSA_X_509 based on reference"); + passed->new_flags |= SC_ALGORITHM_RSA_RAW + | SC_ALGORITHM_RSA_PAD_NONE; + sa->mechanism = 0x03; + } else + passed->new_flags |= SC_ALGORITHM_RSA_PAD_PKCS1; + break; + case 0x03 : + passed->new_flags |= SC_ALGORITHM_RSA_RAW + | SC_ALGORITHM_RSA_PAD_NONE; + break; + case 0x06 : + passed->new_flags |= SC_ALGORITHM_RSA_HASH_SHA1; + break; + case 0x1041: + passed->ec_flags |= SC_ALGORITHM_ECDSA_RAW; + /* no old_ec_flags */ + /* TODO turn on sizes from ec curves OIDS */ + break; + default: + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "UNKNOWN MECH: 0x%8.8x", sa->mechanism); + } + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "New_flags 0x%8.8lx New_ec_flags: 0x%8.8lx", + passed->new_flags, passed->ec_flags); + } + + if (passed->new_flags == 0) { + if (p15card->tokeninfo && p15card->tokeninfo->flags & SC_PKCS15_TOKEN_EID_COMPLIANT) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "EID_COMPLIANT flag found"); + passed->new_flags = (passed->used_flags & ~SC_ALGORITHM_SPECIFIC_FLAGS) | SC_ALGORITHM_RSA_PAD_PKCS1; + } else + passed->new_flags = passed->used_flags; /* from default cardos_init */ + } + } + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Final New_flags 0x%8.8lx New_ec_flags: 0x%8.8lx", passed->new_flags, passed->ec_flags); + + passed->pass = 2; /* tell card driver to use the new flags */ + r = sc_card_ctl(p15card->card, SC_CARDCTL_CARDOS_PASS_ALGO_FLAGS, passed); + + free(passed); + LOG_FUNC_RETURN(card->ctx, r); +} + +static int +cardos_pkcs15emu_detect_card(sc_pkcs15_card_t *p15card) +{ + if (p15card->card->type < SC_CARD_TYPE_CARDOS_BASE) + return SC_ERROR_WRONG_CARD; + + if (p15card->card->type >= SC_CARD_TYPE_CARDOS_BASE + 1000) + return SC_ERROR_WRONG_CARD; + + return SC_SUCCESS; +} + + +static int +sc_pkcs15emu_cardos_init(struct sc_pkcs15_card *p15card, struct sc_aid *aid) +{ + sc_card_t *card = p15card->card; + int r; + + LOG_FUNC_CALLED(card->ctx); + + r = sc_pkcs15_bind_internal(p15card, aid); + LOG_TEST_RET(card->ctx, r, "sc_pkcs15_bind_internal failed"); + + /* If card has created algorithms, return */ + sc_log(card->ctx, " card->algorithms:%p card->algorithm_count:%d", card->algorithms, card->algorithm_count); + if (!card->algorithms && card->algorithm_count == 0) { + r = cardos_fix_token_info(p15card); + } + + LOG_FUNC_RETURN(card->ctx, r); +} + + +int +sc_pkcs15emu_cardos_init_ex(struct sc_pkcs15_card *p15card, struct sc_aid *aid) +{ + if (cardos_pkcs15emu_detect_card(p15card)) + return SC_ERROR_WRONG_CARD; + + return sc_pkcs15emu_cardos_init(p15card, aid); +} diff --git a/src/libopensc/pkcs15-cert.c b/src/libopensc/pkcs15-cert.c index 5a607e161c..9088256da6 100644 --- a/src/libopensc/pkcs15-cert.c +++ b/src/libopensc/pkcs15-cert.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -39,7 +39,7 @@ static int parse_x509_cert(sc_context_t *ctx, struct sc_pkcs15_der *der, struct sc_pkcs15_cert *cert) { int r; - struct sc_algorithm_id sig_alg; + struct sc_algorithm_id sig_alg = {0}; struct sc_pkcs15_pubkey *pubkey = NULL; unsigned char *serial = NULL, *issuer = NULL, *subject = NULL, *buf = der->value; size_t serial_len = 0, issuer_len = 0, subject_len = 0, data_len = 0, buflen = der->len; @@ -85,6 +85,8 @@ parse_x509_cert(sc_context_t *ctx, struct sc_pkcs15_der *der, struct sc_pkcs15_c const u8 *obj; size_t objlen; + LOG_FUNC_CALLED(ctx); + memset(cert, 0, sizeof(*cert)); obj = sc_asn1_verify_tag(ctx, buf, buflen, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, &objlen); if (obj == NULL) @@ -98,38 +100,41 @@ parse_x509_cert(sc_context_t *ctx, struct sc_pkcs15_der *der, struct sc_pkcs15_c cert->data.len = data_len; r = sc_asn1_decode(ctx, asn1_cert, obj, objlen, NULL, NULL); - LOG_TEST_RET(ctx, r, "ASN.1 parsing of certificate failed"); - + cert->key = pubkey; cert->version++; + LOG_TEST_GOTO_ERR(ctx, r, "ASN.1 parsing of certificate failed"); + if (!pubkey) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "Unable to decode subjectPublicKeyInfo from cert"); - cert->key = pubkey; + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "Unable to decode subjectPublicKeyInfo from cert"); - sc_asn1_clear_algorithm_id(&sig_alg); if (serial && serial_len) { sc_format_asn1_entry(asn1_serial_number + 0, serial, &serial_len, 1); r = sc_asn1_encode(ctx, asn1_serial_number, &cert->serial, &cert->serial_len); - free(serial); - LOG_TEST_RET(ctx, r, "ASN.1 encoding of serial failed"); + LOG_TEST_GOTO_ERR(ctx, r, "ASN.1 encoding of serial failed"); } if (subject && subject_len) { sc_format_asn1_entry(asn1_subject + 0, subject, &subject_len, 1); r = sc_asn1_encode(ctx, asn1_subject, &cert->subject, &cert->subject_len); - free(subject); - LOG_TEST_RET(ctx, r, "ASN.1 encoding of subject"); + LOG_TEST_GOTO_ERR(ctx, r, "ASN.1 encoding of subject"); } if (issuer && issuer_len) { sc_format_asn1_entry(asn1_issuer + 0, issuer, &issuer_len, 1); r = sc_asn1_encode(ctx, asn1_issuer, &cert->issuer, &cert->issuer_len); - free(issuer); - LOG_TEST_RET(ctx, r, "ASN.1 encoding of issuer"); + LOG_TEST_GOTO_ERR(ctx, r, "ASN.1 encoding of issuer"); } - return SC_SUCCESS; +err: + /* not used for anything */ + sc_asn1_clear_algorithm_id(&sig_alg); + free(serial); + free(subject); + free(issuer); + + LOG_FUNC_RETURN(ctx, r); } @@ -159,12 +164,12 @@ sc_pkcs15_get_name_from_dn(struct sc_context *ctx, const u8 *dn, size_t dn_len, rdn = sc_asn1_skip_tag(ctx, &dn, &dn_len, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, &rdn_len); if (rdn == NULL) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "ASN.1 decoding of Distiguished Name"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "ASN.1 decoding of Distinguished Name"); for (next_ava = rdn, next_ava_len = rdn_len; next_ava_len; ) { const u8 *ava, *dummy, *oidp; struct sc_object_id oid; - size_t ava_len, dummy_len, oid_len; + size_t ava_len = 0, dummy_len, oid_len = 0; /* unwrap the set and point to the next ava */ ava = sc_asn1_skip_tag(ctx, &next_ava, &next_ava_len, SC_ASN1_TAG_SET | SC_ASN1_CONS, &ava_len); @@ -200,6 +205,8 @@ sc_pkcs15_get_name_from_dn(struct sc_context *ctx, const u8 *dn, size_t dn_len, /* Yes, then return the name */ dummy = sc_asn1_skip_tag(ctx, &ava, &ava_len, ava[0] & SC_ASN1_TAG_PRIMITIVE, &dummy_len); + if (dummy == NULL) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "ASN.1 decoding of AVA name"); if (*name == NULL) { *name = malloc(dummy_len); if (*name == NULL) @@ -220,7 +227,7 @@ sc_pkcs15_get_name_from_dn(struct sc_context *ctx, const u8 *dn, size_t dn_len, * The extension is identified by it's oid value. * NOTE: extensions can occur in any number or any order, which is why we * can't parse them with a single pass of the asn1 decoder. - * If is_critical is supplied, then it is set to 1 if the extention is critical + * If is_critical is supplied, then it is set to 1 if the extension is critical * and 0 if it is not. * The data in the extension is extension specific. * The following are common extension values: @@ -255,6 +262,8 @@ sc_pkcs15_get_extension(struct sc_context *ctx, struct sc_pkcs15_cert *cert, { NULL, 0, 0, 0, NULL, NULL } }; + LOG_FUNC_CALLED(ctx); + for (next_ext = cert->extensions, next_ext_len = cert->extensions_len; next_ext_len; ) { /* unwrap the set and point to the next ava */ ext = sc_asn1_skip_tag(ctx, &next_ext, &next_ext_len, @@ -282,14 +291,16 @@ sc_pkcs15_get_extension(struct sc_context *ctx, struct sc_pkcs15_cert *cert, } else { *ext_val_len = MIN(*ext_val_len, val_len); - memcpy(*ext_val, val, *ext_val_len); - free(val); + if (val) { + memcpy(*ext_val, val, *ext_val_len); + free(val); + } } if (is_critical) *is_critical = critical; - r = val_len; + r = (int)val_len; LOG_FUNC_RETURN(ctx, r); } if (val) { @@ -310,22 +321,24 @@ sc_pkcs15_get_extension(struct sc_context *ctx, struct sc_pkcs15_cert *cert, int sc_pkcs15_get_bitstring_extension(struct sc_context *ctx, struct sc_pkcs15_cert *cert, const struct sc_object_id *type, - unsigned long long *value, int *is_critical) + unsigned int *value, int *is_critical) { int r; u8 *bit_string = NULL; size_t bit_string_len=0, val_len = sizeof(*value); struct sc_asn1_entry asn1_bit_string[] = { - { "bitString", SC_ASN1_BIT_STRING, SC_ASN1_TAG_BIT_STRING, 0, value, &val_len }, + { "bitString", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, 0, value, &val_len }, { NULL, 0, 0, 0, NULL, NULL } }; + LOG_FUNC_CALLED(ctx); + r = sc_pkcs15_get_extension(ctx, cert, type, &bit_string, &bit_string_len, is_critical); LOG_TEST_RET(ctx, r, "Get extension error"); r = sc_asn1_decode(ctx, asn1_bit_string, bit_string, bit_string_len, NULL, NULL); - LOG_TEST_RET(ctx, r, "Decoding extension bit string"); free(bit_string); + LOG_TEST_RET(ctx, r, "Decoding extension bit string"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -354,7 +367,7 @@ sc_pkcs15_pubkey_from_cert(struct sc_context *ctx, int sc_pkcs15_read_certificate(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_cert_info *info, - struct sc_pkcs15_cert **cert_out) + int private_obj, struct sc_pkcs15_cert **cert_out) { struct sc_context *ctx = NULL; struct sc_pkcs15_cert *cert = NULL; @@ -371,7 +384,7 @@ sc_pkcs15_read_certificate(struct sc_pkcs15_card *p15card, const struct sc_pkcs1 sc_der_copy(&der, &info->value); } else if (info->path.len) { - r = sc_pkcs15_read_file(p15card, &info->path, &der.value, &der.len); + r = sc_pkcs15_read_file(p15card, &info->path, &der.value, &der.len, private_obj); LOG_TEST_RET(ctx, r, "Unable to read certificate file."); } else { @@ -475,7 +488,11 @@ sc_pkcs15_decode_cdf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_obje return r; LOG_TEST_RET(ctx, r, "ASN.1 decoding failed"); - if (!p15card->app || !p15card->app->ddo.aid.len) { + if (!p15card->app || !p15card->app->ddo.aid.len) { + if (!p15card->file_app) { + free(der->value); + return SC_ERROR_INTERNAL; + } r = sc_pkcs15_make_absolute_path(&p15card->file_app->path, &info.path); LOG_TEST_RET(ctx, r, "Cannot make absolute path"); } @@ -484,6 +501,17 @@ sc_pkcs15_decode_cdf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_obje } sc_log(ctx, "Certificate path '%s'", sc_print_path(&info.path)); + switch (p15card->opts.pin_protected_certificate) { + case SC_PKCS15_CARD_OPTS_PRIV_CERT_DECLASSIFY: + sc_log(ctx, "Declassifying certificate"); + obj->flags &= ~SC_PKCS15_CO_FLAG_PRIVATE; + break; + case SC_PKCS15_CARD_OPTS_PRIV_CERT_IGNORE: + sc_log(ctx, "Ignoring certificate"); + free(der->value); + return 0; + } + obj->type = SC_PKCS15_TYPE_CERT_X509; obj->data = malloc(sizeof(info)); if (obj->data == NULL) @@ -531,6 +559,86 @@ sc_pkcs15_encode_cdf_entry(sc_context_t *ctx, const struct sc_pkcs15_object *obj return r; } +/* Only certain usages are valid for a given algorithm, return all the usages + * that the algorithm supports so we can use it as a filter for all + * the public and private key usages + */ +static unsigned int +sc_pkcs15_alg_flags_from_algorithm(unsigned long algorithm) +{ + switch (algorithm) { + case SC_ALGORITHM_RSA: + return SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP | + SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER | + SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP | + SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | + SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; +#ifdef SC_ALGORITHM_DH + case SC_ALGORITHM_DH: + return SC_PKCS15_PRKEY_USAGE_DERIVE ; +#endif + case SC_ALGORITHM_EC: + return SC_PKCS15_PRKEY_USAGE_DERIVE | SC_PKCS15_PRKEY_USAGE_VERIFY| + SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; + case SC_ALGORITHM_GOSTR3410: + return SC_PKCS15_PRKEY_USAGE_DERIVE | SC_PKCS15_PRKEY_USAGE_VERIFY| + SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; + } + return 0; +} + +/* These are the cert key usage bits that map to various PKCS #11 (and thus PKCS #15) flags */ +#define SC_PKCS15_X509_USAGE_SIGNATURE \ + (SC_X509_DIGITAL_SIGNATURE | \ + SC_X509_NON_REPUDIATION | \ + SC_X509_KEY_CERT_SIGN | \ + SC_X509_CRL_SIGN) +#define SC_PKCS15_X509_USAGE_DERIVE \ + (SC_X509_KEY_ENCIPHERMENT | \ + SC_X509_KEY_AGREEMENT) +#define SC_PKCS15_X509_USAGE_UNWRAP \ + (SC_X509_KEY_ENCIPHERMENT | \ + SC_X509_KEY_AGREEMENT) +#define SC_PKCS15_X509_USAGE_DECRYPT \ + (SC_X509_DATA_ENCIPHERMENT | \ + SC_X509_ENCIPHER_ONLY) +#define SC_PKCS15_X509_USAGE_NONREPUDIATION \ + SC_X509_NON_REPUDIATION + +/* map a cert usage and algorithm to public and private key usages */ +int +sc_pkcs15_map_usage(unsigned int cert_usage, unsigned long algorithm, + unsigned int *pub_usage_ptr, unsigned int *pr_usage_ptr, + int allow_nonrepudiation) +{ + unsigned int pub_usage = 0, pr_usage = 0; + unsigned int alg_flags = sc_pkcs15_alg_flags_from_algorithm(algorithm); + + if (cert_usage & SC_PKCS15_X509_USAGE_SIGNATURE) { + pub_usage |= SC_PKCS15_PRKEY_USAGE_VERIFY|SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER; + pr_usage |= SC_PKCS15_PRKEY_USAGE_SIGN|SC_PKCS15_PRKEY_USAGE_SIGNRECOVER; + } + if (cert_usage & SC_PKCS15_X509_USAGE_DERIVE) { + pub_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE; + pr_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE; + } + if (cert_usage & (SC_PKCS15_X509_USAGE_DECRYPT|SC_PKCS15_X509_USAGE_UNWRAP)) { + pub_usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT; + pr_usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT; + } + if (allow_nonrepudiation && (cert_usage & SC_PKCS15_X509_USAGE_NONREPUDIATION)) { + pub_usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; + pr_usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; + } + /* filter usages algorithm */ + if (pub_usage_ptr) { + *pub_usage_ptr = pub_usage & alg_flags; + } + if (pr_usage_ptr) { + *pr_usage_ptr = pr_usage & alg_flags; + } + return SC_SUCCESS; +} void sc_pkcs15_free_certificate(struct sc_pkcs15_cert *cert) @@ -539,8 +647,7 @@ sc_pkcs15_free_certificate(struct sc_pkcs15_cert *cert) return; } - if (cert->key) - sc_pkcs15_free_pubkey(cert->key); + sc_pkcs15_free_pubkey(cert->key); free(cert->subject); free(cert->issuer); free(cert->serial); @@ -555,7 +662,6 @@ sc_pkcs15_free_cert_info(sc_pkcs15_cert_info_t *cert) { if (!cert) return; - if (cert->value.value) - free(cert->value.value); + free(cert->value.value); free(cert); } diff --git a/src/libopensc/pkcs15-coolkey.c b/src/libopensc/pkcs15-coolkey.c index 5064a0f4f7..bd92c5508a 100644 --- a/src/libopensc/pkcs15-coolkey.c +++ b/src/libopensc/pkcs15-coolkey.c @@ -23,10 +23,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -41,9 +41,6 @@ #include "pkcs15.h" #include "../pkcs11/pkcs11.h" -int sc_pkcs15emu_coolkey_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); - - typedef struct pdata_st { const char *id; const char *label; @@ -150,9 +147,9 @@ coolkey_find_matching_cert(sc_card_t *card, sc_cardctl_coolkey_object_t *in_obj, static int coolkey_get_attribute_ulong(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, CK_ULONG *value) { - const u8 *val; - size_t val_len; - u8 data_type; + const u8 *val = NULL; + size_t val_len = 0; + u8 data_type = 0; int r; r = coolkey_get_attribute(card, obj, type, &val, &val_len, &data_type); @@ -171,8 +168,8 @@ static int coolkey_get_attribute_boolean(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE attr_type) { int r; - const u8 *val; - size_t val_len; + const u8 *val = NULL; + size_t val_len = 0; r = coolkey_get_attribute(card, obj, attr_type, &val, &val_len, NULL); if (r < 0) { @@ -189,7 +186,7 @@ static int coolkey_get_attribute_bytes(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, u8 *data, size_t *data_len, size_t max_data_len) { const u8 *val; - size_t val_len; + size_t val_len = 0; int r; r = coolkey_get_attribute(card, obj, type, &val, &val_len, NULL); @@ -212,7 +209,7 @@ coolkey_get_attribute_bytes_alloc(sc_card_t *card, sc_cardctl_coolkey_object_t * int r; r = coolkey_get_attribute(card, obj, type, &val, &val_len, NULL); - if (r < 0) { + if (r < 0 || val_len < 1) { return r; } *data = malloc(val_len); @@ -426,14 +423,19 @@ coolkey_get_public_key_from_certificate(sc_pkcs15_card_t *p15card, sc_cardctl_co sc_pkcs15_cert_info_t cert_info; sc_pkcs15_cert_t *cert_out = NULL; sc_pkcs15_pubkey_t *key = NULL; - int r; + int r, private_obj; + unsigned int flags; + + memset(&cert_info, 0, sizeof(cert_info)); - cert_info.value.value = NULL; r = coolkey_get_certificate(p15card->card, obj, &cert_info.value); if (r < 0) { goto fail; } - r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out); + + coolkey_get_flags(p15card->card, obj, &flags); + private_obj = flags & SC_PKCS15_CO_FLAG_PRIVATE; + r = sc_pkcs15_read_certificate(p15card, &cert_info, private_obj, &cert_out); if (r < 0) { goto fail; } @@ -463,6 +465,7 @@ coolkey_get_public_key(sc_pkcs15_card_t *p15card, sc_cardctl_coolkey_object_t *o static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card) { + int use_pin_cache_backup = p15card->opts.use_pin_cache; static const pindata pins[] = { { "1", NULL, "", 0x00, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, @@ -484,7 +487,7 @@ static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card) sc_card_t *card = p15card->card; sc_serial_number_t serial; int count; - + struct sc_pkcs15_object *obj; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); @@ -495,18 +498,17 @@ static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card) * anyway */ p15card->opts.use_pin_cache = 0; - /* get the token info from the card */ r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_GET_TOKEN_INFO, p15card->tokeninfo); if (r < 0) { /* put some defaults in if we didn't succeed */ - p15card->tokeninfo->label = strdup("Coolkey"); - p15card->tokeninfo->manufacturer_id = strdup("Unknown"); - p15card->tokeninfo->serial_number = strdup("00000000"); + set_string(&p15card->tokeninfo->label, "Coolkey"); + set_string(&p15card->tokeninfo->manufacturer_id, "Unknown"); + set_string(&p15card->tokeninfo->serial_number, "00000000"); } /* set pins */ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Coolkey adding pins..."); + sc_log(card->ctx, "Coolkey adding pins..."); for (i = 0; pins[i].id; i++) { struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; @@ -528,21 +530,23 @@ static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card) pin_info.tries_left = -1; label = pins[i].label? pins[i].label : p15card->tokeninfo->label; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Coolkey Adding pin %d label=%s",i, label); + sc_log(card->ctx, "Coolkey Adding pin %d label=%s",i, label); strncpy(pin_obj.label, label, SC_PKCS15_MAX_LABEL_SIZE - 1); pin_obj.flags = pins[i].obj_flags; r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); - if (r < 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not add pin object."); } /* set other objects */ r = (card->ops->card_ctl)(card, SC_CARDCTL_COOLKEY_INIT_GET_OBJECTS, &count); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not initiate objects."); + + sc_log(card->ctx, "Iterating over %d objects", count); for (i = 0; i < count; i++) { struct sc_cardctl_coolkey_object coolkey_obj; struct sc_pkcs15_object obj_obj; - struct sc_pkcs15_cert_info cert_info; + struct sc_pkcs15_cert_info cert_info = {0}; struct sc_pkcs15_pubkey_info pubkey_info; struct sc_pkcs15_prkey_info prkey_info; sc_pkcs15_pubkey_t *key = NULL; @@ -553,11 +557,11 @@ static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card) size_t len; r = (card->ops->card_ctl)(card, SC_CARDCTL_COOLKEY_GET_NEXT_OBJECT, &coolkey_obj); - if (r < 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); - - + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not get next object from card."); + sc_log(card->ctx, "Loading object %d", i); memset(&obj_obj, 0, sizeof(obj_obj)); + /* coolkey applets have label only on the certificates, + * but we should copy it also to the keys matching the same ID */ coolkey_get_attribute_bytes(card, &coolkey_obj, CKA_LABEL, (u8 *)obj_obj.label, &len, sizeof(obj_obj.label)); coolkey_get_flags(card, &coolkey_obj, &obj_obj.flags); if (obj_obj.flags & SC_PKCS15_CO_FLAG_PRIVATE) { @@ -570,6 +574,7 @@ static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card) } switch (obj_class) { case CKO_PRIVATE_KEY: + sc_log(card->ctx, "Processing private key object %d", i); r = coolkey_get_attribute_ulong(card, &coolkey_obj, CKA_KEY_TYPE, &key_type); /* default to CKK_RSA */ if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) { @@ -584,7 +589,7 @@ static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card) memset(&prkey_info, 0, sizeof(prkey_info)); coolkey_get_id(card, &coolkey_obj, &prkey_info.id); prkey_info.path = coolkey_obj.path; - prkey_info.key_reference = coolkey_obj.id; + prkey_info.key_reference = (int)coolkey_obj.id; prkey_info.native = 1; coolkey_get_usage(card, &coolkey_obj, &prkey_info.usage); coolkey_get_access(card, &coolkey_obj, &prkey_info.access_flags); @@ -592,12 +597,12 @@ static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card) if (key_type == CKK_RSA) { obj_type = SC_PKCS15_TYPE_PRKEY_RSA; if (key) { - prkey_info.modulus_length = key->u.rsa.modulus.len*8; + prkey_info.modulus_length = key->u.rsa.modulus.len*8; } } else if (key_type == CKK_EC) { - obj_type = SC_PKCS15_TYPE_PUBKEY_EC; + obj_type = SC_PKCS15_TYPE_PRKEY_EC; if (key) { - prkey_info.field_length = key->u.ec.params.field_length; + prkey_info.field_length = key->u.ec.params.field_length; } } else { goto fail; @@ -605,6 +610,7 @@ static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card) break; case CKO_PUBLIC_KEY: + sc_log(card->ctx, "Processing public key object %d", i); r = coolkey_get_attribute_ulong(card, &coolkey_obj, CKA_KEY_TYPE, &key_type); /* default to CKK_RSA */ if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) { @@ -622,22 +628,23 @@ static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card) obj_info = &pubkey_info; memset(&pubkey_info, 0, sizeof(pubkey_info)); r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, key, &pubkey_info.direct.spki.value, - &pubkey_info.direct.spki.len); + &pubkey_info.direct.spki.len); if (r < 0) goto fail; coolkey_get_id(card, &coolkey_obj, &pubkey_info.id); pubkey_info.path = coolkey_obj.path; - pubkey_info.native = 1; - pubkey_info.key_reference = coolkey_obj.id; + pubkey_info.native = 1; + pubkey_info.key_reference = (int)coolkey_obj.id; coolkey_get_usage(card, &coolkey_obj, &pubkey_info.usage); coolkey_get_access(card, &coolkey_obj, &pubkey_info.access_flags); if (key_type == CKK_RSA) { obj_type = SC_PKCS15_TYPE_PUBKEY_RSA; - pubkey_info.modulus_length = key->u.rsa.modulus.len*8; + pubkey_info.modulus_length = key->u.rsa.modulus.len*8; } else if (key_type == CKK_EC) { obj_type = SC_PKCS15_TYPE_PUBKEY_EC; - pubkey_info.field_length = key->u.ec.params.field_length; + pubkey_info.field_length = key->u.ec.params.field_length; } else { + free(pubkey_info.direct.spki.value); goto fail; } /* set the obj values */ @@ -646,6 +653,7 @@ static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card) break; case CKO_CERTIFICATE: + sc_log(card->ctx, "Processing certificate object %d", i); obj_info = &cert_info; memset(&cert_info, 0, sizeof(cert_info)); coolkey_get_id(card, &coolkey_obj, &cert_info.id); @@ -662,9 +670,7 @@ static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card) default: /* no other recognized types which are stored 'on card' */ - break; - } - if (obj_info == NULL) { + sc_log(card->ctx, "Unknown object type %lu, skipping", obj_class); continue; } @@ -672,17 +678,57 @@ static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card) if (r != SC_SUCCESS) sc_log(card->ctx, "sc_pkcs15emu_object_add() returned %d", r); fail: - if (key) { sc_pkcs15_free_pubkey(key); } - + if (key) { + sc_pkcs15_free_pubkey(key); + } + if (r != SC_SUCCESS) { + free(cert_info.value.value); + } } r = (card->ops->card_ctl)(card, SC_CARDCTL_COOLKEY_FINAL_GET_OBJECTS, &count); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not finalize objects."); + + /* Iterate over all the created objects and fill missing labels */ + for (obj = p15card->obj_list; obj != NULL; obj = obj->next) { + struct sc_pkcs15_id *id = NULL; + struct sc_pkcs15_object *cert_object; + + /* label non-empty -- do not overwrite */ + if (obj->label[0] != '\0') + continue; + + switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) { + case SC_PKCS15_TYPE_PUBKEY: + id = &((struct sc_pkcs15_pubkey_info *)obj->data)->id; + break; + case SC_PKCS15_TYPE_PRKEY: + id = &((struct sc_pkcs15_prkey_info *)obj->data)->id; + break; + default: + /* We do not care about other objects */ + continue; + } + r = sc_pkcs15_find_cert_by_id(p15card, id, &cert_object); + if (r != 0) + continue; + + sc_log(card->ctx, "Copy label \"%s\" from cert to key object", + cert_object->label); + memcpy(obj->label, cert_object->label, SC_PKCS15_MAX_LABEL_SIZE); + } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + +err: + sc_pkcs15_card_clear(p15card); + p15card->opts.use_pin_cache = use_pin_cache_backup; + + LOG_FUNC_RETURN(card->ctx, r); } int sc_pkcs15emu_coolkey_init_ex(sc_pkcs15_card_t *p15card, - struct sc_aid *aid, sc_pkcs15emu_opt_t *opts) + struct sc_aid *aid) { sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; @@ -690,14 +736,10 @@ sc_pkcs15emu_coolkey_init_ex(sc_pkcs15_card_t *p15card, LOG_FUNC_CALLED(ctx); - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - rv = sc_pkcs15emu_coolkey_init(p15card); - else { - rv = coolkey_detect_card(p15card); - if (rv) - LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_CARD); - rv = sc_pkcs15emu_coolkey_init(p15card); - } + rv = coolkey_detect_card(p15card); + if (rv) + LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_CARD); + rv = sc_pkcs15emu_coolkey_init(p15card); LOG_FUNC_RETURN(ctx, rv); } diff --git a/src/libopensc/pkcs15-data.c b/src/libopensc/pkcs15-data.c index d1d389d458..0d69650f95 100644 --- a/src/libopensc/pkcs15-data.c +++ b/src/libopensc/pkcs15-data.c @@ -17,10 +17,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -41,9 +41,10 @@ int sc_pkcs15_read_data_object(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_data_info *info, + int private_obj, struct sc_pkcs15_data **data_object_out) { - struct sc_context *ctx = p15card->card->ctx; + struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_data *data_object; struct sc_pkcs15_der der; int r; @@ -53,15 +54,14 @@ sc_pkcs15_read_data_object(struct sc_pkcs15_card *p15card, LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (!info->data.value) { - r = sc_pkcs15_read_file(p15card, &info->path, (unsigned char **) &info->data.value, (size_t *) &info->data.len); + r = sc_pkcs15_read_file(p15card, &info->path, (unsigned char **) &info->data.value, (size_t *) &info->data.len, private_obj); LOG_TEST_RET(ctx, r, "Cannot get DATA object data"); } - sc_der_copy(&der, &info->data); - if (!der.value) - LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate memory for der value"); + r = sc_der_copy(&der, &info->data); + LOG_TEST_RET(ctx, r, "Cannot allocate memory for der value"); - data_object = calloc(sizeof(struct sc_pkcs15_data), 1); + data_object = calloc(1, sizeof(struct sc_pkcs15_data)); if (!data_object) { free(der.value); LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate memory for data object"); @@ -100,9 +100,11 @@ int sc_pkcs15_decode_dodf_entry(struct sc_pkcs15_card *p15card, asn1_data[2]; struct sc_asn1_pkcs15_object data_obj = { obj, asn1_com_data_attr, NULL, asn1_type_data_attr }; - size_t label_len = sizeof(info.app_label); + size_t label_len = sizeof(info.app_label) - 1; int r; + memset(info.app_label, 0, sizeof(info.app_label)); + sc_copy_asn1_entry(c_asn1_com_data_attr, asn1_com_data_attr); sc_copy_asn1_entry(c_asn1_type_data_attr, asn1_type_data_attr); sc_copy_asn1_entry(c_asn1_data, asn1_data); @@ -119,9 +121,12 @@ int sc_pkcs15_decode_dodf_entry(struct sc_pkcs15_card *p15card, r = sc_asn1_decode(ctx, asn1_data, *buf, *buflen, buf, buflen); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) return r; - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "ASN.1 decoding failed"); + LOG_TEST_RET(ctx, r, "ASN.1 decoding failed"); - if (!p15card->app || !p15card->app->ddo.aid.len) { + if (!p15card->app || !p15card->app->ddo.aid.len) { + if (!p15card->file_app) { + return SC_ERROR_INTERNAL; + } r = sc_pkcs15_make_absolute_path(&p15card->file_app->path, &info.path); if (r < 0) return r; @@ -133,7 +138,7 @@ int sc_pkcs15_decode_dodf_entry(struct sc_pkcs15_card *p15card, obj->type = SC_PKCS15_TYPE_DATA_OBJECT; obj->data = malloc(sizeof(info)); if (obj->data == NULL) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(obj->data, &info, sizeof(info)); return SC_SUCCESS; diff --git a/src/libopensc/pkcs15-din-66291.c b/src/libopensc/pkcs15-din-66291.c new file mode 100644 index 0000000000..c0565b66c4 --- /dev/null +++ b/src/libopensc/pkcs15-din-66291.c @@ -0,0 +1,282 @@ +/* + * PKCS15 emulation layer for DIN 66291–4 profile. + * + * Copyright (C) 2017, Frank Morgner + * + * 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 + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "internal.h" +#include "common/compat_strlcpy.h" +#include "log.h" +#include "pkcs15.h" +#include +#include + +static const unsigned char aid_CIA[] = {0xE8, 0x28, 0xBD, 0x08, 0x0F, + 0xA0, 0x00, 0x00, 0x01, 0x67, 0x45, 0x53, 0x49, 0x47, 0x4E}; +static const unsigned char aid_ESIGN[] = {0xA0, 0x00, 0x00, 0x01, 0x67, + 0x45, 0x53, 0x49, 0x47, 0x4E}; +static const unsigned char aid_gematik_egk[] = {0xD2, 0x76, + 0x00, 0x01, 0x44, 0x80, 0x00}; + +static int +sc_pkcs15emu_din_66291_init(sc_pkcs15_card_t *p15card) +{ + /* EF.C.CH.AUT + * fileIdentifier ´C5 00´ + * shortFileIdentifier ´01´= 1 + * PrK.CH.AUT + * keyIdentifier ´02´ = 2 + * privateKey …, Moduluslänge 2048 Bit + * + * EF.C.CH.ENC + * fileIdentifier ´C2 00´ + * shortFileIdentifier ´02´= 2 + * PrK.CH.ENC + * keyIdentifier ´03´ = 3 + * privateKey …, Moduluslänge 2048 Bit + */ + sc_path_t path; + size_t i; + struct sc_pin_cmd_data data; + const unsigned char user_pin_ref = 0x02; + + sc_path_set(&path, SC_PATH_TYPE_DF_NAME, aid_ESIGN, sizeof aid_ESIGN, 0, 0); + if (SC_SUCCESS != sc_select_file(p15card->card, &path, NULL)) + return SC_ERROR_WRONG_CARD; + + memset(&data, 0, sizeof(data)); + data.cmd = SC_PIN_CMD_GET_INFO; + data.pin_type = SC_AC_CHV; + data.pin_reference = user_pin_ref; + + if (SC_SUCCESS == sc_pin_cmd(p15card->card, &data)) { + const unsigned char user_pin_id = 1; + + for (i = 0; i < 2; i++) { + const char *pin_names[3] = { "PIN", "PUK" }; + const int pin_min[] = {6, 10}; + const int pin_max[] = {8, 8}; + const unsigned char user_puk_id = 2; + const int pin_id[] = {user_pin_id, user_puk_id}; + const int pin_flags[] = {SC_PKCS15_PIN_FLAG_INITIALIZED, + SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN|SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED}; + const int max_tries[] = {3, 10}; + struct sc_pkcs15_auth_info pin_info; + struct sc_pkcs15_object pin_obj; + + memset(&pin_info, 0, sizeof(pin_info)); + memset(&pin_obj, 0, sizeof(pin_obj)); + + pin_info.auth_id.value[0] = pin_id[i]; + pin_info.auth_id.len = 1; + pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; + pin_info.attrs.pin.flags = pin_flags[i]; + pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; + pin_info.attrs.pin.min_length = pin_min[i]; + pin_info.attrs.pin.stored_length = pin_max[i]; + pin_info.attrs.pin.max_length = pin_max[i]; + pin_info.max_tries = max_tries[i]; + + strlcpy(pin_obj.label, pin_names[i], sizeof(pin_obj.label)); + + /* catch the differences between PIN and PUK */ + if (pin_flags[i] & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) { + pin_info.tries_left = max_tries[i]; + } else { + pin_info.attrs.pin.reference = user_pin_ref; + pin_info.tries_left = data.pin1.tries_left; + pin_info.logged_in = data.pin1.logged_in; + pin_obj.auth_id.value[0] = user_puk_id; + pin_obj.auth_id.len = 1; + } + + if (0 > sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info)) { + sc_pkcs15_card_clear(p15card); + return SC_ERROR_INTERNAL; + } + } + + for (i = 0; i < 2; i++) { + struct sc_aid aid; + const char *din_66291_cert_fids[] = { "C500", "C200"}; + const char prk_id[] = { 0x10, 0x11,}; + struct sc_pkcs15_cert_info cert_info; + struct sc_pkcs15_object cert_obj; + struct sc_pkcs15_prkey_info prkey_info; + struct sc_pkcs15_object prkey_obj; + const int prk_usage[2] = { + SC_PKCS15_PRKEY_USAGE_ENCRYPT + | SC_PKCS15_PRKEY_USAGE_DECRYPT + | SC_PKCS15_PRKEY_USAGE_SIGN, + SC_PKCS15_PRKEY_USAGE_NONREPUDIATION}; + + memcpy(aid.value, aid_CIA, sizeof aid_CIA); + aid.len = sizeof aid_CIA; + + memset(&prkey_info, 0, sizeof(prkey_info)); + memset(&prkey_obj, 0, sizeof(prkey_obj)); + memset(&cert_info, 0, sizeof(cert_info)); + memset(&cert_obj, 0, sizeof(cert_obj)); + + + sc_format_path(din_66291_cert_fids[i], &cert_info.path); + if (SC_SUCCESS != sc_select_file(p15card->card, &cert_info.path, NULL)) + continue; + cert_info.path.aid = aid; + + cert_info.id.value[0] = prk_id[i]; + cert_info.id.len = 1; + + if (0 > sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info)) + continue; + + if (i == 0) { + sc_pkcs15_cert_t *cert; + if (SC_SUCCESS == sc_pkcs15_read_certificate(p15card, &cert_info, 0, &cert)) { + static const struct sc_object_id cn_oid = {{ 2, 5, 4, 3, -1 }}; + u8 *cn_name = NULL; + size_t cn_len = 0; + sc_pkcs15_get_name_from_dn(p15card->card->ctx, cert->subject, + cert->subject_len, &cn_oid, &cn_name, &cn_len); + if (cn_len > 0) { + char *token_name = malloc(cn_len+1); + if (token_name) { + memcpy(token_name, cn_name, cn_len); + token_name[cn_len] = '\0'; + free(p15card->tokeninfo->label); + p15card->tokeninfo->label = token_name; + } + } + free(cn_name); + sc_pkcs15_free_certificate(cert); + } + } + + memset(&prkey_info, 0, sizeof(prkey_info)); + memset(&prkey_obj, 0, sizeof(prkey_obj)); + + prkey_info.id.value[0] = prk_id[i]; + prkey_info.id.len = 1; + prkey_info.usage = prk_usage[i]; + prkey_info.native = 1; + prkey_info.key_reference = prk_id[i]; + prkey_info.modulus_length = 2048; + prkey_obj.auth_id.value[0] = user_pin_id; + prkey_obj.auth_id.len = 1; + prkey_obj.user_consent = 0; + prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; + + if (0 > sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info)) + continue; + } + } + + return SC_SUCCESS; +} + +int sc_pkcs15emu_din_66291_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid) +{ + int r = SC_ERROR_WRONG_CARD; + sc_path_t path; + unsigned char *tokeninfo_content = NULL; + struct sc_file *file_tokeninfo = NULL; + struct sc_pkcs15_tokeninfo *tokeninfo = NULL; + sc_serial_number_t serial; + + if (!p15card || ! p15card->card) + return SC_ERROR_INVALID_ARGUMENTS; + + SC_FUNC_CALLED(p15card->card->ctx, 1); + + tokeninfo = sc_pkcs15_tokeninfo_new(); + if (!p15card || !tokeninfo + || (aid && (aid->len != sizeof aid_CIA + || 0 != memcmp(aid->value, aid_CIA, sizeof aid_CIA)))) + goto err; + + if (!p15card->tokeninfo + || !p15card->tokeninfo->profile_indication.name + || 0 != strcmp("DIN V 66291", + p15card->tokeninfo->profile_indication.name)) { + /* it is possible that p15card->tokeninfo has not been touched yet */ + if (SC_SUCCESS == sc_path_set(&path, SC_PATH_TYPE_DF_NAME, + aid_CIA, sizeof aid_CIA, 0, 0) + && SC_SUCCESS == sc_select_file(p15card->card, &path, NULL)) { + sc_format_path("5032", &path); + if (SC_SUCCESS != sc_select_file(p15card->card, &path, &file_tokeninfo)) + goto err; + + tokeninfo_content = malloc(file_tokeninfo->size); + if (!tokeninfo_content) + goto err; + r = sc_read_binary(p15card->card, 0, tokeninfo_content, file_tokeninfo->size, 0); + if (r < 0) + goto err; + r = sc_pkcs15_parse_tokeninfo(p15card->card->ctx, tokeninfo, tokeninfo_content, r); + if (r != SC_SUCCESS) + goto err; + + if (!tokeninfo->profile_indication.name + || 0 != strcmp("DIN V 66291", + tokeninfo->profile_indication.name)) { + goto err; + } + } else { + /* BARMER eGK doesn't include MF / DF.CIA_ESIGN / EF.CIA_Info + * just detect it via its specific AID */ + if (SC_SUCCESS != sc_path_set(&path, SC_PATH_TYPE_DF_NAME, + aid_gematik_egk, sizeof aid_gematik_egk, 0, 0) + || SC_SUCCESS != sc_select_file(p15card->card, &path, NULL)) + goto err; + + tokeninfo->profile_indication.name = strdup("DIN V 66291"); + } + } + + if (SC_SUCCESS != sc_pkcs15emu_din_66291_init(p15card)) + goto err; + + /* save tokeninfo and file_tokeninfo */ + sc_pkcs15_free_tokeninfo(p15card->tokeninfo); + sc_file_free(p15card->file_tokeninfo); + p15card->tokeninfo = tokeninfo; + p15card->file_tokeninfo = file_tokeninfo; + tokeninfo = NULL; + file_tokeninfo = NULL; + + /* get the card serial number */ + if (!p15card->tokeninfo->serial_number + && SC_SUCCESS == sc_card_ctl(p15card->card, SC_CARDCTL_GET_SERIALNR, &serial)) { + char serial_hex[SC_MAX_SERIALNR*2+2]; + sc_bin_to_hex(serial.value, serial.len , serial_hex, sizeof serial_hex, 0); + set_string(&p15card->tokeninfo->serial_number, serial_hex); + } + + r = SC_SUCCESS; + +err: + sc_pkcs15_free_tokeninfo(tokeninfo); + sc_file_free(file_tokeninfo); + free(tokeninfo_content); + + return r; +} diff --git a/src/libopensc/pkcs15-dnie.c b/src/libopensc/pkcs15-dnie.c index 00865ad481..c72ee79c6b 100644 --- a/src/libopensc/pkcs15-dnie.c +++ b/src/libopensc/pkcs15-dnie.c @@ -16,12 +16,12 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "libopensc/log.h" @@ -143,6 +143,7 @@ static int sc_pkcs15emu_dnie_init(sc_pkcs15_card_t * p15card) size_t len = sizeof(buf); int rv; struct sc_pkcs15_cert_info *p15_info = NULL; + int use_pin_cache_backup = p15card->opts.use_pin_cache; sc_context_t *ctx = p15card->card->ctx; LOG_FUNC_CALLED(ctx); @@ -169,37 +170,34 @@ static int sc_pkcs15emu_dnie_init(sc_pkcs15_card_t * p15card) } /* Set root path of this application */ + sc_file_free(p15card->file_app); p15card->file_app = sc_file_new(); + if (NULL == p15card->file_app) { + rv = SC_ERROR_NOT_ENOUGH_MEMORY; + LOG_TEST_GOTO_ERR(ctx, rv, "Can not create file."); + } sc_format_path("3F00", &p15card->file_app->path); /* Load TokenInfo */ rv = dump_ef(p15card->card, "3F0050155032", buf, &len); - if (rv != SC_SUCCESS) { - sc_log(ctx, "Reading of EF.TOKENINFO failed: %d", rv); - LOG_FUNC_RETURN(ctx, rv); - } + LOG_TEST_GOTO_ERR(ctx, rv, "Reading of EF.TOKENINFO failed."); + rv = sc_pkcs15_parse_tokeninfo(p15card->card->ctx, p15card->tokeninfo, buf, len); - if (rv != SC_SUCCESS) { - sc_log(ctx, "Decoding of EF.TOKENINFO failed: %d", rv); - LOG_FUNC_RETURN(ctx, rv); - } + LOG_TEST_GOTO_ERR(ctx, rv, "Decoding of EF.TOKENINFO failed."); /* Only accept the original stuff */ - if (strcmp(p15card->tokeninfo->manufacturer_id, "DGP-FNMT") != 0) - LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_CARD); + if (strcmp(p15card->tokeninfo->manufacturer_id, "DGP-FNMT") != 0) { + rv = SC_ERROR_WRONG_CARD; + LOG_TEST_GOTO_ERR(ctx, rv, "Wrong card."); + } /* Load ODF */ rv = dump_ef(p15card->card, "3F0050155031", buf, &len); - if (rv != SC_SUCCESS) { - sc_log(ctx, "Reading of ODF failed: %d", rv); - LOG_FUNC_RETURN(ctx, rv); - } + LOG_TEST_GOTO_ERR(ctx, rv, "Reading of ODF failed."); + rv = parse_odf(buf, len, p15card); - if (rv != SC_SUCCESS) { - sc_log(ctx, "Decoding of ODF failed: %d", rv); - LOG_FUNC_RETURN(ctx, rv); - } + LOG_TEST_GOTO_ERR(ctx, rv, "Decoding of ODF failed."); /* Decode EF.PrKDF, EF.PuKDF and EF.CDF */ for (df = p15card->df_list; df != NULL; df = df->next) { @@ -267,6 +265,11 @@ static int sc_pkcs15emu_dnie_init(sc_pkcs15_card_t * p15card) } LOG_FUNC_RETURN(ctx, SC_SUCCESS); + +err: + sc_pkcs15_card_clear(p15card); + p15card->opts.use_pin_cache = use_pin_cache_backup; + LOG_FUNC_RETURN(ctx, rv); } #endif @@ -274,17 +277,13 @@ static int sc_pkcs15emu_dnie_init(sc_pkcs15_card_t * p15card) /* public functions for in-built module */ /****************************************/ int sc_pkcs15emu_dnie_init_ex(sc_pkcs15_card_t * p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t * opts) + struct sc_aid *aid) { int r=SC_SUCCESS; sc_context_t *ctx = p15card->card->ctx; LOG_FUNC_CALLED(ctx); #if defined(ENABLE_OPENSSL) && defined(ENABLE_SM) - /* if no check flag execute unconditionally */ - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - LOG_FUNC_RETURN(ctx, sc_pkcs15emu_dnie_init(p15card)); /* check for proper card */ r = dnie_match_card(p15card->card); if (r == 0) diff --git a/src/libopensc/pkcs15-dtrust.c b/src/libopensc/pkcs15-dtrust.c new file mode 100644 index 0000000000..2c4fd07e1f --- /dev/null +++ b/src/libopensc/pkcs15-dtrust.c @@ -0,0 +1,130 @@ +/* + * PKCS15 emulation layer for D-Trust card. + * + * Copyright (C) 2024, Mario Haustein + * + * 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 + */ + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "internal.h" +#include "pkcs15.h" + +static int +_dtrust_parse_df(struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_pkcs15_object *pkobjs[32]; + struct sc_pkcs15_prkey_info *prkey_info; + int rv, i, count; + + LOG_FUNC_CALLED(ctx); + + if (!df) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + if (df->enumerated) + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + + rv = sc_pkcs15_parse_df(p15card, df); + LOG_TEST_RET(ctx, rv, "DF parse error"); + + if (df->type != SC_PKCS15_PRKDF) + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + + rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, pkobjs, sizeof(pkobjs) / sizeof(pkobjs[0])); + LOG_TEST_RET(ctx, rv, "Cannot get PRKEY objects list"); + + count = rv; + for (i = 0; i < count; i++) { + prkey_info = (struct sc_pkcs15_prkey_info *)pkobjs[i]->data; + + switch (p15card->card->type) { + /* Cards with EC keys, don't encode the curve size in the + * private key directory file. We need to set the field_length + * element after parsing the private key directory file. */ + case SC_CARD_TYPE_DTRUST_V4_1_MULTI: + case SC_CARD_TYPE_DTRUST_V4_1_M100: + case SC_CARD_TYPE_DTRUST_V4_4_MULTI: + prkey_info->field_length = 256; + break; + + case SC_CARD_TYPE_DTRUST_V5_1_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_M100: + case SC_CARD_TYPE_DTRUST_V5_4_MULTI: + prkey_info->field_length = 384; + break; + } + } + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + +static int +dtrust_pkcs15emu_detect_card(sc_pkcs15_card_t *p15card) +{ + if (p15card->card->type < SC_CARD_TYPE_DTRUST_V4_1_STD) + return SC_ERROR_WRONG_CARD; + + if (p15card->card->type > SC_CARD_TYPE_DTRUST_V5_4_MULTI) + return SC_ERROR_WRONG_CARD; + + return SC_SUCCESS; +} + +static int +sc_pkcs15emu_dtrust_init(struct sc_pkcs15_card *p15card, struct sc_aid *aid) +{ + struct sc_context *ctx = p15card->card->ctx; + int rv; + + LOG_FUNC_CALLED(ctx); + + rv = sc_pkcs15_bind_internal(p15card, aid); + + p15card->ops.parse_df = _dtrust_parse_df; + +#if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) + struct sc_pkcs15_search_key sk; + struct sc_pkcs15_object *objs[8]; + int i, len; + + memset(&sk, 0, sizeof(sk)); + sk.class_mask = SC_PKCS15_SEARCH_CLASS_AUTH; + len = sc_pkcs15_search_objects(p15card, &sk, (struct sc_pkcs15_object **)&objs, sizeof(objs) / sizeof(struct sc_pkcs15_object *)); + for (i = 0; i < len; i++) { + if (!strcmp(objs[i]->label, "CAN")) { + /* Mark "Card CAN" as NOT a PIN object, so that it doesn't get it's own PKCS#11 slot */ + objs[i]->type &= ~SC_PKCS15_TYPE_AUTH_PIN; + } + } +#endif + + LOG_FUNC_RETURN(ctx, rv); +} + +int +sc_pkcs15emu_dtrust_init_ex(struct sc_pkcs15_card *p15card, struct sc_aid *aid) +{ + if (dtrust_pkcs15emu_detect_card(p15card)) + return SC_ERROR_WRONG_CARD; + + return sc_pkcs15emu_dtrust_init(p15card, aid); +} diff --git a/src/libopensc/pkcs15-emulator-filter.c b/src/libopensc/pkcs15-emulator-filter.c new file mode 100644 index 0000000000..eba69b7245 --- /dev/null +++ b/src/libopensc/pkcs15-emulator-filter.c @@ -0,0 +1,128 @@ +/* + * pkcs15-emulator-filter.c: PKCS #15 emulator filter + * + * Copyright (C) 2021 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "internal.h" +#include "pkcs15-syn.h" +#include "pkcs15-emulator-filter.h" + +static int add_emul(struct _sc_pkcs15_emulators* filtered_emulators, + struct sc_pkcs15_emulator_handler* emul_handler) +{ + struct sc_pkcs15_emulator_handler** lst; + int *cp, max, i; + + if (!filtered_emulators || !emul_handler || !emul_handler->name || !emul_handler->handler) + return SC_ERROR_INVALID_ARGUMENTS; + + lst = filtered_emulators->list_of_handlers; + cp = &filtered_emulators->ccount; + max = SC_MAX_PKCS15_EMULATORS; + + if (*cp > max) + return SC_ERROR_INVALID_ARGUMENTS; + if (*cp == max) + return SC_ERROR_TOO_MANY_OBJECTS; + + for (i = 0; i < *cp; i++) { + if (!lst[i]) + return SC_ERROR_OBJECT_NOT_VALID; + if (!strcmp(lst[i]->name, emul_handler->name)) + return SC_SUCCESS; + } + + lst[*cp] = emul_handler; + (*cp)++; + return SC_SUCCESS; +} + +static int add_emul_list(struct _sc_pkcs15_emulators* filtered_emulators, + struct sc_pkcs15_emulator_handler* emulators) +{ + struct sc_pkcs15_emulator_handler* lst; + int i, r; + + if (!filtered_emulators || !emulators) + return SC_ERROR_INVALID_ARGUMENTS; + + lst = emulators; + for(i = 0; lst[i].name; i++) { + if ((r = add_emul(filtered_emulators, &lst[i]))) + return r; + } + return SC_SUCCESS; +} + +int set_emulators(sc_context_t *ctx, struct _sc_pkcs15_emulators* filtered_emulators, const scconf_list *list, + struct sc_pkcs15_emulator_handler* internal, struct sc_pkcs15_emulator_handler* old) +{ + const scconf_list *item; + int *cp, i, r, count; + + LOG_FUNC_CALLED(ctx); + + if (!filtered_emulators || !list || !internal || !old) + return SC_ERROR_INVALID_ARGUMENTS; + + cp = &filtered_emulators->ccount; + r = SC_SUCCESS; + + for (item = list; item; item = item->next) { + if (!item->data) + continue; + + if (!strcmp(item->data, "internal")) { + if ((r = add_emul_list(filtered_emulators, internal))) + goto out; + } else if (!strcmp(item->data, "old")) { + if ((r = add_emul_list(filtered_emulators, old))) + goto out; + } else { + count = *cp; + for (i = 0; internal[i].name; i++) { + if (!strcmp(internal[i].name, item->data)) { + if ((r = add_emul(filtered_emulators, &internal[i]))) + goto out; + break; + } + } + for (i = 0; old[i].name && count == *cp; i++) { + if (!strcmp(old[i].name, item->data)) { + if ((r = add_emul(filtered_emulators, &old[i]))) + goto out; + break; + } + } + if (count == *cp) + sc_log(ctx, "Warning: Trying to add non-existing emulator '%s'.", item->data); + } + } +out: + if (r == SC_SUCCESS || r == SC_ERROR_TOO_MANY_OBJECTS) { + filtered_emulators->list_of_handlers[*cp] = NULL; + if (r == SC_ERROR_TOO_MANY_OBJECTS) + sc_log(ctx, "Warning: Number of filtered emulators exceeded %d.", SC_MAX_PKCS15_EMULATORS); + } + LOG_FUNC_RETURN(ctx, r); +} diff --git a/src/libopensc/pkcs15-emulator-filter.h b/src/libopensc/pkcs15-emulator-filter.h new file mode 100644 index 0000000000..29346786d2 --- /dev/null +++ b/src/libopensc/pkcs15-emulator-filter.h @@ -0,0 +1,28 @@ +/* + * pkcs15-emulator-filter.h: PKCS #15 emulator filter + * + * Copyright (C) 2021 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +struct _sc_pkcs15_emulators { + struct sc_pkcs15_emulator_handler *list_of_handlers[SC_MAX_PKCS15_EMULATORS + 1]; + int ccount; +}; + +int set_emulators(sc_context_t *ctx, struct _sc_pkcs15_emulators* filtered_emulators, const scconf_list *list, + struct sc_pkcs15_emulator_handler* builtin, struct sc_pkcs15_emulator_handler* old); diff --git a/src/libopensc/pkcs15-eoi.c b/src/libopensc/pkcs15-eoi.c new file mode 100644 index 0000000000..8430ef5987 --- /dev/null +++ b/src/libopensc/pkcs15-eoi.c @@ -0,0 +1,146 @@ +/* + * Support for the eOI card. + * + * Copyright (C) 2022 Luka Logar + * + * 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 + */ + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "log.h" +#include "pkcs15.h" + +#if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) + +#include "cards.h" +#include "card-eoi.h" + +int sc_pkcs15emu_eoi_init_ex(struct sc_pkcs15_card *p15card, struct sc_aid *aid) +{ + struct sc_card *card = p15card->card; + struct eoi_privdata *privdata = (struct eoi_privdata *)card->drv_data; + struct sc_pkcs15_search_key sk; + struct sc_pkcs15_object *objs[MAX_OBJECTS]; + int i, j, len; + + LOG_FUNC_CALLED(card->ctx); + + if (card->type != SC_CARD_TYPE_EOI && card->type != SC_CARD_TYPE_EOI_CONTACTLESS) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_CARD); + + if (!privdata) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + + /* + * Some of the data is not accessible over the unencrypted channel + * when contactless reader is used. So start SM now (if not yet established). + */ + if (card->type == SC_CARD_TYPE_EOI_CONTACTLESS && card->sm_ctx.sm_mode == SM_MODE_NONE) { + int r = card->sm_ctx.ops.open(card); + if (r != SC_SUCCESS) + LOG_FUNC_RETURN(card->ctx, r); + } + + /* + * Get the card objects, so we can manipulate them. See below + */ + LOG_TEST_RET(card->ctx, sc_pkcs15_bind_internal(p15card, aid), + "sc_pkcs15_bind_internal failed"); + + /* + * PIN objects: + * 1) Find the "Card CAN" PIN and store it's path, so we'll be able to fetch the CAN and do the PACE auth + * 2) Add PIN's auth_info->path to the list of paths that can fail on select. sc_pin_cmd would break otherwise + */ + memset(&sk, 0, sizeof(sk)); + sk.class_mask = SC_PKCS15_SEARCH_CLASS_AUTH; + len = sc_pkcs15_search_objects(p15card, &sk, (struct sc_pkcs15_object **)&objs, MAX_OBJECTS); + for (i = 0, j = 0; i < len; i++) { + struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *)objs[i]->data; + if (auth_info && auth_info->auth_id.len == 8 && !strcmp((char*)auth_info->auth_id.value, "Card CAN")) { + auth_info->path.type = SC_PATH_TYPE_PATH; + /* Read the file that contains serial and encrypted CAN */ + if (sc_pkcs15_read_file(p15card, &auth_info->path, &privdata->enc_can.value, &privdata->enc_can.len, 0) == SC_SUCCESS) { + /* File should be 24 bytes long */ + if (privdata->enc_can.len != 24) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_CORRUPTED_DATA); + if (strlen(p15card->tokeninfo->serial_number) != 20) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_CORRUPTED_DATA); + /* First 8 bytes are used as serial number */ + sc_bin_to_hex(privdata->enc_can.value, 8, &p15card->tokeninfo->serial_number[4], 17, 0); + } + /* Do not add "Card CAN" to the list of PIN paths to ignore, otherwise the 2nd PKCS#15 app can not access it */ + auth_info = NULL; + /* Mark "Card CAN" as NOT a PIN object, so that it doesn't get it's own PKCS#11 slot */ + objs[i]->type &= ~SC_PKCS15_TYPE_AUTH_PIN; + } + /* + * For some reason QES app has "Norm PUK" not flagged as unblocking PIN and thus "Norm PUK" appears as a slot in + * PKCS#11. Flag it as unblockingPin, so it doesn't appear as a separate slot. + */ + if (auth_info && auth_info->auth_id.len == 8 && !strcmp((char*)auth_info->auth_id.value, "Norm PUK")) { + auth_info->attrs.pin.flags |= SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN; + } + if (auth_info) { + privdata->pin_paths[j++] = &auth_info->path; + } + } + + /* + * Private key objects: + * 1) Rename "Card PIN" to "Norm PIN" as it's the later name that is used throughout the PKCS#15 objects + * 2) Add the key references to the prkey_mappings array, as it seems that eOI expects them counted from 0xA0 up (starting from 1 within each app) + * Currently there are 3 private keys on the card + * key_ref + * 2 - for pinless entry (Prijava brez PIN-a), maps to 0xA1 + * 1 - for authentication in QES app (Podpis in prijava), maps to 0xA1 + * 3 - for signing in QES app (Podpis in prijava), maps to 0xA2 + */ + memset(&sk, 0, sizeof(sk)); + sk.class_mask = SC_PKCS15_SEARCH_CLASS_PRKEY; + len = sc_pkcs15_search_objects(p15card, &sk, (struct sc_pkcs15_object **)&objs, MAX_OBJECTS); + /* + * If both PKCS#15 apps are enabled, prkey_mappings can already be partially filled up from the first PKCS#15 app + * as the privdata is shared between both apps which use the same driver + */ + for (j = 0; j < MAX_OBJECTS && privdata->prkey_mappings[j][1] != 0; j++) { + /* NOP */ + } + for (i = 0; i < len; i++) { + struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *)objs[i]->data; + if ((objs[i]->auth_id.len == 8) && !strncmp((char*)objs[i]->auth_id.value, "Card PIN", 8)) { + memcpy(objs[i]->auth_id.value, "Norm PIN", 8); + } + if (prkey_info && j < MAX_OBJECTS) { + privdata->prkey_mappings[j][0] = prkey_info->key_reference; + privdata->prkey_mappings[j++][1] = 0xA0 + (i + 1); + } + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +#else + +int sc_pkcs15emu_eoi_init_ex(struct sc_pkcs15_card *p15card, struct sc_aid *aid) +{ + LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_WRONG_CARD); +} + +#endif diff --git a/src/libopensc/pkcs15-esinit.c b/src/libopensc/pkcs15-esinit.c index e4749461b3..bdade82079 100644 --- a/src/libopensc/pkcs15-esinit.c +++ b/src/libopensc/pkcs15-esinit.c @@ -11,11 +11,11 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* Initially written by Weitao Sun (weitao@ftsafe.com) 2008*/ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -29,8 +29,6 @@ #define MANU_ID "entersafe" -int sc_pkcs15emu_entersafe_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); - static int entersafe_detect_card( sc_pkcs15_card_t *p15card) { sc_card_t *card = p15card->card; @@ -55,39 +53,33 @@ static int sc_pkcs15emu_entersafe_init( sc_pkcs15_card_t *p15card) /* get serial number */ r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); + if (r != SC_SUCCESS) + return SC_ERROR_INTERNAL; r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0); if (r != SC_SUCCESS) return SC_ERROR_INTERNAL; - if (p15card->tokeninfo->serial_number) - free(p15card->tokeninfo->serial_number); - p15card->tokeninfo->serial_number = malloc(strlen(buf) + 1); + + set_string(&p15card->tokeninfo->serial_number, buf); if (!p15card->tokeninfo->serial_number) return SC_ERROR_INTERNAL; - strcpy(p15card->tokeninfo->serial_number, buf); /* the manufacturer ID, in this case Giesecke & Devrient GmbH */ - if (p15card->tokeninfo->manufacturer_id) - free(p15card->tokeninfo->manufacturer_id); - p15card->tokeninfo->manufacturer_id = malloc(strlen(MANU_ID) + 1); - if (!p15card->tokeninfo->manufacturer_id) + set_string(&p15card->tokeninfo->manufacturer_id, MANU_ID); + if (!p15card->tokeninfo->manufacturer_id) { + free(p15card->tokeninfo->serial_number); + p15card->tokeninfo->serial_number = NULL; return SC_ERROR_INTERNAL; - strcpy(p15card->tokeninfo->manufacturer_id, MANU_ID); + } return SC_SUCCESS; } int sc_pkcs15emu_entersafe_init_ex(sc_pkcs15_card_t *p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t *opts) + struct sc_aid *aid) { SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - return sc_pkcs15emu_entersafe_init(p15card); - else { - int r = entersafe_detect_card(p15card); - if (r) - return SC_ERROR_WRONG_CARD; - return sc_pkcs15emu_entersafe_init(p15card); - } + if (entersafe_detect_card(p15card)) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_entersafe_init(p15card); } diff --git a/src/libopensc/pkcs15-esteid.c b/src/libopensc/pkcs15-esteid.c deleted file mode 100644 index 361b2525a4..0000000000 --- a/src/libopensc/pkcs15-esteid.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * PKCS15 emulation layer for EstEID card. - * - * Copyright (C) 2004, Martin Paljak - * Copyright (C) 2004, Bud P. Bruegger - * Copyright (C) 2004, Antonino Iacono - * Copyright (C) 2003, Olaf Kirch - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#include "common/compat_strlcpy.h" -#include "common/compat_strlcat.h" - -#include "internal.h" -#include "pkcs15.h" -#include "esteid.h" - -int sc_pkcs15emu_esteid_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); - -static void -set_string (char **strp, const char *value) -{ - if (*strp) - free (*strp); - *strp = value ? strdup (value) : NULL; -} - - -int -select_esteid_df (sc_card_t * card) -{ - int r; - sc_path_t tmppath; - sc_format_path ("3F00EEEE", &tmppath); - r = sc_select_file (card, &tmppath, NULL); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "esteid select DF failed"); - return r; -} - -static int -sc_pkcs15emu_esteid_init (sc_pkcs15_card_t * p15card) -{ - sc_card_t *card = p15card->card; - unsigned char buff[128]; - int r, i; - sc_path_t tmppath; - - set_string (&p15card->tokeninfo->label, "ID-kaart"); - set_string (&p15card->tokeninfo->manufacturer_id, "AS Sertifitseerimiskeskus"); - - /* Select application directory */ - sc_format_path ("3f00eeee5044", &tmppath); - r = sc_select_file (card, &tmppath, NULL); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "select esteid PD failed"); - - /* read the serial (document number) */ - r = sc_read_record (card, SC_ESTEID_PD_DOCUMENT_NR, buff, sizeof(buff), SC_RECORD_BY_REC_NR); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "read document number failed"); - buff[r] = '\0'; - set_string (&p15card->tokeninfo->serial_number, (const char *) buff); - - p15card->tokeninfo->flags = SC_PKCS15_TOKEN_PRN_GENERATION - | SC_PKCS15_TOKEN_EID_COMPLIANT - | SC_PKCS15_TOKEN_READONLY; - - /* add certificates */ - for (i = 0; i < 2; i++) { - static const char *esteid_cert_names[2] = { - "Isikutuvastus", - "Allkirjastamine"}; - static char const *esteid_cert_paths[2] = { - "3f00eeeeaace", - "3f00eeeeddce"}; - static int esteid_cert_ids[2] = {1, 2}; - - struct sc_pkcs15_cert_info cert_info; - struct sc_pkcs15_object cert_obj; - - memset(&cert_info, 0, sizeof(cert_info)); - memset(&cert_obj, 0, sizeof(cert_obj)); - - cert_info.id.value[0] = esteid_cert_ids[i]; - cert_info.id.len = 1; - sc_format_path(esteid_cert_paths[i], &cert_info.path); - strlcpy(cert_obj.label, esteid_cert_names[i], sizeof(cert_obj.label)); - r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); - if (r < 0) - return SC_ERROR_INTERNAL; - if (i == 0) { - sc_pkcs15_cert_t *cert; - r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert); - if (r == SC_SUCCESS) { - static const struct sc_object_id cn_oid = {{ 2, 5, 4, 3, -1 }}; - u8 *cn_name = NULL; - size_t cn_len = 0; - sc_pkcs15_get_name_from_dn(card->ctx, cert->subject, - cert->subject_len, &cn_oid, &cn_name, &cn_len); - if (cn_len > 0) { - char *token_name = malloc(cn_len+1); - if (token_name) { - memcpy(token_name, cn_name, cn_len); - token_name[cn_len] = '\0'; - set_string(&p15card->tokeninfo->label, (const char*)token_name); - free(token_name); - } - } - free(cn_name); - sc_pkcs15_free_certificate(cert); - } - } - } - - /* the file with key pin info (tries left) */ - sc_format_path ("3f000016", &tmppath); - r = sc_select_file (card, &tmppath, NULL); - if (r < 0) - return SC_ERROR_INTERNAL; - - /* add pins */ - for (i = 0; i < 3; i++) { - unsigned char tries_left; - static const char *esteid_pin_names[3] = { - "PIN1", - "PIN2", - "PUK" }; - - static const int esteid_pin_min[3] = {4, 5, 8}; - static const int esteid_pin_ref[3] = {1, 2, 0}; - static const int esteid_pin_authid[3] = {1, 2, 3}; - static const int esteid_pin_flags[3] = {0, 0, SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN}; - - struct sc_pkcs15_auth_info pin_info; - struct sc_pkcs15_object pin_obj; - - memset(&pin_info, 0, sizeof(pin_info)); - memset(&pin_obj, 0, sizeof(pin_obj)); - - /* read the number of tries left for the PIN */ - r = sc_read_record (card, i + 1, buff, sizeof(buff), SC_RECORD_BY_REC_NR); - if (r < 0) - return SC_ERROR_INTERNAL; - tries_left = buff[5]; - - pin_info.auth_id.len = 1; - pin_info.auth_id.value[0] = esteid_pin_authid[i]; - pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; - pin_info.attrs.pin.reference = esteid_pin_ref[i]; - pin_info.attrs.pin.flags = esteid_pin_flags[i]; - pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; - pin_info.attrs.pin.min_length = esteid_pin_min[i]; - pin_info.attrs.pin.stored_length = 12; - pin_info.attrs.pin.max_length = 12; - pin_info.attrs.pin.pad_char = '\0'; - pin_info.tries_left = (int)tries_left; - pin_info.max_tries = 3; - - strlcpy(pin_obj.label, esteid_pin_names[i], sizeof(pin_obj.label)); - pin_obj.flags = esteid_pin_flags[i]; - - /* Link normal PINs with PUK */ - if (i < 2) { - pin_obj.auth_id.len = 1; - pin_obj.auth_id.value[0] = 3; - } - - r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); - if (r < 0) - return SC_ERROR_INTERNAL; - } - - /* add private keys */ - for (i = 0; i < 2; i++) { - static int prkey_pin[2] = {1, 2}; - static int prkey_usage[2] = { - SC_PKCS15_PRKEY_USAGE_ENCRYPT - | SC_PKCS15_PRKEY_USAGE_DECRYPT - | SC_PKCS15_PRKEY_USAGE_SIGN, - SC_PKCS15_PRKEY_USAGE_NONREPUDIATION}; - - static const char *prkey_name[2] = { - "Isikutuvastus", - "Allkirjastamine"}; - - struct sc_pkcs15_prkey_info prkey_info; - struct sc_pkcs15_object prkey_obj; - - memset(&prkey_info, 0, sizeof(prkey_info)); - memset(&prkey_obj, 0, sizeof(prkey_obj)); - - prkey_info.id.len = 1; - prkey_info.id.value[0] = prkey_pin[i]; - prkey_info.usage = prkey_usage[i]; - prkey_info.native = 1; - prkey_info.key_reference = i + 1; - if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V30) - prkey_info.modulus_length = 2048; - else - prkey_info.modulus_length = 1024; - - strlcpy(prkey_obj.label, prkey_name[i], sizeof(prkey_obj.label)); - prkey_obj.auth_id.len = 1; - prkey_obj.auth_id.value[0] = prkey_pin[i]; - prkey_obj.user_consent = 0; - prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; - - r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); - if (r < 0) - return SC_ERROR_INTERNAL; - } - - return SC_SUCCESS; -} - -static int esteid_detect_card(sc_pkcs15_card_t *p15card) -{ - if (is_esteid_card(p15card->card)) - return SC_SUCCESS; - else - return SC_ERROR_WRONG_CARD; -} - -int sc_pkcs15emu_esteid_init_ex(sc_pkcs15_card_t *p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t *opts) -{ - - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - return sc_pkcs15emu_esteid_init(p15card); - else { - int r = esteid_detect_card(p15card); - if (r) - return SC_ERROR_WRONG_CARD; - return sc_pkcs15emu_esteid_init(p15card); - } -} diff --git a/src/libopensc/pkcs15-esteid2018.c b/src/libopensc/pkcs15-esteid2018.c new file mode 100644 index 0000000000..48edbdeacc --- /dev/null +++ b/src/libopensc/pkcs15-esteid2018.c @@ -0,0 +1,262 @@ +/* + * PKCS15 emulation layer for EstEID card issued from December 2018. + * + * Copyright (C) 2019, Martin Paljak + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "common/compat_strlcpy.h" + +#include "internal.h" +#include "opensc.h" +#include "pkcs15.h" + +static int +is_latvian_eid(sc_pkcs15_card_t *p15card) +{ + return p15card->card->type == SC_CARD_TYPE_LATEID_2018 || + p15card->card->type == SC_CARD_TYPE_LATEID_2018_V2_2025; +} + +static int sc_pkcs15emu_esteid2018_init(sc_pkcs15_card_t *p15card) { + sc_card_t *card = p15card->card; + u8 buff[11]; + int r, i, cert_slot = 0; + size_t field_length = 0, taglen, buflen; + sc_path_t tmppath; + + /* Read documber number to be used as serial */ + sc_format_path("3F00D003", &tmppath); + LOG_TEST_RET(card->ctx, sc_select_file(card, &tmppath, NULL), "SELECT docnr"); + r = sc_read_binary(card, 0, buff, 11, 0); + LOG_TEST_RET(card->ctx, r, "read document number failed"); + const u8 *tag = sc_asn1_find_tag(card->ctx, buff, (size_t)r, 0x04, &taglen); + if (tag == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + + for (size_t j = 0; j < taglen; j++) + if (!isalnum(tag[j])) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + free(p15card->tokeninfo->serial_number); + p15card->tokeninfo->serial_number = malloc(taglen + 1); + if (!p15card->tokeninfo->serial_number) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + + set_string(&p15card->tokeninfo->label, "ID-kaart"); + set_string(&p15card->tokeninfo->manufacturer_id, "IDEMIA"); + p15card->tokeninfo->serial_number = memcpy(p15card->tokeninfo->serial_number, tag, taglen); + p15card->tokeninfo->serial_number[taglen] = '\0'; + p15card->tokeninfo->flags = SC_PKCS15_TOKEN_READONLY; + + if (is_latvian_eid(p15card)) { + u8 *buf; + const u8 *ptr; + sc_pkcs15_object_t obj = {0}; + sc_pkcs15_cert_info_t *cert_info = NULL; + + if (!p15card->file_app) { + p15card->file_app = sc_file_new(); + } + if (!p15card->file_app) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + + sc_format_path("3F00adf1", &p15card->file_app->path); + sc_format_path("3F00adf17005", &tmppath); + r = sc_pkcs15_read_file(p15card, &tmppath, &buf, &buflen, 0); + LOG_TEST_GOTO_ERR(card->ctx, r, "Reading EF.CDF file failed"); + ptr = buf; + r = sc_pkcs15_decode_cdf_entry(p15card, &obj, &ptr, &buflen); + LOG_TEST_GOTO_ERR(card->ctx, r, "Decoding EF.CDF file failed"); + + cert_info = (struct sc_pkcs15_cert_info *)obj.data; + if (cert_info && cert_info->path.len > 0) { + cert_slot = cert_info->path.value[cert_info->path.len - 1] - 1; + if (cert_slot != 0 && cert_slot != 1) { + /* unknown slot, see `cert_paths` below */ + r = SC_ERROR_UNKNOWN_DATA_RECEIVED; + LOG_TEST_GOTO_ERR(card->ctx, r, "Unknown certificate path"); + } + } + + sc_pkcs15_free_cert_info(cert_info); + sc_file_free(p15card->file_app); + p15card->file_app = NULL; + free(buf); + } + + /* add certificates */ + for (i = 0; i < 2; i++) { + static const char *esteid_cert_names[2] = {"Isikutuvastus", "Allkirjastamine"}; + static const char *cert_paths[2][2] = { + {"3f00:adf1:3401", "3f00:adf2:341f"}, + {"3f00:adf1:3402", "3f00:adf2:341e"} + }; + static const u8 esteid_cert_ids[2] = {1, 2}; + + struct sc_pkcs15_cert_info cert_info = { + .id = {.len = 1, .value[0] = esteid_cert_ids[i]} + }; + struct sc_pkcs15_object cert_obj = {0}; + + strlcpy(cert_obj.label, esteid_cert_names[i], sizeof(cert_obj.label)); + sc_format_path(cert_paths[cert_slot][i], &cert_info.path); + r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not add cert oebjct"); + + // Read data from first cert + if (i != 0) + continue; + + sc_pkcs15_cert_t *cert = NULL; + r = sc_pkcs15_read_certificate(p15card, &cert_info, 0, &cert); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not read authentication certificate"); + + if (cert->key->algorithm == SC_ALGORITHM_EC) + field_length = cert->key->u.ec.params.field_length; + + static const struct sc_object_id cn_oid = { + {2, 5, 4, 3, -1} + }; + u8 *cn_name = NULL; + size_t cn_len = 0; + sc_pkcs15_get_name_from_dn(card->ctx, cert->subject, cert->subject_len, &cn_oid, &cn_name, &cn_len); + sc_pkcs15_free_certificate(cert); + if (cn_len > 0) { + char *token_name = (char *)realloc(cn_name, cn_len + 1); + if (token_name) { + token_name[cn_len] = '\0'; + free(p15card->tokeninfo->label); + p15card->tokeninfo->label = token_name; + } else + free(cn_name); + } + } + + /* add pins */ + static const u8 pin_authid[3] = {1, 2, 3}; + for (i = 0; i < 3; i++) { + static const char *esteid_pin_names[3] = {"PIN1", "PIN2", "PUK"}; + static const size_t pin_min[2][3] = { + {4, 5, 8}, // Estonian + {4, 6, 8}, // Latvian + }; + static const int esteid_pin_ref[3] = {0x01, 0x85, 0x02}; + static const char *esteid_pin_path[3] = {"3F00", "3F00ADF2", "3F00"}; + + static const unsigned int esteid_pin_flags[3] = { + SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_TYPE_FLAGS_PIN_GLOBAL, + SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_TYPE_FLAGS_PIN_LOCAL, + SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_TYPE_FLAGS_PUK_GLOBAL}; + + struct sc_pkcs15_auth_info pin_info = { + .auth_id = {.len = 1, .value[0] = pin_authid[i]}, + .auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN, + .attrs = { + .pin = { + .reference = esteid_pin_ref[i], + .flags = esteid_pin_flags[i], + .type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, + .min_length = pin_min[is_latvian_eid(p15card)][i], + .stored_length = 12, + .max_length = 12, + .pad_char = 0xFF}}, + .tries_left = 3, + .max_tries = 3 + }; + struct sc_pkcs15_object pin_obj = { + .flags = esteid_pin_flags[i]}; + + sc_format_path(esteid_pin_path[i], &pin_info.path); + strlcpy(pin_obj.label, esteid_pin_names[i], sizeof(pin_obj.label)); + + /* Link normal PINs with PUK */ + if (i < 2) { + pin_obj.auth_id.len = 1; + pin_obj.auth_id.value[0] = 3; + } + + r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not add pin object"); + } + + // trigger PIN counter refresh via pin_cmd + struct sc_pkcs15_object *objs[3]; + r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH, objs, 3); + if (r != 3) { + sc_log(card->ctx, "Can not get auth objects"); + goto err; + } + for (i = 0; i < r; i++) { + r = sc_pkcs15_get_pin_info(p15card, objs[i]); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not get pin object"); + } + + /* add private keys */ + for (i = 0; i < 2; i++) { + static const u8 prkey_id[2] = {1, 2}; + static const char *prkey_name[2] = {"Isikutuvastus", "Allkirjastamine"}; + static const char *prkey_path[2] = {"3F00:ADF1", "3F00:ADF2"}; + static const unsigned int prkey_usage[2] = {SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_DERIVE, + SC_PKCS15_PRKEY_USAGE_NONREPUDIATION}; + static const int prkey_consent[2] = {0, 1}; + static const u8 prkey_ref[2][2] = { + {0x81, 0x9F}, // Slot 1 + {0x82, 0x9E}, // Slot 2 + }; + + struct sc_pkcs15_prkey_info prkey_info = { + .id = {.len = 1, .value[0] = prkey_id[i]}, + .native = 1, + .key_reference = prkey_ref[is_latvian_eid(p15card)][i], + .field_length = field_length, + .usage = prkey_usage[i] + }; + struct sc_pkcs15_object prkey_obj = { + .auth_id = {.len = 1, .value[0] = pin_authid[i]}, + .user_consent = prkey_consent[i], + .flags = SC_PKCS15_CO_FLAG_PRIVATE + }; + + sc_format_path(prkey_path[i], &prkey_info.path); + strlcpy(prkey_obj.label, prkey_name[i], sizeof(prkey_obj.label)); + + r = sc_pkcs15emu_add_ec_prkey(p15card, &prkey_obj, &prkey_info); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not add private key object"); + } + + return SC_SUCCESS; +err: + sc_pkcs15_card_clear(p15card); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); +} + +int sc_pkcs15emu_esteid2018_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid) { + if (p15card->card->type == SC_CARD_TYPE_ESTEID_2018 || + p15card->card->type == SC_CARD_TYPE_ESTEID_2018_V2_2025 || + is_latvian_eid(p15card)) + return sc_pkcs15emu_esteid2018_init(p15card); + return SC_ERROR_WRONG_CARD; +} diff --git a/src/libopensc/pkcs15-esteid2025.c b/src/libopensc/pkcs15-esteid2025.c new file mode 100644 index 0000000000..bd6b42fff5 --- /dev/null +++ b/src/libopensc/pkcs15-esteid2025.c @@ -0,0 +1,264 @@ +/* + * PKCS15 emulation layer for EstEID card issued from December 2025. + * + * Copyright (C) 2025, Raul Metsma + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "common/compat_strlcpy.h" + +#include "internal.h" +#include "opensc.h" +#include "pkcs15.h" + +static int +is_fin_eid(sc_pkcs15_card_t *p15card) +{ + return p15card->card->type == SC_CARD_TYPE_FINEID_2022 || + p15card->card->type == SC_CARD_TYPE_FINEID_2025; +} + +static int +sc_pkcs15emu_esteid2025_init(sc_pkcs15_card_t *p15card) +{ + sc_card_t *card = p15card->card; + int r, i; + size_t field_length = 0; + sc_path_t tmppath; + static const u8 prkey_id[2] = {0x01, 0x02}; + static const u8 pin_authid[3] = {1, 2, 3}; + + if (is_fin_eid(p15card)) { + u8 buf[SC_MAX_APDU_BUFFER_SIZE]; + const u8 *tag; + size_t taglen; + + sc_format_path("5032", &tmppath); + r = sc_select_file(card, &tmppath, NULL); + LOG_TEST_RET(card->ctx, r, "Selecting CIA path failed"); + + r = sc_read_binary(p15card->card, 4, buf, sizeof(buf), 0); + LOG_TEST_RET(card->ctx, r, "Reading tokeninfo file failed"); + + tag = sc_asn1_find_tag(card->ctx, buf, r, 0x04, &taglen); + if (tag == NULL) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_CARD, "Finding document number tag failed"); + + for (size_t j = 0; j < taglen; j++) { + if (!isalnum(tag[j])) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_CARD, "Invalid character in document number"); + } + + free(p15card->tokeninfo->serial_number); + p15card->tokeninfo->serial_number = malloc(taglen + 1); + if (p15card->tokeninfo->serial_number == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + p15card->tokeninfo->serial_number = memcpy(p15card->tokeninfo->serial_number, tag, taglen); + p15card->tokeninfo->serial_number[taglen] = '\0'; + } else { + u8 *buf; + size_t buflen = 9; + + /* Read document number to be used as serial */ + sc_format_path("DFDD5007", &tmppath); + r = sc_select_file(card, &tmppath, NULL); + LOG_TEST_RET(card->ctx, r, "Selecting document number file failed"); + + buf = malloc(buflen + 1); + if (!buf) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + r = sc_read_binary(card, 0, buf, buflen, 0); + if (r < 0) { + free(buf); + LOG_TEST_GOTO_ERR(card->ctx, r, "Reading document number failed"); + } + + for (int j = 0; j < r; j++) { + if (!isalnum(buf[j])) { + free(buf); + LOG_TEST_GOTO_ERR(card->ctx, SC_ERROR_INVALID_CARD, "Invalid character in document number"); + } + } + buf[r] = '\0'; + + free(p15card->tokeninfo->serial_number); + p15card->tokeninfo->serial_number = (char *)buf; + } + + set_string(&p15card->tokeninfo->label, "ID-kaart"); + set_string(&p15card->tokeninfo->manufacturer_id, "Thales"); + p15card->tokeninfo->flags = SC_PKCS15_TOKEN_READONLY; + + /* add certificates */ + for (i = 0; i < 2; i++) { + static const char *cert_names[2] = {"Isikutuvastus", "Allkirjastamine"}; + static const char *cert_paths[2][2] = { + {"ADF1:3411", "ADF2:3421"}, + {"4331", "5016:4332"}, + }; + + struct sc_pkcs15_cert_info cert_info = { + .id = {.len = 1, .value[0] = prkey_id[i]} + }; + struct sc_pkcs15_object cert_obj = {0}; + + strlcpy(cert_obj.label, cert_names[i], sizeof(cert_obj.label)); + sc_format_path(cert_paths[is_fin_eid(p15card)][i], &cert_info.path); + r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not add cert object"); + + if (i != 0) + continue; + + sc_pkcs15_cert_t *cert = NULL; + r = sc_pkcs15_read_certificate(p15card, &cert_info, 0, &cert); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not read authentication certificate"); + + if (cert->key->algorithm == SC_ALGORITHM_EC) + field_length = cert->key->u.ec.params.field_length; + + static const struct sc_object_id cn_oid = { + {2, 5, 4, 3, -1} + }; + u8 *cn_name = NULL; + size_t cn_len = 0; + r = sc_pkcs15_get_name_from_dn(card->ctx, cert->subject, cert->subject_len, &cn_oid, &cn_name, &cn_len); + sc_pkcs15_free_certificate(cert); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not read authentication certificate"); + if (cn_len > 0) { + char *token_name = (char *)realloc(cn_name, cn_len + 1); + if (token_name) { + token_name[cn_len] = '\0'; + free(p15card->tokeninfo->label); + p15card->tokeninfo->label = token_name; + } else + free(cn_name); + } + } + + /* add pins */ + for (i = 0; i < 3; i++) { + static const char *pin_names[3] = {"PIN1", "PIN2", "PUK"}; + static const size_t pin_min[2][3] = { + {4, 5, 8}, + {4, 6, 8}, + }; + static const int pin_ref[2][3] = { + {0x81, 0x82, 0x83}, + {0x11, 0x82, 0x83}, + }; + + static const unsigned int pin_flags[2][3] = { + {SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_TYPE_FLAGS_PIN_LOCAL, + SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_TYPE_FLAGS_PIN_LOCAL, + SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_TYPE_FLAGS_PUK_LOCAL}, + {SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_TYPE_FLAGS_PIN_GLOBAL, + SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_TYPE_FLAGS_PIN_LOCAL, + SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_TYPE_FLAGS_PUK_LOCAL}, + }; + + struct sc_pkcs15_auth_info pin_info = { + .auth_id = {.len = 1, .value[0] = pin_authid[i]}, + .auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN, + .attrs = { + .pin = { + .reference = pin_ref[is_fin_eid(p15card)][i], + .flags = pin_flags[is_fin_eid(p15card)][i], + .type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, + .min_length = pin_min[is_fin_eid(p15card)][i], + .stored_length = 12, + .max_length = 12, + .pad_char = 0x00}}, + .tries_left = 3, + .max_tries = 3 + }; + struct sc_pkcs15_object pin_obj = {.flags = pin_flags[is_fin_eid(p15card)][i]}; + + strlcpy(pin_obj.label, pin_names[i], sizeof(pin_obj.label)); + + /* Link normal PINs with PUK */ + if (i < 2) { + pin_obj.auth_id.len = 1; + pin_obj.auth_id.value[0] = 3; + } + + r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not add pin object"); + } + + // trigger PIN counter refresh via pin_cmd + struct sc_pkcs15_object *objs[3]; + r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH, objs, 3); + if (r != 3) { + sc_log(card->ctx, "Can not get auth objects"); + goto err; + } + for (i = 0; i < r; i++) { + r = sc_pkcs15_get_pin_info(p15card, objs[i]); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not get pin object"); + } + + /* add private keys */ + for (i = 0; i < 2; i++) { + static const u8 prkey_ref[2][2] = { + {0x01, 0x05}, + {0x01, 0x02}, + }; + static const char *prkey_name[2] = {"Isikutuvastus", "Allkirjastamine"}; + static const unsigned int prkey_usage[2] = {SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_DERIVE, + SC_PKCS15_PRKEY_USAGE_NONREPUDIATION}; + static const int prkey_consent[2] = {0, 1}; + + struct sc_pkcs15_prkey_info prkey_info = { + .id = {.len = 1, .value[0] = prkey_id[i]}, + .native = 1, + .key_reference = prkey_ref[is_fin_eid(p15card)][i], + .field_length = field_length, + .usage = prkey_usage[i] + }; + struct sc_pkcs15_object prkey_obj = { + .auth_id = {.len = 1, .value[0] = pin_authid[i]}, + .user_consent = prkey_consent[i], + .flags = SC_PKCS15_CO_FLAG_PRIVATE + }; + + strlcpy(prkey_obj.label, prkey_name[i], sizeof(prkey_obj.label)); + + r = sc_pkcs15emu_add_ec_prkey(p15card, &prkey_obj, &prkey_info); + LOG_TEST_GOTO_ERR(card->ctx, r, "Could not add private key object"); + } + + return SC_SUCCESS; +err: + sc_pkcs15_card_clear(p15card); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); +} + +int +sc_pkcs15emu_esteid2025_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid) +{ + if (p15card->card->type == SC_CARD_TYPE_ESTEID_2025 || is_fin_eid(p15card)) + return sc_pkcs15emu_esteid2025_init(p15card); + return SC_ERROR_WRONG_CARD; +} diff --git a/src/libopensc/pkcs15-gemsafeGPK.c b/src/libopensc/pkcs15-gemsafeGPK.c deleted file mode 100644 index 0b8185db1e..0000000000 --- a/src/libopensc/pkcs15-gemsafeGPK.c +++ /dev/null @@ -1,526 +0,0 @@ -/* - * partial PKCS15 emulation for gemsafe GPK cards - * - * Copyright (C) 2005, Douglas E. Engert - * 2004, Nils Larsch - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#include "common/compat_strlcpy.h" -#include "internal.h" -#include "pkcs15.h" -#include "log.h" -#include "cardctl.h" - -#define MANU_ID "GemSAFE on GPK16000" - -int sc_pkcs15emu_gemsafeGPK_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); - -static int (*pin_cmd_save)(struct sc_card *, struct sc_pin_cmd_data *, - int *tries_left); - -typedef struct cdata_st { - const char *label; - int authority; - const char *path; - const char *id; - int obj_flags; -} cdata; - -typedef struct pdata_st { - const char *id; - const char *label; - const char *path; - int ref; - int type; - unsigned int maxlen; - unsigned int minlen; - unsigned int storedlen; - int flags; - int tries_left; - const char pad_char; - int obj_flags; -} pindata; - -typedef struct prdata_st { - const char *id; - const char *label; - unsigned int modulus_len; - int usage; - const char *path; - int ref; - const char *auth_id; - int obj_flags; -} prdata; - -typedef struct keyinfo_st { - int fileid; - sc_pkcs15_id_t id; - unsigned int modulus_len; - u8 modulus[1024/8]; -} keyinfo; - -#define USAGE_NONREP SC_PKCS15_PRKEY_USAGE_NONREPUDIATION -#define USAGE_KE SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ - SC_PKCS15_PRKEY_USAGE_DECRYPT | \ - SC_PKCS15_PRKEY_USAGE_WRAP | \ - SC_PKCS15_PRKEY_USAGE_UNWRAP -#define USAGE_AUT SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ - SC_PKCS15_PRKEY_USAGE_DECRYPT | \ - SC_PKCS15_PRKEY_USAGE_WRAP | \ - SC_PKCS15_PRKEY_USAGE_UNWRAP | \ - SC_PKCS15_PRKEY_USAGE_SIGN - -static const u8 gemsafe_aid[] = {0xA0, 0x00, 0x00, 0x00, 0x18, - 0x0F, 0x00, 0x00, 0x01, 0x63, 0x00, 0x01}; - -static int my_pin_cmd(sc_card_t * card, struct sc_pin_cmd_data * data, - int *tries_left) -{ - /* GemSAFE pin uses a null terminated string with 0xFF */ - /* so we need to add the 0x00 to the pin then pad with 0xFF */ - - int r; - const u8 *saved_data = NULL; - int saved_len = 0; - u8 newpin[8]; - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); - - memset(newpin, 0xff, sizeof(newpin)); - - if (data->pin1.data && data->pin1.len < 8 && data->pin1.len > 0) { - memcpy(newpin,data->pin1.data, (size_t)data->pin1.len); - newpin[data->pin1.len] = 0x00; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "pin len=%d", data->pin1.len); - - saved_data = data->pin1.data; - saved_len = data->pin1.len; - data->pin1.data = newpin; - data->pin1.len = sizeof(newpin); - } - - r = pin_cmd_save(card, data, tries_left); - - if (saved_data) { - data->pin1.data = saved_data; - data->pin1.len = saved_len; - } - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); -} - - -static int is_seq(unsigned char * seq, unsigned int *seq_size, unsigned int *seq_len) -{ - int i,j,k; - - if (seq[0] != 0x30) - return 0; /* not a sequence */ - if (seq[1] & 0x80) { - i = seq[1] & 0x7f; - if (i > 2 || i == 0) - return 0; /* cert would be bigger then 65k or zero */ - if (seq[2] == 0) - return 0; /* DER would not have extra zero */ - k = 0; - for (j = 0; j < i; j++) { - k = (k << 8) + seq[j + 2]; - } - if (k < 128) - return 0; /* DER would have used single byte for len */ - } else { - i = 0; - k = seq[1]; - } - - *seq_size = i + 2; - *seq_len = k; - return 1; -} - -static int gemsafe_detect_card(sc_pkcs15_card_t *p15card) -{ - sc_card_t *card = p15card->card; - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - - - if (strcmp(card->name, "Gemplus GPK")) - return SC_ERROR_WRONG_CARD; - - return SC_SUCCESS; -} - -static int sc_pkcs15emu_gemsafeGPK_init(sc_pkcs15_card_t *p15card) -{ - const cdata certs[] = { - {"User certificate",0, "","1", 0}, - {NULL, 0, NULL, NULL, 0} - }; - - const pindata pins[] = { - { "01", "pin", "3F000200", 0x00, - SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, - 8, 4, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | - SC_PKCS15_PIN_FLAG_LOCAL, -1, 0x00, - SC_PKCS15_CO_FLAG_PRIVATE }, - { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0} - }; - - const prdata prkeys[] = { - { "01", "AUTH key", 1024, USAGE_AUT, "I0009", - 0x00, "01", 0}, - { NULL, NULL, 0, 0, NULL, 0, NULL, 0} - }; - - int r, i, j; - int dfpath; - sc_path_t path; - sc_file_t *file = NULL; - sc_card_t *card = p15card->card; - unsigned char *gsdata = NULL; - unsigned int idxlen, idx1, idx2, seq_len1, seq_len2, seq_size1, seq_size2; - sc_serial_number_t serial; - - u8 sysrec[7]; - int num_keyinfo = 0; - keyinfo kinfo[8]; /* will loook for 8 keys */ - u8 modulus_buf[ 1 + 1024 / 8]; /* tag+modulus */ - u8 *cp; - char buf[256]; - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - - /* need to limit to 248 */ - card->max_send_size = 248; - card->max_recv_size = 248; - - - /* could read this off card if needed */ - - p15card->tokeninfo->label = strdup("GemSAFE"); - p15card->tokeninfo->manufacturer_id = strdup(MANU_ID); - /* get serial number */ - r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); - r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0); - if (r != SC_SUCCESS) - return SC_ERROR_INTERNAL; - p15card->tokeninfo->serial_number = strdup(buf); - - /* test if we have a gemsafe app df */ - memset(&path, 0, sizeof(path)); - memcpy(path.value, gemsafe_aid, sizeof(gemsafe_aid)); - path.len = sizeof(gemsafe_aid); - path.type = SC_PATH_TYPE_DF_NAME; - r = sc_select_file(card, &path, &file); - if (r < 0) { - /* OK, then lets try for 3f000200 */ - sc_format_path("3F000200",&path); - path.type = SC_PATH_TYPE_PATH; - r = sc_select_file(card, &path, &file); - } - - if (r < 0) - return SC_ERROR_WRONG_CARD; - - /* we will use dfpath in all other references */ - dfpath = file->id; - sc_file_free(file); - file = NULL; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "GemSafe file found, id=%d",dfpath); - - /* There may be more then one key in the directory. */ - /* we need to find them so we can associate them with the */ - /* the certificate. The files are 0007 to 000f */ - - for (i = 7; i < 16; i++) { - path.value[0] = 0x00; - path.value[1] = i; - path.len = 2; - path.type = SC_PATH_TYPE_FILE_ID; - r = sc_select_file(card, &path, NULL); - if (r < 0) - continue; - r = sc_read_record(card, 1, sysrec, sizeof(sysrec), SC_RECORD_BY_REC_NR); - if (r != 7 || sysrec[0] != 0) { - continue; - } - if (sysrec[5] != 0x00) { - continue; - } - - switch (sysrec[1]) { - case 0x00: kinfo[num_keyinfo].modulus_len = 512 / 8; break; - case 0x10: kinfo[num_keyinfo].modulus_len = 768 / 8; break; - case 0x11: kinfo[num_keyinfo].modulus_len = 1024 / 8; break; - default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported modulus length"); - continue; - } - - kinfo[num_keyinfo].fileid = i; - sc_pkcs15_format_id("", &kinfo[num_keyinfo].id); - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"reading modulus"); - r = sc_read_record(card, 2, modulus_buf, - kinfo[num_keyinfo].modulus_len+1, SC_RECORD_BY_REC_NR); - if (r < 0) - continue; - - /* need to reverse the modulus skiping the tag */ - j = kinfo[num_keyinfo].modulus_len; - cp = kinfo[num_keyinfo].modulus; - while (j--) - *cp++ = modulus_buf[j + 1]; - num_keyinfo++; - } - - /* Get the gemsafe data with the cert */ - sc_format_path("3F000200004", &path); - - /* file.id has the real DF of the GemSAFE file from above*/ - path.value[2] = dfpath >> 8; - path.value[3] = dfpath & 0xff; - - if (sc_select_file(card, &path, &file) < 0) { - return SC_ERROR_WRONG_CARD; - } - - /* the GemSAFE file has our cert, but we do not know the format */ - /* of the file. But we do know a cert has SEQ SEQ SEQOF INT 2 */ - /* so we will look for that. We assume cert is larger then 127 bytes */ - /* and less then 65K, and must be fit in the file->size */ - /* There is a chance that we might find something that is not */ - /* a cert, but the chances are low. If GemPlus ever publishes */ - /* the format of the file, we can used that instead. */ - - /* For performance reasons we will only */ - /* read part of the file , as it is about 6100 bytes */ - - gsdata = malloc(file->size); - - if (!gsdata) - return SC_ERROR_OUT_OF_MEMORY; - - /* set indcies of data in gsdata */ - idx1 = 0; /* start point */ - idx2 = 0; /* index of last data read so far */ - - - /* set certs We only have one we are interested in */ - /* but the read loop is set up to allow for more in future */ - - for (i = 0; certs[i].label; i++) { - struct sc_pkcs15_cert_info cert_info; - struct sc_pkcs15_object cert_obj; - sc_pkcs15_cert_t *cert_out; - - memset(&cert_info, 0, sizeof(cert_info)); - memset(&cert_obj, 0, sizeof(cert_obj)); - - sc_pkcs15_format_id(certs[i].id, &cert_info.id); - cert_info.authority = certs[i].authority; - - strlcpy(cert_obj.label, certs[i].label, sizeof(cert_obj.label)); - cert_obj.flags = certs[i].obj_flags; - - while (idx1 < file->size - 16) { /* actually 13 for all these tests */ - if (idx1 + 16 > idx2 ) { /* need more data in buff */ - idxlen = 248; /* read in next 248 bytes */ - if (idxlen > file->size - idx2) - idxlen = file->size - idx2; - r = sc_read_binary(card, idx2, gsdata + idx2, idxlen, 0); - if (r < 0) - break; - idx2 = idx2 + idxlen; - } - - if ( gsdata[idx1] == 0x30 && - is_seq(gsdata + idx1, &seq_size1, &seq_len1) && - is_seq(gsdata + idx1 + seq_size1, &seq_size2, &seq_len2) && - gsdata[idx1 + seq_size1 + seq_size2 + 0] == 0xa0 && - gsdata[idx1 + seq_size1 + seq_size2 + 1] == 0x03 && - gsdata[idx1 + seq_size1 + seq_size2 + 2] == 0x02 && - gsdata[idx1 + seq_size1 + seq_size2 + 3] == 0x01 && - gsdata[idx1 + seq_size1 + seq_size2 + 4] == 0x02 && - idx1 + 4 + seq_len1 < file->size) { - /* we have a cert (I hope) */ - /* read in rest if needed */ - idxlen = idx1 + seq_len1 + 4 - idx2; - if (idxlen > 0) { - idxlen = (idxlen + 3) & 0xfffffffc; - r = sc_read_binary(card, idx2, gsdata + idx2, idxlen, 0); - if (r < 0) - break; /* can not read cert */ - idx2 = idx2 + idxlen; - } - cert_info.value.len = seq_len1 + 4; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Found cert at offset %d", idx1); - cert_info.value.value = (unsigned char *) - malloc(cert_info.value.len); - if (!cert_info.value.value) - return SC_ERROR_OUT_OF_MEMORY; - - memcpy(cert_info.value.value, gsdata + idx1, cert_info.value.len); - idx1 = idx1 + cert_info.value.len; - break; - } - idx1++; - } - - if (cert_info.value.value == NULL) - break; /* cert not found, no more certs */ - - r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); - if (r < 0) { - free(gsdata); - return SC_ERROR_INTERNAL; - } - - /* now lets see if we have a matching key for this cert */ - - r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out); - if (r < 0) { - free(gsdata); - return SC_ERROR_INTERNAL; - } - - for (j = 0; j < num_keyinfo; j++) { - if (cert_out->key->u.rsa.modulus.len == kinfo[j].modulus_len && - memcmp(cert_out->key->u.rsa.modulus.data, - &kinfo[j].modulus, cert_out->key->u.rsa.modulus.len) == 0) { - memcpy(&kinfo[j].id, &cert_info.id, sizeof(sc_pkcs15_id_t)); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found match"); - } - } - sc_pkcs15_free_certificate(cert_out); - } - - if (gsdata) - free(gsdata); - - /* set pins */ - - /* GemSAFE uses different padding, so need to trap */ - /* the pin_cmd and reset the padding */ - - pin_cmd_save = card->ops->pin_cmd; - card->ops->pin_cmd = my_pin_cmd; - - for (i = 0; pins[i].label; i++) { - struct sc_pkcs15_auth_info pin_info; - struct sc_pkcs15_object pin_obj; - - memset(&pin_info, 0, sizeof(pin_info)); - memset(&pin_obj, 0, sizeof(pin_obj)); - - sc_pkcs15_format_id(pins[i].id, &pin_info.auth_id); - pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; - pin_info.attrs.pin.reference = pins[i].ref; - pin_info.attrs.pin.flags = pins[i].flags; - pin_info.attrs.pin.type = pins[i].type; - pin_info.attrs.pin.min_length = pins[i].minlen; - pin_info.attrs.pin.stored_length = pins[i].storedlen; - pin_info.attrs.pin.max_length = pins[i].maxlen; - pin_info.attrs.pin.pad_char = pins[i].pad_char; - sc_format_path(pins[i].path, &pin_info.path); - pin_info.path.value[2] = dfpath >> 8; - pin_info.path.value[3] = dfpath & 0xff; - pin_info.tries_left = -1; - pin_info.logged_in = SC_PIN_STATE_UNKNOWN; - - strlcpy(pin_obj.label, pins[i].label, sizeof(pin_obj.label)); - pin_obj.flags = pins[i].obj_flags; - - r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); - if (r < 0) - return SC_ERROR_INTERNAL; - } - - /* needs work, as we may want to add more then one key */ - /* but not sure what the other keys do */ - - /* set private keys */ - for (i = 0; prkeys[i].label; i++) { - struct sc_pkcs15_prkey_info prkey_info; - struct sc_pkcs15_object prkey_obj; - - memset(&prkey_info, 0, sizeof(prkey_info)); - memset(&prkey_obj, 0, sizeof(prkey_obj)); - - sc_pkcs15_format_id(prkeys[i].id, &prkey_info.id); - prkey_info.usage = prkeys[i].usage; - prkey_info.native = 1; - prkey_info.key_reference = prkeys[i].ref; - prkey_info.modulus_length= prkeys[i].modulus_len; - sc_format_path(prkeys[i].path, &prkey_info.path); - - /*DEE need to look for them by reading and checking mudulus vs cert */ - - /* will use the default path, unless we found a key with */ - /* the same modulus as the cert(s) we already added */ - /* This allows us to have a card with a key but no cert */ - - for (j = 0; j < num_keyinfo; j++) { - if (sc_pkcs15_compare_id(&kinfo[j].id, &prkey_info.id)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found key in file %d for id %s", - kinfo[j].fileid, prkeys[i].id); - prkey_info.path.value[0] = kinfo[j].fileid >> 8; - prkey_info.path.value[1] = kinfo[j].fileid & 0xff; - break; - } - } - - strlcpy(prkey_obj.label, prkeys[i].label, sizeof(prkey_obj.label)); - prkey_obj.flags = prkeys[i].obj_flags; - if (prkeys[i].auth_id) - sc_pkcs15_format_id(prkeys[i].auth_id, &prkey_obj.auth_id); - - r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); - if (r < 0) - return SC_ERROR_INTERNAL; - } - return SC_SUCCESS; -} - -int sc_pkcs15emu_gemsafeGPK_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid, - sc_pkcs15emu_opt_t *opts) -{ - sc_card_t *card = p15card->card; - sc_context_t *ctx = card->ctx; - - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Entering %s", __FUNCTION__); - - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - return sc_pkcs15emu_gemsafeGPK_init(p15card); - else { - int r = gemsafe_detect_card(p15card); - if (r) - return SC_ERROR_WRONG_CARD; - return sc_pkcs15emu_gemsafeGPK_init(p15card); - } -} diff --git a/src/libopensc/pkcs15-gemsafeV1.c b/src/libopensc/pkcs15-gemsafeV1.c index ccc43c6bc9..317c4bb1be 100644 --- a/src/libopensc/pkcs15-gemsafeV1.c +++ b/src/libopensc/pkcs15-gemsafeV1.c @@ -11,13 +11,13 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* Initially written by David Mattes */ /* Support for multiple key containers by Lukas Wunner */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -40,8 +40,6 @@ #define GEMSAFE_READ_QUANTUM 248 #define GEMSAFE_MAX_OBJLEN 28672 -int sc_pkcs15emu_gemsafeV1_init_ex(sc_pkcs15_card_t *, struct sc_aid *,sc_pkcs15emu_opt_t *); - static int sc_pkcs15emu_add_cert(sc_pkcs15_card_t *p15card, int type, int authority, @@ -69,8 +67,8 @@ typedef struct cdata_st { char *label; int authority; const char *path; - size_t index; - size_t count; + int index; + int count; const char *id; int obj_flags; } cdata; @@ -168,18 +166,23 @@ static int gemsafe_get_cert_len(sc_card_t *card) u8 *iptr; struct sc_path path; struct sc_file *file; - size_t objlen, certlen; + size_t objlen; + int certlen; unsigned int ind, i=0; + int read_len; sc_format_path(GEMSAFE_PATH, &path); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS || !file) return SC_ERROR_INTERNAL; + sc_file_free(file); /* Initial read */ - r = sc_read_binary(card, 0, ibuf, GEMSAFE_READ_QUANTUM, 0); - if (r < 0) + read_len = sc_read_binary(card, 0, ibuf, GEMSAFE_READ_QUANTUM, 0); + if (read_len <= 2) { + sc_log(card->ctx, "Invalid size of object data: %d", read_len); return SC_ERROR_INTERNAL; + } /* Actual stored object size is encoded in first 2 bytes * (allocated EF space is much greater!) @@ -208,7 +211,7 @@ static int gemsafe_get_cert_len(sc_card_t *card) * the private key. */ ind = 2; /* skip length */ - while (ibuf[ind] == 0x01) { + while (ind + 1 < (size_t)read_len && ibuf[ind] == 0x01 && i < gemsafe_cert_max) { if (ibuf[ind+1] == 0xFE) { gemsafe_prkeys[i].ref = ibuf[ind+4]; sc_log(card->ctx, "Key container %d is allocated and uses key_ref %d", @@ -225,7 +228,7 @@ static int gemsafe_get_cert_len(sc_card_t *card) } /* Delete additional key containers from the data structures if - * this card can't accomodate them. + * this card can't accommodate them. */ for (; i < gemsafe_cert_max; i++) { gemsafe_prkeys[i].label = NULL; @@ -235,15 +238,22 @@ static int gemsafe_get_cert_len(sc_card_t *card) /* Read entire file, then dissect in memory. * Gemalto ClassicClient seems to do it the same way. */ - iptr = ibuf + GEMSAFE_READ_QUANTUM; + iptr = ibuf + read_len; while ((size_t)(iptr - ibuf) < objlen) { - r = sc_read_binary(card, iptr - ibuf, iptr, + r = sc_read_binary(card, (unsigned)(iptr - ibuf), iptr, MIN(GEMSAFE_READ_QUANTUM, objlen - (iptr - ibuf)), 0); if (r < 0) { sc_log(card->ctx, "Could not read cert object"); return SC_ERROR_INTERNAL; } - iptr += GEMSAFE_READ_QUANTUM; + if (r == 0) + break; + read_len += r; + iptr += r; + } + if ((size_t)read_len < objlen) { + sc_log(card->ctx, "Could not read cert object"); + return SC_ERROR_INTERNAL; } /* Search buffer for certificates, they start with 0x3082. */ @@ -260,9 +270,9 @@ static int gemsafe_get_cert_len(sc_card_t *card) /* DER cert len is encoded this way */ if (ind+3 >= sizeof ibuf) return SC_ERROR_INVALID_DATA; - certlen = ((((size_t) ibuf[ind+2]) << 8) | ibuf[ind+3]) + 4; + certlen = ((((int)ibuf[ind + 2]) << 8) | ibuf[ind + 3]) + 4; sc_log(card->ctx, - "Found certificate of key container %d at offset %d, len %"SC_FORMAT_LEN_SIZE_T"u", + "Found certificate of key container %d at offset %d, len %d", i+1, ind, certlen); gemsafe_cert[i].index = ind; gemsafe_cert[i].count = certlen; @@ -306,49 +316,49 @@ static int sc_pkcs15emu_gemsafeV1_init( sc_pkcs15_card_t *p15card) sc_log(p15card->card->ctx, "Setting pkcs15 parameters"); - if (p15card->tokeninfo->label) - free(p15card->tokeninfo->label); - p15card->tokeninfo->label = malloc(strlen(APPLET_NAME) + 1); + set_string(&p15card->tokeninfo->label, APPLET_NAME); if (!p15card->tokeninfo->label) return SC_ERROR_INTERNAL; - strcpy(p15card->tokeninfo->label, APPLET_NAME); - if (p15card->tokeninfo->serial_number) - free(p15card->tokeninfo->serial_number); - p15card->tokeninfo->serial_number = malloc(strlen(DRIVER_SERIAL_NUMBER) + 1); - if (!p15card->tokeninfo->serial_number) + set_string(&p15card->tokeninfo->serial_number, DRIVER_SERIAL_NUMBER); + if (!p15card->tokeninfo->serial_number) { + free(p15card->tokeninfo->label); + p15card->tokeninfo->label = NULL; return SC_ERROR_INTERNAL; - strcpy(p15card->tokeninfo->serial_number, DRIVER_SERIAL_NUMBER); + } /* the GemSAFE applet version number */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0xdf, 0x03); apdu.cla = 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); - /* Manual says Le=0x05, but should be 0x08 to return full version numer */ + /* Manual says Le=0x05, but should be 0x08 to return full version number */ apdu.le = 0x08; apdu.lc = 0; apdu.datalen = 0; r = sc_transmit_apdu(card, &apdu); + if (r < 0) + sc_pkcs15_card_clear(p15card); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) - return SC_ERROR_INTERNAL; - if (r != SC_SUCCESS) + if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00 || r != SC_SUCCESS) { + sc_pkcs15_card_clear(p15card); return SC_ERROR_INTERNAL; + } /* the manufacturer ID, in this case GemPlus */ - if (p15card->tokeninfo->manufacturer_id) - free(p15card->tokeninfo->manufacturer_id); - p15card->tokeninfo->manufacturer_id = malloc(strlen(MANU_ID) + 1); - if (!p15card->tokeninfo->manufacturer_id) + set_string(&p15card->tokeninfo->manufacturer_id, MANU_ID); + if (!p15card->tokeninfo->manufacturer_id) { + sc_pkcs15_card_clear(p15card); return SC_ERROR_INTERNAL; - strcpy(p15card->tokeninfo->manufacturer_id, MANU_ID); + } /* determine allocated key containers and length of certificates */ r = gemsafe_get_cert_len(card); - if (r != SC_SUCCESS) + if (r != SC_SUCCESS) { + sc_pkcs15_card_clear(p15card); return SC_ERROR_INTERNAL; + } /* set certs */ sc_log(p15card->card->ctx, "Setting certificates"); @@ -411,7 +421,7 @@ static int sc_pkcs15emu_gemsafeV1_init( sc_pkcs15_card_t *p15card) */ if ( p15card->card->flags & 0x0F) { key_ref = p15card->card->flags & 0x0F; - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(p15card->card->ctx, "Overriding key_ref %d with %d\n", gemsafe_prkeys[i].ref, key_ref); } else @@ -427,28 +437,23 @@ static int sc_pkcs15emu_gemsafeV1_init( sc_pkcs15_card_t *p15card) sc_log(p15card->card->ctx, "Selecting application DF"); sc_format_path(GEMSAFE_APP_PATH, &path); r = sc_select_file(card, &path, &file); - if (r != SC_SUCCESS || !file) + if (r != SC_SUCCESS || !file) { + sc_pkcs15_card_clear(p15card); return SC_ERROR_INTERNAL; + } /* set the application DF */ - if (p15card->file_app) - free(p15card->file_app); + sc_file_free(p15card->file_app); p15card->file_app = file; return SC_SUCCESS; } int sc_pkcs15emu_gemsafeV1_init_ex( sc_pkcs15_card_t *p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t *opts) + struct sc_aid *aid) { - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - return sc_pkcs15emu_gemsafeV1_init(p15card); - else { - int r = gemsafe_detect_card(p15card); - if (r) - return SC_ERROR_WRONG_CARD; - return sc_pkcs15emu_gemsafeV1_init(p15card); - } + if (gemsafe_detect_card(p15card)) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_gemsafeV1_init(p15card); } static sc_pkcs15_df_t * @@ -467,7 +472,8 @@ sc_pkcs15emu_get_df(sc_pkcs15_card_t *p15card, unsigned int type) } } - assert(created == 0); + if (created != 0) + return NULL; file = sc_file_new(); if (!file) @@ -488,6 +494,9 @@ sc_pkcs15emu_add_object(sc_pkcs15_card_t *p15card, int type, int df_type; obj = calloc(1, sizeof(*obj)); + if (!obj) { + LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); + } obj->type = type; obj->data = data; diff --git a/src/libopensc/pkcs15-gids.c b/src/libopensc/pkcs15-gids.c index a01263c5f9..99571fd0d6 100644 --- a/src/libopensc/pkcs15-gids.c +++ b/src/libopensc/pkcs15-gids.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -50,7 +50,7 @@ static int sc_pkcs15emu_gids_add_prkey(sc_pkcs15_card_t * p15card, sc_cardctl_gi sc_pkcs15_object_t cert_obj; int r; char ch_tmp[10]; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Got args: containerIndex=%"SC_FORMAT_LEN_SIZE_T"x\n", container->containernum); @@ -62,7 +62,7 @@ static int sc_pkcs15emu_gids_add_prkey(sc_pkcs15_card_t * p15card, sc_cardctl_gi prkey_info.modulus_length = container->module_length; prkey_info.usage = container->prvusage; prkey_info.native = 1; - prkey_info.key_reference = 0x81 + container->containernum; + prkey_info.key_reference = (int)(0x81 + container->containernum); strlcpy(prkey_obj.label, container->label, sizeof(prkey_obj.label)); prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; @@ -70,7 +70,7 @@ static int sc_pkcs15emu_gids_add_prkey(sc_pkcs15_card_t * p15card, sc_cardctl_gi prkey_obj.auth_id.value[0] = 0x80; r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to sc_pkcs15emu_add_rsa_prkey"); + LOG_TEST_RET(card->ctx, r, "unable to sc_pkcs15emu_add_rsa_prkey"); memset(&pubkey_info, 0, sizeof(pubkey_info)); memset(&pubkey_obj, 0, sizeof(pubkey_obj)); @@ -86,7 +86,7 @@ static int sc_pkcs15emu_gids_add_prkey(sc_pkcs15_card_t * p15card, sc_cardctl_gi pubkey_info.id = prkey_info.id; r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to sc_pkcs15emu_add_rsa_pubkey"); + LOG_TEST_RET(card->ctx, r, "unable to sc_pkcs15emu_add_rsa_pubkey"); if (container->certificatepath.len > 0) { memset(&cert_info, 0, sizeof(cert_info)); @@ -100,7 +100,7 @@ static int sc_pkcs15emu_gids_add_prkey(sc_pkcs15_card_t * p15card, sc_cardctl_gi r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); LOG_TEST_RET(card->ctx, r, "Could not add certificate"); } else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No certificate found"); + sc_log(card->ctx, "No certificate found"); } return SC_SUCCESS; @@ -122,11 +122,12 @@ static int sc_pkcs15emu_gids_init (sc_pkcs15_card_t * p15card) int has_puk; r = sc_card_ctl(card, SC_CARDCTL_GIDS_GET_ALL_CONTAINERS, &recordsnum); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the containers. Uninitialized card ?"); + LOG_TEST_RET(card->ctx, r, "unable to get the containers. Uninitialized card ?"); r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, NULL); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the serial number. Uninitialized card ?"); + LOG_TEST_RET(card->ctx, r, "unable to get the serial number. Uninitialized card ?"); + free(p15card->tokeninfo->serial_number); p15card->tokeninfo->serial_number = (char*) malloc(card->serialnr.len *2 +1); if (!p15card->tokeninfo->serial_number) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); @@ -135,8 +136,11 @@ static int sc_pkcs15emu_gids_init (sc_pkcs15_card_t * p15card) if (p15card->tokeninfo->label == NULL) { p15card->tokeninfo->label = strdup("GIDS card"); - if (p15card->tokeninfo->label == NULL) + if (p15card->tokeninfo->label == NULL) { + free(p15card->tokeninfo->serial_number); + p15card->tokeninfo->serial_number = NULL; LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } } if ((p15card->tokeninfo->manufacturer_id != NULL) && !strcmp("(unknown)", p15card->tokeninfo->manufacturer_id)) { @@ -146,8 +150,10 @@ static int sc_pkcs15emu_gids_init (sc_pkcs15_card_t * p15card) if (p15card->tokeninfo->manufacturer_id == NULL) { p15card->tokeninfo->manufacturer_id = strdup("www.mysmartlogon.com"); - if (p15card->tokeninfo->manufacturer_id == NULL) + if (p15card->tokeninfo->manufacturer_id == NULL) { + sc_pkcs15_card_clear(p15card); LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } } if (p15card->card->type == SC_CARD_TYPE_GIDS_V2) { p15card->tokeninfo->version = 2; @@ -176,7 +182,7 @@ static int sc_pkcs15emu_gids_init (sc_pkcs15_card_t * p15card) pin_cmd_data.pin_type = SC_AC_CHV; pin_cmd_data.pin_reference = pin_info.attrs.pin.reference; - r = sc_pin_cmd(card, &pin_cmd_data, NULL); + r = sc_pin_cmd(card, &pin_cmd_data); if (r == SC_SUCCESS) { pin_info.max_tries = pin_cmd_data.pin1.max_tries; pin_info.tries_left = pin_cmd_data.pin1.tries_left; @@ -190,14 +196,14 @@ static int sc_pkcs15emu_gids_init (sc_pkcs15_card_t * p15card) * link PIN with PUK. */ pin_cmd_data.pin_reference = 0x81; - has_puk = sc_pin_cmd(card, &pin_cmd_data, NULL) == SC_SUCCESS; + has_puk = sc_pin_cmd(card, &pin_cmd_data) == SC_SUCCESS; if (has_puk) { pin_obj.auth_id.len = 1; pin_obj.auth_id.value[0] = 0x81; } r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to sc_pkcs15emu_add_pin_obj"); + LOG_TEST_GOTO_ERR(card->ctx, r, "unable to sc_pkcs15emu_add_pin_obj"); if (has_puk) { pin_info.auth_id.value[0] = 0x81; @@ -208,11 +214,11 @@ static int sc_pkcs15emu_gids_init (sc_pkcs15_card_t * p15card) strlcpy(pin_obj.label, "PUK", sizeof(pin_obj.label)); pin_obj.auth_id.len = 0; r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to sc_pkcs15emu_add_pin_obj with PUK"); + LOG_TEST_GOTO_ERR(card->ctx, r, "unable to sc_pkcs15emu_add_pin_obj with PUK"); } r = sc_card_ctl(card, SC_CARDCTL_GIDS_GET_ALL_CONTAINERS, &recordsnum); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_card_ctl SC_CARDCTL_GIDS_GET_ALL_CONTAINERS"); + LOG_TEST_GOTO_ERR(card->ctx, r, "sc_card_ctl SC_CARDCTL_GIDS_GET_ALL_CONTAINERS"); for (i = 0; i < recordsnum; i++) { sc_cardctl_gids_get_container_t container; @@ -227,27 +233,24 @@ static int sc_pkcs15emu_gids_init (sc_pkcs15_card_t * p15card) sc_pkcs15emu_gids_add_prkey(p15card, &container); } return SC_SUCCESS; +err: + sc_pkcs15_card_clear(p15card); + LOG_FUNC_RETURN(card->ctx, r); } int sc_pkcs15emu_gids_init_ex(sc_pkcs15_card_t *p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t *opts) + struct sc_aid *aid) { - if (opts && (opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)) { - return sc_pkcs15emu_gids_init(p15card); - } else { - if (p15card->card->type != SC_CARD_TYPE_GIDS_GENERIC && p15card->card->type != SC_CARD_TYPE_GIDS_V1 && p15card->card->type != SC_CARD_TYPE_GIDS_V2) { - return SC_ERROR_WRONG_CARD; - } - return sc_pkcs15emu_gids_init(p15card); + if (p15card->card->type != SC_CARD_TYPE_GIDS_GENERIC && p15card->card->type != SC_CARD_TYPE_GIDS_V1 && p15card->card->type != SC_CARD_TYPE_GIDS_V2) { + return SC_ERROR_WRONG_CARD; } + return sc_pkcs15emu_gids_init(p15card); } #else int sc_pkcs15emu_gids_init_ex(sc_pkcs15_card_t *p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t *opts) + struct sc_aid *aid) { return SC_ERROR_WRONG_CARD; } diff --git a/src/libopensc/pkcs15-iasecc.c b/src/libopensc/pkcs15-iasecc.c index d291ccc8d4..1b987fbea7 100644 --- a/src/libopensc/pkcs15-iasecc.c +++ b/src/libopensc/pkcs15-iasecc.c @@ -18,10 +18,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -34,10 +34,11 @@ #include "internal.h" #include "pkcs15.h" +#include "../pkcs15init/pkcs15-iasecc.h" #include "iasecc.h" #include "aux-data.h" -#define IASECC_GEMALTO_MD_APPLICAITON_NAME "CSP" +#define IASECC_GEMALTO_MD_APPLICATION_NAME "CSP" #define IASECC_GEMALTO_MD_DEFAULT_CONT_LABEL "Default Key Container" static int @@ -50,25 +51,37 @@ _iasecc_md_update_keyinfo(struct sc_pkcs15_card *p15card, struct sc_pkcs15_objec struct sc_pkcs15_id id; int rv, offs; unsigned flags; + int private_obj; LOG_FUNC_CALLED(ctx); if (!dobj) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - rv = sc_pkcs15_read_data_object(p15card, (struct sc_pkcs15_data_info *)dobj->data, &ddata); + private_obj = dobj->flags & SC_PKCS15_CO_FLAG_PRIVATE; + rv = sc_pkcs15_read_data_object(p15card, (struct sc_pkcs15_data_info *)dobj->data, private_obj, &ddata); LOG_TEST_RET(ctx, rv, "Failed to read container DATA object data"); + /* [0] = 0x01 + * [1] = length + * [2 .. 2 + length] = ID + * [2 + length] = 0x02 + * [2 + length + 1] = 0x01 + * [3 + length + 2] = flags + */ offs = 0; - rv = SC_ERROR_INVALID_DATA; - if (*(ddata->data + offs++) != 0x01) { + if (ddata->data_len < 2 || *(ddata->data + offs++) != 0x01) { sc_pkcs15_free_data_object(ddata); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); } id.len = *(ddata->data + offs++); + if (id.len > sizeof(id.value) || ddata->data_len < offs + id.len + 3) { + sc_pkcs15_free_data_object(ddata); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + } memcpy(id.value, ddata->data + offs, id.len); - offs += (int) id.len; + offs += (int)id.len; if (*(ddata->data + offs++) != 0x02) { sc_pkcs15_free_data_object(ddata); @@ -104,6 +117,38 @@ _iasecc_md_update_keyinfo(struct sc_pkcs15_card *p15card, struct sc_pkcs15_objec } +/* + * CPx cards have an undocumented issue: they lack of + * Algorithm's reference into their PKCS's ASN1 encoding. + */ +static int +_iasecc_cpx_fixup_prkdf(struct sc_pkcs15_card *p15card) +{ + struct sc_context * const ctx = p15card->card->ctx; + struct sc_pkcs15_object *pkobjs[32]; + int ii, count; + int rv = SC_SUCCESS; + + LOG_FUNC_CALLED(ctx); + + rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, pkobjs, sizeof(pkobjs)/sizeof(pkobjs[0])); + LOG_TEST_RET(ctx, rv, "Cannot get PRKEY objects list"); + + count = rv; + for(ii=0; iicard->type != SC_CARD_TYPE_IASECC_GEMALTO) - LOG_FUNC_RETURN(ctx, SC_SUCCESS); + switch(p15card->card->type) { + /* enumerate the IASECC cards that need a fixup of the keyInfo */ + case SC_CARD_TYPE_IASECC_GEMALTO: + case SC_CARD_TYPE_IASECC_CPX: + case SC_CARD_TYPE_IASECC_CPXCL: + sc_log(ctx, "Warning: the %d card has an invalid DF, hot patch to be applied", + p15card->card->type); + break; + default: + sc_log(ctx, "the %d card has a proper DF, no need for a hot patch", + p15card->card->type); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + break; + } if (df->type != SC_PKCS15_PRKDF) LOG_FUNC_RETURN(ctx, SC_SUCCESS); sc_log(ctx, "parse of SC_PKCS15_PRKDF"); + rv = _iasecc_cpx_fixup_prkdf(p15card); + LOG_TEST_RET(ctx, rv, "Cannot fixup PrKDF"); + rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_DATA_OBJECT, dobjs, sizeof(dobjs)/sizeof(dobjs[0])); LOG_TEST_RET(ctx, rv, "Cannot get DATA objects list"); count = rv; for(ii=0; iidata; + int private_obj = dobjs[ii]->flags & SC_PKCS15_CO_FLAG_PRIVATE; - if (strcmp(dinfo->app_label, IASECC_GEMALTO_MD_APPLICAITON_NAME)) + if (strcmp(dinfo->app_label, IASECC_GEMALTO_MD_APPLICATION_NAME)) continue; if (!strcmp(dobjs[ii]->label, IASECC_GEMALTO_MD_DEFAULT_CONT_LABEL)) { - rv = sc_pkcs15_read_data_object(p15card, (struct sc_pkcs15_data_info *)dobjs[ii]->data, &default_guid); + rv = sc_pkcs15_read_data_object(p15card, (struct sc_pkcs15_data_info *)dobjs[ii]->data, private_obj, &default_guid); LOG_TEST_RET(ctx, rv, "Failed to read 'default container' DATA object data"); break; } @@ -152,7 +213,7 @@ _iasecc_parse_df(struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df) struct sc_pkcs15_data_info *dinfo = (struct sc_pkcs15_data_info *)dobjs[ii]->data; int default_cont = 0; - if (strcmp(dinfo->app_label, IASECC_GEMALTO_MD_APPLICAITON_NAME)) + if (strcmp(dinfo->app_label, IASECC_GEMALTO_MD_APPLICATION_NAME)) continue; if (!strcmp(dobjs[ii]->label, IASECC_GEMALTO_MD_DEFAULT_CONT_LABEL)) @@ -203,11 +264,8 @@ sc_pkcs15emu_iasecc_init (struct sc_pkcs15_card *p15card, struct sc_aid *aid) int -sc_pkcs15emu_iasecc_init_ex(struct sc_pkcs15_card *p15card, struct sc_aid *aid, struct sc_pkcs15emu_opt *opts) +sc_pkcs15emu_iasecc_init_ex(struct sc_pkcs15_card *p15card, struct sc_aid *aid) { - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - return sc_pkcs15emu_iasecc_init(p15card, aid); - if (iasecc_pkcs15emu_detect_card(p15card)) return SC_ERROR_WRONG_CARD; diff --git a/src/libopensc/pkcs15-idprime.c b/src/libopensc/pkcs15-idprime.c new file mode 100644 index 0000000000..34192f5aa2 --- /dev/null +++ b/src/libopensc/pkcs15-idprime.c @@ -0,0 +1,367 @@ +/* + * partial PKCS15 emulation for IDPrime cards. + * + * We can not use the ISO code, since the EF.DIR and EF.ATR for + * object discovery are missing + * + * Copyright (C) 2019, Red Hat, Inc. + * + * Author: Jakub Jelen + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "internal.h" +#include "cardctl.h" +#include "pkcs15.h" + +#define CERT_LABEL_TEMPLATE "Certificate %d" +#define PUBKEY_LABEL_TEMPLATE "Public key %d" +#define PRIVKEY_LABEL_TEMPLATE "Private key %d" + +static int idprime_detect_card(sc_pkcs15_card_t *p15card) +{ + sc_card_t *card = p15card->card; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + if (card->type < SC_CARD_TYPE_IDPRIME_BASE + || card->type >= SC_CARD_TYPE_IDPRIME_BASE+1000) + return SC_ERROR_INVALID_CARD; + return SC_SUCCESS; +} + +static int sc_pkcs15emu_idprime_init(sc_pkcs15_card_t *p15card) +{ + int r, i; + sc_card_t *card = p15card->card; + sc_serial_number_t serial; + char buf[SC_MAX_SERIALNR * 2 + 1]; + int count; + char *token_name = NULL; + struct sc_pkcs15_auth_info pin_info; + struct sc_pkcs15_object pin_obj; + const char pin_label[] = "PIN"; + const char *pin_id = "11"; + const char sig_pin_label[] = "Digital Signature PIN"; + const char *sig_pin_id = "83"; + + /* oid for key usage */ + static const struct sc_object_id usage_type = {{ 2, 5, 29, 15, -1 }}; + unsigned int usage; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* could read this off card if needed */ + set_string(&p15card->tokeninfo->label, "IDPrime"); + set_string(&p15card->tokeninfo->manufacturer_id, "Gemalto"); + + /* + * get serial number + */ + memset(&serial, 0, sizeof(serial)); + r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); + if (r < 0) { + sc_log(card->ctx, "sc_card_ctl rc=%d", r); + set_string(&p15card->tokeninfo->serial_number, "00000000"); + } else { + sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0); + set_string(&p15card->tokeninfo->serial_number, buf); + } + /* set pin */ + sc_log(card->ctx, "IDPrime adding pin..."); + memset(&pin_info, 0, sizeof(pin_info)); + memset(&pin_obj, 0, sizeof(pin_obj)); + + pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; + sc_pkcs15_format_id(pin_id, &pin_info.auth_id); + pin_info.attrs.pin.reference = 0x11; + pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_INITIALIZED; + pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; + pin_info.attrs.pin.min_length = 4; + pin_info.attrs.pin.stored_length = 0; + pin_info.attrs.pin.max_length = 16; + pin_info.tries_left = -1; + + if (card->type == SC_CARD_TYPE_IDPRIME_840 + || card->type == SC_CARD_TYPE_IDPRIME_940 + || card->type == SC_CARD_TYPE_IDPRIME_GENERIC) { + pin_info.attrs.pin.flags |= SC_PKCS15_PIN_FLAG_NEEDS_PADDING; + pin_info.attrs.pin.stored_length = 16; + pin_info.attrs.pin.pad_char = 0x00; + } + + sc_log(card->ctx, "IDPrime Adding pin with label=%s", pin_label); + strncpy(pin_obj.label, pin_label, SC_PKCS15_MAX_LABEL_SIZE - 1); + pin_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; + + r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not add pin object"); + + /* set signature pin for 940 cards */ + if (card->type == SC_CARD_TYPE_IDPRIME_940) { + sc_log(card->ctx, "IDPrime adding Digital Signature pin..."); + memset(&pin_info, 0, sizeof(pin_info)); + memset(&pin_obj, 0, sizeof(pin_obj)); + + pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; + sc_pkcs15_format_id(sig_pin_id, &pin_info.auth_id); + pin_info.attrs.pin.reference = 0x83; + pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_INITIALIZED; + pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; + pin_info.attrs.pin.min_length = 4; + pin_info.attrs.pin.stored_length = 0; + pin_info.attrs.pin.max_length = 16; + pin_info.tries_left = -1; + + pin_info.attrs.pin.flags |= SC_PKCS15_PIN_FLAG_NEEDS_PADDING; + pin_info.attrs.pin.stored_length = 16; + pin_info.attrs.pin.pad_char = 0x00; + + sc_log(card->ctx, "IDPrime Adding Digital Signature pin with label=%s", sig_pin_label); + strncpy(pin_obj.label, sig_pin_label, SC_PKCS15_MAX_LABEL_SIZE - 1); + pin_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; + + r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not add Digital Signature pin object"); + } + + /* + * get token name if provided + */ + r = sc_card_ctl(card, SC_CARDCTL_IDPRIME_GET_TOKEN_NAME, &token_name); + if (r < 0) { + /* On failure we will get the token name from certificates later */ + sc_log(card->ctx, "sc_card_ctl rc=%d", r); + } else { + free(p15card->tokeninfo->label); + p15card->tokeninfo->label = token_name; + sc_log(card->ctx, "IDPrime setting token label = %s", token_name); + } + /* + * certs, pubkeys and priv keys are related and we assume + * they are in order + * We need to read the cert, get modulus and keylen + * We use those for the pubkey, and priv key objects. + */ + sc_log(card->ctx, "IDPrime adding certs, pub and priv keys..."); + r = (card->ops->card_ctl)(card, SC_CARDCTL_IDPRIME_INIT_GET_OBJECTS, &count); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not initiate cert objects."); + + for (i = 0; i < count; i++) { + struct sc_pkcs15_prkey_info prkey_info; + struct sc_pkcs15_cert_info cert_info; + struct sc_pkcs15_pubkey_info pubkey_info; + struct sc_pkcs15_object cert_obj; + struct sc_pkcs15_object pubkey_obj; + struct sc_pkcs15_object prkey_obj; + sc_pkcs15_der_t cert_der; + sc_pkcs15_cert_t *cert_out = NULL; + const char *pin_id = NULL; + + r = (card->ops->card_ctl)(card, SC_CARDCTL_IDPRIME_GET_PIN_ID, (void *) &pin_id); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not get PIN id of next object "); + r = (card->ops->card_ctl)(card, SC_CARDCTL_IDPRIME_GET_NEXT_OBJECT, &prkey_info); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not get next object"); + + memset(&cert_info, 0, sizeof(cert_info)); + memset(&pubkey_info, 0, sizeof(pubkey_info)); + /* prkey_info cleaned by the card_ctl call */ + memset(&cert_obj, 0, sizeof(cert_obj)); + memset(&pubkey_obj, 0, sizeof(pubkey_obj)); + memset(&prkey_obj, 0, sizeof(prkey_obj)); + + cert_info.id = prkey_info.id; + pubkey_info.id = prkey_info.id; + cert_info.path = prkey_info.path; + /* For private keys, we no longer care for the path, just + * the key reference later used in the security environment */ + prkey_info.path.len = 0; + prkey_info.path.aid.len = 0; + pubkey_info.key_reference = prkey_info.key_reference; + sc_log(card->ctx, "Key ref r=%x", prkey_info.key_reference); + + pubkey_info.native = 1; + prkey_info.native = 1; + + snprintf(cert_obj.label, SC_PKCS15_MAX_LABEL_SIZE, CERT_LABEL_TEMPLATE, i+1); + snprintf(pubkey_obj.label, SC_PKCS15_MAX_LABEL_SIZE, PUBKEY_LABEL_TEMPLATE, i+1); + snprintf(prkey_obj.label, SC_PKCS15_MAX_LABEL_SIZE, PRIVKEY_LABEL_TEMPLATE, i+1); + prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; + + /* Differentiate between objects accessible with normal and with digital signature pin */ + sc_pkcs15_format_id(pin_id, &prkey_obj.auth_id); + sc_log(card->ctx, "Pin ID r=%s", pin_id); + + if (memcmp(pin_id, sig_pin_id, 2) == 0) + prkey_obj.user_consent = 1; + + r = sc_pkcs15_read_file(p15card, &cert_info.path, &cert_der.value, &cert_der.len, 0); + + if (r) { + sc_log(card->ctx, "No cert found,i=%d", i); + continue; + } + cert_info.path.count = (int)cert_der.len; + + sc_log(card->ctx, + "cert len=%"SC_FORMAT_LEN_SIZE_T"u, cert_info.path.count=%d r=%d\n", + cert_der.len, cert_info.path.count, r); + sc_log_hex(card->ctx, "cert", cert_der.value, cert_der.len); + + /* cache it using the PKCS15 emulation objects */ + /* as it does not change */ + if (cert_der.value) { + cert_info.value.value = cert_der.value; + cert_info.value.len = cert_der.len; + cert_info.path.len = 0; /* use in mem cert from now on */ + } + + /* following will find the cached cert in cert_info */ + r = sc_pkcs15_read_certificate(p15card, &cert_info, 0, &cert_out); + if (r < 0 || cert_out->key == NULL) { + sc_log(card->ctx, "Failed to read/parse the certificate r=%d",r); + if (cert_out != NULL) + sc_pkcs15_free_certificate(cert_out); + free(cert_der.value); + continue; + } + + r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); + if (r < 0) { + sc_log(card->ctx, " Failed to add cert obj r=%d",r); + sc_pkcs15_free_certificate(cert_out); + free(cert_der.value); + continue; + } + /* set the token name to the name of the CN of the first certificate */ + if (!token_name) { + u8 * cn_name = NULL; + size_t cn_len = 0; + static const struct sc_object_id cn_oid = {{ 2, 5, 4, 3, -1 }}; + r = sc_pkcs15_get_name_from_dn(card->ctx, cert_out->subject, + cert_out->subject_len, &cn_oid, &cn_name, &cn_len); + if (r == SC_SUCCESS) { + token_name = malloc (cn_len+1); + if (!token_name) { + free(cn_name); + r = SC_ERROR_OUT_OF_MEMORY; + goto fail; + } + memcpy(token_name, cn_name, cn_len); + free(cn_name); + token_name[cn_len] = '\0'; + free(p15card->tokeninfo->label); + p15card->tokeninfo->label = token_name; + } + } + + + r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, cert_out->key, + &pubkey_info.direct.spki.value, &pubkey_info.direct.spki.len); + if (r < 0) + goto fail; + pubkey_obj.emulated = cert_out->key; + + r = sc_pkcs15_get_bitstring_extension(card->ctx, cert_out, &usage_type, &usage, NULL); + if (r < 0) { + usage = SC_X509_DATA_ENCIPHERMENT|SC_X509_DIGITAL_SIGNATURE; /* basic default usage */ + } + sc_pkcs15_map_usage(usage, cert_out->key->algorithm, &pubkey_info.usage, &prkey_info.usage, 1); + sc_log(card->ctx, "cert %s: cert_usage=0x%x, pub_usage=0x%x priv_usage=0x%x\n", + sc_dump_hex(cert_info.id.value, cert_info.id.len), + usage, pubkey_info.usage, prkey_info.usage); + + if (cert_out->key->algorithm == SC_ALGORITHM_RSA) { + pubkey_info.modulus_length = cert_out->key->u.rsa.modulus.len * 8; + prkey_info.modulus_length = cert_out->key->u.rsa.modulus.len * 8; + sc_log(card->ctx, "adding rsa public key r=%d usage=%x", r, pubkey_info.usage); + r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); + if (r < 0) { + free(pubkey_info.direct.spki.value); + goto fail; + } + pubkey_info.direct.spki.value = NULL; /* moved to the pubkey object on p15card */ + pubkey_info.direct.spki.len = 0; + if (prkey_info.key_reference >= 0) { + sc_log(card->ctx, "adding rsa private key r=%d usage=%x", r, prkey_info.usage); + r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); + } else { + sc_log(card->ctx, "missing rsa private key r=%d usage=%x", r, prkey_info.usage); + } + if (r < 0) + goto fail; + } else if (cert_out->key->algorithm == SC_ALGORITHM_EC) { + pubkey_info.field_length = cert_out->key->u.ec.params.field_length; + prkey_info.field_length = cert_out->key->u.ec.params.field_length; + sc_log(card->ctx, "adding ec public key r=%d usage=%x", r, pubkey_info.usage); + r = sc_pkcs15emu_add_ec_pubkey(p15card, &pubkey_obj, &pubkey_info); + if (r < 0) { + free(pubkey_info.direct.spki.value); + goto fail; + } + pubkey_info.direct.spki.value = NULL; + pubkey_info.direct.spki.len = 0; + if (prkey_info.key_reference >= 0) { + sc_log(card->ctx, "adding ec private key r=%d usage=%x", r, prkey_info.usage); + r = sc_pkcs15emu_add_ec_prkey(p15card, &prkey_obj, &prkey_info); + } else { + sc_log(card->ctx, "missing ec private key r=%d usage=%x", r, prkey_info.usage); + } + if (r < 0) + goto fail; + } else { + sc_log(card->ctx, "unsupported key.algorithm %lu", cert_out->key->algorithm); + sc_pkcs15_free_certificate(cert_out); + free(pubkey_info.direct.spki.value); + continue; + } + + cert_out->key = NULL; +fail: + sc_pkcs15_free_certificate(cert_out); + if (r < 0) { + (card->ops->card_ctl)(card, SC_CARDCTL_IDPRIME_FINAL_GET_OBJECTS, &count); + LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to add object."); + } + + } + r = (card->ops->card_ctl)(card, SC_CARDCTL_IDPRIME_FINAL_GET_OBJECTS, &count); + LOG_TEST_GOTO_ERR(card->ctx, r, "Can not finalize cert objects."); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + +err: + sc_pkcs15_card_clear(p15card); + LOG_FUNC_RETURN(card->ctx, r); +} + +int sc_pkcs15emu_idprime_init_ex(sc_pkcs15_card_t *p15card, + struct sc_aid *aid) +{ + sc_card_t *card = p15card->card; + sc_context_t *ctx = card->ctx; + + LOG_FUNC_CALLED(ctx); + + if (idprime_detect_card(p15card)) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_idprime_init(p15card); +} diff --git a/src/libopensc/pkcs15-infocamere.c b/src/libopensc/pkcs15-infocamere.c deleted file mode 100644 index 14c188794a..0000000000 --- a/src/libopensc/pkcs15-infocamere.c +++ /dev/null @@ -1,849 +0,0 @@ -/* - * PKCS15 emulation layer for 1202, 1203 and 1400 Infocamere card. - * To see how this works, run p15dump on your Infocamere card. - * - * Copyright (C) 2005, Sirio Capizzi - * Copyright (C) 2004, Antonino Iacono - * Copyright (C) 2003, Olaf Kirch - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#ifdef ENABLE_ZLIB -#include -#endif - -#include "common/compat_strlcpy.h" -#include "pkcs15.h" -#include "log.h" - -int sc_pkcs15emu_infocamere_init_ex(sc_pkcs15_card_t *, struct sc_aid *aid, - sc_pkcs15emu_opt_t *); - -static int (*set_security_env) (sc_card_t *, const sc_security_env_t *, - int); - -static int set_sec_env(sc_card_t * card, const sc_security_env_t * env, - int se_num) -{ - sc_security_env_t tenv = *env; - if (tenv.operation == SC_SEC_OPERATION_SIGN) - tenv.operation = SC_SEC_OPERATION_DECIPHER; - return set_security_env(card, &tenv, se_num); -} - -static int do_sign(sc_card_t * card, const u8 * in, size_t inlen, u8 * out, - size_t outlen) -{ - return card->ops->decipher(card, in, inlen, out, outlen); -} - -static void set_string(char **strp, const char *value) -{ - if (*strp) - free(*strp); - *strp = value ? strdup(value) : NULL; -} - -#if 1 -/* XXX: temporary copy of the old pkcs15emu functions, - * to be removed */ -static int sc_pkcs15emu_add_pin(sc_pkcs15_card_t *p15card, - const sc_pkcs15_id_t *id, const char *label, - const sc_path_t *path, int ref, int type, - unsigned int min_length, - unsigned int max_length, - int flags, int tries_left, const char pad_char, int obj_flags) -{ - sc_pkcs15_auth_info_t info; - sc_pkcs15_object_t obj; - - memset(&info, 0, sizeof(info)); - memset(&obj, 0, sizeof(obj)); - - info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; - info.auth_id = *id; - info.attrs.pin.min_length = min_length; - info.attrs.pin.max_length = max_length; - info.attrs.pin.stored_length = max_length; - info.attrs.pin.type = type; - info.attrs.pin.reference = ref; - info.attrs.pin.flags = flags; - info.attrs.pin.pad_char = pad_char; - info.tries_left = tries_left; - info.logged_in = SC_PIN_STATE_UNKNOWN; - - if (path) - info.path = *path; - if (type == SC_PKCS15_PIN_TYPE_BCD) - info.attrs.pin.stored_length /= 2; - - strlcpy(obj.label, label, sizeof(obj.label)); - obj.flags = obj_flags; - - return sc_pkcs15emu_add_pin_obj(p15card, &obj, &info); -} - -static int sc_pkcs15emu_add_prkey(sc_pkcs15_card_t *p15card, - const sc_pkcs15_id_t *id, - const char *label, - int type, unsigned int modulus_length, int usage, - const sc_path_t *path, int ref, - const sc_pkcs15_id_t *auth_id, int obj_flags) -{ - sc_pkcs15_prkey_info_t info; - sc_pkcs15_object_t obj; - - memset(&info, 0, sizeof(info)); - memset(&obj, 0, sizeof(obj)); - - info.id = *id; - info.modulus_length = modulus_length; - info.usage = usage; - info.native = 1; - info.key_reference = ref; - - if (path) - info.path = *path; - - obj.flags = obj_flags; - strlcpy(obj.label, label, sizeof(obj.label)); - if (auth_id != NULL) - obj.auth_id = *auth_id; - - return sc_pkcs15emu_add_rsa_prkey(p15card, &obj, &info); -} - -static int sc_pkcs15emu_add_cert(sc_pkcs15_card_t *p15card, - int type, int authority, const sc_path_t *path, - const sc_pkcs15_id_t *id, const char *label, int obj_flags) -{ - /* const char *label = "Certificate"; */ - sc_pkcs15_cert_info_t info; - sc_pkcs15_object_t obj; - - memset(&info, 0, sizeof(info)); - memset(&obj, 0, sizeof(obj)); - - info.id = *id; - info.authority = authority; - if (path) - info.path = *path; - - strlcpy(obj.label, label, sizeof(obj.label)); - obj.flags = obj_flags; - - return sc_pkcs15emu_add_x509_cert(p15card, &obj, &info); -} -#endif - -static int infocamere_1200_init(sc_pkcs15_card_t * p15card) -{ - const int prkey_usage = SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; - const int authprkey_usage = SC_PKCS15_PRKEY_USAGE_SIGN - | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER - | SC_PKCS15_PRKEY_USAGE_ENCRYPT - | SC_PKCS15_PRKEY_USAGE_DECRYPT; - - sc_card_t *card = p15card->card; - sc_path_t path; - sc_file_t *file; - sc_pkcs15_id_t id, auth_id; - unsigned char buffer[256]; - unsigned char ef_gdo[256]; - char serial[256]; - unsigned char certlen[2]; - int authority, change_sign = 0; - struct sc_pkcs15_cert_info cert_info; - struct sc_pkcs15_object cert_obj; - - const char *label = "User Non-repudiation Certificate"; - const char *calabel = "CA Certificate"; - const char *authlabel = "User Authentication Certificate"; - - const char *infocamere_cert_path[2] = { - "DF01C000", - "3F00000011111A02" - }; - - const char *infocamere_auth_certpath[2] = { - "11111A02", - "000011111B02" - }; - - const char *infocamere_cacert_path[2] = { - "DF01C008", - "000011114101" - }; - - const char *infocamere_auth_path[2] = { - "3F001111", - "3F0000001111" - }; - - const char *infocamere_nrepud_path[2] = { - "3F00DF01", - "3F0000001111" - }; - - const int infocamere_idpin_auth_obj[2] = { - 0x95, - 0x81 - }; - - const int infocamere_idpin_nrepud_obj[2] = { - 0x99, - 0x81 - }; - - const int infocamere_idprkey_auth_obj[2] = { - 0x9B, - 0x01 - }; - - const int infocamere_idprkey_nrepud_obj[2] = { - 0x84, - 0x01 - }; - - const char *authPIN = "Authentication PIN"; - const char *nonrepPIN = "Non-repudiation PIN"; - - const char *authPRKEY = "Authentication Key"; - const char *nonrepPRKEY = "Non repudiation Key"; - - const int flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | - SC_PKCS15_PIN_FLAG_INITIALIZED | - SC_PKCS15_PIN_FLAG_NEEDS_PADDING; - - int r, len_iccsn, len_chn; - - sc_format_path("3F002F02", &path); - - r = sc_select_file(card, &path, &file); - - if (r != SC_SUCCESS || file->size > 255) { - /* Not EF.GDO */ - sc_file_free(file); - return SC_ERROR_WRONG_CARD; - } - - sc_read_binary(card, 0, ef_gdo, file->size, 0); - - if (ef_gdo[0] != 0x5A || file->size < 3) { - /* Not EF.GDO */ - sc_file_free(file); - return SC_ERROR_WRONG_CARD; - } - - len_iccsn = ef_gdo[1]; - - memcpy(buffer, ef_gdo + 2, len_iccsn); - - sc_bin_to_hex(buffer, len_iccsn, serial, sizeof(serial), 0); - - if (file->size < (size_t) (len_iccsn + 5)) { - /* Not CHN */ - sc_file_free(file); - return SC_ERROR_WRONG_CARD; - } - sc_file_free(file); - - if (! - (ef_gdo[len_iccsn + 2] == 0x5F - && ef_gdo[len_iccsn + 3] == 0x20)) { - /* Not CHN */ - return SC_ERROR_WRONG_CARD; - } - - len_chn = ef_gdo[len_iccsn + 4]; - - if (len_chn < 2 || len_chn > 8) { - /* Length CHN incorrect */ - return SC_ERROR_WRONG_CARD; - } - - if (! - (ef_gdo[len_iccsn + 5] == 0x12 - && (ef_gdo[len_iccsn + 6] == 0x02 - || ef_gdo[len_iccsn + 6] == 0x03))) { - /* Not Infocamere Card */ - return SC_ERROR_WRONG_CARD; - } - - set_string(&p15card->tokeninfo->serial_number, serial); - - if (ef_gdo[len_iccsn + 6] == 0x02) - set_string(&p15card->tokeninfo->label, "Infocamere 1202 Card"); - else { - set_string(&p15card->tokeninfo->label, "Infocamere 1203 Card"); - change_sign = 1; - } - - set_string(&p15card->tokeninfo->manufacturer_id, "Infocamere"); - - authority = 0; - - /* Get the authentication certificate length */ - - sc_format_path(infocamere_auth_certpath[ef_gdo[len_iccsn+6]-2], &path); - - r = sc_select_file(card, &path, NULL); - - if (r >= 0) { - - sc_read_binary(card, 0, certlen, 2, 0); - - /* Now set the certificate offset/len */ - - path.index = 2; - path.count = (certlen[1] << 8) + certlen[0]; - - memset(&cert_info, 0, sizeof(cert_info)); - memset(&cert_obj, 0, sizeof(cert_obj)); - - sc_pkcs15_format_id("01", &cert_info.id); - cert_info.authority = authority; - cert_info.path = path; - strlcpy(cert_obj.label, authlabel, sizeof(cert_obj.label)); - cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; - - r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); - if (r < 0) - return SC_ERROR_INTERNAL; - - /* XXX: the IDs for the key/pin in case of the 1203 type - * are wrong, therefore I disable them for now -- Nils */ - if (!change_sign) { - /* add authentication PIN */ - - sc_format_path(infocamere_auth_path[ef_gdo[len_iccsn+6]-2], &path); - - sc_pkcs15_format_id("01", &id); - sc_pkcs15emu_add_pin(p15card, &id, - authPIN, &path, infocamere_idpin_auth_obj[ef_gdo[len_iccsn+6]-2], - SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, - 5, 8, flags, 3, 0, - SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE); - - /* add authentication private key */ - - auth_id.value[0] = 1; - auth_id.len = 1; - - sc_pkcs15emu_add_prkey(p15card, &id, - authPRKEY, - SC_PKCS15_TYPE_PRKEY_RSA, - 1024, authprkey_usage, - &path, infocamere_idprkey_auth_obj[ef_gdo[len_iccsn+6]-2], - &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); - } - - } - - /* Get the non-repudiation certificate length */ - - sc_format_path(infocamere_cert_path[ef_gdo[len_iccsn+6]-2], &path); - - if (sc_select_file(card, &path, NULL) < 0) { - return SC_ERROR_INTERNAL; - } - - sc_read_binary(card, 0, certlen, 2, 0); - - /* Now set the certificate offset/len */ - path.index = 2; - path.count = (certlen[1] << 8) + certlen[0]; - - memset(&cert_info, 0, sizeof(cert_info)); - memset(&cert_obj, 0, sizeof(cert_obj)); - - sc_pkcs15_format_id("02", &cert_info.id); - - cert_info.authority = authority; - cert_info.path = path; - strlcpy(cert_obj.label, label, sizeof(cert_obj.label)); - cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; - - r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); - if (r < 0) - return SC_ERROR_INTERNAL; - - /* Get the CA certificate length */ - - authority = 1; - - sc_format_path(infocamere_cacert_path[ef_gdo[len_iccsn+6]-2], &path); - - r = sc_select_file(card, &path, NULL); - - if (r >= 0) { - size_t len; - - sc_read_binary(card, 0, certlen, 2, 0); - - len = (certlen[1] << 8) + certlen[0]; - - if (len != 0) { - /* Now set the certificate offset/len */ - path.index = 2; - path.count = len; - - memset(&cert_info, 0, sizeof(cert_info)); - memset(&cert_obj, 0, sizeof(cert_obj)); - - sc_pkcs15_format_id("03", &cert_info.id); - cert_info.authority = authority; - cert_info.path = path; - strlcpy(cert_obj.label, calabel, sizeof(cert_obj.label)); - cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; - - r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); - if (r < 0) - return SC_ERROR_INTERNAL; - } - } - - /* add non repudiation PIN */ - - sc_format_path(infocamere_nrepud_path[ef_gdo[len_iccsn+6]-2], &path); - - sc_pkcs15_format_id("02", &id); - sc_pkcs15emu_add_pin(p15card, &id, - nonrepPIN, &path, infocamere_idpin_nrepud_obj[ef_gdo[len_iccsn+6]-2], - SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 5, 8, flags, 3, 0, - SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE); - - - /* add non repudiation private key */ - - auth_id.value[0] = 2; - auth_id.len = 1; - - sc_pkcs15emu_add_prkey(p15card, &id, nonrepPRKEY, - SC_PKCS15_TYPE_PRKEY_RSA, - 1024, prkey_usage, - &path, infocamere_idprkey_nrepud_obj[ef_gdo[len_iccsn+6]-2], - &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); - - - /* return to MF */ - sc_format_path("3F00", &path); - r = sc_select_file(card, &path, NULL); - if (r != SC_SUCCESS) - return r; - - if (change_sign) { - /* save old signature funcs */ - set_security_env = card->ops->set_security_env; - /* set new one */ - card->ops->set_security_env = set_sec_env; - card->ops->compute_signature = do_sign; - } - - return SC_SUCCESS; -} - -static int infocamere_1400_set_sec_env(struct sc_card *card, - const struct sc_security_env *env, - int se_num) -{ - int r; - - struct sc_security_env tenv = *env; - if (tenv.operation == SC_SEC_OPERATION_SIGN) - tenv.operation = SC_SEC_OPERATION_DECIPHER; - - if ((r = - card->ops->restore_security_env(card, 0x40)) == SC_SUCCESS) - return set_security_env(card, &tenv, se_num); - else - return r; -} - -#ifdef ENABLE_ZLIB - -static const u8 ATR_1400[] = -{ 0x3b, 0xfc, 0x98, 0x00, 0xff, 0xc1, 0x10, 0x31, 0xfe, 0x55, 0xc8, - 0x03, 0x49, 0x6e, 0x66, 0x6f, 0x63, 0x61, 0x6d, 0x65, 0x72, 0x65, - 0x28 -}; - -/* Loads certificates. - * Certificates are stored in a ZLib compressed form with - * a 4 byte header, so we extract, decompress and cache - * them. - */ -static int loadCertificate(sc_pkcs15_card_t * p15card, int i, - const char *certPath, const char *certLabel) -{ - unsigned char *compCert = NULL, *cert = NULL, size[2]; - unsigned long int compLen, len; - sc_pkcs15_cert_info_t cert_info; - sc_pkcs15_object_t cert_obj; - sc_path_t cpath; - sc_card_t *card = p15card->card; - sc_pkcs15_id_t id; - int r; - - memset(&cert_info, 0, sizeof(cert_info)); - memset(&cert_obj, 0, sizeof(cert_obj)); - - sc_format_path(certPath, &cpath); - - if (sc_select_file(card, &cpath, NULL) != SC_SUCCESS) - return SC_ERROR_WRONG_CARD; - - sc_read_binary(card, 2, size, 2, 0); - - compLen = (size[0] << 8) + size[1]; - compCert = malloc(compLen * sizeof(unsigned char)); - len = 4 * compLen; /*Approximation of the uncompressed size */ - cert = malloc(len * sizeof(unsigned char)); - if (!cert || !compCert) { - free(cert); - free(compCert); - return SC_ERROR_OUT_OF_MEMORY; - } - - sc_read_binary(card, 4, compCert, compLen, 0); - - if ((r = uncompress(cert, &len, compCert, compLen)) != Z_OK) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Zlib error: %d", r); - return SC_ERROR_INTERNAL; - } - - cpath.index = 0; - cpath.count = len; - - sc_pkcs15_cache_file(p15card, &cpath, cert, len); - - id.len=1; - id.value[0] = i + 1; - - cert_info.id = id; - cert_info.path = cpath; - cert_info.authority = (i == 2); - - strlcpy(cert_obj.label, certLabel, sizeof(cert_obj.label)); - cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; - - sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); - - return SC_SUCCESS; -} - - -static int infocamere_1400_init(sc_pkcs15_card_t * p15card) -{ - sc_card_t *card = p15card->card; - sc_path_t path; - sc_pkcs15_id_t id, auth_id; - unsigned char serial[16]; - int flags; - int r; - int hasAuthCert = 0; - - const char *certLabel[] = { "User Non-repudiation Certificate", - "User Authentication Certificate", - "CA Certificate" - }; - - const char *certPath[] = - { "300060000000", "300060000001", "300060000002" }; - - const char *pinLabel[] = - { "Non-repudiation PIN", "Authentication PIN" }; - int retries[] = { 3, -1 }; - - const char *keyPath[] = { "30004000001", "30004000002" }; - const char *keyLabel[] = - { "Non repudiation Key", "Authentication Key" }; - static int usage[] = { SC_PKCS15_PRKEY_USAGE_NONREPUDIATION, - SC_PKCS15_PRKEY_USAGE_SIGN - | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER - | SC_PKCS15_PRKEY_USAGE_ENCRYPT - | SC_PKCS15_PRKEY_USAGE_DECRYPT - }; - - auth_id.len = 1; - id.len = 1; - - /* OpenSC doesn't define constants to identify BSOs for - * restoring security environment, so we overload - * the set_security_env function to support restore_sec_env */ - set_security_env = card->ops->set_security_env; - card->ops->set_security_env = infocamere_1400_set_sec_env; - card->ops->compute_signature = do_sign; - p15card->opts.use_file_cache = 1; - - sc_format_path("30000001", &path); - - r = sc_select_file(card, &path, NULL); - - if (r != SC_SUCCESS) - return SC_ERROR_WRONG_CARD; - - sc_read_binary(card, 15, serial, 15, 0); - serial[15] = '\0'; - - set_string(&p15card->tokeninfo->serial_number, (char *)serial); - set_string(&p15card->tokeninfo->label, "Infocamere 1400 Card"); - set_string(&p15card->tokeninfo->manufacturer_id, "Infocamere"); - - if ((r = loadCertificate(p15card, 0, certPath[0], certLabel[0])) != - SC_SUCCESS) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "%s", sc_strerror(r)); - return SC_ERROR_WRONG_CARD; - } - - hasAuthCert = - loadCertificate(p15card, 1, certPath[1], - certLabel[1]) == SC_SUCCESS; - loadCertificate(p15card, 2, certPath[2], certLabel[2]); - - flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | - SC_PKCS15_PIN_FLAG_INITIALIZED | - SC_PKCS15_PIN_FLAG_NEEDS_PADDING; - - /* adding PINs & private keys */ - - sc_format_path("30004000", &path); - id.value[0] = 1; - - sc_pkcs15emu_add_pin(p15card, &id, - pinLabel[0], &path, 1, - SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, - 5, 8, flags, retries[0], 0, - SC_PKCS15_CO_FLAG_MODIFIABLE | - SC_PKCS15_CO_FLAG_PRIVATE); - - sc_format_path(keyPath[0], &path); - auth_id.value[0] = 1; - sc_pkcs15emu_add_prkey(p15card, &id, - keyLabel[0], - SC_PKCS15_TYPE_PRKEY_RSA, - 1024, usage[0], - &path, 1, - &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); - - - if (hasAuthCert) { - sc_format_path("30004000", &path); - id.value[0] = 2; - - sc_pkcs15emu_add_pin(p15card, &id, - pinLabel[1], &path, 2, - SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, - 5, 8, flags, retries[1], 0, - SC_PKCS15_CO_FLAG_MODIFIABLE | - SC_PKCS15_CO_FLAG_PRIVATE); - - sc_format_path(keyPath[1], &path); - auth_id.value[0] = 2; - sc_pkcs15emu_add_prkey(p15card, &id, - keyLabel[1], - SC_PKCS15_TYPE_PRKEY_RSA, - 1024, usage[1], - &path, 2, - &auth_id, - SC_PKCS15_CO_FLAG_PRIVATE); - } - - /* return to MF */ - sc_format_path("3F00", &path); - r = sc_select_file(card, &path, NULL); - return r; -} - -#endif - -static const u8 ATR_1600[] = { 0x3B, 0xF4, 0x98, 0x00, 0xFF, 0xC1, 0x10, - 0x31, 0xFE, 0x55, 0x4D, 0x34, 0x63, 0x76, 0xB4 -}; - -static int infocamere_1600_init(sc_pkcs15_card_t * p15card) -{ - sc_card_t *card = p15card->card; - sc_path_t path; - sc_pkcs15_id_t id, auth_id; - unsigned char serial[17]; - int flags; - int r; - int hasAuthCert = 0; - - const char *certLabel[] = { "User Non-repudiation Certificate", - "User Authentication Certificate" - }; - - const char *certPath[] = { "200020010008", "20002001000E" }; - - const char *pinLabel[] = - { "Non-repudiation PIN", "Authentication PIN" }; - int retries[] = { 3, -1 }; - - const char *keyPath[] = { "200020010004", "20002001000A" }; - const char *keyLabel[] = - { "Non repudiation Key", "Authentication Key" }; - static int usage[] = { SC_PKCS15_PRKEY_USAGE_NONREPUDIATION, - SC_PKCS15_PRKEY_USAGE_SIGN - | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER - | SC_PKCS15_PRKEY_USAGE_ENCRYPT - | SC_PKCS15_PRKEY_USAGE_DECRYPT - }; - - auth_id.len = 1; - id.len = 1; - - /* OpenSC doesn't define constants to identify BSOs for - * restoring security environment, so we overload - * the set_security_env function to support restore_sec_env */ - set_security_env = card->ops->set_security_env; - card->ops->set_security_env = infocamere_1400_set_sec_env; - card->ops->compute_signature = do_sign; - - sc_format_path("200020012002", &path); - - r = sc_select_file(card, &path, NULL); - - if (r != SC_SUCCESS) - return SC_ERROR_WRONG_CARD; - - sc_read_binary(card, 30, serial, 16, 0); - serial[16] = '\0'; - - set_string(&p15card->tokeninfo->serial_number, (char *) serial); - set_string(&p15card->tokeninfo->label, "Infocamere 1600 Card"); - set_string(&p15card->tokeninfo->manufacturer_id, "Infocamere"); - - /* Adding certificates. - * Certificates are stored in a ZLib compressed form with - * a 4 byte header, so we extract, decompress and cache - * them. - */ - sc_format_path(certPath[0], &path); - if (sc_select_file(card, &path, NULL) != SC_SUCCESS) - return SC_ERROR_WRONG_CARD; - - id.value[0] = 1; - - sc_pkcs15emu_add_cert(p15card, - SC_PKCS15_TYPE_CERT_X509, 0, - &path, &id, certLabel[0], - SC_PKCS15_CO_FLAG_MODIFIABLE); - - sc_format_path(certPath[1], &path); - if (sc_select_file(card, &path, NULL) == SC_SUCCESS) { - hasAuthCert = 1; - - id.value[0] = 2; - - sc_pkcs15emu_add_cert(p15card, - SC_PKCS15_TYPE_CERT_X509, 1, - &path, &id, certLabel[1], - SC_PKCS15_CO_FLAG_MODIFIABLE); - } - - flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | - SC_PKCS15_PIN_FLAG_INITIALIZED | - SC_PKCS15_PIN_FLAG_NEEDS_PADDING; - - /* adding PINs & private keys */ - sc_format_path("2000", &path); - id.value[0] = 1; - - sc_pkcs15emu_add_pin(p15card, &id, - pinLabel[0], &path, 1, - SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, - 5, 8, flags, retries[0], 0, - SC_PKCS15_CO_FLAG_MODIFIABLE | - SC_PKCS15_CO_FLAG_PRIVATE); - - sc_format_path(keyPath[0], &path); - auth_id.value[0] = 1; - sc_pkcs15emu_add_prkey(p15card, &id, - keyLabel[0], - SC_PKCS15_TYPE_PRKEY_RSA, - 1024, usage[0], - &path, 1, - &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); - - if (hasAuthCert) { - id.value[0] = 2; - - sc_pkcs15emu_add_pin(p15card, &id, - pinLabel[1], &path, 2, - SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, - 5, 8, flags, retries[1], 0, - SC_PKCS15_CO_FLAG_MODIFIABLE | - SC_PKCS15_CO_FLAG_PRIVATE); - - sc_format_path(keyPath[1], &path); - auth_id.value[0] = 2; - sc_pkcs15emu_add_prkey(p15card, &id, - keyLabel[1], - SC_PKCS15_TYPE_PRKEY_RSA, - 1024, usage[1], - &path, 2, - &auth_id, - SC_PKCS15_CO_FLAG_PRIVATE); - } - - /* return to MF */ - sc_format_path("3F00", &path); - r = sc_select_file(card, &path, NULL); - - return SC_SUCCESS; -} - -static int infocamere_detect_card(sc_pkcs15_card_t * p15card) -{ - sc_card_t *card = p15card->card; - - /* check if we have the correct card OS */ - if (strcmp(card->name, "STARCOS SPK 2.3") - && strcmp(card->name, "CardOS M4")) - return SC_ERROR_WRONG_CARD; - return SC_SUCCESS; -} - -int sc_pkcs15emu_infocamere_init_ex(sc_pkcs15_card_t * p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t * opts) -{ - - if (!(opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)) { - if (infocamere_detect_card(p15card)) - return SC_ERROR_WRONG_CARD; - } - - if (memcmp(p15card->card->atr.value, ATR_1600, sizeof(ATR_1600)) == 0) - return infocamere_1600_init(p15card); -#ifdef ENABLE_ZLIB - else if (memcmp(p15card->card->atr.value, ATR_1400, sizeof(ATR_1400)) == - 0) - return infocamere_1400_init(p15card); -#endif - else - return infocamere_1200_init(p15card); - -} diff --git a/src/libopensc/pkcs15-itacns.c b/src/libopensc/pkcs15-itacns.c index 53aa2b0a53..17580f0a39 100644 --- a/src/libopensc/pkcs15-itacns.c +++ b/src/libopensc/pkcs15-itacns.c @@ -17,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* @@ -34,11 +34,6 @@ #include #include -#ifdef ENABLE_OPENSSL -#include -#include -#endif - #include "internal.h" #include "pkcs15.h" #include "log.h" @@ -52,8 +47,6 @@ #include #endif -int sc_pkcs15emu_itacns_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); - static const char path_serial[] = "10001003"; /* Manufacturers */ @@ -64,14 +57,15 @@ const char * itacns_mask_manufacturers[] = { "Gemplus", "Ghirlanda", "Giesecke & Devrient", - "Oberthur Card Systems", + "IDEMIA (Oberthur)", "Orga", "Axalto", "Siemens", "STIncard", "GEP", "EPS Corp", - "Athena" + "Athena", + "Gemalto", }; const char * iso7816_ic_manufacturers[] = { @@ -170,18 +164,11 @@ static const struct { * Utility functions */ -static void set_string(char **strp, const char *value) -{ - if (*strp) - free(*strp); - *strp = value ? strdup(value) : NULL; -} - static int loadFile(const sc_pkcs15_card_t *p15card, const sc_path_t *path, u8 *buf, const size_t buflen) { int sc_res; - SC_FUNC_CALLED(p15card->card->ctx, 1); + SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_NORMAL); sc_res = sc_select_file(p15card->card, path, NULL); if(sc_res != SC_SUCCESS) @@ -198,7 +185,7 @@ static int loadFile(const sc_pkcs15_card_t *p15card, const sc_path_t *path, static int itacns_add_cert(sc_pkcs15_card_t *p15card, int type, int authority, const sc_path_t *path, const sc_pkcs15_id_t *id, const char *label, int obj_flags, - int *ext_info_ok, int *key_usage, int *x_key_usage) + int *ext_info_ok, int *key_usage, int *x_key_usage, size_t *modulus_len) { int r; /* const char *label = "Certificate"; */ @@ -207,18 +194,19 @@ static int itacns_add_cert(sc_pkcs15_card_t *p15card, #ifdef ENABLE_OPENSSL X509 *x509; sc_pkcs15_cert_t *cert; + int private_obj; #endif - SC_FUNC_CALLED(p15card->card->ctx, 1); - + SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_NORMAL); + if(type != SC_PKCS15_TYPE_CERT_X509) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(p15card->card->ctx, "Cannot add a certificate of a type other than X.509"); return 1; } - + *ext_info_ok = 0; - + memset(&info, 0, sizeof(info)); memset(&obj, 0, sizeof(obj)); @@ -229,25 +217,28 @@ static int itacns_add_cert(sc_pkcs15_card_t *p15card, info.path = *path; strlcpy(obj.label, label, sizeof(obj.label)); - obj.flags = obj_flags; - - r = sc_pkcs15emu_add_x509_cert(p15card, &obj, &info); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, - "Could not add X.509 certificate"); /* If we have OpenSSL, read keyUsage */ #ifdef ENABLE_OPENSSL - - r = sc_pkcs15_read_certificate(p15card, &info, &cert); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, + private_obj = obj_flags & SC_PKCS15_CO_FLAG_PRIVATE; + r = sc_pkcs15_read_certificate(p15card, &info, private_obj, &cert); + LOG_TEST_RET(p15card->card->ctx, r, "Could not read X.509 certificate"); { const u8 *throwaway = cert->data.value; x509 = d2i_X509(NULL, &throwaway, cert->data.len); } + + if (cert->key && cert->key->algorithm == SC_ALGORITHM_RSA) { + *modulus_len = cert->key->u.rsa.modulus.len * 8; + } + sc_pkcs15_free_certificate(cert); - if (!x509) return SC_SUCCESS; + if (!x509) { + sc_log_openssl(p15card->card->ctx); + return SC_SUCCESS; + } X509_check_purpose(x509, -1, 0); if(X509_get_extension_flags(x509) & EXFLAG_KUSAGE) { @@ -257,25 +248,25 @@ static int itacns_add_cert(sc_pkcs15_card_t *p15card, } OPENSSL_free(x509); - return SC_SUCCESS; - -#else /* ENABLE_OPENSSL */ - - return SC_SUCCESS; - #endif /* ENABLE_OPENSSL */ + obj.flags = obj_flags; + r = sc_pkcs15emu_add_x509_cert(p15card, &obj, &info); + LOG_TEST_RET(p15card->card->ctx, r, + "Could not add X.509 certificate"); + + return r; } static int itacns_add_pubkey(sc_pkcs15_card_t *p15card, const sc_path_t *path, const sc_pkcs15_id_t *id, const char *label, - int usage, int ref, int obj_flags, int *modulus_len_out) + int usage, int ref, int obj_flags, size_t modulus_len) { int r; sc_pkcs15_pubkey_info_t info; sc_pkcs15_object_t obj; - SC_FUNC_CALLED(p15card->card->ctx, 1); + SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_NORMAL); memset(&info, 0, sizeof(info)); memset(&obj, 0, sizeof(obj)); @@ -288,15 +279,10 @@ static int itacns_add_pubkey(sc_pkcs15_card_t *p15card, strlcpy(obj.label, label, sizeof(obj.label)); obj.flags = obj_flags; - /* - * This is hard-coded, unless unforeseen versions of the CNS - * turn up sometime. - */ - info.modulus_length = 1024; + info.modulus_length = modulus_len; - *modulus_len_out = info.modulus_length; r = sc_pkcs15emu_add_rsa_pubkey(p15card, &obj, &info); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_RET(p15card->card->ctx, r, "Could not add pub key"); return r; } @@ -304,17 +290,17 @@ static int itacns_add_pubkey(sc_pkcs15_card_t *p15card, static int itacns_add_prkey(sc_pkcs15_card_t *p15card, const sc_pkcs15_id_t *id, const char *label, - int type, unsigned int modulus_length, int usage, + int type, size_t modulus_length, int usage, const sc_path_t *path, int ref, const sc_pkcs15_id_t *auth_id, int obj_flags) { sc_pkcs15_prkey_info_t info; sc_pkcs15_object_t obj; - SC_FUNC_CALLED(p15card->card->ctx, 1); + SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_NORMAL); if(type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(p15card->card->ctx, "Cannot add a private key of a type other than RSA"); return 1; } @@ -350,7 +336,7 @@ static int itacns_add_pin(sc_pkcs15_card_t *p15card, struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; - SC_FUNC_CALLED(p15card->card->ctx, 1); + SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_NORMAL); memset(&pin_info, 0, sizeof(pin_info)); pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; @@ -388,26 +374,39 @@ static int hextoint(char *src, unsigned int len) if(len >= sizeof(hex)) return -1; - strncpy(hex, src, len+1); + strncpy(hex, src, len); hex[len] = '\0'; - res = strtol(hex, &end, 0x10); + res = (int)strtol(hex, &end, 0x10); if(end != (char*)&hex[len]) return -1; return res; } static int get_name_from_EF_DatiPersonali(unsigned char *EFdata, - char name[], int name_len) + size_t EFdata_len, char name[], int name_len) { + const unsigned int EF_personaldata_maxlen = 400; + const unsigned int tlv_length_size = 6; + char *file = NULL; + int file_size; + /* * Bytes 0-5 contain the ASCII encoding of the following TLV - * strcture's total size, in base 16. + * structure's total size, in base 16. */ - - const unsigned int EF_personaldata_maxlen = 400; - const unsigned int tlv_length_size = 6; - char *file = (char*)&EFdata[tlv_length_size]; - int file_size = hextoint((char*)EFdata, tlv_length_size); + if (EFdata_len < tlv_length_size) { + /* We need at least 6 bytes for file length here */ + return -1; + } + file_size = hextoint((char*)EFdata, tlv_length_size); + if (EFdata_len < (file_size + tlv_length_size)) { + /* Inconsistent external file length and internal file length + * suggests we are trying to process junk data. + * If the internal data length is shorter, the data can be padded, + * but we should be fine as we will not go behind the buffer limits */ + return -1; + } + file = (char*)&EFdata[tlv_length_size]; enum { f_issuer_code = 0, @@ -436,7 +435,7 @@ static int get_name_from_EF_DatiPersonali(unsigned char *EFdata, int i=0; /* offset inside the file */ int f; /* field number */ - if(file_size < 0) + if (file_size < 0) return -1; /* @@ -451,17 +450,16 @@ static int get_name_from_EF_DatiPersonali(unsigned char *EFdata, for(f=0; f file_size) + if(i+2 > file_size) return -1; - field_size = hextoint((char*) &file[i], 2); - if((field_size < 0) || (field_size+i > file_size)) - return -1; - i += 2; - if(field_size >= (int)sizeof(fields[f].value)) + if (field_size < 0 + || i + field_size > file_size + || field_size >= (int)sizeof(fields[f].value)) return -1; fields[f].len = field_size; @@ -473,8 +471,10 @@ static int get_name_from_EF_DatiPersonali(unsigned char *EFdata, if (fields[f_first_name].len + fields[f_last_name].len + 1 >= name_len) return -1; - snprintf(name, name_len, "%s %s", - fields[f_first_name].value, fields[f_last_name].value); + /* the lengths are already checked that they will fit in buffer */ + snprintf(name, name_len, "%.*s %.*s", + fields[f_first_name].len, fields[f_first_name].value, + fields[f_last_name].len, fields[f_last_name].value); return 0; } @@ -488,6 +488,7 @@ static int itacns_add_data_files(sc_pkcs15_card_t *p15card) sc_pkcs15_data_info_t dinfo; struct sc_pkcs15_object *objs[32]; struct sc_pkcs15_data_info *cinfo; + int private_obj; for(i=0; i < array_size; i++) { sc_path_t path; @@ -508,7 +509,7 @@ static int itacns_add_data_files(sc_pkcs15_card_t *p15card) sizeof(obj.label)); data.path = path; rv = sc_pkcs15emu_add_data_object(p15card, &obj, &data); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, rv, + LOG_TEST_RET(p15card->card->ctx, rv, "Could not add data file"); } @@ -518,14 +519,14 @@ static int itacns_add_data_files(sc_pkcs15_card_t *p15card) * user-friendlier card name. */ memset(&dinfo, 0, sizeof(dinfo)); - strcpy(dinfo.app_label, "EF_DatiPersonali"); + strlcpy(dinfo.app_label, "EF_DatiPersonali", sizeof(dinfo.app_label)); /* Find EF_DatiPersonali */ rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_DATA_OBJECT, objs, 32); if(rv < 0) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(p15card->card->ctx, "Data enumeration failed"); return SC_SUCCESS; } @@ -537,31 +538,37 @@ static int itacns_add_data_files(sc_pkcs15_card_t *p15card) } if(i>=32) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(p15card->card->ctx, "Could not find EF_DatiPersonali: " "keeping generic card name"); return SC_SUCCESS; } - rv = sc_pkcs15_read_data_object(p15card, cinfo, &p15_personaldata); + private_obj = objs[i]->flags & SC_PKCS15_CO_FLAG_PRIVATE; + rv = sc_pkcs15_read_data_object(p15card, cinfo, private_obj, &p15_personaldata); if (rv) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(p15card->card->ctx, "Could not read EF_DatiPersonali: " "keeping generic card name"); + return SC_SUCCESS; } - { + if (p15_personaldata->data) { char fullname[160]; - if(get_name_from_EF_DatiPersonali(p15_personaldata->data, - fullname, sizeof(fullname))) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, + if (get_name_from_EF_DatiPersonali(p15_personaldata->data, + p15_personaldata->data_len, fullname, sizeof(fullname))) { + sc_log(p15card->card->ctx, "Could not parse EF_DatiPersonali: " "keeping generic card name"); sc_pkcs15_free_data_object(p15_personaldata); + free(cinfo->data.value); + cinfo->data.value = NULL; return SC_SUCCESS; } set_string(&p15card->tokeninfo->label, fullname); } + free(cinfo->data.value); + cinfo->data.value = NULL; sc_pkcs15_free_data_object(p15_personaldata); return SC_SUCCESS; } @@ -570,7 +577,7 @@ static int itacns_add_keyset(sc_pkcs15_card_t *p15card, const char *label, int sec_env, sc_pkcs15_id_t *cert_id, const char *pubkey_path, const char *prkey_path, unsigned int pubkey_usage_flags, unsigned int prkey_usage_flags, - u8 pin_ref) + u8 pin_ref, size_t modulus_len) { int r; sc_path_t path; @@ -578,16 +585,14 @@ static int itacns_add_keyset(sc_pkcs15_card_t *p15card, char pinlabel[16]; int fake_puk_authid, pin_flags; - /* This is hard-coded, for the time being. */ - int modulus_length = 1024; /* Public key; not really needed */ /* FIXME: set usage according to the certificate. */ if (pubkey_path) { sc_format_path(pubkey_path, &path); r = itacns_add_pubkey(p15card, &path, cert_id, label, - pubkey_usage_flags, sec_env, 0, &modulus_length); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, + pubkey_usage_flags, sec_env, 0, modulus_len); + LOG_TEST_RET(p15card->card->ctx, r, "Could not add public key"); } @@ -600,10 +605,10 @@ static int itacns_add_keyset(sc_pkcs15_card_t *p15card, private_path = &path; } r = itacns_add_prkey(p15card, cert_id, label, SC_PKCS15_TYPE_PRKEY_RSA, - modulus_length, + modulus_len, prkey_usage_flags, private_path, sec_env, cert_id, SC_PKCS15_CO_FLAG_PRIVATE); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_RET(p15card->card->ctx, r, "Could not add private key"); /* PIN and PUK */ @@ -616,7 +621,7 @@ static int itacns_add_keyset(sc_pkcs15_card_t *p15card, | SC_PKCS15_PIN_FLAG_INITIALIZED; r = itacns_add_pin(p15card, pinlabel, sec_env, fake_puk_authid, pin_ref, private_path, pin_flags); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_RET(p15card->card->ctx, r, "Could not add PIN"); strlcpy(pinlabel, "PUK ", sizeof(pinlabel)); @@ -630,7 +635,7 @@ static int itacns_add_keyset(sc_pkcs15_card_t *p15card, | SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED; r = itacns_add_pin(p15card, pinlabel, fake_puk_authid, 0, pin_ref+1, private_path, pin_flags); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_RET(p15card->card->ctx, r, "Could not add PUK"); return 0; @@ -651,7 +656,8 @@ static int itacns_check_and_add_keyset(sc_pkcs15_card_t *p15card, sc_path_t path; sc_pkcs15_id_t cert_id; int ext_info_ok; - int ku, xku; + int ku = 0, xku = 0; + size_t modulus_len = 0; int pubkey_usage_flags = 0, prkey_usage_flags = 0; cert_id.len = 1; @@ -660,7 +666,7 @@ static int itacns_check_and_add_keyset(sc_pkcs15_card_t *p15card, /* Certificate */ if (!cert_path) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(p15card->card->ctx, "We cannot use keys without a matching certificate"); return SC_ERROR_NOT_SUPPORTED; } @@ -670,7 +676,7 @@ static int itacns_check_and_add_keyset(sc_pkcs15_card_t *p15card, if (r == SC_ERROR_FILE_NOT_FOUND) return 0; if (r != SC_SUCCESS) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(p15card->card->ctx, "Could not find certificate for %s", label); return r; } @@ -682,22 +688,25 @@ static int itacns_check_and_add_keyset(sc_pkcs15_card_t *p15card, */ if (cert_offset) { u8 certlen[3]; + memset(certlen, 0, sizeof certlen); r = loadFile(p15card, &path, certlen, sizeof(certlen)); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_RET(p15card->card->ctx, r, "Could not read certificate file"); - path.index = cert_offset; + if (r < 3) + return SC_ERROR_INVALID_DATA; + path.index = (int)cert_offset; path.count = (certlen[1] << 8) + certlen[2]; - /* If those bytes are 00, then we are probably dealign with an + /* If those bytes are 00, then we are probably dealing with an * empty file. */ if (path.count == 0) return 0; } r = itacns_add_cert(p15card, SC_PKCS15_TYPE_CERT_X509, 0, - &path, &cert_id, label, 0, &ext_info_ok, &ku, &xku); + &path, &cert_id, label, 0, &ext_info_ok, &ku, &xku, &modulus_len); if (r == SC_ERROR_INVALID_ASN1_OBJECT) return 0; - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_RET(p15card->card->ctx, r, "Could not add certificate"); (*found_certificates)++; @@ -722,7 +731,7 @@ static int itacns_check_and_add_keyset(sc_pkcs15_card_t *p15card, prkey_usage_flags |= SC_PKCS15_PRKEY_USAGE_DECRYPT; } #else /* ENABLE_OPENSSL */ - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(p15card->card->ctx, "Extended certificate info retrieved without OpenSSL. " "How is this possible?"); return SC_ERROR_INTERNAL; @@ -739,8 +748,8 @@ static int itacns_check_and_add_keyset(sc_pkcs15_card_t *p15card, r = itacns_add_keyset(p15card, label, sec_env, &cert_id, pubkey_path, prkey_path, pubkey_usage_flags, prkey_usage_flags, - pin_ref); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, + pin_ref, modulus_len); + LOG_TEST_RET(p15card->card->ctx, r, "Could not add keys for this certificate"); return r; @@ -756,7 +765,7 @@ static int itacns_init(sc_pkcs15_card_t *p15card) int found_certs; int card_is_cie_v1, cns0_secenv; - SC_FUNC_CALLED(p15card->card->ctx, 1); + SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_NORMAL); set_string(&p15card->tokeninfo->label, p15card->card->name); if(p15card->card->drv_data) { @@ -814,7 +823,7 @@ static int itacns_init(sc_pkcs15_card_t *p15card) /* Data files */ r = itacns_add_data_files(p15card); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_GOTO_ERR(p15card->card->ctx, r, "Could not add data files"); /*** Certificate and keys. ***/ @@ -822,7 +831,7 @@ static int itacns_init(sc_pkcs15_card_t *p15card) r = itacns_check_and_add_keyset(p15card, "CNS0", cns0_secenv, 0, "3F0011001101", "3F003F01", NULL, 0x10, &found_certs); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_GOTO_ERR(p15card->card->ctx, r, "Could not add CNS0"); certificate_count += found_certs; @@ -830,7 +839,7 @@ static int itacns_init(sc_pkcs15_card_t *p15card) r = itacns_check_and_add_keyset(p15card, "CNS01", 0x21, 5, "3F002FFF8228", NULL, "3F002FFF0000", 0x10, &found_certs); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_GOTO_ERR(p15card->card->ctx, r, "Could not add CNS01"); certificate_count += found_certs; @@ -838,39 +847,48 @@ static int itacns_init(sc_pkcs15_card_t *p15card) r = itacns_check_and_add_keyset(p15card, "CNS1", 0x10, 0, "3F0014009010", "3F00140081108010", "3F0014008110", 0x1a, &found_certs); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_GOTO_ERR(p15card->card->ctx, r, + "Could not add CNS1"); + certificate_count += found_certs; + + /* Idemia card */ + r = itacns_check_and_add_keyset(p15card, "CNS1", 0x02, + 0, "3F00140090012002", "3F0011001102", "3F0014009002", + 0x10, &found_certs); + LOG_TEST_RET(p15card->card->ctx, r, "Could not add CNS1"); certificate_count += found_certs; /* Did we find anything? */ if (certificate_count == 0) - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE, + sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Warning: no certificates found!"); /* Back to Master File */ sc_format_path("3F00", &path); r = sc_select_file(p15card->card, &path, NULL); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_GOTO_ERR(p15card->card->ctx, r, "Could not select master file again"); - return r; + LOG_FUNC_RETURN(p15card->card->ctx, r); + +err: + sc_pkcs15_card_clear(p15card); + LOG_FUNC_RETURN(p15card->card->ctx, r); } -int sc_pkcs15emu_itacns_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid, - sc_pkcs15emu_opt_t *opts) +int sc_pkcs15emu_itacns_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid) { sc_card_t *card = p15card->card; - SC_FUNC_CALLED(card->ctx, 1); + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); /* Check card */ - if (!(opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)) { - if (! ( - (card->type > SC_CARD_TYPE_ITACNS_BASE && - card->type < SC_CARD_TYPE_ITACNS_BASE + 1000) - || card->type == SC_CARD_TYPE_CARDOS_CIE_V1) - ) - return SC_ERROR_WRONG_CARD; - } + if (! ( + (card->type > SC_CARD_TYPE_ITACNS_BASE && + card->type < SC_CARD_TYPE_ITACNS_BASE + 1000) + || card->type == SC_CARD_TYPE_CARDOS_CIE_V1) + ) + return SC_ERROR_WRONG_CARD; /* Init card */ return itacns_init(p15card); diff --git a/src/libopensc/pkcs15-jpki.c b/src/libopensc/pkcs15-jpki.c index 1a43320cef..05e1cb330c 100644 --- a/src/libopensc/pkcs15-jpki.c +++ b/src/libopensc/pkcs15-jpki.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -33,9 +33,6 @@ #include "pkcs15.h" #include "jpki.h" -int sc_pkcs15emu_jpki_init_ex(sc_pkcs15_card_t *, struct sc_aid *, - sc_pkcs15emu_opt_t *); - static int sc_pkcs15emu_jpki_init(sc_pkcs15_card_t * p15card) { @@ -45,10 +42,10 @@ sc_pkcs15emu_jpki_init(sc_pkcs15_card_t * p15card) LOG_FUNC_CALLED(p15card->card->ctx); - p15card->tokeninfo->label = strdup("JPKI"); - p15card->tokeninfo->manufacturer_id = strdup("JPKI"); + set_string(&p15card->tokeninfo->label, "JPKI"); + set_string(&p15card->tokeninfo->manufacturer_id, "JPKI"); /* set dummy until we found serial number */ - p15card->tokeninfo->serial_number = strdup("00000000"); + set_string(&p15card->tokeninfo->serial_number, "00000000"); /* Select application directory */ if (drvdata->selected != SELECT_JPKI_AP) { @@ -93,8 +90,10 @@ sc_pkcs15emu_jpki_init(sc_pkcs15_card_t * p15card) cert_info.authority = jpki_cert_authority[i]; cert_obj.flags = jpki_cert_flags[i]; rc = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); - if (rc < 0) + if (rc < 0) { + sc_pkcs15_card_clear(p15card); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } } @@ -143,14 +142,17 @@ sc_pkcs15emu_jpki_init(sc_pkcs15_card_t * p15card) pin_cmd_data.cmd = SC_PIN_CMD_GET_INFO; pin_cmd_data.pin_type = SC_AC_CHV; pin_cmd_data.pin_reference = jpki_pin_ref[i]; - rc = sc_pin_cmd(card, &pin_cmd_data, &pin_info.tries_left); + rc = sc_pin_cmd(card, &pin_cmd_data); + pin_info.tries_left = pin_cmd_data.pin1.tries_left; LOG_TEST_RET(card->ctx, rc, "sc_pin_cmd failed"); strlcpy(pin_obj.label, jpki_pin_names[i], sizeof(pin_obj.label)); pin_obj.flags = jpki_pin_flags[i]; rc = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); - if (rc < 0) + if (rc < 0) { + sc_pkcs15_card_clear(p15card); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } } /* add private keys */ @@ -164,7 +166,7 @@ sc_pkcs15emu_jpki_init(sc_pkcs15_card_t * p15card) "User Authentication Key", "Digital Signature Key" }; - + static int prkey_user_consent[2] = { 0, 1 }; struct sc_pkcs15_prkey_info prkey_info; struct sc_pkcs15_object prkey_obj; @@ -181,27 +183,63 @@ sc_pkcs15emu_jpki_init(sc_pkcs15_card_t * p15card) strlcpy(prkey_obj.label, prkey_name[i], sizeof (prkey_obj.label)); prkey_obj.auth_id.len = 1; prkey_obj.auth_id.value[0] = prkey_pin[i]; - prkey_obj.user_consent = 0; + prkey_obj.user_consent = prkey_user_consent[i]; prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; rc = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); - if (rc < 0) + if (rc < 0) { + sc_pkcs15_card_clear(p15card); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + } + + /* add public keys */ + for (i = 0; i < 2; i++) { + static int pubkey_id[2] = { 1, 2 }; + static const char *jpki_pubkey_names[2] = { + "User Authentication Public Key", + "Digital Signature Public Key" + }; + static int jpki_pubkey_flags[2] = { + 0, + SC_PKCS15_CO_FLAG_PRIVATE}; + static int jpki_pubkey_auth_id[2] = {0, 2}; + struct sc_pkcs15_pubkey_info pubkey_info; + struct sc_pkcs15_object pubkey_obj; + static char const *jpki_pubkey_paths[2] = { + "000A", + "0001" + }; + + memset(&pubkey_info, 0, sizeof (pubkey_info)); + memset(&pubkey_obj, 0, sizeof (pubkey_obj)); + + strlcpy(pubkey_obj.label, jpki_pubkey_names[i], sizeof (pubkey_obj.label)); + pubkey_info.id.len = 1; + pubkey_info.id.value[0] = pubkey_id[i]; + pubkey_info.native = 1; + pubkey_info.key_reference = i + 1; + + sc_format_path(jpki_pubkey_paths[i], &pubkey_info.path); + pubkey_info.path.type = SC_PATH_TYPE_FILE_ID; + pubkey_obj.flags = jpki_pubkey_flags[i]; + pubkey_obj.auth_id.len = 1; + pubkey_obj.auth_id.value[0] = jpki_pubkey_auth_id[i]; + + rc = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); + if (rc < 0) { + sc_pkcs15_card_clear(p15card); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } int sc_pkcs15emu_jpki_init_ex(sc_pkcs15_card_t * p15card, - struct sc_aid *aid, sc_pkcs15emu_opt_t * opts) + struct sc_aid *aid) { - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) { - return sc_pkcs15emu_jpki_init(p15card); - } - else { - if (p15card->card->type != SC_CARD_TYPE_JPKI_BASE) - return SC_ERROR_WRONG_CARD; - - return sc_pkcs15emu_jpki_init(p15card); - } + if (p15card->card->type != SC_CARD_TYPE_JPKI_BASE) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_jpki_init(p15card); } diff --git a/src/libopensc/pkcs15-lteid.c b/src/libopensc/pkcs15-lteid.c new file mode 100644 index 0000000000..87ec8e17a9 --- /dev/null +++ b/src/libopensc/pkcs15-lteid.c @@ -0,0 +1,76 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "internal.h" +#include "pkcs15.h" + +static int +lteid_parse_df(struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_pkcs15_object *pkobjs[32]; + int rv, count; + + LOG_FUNC_CALLED(ctx); + + if (!df) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + if (df->enumerated) + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + + rv = sc_pkcs15_parse_df(p15card, df); + LOG_TEST_RET(ctx, rv, "DF parse error"); + + if (df->type != SC_PKCS15_PRKDF) + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + + rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, pkobjs, sizeof(pkobjs) / sizeof(pkobjs[0])); + LOG_TEST_RET(ctx, rv, "Cannot get PRKEY objects list"); + + count = rv; + for (int i = 0; i < count; i++) { + struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *)pkobjs[i]->data; + prkey_info->field_length = 384; + } + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + +static int +sc_pkcs15emu_lteid_init(struct sc_pkcs15_card *p15card, struct sc_aid *aid) +{ + LOG_FUNC_CALLED(p15card->card->ctx); + + int rv = sc_pkcs15_bind_internal(p15card, aid); + p15card->ops.parse_df = lteid_parse_df; + + LOG_FUNC_RETURN(p15card->card->ctx, rv); +} + +int +sc_pkcs15emu_lteid_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid) +{ + if (p15card->card->type == SC_CARD_TYPE_LTEID) { + return sc_pkcs15emu_lteid_init(p15card, aid); + } + + return SC_ERROR_WRONG_CARD; +} \ No newline at end of file diff --git a/src/libopensc/pkcs15-nqApplet.c b/src/libopensc/pkcs15-nqApplet.c new file mode 100644 index 0000000000..c97d7666ea --- /dev/null +++ b/src/libopensc/pkcs15-nqApplet.c @@ -0,0 +1,231 @@ +/* + * PKCS15 emulation for JCOP4 Cards with NQ-Applet + * + * Copyright (C) 2021 jozsefd + * + * 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 + */ + +#include +#include + +#include "internal.h" +#include "opensc.h" +#include "cards.h" +#include "common/compat_strlcpy.h" +#include "log.h" +#include "pkcs15.h" + +static const char name_Card[] = "NQ-Applet"; +static const char name_Vendor[] = "NXP"; + +static int get_nqapplet_certificate(sc_card_t *card, u8 data_id, struct sc_pkcs15_der *cert_info) +{ + int rv; + u8 buffer[3072]; + size_t cb_buffer = sizeof(buffer); + LOG_FUNC_CALLED(card->ctx); + + rv = sc_get_data(card, data_id, buffer, cb_buffer); + LOG_TEST_RET(card->ctx, rv, "GET DATA failed"); + if (rv == 0) { + LOG_TEST_RET(card->ctx, SC_ERROR_FILE_NOT_FOUND, "No certificate data returned"); + } + + if (cert_info != NULL) { + free(cert_info->value); + cert_info->value = malloc(rv); + if (cert_info->value != NULL) { + cert_info->len = rv; + memcpy(cert_info->value, buffer, rv); + } + } + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int add_nqapplet_pin(sc_pkcs15_card_t *p15card, const char *id, u8 reference) +{ + int rv; + struct sc_pkcs15_auth_info pin_info; + struct sc_pkcs15_object pin_obj; + sc_card_t *card = p15card->card; + + LOG_FUNC_CALLED(card->ctx); + + memset(&pin_info, 0, sizeof(pin_info)); + memset(&pin_obj, 0, sizeof(pin_obj)); + + sc_pkcs15_format_id(id, &pin_info.auth_id); + pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; + pin_info.attrs.pin.reference = reference; + pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | + SC_PKCS15_PIN_TYPE_FLAGS_PIN_GLOBAL | SC_PKCS15_PIN_AUTH_TYPE_PIN; + pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_UTF8; + pin_info.attrs.pin.min_length = 6; + pin_info.attrs.pin.stored_length = 6; + pin_info.attrs.pin.max_length = 6; + pin_info.attrs.pin.pad_char = '\0'; + pin_info.tries_left = -1; // TODO + pin_info.max_tries = 3; + + strlcpy(pin_obj.label, "UserPIN", sizeof(pin_obj.label)); + pin_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE; + + rv = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); + LOG_TEST_RET(card->ctx, rv, "sc_pkcs15emu_add_pin_obj failed"); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int add_nqapplet_certificate(sc_pkcs15_card_t *p15card, const char *id, const char *name, u8 data_id) +{ + int rv; + struct sc_pkcs15_cert_info cert_info; + struct sc_pkcs15_object cert_obj; + sc_card_t *card = p15card->card; + + LOG_FUNC_CALLED(card->ctx); + + memset(&cert_info, 0, sizeof(cert_info)); + memset(&cert_obj, 0, sizeof(cert_obj)); + + sc_pkcs15_format_id(id, &cert_info.id); + rv = get_nqapplet_certificate(card, data_id, &cert_info.value); + LOG_TEST_RET(card->ctx, rv, "Failed to get certificate"); + + strlcpy(cert_obj.label, name, sizeof(cert_obj.label)); + + rv = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); + LOG_TEST_RET(card->ctx, rv, "sc_pkcs15emu_add_x509_cert failed"); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int add_nqapplet_private_key(sc_pkcs15_card_t *p15card, const char *id, int reference, + const char *name, const char *pin_id, unsigned int usage) +{ + int rv; + struct sc_pkcs15_prkey_info prkey_info; + struct sc_pkcs15_object prkey_obj; + sc_card_t *card = p15card->card; + + LOG_FUNC_CALLED(card->ctx); + + memset(&prkey_info, 0, sizeof(prkey_info)); + memset(&prkey_obj, 0, sizeof(prkey_obj)); + + sc_pkcs15_format_id(id, &prkey_info.id); + prkey_info.usage = usage; + prkey_info.native = 1; + prkey_info.key_reference = reference; + prkey_info.modulus_length = 3072; + + strlcpy(prkey_obj.label, name, sizeof(prkey_obj.label)); + prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; + sc_pkcs15_format_id(pin_id, &prkey_obj.auth_id); + + rv = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); + LOG_TEST_RET(card->ctx, rv, "sc_pkcs15emu_add_rsa_prkey failed"); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int add_nqapplet_objects(sc_pkcs15_card_t *p15card) +{ + int rv; + sc_card_t *card = p15card->card; + + LOG_FUNC_CALLED(card->ctx); + + // 1) User PIN + rv = add_nqapplet_pin(p15card, "1", 0x01); + LOG_TEST_RET(card->ctx, rv, "Failed to add PIN 1"); + + // 2.1) C.CH.Auth + rv = add_nqapplet_certificate(p15card, "1", "C.CH.Auth", 0x00); + LOG_TEST_RET(card->ctx, rv, "Failed to add Auth. certificate"); + + // 2.2) PrK.CH.Auth + rv = add_nqapplet_private_key(p15card, "1", 0x01, "PrK.CH.Auth", "1", + SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_DECRYPT); + LOG_TEST_RET(card->ctx, rv, "Failed to add Auth. private key"); + + // 3.1) C.CH.Encr + rv = add_nqapplet_certificate(p15card, "2", "C.CH.Encr", 0x01); + if (rv == SC_SUCCESS) { + // 3.2) PrK.CH.Encr + rv = add_nqapplet_private_key(p15card, "2", 0x02, "PrK.CH.Encr", "1", SC_PKCS15_PRKEY_USAGE_DECRYPT); + LOG_TEST_RET(card->ctx, rv, "Failed to add Encr. private key"); + } else { + sc_log(p15card->card->ctx, "The card has no C.CH.Encr, ignoring CH.Encr container"); + } + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +int sc_pkcs15emu_nqapplet_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid) +{ + int rv = SC_ERROR_WRONG_CARD; + sc_context_t *ctx; + sc_card_t *card; + + if (!p15card || !p15card->card || !p15card->card->ctx) { + return SC_ERROR_INVALID_ARGUMENTS; + } + + card = p15card->card; + ctx = card->ctx; + + SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + + if (card->type < SC_CARD_TYPE_NQ_APPLET || card->type > SC_CARD_TYPE_NQ_APPLET_RFID) { + sc_log(p15card->card->ctx, "Unsupported card type: %d", card->type); + return SC_ERROR_WRONG_CARD; + } + + rv = add_nqapplet_objects(p15card); + LOG_TEST_GOTO_ERR(ctx, rv, "Failed to add PKCS15"); + + if (aid != NULL) { + struct sc_file *file = sc_file_new(); + if (file != NULL) { + /* PKCS11 depends on the file_app object, provide MF */ + sc_format_path("3f00", &file->path); + sc_file_free(p15card->file_app); + p15card->file_app = file; + } + } + + sc_pkcs15_free_tokeninfo(p15card->tokeninfo); + + p15card->tokeninfo = sc_pkcs15_tokeninfo_new(); + if (p15card->tokeninfo == NULL) { + rv = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, rv, "unable to create tokeninfo struct"); + } else { + char serial_hex[SC_MAX_SERIALNR * 2 + 2]; + + sc_bin_to_hex(card->serialnr.value, card->serialnr.len, serial_hex, sizeof(serial_hex), 0); + set_string(&p15card->tokeninfo->serial_number, serial_hex); + set_string(&p15card->tokeninfo->label, name_Card); + set_string(&p15card->tokeninfo->manufacturer_id, name_Vendor); + p15card->tokeninfo->flags = SC_PKCS15_TOKEN_READONLY; + } + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +err: + sc_pkcs15_card_clear(p15card); + LOG_FUNC_RETURN(ctx, rv); +} diff --git a/src/libopensc/pkcs15-oberthur.c b/src/libopensc/pkcs15-oberthur.c index 1426ba80d0..561e302bbe 100644 --- a/src/libopensc/pkcs15-oberthur.c +++ b/src/libopensc/pkcs15-oberthur.c @@ -19,7 +19,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H @@ -29,6 +29,7 @@ #include #include #include +#include "../common/compat_strlcpy.h" #include "pkcs15.h" #include "log.h" @@ -37,7 +38,6 @@ #ifdef ENABLE_OPENSSL #include -#include #include #include #endif @@ -91,8 +91,6 @@ static int sc_pkcs15emu_oberthur_add_pubkey(struct sc_pkcs15_card *, unsigned, u static int sc_pkcs15emu_oberthur_add_cert(struct sc_pkcs15_card *, unsigned); static int sc_pkcs15emu_oberthur_add_data(struct sc_pkcs15_card *, unsigned, unsigned, int); -int sc_pkcs15emu_oberthur_init_ex(struct sc_pkcs15_card *, struct sc_aid *, struct sc_pkcs15emu_opt *); - static int sc_oberthur_parse_tokeninfo (struct sc_pkcs15_card *, unsigned char *, size_t, int); static int sc_oberthur_parse_containers (struct sc_pkcs15_card *, unsigned char *, size_t, int); static int sc_oberthur_parse_publicinfo (struct sc_pkcs15_card *, unsigned char *, size_t, int); @@ -186,7 +184,7 @@ sc_oberthur_get_friends (unsigned int id, struct crypto_container *ccont) static int -sc_oberthur_get_certificate_authority(struct sc_pkcs15_der *der, int *out_authority) +sc_oberthur_get_certificate_authority(sc_context_t *ctx, struct sc_pkcs15_der *der, int *out_authority) { #ifdef ENABLE_OPENSSL X509 *x; @@ -205,14 +203,20 @@ sc_oberthur_get_certificate_authority(struct sc_pkcs15_der *der, int *out_author buf_mem.max = buf_mem.length = der->len; bio = BIO_new(BIO_s_mem()); - if(!bio) + if (!bio) { + free(buf_mem.data); + sc_log_openssl(ctx); return SC_ERROR_OUT_OF_MEMORY; + } BIO_set_mem_buf(bio, &buf_mem, BIO_NOCLOSE); x = d2i_X509_bio(bio, 0); + free(buf_mem.data); BIO_free(bio); - if (!x) + if (!x) { + sc_log_openssl(ctx); return SC_ERROR_INVALID_DATA; + } bs = (BASIC_CONSTRAINTS *)X509_get_ext_d2i(x, NID_basic_constraints, NULL, NULL); if (out_authority) @@ -250,27 +254,36 @@ sc_oberthur_read_file(struct sc_pkcs15_card *p15card, const char *in_path, sc_format_path(in_path, &path); rv = sc_select_file(card, &path, &file); - LOG_TEST_RET(ctx, rv, "Cannot select oberthur file to read"); + if (rv != SC_SUCCESS) { + sc_file_free(file); + LOG_TEST_RET(ctx, rv, "Cannot select oberthur file to read"); + } if (file->ef_structure == SC_FILE_EF_TRANSPARENT) sz = file->size; else sz = (file->record_length + 2) * file->record_count; - *out = calloc(sz, 1); - if (*out == NULL) + *out = calloc(1, sz); + if (*out == NULL) { + sc_file_free(file); LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot read oberthur file"); + } if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { rv = sc_read_binary(card, 0, *out, sz, 0); } else { - int rec; - int offs = 0; - int rec_len = file->record_length; + unsigned int rec; + size_t offs = 0; + size_t rec_len = file->record_length; for (rec = 1; ; rec++) { - rv = sc_read_record(card, rec, *out + offs + 2, rec_len, SC_RECORD_BY_REC_NR); + if (rec > file->record_count) { + rv = 0; + break; + } + rv = sc_read_record(card, rec, 0, *out + offs + 2, rec_len, SC_RECORD_BY_REC_NR); if (rv == SC_ERROR_RECORD_NOT_FOUND) { rv = 0; break; @@ -294,15 +307,28 @@ sc_oberthur_read_file(struct sc_pkcs15_card *p15card, const char *in_path, if (verify_pin && rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { struct sc_pkcs15_object *objs[0x10], *pin_obj = NULL; const struct sc_acl_entry *acl = sc_file_get_acl_entry(file, SC_AC_OP_READ); - int ii; + int ii, nobjs; - rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 0x10); - LOG_TEST_RET(ctx, rv, "Cannot read oberthur file: get AUTH objects error"); + if (acl == NULL) { + sc_file_free(file); + free(*out); + *out = NULL; + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + } + + nobjs = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 0x10); + if (nobjs < 1) { + sc_file_free(file); + free(*out); + *out = NULL; + LOG_TEST_RET(ctx, SC_ERROR_DATA_OBJECT_NOT_FOUND, + "Cannot read oberthur file: get AUTH objects error"); + } - for (ii=0; iidata; sc_log(ctx, "compare PIN/ACL refs:%i/%i, method:%i/%i", - auth_info->attrs.pin.reference, acl->key_ref, auth_info->auth_method, acl->method); + auth_info->attrs.pin.reference, acl->key_ref, auth_info->auth_method, acl->method); if (auth_info->attrs.pin.reference == (int)acl->key_ref && auth_info->auth_method == (unsigned)acl->method) { pin_obj = objs[ii]; break; @@ -317,7 +343,7 @@ sc_oberthur_read_file(struct sc_pkcs15_card *p15card, const char *in_path, if (!rv) rv = sc_oberthur_read_file(p15card, in_path, out, out_len, 0); } - }; + } sc_file_free(file); @@ -356,8 +382,8 @@ sc_oberthur_parse_tokeninfo (struct sc_pkcs15_card *p15card, flags = *(buff + 0x22) * 0x100 + *(buff + 0x23); - p15card->tokeninfo->label = strdup(label); - p15card->tokeninfo->manufacturer_id = strdup("Oberthur/OpenSC"); + set_string(&p15card->tokeninfo->label, label); + set_string(&p15card->tokeninfo->manufacturer_id, "Oberthur/OpenSC"); if (flags & 0x01) p15card->tokeninfo->flags |= SC_PKCS15_TOKEN_PRN_GENERATION; @@ -385,7 +411,7 @@ sc_oberthur_parse_containers (struct sc_pkcs15_card *p15card, Containers = next; } - for (offs=0; offs < len;) { + for (offs=0; offs + 2 + 2+2+2 + 2+2+2 + 2+36 <= len;) { struct container *cont; unsigned char *ptr = buff + offs + 2; @@ -395,7 +421,7 @@ sc_oberthur_parse_containers (struct sc_pkcs15_card *p15card, if (*(buff + offs) != 'R') return SC_ERROR_INVALID_DATA; - cont = (struct container *)calloc(sizeof(struct container), 1); + cont = (struct container *)calloc(1, sizeof(struct container)); if (!cont) return SC_ERROR_OUT_OF_MEMORY; @@ -436,7 +462,7 @@ sc_oberthur_parse_publicinfo (struct sc_pkcs15_card *p15card, int rv; LOG_FUNC_CALLED(ctx); - for (ii=0; ii info_len) + if (offs > info_len) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add public key: no 'tag'"); + } flags = *(info_blob + 0) * 0x100 + *(info_blob + 1); key_info.usage = sc_oberthur_decode_usage(flags); if (flags & OBERTHUR_ATTR_MODIFIABLE) @@ -578,10 +606,15 @@ sc_pkcs15emu_oberthur_add_pubkey(struct sc_pkcs15_card *p15card, sc_log(ctx, "Public key key-usage:%04X", key_info.usage); /* Label */ - if (offs + 2 > info_len) + if (offs + 2 > info_len) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add public key: no 'Label'"); + } len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; - if (len) { + if (offs + 2 + len > info_len) { + free(info_blob); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Failed to add public key: invalid 'Label' length"); + } else if (len) { if (len > sizeof(key_obj.label) - 1) len = sizeof(key_obj.label) - 1; memcpy(key_obj.label, info_blob + offs + 2, len); @@ -589,14 +622,22 @@ sc_pkcs15emu_oberthur_add_pubkey(struct sc_pkcs15_card *p15card, offs += 2 + len; /* ID */ - if (offs > info_len) + if (offs + 2 > info_len) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add public key: no 'ID'"); + } len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; - if (!len || len > sizeof(key_info.id.value)) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Failed to add public key: invalie 'ID' length"); + if (len == 0 + || len > sizeof(key_info.id.value) + || offs + 2 + len > info_len) { + free(info_blob); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Failed to add public key: invalid 'ID' length"); + } memcpy(key_info.id.value, info_blob + offs + 2, len); key_info.id.len = len; + free(info_blob); + /* Ignore Start/End dates */ snprintf(ch_tmp, sizeof(ch_tmp), "%s%04X", AWP_OBJECTS_DF_PUB, file_id); @@ -626,7 +667,7 @@ sc_pkcs15emu_oberthur_add_cert(struct sc_pkcs15_card *p15card, unsigned int file struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_cert_info cinfo; struct sc_pkcs15_object cobj; - unsigned char *info_blob, *cert_blob; + unsigned char *info_blob = NULL, *cert_blob = NULL; size_t info_len, cert_len, len, offs; unsigned flags; int rv; @@ -642,16 +683,23 @@ sc_pkcs15emu_oberthur_add_cert(struct sc_pkcs15_card *p15card, unsigned int file rv = sc_oberthur_read_file(p15card, ch_tmp, &info_blob, &info_len, 1); LOG_TEST_RET(ctx, rv, "Failed to add certificate: read oberthur file error"); - if (info_len < 2) + if (info_len < 2) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add certificate: no 'tag'"); + } flags = *(info_blob + 0) * 0x100 + *(info_blob + 1); offs = 2; /* Label */ - if (offs + 2 > info_len) + if (offs + 2 > info_len) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add certificate: no 'CN'"); + } len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; - if (len) { + if (len + offs + 2 > info_len) { + free(info_blob); + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Invalid 'CN' length"); + } else if (len) { if (len > sizeof(cobj.label) - 1) len = sizeof(cobj.label) - 1; memcpy(cobj.label, info_blob + offs + 2, len); @@ -659,14 +707,23 @@ sc_pkcs15emu_oberthur_add_cert(struct sc_pkcs15_card *p15card, unsigned int file offs += 2 + len; /* ID */ - if (offs > info_len) + if (offs + 2 > info_len) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add certificate: no 'ID'"); + } len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; - if (len > sizeof(cinfo.id.value)) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Failed to add certificate: invalie 'ID' length"); + if (len + offs + 2 > info_len) { + free(info_blob); + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Invalid 'ID' length"); + } else if (len > sizeof(cinfo.id.value)) { + free(info_blob); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Failed to add certificate: invalid 'ID' length"); + } memcpy(cinfo.id.value, info_blob + offs + 2, len); cinfo.id.len = len; + free(info_blob); + /* Ignore subject, issuer and serial */ snprintf(ch_tmp, sizeof(ch_tmp), "%s%04X", AWP_OBJECTS_DF_PUB, file_id); @@ -677,15 +734,18 @@ sc_pkcs15emu_oberthur_add_cert(struct sc_pkcs15_card *p15card, unsigned int file cinfo.value.value = cert_blob; cinfo.value.len = cert_len; - rv = sc_oberthur_get_certificate_authority(&cinfo.value, &cinfo.authority); - LOG_TEST_RET(ctx, rv, "Failed to add certificate: get certificate attributes error"); + rv = sc_oberthur_get_certificate_authority(ctx, &cinfo.value, &cinfo.authority); + if (rv != SC_SUCCESS) { + free(cinfo.value.value); + LOG_TEST_RET(ctx, rv, "Failed to add certificate: get certificate attributes error"); + } if (flags & OBERTHUR_ATTR_MODIFIABLE) cobj.flags |= SC_PKCS15_CO_FLAG_MODIFIABLE; rv = sc_pkcs15emu_add_x509_cert(p15card, &cobj, &cinfo); - SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(p15card->card->ctx, rv); } @@ -738,7 +798,7 @@ sc_pkcs15emu_oberthur_add_prvkey(struct sc_pkcs15_card *p15card, unsigned int id = path.value[path.len - 2] * 0x100 + path.value[path.len - 1]; if (id == ccont.id_cert) { - strncpy(kobj.label, objs[ii]->label, sizeof(kobj.label) - 1); + strlcpy(kobj.label, objs[ii]->label, sizeof(kobj.label)); break; } } @@ -751,15 +811,23 @@ sc_pkcs15emu_oberthur_add_prvkey(struct sc_pkcs15_card *p15card, rv = sc_oberthur_read_file(p15card, ch_tmp, &info_blob, &info_len, 1); LOG_TEST_RET(ctx, rv, "Failed to add private key: read oberthur file error"); - if (info_len < 2) + if (info_len < 2) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add private key: no 'tag'"); + } flags = *(info_blob + 0) * 0x100 + *(info_blob + 1); offs = 2; /* CN */ - if (offs > info_len) + if (offs + 2 > info_len) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add private key: no 'CN'"); + } len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; + if (len + offs + 2 > info_len) { + free(info_blob); + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Invalid 'CN' length"); + } if (len && !strlen(kobj.label)) { if (len > sizeof(kobj.label) - 1) len = sizeof(kobj.label) - 1; @@ -768,13 +836,21 @@ sc_pkcs15emu_oberthur_add_prvkey(struct sc_pkcs15_card *p15card, offs += 2 + len; /* ID */ - if (offs > info_len) + if (offs + 2 > info_len) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add private key: no 'ID'"); + } len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; - if (!len) + if (!len) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add private key: zero length ID"); - else if (len > sizeof(kinfo.id.value)) + } else if (len + offs + 2 > info_len) { + free(info_blob); + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Invalid 'ID' length"); + } else if (len > sizeof(kinfo.id.value)) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Failed to add private key: invalid ID length"); + } memcpy(kinfo.id.value, info_blob + offs + 2, len); kinfo.id.len = len; offs += 2 + len; @@ -783,19 +859,28 @@ sc_pkcs15emu_oberthur_add_prvkey(struct sc_pkcs15_card *p15card, offs += 16; /* Subject encoded in ASN1 */ - if (offs > info_len) - return SC_ERROR_UNKNOWN_DATA_RECEIVED; + if (offs + 2 > info_len) { + free(info_blob); + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add private key: no 'subject'"); + } len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; - if (len) { + if (len + offs + 2 > info_len) { + free(info_blob); + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Invalid 'subject' length"); + } else if (len) { kinfo.subject.value = malloc(len); - if (!kinfo.subject.value) + if (!kinfo.subject.value) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Failed to add private key: memory allocation error"); + } kinfo.subject.len = len; memcpy(kinfo.subject.value, info_blob + offs + 2, len); } /* Modulus and exponent are ignored */ + free(info_blob); + snprintf(ch_tmp, sizeof(ch_tmp), "%s%04X", AWP_OBJECTS_DF_PRV, file_id); sc_format_path(ch_tmp, &kinfo.path); sc_log(ctx, "Private key info path %s", ch_tmp); @@ -843,37 +928,59 @@ sc_pkcs15emu_oberthur_add_data(struct sc_pkcs15_card *p15card, rv = sc_oberthur_read_file(p15card, ch_tmp, &info_blob, &info_len, 1); LOG_TEST_RET(ctx, rv, "Failed to add data: read oberthur file error"); - if (info_len < 2) + if (info_len < 2) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add certificate: no 'tag'"); + } flags = *(info_blob + 0) * 0x100 + *(info_blob + 1); offs = 2; /* Label */ - if (offs > info_len) + if (offs + 2 > info_len) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add data: no 'label'"); + } label = info_blob + offs + 2; label_len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; + if (offs + 2 + label_len > info_len) { + free(info_blob); + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Invalid length of 'label' received"); + } if (label_len > sizeof(dobj.label) - 1) label_len = sizeof(dobj.label) - 1; offs += 2 + *(info_blob + offs + 1); /* Application */ - if (offs > info_len) + if (offs + 2 > info_len) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add data: no 'application'"); + } app = info_blob + offs + 2; app_len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; + if (offs + 2 + app_len > info_len) { + free(info_blob); + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Invalid length of 'application' received"); + } if (app_len > sizeof(dinfo.app_label) - 1) app_len = sizeof(dinfo.app_label) - 1; offs += 2 + app_len; /* OID encode like DER(ASN.1(oid)) */ - if (offs > info_len) + if (offs + 2 > info_len) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add data: no 'OID'"); + } oid_len = *(info_blob + offs + 1) + *(info_blob + offs) * 0x100; - if (oid_len) { + if (offs + 2 + oid_len > info_len) { + free(info_blob); + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Invalid length of 'oid' received"); + } + if (oid_len > 2) { oid = info_blob + offs + 2; - if (*oid != 0x06 || (*(oid + 1) != oid_len - 2)) + if (*oid != 0x06 || (*(oid + 1) != oid_len - 2)) { + free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add data: invalid 'OID' format"); + } oid += 2; oid_len -= 2; } @@ -900,7 +1007,8 @@ sc_pkcs15emu_oberthur_add_data(struct sc_pkcs15_card *p15card, rv = sc_pkcs15emu_add_data_object(p15card, &dobj, &dinfo); - SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, rv); + free(info_blob); + LOG_FUNC_RETURN(p15card->card->ctx, rv); } @@ -918,7 +1026,7 @@ sc_pkcs15emu_oberthur_init(struct sc_pkcs15_card * p15card) SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_bin_to_hex(card->serialnr.value, card->serialnr.len, serial, sizeof(serial), 0); - p15card->tokeninfo->serial_number = strdup(serial); + set_string(&p15card->tokeninfo->serial_number, serial); p15card->ops.parse_df = sc_awp_parse_df; p15card->ops.clear = sc_awp_clear; @@ -927,7 +1035,7 @@ sc_pkcs15emu_oberthur_init(struct sc_pkcs15_card * p15card) sc_format_path(AWP_PIN_DF, &path); rv = sc_select_file(card, &path, NULL); - LOG_TEST_RET(ctx, rv, "Oberthur init failed: cannot select PIN dir"); + LOG_TEST_GOTO_ERR(ctx, rv, "Oberthur init failed: cannot select PIN dir"); tries_left = -1; rv = sc_verify(card, SC_AC_CHV, sopin_reference, (unsigned char *)"", 0, &tries_left); @@ -936,7 +1044,7 @@ sc_pkcs15emu_oberthur_init(struct sc_pkcs15_card * p15card) rv = sc_verify(card, SC_AC_CHV, sopin_reference, (unsigned char *)"", 0, &tries_left); } if (rv && rv != SC_ERROR_PIN_CODE_INCORRECT) - LOG_TEST_RET(ctx, rv, "Invalid state of SO-PIN"); + LOG_TEST_GOTO_ERR(ctx, rv, "Invalid state of SO-PIN"); /* add PIN */ memset(&auth_info, 0, sizeof(auth_info)); @@ -965,7 +1073,7 @@ sc_pkcs15emu_oberthur_init(struct sc_pkcs15_card * p15card) sc_log(ctx, "Add PIN(%s,auth_id:%s,reference:%i)", obj.label, sc_pkcs15_print_id(&auth_info.auth_id), auth_info.attrs.pin.reference); rv = sc_pkcs15emu_add_pin_obj(p15card, &obj, &auth_info); - LOG_TEST_RET(ctx, rv, "Oberthur init failed: cannot add PIN object"); + LOG_TEST_GOTO_ERR(ctx, rv, "Oberthur init failed: cannot add PIN object"); tries_left = -1; rv = sc_verify(card, SC_AC_CHV, 0x81, (unsigned char *)"", 0, &tries_left); @@ -1009,27 +1117,32 @@ sc_pkcs15emu_oberthur_init(struct sc_pkcs15_card * p15card) sc_log(ctx, "Add PIN(%s,auth_id:%s,reference:%i)", obj.label, sc_pkcs15_print_id(&auth_info.auth_id), auth_info.attrs.pin.reference); rv = sc_pkcs15emu_add_pin_obj(p15card, &obj, &auth_info); - LOG_TEST_RET(ctx, rv, "Oberthur init failed: cannot add PIN object"); + LOG_TEST_GOTO_ERR(ctx, rv, "Oberthur init failed: cannot add PIN object"); } else if (rv != SC_ERROR_DATA_OBJECT_NOT_FOUND) { - LOG_TEST_RET(ctx, rv, "Oberthur init failed: cannot verify PIN"); + LOG_TEST_GOTO_ERR(ctx, rv, "Oberthur init failed: cannot verify PIN"); } for (ii=0; oberthur_infos[ii].name; ii++) { sc_log(ctx, "Oberthur init: read %s file", oberthur_infos[ii].name); + free(oberthur_infos[ii].content); rv = sc_oberthur_read_file(p15card, oberthur_infos[ii].path, &oberthur_infos[ii].content, &oberthur_infos[ii].len, 1); - LOG_TEST_RET(ctx, rv, "Oberthur init failed: read oberthur file error"); + LOG_TEST_GOTO_ERR(ctx, rv, "Oberthur init failed: read oberthur file error"); sc_log(ctx, "Oberthur init: parse %s file, content length %"SC_FORMAT_LEN_SIZE_T"u", oberthur_infos[ii].name, oberthur_infos[ii].len); rv = oberthur_infos[ii].parser(p15card, oberthur_infos[ii].content, oberthur_infos[ii].len, oberthur_infos[ii].postpone_allowed); - LOG_TEST_RET(ctx, rv, "Oberthur init failed: parse error"); + LOG_TEST_GOTO_ERR(ctx, rv, "Oberthur init failed: parse error"); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); + +err: + sc_pkcs15_card_clear(p15card); + LOG_FUNC_RETURN(ctx, rv); } @@ -1040,26 +1153,20 @@ oberthur_detect_card(struct sc_pkcs15_card * p15card) SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (p15card->card->type != SC_CARD_TYPE_OBERTHUR_64K) - SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_WRONG_CARD); - SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_WRONG_CARD); + LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } int -sc_pkcs15emu_oberthur_init_ex(struct sc_pkcs15_card * p15card, struct sc_aid *aid, - struct sc_pkcs15emu_opt * opts) +sc_pkcs15emu_oberthur_init_ex(struct sc_pkcs15_card * p15card, struct sc_aid *aid) { int rv; LOG_FUNC_CALLED(p15card->card->ctx); - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) { + rv = oberthur_detect_card(p15card); + if (!rv) rv = sc_pkcs15emu_oberthur_init(p15card); - } - else { - rv = oberthur_detect_card(p15card); - if (!rv) - rv = sc_pkcs15emu_oberthur_init(p15card); - } LOG_FUNC_RETURN(p15card->card->ctx, rv); } @@ -1081,7 +1188,7 @@ sc_awp_parse_df(struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df) LOG_FUNC_RETURN(ctx, SC_SUCCESS); rv = sc_oberthur_read_file(p15card, AWP_OBJECTS_LIST_PRV, &buf, &buf_len, 1); - LOG_TEST_RET(ctx, rv, "Parse DF: read pribate objects info failed"); + LOG_TEST_RET(ctx, rv, "Parse DF: read private objects info failed"); rv = sc_oberthur_parse_privateinfo(p15card, buf, buf_len, 0); diff --git a/src/libopensc/pkcs15-openpgp.c b/src/libopensc/pkcs15-openpgp.c index 678dedf6c4..f6fa3248e9 100644 --- a/src/libopensc/pkcs15-openpgp.c +++ b/src/libopensc/pkcs15-openpgp.c @@ -16,10 +16,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -32,8 +32,8 @@ #include "internal.h" #include "pkcs15.h" #include "log.h" +#include "card-openpgp.h" -int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); static int sc_pkcs15emu_openpgp_add_data(sc_pkcs15_card_t *); @@ -45,6 +45,7 @@ static int sc_pkcs15emu_openpgp_add_data(sc_pkcs15_card_t *); | SC_PKCS15_PIN_FLAG_SO_PIN) #define PGP_NUM_PRIVDO 4 +#define PGP_MAX_NUM_CERTS 3 typedef struct _pgp_pin_cfg { const char *label; @@ -54,6 +55,7 @@ typedef struct _pgp_pin_cfg { int do_index; } pgp_pin_cfg_t; +// clang-format off /* OpenPGP cards v1: * "Signature PIN2 & "Encryption PIN" are two different PINs - not sync'ed by hardware */ @@ -70,7 +72,9 @@ static const pgp_pin_cfg_t pin_cfg_v2[3] = { { "User PIN (sig)", 0x01, PGP_USER_PIN_FLAGS, 6, 0 }, // used for PSO:CDS { "Admin PIN", 0x03, PGP_ADMIN_PIN_FLAGS, 8, 2 } }; +// clang-format on +static struct sc_object_id curve25519_oid = {{1, 3, 6, 1, 4, 1, 3029, 1, 5, 1, -1}}; #define PGP_SIG_PRKEY_USAGE (SC_PKCS15_PRKEY_USAGE_SIGN \ | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER \ @@ -93,42 +97,62 @@ typedef struct _pgp_key_cfg { int pubkey_usage; } pgp_key_cfg_t; +typedef struct cdata_st { + const char *label; + int authority; + const char *path; + const char *id; + int obj_flags; +} cdata; + +// clang-format off static const pgp_key_cfg_t key_cfg[3] = { { "Signature key", "B601", 1, PGP_SIG_PRKEY_USAGE, PGP_SIG_PUBKEY_USAGE }, { "Encryption key", "B801", 2, PGP_ENC_PRKEY_USAGE, PGP_ENC_PUBKEY_USAGE }, { "Authentication key", "A401", 2, PGP_AUTH_PRKEY_USAGE | PGP_ENC_PRKEY_USAGE, PGP_AUTH_PUBKEY_USAGE | PGP_ENC_PUBKEY_USAGE } }; +static const cdata certs[PGP_MAX_NUM_CERTS] = { + {"AUT certificate", 0, "3F007F21", "3", SC_PKCS15_CO_FLAG_MODIFIABLE}, + {"DEC certificate", 0, "3F007F21", "2", SC_PKCS15_CO_FLAG_MODIFIABLE}, + {"SIG certificate", 0, "3F007F21", "1", SC_PKCS15_CO_FLAG_MODIFIABLE} +}; +// clang-format on typedef struct _pgp_manuf_map { unsigned short id; const char *name; } pgp_manuf_map_t; +// clang-format off static const pgp_manuf_map_t manuf_map[] = { - { 0x0001, "PPC Card Systems" }, - { 0x0002, "Prism" }, - { 0x0003, "OpenFortress" }, - { 0x0004, "Wewid AB" }, - { 0x0005, "ZeitControl" }, - { 0x0006, "Yubico" }, - { 0x0007, "OpenKMS" }, - { 0x0008, "LogoEmail" }, - { 0x002A, "Magrathea" }, - { 0xF517, "FSIJ" }, - { 0x0000, "test card" }, - { 0xffff, "test card" }, + { 0x0001, "PPC Card Systems" }, + { 0x0002, "Prism" }, + { 0x0003, "OpenFortress" }, + { 0x0004, "Wewid AB" }, + { 0x0005, "ZeitControl" }, + { 0x0006, "Yubico" }, + { 0x0007, "OpenKMS" }, + { 0x0008, "LogoEmail" }, + { 0x0009, "Fidesmo" }, + { 0x000A, "Dangerous Things" }, + { 0x000B, "Feitian Technologies" }, + { 0x002A, "Magrathea" }, + { 0x0042, "GnuPG e.V." }, + { 0x1337, "Warsaw Hackerspace" }, + { 0x2342, "warpzone" }, + { 0x4354, "Confidential Technologies" }, + { 0x5443, "TIF-IT e.V." }, + { 0x63AF, "Trustica" }, + { 0xBA53, "c-base e.V." }, + { 0xBD0E, "Paranoidlabs" }, + { 0xF517, "FSIJ" }, + { 0xF5EC, "F-Secure" }, + { 0x0000, "test card" }, + { 0xffff, "test card" }, { 0, NULL } }; - - -static void -set_string(char **strp, const char *value) -{ - if (*strp) - free(*strp); - *strp = value? strdup(value) : NULL; -} +// clang-format on /* * This function pretty much follows what find_tlv in the GNUpg @@ -147,6 +171,9 @@ read_file(sc_card_t *card, const char *path_name, void *buf, size_t len) if (file->size < len) len = file->size; + + sc_file_free(file); + return sc_read_binary(card, 0, (u8 *) buf, len, 0); } @@ -155,14 +182,17 @@ sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card) { sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; + struct pgp_priv_data *priv = DRVDATA(card); char string[256]; u8 c4data[10]; - u8 c5data[70]; + u8 c5data[100]; int r, i; - const pgp_pin_cfg_t *pin_cfg = (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK) - ? pin_cfg_v2 : pin_cfg_v1; + const pgp_pin_cfg_t *pin_cfg = (card->type == SC_CARD_TYPE_OPENPGP_V1) + ? pin_cfg_v1 : pin_cfg_v2; sc_path_t path; - sc_file_t *file; + sc_file_t *file = NULL; + + LOG_FUNC_CALLED(ctx); set_string(&p15card->tokeninfo->label, "OpenPGP card"); set_string(&p15card->tokeninfo->manufacturer_id, "OpenPGP project"); @@ -172,7 +202,7 @@ sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card) unsigned short manuf_id = bebytes2ushort(card->serialnr.value); int j; - sc_bin_to_hex(card->serialnr.value, card->serialnr.len, string, sizeof(string)-1, 0); + sc_bin_to_hex(card->serialnr.value, card->serialnr.len, string, sizeof(string), 0); set_string(&p15card->tokeninfo->serial_number, string); for (j = 0; manuf_map[j].name != NULL; j++) { @@ -198,12 +228,13 @@ sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card) * 01-03: max length of pins 1-3 * 04-07: tries left for pins 1-3 */ + sc_log(ctx, "Reading PW status bytes"); if ((r = read_file(card, "006E:0073:00C4", c4data, sizeof(c4data))) < 0) goto failed; if (r != 7) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "CHV status bytes have unexpected length (expected 7, got %d)\n", r); - return SC_ERROR_OBJECT_NOT_VALID; + sc_log(ctx, "CHV status bytes have unexpected length (expected 7, got %d)\n", r); + r = SC_ERROR_OBJECT_NOT_VALID; + goto failed; } /* Add PIN codes */ @@ -237,8 +268,10 @@ sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card) } r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); - if (r < 0) - return SC_ERROR_INTERNAL; + if (r < 0) { + r = SC_ERROR_INTERNAL; + goto failed; + } } /* Get private key finger prints from DO 006E/0073/00C5: @@ -246,32 +279,33 @@ sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card) * 20-39: finger print for ENC key * 40-59: finger print for AUT key */ + sc_log(ctx, "Reading Fingerprints"); if ((r = read_file(card, "006E:0073:00C5", c5data, sizeof(c5data))) < 0) goto failed; - if (r != 60) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + if (r < 60) { + sc_log(ctx, "finger print bytes have unexpected length (expected 60, got %d)\n", r); - return SC_ERROR_OBJECT_NOT_VALID; + r = SC_ERROR_OBJECT_NOT_VALID; + goto failed; } - /* XXX: check if "halfkeys" can be stored with gpg2. If not, add keypairs in one loop */ + sc_log(ctx, "Adding private keys"); + /* XXX: check if "halfkeys" can be stored with gpg2. If not, add key pairs in one loop */ for (i = 0; i < 3; i++) { sc_pkcs15_prkey_info_t prkey_info; sc_pkcs15_object_t prkey_obj; - u8 cxdata[10]; + u8 cxdata[12]; + int cxdata_len = sizeof(cxdata); char path_template[] = "006E:0073:00Cx"; int j; memset(&prkey_info, 0, sizeof(prkey_info)); memset(&prkey_obj, 0, sizeof(prkey_obj)); + memset(&cxdata, 0, sizeof(cxdata)); path_template[13] = '1' + i; /* The needed tags are C1 C2 and C3 */ - if ((r = read_file(card, path_template, cxdata, sizeof(cxdata))) < 0) + if ((cxdata_len = read_file(card, path_template, cxdata, sizeof(cxdata))) < 1) goto failed; - if (r != 6) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Key info bytes have unexpected length (expected 6, got %d)\n", r); - return SC_ERROR_INTERNAL; - } /* check validity using finger prints */ for (j = 19; j >= 0; j--) { @@ -281,41 +315,132 @@ sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card) /* only add valid keys, i.e. those with a legal algorithm identifier & finger print */ if (j >= 0 && cxdata[0] != 0) { + struct sc_object_id oid = {0}; + struct sc_algorithm_info * algorithm_info; /* no need to free */ + + algorithm_info = NULL; prkey_info.id.len = 1; prkey_info.id.value[0] = i + 1; prkey_info.usage = key_cfg[i].prkey_usage; prkey_info.native = 1; prkey_info.key_reference = i; - prkey_info.modulus_length = bebytes2ushort(cxdata + 1); strlcpy(prkey_obj.label, key_cfg[i].label, sizeof(prkey_obj.label)); prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE | SC_PKCS15_CO_FLAG_MODIFIABLE; prkey_obj.auth_id.len = 1; prkey_obj.auth_id.value[0] = key_cfg[i].prkey_pin; - r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); - if (r < 0) - return SC_ERROR_INTERNAL; + /* need to get size from algorithms using oid */ + if (cxdata[0] == SC_OPENPGP_KEYALGO_ECDH || + cxdata[0] == SC_OPENPGP_KEYALGO_ECDSA || + cxdata[0] == SC_OPENPGP_KEYALGO_EDDSA) { + /* Last byte could be Import-Format of private key, let's ignore it, + * as it is not part of OID */ + if (cxdata[cxdata_len-1] == SC_OPENPGP_KEYFORMAT_EC_STD || + cxdata[cxdata_len-1] == SC_OPENPGP_KEYFORMAT_EC_STDPUB) + cxdata_len--; + r = sc_asn1_decode_object_id(&cxdata[1], cxdata_len-1, &oid); + if (r != SC_SUCCESS) { + sc_log(ctx, "Failed to parse OID for elliptic curve algorithm"); + } + } + + switch (cxdata[0]) { + case SC_OPENPGP_KEYALGO_ECDH: + if (sc_compare_oid(&oid, &curve25519_oid)) { + if ((algorithm_info = sc_card_find_xeddsa_alg(card, 0, &oid))) + prkey_info.field_length = algorithm_info->key_length; + else { + sc_log(ctx, "algorithm not found"); + continue; + } + break; + } + /* Fall through */ + case SC_OPENPGP_KEYALGO_ECDSA: + if((algorithm_info = sc_card_find_ec_alg(card, 0, &oid))) + prkey_info.field_length = algorithm_info->key_length; + else { + sc_log(ctx, "algorithm not found"); + continue; + } + break; + case SC_OPENPGP_KEYALGO_EDDSA: + if ((algorithm_info = sc_card_find_eddsa_alg(card, 0, &oid))) + prkey_info.field_length = algorithm_info->key_length; + else { + sc_log(ctx, "algorithm not found"); + continue; + } + break; + } + + switch (cxdata[0]) { + case SC_OPENPGP_KEYALGO_EDDSA: + /* Filter out invalid usage: EdDSA does not support anything but sign */ + prkey_info.usage &= PGP_SIG_PRKEY_USAGE; + r = sc_pkcs15emu_add_eddsa_prkey(p15card, &prkey_obj, &prkey_info); + break; + + case SC_OPENPGP_KEYALGO_ECDH: + /* This can result in either ECDSA key or EC_MONTGOMERY + * so we need to check OID */ + if (sc_compare_oid(&oid, &curve25519_oid)) { + /* This can do only DERIVE */ + prkey_info.usage = SC_PKCS15_PRKEY_USAGE_DERIVE; + r = sc_pkcs15emu_add_xeddsa_prkey(p15card, &prkey_obj, &prkey_info); + break; + } + prkey_info.usage |= SC_PKCS15_PRKEY_USAGE_DERIVE; + prkey_info.usage &= ~PGP_ENC_PRKEY_USAGE; + r = sc_pkcs15emu_add_ec_prkey(p15card, &prkey_obj, &prkey_info); + break; + + case SC_OPENPGP_KEYALGO_ECDSA: + prkey_info.usage = SC_PKCS15_PRKEY_USAGE_SIGN; + r = sc_pkcs15emu_add_ec_prkey(p15card, &prkey_obj, &prkey_info); + break; + + case SC_OPENPGP_KEYALGO_RSA: + if (cxdata_len >= 3) { + /* with Authentication key, can only decrypt if can change MSE */ + if (i == 2 && !(priv->ext_caps & EXT_CAP_MSE)) { + prkey_info.usage &= ~PGP_ENC_PRKEY_USAGE; + } + prkey_info.modulus_length = bebytes2ushort(cxdata + 1); + r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); + break; + } + /* Fallthrough */ + default: + sc_log(ctx, "Invalid algorithm identifier %x (length = %d)", + cxdata[0], r); + } + + if (r < 0) { + r = SC_ERROR_INTERNAL; + goto failed; + } } } + + sc_log(ctx, "Adding public keys"); /* Add public keys */ for (i = 0; i < 3; i++) { sc_pkcs15_pubkey_info_t pubkey_info; sc_pkcs15_object_t pubkey_obj; - u8 cxdata[10]; + u8 cxdata[12]; + int cxdata_len = sizeof(cxdata); char path_template[] = "006E:0073:00Cx"; int j; memset(&pubkey_info, 0, sizeof(pubkey_info)); memset(&pubkey_obj, 0, sizeof(pubkey_obj)); + memset(&cxdata, 0, sizeof(cxdata)); path_template[13] = '1' + i; /* The needed tags are C1 C2 and C3 */ - if ((r = read_file(card, path_template, cxdata, sizeof(cxdata))) < 0) + if ((cxdata_len = read_file(card, path_template, cxdata, sizeof(cxdata))) < 1) goto failed; - if (r != 6) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Key info bytes have unexpected length (expected 6, got %d)\n", r); - return SC_ERROR_INTERNAL; - } /* check validity using finger prints */ for (j = 19; j >= 0; j--) { @@ -325,18 +450,105 @@ sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card) /* only add valid keys, i.e. those with a legal algorithm identifier & finger print */ if (j >= 0 && cxdata[0] != 0) { + struct sc_object_id oid; + struct sc_algorithm_info * algorithm_info; /* no need to free */ + + algorithm_info = NULL; pubkey_info.id.len = 1; pubkey_info.id.value[0] = i + 1; - pubkey_info.modulus_length = bebytes2ushort(cxdata + 1); pubkey_info.usage = key_cfg[i].pubkey_usage; sc_format_path(key_cfg[i].pubkey_path, &pubkey_info.path); strlcpy(pubkey_obj.label, key_cfg[i].label, sizeof(pubkey_obj.label)); pubkey_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; - r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); - if (r < 0) - return SC_ERROR_INTERNAL; + if (cxdata[0] == SC_OPENPGP_KEYALGO_ECDH || + cxdata[0] == SC_OPENPGP_KEYALGO_ECDSA || + cxdata[0] == SC_OPENPGP_KEYALGO_EDDSA) { + /* Last byte could be Import-Format of private key, let's ignore it, + * as it is not part of OID */ + if (cxdata[cxdata_len-1] == SC_OPENPGP_KEYFORMAT_EC_STD || + cxdata[cxdata_len-1] == SC_OPENPGP_KEYFORMAT_EC_STDPUB) + cxdata_len--; + r = sc_asn1_decode_object_id(&cxdata[1], cxdata_len-1, &oid); + if (r != SC_SUCCESS) { + sc_log(ctx, "Failed to parse OID for elliptic curve algorithm"); + } + } + + switch (cxdata[0]) { + case SC_OPENPGP_KEYALGO_ECDH: + if (sc_compare_oid(&oid, &curve25519_oid)) { + if ((algorithm_info = sc_card_find_xeddsa_alg(card, 0, &oid))) + pubkey_info.field_length = algorithm_info->key_length; + else { + sc_log(ctx, "algorithm not found"); + continue; + } + break; + } + /* Fall through */ + case SC_OPENPGP_KEYALGO_ECDSA: + if((algorithm_info = sc_card_find_ec_alg(card, 0, &oid))) + pubkey_info.field_length = algorithm_info->key_length; + else { + sc_log(ctx, "algorithm not found"); + continue; + } + break; + case SC_OPENPGP_KEYALGO_EDDSA: + if ((algorithm_info = sc_card_find_eddsa_alg(card, 0, &oid))) + pubkey_info.field_length = algorithm_info->key_length; + else { + sc_log(ctx, "algorithm not found"); + continue; + } + break; + } + + switch (cxdata[0]) { + case SC_OPENPGP_KEYALGO_EDDSA: + /* assuming Ed25519 as it is the only supported now */ + /* Filter out invalid usage: ED does not support anything but sign */ + pubkey_info.usage &= PGP_SIG_PUBKEY_USAGE; + r = sc_pkcs15emu_add_eddsa_pubkey(p15card, &pubkey_obj, &pubkey_info); + break; + case SC_OPENPGP_KEYALGO_ECDH: + /* This can result in either ECDSA key or EC_MONTGOMERY + * so we need to check OID */ + if (sc_compare_oid(&oid, &curve25519_oid)) { + /* XXX What can this key do? */ + pubkey_info.usage = SC_PKCS15_PRKEY_USAGE_DERIVE; + r = sc_pkcs15emu_add_xeddsa_pubkey(p15card, &pubkey_obj, &pubkey_info); + break; + } + pubkey_info.usage = SC_PKCS15_PRKEY_USAGE_DERIVE; + r = sc_pkcs15emu_add_ec_pubkey(p15card, &pubkey_obj, &pubkey_info); + break; + case SC_OPENPGP_KEYALGO_ECDSA: + pubkey_info.usage = PGP_SIG_PUBKEY_USAGE; + r = sc_pkcs15emu_add_ec_pubkey(p15card, &pubkey_obj, &pubkey_info); + break; + case SC_OPENPGP_KEYALGO_RSA: + if (cxdata_len >= 3) { + /* with Authentication pubkey, can only encrypt if can change MSE */ + if (i == 2 && !(priv->ext_caps & EXT_CAP_MSE)) { + pubkey_info.usage &= ~PGP_ENC_PUBKEY_USAGE; + } + pubkey_info.modulus_length = bebytes2ushort(cxdata + 1); + r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); + break; + } + /* Fall through */ + default: + sc_log(ctx, "Invalid algorithm identifier %x (length = %d)", + cxdata[0], r); + } + + if (r < 0) { + r = SC_ERROR_INTERNAL; + goto failed; + } } } @@ -346,38 +558,79 @@ sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card) if (r < 0) goto failed; - /* If DO 7F21 holds data, we declare a cert object for pkcs15 */ - if (file->size > 0) { + for(u8 i=0; i= v3, errors are non-critical */ + sc_card_ctl(card, SC_CARDCTL_OPENPGP_SELECT_DATA, &i); + + sc_format_path(certs[i].path, &cert_info.path); + /* Certificate ID. We use the same ID as the authentication key */ - cert_info.id.value[0] = 3; - cert_info.id.len = 1; - /* Authority, flag is zero */ - /* The path following which PKCS15 will find the content of the object */ - sc_format_path("3F007F21", &cert_info.path); + sc_pkcs15_format_id(certs[i].id, &cert_info.id); + + resp_len = sc_get_data(card, 0x7F21, buffer, MAX_OPENPGP_DO_SIZE); + + /* Response length => free buffer and continue with next id */ + if (resp_len == 0) { + free(buffer); + continue; + } + + /* Catch error during sc_get_data */ + if (resp_len < 0) { + free(buffer); + goto failed; + } + + /* Assemble certificate info struct, based on `certs` array */ + cert_info.value.len = resp_len; + cert_info.value.value = buffer; + cert_info.authority = certs[i].authority; + cert_obj.flags = certs[i].obj_flags; + /* Object label */ - strlcpy(cert_obj.label, "Cardholder certificate", sizeof(cert_obj.label)); + strlcpy(cert_obj.label, certs[i].label, sizeof(cert_obj.label)); r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); - if (r < 0) + if (r < 0) { + free(buffer); goto failed; - } - - /* PKCS#15 DATA object from OpenPGP private DOs */ - r = sc_pkcs15emu_openpgp_add_data(p15card); + } - return 0; + /* only iterate, for OpenPGP >= v3, thus break on < v3 */ + if (card->type < SC_CARD_TYPE_OPENPGP_V3) + break; + } -failed: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to initialize OpenPGP emulation: %s\n", - sc_strerror(r)); - return r; + /* Add PKCS#15 DATA objects from other OpenPGP card DOs. The return + * value is ignored, so this will not cause initialization to fail. + */ + sc_pkcs15emu_openpgp_add_data(p15card); + +failed: + if (r < 0) { + sc_log(card->ctx, + "Failed to initialize OpenPGP emulation: %s\n", + sc_strerror(r)); + sc_pkcs15_card_clear(p15card); + } + sc_file_free(file); + LOG_FUNC_RETURN(ctx, r); } +// see https://stackoverflow.com/a/10536254 for explanation +#define LEN_MAX_INT_AS_STR (3 * sizeof(int) + 2) + static int sc_pkcs15emu_openpgp_add_data(sc_pkcs15_card_t *p15card) { @@ -385,12 +638,12 @@ sc_pkcs15emu_openpgp_add_data(sc_pkcs15_card_t *p15card) int i, r; LOG_FUNC_CALLED(ctx); - /* There is 4 private DO from 0101 to 0104 */ + /* Optional private use DOs 0101 to 0104 */ for (i = 1; i <= PGP_NUM_PRIVDO; i++) { sc_pkcs15_data_info_t dat_info; sc_pkcs15_object_t dat_obj; - char name[8]; - char path[9]; + char name[6 + LEN_MAX_INT_AS_STR]; + char path[7 + LEN_MAX_INT_AS_STR]; u8 content[254]; memset(&dat_info, 0, sizeof(dat_info)); memset(&dat_obj, 0, sizeof(dat_obj)); @@ -398,8 +651,8 @@ sc_pkcs15emu_openpgp_add_data(sc_pkcs15_card_t *p15card) snprintf(name, 8, "PrivDO%d", i); snprintf(path, 9, "3F00010%d", i); - /* Check if the DO can be read. - * We won't expose pkcs15 DATA object if DO is empty. + /* Check if the DO can be read and is not empty. Otherwise we + * won't expose a PKCS#15 DATA object. */ r = read_file(p15card->card, path, content, sizeof(content)); if (r <= 0 ) { @@ -421,28 +674,26 @@ sc_pkcs15emu_openpgp_add_data(sc_pkcs15_card_t *p15card) sc_log(ctx, "Add %s data object", name); r = sc_pkcs15emu_add_data_object(p15card, &dat_obj, &dat_info); + LOG_TEST_RET(ctx, r, "Could not add data object to framework"); } - LOG_FUNC_RETURN(ctx, r); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int openpgp_detect_card(sc_pkcs15_card_t *p15card) { - if (p15card->card->type == SC_CARD_TYPE_OPENPGP_V1 || p15card->card->type == SC_CARD_TYPE_OPENPGP_V2 - || p15card->card->type == SC_CARD_TYPE_OPENPGP_GNUK) + if (p15card->card->type == SC_CARD_TYPE_OPENPGP_BASE + || p15card->card->type == SC_CARD_TYPE_OPENPGP_V1 + || p15card->card->type == SC_CARD_TYPE_OPENPGP_V2 + || p15card->card->type == SC_CARD_TYPE_OPENPGP_GNUK + || p15card->card->type == SC_CARD_TYPE_OPENPGP_V3) return SC_SUCCESS; else return SC_ERROR_WRONG_CARD; } -int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid, - sc_pkcs15emu_opt_t *opts) +int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid) { - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - return sc_pkcs15emu_openpgp_init(p15card); - else { - int r = openpgp_detect_card(p15card); - if (r) - return SC_ERROR_WRONG_CARD; - return sc_pkcs15emu_openpgp_init(p15card); - } + if (openpgp_detect_card(p15card)) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_openpgp_init(p15card); } diff --git a/src/libopensc/pkcs15-pin.c b/src/libopensc/pkcs15-pin.c index 1c7df89148..816ddb9b7f 100644 --- a/src/libopensc/pkcs15-pin.c +++ b/src/libopensc/pkcs15-pin.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -30,6 +30,7 @@ #include "internal.h" #include "asn1.h" #include "pkcs15.h" +#include "ui/notify.h" int _sc_pkcs15_verify_pin(struct sc_pkcs15_card *, struct sc_pkcs15_object *, const unsigned char *, size_t); @@ -142,7 +143,7 @@ sc_pkcs15_decode_aodf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_obj r = sc_asn1_decode(ctx, asn1_auth_type, *buf, *buflen, buf, buflen); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) return r; - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "ASN.1 decoding failed"); + LOG_TEST_RET(ctx, r, "ASN.1 decoding failed"); if (asn1_auth_type_choice[0].flags & SC_ASN1_PRESENT) { sc_log(ctx, "AuthType: PIN"); @@ -175,14 +176,16 @@ sc_pkcs15_decode_aodf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_obj /* Give priority to AID defined in the application DDO */ if (p15card->app && p15card->app->ddo.aid.len) info.path.aid = p15card->app->ddo.aid; - else if (p15card->file_app->path.len) + else if (p15card->file_app && p15card->file_app->path.len) info.path = p15card->file_app->path; + else + return SC_ERROR_INTERNAL; } } sc_debug(ctx, SC_LOG_DEBUG_ASN1, "decoded PIN(ref:%X,path:%s)", info.attrs.pin.reference, sc_print_path(&info.path)); } else if (asn1_auth_type_choice[1].flags & SC_ASN1_PRESENT) { - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "BIO authentication object not yet supported"); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "BIO authentication object not yet supported"); } else if (asn1_auth_type_choice[2].flags & SC_ASN1_PRESENT) { sc_log(ctx, "AuthType: AuthKey"); @@ -193,12 +196,12 @@ sc_pkcs15_decode_aodf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_obj info.attrs.authkey.derived = 1; } else { - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "unknown authentication type"); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "unknown authentication type"); } obj->data = malloc(sizeof(info)); if (obj->data == NULL) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(obj->data, &info, sizeof(info)); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, SC_SUCCESS); @@ -295,24 +298,16 @@ sc_pkcs15_verify_pin(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *pi const unsigned char *pincode, size_t pinlen) { struct sc_context *ctx = p15card->card->ctx; - struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; + struct sc_pkcs15_auth_info *auth_info; int r; LOG_FUNC_CALLED(ctx); - /* - * if pin cache is disabled, we can get here with no PIN data. - * in this case, to avoid error or unnecessary pin prompting on pinpad, - * check if the PIN has been already verified and the access condition - * is still open on card. - */ - if (pinlen == 0) { - r = sc_pkcs15_get_pin_info(p15card, pin_obj); - - if (r == SC_SUCCESS && auth_info->logged_in == SC_PIN_STATE_LOGGED_IN) - LOG_FUNC_RETURN(ctx, r); - } + if (!pin_obj || !pin_obj->data) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_PIN_REFERENCE); + auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; + /* Check the provided pin matches pin requirements */ r = _validate_pin(p15card, auth_info, pinlen); if (r) @@ -358,11 +353,13 @@ int sc_pkcs15_verify_pin_with_session_pin(struct sc_pkcs15_card *p15card, "PIN(type:%X; method:%X; value(%p:%"SC_FORMAT_LEN_SIZE_T"u)", auth_info->auth_type, auth_info->auth_method, pincode, pinlen); + card = p15card->card; - if (pinlen > SC_MAX_PIN_SIZE) + if (pinlen > SC_MAX_PIN_SIZE) { + sc_notify_id(card->ctx, &card->reader->atr, p15card, + NOTIFY_PIN_BAD); LOG_TEST_RET(ctx, SC_ERROR_INVALID_PIN_LENGTH, "Invalid PIN size"); - - card = p15card->card; + } /* Initialize arguments */ memset(&data, 0, sizeof(data)); @@ -408,10 +405,10 @@ int sc_pkcs15_verify_pin_with_session_pin(struct sc_pkcs15_card *p15card, data.pin_reference = skey_info->key_reference; } - if((p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD - || p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH) - && !pinlen) { - data.flags |= SC_PIN_CMD_USE_PINPAD; + if ((p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD + || p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH)) { + if (!pincode && !pinlen) + data.flags |= SC_PIN_CMD_USE_PINPAD; if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) data.pin1.prompt = "Please enter SO PIN"; @@ -442,18 +439,28 @@ int sc_pkcs15_verify_pin_with_session_pin(struct sc_pkcs15_card *p15card, goto out; } - r = sc_pin_cmd(card, &data, &auth_info->tries_left); + r = sc_pin_cmd(card, &data); + auth_info->tries_left = data.pin1.tries_left; sc_log(ctx, "PIN cmd result %i", r); if (r == SC_SUCCESS) { sc_pkcs15_pincache_add(p15card, pin_obj, pincode, pinlen); - if (data.cmd == SC_PIN_CMD_GET_SESSION_PIN) { + if (data.cmd == SC_PIN_CMD_GET_SESSION_PIN && sessionpinlen) { *sessionpinlen = data.pin2.len; } } else { - if (data.cmd == SC_PIN_CMD_GET_SESSION_PIN) { + sc_notify_id(card->ctx, &card->reader->atr, p15card, + NOTIFY_PIN_BAD); + if (data.cmd == SC_PIN_CMD_GET_SESSION_PIN && sessionpinlen) { *sessionpinlen = 0; } } + + if (auth_info->auth_type == SC_PKCS15_PIN_AUTH_TYPE_PIN + && auth_info->auth_method != SC_AC_SESSION) { + sc_notify_id(card->ctx, &card->reader->atr, p15card, + r == SC_SUCCESS ? NOTIFY_PIN_GOOD : NOTIFY_PIN_BAD); + } + out: sc_unlock(card); LOG_FUNC_RETURN(ctx, r); @@ -542,7 +549,8 @@ int sc_pkcs15_change_pin(struct sc_pkcs15_card *p15card, } } - r = sc_pin_cmd(card, &data, &auth_info->tries_left); + r = sc_pin_cmd(card, &data); + auth_info->tries_left = data.pin1.tries_left; if (r == SC_SUCCESS) sc_pkcs15_pincache_add(p15card, pin_obj, newpin, newpinlen); @@ -563,6 +571,7 @@ int sc_pkcs15_unblock_pin(struct sc_pkcs15_card *p15card, struct sc_pin_cmd_data data; struct sc_pkcs15_object *puk_obj; struct sc_pkcs15_auth_info *puk_info = NULL; + int pukref = 0; struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; struct sc_card *card = p15card->card; int r; @@ -584,6 +593,7 @@ int sc_pkcs15_unblock_pin(struct sc_pkcs15_card *p15card, if (r >= 0 && puk_obj) { /* second step: get the pkcs15 info object of the puk */ puk_info = (struct sc_pkcs15_auth_info *)puk_obj->data; + pukref = puk_info->attrs.pin.reference; } if (!puk_info) { @@ -594,6 +604,15 @@ int sc_pkcs15_unblock_pin(struct sc_pkcs15_card *p15card, r = _validate_pin(p15card, puk_info, puklen); LOG_TEST_RET(ctx, r, "PIN do not conforms PIN policy"); + /* + * With the current card driver interface we have no way of specifying different padding + * flags for the PIN and the PUK. Therefore reject this case. + */ + if ((auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_NEEDS_PADDING) != + (puk_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_NEEDS_PADDING)) { + LOG_TEST_RET(ctx, r, "Padding mismatch for PIN/PUK"); + } + r = sc_lock(card); LOG_TEST_RET(ctx, r, "sc_lock() failed"); @@ -609,18 +628,19 @@ int sc_pkcs15_unblock_pin(struct sc_pkcs15_card *p15card, data.cmd = SC_PIN_CMD_UNBLOCK; data.pin_type = SC_AC_CHV; data.pin_reference = auth_info->attrs.pin.reference; + data.puk_reference = pukref; data.pin1.data = puk; data.pin1.len = puklen; - data.pin1.pad_char = auth_info->attrs.pin.pad_char; - data.pin1.min_length = auth_info->attrs.pin.min_length; - data.pin1.max_length = auth_info->attrs.pin.max_length; - data.pin1.pad_length = auth_info->attrs.pin.stored_length; + data.pin1.pad_char = puk_info->attrs.pin.pad_char; + data.pin1.min_length = puk_info->attrs.pin.min_length; + data.pin1.max_length = puk_info->attrs.pin.max_length; + data.pin1.pad_length = puk_info->attrs.pin.stored_length; data.pin2.data = newpin; data.pin2.len = newpinlen; - data.pin2.pad_char = puk_info->attrs.pin.pad_char; - data.pin2.min_length = puk_info->attrs.pin.min_length; - data.pin2.max_length = puk_info->attrs.pin.max_length; - data.pin2.pad_length = puk_info->attrs.pin.stored_length; + data.pin2.pad_char = auth_info->attrs.pin.pad_char; + data.pin2.min_length = auth_info->attrs.pin.min_length; + data.pin2.max_length = auth_info->attrs.pin.max_length; + data.pin2.pad_length = auth_info->attrs.pin.stored_length; if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_NEEDS_PADDING) data.flags |= SC_PIN_CMD_NEED_PADDING; @@ -656,7 +676,8 @@ int sc_pkcs15_unblock_pin(struct sc_pkcs15_card *p15card, } } - r = sc_pin_cmd(card, &data, &auth_info->tries_left); + r = sc_pin_cmd(card, &data); + auth_info->tries_left = data.pin1.tries_left; if (r == SC_SUCCESS) sc_pkcs15_pincache_add(p15card, pin_obj, newpin, newpinlen); @@ -685,17 +706,23 @@ int sc_pkcs15_get_pin_info(struct sc_pkcs15_card *p15card, goto out; } + /* the path in the pin object is optional */ + if ((pin_info->path.len > 0) || ((pin_info->path.aid.len > 0))) { + r = sc_select_file(card, &pin_info->path, NULL); + if (r) + goto out; + } + /* Try to update PIN info from card */ memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_GET_INFO; - data.pin_type = SC_AC_CHV; + data.pin_type = pin_info->auth_method; data.pin_reference = pin_info->attrs.pin.reference; - r = sc_pin_cmd(card, &data, NULL); + r = sc_pin_cmd(card, &data); if (r == SC_SUCCESS) { if (data.pin1.max_tries > 0) pin_info->max_tries = data.pin1.max_tries; - /* tries_left must be supported or sc_pin_cmd should not return SC_SUCCESS */ pin_info->tries_left = data.pin1.tries_left; pin_info->logged_in = data.pin1.logged_in; } @@ -747,12 +774,12 @@ void sc_pkcs15_pincache_add(struct sc_pkcs15_card *p15card, struct sc_pkcs15_obj if (sc_pkcs15_compare_id(&obj->auth_id, &auth_info->auth_id)) { /* Caching is refused, if the protected object requires user consent */ - if (!p15card->opts.pin_cache_ignore_user_consent) { - if (obj->user_consent > 0) { - sc_log(ctx, "caching refused (user consent)"); - return; + if (!p15card->opts.pin_cache_ignore_user_consent) { + if (obj->user_consent > 0) { + sc_log(ctx, "caching refused (user consent)"); + return; + } } - } } obj = obj->next; @@ -823,7 +850,7 @@ void sc_pkcs15_pincache_clear(struct sc_pkcs15_card *p15card) struct sc_pkcs15_object *objs[32]; int i, r; - SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(p15card->card->ctx); r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 32); for (i = 0; i < r; i++) sc_pkcs15_free_object_content(objs[i]); diff --git a/src/libopensc/pkcs15-piv.c b/src/libopensc/pkcs15-piv.c index d38d7ba736..6bad33b52b 100644 --- a/src/libopensc/pkcs15-piv.c +++ b/src/libopensc/pkcs15-piv.c @@ -2,10 +2,11 @@ * partial PKCS15 emulation for PIV-II cards * only minimal use of the authentication cert and key * - * Copyright (C) 2005,2006,2007,2008,2009,2010 - * Douglas E. Engert + * Copyright (C) 2005,2006,2007,2008,2009,2010 + * Douglas E. Engert + * Copyright (C) 2020-2026 Douglas E. Engert * 2004, Nils Larsch - * Copyright (C) 2006, Identity Alliance, + * Copyright (C) 2006, Identity Alliance, * Thomas Harning * Copyright (C) 2007, EMC, Russell Larner * @@ -21,10 +22,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -40,8 +41,6 @@ #define MANU_ID "piv_II " -int sc_pkcs15emu_piv_init_ex(sc_pkcs15_card_t *, struct sc_aid *aid, sc_pkcs15emu_opt_t *); - typedef struct objdata_st { const char *id; const char *label; @@ -54,8 +53,8 @@ typedef struct objdata_st { typedef struct cdata_st { const char *id; const char *label; - int authority; const char *path; + int authority; int obj_flags; } cdata; @@ -68,11 +67,12 @@ typedef struct pdata_st { unsigned int maxlen; unsigned int minlen; unsigned int storedlen; - int flags; + int flags; int tries_left; const unsigned char pad_char; int obj_flags; -} pindata; + int cardmod; /* only use with cardmod minidriver */ +} pindata; typedef struct pubdata_st { const char *id; @@ -95,16 +95,16 @@ typedef struct prdata_st { int ref; const char *auth_id; int obj_flags; - int user_consent; + int user_consent; } prdata; typedef struct common_key_info_st { int cert_found; int pubkey_found; int pubkey_from_file; - int key_alg; - unsigned int pubkey_len; - unsigned long long cert_keyUsage; /* x509 key usage as defined in certificate */ + unsigned long key_alg; + size_t pubkey_len; + unsigned int cert_keyUsage; /* x509 key usage as defined in certificate */ int cert_keyUsage_present; /* 1 if keyUsage found in certificate */ int pub_usage; int priv_usage; @@ -118,11 +118,11 @@ typedef struct common_key_info_st { * is used, or the GUID is used as a serial number. * We need to return a GUID like value for each object * But this needs to be some what unique. - * So we will use two different methods, depending - * on the size of the sereal number. + * So we will use two different methods, depending + * on the size of the serial number. * If it is 25 bytes, then it was from a FASCN. If 16 bytes * its from a GUID. - * If neither, we will uase the default method. + * If neither, we will uase the default method. */ static int piv_get_guid(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *obj, @@ -146,6 +146,8 @@ static int piv_get_guid(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_o r = sc_card_ctl(p15card->card, SC_CARDCTL_GET_SERIALNR, &serialnr); if (r) return r; + if (serialnr.len > SC_MAX_SERIALNR) + return SC_ERROR_INTERNAL; memset(guid_bin, 0, sizeof(guid_bin)); memset(out, 0, *out_size); @@ -153,14 +155,14 @@ static int piv_get_guid(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_o if (id.len == 1 && serialnr.len == 25) { /* It is from a FASCN, and we need to shorten it but keep - * as much uniquness as possible. - * FASC-N is stored like a ISO 7811 Magnetic Strip Card + * as much uniqueness as possible. + * FASC-N is stored like a ISO 7811 Magnetic Strip Card * Using the ANSI/ISO BCD Data Format - * 4 data bit + 1 parity bit (odd) least significant bit first. - * It starts with the Start Sentinel 0x0b ";" - * Fields are seperated by 0x0d "=" + * 4 data bit + 1 parity bit (odd) least significant bit first. + * It starts with the Start Sentinel 0x0b ";" + * Fields are separated by 0x0d "=" * Ends with End Sentinel 0x0f "?" - * Its 39 characters + the LRC + * Its 39 characters + the LRC * http://www.dataip.co.uk/Reference/MagneticCardBCD.php * 0x0a, 0x0c, 0x0e are some type of control * the FASCN has a lot of extra bits, with only 32 digits. @@ -196,17 +198,17 @@ static int piv_get_guid(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_o } /* overwrite two insignificant digits in middle with id */ - memcpy(guid_bin + 7, id.value, id.len); + memcpy(guid_bin + 7, id.value, id.len); tlen = 16; } else if (id.len == 1 && serialnr.len == 16) { - /* its from a GUID, we will overwrite the + /* its from a GUID, we will overwrite the * first byte with id.value, as this preserves most - * of the uniqueness. - */ + * of the uniqueness. + */ memcpy(guid_bin, id.value, id.len); memcpy(guid_bin + id.len, serialnr.value + 1, serialnr.len - 1); - + tlen = id.len + serialnr.len - 1; /* i.e. 16 */ } else { /* not what was expected... use default */ @@ -236,8 +238,8 @@ static int piv_detect_card(sc_pkcs15_card_t *p15card) sc_card_t *card = p15card->card; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - if (card->type < SC_CARD_TYPE_PIV_II_GENERIC - || card->type >= SC_CARD_TYPE_PIV_II_GENERIC+1000) + if (card->type < SC_CARD_TYPE_PIV_II_BASE + || card->type >= SC_CARD_TYPE_PIV_II_BASE + 1000) return SC_ERROR_INVALID_CARD; return SC_SUCCESS; } @@ -249,8 +251,9 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) /* The cert objects will return all the data */ /* Note: pkcs11 objects do not have CK_ID values */ + // clang-format off static const objdata objects[] = { - {"01", "Card Capability Container", + {"01", "Card Capability Container", "2.16.840.1.101.3.7.1.219.0", NULL, "DB00", 0}, {"02", "Card Holder Unique Identifier", "2.16.840.1.101.3.7.2.48.0", NULL, "3000", 0}, @@ -262,11 +265,11 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) "2.16.840.1.101.3.7.2.96.16", "01", "6010", SC_PKCS15_CO_FLAG_PRIVATE}, {"06", "Printed Information", "2.16.840.1.101.3.7.2.48.1", "01", "3001", SC_PKCS15_CO_FLAG_PRIVATE}, - {"07", "Cardholder Facial Image", + {"07", "Cardholder Facial Image", "2.16.840.1.101.3.7.2.96.48", "01", "6030", SC_PKCS15_CO_FLAG_PRIVATE}, {"08", "X.509 Certificate for Digital Signature", "2.16.840.1.101.3.7.2.1.0", NULL, "0100", 0}, - {"09", "X.509 Certificate for Key Management", + {"09", "X.509 Certificate for Key Management", "2.16.840.1.101.3.7.2.1.2", NULL, "0102", 0}, {"10","X.509 Certificate for Card Authentication", "2.16.840.1.101.3.7.2.5.0", NULL, "0500", 0}, @@ -279,125 +282,155 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) {"14", "Cardholder Iris Image", "2.16.840.1.101.3.7.2.16.21", NULL, "1015", SC_PKCS15_CO_FLAG_PRIVATE}, - {"15", "Retired X.509 Certificate for Key Management 1", + {"15", "Retired X.509 Certificate for Key Management 1", "2.16.840.1.101.3.7.2.16.1", NULL, "1001", 0}, - {"16", "Retired X.509 Certificate for Key Management 2", + {"16", "Retired X.509 Certificate for Key Management 2", "2.16.840.1.101.3.7.2.16.2", NULL, "1002", 0}, - {"17", "Retired X.509 Certificate for Key Management 3", + {"17", "Retired X.509 Certificate for Key Management 3", "2.16.840.1.101.3.7.2.16.3", NULL, "1003", 0}, - {"18", "Retired X.509 Certificate for Key Management 4", + {"18", "Retired X.509 Certificate for Key Management 4", "2.16.840.1.101.3.7.2.16.4", NULL, "1004", 0}, - {"19", "Retired X.509 Certificate for Key Management 5", + {"19", "Retired X.509 Certificate for Key Management 5", "2.16.840.1.101.3.7.2.16.5", NULL, "1005", 0}, - {"20", "Retired X.509 Certificate for Key Management 6", + {"20", "Retired X.509 Certificate for Key Management 6", "2.16.840.1.101.3.7.2.16.6", NULL, "1006", 0}, - {"21", "Retired X.509 Certificate for Key Management 7", + {"21", "Retired X.509 Certificate for Key Management 7", "2.16.840.1.101.3.7.2.16.7", NULL, "1007", 0}, - {"22", "Retired X.509 Certificate for Key Management 8", + {"22", "Retired X.509 Certificate for Key Management 8", "2.16.840.1.101.3.7.2.16.8", NULL, "1008", 0}, - {"23", "Retired X.509 Certificate for Key Management 9", + {"23", "Retired X.509 Certificate for Key Management 9", "2.16.840.1.101.3.7.2.16.9", NULL, "1009", 0}, - {"24", "Retired X.509 Certificate for Key Management 10", + {"24", "Retired X.509 Certificate for Key Management 10", "2.16.840.1.101.3.7.2.16.10", NULL, "100A", 0}, - {"25", "Retired X.509 Certificate for Key Management 11", + {"25", "Retired X.509 Certificate for Key Management 11", "2.16.840.1.101.3.7.2.16.11", NULL, "100B", 0}, - {"26", "Retired X.509 Certificate for Key Management 12", + {"26", "Retired X.509 Certificate for Key Management 12", "2.16.840.1.101.3.7.2.16.12", NULL, "100C", 0}, - {"27", "Retired X.509 Certificate for Key Management 13", + {"27", "Retired X.509 Certificate for Key Management 13", "2.16.840.1.101.3.7.2.16.13", NULL, "100D", 0}, - {"28", "Retired X.509 Certificate for Key Management 14", + {"28", "Retired X.509 Certificate for Key Management 14", "2.16.840.1.101.3.7.2.16.14", NULL, "100E", 0}, - {"29", "Retired X.509 Certificate for Key Management 15", + {"29", "Retired X.509 Certificate for Key Management 15", "2.16.840.1.101.3.7.2.16.15", NULL, "100F", 0}, - {"30", "Retired X.509 Certificate for Key Management 16", + {"30", "Retired X.509 Certificate for Key Management 16", "2.16.840.1.101.3.7.2.16.16", NULL, "1010", 0}, - {"31", "Retired X.509 Certificate for Key Management 17", + {"31", "Retired X.509 Certificate for Key Management 17", "2.16.840.1.101.3.7.2.16.17", NULL, "1011", 0}, - {"32", "Retired X.509 Certificate for Key Management 18", + {"32", "Retired X.509 Certificate for Key Management 18", "2.16.840.1.101.3.7.2.16.18", NULL, "1012", 0}, - {"33", "Retired X.509 Certificate for Key Management 19", + {"33", "Retired X.509 Certificate for Key Management 19", "2.16.840.1.101.3.7.2.16.19", NULL, "1013", 0}, - {"34", "Retired X.509 Certificate for Key Management 20", + {"34", "Retired X.509 Certificate for Key Management 20", "2.16.840.1.101.3.7.2.16.20", NULL, "1014", 0}, + /* new in 800-73-4 */ + {"35", "Biometric Information Templates Group Template", + "2.16.840.1.101.3.7.2.16.22", NULL, "1016", 0}, + {"36", "Secure Messaging Certificate Signer", + "2.16.840.1.101.3.7.2.16.23", NULL, "1017", 0}, + {"37", "Pairing Code Reference Data Container", + "2.16.840.1.101.3.7.2.16.24", NULL, "1018", SC_PKCS15_CO_FLAG_PRIVATE}, {NULL, NULL, NULL, NULL, NULL, 0} -}; - /* - * NIST 800-73-1 lifted the restriction on - * requering pin protected certs. Thus the default is to + }; + // clang-format on + /* NIST 800-73-1 lifted the restriction on + * requiring pin protected certs. Thus the default is to * not require this. + * + * Certs will be pulled out from the cert objects + * But there may be extra certs (SM Signer cert) that do + * not have a private keys on the card. These certs must be last */ - /* certs will be pulled out from the cert objects */ - /* the number of cert, pubkey and prkey triplets */ - -#define PIV_NUM_CERTS_AND_KEYS 24 - - static const cdata certs[PIV_NUM_CERTS_AND_KEYS] = { - {"01", "Certificate for PIV Authentication", 0, "0101cece", 0}, - {"02", "Certificate for Digital Signature", 0, "0100cece", 0}, - {"03", "Certificate for Key Management", 0, "0102cece", 0}, - {"04", "Certificate for Card Authentication", 0, "0500cece", 0}, - {"05", "Retired Certificate for Key Management 1", 0, "1001cece", 0}, - {"06", "Retired Certificate for Key Management 2", 0, "1002cece", 0}, - {"07", "Retired Certificate for Key Management 3", 0, "1003cece", 0}, - {"08", "Retired Certificate for Key Management 4", 0, "1004cece", 0}, - {"09", "Retired Certificate for Key Management 5", 0, "1005cece", 0}, - {"10", "Retired Certificate for Key Management 6", 0, "1006cece", 0}, - {"11", "Retired Certificate for Key Management 7", 0, "1007cece", 0}, - {"12", "Retired Certificate for Key Management 8", 0, "1008cece", 0}, - {"13", "Retired Certificate for Key Management 9", 0, "1009cece", 0}, - {"14", "Retired Certificate for Key Management 10", 0, "100Acece", 0}, - {"15", "Retired Certificate for Key Management 11", 0, "100Bcece", 0}, - {"16", "Retired Certificate for Key Management 12", 0, "100Ccece", 0}, - {"17", "Retired Certificate for Key Management 13", 0, "100Dcece", 0}, - {"18", "Retired Certificate for Key Management 14", 0, "100Ecece", 0}, - {"19", "Retired Certificate for Key Management 15", 0, "100Fcece", 0}, - {"20", "Retired Certificate for Key Management 16", 0, "1010cece", 0}, - {"21", "Retired Certificate for Key Management 17", 0, "1011cece", 0}, - {"22", "Retired Certificate for Key Management 18", 0, "1012cece", 0}, - {"23", "Retired Certificate for Key Management 19", 0, "1013cece", 0}, - {"24", "Retired Certificate for Key Management 20", 0, "1014cece", 0} + + /* Any certs on card without private key must be last */ +#define PIV_NUM_CERTS 25 +#define PIV_NUM_KEYS 24 + + // clang-format off + static const cdata certs[PIV_NUM_CERTS] = { + {"01", "Certificate for PIV Authentication", "0101cece", 0, 0}, + {"02", "Certificate for Digital Signature", "0100cece", 0, 0}, + {"03", "Certificate for Key Management", "0102cece", 0, 0}, + {"04", "Certificate for Card Authentication", "0500cece", 0, 0}, + {"05", "Retired Certificate for Key Management 1", "1001cece", 0, 0}, + {"06", "Retired Certificate for Key Management 2", "1002cece", 0, 0}, + {"07", "Retired Certificate for Key Management 3", "1003cece", 0, 0}, + {"08", "Retired Certificate for Key Management 4", "1004cece", 0, 0}, + {"09", "Retired Certificate for Key Management 5", "1005cece", 0, 0}, + {"10", "Retired Certificate for Key Management 6", "1006cece", 0, 0}, + {"11", "Retired Certificate for Key Management 7", "1007cece", 0, 0}, + {"12", "Retired Certificate for Key Management 8", "1008cece", 0, 0}, + {"13", "Retired Certificate for Key Management 9", "1009cece", 0, 0}, + {"14", "Retired Certificate for Key Management 10", "100Acece", 0, 0}, + {"15", "Retired Certificate for Key Management 11", "100Bcece", 0, 0}, + {"16", "Retired Certificate for Key Management 12", "100Ccece", 0, 0}, + {"17", "Retired Certificate for Key Management 13", "100Dcece", 0, 0}, + {"18", "Retired Certificate for Key Management 14", "100Ecece", 0, 0}, + {"19", "Retired Certificate for Key Management 15", "100Fcece", 0, 0}, + {"20", "Retired Certificate for Key Management 16", "1010cece", 0, 0}, + {"21", "Retired Certificate for Key Management 17", "1011cece", 0, 0}, + {"22", "Retired Certificate for Key Management 18", "1012cece", 0, 0}, + {"23", "Retired Certificate for Key Management 19", "1013cece", 0, 0}, + {"24", "Retired Certificate for Key Management 20", "1014cece", 0, 0}, + /* Yubikey Attestation uses "25" but not read via GET_DATA */ + {"81", "Secure Messaging Certificate Signer", "1017cece", 0, 0} /* no keys on card */ }; + // clang-format on + // clang-format off static const pindata pins[] = { - { "01", "PIV Card Holder pin", "", 0x80, + { "01", "PIN", "", 0x80, /* label, flag and ref will change if using global pin */ SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, - 8, 4, 8, + 8, 4, 8, + SC_PKCS15_PIN_FLAG_NEEDS_PADDING | + SC_PKCS15_PIN_FLAG_INITIALIZED | + SC_PKCS15_PIN_FLAG_LOCAL, + -1, 0xFF, + SC_PKCS15_CO_FLAG_PRIVATE, 0}, + + { "02", "PIV PUK", "", 0x81, + SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, + 8, 4, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_INITIALIZED | - SC_PKCS15_PIN_FLAG_LOCAL, + SC_PKCS15_PIN_FLAG_SO_PIN | + SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN, -1, 0xFF, - SC_PKCS15_CO_FLAG_PRIVATE }, - { "02", "PIV PUK", "", 0x81, + SC_PKCS15_CO_FLAG_PRIVATE, 0}, + + /* only used with minidriver */ + { "03", "PIN", "", 0x80, + /* used in minidriver as the sign key and for 9C key */ + /* label, flag and ref will change if using global pin */ SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, - 8, 4, 8, + 8, 4, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_INITIALIZED | - SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_SO_PIN | - SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN, - -1, 0xFF, - SC_PKCS15_CO_FLAG_PRIVATE }, - { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0} + SC_PKCS15_PIN_FLAG_LOCAL, + -1, 0xFF, + SC_PKCS15_CO_FLAG_PRIVATE, 1}, /* only use if cardmod */ + { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + // clang-format on /* * The size of the key or the algid is not really known - * but can be derived from the certificates. - * the cert, pubkey and privkey are a set. + * but can be derived from the certificates. + * the cert, pubkey and privkey are a set. * Key usages bits taken from pkcs15v1_1 Table 2 - * RSA and EC hav differents set of usage + * RSA and EC have different sets of usage */ - static const pubdata pubkeys[PIV_NUM_CERTS_AND_KEYS] = { - - { "01", "PIV AUTH pubkey", + // clang-format off + static const pubdata pubkeys[PIV_NUM_KEYS] = { + { "01", "PIV AUTH pubkey", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP | SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER, /*EC*/SC_PKCS15_PRKEY_USAGE_VERIFY, "9A06", 0x9A, NULL, 0, "PIV_9A_KEY"}, - { "02", "SIGN pubkey", + { "02", "SIGN pubkey", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER | @@ -405,122 +438,128 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) /*EC*/SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION, "9C06", 0x9C, NULL, 0, "PIV_9C_KEY"}, - { "03", "KEY MAN pubkey", + { "03", "KEY MAN pubkey", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT| SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "9D06", 0x9D, NULL, 0, "PIV_9D_KEY"}, - { "04", "CARD AUTH pubkey", + { "04", "CARD AUTH pubkey", /*RSA*/SC_PKCS15_PRKEY_USAGE_VERIFY | - SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER, + SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER, /*EC*/SC_PKCS15_PRKEY_USAGE_VERIFY, "9E06", 0x9E, NULL, 0, "PIV_9E_KEY"}, /* no pin, and avail in contactless */ { "05", "Retired KEY MAN 1", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "8206", 0x82, NULL, 0, NULL}, + "8206", 0x82, NULL, 0, "PIV_82_KEY"}, { "06", "Retired KEY MAN 2", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "8306", 0x83, NULL, 0, NULL}, + "8306", 0x83, NULL, 0, "PIV_83_KEY"}, { "07", "Retired KEY MAN 3", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "8406", 0x84, NULL, 0, NULL}, + "8406", 0x84, NULL, 0, "PIV_84_KEY"}, { "08", "Retired KEY MAN 4", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "8506", 0x85, NULL, 0, NULL}, + "8506", 0x85, NULL, 0, "PIV_85_KEY"}, { "09", "Retired KEY MAN 5", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "8606", 0x86, NULL, 0, NULL}, + "8606", 0x86, NULL, 0, "PIV_86_KEY"}, { "10", "Retired KEY MAN 6", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "8706", 0x87, NULL, 0, NULL}, + "8706", 0x87, NULL, 0, "PIV_87_KEY"}, { "11", "Retired KEY MAN 7", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "8806", 0x88, NULL, 0, NULL}, + "8806", 0x88, NULL, 0, "PIV_88_KEY"}, { "12", "Retired KEY MAN 8", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "8906", 0x89, NULL, 0, NULL}, + "8906", 0x89, NULL, 0, "PIV_89_KEY"}, { "13", "Retired KEY MAN 9", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "8A06", 0x8A, NULL, 0, NULL}, + "8A06", 0x8A, NULL, 0, "PIV_8A_KEY"}, { "14", "Retired KEY MAN 10", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "8B06", 0x8B, NULL, 0, NULL}, + "8B06", 0x8B, NULL, 0, "PIV_8B_KEY"}, { "15", "Retired KEY MAN 11", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "8C06", 0x8C, NULL, 0, NULL}, + "8C06", 0x8C, NULL, 0, "PIV_8C_KEY"}, { "16", "Retired KEY MAN 12", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "8D06", 0x8D, NULL, 0, NULL}, + "8D06", 0x8D, NULL, 0, "PIV_8D_KEY"}, { "17", "Retired KEY MAN 13", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "8E06", 0x8E, NULL, 0, NULL}, + "8E06", 0x8E, NULL, 0, "PIV_8E_KEY"}, { "18", "Retired KEY MAN 14", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "8F06", 0x8F, NULL, 0, NULL}, + "8F06", 0x8F, NULL, 0, "PIV_8F_KEY"}, { "19", "Retired KEY MAN 15", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "9006", 0x90, NULL, 0, NULL}, + "9006", 0x90, NULL, 0, "PIV_90_KEY"}, { "20", "Retired KEY MAN 16", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "9106", 0x91, NULL, 0, NULL}, + "9106", 0x91, NULL, 0, "PIV_91_KEY"}, { "21", "Retired KEY MAN 17", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "9206", 0x92, NULL, 0, NULL}, + "9206", 0x92, NULL, 0, "PIV_92_KEY"}, { "22", "Retired KEY MAN 18", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "9306", 0x93, NULL, 0, NULL}, + "9306", 0x93, NULL, 0, "PIV_93_KEY"}, { "23", "Retired KEY MAN 19", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "9406", 0x94, NULL, 0, NULL}, + "9406", 0x94, NULL, 0, "PIV_94_KEY"}, { "24", "Retired KEY MAN 20", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "9506", 0x95, NULL, 0, NULL} }; + "9506", 0x95, NULL, 0, "PIV_95_KEY"} + }; + // clang-format on /* - * note some of the SC_PKCS15_PRKEY values are dependent - * on the key algorithm, and will be reset. + * Note some of the SC_PKCS15_PRKEY values are dependent + * on the key algorithm, and will be reset. + + * No SM Signer private Key on card + * The 04 SM ECC CVC pubkey is in response to SELECT AID */ - static const prdata prkeys[PIV_NUM_CERTS_AND_KEYS] = { - { "01", "PIV AUTH key", + // clang-format off + static const prdata prkeys[PIV_NUM_KEYS] = { + { "01", "PIV AUTH key", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP | SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER, /*EC*/SC_PKCS15_PRKEY_USAGE_SIGN, "", 0x9A, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, - { "02", "SIGN key", + { "02", "SIGN key", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION, - /*EC*/SC_PKCS15_PRKEY_USAGE_SIGN | + /*EC*/SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION, - "", 0x9C, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, - { "03", "KEY MAN key", + "", 0x9C, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, /* use sign pin and user_consent */ + { "03", "KEY MAN key", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, "", 0x9D, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, - { "04", "CARD AUTH key", + { "04", "CARD AUTH key", /*RSA*/SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER, /*EC*/SC_PKCS15_PRKEY_USAGE_SIGN, @@ -528,92 +567,94 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) { "05", "Retired KEY MAN 1", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x82, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x82, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "06", "Retired KEY MAN 2", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x83, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x83, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "07", "Retired KEY MAN 3", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x84, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x84, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "08", "Retired KEY MAN 4", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x85, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x85, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "09", "Retired KEY MAN 5", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x86, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x86, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "10", "Retired KEY MAN 6", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x87, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x87, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "11", "Retired KEY MAN 7", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x88, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x88, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "12", "Retired KEY MAN 8", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x89, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x89, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "13", "Retired KEY MAN 9", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x8A, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x8A, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "14", "Retired KEY MAN 10", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x8B, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x8B, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "15", "Retired KEY MAN 11", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x8C, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x8C, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "16", "Retired KEY MAN 12", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x8D, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x8D, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "17", "Retired KEY MAN 13", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x8E, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x8E, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "18", "Retired KEY MAN 14", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x8F, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x8F, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "19", "Retired KEY MAN 15", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x90, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x90, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "20", "Retired KEY MAN 16", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x91, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x91, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "21", "Retired KEY MAN 17", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x92, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x92, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "22", "Retired KEY MAN 18", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x93, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x93, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "23", "Retired KEY MAN 19", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x94, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1}, + "", 0x94, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0}, { "24", "Retired KEY MAN 20", /*RSA*/SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, /*EC*/SC_PKCS15_PRKEY_USAGE_DERIVE, - "", 0x95, "01", SC_PKCS15_CO_FLAG_PRIVATE, 1} + "", 0x95, "01", SC_PKCS15_CO_FLAG_PRIVATE, 0} + /* SM Signer certificate does not have private key on card */ }; + // clang-format on int r, i; sc_card_t *card = p15card->card; sc_serial_number_t serial; char buf[SC_MAX_SERIALNR * 2 + 1]; - common_key_info ckis[PIV_NUM_CERTS_AND_KEYS]; + common_key_info ckis[PIV_NUM_CERTS]; int follows_nist_fascn = 0; - + char *token_name = NULL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); @@ -622,30 +663,30 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) /* could read this off card if needed */ /* CSP does not like a - in the name */ - p15card->tokeninfo->label = strdup("PIV_II"); - p15card->tokeninfo->manufacturer_id = strdup(MANU_ID); + set_string(&p15card->tokeninfo->label, "PIV_II"); + set_string(&p15card->tokeninfo->manufacturer_id, MANU_ID); /* - * get serial number + * get serial number * We will use the FASC-N from the CHUID * Note we are not verifying CHUID, belongs to this card - * but need serial number for Mac tokend + * but need serial number for Mac tokend */ r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"sc_card_ctl rc=%d",r); - p15card->tokeninfo->serial_number = strdup("00000000"); + sc_log(card->ctx, "sc_card_ctl rc=%d",r); + set_string(&p15card->tokeninfo->serial_number, "00000000"); } else { sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0); - p15card->tokeninfo->serial_number = strdup(buf); + set_string(&p15card->tokeninfo->serial_number, buf); } /* US gov issued PIVs have CHUID with a FASCN that does not start with 9999 */ if (serial.len == 25 && !(serial.value[0] == 0xD4 && serial.value[1] == 0xE7 && serial.value[2] == 0x39 && (serial.value[3] | 0x7F) == 0xFF)) { follows_nist_fascn = 1; } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "PIV-II adding objects..."); + sc_log(card->ctx, "PIV-II adding objects..."); /* set other objects */ for (i = 0; objects[i].label; i++) { @@ -658,60 +699,48 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) sc_format_path(objects[i].path, &obj_info.path); /* See if the object can not be present on the card */ - r = (card->ops->card_ctl)(card, SC_CARDCTL_PIV_OBJECT_PRESENT, &obj_info.path); + r = sc_card_ctl(card, SC_CARDCTL_PIV_OBJECT_PRESENT, &obj_info.path); if (r == 1) continue; /* Not on card, do not define the object */ - + strncpy(obj_info.app_label, objects[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); r = sc_format_oid(&obj_info.app_oid, objects[i].aoid); if (r != SC_SUCCESS) - return r; + LOG_FUNC_RETURN(card->ctx, r); + /* We can not read all the objects, as some need the PIN! */ if (objects[i].auth_id) sc_pkcs15_format_id(objects[i].auth_id, &obj_obj.auth_id); strncpy(obj_obj.label, objects[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); obj_obj.flags = objects[i].obj_flags; - - r = sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_DATA_OBJECT, - &obj_obj, &obj_info); + + r = sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_DATA_OBJECT, + &obj_obj, &obj_info); + if (r < 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); -/* TODO - * PIV key 9C requires the pin verify be done just before any - * crypto operation using the key. - * - * Nss 3.12.7 does not check the CKA_ALWAYS_AUTHENTICATE attribute of a key - * and will do a C_FindObjects with only CKA_VALUE looking for a certificate - * it had found earlier after c_Login. The template does not add CKA_TYPE=cert. - * This will cause the card-piv to read all the objects and will reset - * the security status for the 9C key. - * Mozilla Bug 357025 - * Mozilla Bug 613507 - * on 5/16/2012, both scheduled for NSS 3.14 - * - * We can not read all the objects, as some need the PIN! - */ + LOG_FUNC_RETURN(card->ctx, r); } /* * certs, pubkeys and priv keys are related and we assume - * they are in order - * We need to read the cert, get modulus and keylen - * We use those for the pubkey, and priv key objects. - * If no cert, then see if pubkey (i.e. we are initilizing, + * they are in order + * We need to read the cert, get modulus and keylen + * We use those for the pubkey, and priv key objects. + * If no cert, then see if pubkey (i.e. we are initializing, * and the pubkey is in a file,) then add pubkey and privkey - * If no cert and no pubkey, skip adding them. - + * If no cert and no pubkey, skip adding them. + */ /* set certs */ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "PIV-II adding certs..."); - for (i = 0; i < PIV_NUM_CERTS_AND_KEYS; i++) { + sc_log(card->ctx, "PIV-II adding certs..."); + for (i = 0; i < PIV_NUM_CERTS; i++) { struct sc_pkcs15_cert_info cert_info; struct sc_pkcs15_object cert_obj; sc_pkcs15_der_t cert_der; sc_pkcs15_cert_t *cert_out = NULL; - + int private_obj; + ckis[i].cert_found = 0; ckis[i].key_alg = -1; ckis[i].pubkey_found = 0; @@ -725,7 +754,7 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) memset(&cert_info, 0, sizeof(cert_info)); memset(&cert_obj, 0, sizeof(cert_obj)); - + sc_pkcs15_format_id(certs[i].id, &cert_info.id); cert_info.authority = certs[i].authority; sc_format_path(certs[i].path, &cert_info.path); @@ -734,16 +763,17 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) cert_obj.flags = certs[i].obj_flags; /* See if the cert might be present or not. */ - r = (card->ops->card_ctl)(card, SC_CARDCTL_PIV_OBJECT_PRESENT, &cert_info.path); + r = sc_card_ctl(card, SC_CARDCTL_PIV_OBJECT_PRESENT, &cert_info.path); if (r == 1) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Cert can not be present,i=%d", i); + sc_log(card->ctx, "Cert can not be present,i=%d", i); continue; } - r = sc_pkcs15_read_file(p15card, &cert_info.path, &cert_der.value, &cert_der.len); + private_obj = cert_obj.flags & SC_PKCS15_CO_FLAG_PRIVATE; + r = sc_pkcs15_read_file(p15card, &cert_info.path, &cert_der.value, &cert_der.len, private_obj); if (r) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No cert found,i=%d", i); + sc_log(card->ctx, "No cert found,i=%d", i); continue; } @@ -753,19 +783,46 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) if (cert_der.value) { cert_info.value.value = cert_der.value; cert_info.value.len = cert_der.len; - if (!p15card->opts.use_file_cache) { + if (!p15card->opts.use_file_cache + || (private_obj && !(p15card->opts.use_file_cache & SC_PKCS15_OPTS_CACHE_ALL_FILES))) { cert_info.path.len = 0; /* use in mem cert from now on */ } } /* following will find the cached cert in cert_info */ - r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out); - if (r < 0 || cert_out->key == NULL) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to read/parse the certificate r=%d",r); + r = sc_pkcs15_read_certificate(p15card, &cert_info, private_obj, &cert_out); + if (r < 0 || cert_out == NULL || cert_out->key == NULL) { + sc_log(card->ctx, "Failed to read/parse the certificate r=%d",r); if (cert_out != NULL) sc_pkcs15_free_certificate(cert_out); + free(cert_der.value); continue; } - /* + + /* set the token name to the name of the CN of the first certificate */ + if (!token_name) { + u8 * cn_name = NULL; + size_t cn_len = 0; + static const struct sc_object_id cn_oid = {{ 2, 5, 4, 3, -1 }}; + r = sc_pkcs15_get_name_from_dn(card->ctx, cert_out->subject, + cert_out->subject_len, &cn_oid, &cn_name, &cn_len); + if (r == SC_SUCCESS) { + token_name = malloc (cn_len+1); + if (!token_name) { + sc_pkcs15_free_certificate(cert_out); + free(cn_name); + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(card->ctx, r, + "Failed to allocate memory"); + } + memcpy(token_name, cn_name, cn_len); + free(cn_name); + token_name[cn_len] = 0; + free(p15card->tokeninfo->label); + p15card->tokeninfo->label = token_name; + } + } + + /* * get keyUsage if present save in ckis[i] * Will only use it if this in a non FED issued card * which has a CHUID with FASC-N not starting with 9999 @@ -773,13 +830,11 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) if (follows_nist_fascn == 0) { struct sc_object_id keyUsage_oid={{2,5,29,15,-1}}; - unsigned long long *value; int r = 0; - value = &ckis[i].cert_keyUsage; r = sc_pkcs15_get_bitstring_extension(card->ctx, cert_out, &keyUsage_oid, - value, NULL); + &ckis[i].cert_keyUsage, NULL); if ( r >= 0) ckis[i].cert_keyUsage_present = 1; /* TODO if no key usage, we could set all uses */ @@ -845,6 +900,8 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) break; case SC_ALGORITHM_EC: + case SC_ALGORITHM_EDDSA: + case SC_ALGORITHM_XEDDSA: ckis[i].pubkey_len = cert_out->key->u.ec.params.field_length; if (ckis[i].cert_keyUsage_present) { if (ckis[i].cert_keyUsage & SC_X509_DIGITAL_SIGNATURE) { @@ -887,28 +944,34 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) break; default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsuported key.algorithm %d", cert_out->key->algorithm); + sc_log(card->ctx, "Unsupported key.algorithm %lu", cert_out->key->algorithm); ckis[i].pubkey_len = 0; /* set some value for now */ } - ckis[i].pubkey_from_cert = cert_out->key; - cert_out->key = NULL; + if (i < PIV_NUM_KEYS) { /* Only save pub key if card can have private key */ + ckis[i].pubkey_from_cert = cert_out->key; + cert_out->key = NULL; + } sc_pkcs15_free_certificate(cert_out); r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " Failed to add cert obj r=%d",r); + sc_log(card->ctx, " Failed to add cert obj r=%d",r); continue; } } /* set pins */ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "PIV-II adding pins..."); + sc_log(card->ctx, "PIV-II adding pins..."); for (i = 0; pins[i].label; i++) { struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; const char * label; int pin_ref; + /* the SignPIN is only used with minidriver */ + if (pins[i].cardmod && (strcmp(card->ctx->app_name, "cardmod") != 0)) + continue; + memset(&pin_info, 0, sizeof(pin_info)); memset(&pin_obj, 0, sizeof(pin_obj)); @@ -921,22 +984,21 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) pin_info.attrs.pin.stored_length = pins[i].storedlen; pin_info.attrs.pin.max_length = pins[i].maxlen; pin_info.attrs.pin.pad_char = pins[i].pad_char; + pin_info.tries_left = pins[i].tries_left; sc_format_path(pins[i].path, &pin_info.path); - pin_info.tries_left = -1; label = pins[i].label; - if (i == 0 && - (card->ops->card_ctl)(card, SC_CARDCTL_PIV_PIN_PREFERENCE, + if ((i == 0 || pins[i].cardmod) && + sc_card_ctl(card, SC_CARDCTL_PIV_PIN_PREFERENCE, &pin_ref) == 0 && pin_ref == 0x00) { /* must be 80 for PIV pin, or 00 for Global PIN */ pin_info.attrs.pin.reference = pin_ref; - pin_info.attrs.pin.flags &= ~SC_PKCS15_PIN_FLAG_LOCAL; label = "Global PIN"; - } -sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE Adding pin %d label=%s",i, label); + } + strncpy(pin_obj.label, label, SC_PKCS15_MAX_LABEL_SIZE - 1); pin_obj.flags = pins[i].obj_flags; - if (i == 0 && pin_info.attrs.pin.reference == 0x80) { + if ((i == 0 || pins[i].cardmod)) { /* * according to description of "RESET RETRY COUNTER" * command in specs PUK can only unblock PIV PIN @@ -946,22 +1008,19 @@ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE Adding pin %d label=%s",i, label); } r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); - if (r < 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to add PIN"); } - - /* set public keys */ - /* We may only need this during initialzation when genkey - * gets the pubkey, but it can not be read from the card - * at a later time. The piv-tool can stach pubkey in file - */ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "PIV-II adding pub keys..."); - for (i = 0; i < PIV_NUM_CERTS_AND_KEYS; i++) { + /* We may only need this during initialization when genkey + * gets the pubkey, but it can not be read from the card + * at a later time. The piv-tool can stash pubkey in file + */ + sc_log(card->ctx, "PIV-II adding pub keys..."); + for (i = 0; i < PIV_NUM_KEYS; i++) { struct sc_pkcs15_pubkey_info pubkey_info; struct sc_pkcs15_object pubkey_obj; - struct sc_pkcs15_pubkey *p15_key; + struct sc_pkcs15_pubkey *p15_key = NULL; memset(&pubkey_info, 0, sizeof(pubkey_info)); memset(&pubkey_obj, 0, sizeof(pubkey_obj)); @@ -976,63 +1035,64 @@ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE Adding pin %d label=%s",i, label); strncpy(pubkey_obj.label, pubkeys[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); pubkey_obj.flags = pubkeys[i].obj_flags; - if (pubkeys[i].auth_id) sc_pkcs15_format_id(pubkeys[i].auth_id, &pubkey_obj.auth_id); - /* If no cert found, piv-tool may have stached the pubkey + /* If no cert found, piv-tool may have stashed the pubkey * so we can use it when generating a certificate request - * The file is a OpenSSL DER EVP_KEY, which looks like + * The file is a OpenSSL DER EVP_KEY, which looks like * a certificate subjectPublicKeyInfo. * */ if (ckis[i].cert_found == 0 ) { /* no cert found */ char * filename = NULL; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"No cert for this pub key i=%d",i); - - /* + + sc_log(card->ctx, "No cert for this pub key i=%d",i); + + /* * If we used the piv-tool to generate a key, * we would have saved the public key as a file. * This code is only used while signing a request * After the certificate is loaded on the card, * the public key is extracted from the certificate. */ - - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DEE look for env %s", + + + sc_log(card->ctx, "DEE look for env %s", pubkeys[i].getenvname?pubkeys[i].getenvname:"NULL"); - if (pubkeys[i].getenvname == NULL) + if (pubkeys[i].getenvname == NULL) continue; - filename = getenv(pubkeys[i].getenvname); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DEE look for file %s", filename?filename:"NULL"); - if (filename == NULL) + filename = getenv(pubkeys[i].getenvname); + sc_log(card->ctx, "DEE look for file %s", filename?filename:"NULL"); + if (filename == NULL) continue; - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Adding pubkey from file %s",filename); + + sc_log(card->ctx, "Adding pubkey from file %s",filename); r = sc_pkcs15_pubkey_from_spki_file(card->ctx, filename, &p15_key); - if (r < 0) + if (r < 0) { + free(p15_key); continue; + } /* Lets also try another method. */ r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, p15_key, &pubkey_info.direct.spki.value, &pubkey_info.direct.spki.len); - LOG_TEST_RET(card->ctx, r, "SPKI encode public key error"); - + LOG_TEST_GOTO_ERR(card->ctx, r, "SPKI encode public key error"); + /* Only get here if no cert, and the the above found the - * pub key file (actually the SPKI version). This only - * happens when trying initializing a card and have set - * env PIV_9A_KEY or 9C, 9D, 9E to point at the file. + * pub key file (actually the SPKI version). This only + * happens when trying initializing a card and have set + * env PIV_9A_KEY or 9C, 9D, 9E to point at the file. * * We will cache it using the PKCS15 emulation objects */ pubkey_info.path.len = 0; - - ckis[i].key_alg = p15_key->algorithm; + + ckis[i].key_alg = p15_key->algorithm; switch (p15_key->algorithm) { case SC_ALGORITHM_RSA: /* save pubkey_len in pub and priv */ @@ -1041,26 +1101,30 @@ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE Adding pin %d label=%s",i, label); ckis[i].pubkey_from_file = 1; break; case SC_ALGORITHM_EC: - ckis[i].key_alg = SC_ALGORITHM_EC; + case SC_ALGORITHM_EDDSA: + case SC_ALGORITHM_XEDDSA: + ckis[i].key_alg = p15_key->algorithm; ckis[i].pubkey_len = p15_key->u.ec.params.field_length; ckis[i].pubkey_found = 1; ckis[i].pubkey_from_file = 1; break; default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Unsupported key_alg %d",p15_key->algorithm); + sc_log(card->ctx, "Unsupported key_alg %lu", p15_key->algorithm); continue; } pubkey_obj.emulated = p15_key; p15_key = NULL; } else if (ckis[i].pubkey_from_cert) { - r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, ckis[i].pubkey_from_cert, &pubkey_info.direct.spki.value, &pubkey_info.direct.spki.len); - LOG_TEST_RET(card->ctx, r, "SPKI encode public key error"); + r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, ckis[i].pubkey_from_cert, + &pubkey_info.direct.spki.value, &pubkey_info.direct.spki.len); + LOG_TEST_GOTO_ERR(card->ctx, r, "SPKI encode public key error"); pubkey_obj.emulated = ckis[i].pubkey_from_cert; + ckis[i].pubkey_from_cert = NULL; } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"adding pubkey for %d keyalg=%d",i, ckis[i].key_alg); + sc_log(card->ctx, "adding pubkey for %d keyalg=%lu", i, ckis[i].key_alg); switch (ckis[i].key_alg) { case SC_ALGORITHM_RSA: if (ckis[i].cert_keyUsage_present) { @@ -1071,39 +1135,49 @@ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE Adding pin %d label=%s",i, label); pubkey_info.modulus_length = ckis[i].pubkey_len; strncpy(pubkey_obj.label, pubkeys[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); + /* should not fail */ r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); - if (r < 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); /* should not fail */ + LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to add RSA pubkey"); ckis[i].pubkey_found = 1; break; case SC_ALGORITHM_EC: + case SC_ALGORITHM_EDDSA: + case SC_ALGORITHM_XEDDSA: if (ckis[i].cert_keyUsage_present) { pubkey_info.usage = ckis[i].pub_usage; } else { - pubkey_info.usage = pubkeys[i].usage_ec; + pubkey_info.usage = pubkeys[i].usage_ec; } - pubkey_info.field_length = ckis[i].pubkey_len; + pubkey_info.field_length = ckis[i].pubkey_len; strncpy(pubkey_obj.label, pubkeys[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); - r = sc_pkcs15emu_add_ec_pubkey(p15card, &pubkey_obj, &pubkey_info); - if (r < 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); /* should not fail */ + /* should not fail */ + + if (ckis[i].key_alg == SC_ALGORITHM_EDDSA) + r = sc_pkcs15emu_add_eddsa_pubkey(p15card, &pubkey_obj, &pubkey_info); + else if (ckis[i].key_alg == SC_ALGORITHM_XEDDSA) + r = sc_pkcs15emu_add_xeddsa_pubkey(p15card, &pubkey_obj, &pubkey_info); + else + r = sc_pkcs15emu_add_ec_pubkey(p15card, &pubkey_obj, &pubkey_info); + + LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to add EC pubkey"); + ckis[i].pubkey_found = 1; break; default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"key_alg %d not supported", ckis[i].key_alg); + sc_log(card->ctx, "key_alg %lu not supported", ckis[i].key_alg); continue; } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"USAGE: cert_keyUsage_present:%d usage:0x%8.8x", - ckis[i].cert_keyUsage_present ,pubkey_info.usage); + sc_log(card->ctx, "USAGE: cert_keyUsage_present:%d usage:0x%8.8x", + ckis[i].cert_keyUsage_present, pubkey_info.usage); } /* set private keys */ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "PIV-II adding private keys..."); - for (i = 0; i < PIV_NUM_CERTS_AND_KEYS; i++) { + sc_log(card->ctx, "PIV-II adding private keys..."); + for (i = 0; i < PIV_NUM_KEYS; i++) { struct sc_pkcs15_prkey_info prkey_info; struct sc_pkcs15_object prkey_obj; @@ -1112,7 +1186,7 @@ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE Adding pin %d label=%s",i, label); if (ckis[i].cert_found == 0 && ckis[i].pubkey_found == 0) continue; /* i.e. no cert or pubkey */ - + sc_pkcs15_format_id(prkeys[i].id, &prkey_info.id); prkey_info.native = 1; prkey_info.key_reference = prkeys[i].ref; @@ -1120,29 +1194,37 @@ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE Adding pin %d label=%s",i, label); strncpy(prkey_obj.label, prkeys[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); prkey_obj.flags = prkeys[i].obj_flags; - prkey_obj.user_consent = prkeys[i].user_consent; + prkey_obj.user_consent = prkeys[i].user_consent; /* only Sign key */ if (prkeys[i].auth_id) sc_pkcs15_format_id(prkeys[i].auth_id, &prkey_obj.auth_id); + /* If using minidriver, use Sign PIN for 9C key */ + if (prkey_obj.user_consent && (strcmp(card->ctx->app_name, "cardmod") == 0)) + sc_pkcs15_format_id("03", &prkey_obj.auth_id); + /* - * When no cert is present and a pubkey in a file was found, - * means the caller is initilaizeing a card. A sign operation - * will be required to sign a certificate request even if - * normal usage would not allow it. Set SC_PKCS15_PRKEY_USAGE_SIGN - * TODO if code is added to allow key generation and reqest - * sign in the same session, similiar code will be needed. + * When no cert is present and a pubkey in a file was found, + * means the caller is initializing a card. A sign operation + * will be required to sign a certificate request even if + * normal usage would not allow it. Set SC_PKCS15_PRKEY_USAGE_SIGN + * TODO if code is added to allow key generation and request + * sign in the same session, similar code will be needed. */ if (ckis[i].pubkey_from_file == 1) { prkey_info.usage = SC_PKCS15_PRKEY_USAGE_SIGN; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Adding SC_PKCS15_PRKEY_USAGE_SIGN"); + sc_log(card->ctx, "Adding SC_PKCS15_PRKEY_USAGE_SIGN"); } switch (ckis[i].key_alg) { case SC_ALGORITHM_RSA: if(ckis[i].cert_keyUsage_present) { prkey_info.usage |= ckis[i].priv_usage; + /* If retired key and non gov cert has NONREPUDIATION, treat as user_consent */ + if (i >= 4 && (ckis[i].priv_usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)) { + prkey_obj.user_consent = 1; + } } else { prkey_info.usage |= prkeys[i].usage_rsa; } @@ -1150,44 +1232,56 @@ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE Adding pin %d label=%s",i, label); r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); break; case SC_ALGORITHM_EC: + case SC_ALGORITHM_EDDSA: + case SC_ALGORITHM_XEDDSA: if (ckis[i].cert_keyUsage_present) { prkey_info.usage |= ckis[i].priv_usage; + /* If retired key and non gov cert has NONREPUDIATION, treat as user_consent */ + if (i >= 4 && (ckis[i].priv_usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)) { + prkey_obj.user_consent = 1; + } } else { prkey_info.usage |= prkeys[i].usage_ec; } prkey_info.field_length = ckis[i].pubkey_len; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE added key_alg %2.2x prkey_obj.flags %8.8x", + sc_log(card->ctx, "DEE added key_alg %2.2lx prkey_obj.flags %8.8x", ckis[i].key_alg, prkey_obj.flags); - r = sc_pkcs15emu_add_ec_prkey(p15card, &prkey_obj, &prkey_info); + + if (ckis[i].key_alg == SC_ALGORITHM_EDDSA) + r = sc_pkcs15emu_add_eddsa_prkey(p15card, &prkey_obj, &prkey_info); + else if (ckis[i].key_alg == SC_ALGORITHM_XEDDSA) + r = sc_pkcs15emu_add_xeddsa_prkey(p15card, &prkey_obj, &prkey_info); + else + r = sc_pkcs15emu_add_ec_prkey(p15card, &prkey_obj, &prkey_info); break; default: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported key_alg %d", ckis[i].key_alg); + sc_log(card->ctx, "Unsupported key_alg %lu", ckis[i].key_alg); r = 0; /* we just skip this one */ } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"USAGE: cert_keyUsage_present:%d usage:0x%8.8x", ckis[i].cert_keyUsage_present ,prkey_info.usage); - if (r < 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + sc_log(card->ctx, "USAGE: cert_keyUsage_present:%d usage:0x%8.8x", ckis[i].cert_keyUsage_present, prkey_info.usage); + LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to add Private key"); } p15card->ops.get_guid = piv_get_guid; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +err: + for (i = 0; i < PIV_NUM_CERTS; i++) { + sc_pkcs15_free_pubkey(ckis[i].pubkey_from_cert); + } + sc_pkcs15_card_clear(p15card); + LOG_FUNC_RETURN(card->ctx, r); } int sc_pkcs15emu_piv_init_ex(sc_pkcs15_card_t *p15card, - struct sc_aid *aid, sc_pkcs15emu_opt_t *opts) + struct sc_aid *aid) { sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - return sc_pkcs15emu_piv_init(p15card); - else { - int r = piv_detect_card(p15card); - if (r) - return SC_ERROR_WRONG_CARD; - return sc_pkcs15emu_piv_init(p15card); - } + if (piv_detect_card(p15card)) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_piv_init(p15card); } diff --git a/src/libopensc/pkcs15-postecert.c b/src/libopensc/pkcs15-postecert.c deleted file mode 100644 index 14a8cc3be7..0000000000 --- a/src/libopensc/pkcs15-postecert.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * PKCS15 emulation layer for Postecert and Cnipa card. - * To see how this works, run p15dump on your Postecert or Cnipa Card. - * - * Copyright (C) 2004, Antonino Iacono - * Copyright (C) 2003, Olaf Kirch - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#include "common/compat_strlcpy.h" -#include "internal.h" -#include "pkcs15.h" -#include "log.h" - -int sc_pkcs15emu_postecert_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); - -static int (*set_security_env) (sc_card_t *, const sc_security_env_t *, int); - -static int set_sec_env(sc_card_t * card, const sc_security_env_t *env, - int se_num) -{ - sc_security_env_t tenv = *env; - if (tenv.operation == SC_SEC_OPERATION_SIGN) - tenv.operation = SC_SEC_OPERATION_DECIPHER; - return set_security_env(card, &tenv, se_num); -} - -static int do_sign(sc_card_t * card, const u8 * in, size_t inlen, u8 * out, - size_t outlen) -{ - return card->ops->decipher(card, in, inlen, out, outlen); -} - -static void set_string(char **strp, const char *value) -{ - if (*strp) - free(*strp); - *strp = value ? strdup(value) : NULL; -} - -#if 1 -/* XXX: temporary copy of the old pkcs15emu functions, - * to be removed */ -static int sc_pkcs15emu_add_pin(sc_pkcs15_card_t *p15card, - const sc_pkcs15_id_t *id, const char *label, - const sc_path_t *path, int ref, int type, - unsigned int min_length, - unsigned int max_length, - int flags, int tries_left, const char pad_char, int obj_flags) -{ - sc_pkcs15_auth_info_t info; - sc_pkcs15_object_t obj; - - memset(&info, 0, sizeof(info)); - memset(&obj, 0, sizeof(obj)); - - info.auth_id = *id; - info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; - info.attrs.pin.min_length = min_length; - info.attrs.pin.max_length = max_length; - info.attrs.pin.stored_length = max_length; - info.attrs.pin.type = type; - info.attrs.pin.reference = ref; - info.attrs.pin.flags = flags; - info.attrs.pin.pad_char = pad_char; - info.tries_left = tries_left; - info.logged_in = SC_PIN_STATE_UNKNOWN; - - if (path) - info.path = *path; - if (type == SC_PKCS15_PIN_TYPE_BCD) - info.attrs.pin.stored_length /= 2; - - strlcpy(obj.label, label, sizeof(obj.label)); - obj.flags = obj_flags; - - return sc_pkcs15emu_add_pin_obj(p15card, &obj, &info); -} - -static int sc_pkcs15emu_add_prkey(sc_pkcs15_card_t *p15card, - const sc_pkcs15_id_t *id, - const char *label, - int type, unsigned int modulus_length, int usage, - const sc_path_t *path, int ref, - const sc_pkcs15_id_t *auth_id, int obj_flags) -{ - sc_pkcs15_prkey_info_t info; - sc_pkcs15_object_t obj; - - memset(&info, 0, sizeof(info)); - memset(&obj, 0, sizeof(obj)); - - info.id = *id; - info.modulus_length = modulus_length; - info.usage = usage; - info.native = 1; - info.key_reference = ref; - - if (path) - info.path = *path; - - obj.flags = obj_flags; - strlcpy(obj.label, label, sizeof(obj.label)); - if (auth_id != NULL) - obj.auth_id = *auth_id; - - return sc_pkcs15emu_add_rsa_prkey(p15card, &obj, &info); -} - -static int sc_pkcs15emu_add_cert(sc_pkcs15_card_t *p15card, - int type, int authority, const sc_path_t *path, - const sc_pkcs15_id_t *id, const char *label, int obj_flags) -{ - /* const char *label = "Certificate"; */ - sc_pkcs15_cert_info_t info; - sc_pkcs15_object_t obj; - - memset(&info, 0, sizeof(info)); - memset(&obj, 0, sizeof(obj)); - - info.id = *id; - info.authority = authority; - if (path) - info.path = *path; - - strlcpy(obj.label, label, sizeof(obj.label)); - obj.flags = obj_flags; - - return sc_pkcs15emu_add_x509_cert(p15card, &obj, &info); -} -#endif - -static int sc_pkcs15emu_postecert_init(sc_pkcs15_card_t * p15card) -{ - static int prkey_usage = SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; - static int authprkey_usage = SC_PKCS15_PRKEY_USAGE_SIGN - | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER - | SC_PKCS15_PRKEY_USAGE_ENCRYPT - | SC_PKCS15_PRKEY_USAGE_DECRYPT; - - sc_card_t *card = p15card->card; - sc_path_t path; - sc_pkcs15_id_t id, auth_id; - unsigned char certlen[2]; - unsigned char *certi = NULL; - int index_cert[4]; - int count_cert[4]; - int flags; - int authority; - size_t i, count; - int r; - int o = 0; - - const char *label = "User Non-repudiation Certificate"; - const char *calabel = "CA Certificate"; - const char *catmslabel = "CA TimeStamper Certificate"; - const char *authlabel = "User Authentication Certificate"; - - const char *postecert_auth_cert_path = "504B0001"; - - const char *authPIN = "Authentication PIN"; - const char *nonrepPIN = "Non-repudiation PIN"; - - const char *authPRKEY = "Authentication Key"; - const char *nonrepPRKEY = "Non repudiation Key"; - - memset(index_cert, 0, sizeof(index_cert)); - memset(count_cert, 0, sizeof(count_cert)); - - /* Get the non-repudiation certificate length */ - sc_format_path(postecert_auth_cert_path, &path); - - if (sc_select_file(card, &path, NULL) < 0) { - r = SC_ERROR_WRONG_CARD; - goto failed; - } - - set_string(&p15card->tokeninfo->label, "Postecert & Cnipa Card"); - set_string(&p15card->tokeninfo->manufacturer_id, "Postecert"); - set_string(&p15card->tokeninfo->serial_number, "0000"); - - sc_read_binary(card, 0, certlen, 2, 0); - - /* Now set the certificate offset/len */ - count = (certlen[0] << 8) + certlen[1]; - if (count < 256) - return SC_ERROR_INTERNAL; - - certi = malloc(count); - - if (!certi) - return SC_ERROR_OUT_OF_MEMORY; - - sc_read_binary(card, 0, certi, count - 500, 0); - - for (i = 2; i < (count - 256); i++) { - /* this file contain more than one certificate */ - if (*(certi + i) == 0x30 && *(certi + i + 1) == 0x82 - && *(certi + i + 4) == 0x30 && *(certi + i + 5) == 0x82 - && *(certi + i + 2) > 1 && *(certi + i + 2) < 8 - && *(certi + i + 6) <= *(certi + i + 2)) { - index_cert[o] = i; - count_cert[o] = - (*(certi + i + 2) << 8) + *(certi + i + 3) + 4; - o++; - if (o >= 4) - break; - i += (*(certi + i + 2) << 8) + *(certi + i + 3); - } - } - - free(certi); - - path.index = index_cert[0]; - path.count = count_cert[0]; - - id.value[0] = 1; - id.len = 1; - - authority = 1; - - sc_pkcs15emu_add_cert(p15card, - SC_PKCS15_TYPE_CERT_X509, authority, - &path, &id, calabel, SC_PKCS15_CO_FLAG_MODIFIABLE); - - path.index = index_cert[1]; - path.count = count_cert[1]; - - id.value[0] = 2; - id.len = 1; - - authority = 1; - - sc_pkcs15emu_add_cert(p15card, - SC_PKCS15_TYPE_CERT_X509, authority, - &path, &id, catmslabel, SC_PKCS15_CO_FLAG_MODIFIABLE); - - path.index = index_cert[2]; - path.count = count_cert[2]; - - id.value[0] = 3; - id.len = 1; - - authority = 0; - - sc_pkcs15emu_add_cert(p15card, - SC_PKCS15_TYPE_CERT_X509, authority, - &path, &id, label, SC_PKCS15_CO_FLAG_MODIFIABLE); - - path.index = index_cert[3]; - path.count = count_cert[3]; - - id.value[0] = 4; - id.len = 1; - - sc_pkcs15emu_add_cert(p15card, - SC_PKCS15_TYPE_CERT_X509, authority, - &path, &id, authlabel, SC_PKCS15_CO_FLAG_MODIFIABLE); - - - flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | - SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_NEEDS_PADDING; - - /* add authentication PIN */ - sc_format_path("3F00504B", &path); - id.value[0] = 1; - sc_pkcs15emu_add_pin(p15card, &id, - authPIN, &path, 0x82, - SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, - 6, 14, flags, 3, 0, - SC_PKCS15_CO_FLAG_MODIFIABLE | - SC_PKCS15_CO_FLAG_PRIVATE); - - /* add authentication private key */ - id.value[0] = 4; - auth_id.value[0] = 1; - auth_id.len = 1; - sc_pkcs15emu_add_prkey(p15card, &id, - authPRKEY, - SC_PKCS15_TYPE_PRKEY_RSA, - 1024, authprkey_usage, - &path, 0x06, &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); - - /* add non repudiation PIN */ - sc_format_path("3F00504B", &path); - id.value[0] = 2; - sc_pkcs15emu_add_pin(p15card, &id, - nonrepPIN, &path, 0x82, - SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, - 6, 14, flags, 3, 0, - SC_PKCS15_CO_FLAG_MODIFIABLE | - SC_PKCS15_CO_FLAG_PRIVATE); - - - /* add non repudiation private key */ - id.value[0] = 3; - auth_id.value[0] = 2; - sc_pkcs15emu_add_prkey(p15card, &id, - nonrepPRKEY, - SC_PKCS15_TYPE_PRKEY_RSA, - 1024, prkey_usage, - &path, 0x01, &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); - - /* return to MF */ - sc_format_path("3F00", &path); - r = sc_select_file(card, &path, NULL); - if (r != SC_SUCCESS) - return r; - - { - /* save old signature funcs */ - set_security_env = card->ops->set_security_env; - /* set new one */ - card->ops->set_security_env = set_sec_env; - card->ops->compute_signature = do_sign; - } - return 0; - -failed: - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "Failed to initialize Postecert and Cnipa emulation: %s\n", - sc_strerror(r)); - return r; -} - -static int postecert_detect_card(sc_pkcs15_card_t * p15card) -{ - sc_card_t *card = p15card->card; - - /* check if we have the correct card OS */ - if (strcmp(card->name, "CardOS M4")) - return SC_ERROR_WRONG_CARD; - return SC_SUCCESS; -} - -int sc_pkcs15emu_postecert_init_ex(sc_pkcs15_card_t * p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t * opts) -{ - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - return sc_pkcs15emu_postecert_init(p15card); - else { - int r = postecert_detect_card(p15card); - if (r) - return SC_ERROR_WRONG_CARD; - return sc_pkcs15emu_postecert_init(p15card); - } -} diff --git a/src/libopensc/pkcs15-prkey.c b/src/libopensc/pkcs15-prkey.c index d3eee98356..2c9067b69f 100644 --- a/src/libopensc/pkcs15-prkey.c +++ b/src/libopensc/pkcs15-prkey.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -33,13 +33,13 @@ #include #include #include -#include -#include -#include -#if OPENSSL_VERSION_NUMBER >= 0x10000000L - #ifndef OPENSSL_NO_EC - #include - #endif +#include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# include +# include +#endif +#ifndef OPENSSL_NO_EC +#include #endif #endif @@ -50,10 +50,18 @@ #include "aux-data.h" /* - * in src/libopensc/types.h SC_MAX_SUPPORTED_ALGORITHMS defined as 8 + * in src/libopensc/types.h SC_MAX_SUPPORTED_ALGORITHMS defined as 16 */ #define C_ASN1_SUPPORTED_ALGORITHMS_SIZE (SC_MAX_SUPPORTED_ALGORITHMS + 1) static const struct sc_asn1_entry c_asn1_supported_algorithms[C_ASN1_SUPPORTED_ALGORITHMS_SIZE] = { + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, @@ -113,41 +121,18 @@ static const struct sc_asn1_entry c_asn1_prk_gostr3410_attr[C_ASN1_PRK_GOSTR3410 { NULL, 0, 0, 0, NULL, NULL } }; -#define C_ASN1_DSAKEY_I_P_ATTR_SIZE 2 -static const struct sc_asn1_entry c_asn1_dsakey_i_p_attr[C_ASN1_DSAKEY_I_P_ATTR_SIZE] = { - { "path", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, - { NULL, 0, 0, 0, NULL, NULL } -}; - -#define C_ASN1_DSAKEY_VALUE_ATTR_SIZE 3 -static const struct sc_asn1_entry c_asn1_dsakey_value_attr[C_ASN1_DSAKEY_VALUE_ATTR_SIZE] = { - { "path", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, - { "pathProtected",SC_ASN1_STRUCT, SC_ASN1_CTX | 1 | SC_ASN1_CONS, 0, NULL, NULL}, - { NULL, 0, 0, 0, NULL, NULL } -}; - -#define C_ASN1_DSAKEY_ATTR_SIZE 2 -static const struct sc_asn1_entry c_asn1_dsakey_attr[C_ASN1_DSAKEY_ATTR_SIZE] = { - { "value", SC_ASN1_CHOICE, 0, 0, NULL, NULL }, - { NULL, 0, 0, 0, NULL, NULL } -}; - -#define C_ASN1_PRK_DSA_ATTR_SIZE 2 -static const struct sc_asn1_entry c_asn1_prk_dsa_attr[C_ASN1_PRK_DSA_ATTR_SIZE] = { - { "privateDSAKeyAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, - { NULL, 0, 0, 0, NULL, NULL } -}; - /* * The element fieldSize is a proprietary extension to ISO 7816-15, providing to the middleware * the size of the underlying ECC field. This value is required for determine a proper size for * buffer allocations. The field follows the definition for modulusLength in RSA keys */ -#define C_ASN1_ECCKEY_ATTR 4 +#define C_ASN1_ECCKEY_ATTR 5 static const struct sc_asn1_entry c_asn1_ecckey_attr[C_ASN1_ECCKEY_ATTR] = { { "value", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_EMPTY_ALLOWED, NULL, NULL }, { "fieldSize", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "keyInfo", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + /* Slovenian eID card also specifies ECC curve OID */ + { "ecDomain", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL}, { NULL, 0, 0, 0, NULL, NULL } }; @@ -157,11 +142,10 @@ static const struct sc_asn1_entry c_asn1_prk_ecc_attr[C_ASN1_PRK_ECC_ATTR] = { { NULL, 0, 0, 0, NULL, NULL } }; -#define C_ASN1_PRKEY_SIZE 5 +#define C_ASN1_PRKEY_SIZE 4 static const struct sc_asn1_entry c_asn1_prkey[C_ASN1_PRKEY_SIZE] = { { "privateRSAKey", SC_ASN1_PKCS15_OBJECT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "privateECCKey", SC_ASN1_PKCS15_OBJECT, 0 | SC_ASN1_CTX | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, - { "privateDSAKey", SC_ASN1_PKCS15_OBJECT, 2 | SC_ASN1_CTX | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "privateGOSTR3410Key", SC_ASN1_PKCS15_OBJECT, 4 | SC_ASN1_CTX | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; @@ -181,10 +165,6 @@ int sc_pkcs15_decode_prkdf_entry(struct sc_pkcs15_card *p15card, struct sc_asn1_entry asn1_com_prkey_attr[C_ASN1_COM_PRKEY_ATTR_SIZE]; struct sc_asn1_entry asn1_rsakey_attr[C_ASN1_RSAKEY_ATTR_SIZE]; struct sc_asn1_entry asn1_prk_rsa_attr[C_ASN1_PRK_RSA_ATTR_SIZE]; - struct sc_asn1_entry asn1_dsakey_attr[C_ASN1_DSAKEY_ATTR_SIZE]; - struct sc_asn1_entry asn1_prk_dsa_attr[C_ASN1_PRK_DSA_ATTR_SIZE]; - struct sc_asn1_entry asn1_dsakey_i_p_attr[C_ASN1_DSAKEY_I_P_ATTR_SIZE]; - struct sc_asn1_entry asn1_dsakey_value_attr[C_ASN1_DSAKEY_VALUE_ATTR_SIZE]; struct sc_asn1_entry asn1_gostr3410key_attr[C_ASN1_GOSTR3410KEY_ATTR_SIZE]; struct sc_asn1_entry asn1_prk_gostr3410_attr[C_ASN1_PRK_GOSTR3410_ATTR_SIZE]; struct sc_asn1_entry asn1_ecckey_attr[C_ASN1_ECCKEY_ATTR]; @@ -192,19 +172,16 @@ int sc_pkcs15_decode_prkdf_entry(struct sc_pkcs15_card *p15card, struct sc_asn1_entry asn1_prkey[C_ASN1_PRKEY_SIZE]; struct sc_asn1_entry asn1_supported_algorithms[C_ASN1_SUPPORTED_ALGORITHMS_SIZE]; struct sc_asn1_pkcs15_object rsa_prkey_obj = {obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_rsa_attr}; - struct sc_asn1_pkcs15_object dsa_prkey_obj = {obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_dsa_attr}; struct sc_asn1_pkcs15_object gostr3410_prkey_obj = {obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_gostr3410_attr}; struct sc_asn1_pkcs15_object ecc_prkey_obj = { obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_ecc_attr }; + u8 ec_domain[32]; + size_t ec_domain_len = sizeof(ec_domain); sc_copy_asn1_entry(c_asn1_prkey, asn1_prkey); sc_copy_asn1_entry(c_asn1_supported_algorithms, asn1_supported_algorithms); sc_copy_asn1_entry(c_asn1_prk_rsa_attr, asn1_prk_rsa_attr); sc_copy_asn1_entry(c_asn1_rsakey_attr, asn1_rsakey_attr); - sc_copy_asn1_entry(c_asn1_prk_dsa_attr, asn1_prk_dsa_attr); - sc_copy_asn1_entry(c_asn1_dsakey_attr, asn1_dsakey_attr); - sc_copy_asn1_entry(c_asn1_dsakey_value_attr, asn1_dsakey_value_attr); - sc_copy_asn1_entry(c_asn1_dsakey_i_p_attr, asn1_dsakey_i_p_attr); sc_copy_asn1_entry(c_asn1_prk_gostr3410_attr, asn1_prk_gostr3410_attr); sc_copy_asn1_entry(c_asn1_gostr3410key_attr, asn1_gostr3410key_attr); sc_copy_asn1_entry(c_asn1_prk_ecc_attr, asn1_prk_ecc_attr); @@ -215,22 +192,15 @@ int sc_pkcs15_decode_prkdf_entry(struct sc_pkcs15_card *p15card, sc_format_asn1_entry(asn1_prkey + 0, &rsa_prkey_obj, NULL, 0); sc_format_asn1_entry(asn1_prkey + 1, &ecc_prkey_obj, NULL, 0); - sc_format_asn1_entry(asn1_prkey + 2, &dsa_prkey_obj, NULL, 0); - sc_format_asn1_entry(asn1_prkey + 3, &gostr3410_prkey_obj, NULL, 0); + sc_format_asn1_entry(asn1_prkey + 2, &gostr3410_prkey_obj, NULL, 0); sc_format_asn1_entry(asn1_prk_rsa_attr + 0, asn1_rsakey_attr, NULL, 0); - sc_format_asn1_entry(asn1_prk_dsa_attr + 0, asn1_dsakey_attr, NULL, 0); sc_format_asn1_entry(asn1_prk_gostr3410_attr + 0, asn1_gostr3410key_attr, NULL, 0); sc_format_asn1_entry(asn1_prk_ecc_attr + 0, asn1_ecckey_attr, NULL, 0); sc_format_asn1_entry(asn1_rsakey_attr + 0, &info.path, NULL, 0); sc_format_asn1_entry(asn1_rsakey_attr + 1, &info.modulus_length, NULL, 0); - sc_format_asn1_entry(asn1_dsakey_attr + 0, asn1_dsakey_value_attr, NULL, 0); - sc_format_asn1_entry(asn1_dsakey_value_attr + 0, &info.path, NULL, 0); - sc_format_asn1_entry(asn1_dsakey_value_attr + 1, asn1_dsakey_i_p_attr, NULL, 0); - sc_format_asn1_entry(asn1_dsakey_i_p_attr + 0, &info.path, NULL, 0); - sc_format_asn1_entry(asn1_gostr3410key_attr + 0, &info.path, NULL, 0); sc_format_asn1_entry(asn1_gostr3410key_attr + 1, &gostr3410_params[0], NULL, 0); sc_format_asn1_entry(asn1_gostr3410key_attr + 2, &gostr3410_params[1], NULL, 0); @@ -238,6 +208,7 @@ int sc_pkcs15_decode_prkdf_entry(struct sc_pkcs15_card *p15card, sc_format_asn1_entry(asn1_ecckey_attr + 0, &info.path, NULL, 0); sc_format_asn1_entry(asn1_ecckey_attr + 1, &info.field_length, NULL, 0); + sc_format_asn1_entry(asn1_ecckey_attr + 3, ec_domain, &ec_domain_len, 0); sc_format_asn1_entry(asn1_com_key_attr + 0, &info.id, NULL, 0); sc_format_asn1_entry(asn1_com_key_attr + 1, &info.usage, &usage_len, 0); @@ -259,45 +230,80 @@ int sc_pkcs15_decode_prkdf_entry(struct sc_pkcs15_card *p15card, r = sc_asn1_decode_choice(ctx, asn1_prkey, *buf, *buflen, buf, buflen); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) - return r; - LOG_TEST_RET(ctx, r, "PrKey DF ASN.1 decoding failed"); + goto err; + LOG_TEST_GOTO_ERR(ctx, r, "PrKey DF ASN.1 decoding failed"); if (asn1_prkey[0].flags & SC_ASN1_PRESENT) { obj->type = SC_PKCS15_TYPE_PRKEY_RSA; } else if (asn1_prkey[1].flags & SC_ASN1_PRESENT) { obj->type = SC_PKCS15_TYPE_PRKEY_EC; +#ifdef ENABLE_OPENSSL + if (!(asn1_ecckey_attr[1].flags & SC_ASN1_PRESENT) && (asn1_ecckey_attr[3].flags & SC_ASN1_PRESENT)) { + const unsigned char *p = ec_domain; + ASN1_OBJECT *object = d2i_ASN1_OBJECT(NULL, &p, ec_domain_len); + int nid; + EC_GROUP *group; + if (!object) { + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto err; + } + nid = OBJ_obj2nid(object); + ASN1_OBJECT_free(object); + if (nid == NID_undef) { + sc_log_openssl(ctx); + r = SC_ERROR_OBJECT_NOT_FOUND; + goto err; + } + group = EC_GROUP_new_by_curve_name(nid); + if (!group) { + sc_log_openssl(ctx); + r = SC_ERROR_INVALID_DATA; + goto err; + } + info.field_length = EC_GROUP_order_bits(group); + EC_GROUP_free(group); + if (!info.field_length) { + sc_log_openssl(ctx); + r = SC_ERROR_CORRUPTED_DATA; + goto err; + } + } +#endif } else if (asn1_prkey[2].flags & SC_ASN1_PRESENT) { - obj->type = SC_PKCS15_TYPE_PRKEY_DSA; - /* If the value was indirect-protected, mark the path */ - if (asn1_dsakey_i_p_attr[0].flags & SC_ASN1_PRESENT) - info.path.type = SC_PATH_TYPE_PATH_PROT; - } - else if (asn1_prkey[3].flags & SC_ASN1_PRESENT) { + /* FIXME proper handling of gost parameters without the need of + * allocating data here. this would also make sc_pkcs15_free_key_params + * obsolete */ obj->type = SC_PKCS15_TYPE_PRKEY_GOSTR3410; - assert(info.modulus_length == 0); + if (info.modulus_length != 0 || info.params.len != 0) { + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto err; + } info.modulus_length = SC_PKCS15_GOSTR3410_KEYSIZE; - assert(info.params.len == 0); info.params.len = sizeof(struct sc_pkcs15_keyinfo_gostparams); info.params.data = malloc(info.params.len); - if (info.params.data == NULL) - LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); - assert(sizeof(*keyinfo_gostparams) == info.params.len); + if (info.params.data == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } keyinfo_gostparams = info.params.data; keyinfo_gostparams->gostr3410 = gostr3410_params[0]; keyinfo_gostparams->gostr3411 = gostr3410_params[1]; keyinfo_gostparams->gost28147 = gostr3410_params[2]; } else { - sc_log(ctx, "Neither RSA or DSA or GOSTR3410 or ECC key in PrKDF entry."); - LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ASN1_OBJECT); + r = SC_ERROR_INVALID_ASN1_OBJECT; + LOG_TEST_GOTO_ERR(ctx, r, "Neither RSA or GOSTR3410 or ECC key in PrKDF entry."); } - if (!p15card->app || !p15card->app->ddo.aid.len) { + if (!p15card->app || !p15card->app->ddo.aid.len) { + if (!p15card->file_app) { + r = SC_ERROR_INTERNAL; + goto err; + } r = sc_pkcs15_make_absolute_path(&p15card->file_app->path, &info.path); if (r < 0) { - sc_pkcs15_free_key_params(&info.params); - return r; + goto err; } } else { @@ -340,14 +346,24 @@ int sc_pkcs15_decode_prkdf_entry(struct sc_pkcs15_card *p15card, obj->data = malloc(sizeof(info)); if (obj->data == NULL) { - sc_pkcs15_free_key_params(&info.params); - LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + r = SC_ERROR_OUT_OF_MEMORY; + goto err; } memcpy(obj->data, &info, sizeof(info)); sc_log(ctx, "Key Subject %s", sc_dump_hex(info.subject.value, info.subject.len)); sc_log(ctx, "Key path %s", sc_print_path(&info.path)); - return 0; + + r = SC_SUCCESS; + +err: + if (r < 0) { + /* This might have allocated something. If so, clear it now */ + free(info.subject.value); + sc_pkcs15_free_key_params(&info.params); + } + + return r; } int sc_pkcs15_encode_prkdf_entry(sc_context_t *ctx, const struct sc_pkcs15_object *obj, @@ -357,10 +373,6 @@ int sc_pkcs15_encode_prkdf_entry(sc_context_t *ctx, const struct sc_pkcs15_objec struct sc_asn1_entry asn1_com_prkey_attr[C_ASN1_COM_PRKEY_ATTR_SIZE]; struct sc_asn1_entry asn1_rsakey_attr[C_ASN1_RSAKEY_ATTR_SIZE]; struct sc_asn1_entry asn1_prk_rsa_attr[C_ASN1_PRK_RSA_ATTR_SIZE]; - struct sc_asn1_entry asn1_dsakey_attr[C_ASN1_DSAKEY_ATTR_SIZE]; - struct sc_asn1_entry asn1_prk_dsa_attr[C_ASN1_PRK_DSA_ATTR_SIZE]; - struct sc_asn1_entry asn1_dsakey_value_attr[C_ASN1_DSAKEY_VALUE_ATTR_SIZE]; - struct sc_asn1_entry asn1_dsakey_i_p_attr[C_ASN1_DSAKEY_I_P_ATTR_SIZE]; struct sc_asn1_entry asn1_gostr3410key_attr[C_ASN1_GOSTR3410KEY_ATTR_SIZE]; struct sc_asn1_entry asn1_prk_gostr3410_attr[C_ASN1_PRK_GOSTR3410_ATTR_SIZE]; struct sc_asn1_entry asn1_ecckey_attr[C_ASN1_ECCKEY_ATTR]; @@ -371,10 +383,6 @@ int sc_pkcs15_encode_prkdf_entry(sc_context_t *ctx, const struct sc_pkcs15_objec (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_rsa_attr }; - struct sc_asn1_pkcs15_object dsa_prkey_obj = { - (struct sc_pkcs15_object *) obj, asn1_com_key_attr, - asn1_com_prkey_attr, asn1_prk_dsa_attr - }; struct sc_asn1_pkcs15_object gostr3410_prkey_obj = { (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_prkey_attr, @@ -395,10 +403,6 @@ int sc_pkcs15_encode_prkdf_entry(sc_context_t *ctx, const struct sc_pkcs15_objec sc_copy_asn1_entry(c_asn1_prk_rsa_attr, asn1_prk_rsa_attr); sc_copy_asn1_entry(c_asn1_rsakey_attr, asn1_rsakey_attr); - sc_copy_asn1_entry(c_asn1_prk_dsa_attr, asn1_prk_dsa_attr); - sc_copy_asn1_entry(c_asn1_dsakey_attr, asn1_dsakey_attr); - sc_copy_asn1_entry(c_asn1_dsakey_value_attr, asn1_dsakey_value_attr); - sc_copy_asn1_entry(c_asn1_dsakey_i_p_attr, asn1_dsakey_i_p_attr); sc_copy_asn1_entry(c_asn1_prk_gostr3410_attr, asn1_prk_gostr3410_attr); sc_copy_asn1_entry(c_asn1_gostr3410key_attr, asn1_gostr3410key_attr); sc_copy_asn1_entry(c_asn1_prk_ecc_attr, asn1_prk_ecc_attr); @@ -420,21 +424,8 @@ int sc_pkcs15_encode_prkdf_entry(sc_context_t *ctx, const struct sc_pkcs15_objec sc_format_asn1_entry(asn1_ecckey_attr + 0, &prkey->path, NULL, 1); sc_format_asn1_entry(asn1_ecckey_attr + 1, &prkey->field_length, NULL, 1); break; - case SC_PKCS15_TYPE_PRKEY_DSA: - sc_format_asn1_entry(asn1_prkey + 2, &dsa_prkey_obj, NULL, 1); - sc_format_asn1_entry(asn1_prk_dsa_attr + 0, asn1_dsakey_value_attr, NULL, 1); - if (prkey->path.type != SC_PATH_TYPE_PATH_PROT) { - /* indirect: just add the path */ - sc_format_asn1_entry(asn1_dsakey_value_attr + 0, &prkey->path, NULL, 1); - } - else { - /* indirect-protected */ - sc_format_asn1_entry(asn1_dsakey_value_attr + 1, asn1_dsakey_i_p_attr, NULL, 1); - sc_format_asn1_entry(asn1_dsakey_i_p_attr + 0, &prkey->path, NULL, 1); - } - break; case SC_PKCS15_TYPE_PRKEY_GOSTR3410: - sc_format_asn1_entry(asn1_prkey + 3, &gostr3410_prkey_obj, NULL, 1); + sc_format_asn1_entry(asn1_prkey + 2, &gostr3410_prkey_obj, NULL, 1); sc_format_asn1_entry(asn1_prk_gostr3410_attr + 0, asn1_gostr3410key_attr, NULL, 1); sc_format_asn1_entry(asn1_gostr3410key_attr + 0, &prkey->path, NULL, 1); if (prkey->params.len == sizeof(*keyinfo_gostparams)) { @@ -502,27 +493,31 @@ sc_pkcs15_prkey_attrs_from_cert(struct sc_pkcs15_card *p15card, struct sc_pkcs15 key_info = (struct sc_pkcs15_prkey_info *) key_object->data; - ERR_load_ERR_strings(); - ERR_load_crypto_strings(); - sc_log(ctx, "CertValue(%"SC_FORMAT_LEN_SIZE_T"u) %p", cert_object->content.len, cert_object->content.value); - mem = BIO_new_mem_buf(cert_object->content.value, cert_object->content.len); - if (!mem) + mem = BIO_new_mem_buf(cert_object->content.value, (int)cert_object->content.len); + if (!mem) { + sc_log_openssl(ctx); LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "MEM buffer allocation error"); + } x = d2i_X509_bio(mem, NULL); - if (!x) + if (!x) { + sc_log_openssl(ctx); LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "x509 parse error"); - + } buff = OPENSSL_malloc(i2d_X509(x,NULL) + EVP_MAX_MD_SIZE); - if (!buff) + if (!buff) { + sc_log_openssl(ctx); LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "OpenSSL allocation error"); + } ptr = buff; rv = i2d_X509_NAME(X509_get_subject_name(x), &ptr); - if (rv <= 0) + if (rv <= 0) { + sc_log_openssl(ctx); LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "Get subject name error"); + } key_info->subject.value = malloc(rv); if (!key_info->subject.value) @@ -543,7 +538,6 @@ sc_pkcs15_prkey_attrs_from_cert(struct sc_pkcs15_card *p15card, struct sc_pkcs15 OPENSSL_free(buff); ERR_clear_error(); - ERR_free_strings(); if (out_key_object) *out_key_object = key_object; @@ -557,7 +551,7 @@ sc_pkcs15_prkey_attrs_from_cert(struct sc_pkcs15_card *p15card, struct sc_pkcs15 void -sc_pkcs15_free_prkey(struct sc_pkcs15_prkey *key) +sc_pkcs15_erase_prkey(struct sc_pkcs15_prkey *key) { if (!key) return; @@ -572,33 +566,35 @@ sc_pkcs15_free_prkey(struct sc_pkcs15_prkey *key) free(key->u.rsa.dmp1.data); free(key->u.rsa.dmq1.data); break; - case SC_ALGORITHM_DSA: - free(key->u.dsa.pub.data); - free(key->u.dsa.p.data); - free(key->u.dsa.q.data); - free(key->u.dsa.g.data); - free(key->u.dsa.priv.data); - break; case SC_ALGORITHM_GOSTR3410: - assert(key->u.gostr3410.d.data); free(key->u.gostr3410.d.data); break; case SC_ALGORITHM_EC: - if (key->u.ec.params.der.value) - free(key->u.ec.params.der.value); - if (key->u.ec.params.named_curve) - free(key->u.ec.params.named_curve); - if (key->u.ec.privateD.data) - free(key->u.ec.privateD.data); - if (key->u.ec.ecpointQ.value) - free(key->u.ec.ecpointQ.value); + case SC_ALGORITHM_EDDSA: + case SC_ALGORITHM_XEDDSA: + /* EC, Edwards and Montgomery use common ec params */ + sc_clear_ec_params(&key->u.ec.params); + free(key->u.ec.privateD.data); + free(key->u.ec.ecpointQ.value); break; } + sc_mem_clear(key, sizeof(*key)); } +void +sc_pkcs15_free_prkey(struct sc_pkcs15_prkey *key) +{ + if (!key) + return; + sc_pkcs15_erase_prkey(key); + free(key); +} void sc_pkcs15_free_prkey_info(sc_pkcs15_prkey_info_t *key) { + if (!key) + return; + if (key->subject.value) free(key->subject.value); @@ -639,89 +635,175 @@ sc_pkcs15_convert_prkey(struct sc_pkcs15_prkey *pkcs15_key, void *evp_key) switch (pk_type) { case EVP_PKEY_RSA: { struct sc_pkcs15_prkey_rsa *dst = &pkcs15_key->u.rsa; - RSA *src = EVP_PKEY_get1_RSA(pk); + +#if OPENSSL_VERSION_NUMBER < 0x30000000L const BIGNUM *src_n, *src_e, *src_d, *src_p, *src_q, *src_iqmp, *src_dmp1, *src_dmq1; + RSA *src = NULL; + if (!(src = EVP_PKEY_get1_RSA(pk))) + return SC_ERROR_INCOMPATIBLE_KEY; RSA_get0_key(src, &src_n, &src_e, &src_d); RSA_get0_factors(src, &src_p, &src_q); RSA_get0_crt_params(src, &src_dmp1, &src_dmq1, &src_iqmp); - +#else + BIGNUM *src_n = NULL, *src_e = NULL, *src_d = NULL, *src_p = NULL, *src_q = NULL; + BIGNUM *src_iqmp = NULL, *src_dmp1 = NULL, *src_dmq1 = NULL; + if (EVP_PKEY_get_bn_param(pk, OSSL_PKEY_PARAM_RSA_N, &src_n) != 1 || + EVP_PKEY_get_bn_param(pk, OSSL_PKEY_PARAM_RSA_E, &src_e) != 1 || + EVP_PKEY_get_bn_param(pk, OSSL_PKEY_PARAM_RSA_D, &src_d) != 1 || + EVP_PKEY_get_bn_param(pk, OSSL_PKEY_PARAM_RSA_FACTOR1, &src_p) != 1 || + EVP_PKEY_get_bn_param(pk, OSSL_PKEY_PARAM_RSA_FACTOR2, &src_q) != 1 || + EVP_PKEY_get_bn_param(pk, OSSL_PKEY_PARAM_RSA_EXPONENT1, &src_dmp1) != 1 || + EVP_PKEY_get_bn_param(pk, OSSL_PKEY_PARAM_RSA_EXPONENT2, &src_dmq1) != 1 || + EVP_PKEY_get_bn_param(pk, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, &src_iqmp) != 1) { + BN_free(src_n); + BN_free(src_e); + BN_free(src_d); + BN_free(src_p); + BN_free(src_q); + BN_free(src_dmp1); BN_free(src_dmq1); + return SC_ERROR_UNKNOWN; + } +#endif pkcs15_key->algorithm = SC_ALGORITHM_RSA; - if (!sc_pkcs15_convert_bignum(&dst->modulus, src_n) - || !sc_pkcs15_convert_bignum(&dst->exponent, src_e) - || !sc_pkcs15_convert_bignum(&dst->d, src_d) - || !sc_pkcs15_convert_bignum(&dst->p, src_p) - || !sc_pkcs15_convert_bignum(&dst->q, src_q)) + if (!sc_pkcs15_convert_bignum(&dst->modulus, src_n) || + !sc_pkcs15_convert_bignum(&dst->exponent, src_e) || + !sc_pkcs15_convert_bignum(&dst->d, src_d) || + !sc_pkcs15_convert_bignum(&dst->p, src_p) || + !sc_pkcs15_convert_bignum(&dst->q, src_q)) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + BN_free(src_n); + BN_free(src_e); + BN_free(src_d); + BN_free(src_p); + BN_free(src_q); + BN_free(src_iqmp); + BN_free(src_dmp1); + BN_free(src_dmq1); +#else + RSA_free(src); +#endif return SC_ERROR_NOT_SUPPORTED; + } if (src_iqmp && src_dmp1 && src_dmq1) { sc_pkcs15_convert_bignum(&dst->iqmp, src_iqmp); sc_pkcs15_convert_bignum(&dst->dmp1, src_dmp1); sc_pkcs15_convert_bignum(&dst->dmq1, src_dmq1); } +#if OPENSSL_VERSION_NUMBER < 0x30000000L RSA_free(src); +#else + BN_free(src_n); + BN_free(src_e); + BN_free(src_d); + BN_free(src_p); + BN_free(src_q); + BN_free(src_iqmp); + BN_free(src_dmp1); + BN_free(src_dmq1); +#endif break; } - case EVP_PKEY_DSA: { - struct sc_pkcs15_prkey_dsa *dst = &pkcs15_key->u.dsa; - DSA *src = EVP_PKEY_get1_DSA(pk); - const BIGNUM *src_pub_key, *src_p, *src_q, *src_g, *src_priv_key; - - DSA_get0_key(src, &src_pub_key, &src_priv_key); - DSA_get0_pqg(src, &src_p, &src_q, &src_g); - - pkcs15_key->algorithm = SC_ALGORITHM_DSA; - sc_pkcs15_convert_bignum(&dst->pub, src_pub_key); - sc_pkcs15_convert_bignum(&dst->p, src_p); - sc_pkcs15_convert_bignum(&dst->q, src_q); - sc_pkcs15_convert_bignum(&dst->g, src_g); - sc_pkcs15_convert_bignum(&dst->priv, src_priv_key); - DSA_free(src); - break; - } -#if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) + +#if !defined(OPENSSL_NO_EC) case NID_id_GostR3410_2001: { struct sc_pkcs15_prkey_gostr3410 *dst = &pkcs15_key->u.gostr3410; - EC_KEY *src = EVP_PKEY_get0(pk); - - assert(src); pkcs15_key->algorithm = SC_ALGORITHM_GOSTR3410; - assert(EC_KEY_get0_private_key(src)); - sc_pkcs15_convert_bignum(&dst->d, EC_KEY_get0_private_key(src)); + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const BIGNUM *src_priv_key = NULL; + EC_KEY *src = NULL; + if (!(src = EVP_PKEY_get0(pk))) + return SC_ERROR_INCOMPATIBLE_KEY; + if (!(src_priv_key = EC_KEY_get0_private_key(src))) + return SC_ERROR_INTERNAL; +#else + BIGNUM *src_priv_key = NULL; + if (EVP_PKEY_get_bn_param(pk, OSSL_PKEY_PARAM_PRIV_KEY, &src_priv_key) != 1) { + return SC_ERROR_UNKNOWN; + } +#endif + sc_pkcs15_convert_bignum(&dst->d, src_priv_key); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + EC_KEY_free(src); +#else + BN_free(src_priv_key); +#endif break; } + case EVP_PKEY_EC: { struct sc_pkcs15_prkey_ec *dst = &pkcs15_key->u.ec; - EC_KEY *src = NULL; - const EC_GROUP *grp = NULL; unsigned char buf[255]; size_t buflen = 255; int nid; - - src = EVP_PKEY_get0(pk); - assert(src); - assert(EC_KEY_get0_private_key(src)); - assert(EC_KEY_get0_public_key(src)); - pkcs15_key->algorithm = SC_ALGORITHM_EC; - - if (!sc_pkcs15_convert_bignum(&dst->privateD, EC_KEY_get0_private_key(src))) +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const EC_KEY *src = NULL; + const EC_GROUP *grp = NULL; + const BIGNUM *src_priv_key = NULL; + const EC_POINT *src_pub_key = NULL; + if (!(src = EVP_PKEY_get0_EC_KEY(pk))) return SC_ERROR_INCOMPATIBLE_KEY; + if (!(src_priv_key = EC_KEY_get0_private_key(src)) || + !(src_pub_key = EC_KEY_get0_public_key(src)) || + !(grp = EC_KEY_get0_group(src))) + return SC_ERROR_INCOMPATIBLE_KEY; + nid = EC_GROUP_get_curve_name(grp); +#else + EC_GROUP *grp = NULL; + BIGNUM *src_priv_key = NULL; + char grp_name[256]; - grp = EC_KEY_get0_group(src); - if(grp == 0) + if (EVP_PKEY_get_bn_param(pk, OSSL_PKEY_PARAM_PRIV_KEY, &src_priv_key) != 1) { + return SC_ERROR_UNKNOWN; + } + + if (EVP_PKEY_get_group_name(pk, grp_name, sizeof(grp_name), NULL) != 1) { + BN_free(src_priv_key); + return SC_ERROR_UNKNOWN; + } + if ((nid = OBJ_sn2nid(grp_name)) == 0) { + BN_free(src_priv_key); + return SC_ERROR_UNKNOWN; + } + if ((grp = EC_GROUP_new_by_curve_name(nid)) == NULL) { + BN_free(src_priv_key); + return SC_ERROR_UNKNOWN; + } +#endif + + if (!sc_pkcs15_convert_bignum(&dst->privateD, src_priv_key)) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + BN_free(src_priv_key); + EC_GROUP_free(grp); +#endif return SC_ERROR_INCOMPATIBLE_KEY; + } - /* get curve name */ - nid = EC_GROUP_get_curve_name(grp); - if(nid != 0) - dst->params.named_curve = strdup(OBJ_nid2sn(nid)); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if(nid != 0) { + const char *sn = OBJ_nid2sn(nid); + if (sn) + dst->params.named_curve = strdup(sn); + } +#else + dst->params.named_curve = strdup(grp_name); + BN_free(src_priv_key); +#endif +#if OPENSSL_VERSION_NUMBER < 0x30000000L /* Decode EC_POINT from a octet string */ - buflen = EC_POINT_point2oct(grp, (const EC_POINT *) EC_KEY_get0_public_key(src), + buflen = EC_POINT_point2oct(grp, src_pub_key, POINT_CONVERSION_UNCOMPRESSED, buf, buflen, NULL); if (!buflen) return SC_ERROR_INCOMPATIBLE_KEY; - +#else + /* Decode EC_POINT from a octet string */ + if (EVP_PKEY_get_octet_string_param(pk, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, buf, buflen, &buflen) != 1) { + return SC_ERROR_INCOMPATIBLE_KEY; + } +#endif /* copy the public key */ dst->ecpointQ.value = malloc(buflen); if (!dst->ecpointQ.value) @@ -730,15 +812,18 @@ sc_pkcs15_convert_prkey(struct sc_pkcs15_prkey *pkcs15_key, void *evp_key) dst->ecpointQ.len = buflen; /* - * In OpenSC the field_length is in bits. Not all curves are a mutiple of 8. + * In OpenSC the field_length is in bits. Not all curves are a multiple of 8. * EC_POINT_point2oct handles this and returns octstrings that can handle - * these curves. Get real field_length from OpenSSL. + * these curves. Get real field_length from OpenSSL. */ dst->params.field_length = EC_GROUP_get_degree(grp); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EC_GROUP_free(grp); +#endif /* Octetstring may need leading zeros if BN is to short */ - if (dst->privateD.len < (dst->params.field_length + 7) / 8) { - size_t d = (dst->params.field_length + 7) / 8 - dst->privateD.len; + if (dst->privateD.len < BYTES4BITS(dst->params.field_length)) { + size_t d = BYTES4BITS(dst->params.field_length) - dst->privateD.len; dst->privateD.data = realloc(dst->privateD.data, dst->privateD.len + d); if (!dst->privateD.data) @@ -752,7 +837,14 @@ sc_pkcs15_convert_prkey(struct sc_pkcs15_prkey *pkcs15_key, void *evp_key) break; } -#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) */ +#endif /* !defined(OPENSSL_NO_EC) */ +#ifdef EVP_PKEY_ED25519 + case EVP_PKEY_ED25519: { + /* TODO */ + break; + } +#endif /* EVP_PKEY_ED25519 */ + default: return SC_ERROR_NOT_SUPPORTED; } diff --git a/src/libopensc/pkcs15-pteid.c b/src/libopensc/pkcs15-pteid.c index d1b8f3bf4f..42b8412226 100644 --- a/src/libopensc/pkcs15-pteid.c +++ b/src/libopensc/pkcs15-pteid.c @@ -17,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Based on the PKCS#15 emulation layer for EstEID card by Martin Paljak * @@ -36,7 +36,7 @@ * */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -49,7 +49,6 @@ #include "pkcs15.h" static int pteid_detect_card(struct sc_card *card); -int sc_pkcs15emu_pteid_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); static int dump_ef(sc_card_t * card, const char *path, u8 * buf, size_t * buf_len) @@ -164,8 +163,7 @@ static int sc_pkcs15emu_pteid_init(sc_pkcs15_card_t * p15card) if (rv != SC_SUCCESS || !file) return SC_ERROR_INTERNAL; /* set the application DF */ - if (p15card->file_app) - free(p15card->file_app); + sc_file_free(p15card->file_app); p15card->file_app = file; /* Load TokenInfo */ @@ -175,7 +173,6 @@ static int sc_pkcs15emu_pteid_init(sc_pkcs15_card_t * p15card) sc_log(ctx, "Reading of EF.TOKENINFO failed: %d", rv); LOG_FUNC_RETURN(ctx, rv); } - memset(p15card->tokeninfo, 0, sizeof(*p15card->tokeninfo)); rv = sc_pkcs15_parse_tokeninfo(p15card->card->ctx, p15card->tokeninfo, buf, len); if (rv != SC_SUCCESS) { @@ -197,6 +194,7 @@ static int sc_pkcs15emu_pteid_init(sc_pkcs15_card_t * p15card) rv = parse_odf(buf, len, p15card); if (rv != SC_SUCCESS) { sc_log(ctx, "Decoding of ODF failed: %d", rv); + sc_pkcs15_card_clear(p15card); LOG_FUNC_RETURN(ctx, rv); } @@ -258,6 +256,13 @@ static int sc_pkcs15emu_pteid_init(sc_pkcs15_card_t * p15card) struct sc_pin_cmd_data pin_cmd_data; struct sc_pkcs15_auth_info *pin_info = (sc_pkcs15_auth_info_t *) p15_obj->data; + if (pin_info->auth_id.value[0] < 1 || pin_info->auth_id.value[0] > 3) { + sc_log(ctx, "Invalid auth_id for PIN: Value %d out of range. Skipping.", + pin_info->auth_id.value[0]); + p15_obj = p15_obj->next; + continue; + } + strlcpy(p15_obj->label, pteid_pin_names[pin_info->auth_id.value[0]-1], sizeof(p15_obj->label)); pin_info->attrs.pin.flags |= SC_PKCS15_PIN_FLAG_NEEDS_PADDING; @@ -269,7 +274,7 @@ static int sc_pkcs15emu_pteid_init(sc_pkcs15_card_t * p15card) pin_cmd_data.cmd = SC_PIN_CMD_GET_INFO; pin_cmd_data.pin_type = pin_info->attrs.pin.type; pin_cmd_data.pin_reference = pin_info->attrs.pin.reference; - rv = sc_pin_cmd(p15card->card, &pin_cmd_data, NULL); + rv = sc_pin_cmd(p15card->card, &pin_cmd_data); if (rv == SC_SUCCESS) { pin_info->tries_left = pin_cmd_data.pin1.tries_left; pin_info->logged_in = pin_cmd_data.pin1.logged_in; @@ -340,15 +345,12 @@ static int pteid_detect_card(struct sc_card *card) return SC_ERROR_WRONG_CARD; } -int sc_pkcs15emu_pteid_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid, sc_pkcs15emu_opt_t *opts) +int sc_pkcs15emu_pteid_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid) { int r=SC_SUCCESS; sc_context_t *ctx = p15card->card->ctx; LOG_FUNC_CALLED(ctx); - /* if no check flag execute unconditionally */ - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - LOG_FUNC_RETURN(ctx, sc_pkcs15emu_pteid_init(p15card)); /* check for proper card */ r = pteid_detect_card(p15card->card); if (r == SC_ERROR_WRONG_CARD) diff --git a/src/libopensc/pkcs15-pubkey.c b/src/libopensc/pkcs15-pubkey.c index 63879fca28..8d6f4d9546 100644 --- a/src/libopensc/pkcs15-pubkey.c +++ b/src/libopensc/pkcs15-pubkey.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -37,15 +37,16 @@ #ifdef ENABLE_OPENSSL #include #include -#include #include #include -#if OPENSSL_VERSION_NUMBER >= 0x10000000L +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# include +# include +#endif #ifndef OPENSSL_NO_EC #include #endif #endif -#endif #include "internal.h" #include "asn1.h" @@ -117,18 +118,6 @@ static const struct sc_asn1_entry c_asn1_ec_type_attr[C_ASN1_EC_TYPE_ATTR_SIZE] { NULL, 0, 0, 0, NULL, NULL } }; -#define C_ASN1_DSAKEY_ATTR_SIZE 2 -static const struct sc_asn1_entry c_asn1_dsakey_attr[C_ASN1_DSAKEY_ATTR_SIZE] = { - { "value", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, - { NULL, 0, 0, 0, NULL, NULL } -}; - -#define C_ASN1_DSA_TYPE_ATTR_SIZE 2 -static const struct sc_asn1_entry c_asn1_dsa_type_attr[C_ASN1_DSA_TYPE_ATTR_SIZE] = { - { "publicDSAKeyAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, - { NULL, 0, 0, 0, NULL, NULL } -}; - #define C_ASN1_GOST3410KEY_ATTR_SIZE 5 static const struct sc_asn1_entry c_asn1_gostr3410key_attr[C_ASN1_GOST3410KEY_ATTR_SIZE] = { { "value", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, @@ -144,10 +133,9 @@ static const struct sc_asn1_entry c_asn1_gostr3410_type_attr[C_ASN1_GOST3410_TYP { NULL, 0, 0, 0, NULL, NULL } }; -#define C_ASN1_PUBKEY_CHOICE_SIZE 5 +#define C_ASN1_PUBKEY_CHOICE_SIZE 4 static const struct sc_asn1_entry c_asn1_pubkey_choice[C_ASN1_PUBKEY_CHOICE_SIZE] = { { "publicRSAKey", SC_ASN1_PKCS15_OBJECT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, - { "publicDSAKey", SC_ASN1_PKCS15_OBJECT, 2 | SC_ASN1_CTX | SC_ASN1_CONS, 0, NULL, NULL }, { "publicGOSTR3410Key", SC_ASN1_PKCS15_OBJECT, 4 | SC_ASN1_CTX | SC_ASN1_CONS, 0, NULL, NULL }, { "publicECKey", SC_ASN1_PKCS15_OBJECT, 0 | SC_ASN1_CTX | SC_ASN1_CONS, 0, NULL, NULL }, /*TODO: -DEE not clear EC is needed here as look like it is for pukdf */ @@ -200,8 +188,8 @@ sc_pkcs15_decode_pubkey_direct_value(struct sc_pkcs15_card *p15card, struct sc_p LOG_TEST_RET(ctx, rv, "Failed to decode 'SPKI' direct value"); rv = sc_pkcs15_encode_pubkey(ctx, pubkey, &info->direct.raw.value, &info->direct.raw.len); - LOG_TEST_RET(ctx, rv, "Failed to endode 'RAW' direct value"); sc_pkcs15_free_pubkey(pubkey); + LOG_TEST_RET(ctx, rv, "Failed to encode 'RAW' direct value"); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); @@ -213,11 +201,10 @@ int sc_pkcs15_decode_pukdf_entry(struct sc_pkcs15_card *p15card, const u8 ** buf, size_t *buflen) { struct sc_context *ctx = p15card->card->ctx; - struct sc_pkcs15_pubkey_info info; + struct sc_pkcs15_pubkey_info *info; int r, gostr3410_params[3]; struct sc_pkcs15_keyinfo_gostparams *keyinfo_gostparams; - size_t usage_len = sizeof(info.usage); - size_t af_len = sizeof(info.access_flags); + size_t usage_len, af_len; struct sc_pkcs15_der *der = &obj->content; struct sc_asn1_entry asn1_com_key_attr[C_ASN1_COM_KEY_ATTR_SIZE]; struct sc_asn1_entry asn1_com_pubkey_attr[C_ASN1_COM_PUBKEY_ATTR_SIZE]; @@ -227,8 +214,6 @@ int sc_pkcs15_decode_pukdf_entry(struct sc_pkcs15_card *p15card, struct sc_asn1_entry asn1_eckey_value_choice[C_ASN1_ECKEY_VALUE_CHOICE_SIZE]; struct sc_asn1_entry asn1_eckey_attr[C_ASN1_ECKEY_ATTR_SIZE]; struct sc_asn1_entry asn1_ec_type_attr[C_ASN1_EC_TYPE_ATTR_SIZE]; - struct sc_asn1_entry asn1_dsakey_attr[C_ASN1_DSAKEY_ATTR_SIZE]; - struct sc_asn1_entry asn1_dsa_type_attr[C_ASN1_DSA_TYPE_ATTR_SIZE]; struct sc_asn1_entry asn1_gostr3410key_attr[C_ASN1_GOST3410KEY_ATTR_SIZE]; struct sc_asn1_entry asn1_gostr3410_type_attr[C_ASN1_GOST3410_TYPE_ATTR_SIZE]; struct sc_asn1_entry asn1_pubkey_choice[C_ASN1_PUBKEY_CHOICE_SIZE]; @@ -237,11 +222,17 @@ int sc_pkcs15_decode_pukdf_entry(struct sc_pkcs15_card *p15card, asn1_com_pubkey_attr, asn1_rsa_type_attr }; struct sc_asn1_pkcs15_object eckey_obj = { obj, asn1_com_key_attr, asn1_com_pubkey_attr, asn1_ec_type_attr }; - struct sc_asn1_pkcs15_object dsakey_obj = { obj, asn1_com_key_attr, - asn1_com_pubkey_attr, asn1_dsa_type_attr }; struct sc_asn1_pkcs15_object gostr3410key_obj = { obj, asn1_com_key_attr, asn1_com_pubkey_attr, asn1_gostr3410_type_attr }; + info = calloc(1, sizeof *info); + if (info == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + usage_len = sizeof(info->usage); + af_len = sizeof(info->access_flags); + sc_copy_asn1_entry(c_asn1_pubkey, asn1_pubkey); sc_copy_asn1_entry(c_asn1_pubkey_choice, asn1_pubkey_choice); sc_copy_asn1_entry(c_asn1_rsa_type_attr, asn1_rsa_type_attr); @@ -250,117 +241,128 @@ int sc_pkcs15_decode_pukdf_entry(struct sc_pkcs15_card *p15card, sc_copy_asn1_entry(c_asn1_ec_type_attr, asn1_ec_type_attr); sc_copy_asn1_entry(c_asn1_eckey_value_choice, asn1_eckey_value_choice); sc_copy_asn1_entry(c_asn1_eckey_attr, asn1_eckey_attr); - sc_copy_asn1_entry(c_asn1_dsa_type_attr, asn1_dsa_type_attr); - sc_copy_asn1_entry(c_asn1_dsakey_attr, asn1_dsakey_attr); sc_copy_asn1_entry(c_asn1_gostr3410_type_attr, asn1_gostr3410_type_attr); sc_copy_asn1_entry(c_asn1_gostr3410key_attr, asn1_gostr3410key_attr); sc_copy_asn1_entry(c_asn1_com_pubkey_attr, asn1_com_pubkey_attr); sc_copy_asn1_entry(c_asn1_com_key_attr, asn1_com_key_attr); - sc_format_asn1_entry(asn1_com_pubkey_attr + 0, &info.subject.value, &info.subject.len, 0); + sc_format_asn1_entry(asn1_com_pubkey_attr + 0, &info->subject.value, &info->subject.len, 0); sc_format_asn1_entry(asn1_pubkey_choice + 0, &rsakey_obj, NULL, 0); - sc_format_asn1_entry(asn1_pubkey_choice + 1, &dsakey_obj, NULL, 0); - sc_format_asn1_entry(asn1_pubkey_choice + 2, &gostr3410key_obj, NULL, 0); - sc_format_asn1_entry(asn1_pubkey_choice + 3, &eckey_obj, NULL, 0); + sc_format_asn1_entry(asn1_pubkey_choice + 1, &gostr3410key_obj, NULL, 0); + sc_format_asn1_entry(asn1_pubkey_choice + 2, &eckey_obj, NULL, 0); sc_format_asn1_entry(asn1_rsa_type_attr + 0, asn1_rsakey_attr, NULL, 0); - sc_format_asn1_entry(asn1_rsakey_value_choice + 0, &info.path, NULL, 0); + sc_format_asn1_entry(asn1_rsakey_value_choice + 0, &info->path, NULL, 0); sc_format_asn1_entry(asn1_rsakey_value_choice + 1, &der->value, &der->len, 0); sc_format_asn1_entry(asn1_rsakey_attr + 0, asn1_rsakey_value_choice, NULL, 0); - sc_format_asn1_entry(asn1_rsakey_attr + 1, &info.modulus_length, NULL, 0); + sc_format_asn1_entry(asn1_rsakey_attr + 1, &info->modulus_length, NULL, 0); sc_format_asn1_entry(asn1_ec_type_attr + 0, asn1_eckey_attr, NULL, 0); - sc_format_asn1_entry(asn1_eckey_value_choice + 0, &info.path, NULL, 0); + sc_format_asn1_entry(asn1_eckey_value_choice + 0, &info->path, NULL, 0); sc_format_asn1_entry(asn1_eckey_value_choice + 1, &der->value, &der->len, 0); sc_format_asn1_entry(asn1_eckey_attr + 0, asn1_eckey_value_choice, NULL, 0); - sc_format_asn1_entry(asn1_dsa_type_attr + 0, asn1_dsakey_attr, NULL, 0); - - sc_format_asn1_entry(asn1_dsakey_attr + 0, &info.path, NULL, 0); - sc_format_asn1_entry(asn1_gostr3410_type_attr + 0, asn1_gostr3410key_attr, NULL, 0); - sc_format_asn1_entry(asn1_gostr3410key_attr + 0, &info.path, NULL, 0); + sc_format_asn1_entry(asn1_gostr3410key_attr + 0, &info->path, NULL, 0); sc_format_asn1_entry(asn1_gostr3410key_attr + 1, &gostr3410_params[0], NULL, 0); sc_format_asn1_entry(asn1_gostr3410key_attr + 2, &gostr3410_params[1], NULL, 0); sc_format_asn1_entry(asn1_gostr3410key_attr + 3, &gostr3410_params[2], NULL, 0); - sc_format_asn1_entry(asn1_com_key_attr + 0, &info.id, NULL, 0); - sc_format_asn1_entry(asn1_com_key_attr + 1, &info.usage, &usage_len, 0); - sc_format_asn1_entry(asn1_com_key_attr + 2, &info.native, NULL, 0); - sc_format_asn1_entry(asn1_com_key_attr + 3, &info.access_flags, &af_len, 0); - sc_format_asn1_entry(asn1_com_key_attr + 4, &info.key_reference, NULL, 0); + sc_format_asn1_entry(asn1_com_key_attr + 0, &info->id, NULL, 0); + sc_format_asn1_entry(asn1_com_key_attr + 1, &info->usage, &usage_len, 0); + sc_format_asn1_entry(asn1_com_key_attr + 2, &info->native, NULL, 0); + sc_format_asn1_entry(asn1_com_key_attr + 3, &info->access_flags, &af_len, 0); + sc_format_asn1_entry(asn1_com_key_attr + 4, &info->key_reference, NULL, 0); sc_format_asn1_entry(asn1_pubkey + 0, asn1_pubkey_choice, NULL, 0); /* Fill in defaults */ - memset(&info, 0, sizeof(info)); - info.key_reference = -1; - info.native = 1; + info->key_reference = -1; + info->native = 1; memset(gostr3410_params, 0, sizeof(gostr3410_params)); r = sc_asn1_decode(ctx, asn1_pubkey, *buf, *buflen, buf, buflen); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) - return r; - LOG_TEST_RET(ctx, r, "ASN.1 decoding failed"); + goto err; + LOG_TEST_GOTO_ERR(ctx, r, "ASN.1 decoding failed"); if (asn1_pubkey_choice[0].flags & SC_ASN1_PRESENT) { obj->type = SC_PKCS15_TYPE_PUBKEY_RSA; - } else if (asn1_pubkey_choice[2].flags & SC_ASN1_PRESENT) { + } else if (asn1_pubkey_choice[1].flags & SC_ASN1_PRESENT) { obj->type = SC_PKCS15_TYPE_PUBKEY_GOSTR3410; - assert(info.modulus_length == 0); - info.modulus_length = SC_PKCS15_GOSTR3410_KEYSIZE; - assert(info.params.len == 0); - info.params.len = sizeof(struct sc_pkcs15_keyinfo_gostparams); - info.params.data = malloc(info.params.len); - if (info.params.data == NULL) - LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); - assert(sizeof(*keyinfo_gostparams) == info.params.len); - keyinfo_gostparams = info.params.data; + if (info->modulus_length != 0) + return SC_ERROR_INTERNAL; + info->modulus_length = SC_PKCS15_GOSTR3410_KEYSIZE; + if (info->params.len != 0) + return SC_ERROR_INTERNAL; + info->params.len = sizeof(struct sc_pkcs15_keyinfo_gostparams); + info->params.data = malloc(info->params.len); + if (info->params.data == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + if (sizeof(*keyinfo_gostparams) != info->params.len) + return SC_ERROR_INTERNAL; + keyinfo_gostparams = info->params.data; keyinfo_gostparams->gostr3410 = (unsigned int)gostr3410_params[0]; keyinfo_gostparams->gostr3411 = (unsigned int)gostr3410_params[1]; keyinfo_gostparams->gost28147 = (unsigned int)gostr3410_params[2]; } - else if (asn1_pubkey_choice[3].flags & SC_ASN1_PRESENT) { + else if (asn1_pubkey_choice[2].flags & SC_ASN1_PRESENT) { obj->type = SC_PKCS15_TYPE_PUBKEY_EC; } else { - obj->type = SC_PKCS15_TYPE_PUBKEY_DSA; + goto err; } - if (!p15card->app || !p15card->app->ddo.aid.len) { - r = sc_pkcs15_make_absolute_path(&p15card->file_app->path, &info.path); + if (!p15card->app || !p15card->app->ddo.aid.len) { + if (!p15card->file_app) { + r = SC_ERROR_INTERNAL; + goto err; + } + r = sc_pkcs15_make_absolute_path(&p15card->file_app->path, &info->path); if (r < 0) { - sc_pkcs15_free_key_params(&info.params); - return r; + goto err; } } else { - info.path.aid = p15card->app->ddo.aid; + info->path.aid = p15card->app->ddo.aid; } - sc_log(ctx, "PubKey path '%s'", sc_print_path(&info.path)); + sc_log(ctx, "PubKey path '%s'", sc_print_path(&info->path)); /* OpenSC 0.11.4 and older encoded "keyReference" as a negative value. Fixed in 0.11.5 we need to add a hack, so old cards continue to work. */ - if (info.key_reference < -1) - info.key_reference += 256; + if (info->key_reference < -1) + info->key_reference += 256; - obj->data = malloc(sizeof(info)); - if (obj->data == NULL) { - sc_pkcs15_free_key_params(&info.params); - LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); - } - memcpy(obj->data, &info, sizeof(info)); + obj->data = info; + info = NULL; r = sc_pkcs15_decode_pubkey_direct_value(p15card, obj); - LOG_TEST_RET(ctx, r, "Decode public key direct value failed"); + if (r < 0) { + info = obj->data; + obj->data = NULL; + } + LOG_TEST_GOTO_ERR(ctx, r, "Decode public key direct value failed"); - return 0; +err: + if (r < 0) { + sc_pkcs15_free_pubkey_info(info); + if (der->len) { + free(der->value); + /* der points to obj->content */ + obj->content.value = NULL; + obj->content.len = 0; + } + } + + LOG_FUNC_RETURN(ctx, r); } @@ -376,8 +378,6 @@ sc_pkcs15_encode_pukdf_entry(struct sc_context *ctx, const struct sc_pkcs15_obje struct sc_asn1_entry asn1_eckey_value_choice[C_ASN1_ECKEY_VALUE_CHOICE_SIZE]; struct sc_asn1_entry asn1_eckey_attr[C_ASN1_ECKEY_ATTR_SIZE]; struct sc_asn1_entry asn1_ec_type_attr[C_ASN1_EC_TYPE_ATTR_SIZE]; - struct sc_asn1_entry asn1_dsakey_attr[C_ASN1_DSAKEY_ATTR_SIZE]; - struct sc_asn1_entry asn1_dsa_type_attr[C_ASN1_DSA_TYPE_ATTR_SIZE]; struct sc_asn1_entry asn1_gostr3410key_attr[C_ASN1_GOST3410KEY_ATTR_SIZE]; struct sc_asn1_entry asn1_gostr3410_type_attr[C_ASN1_GOST3410_TYPE_ATTR_SIZE]; struct sc_asn1_entry asn1_pubkey_choice[C_ASN1_PUBKEY_CHOICE_SIZE]; @@ -390,9 +390,6 @@ sc_pkcs15_encode_pukdf_entry(struct sc_context *ctx, const struct sc_pkcs15_obje struct sc_asn1_pkcs15_object eckey_obj = { (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_pubkey_attr, asn1_ec_type_attr }; - struct sc_asn1_pkcs15_object dsakey_obj = { (struct sc_pkcs15_object *) obj, - asn1_com_key_attr, - asn1_com_pubkey_attr, asn1_dsa_type_attr }; struct sc_asn1_pkcs15_object gostr3410key_obj = { (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_pubkey_attr, asn1_gostr3410_type_attr }; @@ -409,8 +406,6 @@ sc_pkcs15_encode_pukdf_entry(struct sc_context *ctx, const struct sc_pkcs15_obje sc_copy_asn1_entry(c_asn1_ec_type_attr, asn1_ec_type_attr); sc_copy_asn1_entry(c_asn1_eckey_value_choice, asn1_eckey_value_choice); sc_copy_asn1_entry(c_asn1_eckey_attr, asn1_eckey_attr); - sc_copy_asn1_entry(c_asn1_dsa_type_attr, asn1_dsa_type_attr); - sc_copy_asn1_entry(c_asn1_dsakey_attr, asn1_dsakey_attr); sc_copy_asn1_entry(c_asn1_gostr3410_type_attr, asn1_gostr3410_type_attr); sc_copy_asn1_entry(c_asn1_gostr3410key_attr, asn1_gostr3410key_attr); sc_copy_asn1_entry(c_asn1_com_pubkey_attr, asn1_com_pubkey_attr); @@ -453,15 +448,8 @@ sc_pkcs15_encode_pukdf_entry(struct sc_context *ctx, const struct sc_pkcs15_obje sc_format_asn1_entry(asn1_rsakey_attr + 0, asn1_rsakey_value_choice, NULL, 1); sc_format_asn1_entry(asn1_rsakey_attr + 1, &pubkey->modulus_length, NULL, 1); break; - case SC_PKCS15_TYPE_PUBKEY_DSA: - sc_format_asn1_entry(asn1_pubkey_choice + 1, &dsakey_obj, NULL, 1); - - sc_format_asn1_entry(asn1_dsa_type_attr + 0, asn1_dsakey_attr, NULL, 1); - - sc_format_asn1_entry(asn1_dsakey_attr + 0, &pubkey->path, NULL, 1); - break; case SC_PKCS15_TYPE_PUBKEY_GOSTR3410: - sc_format_asn1_entry(asn1_pubkey_choice + 2, &gostr3410key_obj, NULL, 1); + sc_format_asn1_entry(asn1_pubkey_choice + 1, &gostr3410key_obj, NULL, 1); sc_format_asn1_entry(asn1_gostr3410_type_attr + 0, asn1_gostr3410key_attr, NULL, 1); @@ -477,7 +465,7 @@ sc_pkcs15_encode_pukdf_entry(struct sc_context *ctx, const struct sc_pkcs15_obje } break; case SC_PKCS15_TYPE_PUBKEY_EC: - sc_format_asn1_entry(asn1_pubkey_choice + 3, &eckey_obj, NULL, 1); + sc_format_asn1_entry(asn1_pubkey_choice + 2, &eckey_obj, NULL, 1); sc_format_asn1_entry(asn1_ec_type_attr + 0, asn1_eckey_attr, NULL, 1); @@ -527,11 +515,11 @@ sc_pkcs15_encode_pukdf_entry(struct sc_context *ctx, const struct sc_pkcs15_obje sc_log(ctx, "Key path %s", sc_print_path(&pubkey->path)); - if (spki_value) - free(spki_value); + free(spki_value); return r; } +// clang-format off #define C_ASN1_PUBLIC_KEY_SIZE 2 static struct sc_asn1_entry c_asn1_public_key[C_ASN1_PUBLIC_KEY_SIZE] = { { "publicKeyCoefficients", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, @@ -545,27 +533,29 @@ static struct sc_asn1_entry c_asn1_rsa_pub_coefficients[C_ASN1_RSA_PUB_COEFFICIE { NULL, 0, 0, 0, NULL, NULL } }; -#define C_ASN1_DSA_PUB_COEFFICIENTS_SIZE 5 -static struct sc_asn1_entry c_asn1_dsa_pub_coefficients[C_ASN1_DSA_PUB_COEFFICIENTS_SIZE] = { - { "publicKey",SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED, NULL, NULL }, - { "paramP", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED, NULL, NULL }, - { "paramQ", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED, NULL, NULL }, - { "paramG", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED, NULL, NULL }, - { NULL, 0, 0, 0, NULL, NULL }, -}; - #define C_ASN1_GOSTR3410_PUB_COEFFICIENTS_SIZE 2 static struct sc_asn1_entry c_asn1_gostr3410_pub_coefficients[C_ASN1_GOSTR3410_PUB_COEFFICIENTS_SIZE] = { { "xy", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; -#define C_ASN1_EC_POINTQ_SIZE 2 +/* PKCS15 raw uses OCTET STRING, SPKI uses BIT STRING */ +/* accept either */ +#define C_ASN1_EC_POINTQ_SIZE 3 static struct sc_asn1_entry c_asn1_ec_pointQ[C_ASN1_EC_POINTQ_SIZE] = { - { "ecpointQ", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_ALLOC, NULL, NULL }, + { "ecpointQ-OS", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, + { "ecpointQ-BS", SC_ASN1_BIT_STRING_NI, SC_ASN1_TAG_BIT_STRING, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; +/* See RFC8410 */ +#define C_ASN1_EDDSA_PUBKEY_SIZE 3 +static struct sc_asn1_entry c_asn1_eddsa_pubkey[C_ASN1_EDDSA_PUBKEY_SIZE] = { + { "ecpointQ-OS", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, + { "ecpointQ-BS", SC_ASN1_BIT_STRING_NI, SC_ASN1_TAG_BIT_STRING, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } +}; +// clang-format on int sc_pkcs15_decode_pubkey_rsa(sc_context_t *ctx, struct sc_pkcs15_pubkey_rsa *key, @@ -613,56 +603,6 @@ sc_pkcs15_encode_pubkey_rsa(sc_context_t *ctx, struct sc_pkcs15_pubkey_rsa *key, } -int -sc_pkcs15_decode_pubkey_dsa(sc_context_t *ctx, struct sc_pkcs15_pubkey_dsa *key, - const u8 *buf, size_t buflen) -{ - struct sc_asn1_entry asn1_public_key[C_ASN1_PUBLIC_KEY_SIZE]; - struct sc_asn1_entry asn1_dsa_pub_coefficients[C_ASN1_DSA_PUB_COEFFICIENTS_SIZE]; - int r; - - LOG_FUNC_CALLED(ctx); - sc_copy_asn1_entry(c_asn1_public_key, asn1_public_key); - sc_copy_asn1_entry(c_asn1_dsa_pub_coefficients, asn1_dsa_pub_coefficients); - - sc_format_asn1_entry(asn1_public_key + 0, asn1_dsa_pub_coefficients, NULL, 1); - sc_format_asn1_entry(asn1_dsa_pub_coefficients + 0, &key->pub.data, &key->pub.len, 0); - sc_format_asn1_entry(asn1_dsa_pub_coefficients + 1, &key->g.data, &key->g.len, 0); - sc_format_asn1_entry(asn1_dsa_pub_coefficients + 2, &key->p.data, &key->p.len, 0); - sc_format_asn1_entry(asn1_dsa_pub_coefficients + 3, &key->q.data, &key->q.len, 0); - - r = sc_asn1_decode(ctx, asn1_public_key, buf, buflen, NULL, NULL); - LOG_TEST_RET(ctx, r, "ASN.1 decoding failed"); - - LOG_FUNC_RETURN(ctx, SC_SUCCESS); -} - - -int -sc_pkcs15_encode_pubkey_dsa(sc_context_t *ctx, struct sc_pkcs15_pubkey_dsa *key, - u8 **buf, size_t *buflen) -{ - struct sc_asn1_entry asn1_public_key[C_ASN1_PUBLIC_KEY_SIZE]; - struct sc_asn1_entry asn1_dsa_pub_coefficients[C_ASN1_DSA_PUB_COEFFICIENTS_SIZE]; - int r; - - LOG_FUNC_CALLED(ctx); - sc_copy_asn1_entry(c_asn1_public_key, asn1_public_key); - sc_copy_asn1_entry(c_asn1_dsa_pub_coefficients, asn1_dsa_pub_coefficients); - - sc_format_asn1_entry(asn1_public_key + 0, asn1_dsa_pub_coefficients, NULL, 1); - sc_format_asn1_entry(asn1_dsa_pub_coefficients + 0, key->pub.data, &key->pub.len, 1); - sc_format_asn1_entry(asn1_dsa_pub_coefficients + 1, key->g.data, &key->g.len, 1); - sc_format_asn1_entry(asn1_dsa_pub_coefficients + 2, key->p.data, &key->p.len, 1); - sc_format_asn1_entry(asn1_dsa_pub_coefficients + 3, key->q.data, &key->q.len, 1); - - r = sc_asn1_encode(ctx, asn1_public_key, buf, buflen); - LOG_TEST_RET(ctx, r, "ASN.1 encoding failed"); - - LOG_FUNC_RETURN(ctx, SC_SUCCESS); -} - - int sc_pkcs15_decode_pubkey_gostr3410(sc_context_t *ctx, struct sc_pkcs15_pubkey_gostr3410 *key, const u8 *buf, size_t buflen) @@ -704,7 +644,8 @@ sc_pkcs15_encode_pubkey_gostr3410(sc_context_t *ctx, } /* - * We are storing the ec_pointQ as u8 string. not as DER + * We are storing the ec_pointQ as u8 string not as DER + * Will accept either BIT STRING or OCTET STRING */ int sc_pkcs15_decode_pubkey_ec(sc_context_t *ctx, @@ -712,31 +653,34 @@ sc_pkcs15_decode_pubkey_ec(sc_context_t *ctx, const u8 *buf, size_t buflen) { int r; - u8 * ecpoint_data; - size_t ecpoint_len; + u8 * ecpoint_data = NULL; + size_t ecpoint_len = 0; struct sc_asn1_entry asn1_ec_pointQ[C_ASN1_EC_POINTQ_SIZE]; LOG_FUNC_CALLED(ctx); sc_copy_asn1_entry(c_asn1_ec_pointQ, asn1_ec_pointQ); - sc_format_asn1_entry(asn1_ec_pointQ + 0, &ecpoint_data, &ecpoint_len, 1); - r = sc_asn1_decode(ctx, asn1_ec_pointQ, buf, buflen, NULL, NULL); - if (r < 0) - LOG_TEST_RET(ctx, r, "ASN.1 decoding failed"); + sc_format_asn1_entry(asn1_ec_pointQ + 0, &ecpoint_data, &ecpoint_len, 0); + sc_format_asn1_entry(asn1_ec_pointQ + 1, &ecpoint_data, &ecpoint_len, 0); + r = sc_asn1_decode_choice(ctx, asn1_ec_pointQ, buf, buflen, NULL, NULL); + if (r < 0 || ecpoint_len == 0 || ecpoint_data == NULL) { + free(ecpoint_data); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ASN1_OBJECT); + } - if (*ecpoint_data != 0x04) - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Supported only uncompressed EC pointQ value"); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "ecpoint_len:%" SC_FORMAT_LEN_SIZE_T "u", ecpoint_len); + /* if from bit string */ + if (asn1_ec_pointQ[1].flags & SC_ASN1_PRESENT) + ecpoint_len = BYTES4BITS(ecpoint_len); + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "ecpoint_len:%" SC_FORMAT_LEN_SIZE_T "u", ecpoint_len); - sc_log(ctx, "decode-EC key=%p, buf=%p, buflen=%"SC_FORMAT_LEN_SIZE_T"u", - key, buf, buflen); + if (*ecpoint_data != 0x04) { + free(ecpoint_data); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Supported only uncompressed EC pointQ value"); + } key->ecpointQ.len = ecpoint_len; key->ecpointQ.value = ecpoint_data; - /* - * Only get here if raw point is stored in pkcs15 without curve name - * spki has the curvename, so we can get the field_length - * Following only true for curves that are multiple of 8 - */ key->params.field_length = (ecpoint_len - 1)/2 * 8; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -747,21 +691,77 @@ sc_pkcs15_encode_pubkey_ec(sc_context_t *ctx, struct sc_pkcs15_pubkey_ec *key, u8 **buf, size_t *buflen) { struct sc_asn1_entry asn1_ec_pointQ[C_ASN1_EC_POINTQ_SIZE]; + size_t key_len; + /* + * PKCS15 uses RAW vs SPKI for pub key, and in raw uses OCTET STRING + * PKCS11 does not define CKA_VALUE for a pub key + * But some PKCS11 modules define a CKA_VALUE for a public key + * and PKCS11 says ECPOINT is encoded as "DER-encoding of ANSI X9.62 ECPoint value Q" + * But ANSI X9.62 (early draft at least) says encode as OCTET STRING + * IETF encodes it in SubjectPublicKeyInfo (SPKI) in BIT STRING + * For now return as OCTET STRING. + */ + + LOG_FUNC_CALLED(ctx); + sc_copy_asn1_entry(c_asn1_ec_pointQ, asn1_ec_pointQ); + + key_len = key->ecpointQ.len; + sc_format_asn1_entry(asn1_ec_pointQ + 0, key->ecpointQ.value, &key_len, 1); + + LOG_FUNC_RETURN(ctx, + sc_asn1_encode(ctx, asn1_ec_pointQ, buf, buflen)); +} + +/* + * all "ec" keys use same pubkey format, keep this external entrypoint + * keys are just byte strings. + * will accept in either BIT STRING or OCTET STRING + */ +int +sc_pkcs15_decode_pubkey_eddsa(sc_context_t *ctx, + struct sc_pkcs15_pubkey_ec *key, + const u8 *buf, size_t buflen) +{ int r; + u8 *ecpoint_data = NULL; + size_t ecpoint_len = 0; + struct sc_asn1_entry asn1_ec_pointQ[C_ASN1_EC_POINTQ_SIZE]; LOG_FUNC_CALLED(ctx); sc_copy_asn1_entry(c_asn1_ec_pointQ, asn1_ec_pointQ); - sc_format_asn1_entry(asn1_ec_pointQ + 0, key->ecpointQ.value, &key->ecpointQ.len, 1); + sc_format_asn1_entry(asn1_ec_pointQ + 0, &ecpoint_data, &ecpoint_len, 1); + sc_format_asn1_entry(asn1_ec_pointQ + 1, &ecpoint_data, &ecpoint_len, 1); + r = sc_asn1_decode_choice(ctx, asn1_ec_pointQ, buf, buflen, NULL, NULL); + if (r < 0 || ecpoint_len == 0 || ecpoint_data == NULL) { + free(ecpoint_data); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ASN1_OBJECT); + } - r = sc_asn1_encode(ctx, asn1_ec_pointQ, buf, buflen); - LOG_TEST_RET(ctx, r, "ASN.1 encoding failed"); + if (asn1_ec_pointQ[1].flags & SC_ASN1_PRESENT) + ecpoint_len = BYTES4BITS(ecpoint_len); + + key->ecpointQ.len = ecpoint_len; + key->ecpointQ.value = ecpoint_data; + key->params.field_length = ecpoint_len * 8; - sc_log(ctx, - "EC key->ecpointQ=%p:%"SC_FORMAT_LEN_SIZE_T"u *buf=%p:%"SC_FORMAT_LEN_SIZE_T"u", - key->ecpointQ.value, key->ecpointQ.len, *buf, *buflen); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } +int +sc_pkcs15_encode_pubkey_eddsa(sc_context_t *ctx, struct sc_pkcs15_pubkey_ec *key, + u8 **buf, size_t *buflen) +{ + struct sc_asn1_entry asn1_eddsa_pubkey[C_ASN1_EDDSA_PUBKEY_SIZE]; + size_t key_len; + + LOG_FUNC_CALLED(ctx); + key_len = key->ecpointQ.len; /* in bytes */ + sc_copy_asn1_entry(c_asn1_eddsa_pubkey, asn1_eddsa_pubkey); + sc_format_asn1_entry(asn1_eddsa_pubkey + 0, key->ecpointQ.value, &key_len, 1); + + LOG_FUNC_RETURN(ctx, + sc_asn1_encode(ctx, asn1_eddsa_pubkey, buf, buflen)); +} int sc_pkcs15_encode_pubkey(sc_context_t *ctx, struct sc_pkcs15_pubkey *key, @@ -769,14 +769,14 @@ sc_pkcs15_encode_pubkey(sc_context_t *ctx, struct sc_pkcs15_pubkey *key, { if (key->algorithm == SC_ALGORITHM_RSA) return sc_pkcs15_encode_pubkey_rsa(ctx, &key->u.rsa, buf, len); - if (key->algorithm == SC_ALGORITHM_DSA) - return sc_pkcs15_encode_pubkey_dsa(ctx, &key->u.dsa, buf, len); if (key->algorithm == SC_ALGORITHM_GOSTR3410) return sc_pkcs15_encode_pubkey_gostr3410(ctx, &key->u.gostr3410, buf, len); if (key->algorithm == SC_ALGORITHM_EC) return sc_pkcs15_encode_pubkey_ec(ctx, &key->u.ec, buf, len); + if (key->algorithm == SC_ALGORITHM_EDDSA || key->algorithm == SC_ALGORITHM_XEDDSA) + return sc_pkcs15_encode_pubkey_eddsa(ctx, &key->u.ec, buf, len); - sc_log(ctx, "Encoding of public key type %u not supported", key->algorithm); + sc_log(ctx, "Encoding of public key type %lu not supported", key->algorithm); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } @@ -808,7 +808,7 @@ sc_pkcs15_encode_pubkey_as_spki(sc_context_t *ctx, struct sc_pkcs15_pubkey *pubk pkey.value = NULL; pkey.len = 0; - sc_log(ctx, "Encoding public key with algorithm %i", pubkey->algorithm); + sc_log(ctx, "Encoding public key with algorithm %lu", pubkey->algorithm); if (!pubkey->alg_id) { pubkey->alg_id = calloc(1, sizeof(struct sc_algorithm_id)); if (!pubkey->alg_id) @@ -820,6 +820,8 @@ sc_pkcs15_encode_pubkey_as_spki(sc_context_t *ctx, struct sc_pkcs15_pubkey *pubk switch (pubkey->algorithm) { case SC_ALGORITHM_EC: + case SC_ALGORITHM_EDDSA: + case SC_ALGORITHM_XEDDSA: /* * most keys, but not EC have only one encoding. * For a SPKI, the ecpoint is placed directly in the @@ -835,18 +837,23 @@ sc_pkcs15_encode_pubkey_as_spki(sc_context_t *ctx, struct sc_pkcs15_pubkey *pubk r = sc_pkcs15_fix_ec_parameters(ctx, &pubkey->u.ec.params); LOG_TEST_RET(ctx, r, "failed to fix EC parameters"); - ec_params = calloc(1, sizeof(struct sc_ec_parameters)); - if (!ec_params) - LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); - ec_params->type = 1; - ec_params->der.value = calloc(pubkey->u.ec.params.der.len, 1); - if (!ec_params->der.value) { - free(ec_params); - LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + /* EDDSA and XEDDSA only have algo and no param in SPKI */ + if (pubkey->algorithm == SC_ALGORITHM_EC) { + ec_params = calloc(1, sizeof(struct sc_ec_parameters)); + if (!ec_params) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + ec_params->type = 1; + ec_params->der.value = calloc(1, pubkey->u.ec.params.der.len); + if (!ec_params->der.value) { + free(ec_params); + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + } + memcpy(ec_params->der.value, pubkey->u.ec.params.der.value, pubkey->u.ec.params.der.len); + ec_params->der.len = pubkey->u.ec.params.der.len; } - memcpy(ec_params->der.value, pubkey->u.ec.params.der.value, pubkey->u.ec.params.der.len); - ec_params->der.len = pubkey->u.ec.params.der.len; - pubkey->alg_id->params = ec_params; + /* This could have been already allocated: avoid memory leak */ + sc_asn1_clear_algorithm_id(pubkey->alg_id); + pubkey->alg_id->params = ec_params; /* NULL for EDDSA and XEDDSA */ } break; case SC_ALGORITHM_GOSTR3410: @@ -871,6 +878,7 @@ sc_pkcs15_encode_pubkey_as_spki(sc_context_t *ctx, struct sc_pkcs15_pubkey *pubk r = sc_asn1_encode(ctx, asn1_spki_key, buf, len); } + /* pkey.len == 0 is flag to not delete */ if (pkey.len && pkey.value) free(pkey.value); @@ -884,14 +892,14 @@ sc_pkcs15_decode_pubkey(sc_context_t *ctx, struct sc_pkcs15_pubkey *key, { if (key->algorithm == SC_ALGORITHM_RSA) return sc_pkcs15_decode_pubkey_rsa(ctx, &key->u.rsa, buf, len); - if (key->algorithm == SC_ALGORITHM_DSA) - return sc_pkcs15_decode_pubkey_dsa(ctx, &key->u.dsa, buf, len); if (key->algorithm == SC_ALGORITHM_GOSTR3410) return sc_pkcs15_decode_pubkey_gostr3410(ctx, &key->u.gostr3410, buf, len); if (key->algorithm == SC_ALGORITHM_EC) return sc_pkcs15_decode_pubkey_ec(ctx, &key->u.ec, buf, len); + if (key->algorithm == SC_ALGORITHM_EDDSA || key->algorithm == SC_ALGORITHM_XEDDSA) + return sc_pkcs15_decode_pubkey_eddsa(ctx, &key->u.ec, buf, len); - sc_log(ctx, "Decoding of public key type %u not supported", key->algorithm); + sc_log(ctx, "Decoding of public key type %lu not supported", key->algorithm); return SC_ERROR_NOT_SUPPORTED; } @@ -909,6 +917,7 @@ sc_pkcs15_read_pubkey(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_obj unsigned char *data = NULL; size_t len; int algorithm, r; + int private_obj; if (p15card == NULL || p15card->card == NULL || p15card->card->ops == NULL || obj == NULL || out == NULL) { @@ -923,15 +932,18 @@ sc_pkcs15_read_pubkey(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_obj case SC_PKCS15_TYPE_PUBKEY_RSA: algorithm = SC_ALGORITHM_RSA; break; - case SC_PKCS15_TYPE_PUBKEY_DSA: - algorithm = SC_ALGORITHM_DSA; - break; case SC_PKCS15_TYPE_PUBKEY_GOSTR3410: algorithm = SC_ALGORITHM_GOSTR3410; break; case SC_PKCS15_TYPE_PUBKEY_EC: algorithm = SC_ALGORITHM_EC; break; + case SC_PKCS15_TYPE_PUBKEY_EDDSA: + algorithm = SC_ALGORITHM_EDDSA; + break; + case SC_PKCS15_TYPE_PUBKEY_XEDDSA: + algorithm = SC_ALGORITHM_XEDDSA; + break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported public key type."); } @@ -939,7 +951,6 @@ sc_pkcs15_read_pubkey(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_obj pubkey = calloc(1, sizeof(struct sc_pkcs15_pubkey)); if (pubkey == NULL) { - free(data); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } pubkey->algorithm = algorithm; @@ -966,7 +977,7 @@ sc_pkcs15_read_pubkey(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_obj else if (p15card->card->ops->read_public_key) { sc_log(ctx, "Call card specific 'read-public-key' handle"); r = p15card->card->ops->read_public_key(p15card->card, algorithm, - (struct sc_path *)&info->path, info->key_reference, info->modulus_length, + (struct sc_path *)&info->path, info->key_reference, (unsigned)info->modulus_length, &data, &len); LOG_TEST_GOTO_ERR(ctx, r, "Card specific 'read-public' procedure failed."); @@ -975,10 +986,12 @@ sc_pkcs15_read_pubkey(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_obj } else if (info->path.len) { sc_log(ctx, "Read from EF and decode"); - r = sc_pkcs15_read_file(p15card, &info->path, &data, &len); + private_obj = obj->flags & SC_PKCS15_CO_FLAG_PRIVATE; + r = sc_pkcs15_read_file(p15card, &info->path, &data, &len, private_obj); LOG_TEST_GOTO_ERR(ctx, r, "Failed to read public key file."); - if (algorithm == SC_ALGORITHM_EC && *data == (SC_ASN1_TAG_SEQUENCE | SC_ASN1_TAG_CONSTRUCTED)) + if ((algorithm == SC_ALGORITHM_EC || algorithm == SC_ALGORITHM_EDDSA || algorithm == SC_ALGORITHM_XEDDSA) + && len > 0 && *data == (SC_ASN1_TAG_SEQUENCE | SC_ASN1_TAG_CONSTRUCTED)) r = sc_pkcs15_pubkey_from_spki_sequence(ctx, data, len, &pubkey); else r = sc_pkcs15_decode_pubkey(ctx, pubkey, data, len); @@ -990,10 +1003,11 @@ sc_pkcs15_read_pubkey(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_obj } err: - if (r) + if (r) { sc_pkcs15_free_pubkey(pubkey); - else + } else *out = pubkey; + free(data); LOG_FUNC_RETURN(ctx, r); } @@ -1041,18 +1055,16 @@ sc_pkcs15_pubkey_from_prvkey(struct sc_context *ctx, struct sc_pkcs15_prkey *prv if (!rv) rv = sc_pkcs15_dup_bignum(&pubkey->u.rsa.exponent, &prvkey->u.rsa.exponent); break; - case SC_ALGORITHM_DSA: - rv = sc_pkcs15_dup_bignum(&pubkey->u.dsa.pub, &prvkey->u.dsa.pub); - if (!rv) - rv = sc_pkcs15_dup_bignum(&pubkey->u.dsa.p, &prvkey->u.dsa.p); - if (!rv) - rv = sc_pkcs15_dup_bignum(&pubkey->u.dsa.q, &prvkey->u.dsa.q); - if (!rv) - rv = sc_pkcs15_dup_bignum(&pubkey->u.dsa.g, &prvkey->u.dsa.g); - break; case SC_ALGORITHM_GOSTR3410: break; case SC_ALGORITHM_EC: + case SC_ALGORITHM_EDDSA: + case SC_ALGORITHM_XEDDSA: + /* Copy pubkey */ + if (prvkey->u.ec.ecpointQ.value == NULL || prvkey->u.ec.ecpointQ.len <= 0) { + sc_pkcs15_free_pubkey(pubkey); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + } pubkey->u.ec.ecpointQ.value = malloc(prvkey->u.ec.ecpointQ.len); if (!pubkey->u.ec.ecpointQ.value) { sc_pkcs15_free_pubkey(pubkey); @@ -1083,6 +1095,8 @@ sc_pkcs15_dup_pubkey(struct sc_context *ctx, struct sc_pkcs15_pubkey *key, struc u8* alg; size_t alglen; + LOG_FUNC_CALLED(ctx); + if (!key || !out) { return SC_ERROR_INVALID_ARGUMENTS; } @@ -1098,8 +1112,10 @@ sc_pkcs15_dup_pubkey(struct sc_context *ctx, struct sc_pkcs15_pubkey *key, struc rv = sc_asn1_encode_algorithm_id(ctx, &alg, &alglen,key->alg_id, 0); if (rv == SC_SUCCESS) { pubkey->alg_id = (struct sc_algorithm_id *)calloc(1, sizeof(struct sc_algorithm_id)); - if (pubkey->alg_id == NULL) + if (pubkey->alg_id == NULL) { + free(pubkey); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + } rv = sc_asn1_decode_algorithm_id(ctx, alg, alglen, pubkey->alg_id, 0); free(alg); } @@ -1111,18 +1127,11 @@ sc_pkcs15_dup_pubkey(struct sc_context *ctx, struct sc_pkcs15_pubkey *key, struc if (!rv) rv = sc_pkcs15_dup_bignum(&pubkey->u.rsa.exponent, &key->u.rsa.exponent); break; - case SC_ALGORITHM_DSA: - rv = sc_pkcs15_dup_bignum(&pubkey->u.dsa.pub, &key->u.dsa.pub); - if (!rv) - rv = sc_pkcs15_dup_bignum(&pubkey->u.dsa.p, &key->u.dsa.p); - if (!rv) - rv = sc_pkcs15_dup_bignum(&pubkey->u.dsa.q, &key->u.dsa.q); - if (!rv) - rv = sc_pkcs15_dup_bignum(&pubkey->u.dsa.g, &key->u.dsa.g); - break; case SC_ALGORITHM_GOSTR3410: break; case SC_ALGORITHM_EC: + case SC_ALGORITHM_EDDSA: + case SC_ALGORITHM_XEDDSA: pubkey->u.ec.ecpointQ.value = malloc(key->u.ec.ecpointQ.len); if (!pubkey->u.ec.ecpointQ.value) { rv = SC_ERROR_OUT_OF_MEMORY; @@ -1131,6 +1140,12 @@ sc_pkcs15_dup_pubkey(struct sc_context *ctx, struct sc_pkcs15_pubkey *key, struc memcpy(pubkey->u.ec.ecpointQ.value, key->u.ec.ecpointQ.value, key->u.ec.ecpointQ.len); pubkey->u.ec.ecpointQ.len = key->u.ec.ecpointQ.len; + if (key->u.ec.params.named_curve) { + rv = sc_pkcs15_fix_ec_parameters(ctx, &key->u.ec.params); + if (rv) + break; + } + pubkey->u.ec.params.der.value = malloc(key->u.ec.params.der.len); if (!pubkey->u.ec.params.der.value) { rv = SC_ERROR_OUT_OF_MEMORY; @@ -1139,9 +1154,17 @@ sc_pkcs15_dup_pubkey(struct sc_context *ctx, struct sc_pkcs15_pubkey *key, struc memcpy(pubkey->u.ec.params.der.value, key->u.ec.params.der.value, key->u.ec.params.der.len); pubkey->u.ec.params.der.len = key->u.ec.params.der.len; - pubkey->u.ec.params.named_curve = strdup(key->u.ec.params.named_curve); - if (!pubkey->u.ec.params.named_curve) - rv = SC_ERROR_OUT_OF_MEMORY; + /* RFC4810 no named_curve */ + if ((key->algorithm != SC_ALGORITHM_EDDSA) && (key->algorithm != SC_ALGORITHM_XEDDSA)) { + if (key->u.ec.params.named_curve) { + pubkey->u.ec.params.named_curve = strdup(key->u.ec.params.named_curve); + if (!pubkey->u.ec.params.named_curve) + rv = SC_ERROR_OUT_OF_MEMORY; + } else { + sc_log(ctx, "named_curve parameter missing"); + rv = SC_ERROR_NOT_SUPPORTED; + } + } break; default: @@ -1171,32 +1194,18 @@ sc_pkcs15_erase_pubkey(struct sc_pkcs15_pubkey *key) } switch (key->algorithm) { case SC_ALGORITHM_RSA: - if (key->u.rsa.modulus.data) - free(key->u.rsa.modulus.data); - if (key->u.rsa.exponent.data) - free(key->u.rsa.exponent.data); - break; - case SC_ALGORITHM_DSA: - if (key->u.dsa.pub.data) - free(key->u.dsa.pub.data); - if (key->u.dsa.g.data) - free(key->u.dsa.g.data); - if (key->u.dsa.p.data) - free(key->u.dsa.p.data); - if (key->u.dsa.q.data) - free(key->u.dsa.q.data); + free(key->u.rsa.modulus.data); + free(key->u.rsa.exponent.data); break; case SC_ALGORITHM_GOSTR3410: - if (key->u.gostr3410.xy.data) - free(key->u.gostr3410.xy.data); + free(key->u.gostr3410.xy.data); break; case SC_ALGORITHM_EC: - if (key->u.ec.params.der.value) - free(key->u.ec.params.der.value); - if (key->u.ec.params.named_curve) - free(key->u.ec.params.named_curve); - if (key->u.ec.ecpointQ.value) - free(key->u.ec.ecpointQ.value); + case SC_ALGORITHM_EDDSA: + case SC_ALGORITHM_XEDDSA: + free(key->u.ec.params.der.value); + free(key->u.ec.params.named_curve); + free(key->u.ec.ecpointQ.value); break; } sc_mem_clear(key, sizeof(*key)); @@ -1216,14 +1225,13 @@ sc_pkcs15_free_pubkey(struct sc_pkcs15_pubkey *key) void sc_pkcs15_free_pubkey_info(sc_pkcs15_pubkey_info_t *info) { - if (info->subject.value) + if (info) { free(info->subject.value); - if (info->direct.spki.value) free(info->direct.spki.value); - if (info->direct.raw.value) free(info->direct.raw.value); - sc_pkcs15_free_key_params(&info->params); - free(info); + sc_pkcs15_free_key_params(&info->params); + free(info); + } } @@ -1234,12 +1242,13 @@ sc_pkcs15_read_der_file(sc_context_t *ctx, char * filename, int r; int f = -1; size_t len, offs; - u8 tagbuf[1024]; /* enough to read in the tag and length */ + u8 tagbuf[16]; /* enough to read in the tag and length */ u8 * rbuf = NULL; size_t rbuflen = 0; const u8 * body = NULL; size_t bodylen; unsigned int cla_out, tag_out; + ssize_t sz; LOG_FUNC_CALLED(ctx); if (!buf || !buflen) @@ -1254,20 +1263,20 @@ sc_pkcs15_read_der_file(sc_context_t *ctx, char * filename, goto out; } - r = read(f, tagbuf, sizeof(tagbuf)); /* get tag and length */ - if (r < 2) { + sz = read(f, tagbuf, sizeof(tagbuf)); /* get tag and length */ + if (sz < 2) { sc_log(ctx, "Problem with '%s'", filename); r = SC_ERROR_DATA_OBJECT_NOT_FOUND; goto out; } - len = r; + len = sz; body = tagbuf; r = sc_asn1_read_tag(&body, len, &cla_out, &tag_out, &bodylen); - if (r != SC_SUCCESS) + if (r != SC_SUCCESS && r != SC_ERROR_ASN1_END_OF_CONTENTS) goto out; - if (tag_out == SC_ASN1_TAG_EOC || body == NULL) { + if (body == NULL) { r = SC_SUCCESS; goto out; } @@ -1284,11 +1293,12 @@ sc_pkcs15_read_der_file(sc_context_t *ctx, char * filename, r = SC_ERROR_OUT_OF_MEMORY; goto out; } + len = MIN(len, rbuflen); memcpy(rbuf, tagbuf, len); /* copy first or only part */ if (rbuflen > len) { /* read rest of file */ - r = read(f, rbuf + len, rbuflen - len); - if (r < (int)(rbuflen - len)) { + sz = read(f, rbuf + len, rbuflen - len); + if (sz < (ssize_t)(rbuflen - len)) { r = SC_ERROR_INVALID_ASN1_OBJECT; free (rbuf); rbuf = NULL; @@ -1298,7 +1308,7 @@ sc_pkcs15_read_der_file(sc_context_t *ctx, char * filename, *buflen = rbuflen; *buf = rbuf; rbuf = NULL; - r = rbuflen; + r = (int)rbuflen; out: if (f >= 0) close(f); @@ -1326,6 +1336,10 @@ sc_pkcs15_pubkey_from_spki_fields(struct sc_context *ctx, struct sc_pkcs15_pubke "sc_pkcs15_pubkey_from_spki_fields() called: %p:%"SC_FORMAT_LEN_SIZE_T"u\n%s", buf, buflen, sc_dump_hex(buf, buflen)); + if (buflen < 1) { + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "subjectPublicKeyInfo can not be empty"); + } + tmp_buf = malloc(buflen); if (!tmp_buf) { r = SC_ERROR_OUT_OF_MEMORY; @@ -1333,7 +1347,7 @@ sc_pkcs15_pubkey_from_spki_fields(struct sc_context *ctx, struct sc_pkcs15_pubke } memcpy(tmp_buf, buf, buflen); - if ((*tmp_buf & SC_ASN1_TAG_CONTEXT)) + if (buflen > 0 && (*tmp_buf & SC_ASN1_TAG_CONTEXT)) *tmp_buf = SC_ASN1_TAG_CONSTRUCTED | SC_ASN1_TAG_SEQUENCE; memset(&pk_alg, 0, sizeof(pk_alg)); @@ -1342,7 +1356,6 @@ sc_pkcs15_pubkey_from_spki_fields(struct sc_context *ctx, struct sc_pkcs15_pubke r = SC_ERROR_OUT_OF_MEMORY; LOG_TEST_GOTO_ERR(ctx, r, ""); } - *outpubkey = pubkey; sc_copy_asn1_entry(c_asn1_pkinfo, asn1_pkinfo); @@ -1350,7 +1363,10 @@ sc_pkcs15_pubkey_from_spki_fields(struct sc_context *ctx, struct sc_pkcs15_pubke sc_format_asn1_entry(asn1_pkinfo + 1, &pk.value, &pk.len, 0); r = sc_asn1_decode(ctx, asn1_pkinfo, tmp_buf, buflen, NULL, NULL); - LOG_TEST_GOTO_ERR(ctx, r, "ASN.1 parsing of subjectPubkeyInfo failed"); + if (r != SC_SUCCESS) { + sc_asn1_clear_algorithm_id(&pk_alg); + LOG_TEST_GOTO_ERR(ctx, r, "ASN.1 parsing of subjectPubkeyInfo failed"); + } pubkey->alg_id = calloc(1, sizeof(struct sc_algorithm_id)); if (pubkey->alg_id == NULL) { @@ -1361,9 +1377,11 @@ sc_pkcs15_pubkey_from_spki_fields(struct sc_context *ctx, struct sc_pkcs15_pubke memcpy(pubkey->alg_id, &pk_alg, sizeof(struct sc_algorithm_id)); pubkey->algorithm = pk_alg.algorithm; pk_alg.params = NULL; - sc_log(ctx, "DEE pk_alg.algorithm=%d", pk_alg.algorithm); + sc_log(ctx, "DEE pk_alg.algorithm=%lu", pk_alg.algorithm); - pk.len = (pk.len + 7)/8; /* convert number of bits to bytes */ + if (pk.len == 0) + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_INTERNAL, "Incorrect length of key"); + pk.len = BYTES4BITS(pk.len); /* convert number of bits to bytes */ if (pk_alg.algorithm == SC_ALGORITHM_EC) { /* EC public key is not encapsulated into BIT STRING -- it's a BIT STRING */ @@ -1389,22 +1407,45 @@ sc_pkcs15_pubkey_from_spki_fields(struct sc_context *ctx, struct sc_pkcs15_pubke } pubkey->u.ec.ecpointQ.value = malloc(pk.len); - if (pubkey->u.ec.ecpointQ.value == NULL) - LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + if (pubkey->u.ec.ecpointQ.value == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "failed to malloc() memory"); + } memcpy(pubkey->u.ec.ecpointQ.value, pk.value, pk.len); pubkey->u.ec.ecpointQ.len = pk.len; - } - else { + } else if (pk_alg.algorithm == SC_ALGORITHM_EDDSA || + pk_alg.algorithm == SC_ALGORITHM_XEDDSA) { + /* + * SPKI will have OID, EDDSA can have ED25519 or ED448 with different sizes + * EDDSA/XEDDSA public key is not encapsulated into BIT STRING -- it's a BIT STRING + * no params, but oid is the params. + */ + r = sc_encode_oid(ctx, &pk_alg.oid, &pubkey->u.ec.params.der.value, &pubkey->u.ec.params.der.len); + LOG_TEST_GOTO_ERR(ctx, r, "failed to encode (X)EDDSA oid"); + r = sc_pkcs15_fix_ec_parameters(ctx, &pubkey->u.ec.params); + LOG_TEST_GOTO_ERR(ctx, r, "failed to fix EC parameters"); + + pubkey->u.ec.ecpointQ.value = malloc(pk.len); + if (pubkey->u.ec.ecpointQ.value == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "failed to malloc() memory"); + } + + memcpy(pubkey->u.ec.ecpointQ.value, pk.value, pk.len); + pubkey->u.ec.ecpointQ.len = pk.len; + } else { /* Public key is expected to be encapsulated into BIT STRING */ r = sc_pkcs15_decode_pubkey(ctx, pubkey, pk.value, pk.len); LOG_TEST_GOTO_ERR(ctx, r, "ASN.1 parsing of subjectPubkeyInfo failed"); } + *outpubkey = pubkey; + pubkey = NULL; + err: - if (pk.value) - free(pk.value); - if (tmp_buf) - free(tmp_buf); + sc_pkcs15_free_pubkey(pubkey); + free(pk.value); + free(tmp_buf); LOG_FUNC_RETURN(ctx, r); } @@ -1425,9 +1466,10 @@ sc_pkcs15_pubkey_from_spki_sequence(struct sc_context *ctx, const unsigned char r = sc_asn1_decode(ctx, asn1_spki, buf, buflen, NULL, NULL); LOG_TEST_RET(ctx, r, "ASN.1 cannot parse subjectPublicKeyInfo"); - if(outpubkey) + if(outpubkey) { + free(*outpubkey); *outpubkey = pubkey; - else + } else free(pubkey); LOG_FUNC_RETURN(ctx, r); @@ -1448,82 +1490,139 @@ sc_pkcs15_pubkey_from_spki_file(struct sc_context *ctx, char * filename, LOG_TEST_RET(ctx, r, "Cannot read SPKI DER file"); r = sc_pkcs15_pubkey_from_spki_sequence(ctx, buf, buflen, outpubkey); - if (buf) - free(buf); + free(buf); LOG_FUNC_RETURN(ctx, r); } - +// clang-format off static struct ec_curve_info { const char *name; const char *oid_str; - const char *oid_encoded; + const struct sc_pkcs15_der oid_der; size_t size; + const unsigned int key_type; + } ec_curve_infos[] = { - {"secp192r1", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192}, - {"prime192v1", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192}, - {"nistp192", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192}, - {"ansiX9p192r1", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192}, - - {"secp224r1", "1.3.132.0.33", "06052b81040021", 224}, - {"nistp224", "1.3.132.0.33", "06052b81040021", 224}, - - {"secp256r1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, - {"prime256v1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, - {"nistp256", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, - {"ansiX9p256r1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, - - {"secp384r1", "1.3.132.0.34", "06052B81040022", 384}, - {"prime384v1", "1.3.132.0.34", "06052B81040022", 384}, - {"nistp384", "1.3.132.0.34", "06052B81040022", 384}, - {"ansiX9p384r1", "1.3.132.0.34", "06052B81040022", 384}, - - {"secp521r1", "1.3.132.0.35", "06052B81040023", 521}, - {"nistp521", "1.3.132.0.35", "06052B81040023", 521}, - - {"brainpoolP192r1", "1.3.36.3.3.2.8.1.1.3", "06092B2403030208010103", 192}, - {"brainpoolP224r1", "1.3.36.3.3.2.8.1.1.5", "06092B2403030208010105", 224}, - {"brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7", "06092B2403030208010107", 256}, - {"brainpoolP320r1", "1.3.36.3.3.2.8.1.1.9", "06092B2403030208010109", 320}, - - {"secp192k1", "1.3.132.0.31", "06052B8104001F", 192}, - {"secp256k1", "1.3.132.0.10", "06052B8104000A", 256}, - {NULL, NULL, NULL, 0}, + {"secp192r1", "1.2.840.10045.3.1.1", {(u8 *)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x01", 10}, 192, SC_ALGORITHM_EC}, + {"prime192v1", "1.2.840.10045.3.1.1", {(u8 *)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x01", 10}, 192, SC_ALGORITHM_EC}, + {"nistp192", "1.2.840.10045.3.1.1", {(u8 *)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x01", 10}, 192, SC_ALGORITHM_EC}, + {"ansiX9p192r1", "1.2.840.10045.3.1.1", {(u8 *)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x01", 10}, 192, SC_ALGORITHM_EC}, + + {"secp224r1", "1.3.132.0.33", {(u8 *)"\x06\x05\x2b\x81\x04\x00\x21", 7}, 224, SC_ALGORITHM_EC}, + {"nistp224", "1.3.132.0.33", {(u8 *)"\x06\x05\x2b\x81\x04\x00\x21", 7}, 224, SC_ALGORITHM_EC}, + + {"prime256v1", "1.2.840.10045.3.1.7", {(u8 *)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x07", 10}, 256, SC_ALGORITHM_EC}, + {"secp256r1", "1.2.840.10045.3.1.7", {(u8 *)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x07", 10}, 256, SC_ALGORITHM_EC}, + {"nistp256", "1.2.840.10045.3.1.7", {(u8 *)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x07", 10}, 256, SC_ALGORITHM_EC}, + {"ansiX9p256r1", "1.2.840.10045.3.1.7", {(u8 *)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x07", 10}, 256, SC_ALGORITHM_EC}, + + {"secp384r1", "1.3.132.0.34", {(u8 *)"\x06\x05\x2B\x81\x04\x00\x22", 7}, 384, SC_ALGORITHM_EC}, + {"prime384v1", "1.3.132.0.34", {(u8 *)"\x06\x05\x2B\x81\x04\x00\x22", 7}, 384, SC_ALGORITHM_EC}, + {"nistp384", "1.3.132.0.34", {(u8 *)"\x06\x05\x2B\x81\x04\x00\x22", 7}, 384, SC_ALGORITHM_EC}, + {"ansiX9p384r1", "1.3.132.0.34", {(u8 *)"\x06\x05\x2B\x81\x04\x00\x22", 7}, 384, SC_ALGORITHM_EC}, + + {"secp521r1", "1.3.132.0.35", {(u8 *)"\x06\x05\x2B\x81\x04\x00\x23", 7}, 521, SC_ALGORITHM_EC}, + {"nistp521", "1.3.132.0.35", {(u8 *)"\x06\x05\x2B\x81\x04\x00\x23", 7}, 521, SC_ALGORITHM_EC}, + + {"brainpoolP192r1", "1.3.36.3.3.2.8.1.1.3", {(u8 *)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x03", 11}, 192, SC_ALGORITHM_EC}, + {"brainpoolP224r1", "1.3.36.3.3.2.8.1.1.5", {(u8 *)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x05", 11}, 224, SC_ALGORITHM_EC}, + {"brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7", {(u8 *)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x07", 11}, 256, SC_ALGORITHM_EC}, + {"brainpoolP320r1", "1.3.36.3.3.2.8.1.1.9", {(u8 *)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x09", 11}, 320, SC_ALGORITHM_EC}, + {"brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11", {(u8 *)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x0B", 11}, 384, SC_ALGORITHM_EC}, + {"brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13", {(u8 *)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x0D", 11}, 512, SC_ALGORITHM_EC}, + + {"brainpoolP192t1", "1.3.36.3.3.2.8.1.1.4", {(u8 *)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x04", 11}, 192, SC_ALGORITHM_EC}, + {"brainpoolP224t1", "1.3.36.3.3.2.8.1.1.6", {(u8 *)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x06", 11}, 224, SC_ALGORITHM_EC}, + {"brainpoolP256t1", "1.3.36.3.3.2.8.1.1.8", {(u8 *)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x08", 11}, 256, SC_ALGORITHM_EC}, + {"brainpoolP320t1", "1.3.36.3.3.2.8.1.1.10", {(u8 *)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x0A", 11}, 320, SC_ALGORITHM_EC}, + {"brainpoolP384t1", "1.3.36.3.3.2.8.1.1.12", {(u8 *)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x0C", 11}, 384, SC_ALGORITHM_EC}, + {"brainpoolP512t1", "1.3.36.3.3.2.8.1.1.14", {(u8 *)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x0E", 11}, 512, SC_ALGORITHM_EC}, + + {"secp192k1", "1.3.132.0.31", {(u8 *)"\x06\x05\x2B\x81\x04\x00\x1F", 7}, 192, SC_ALGORITHM_EC}, + {"secp256k1", "1.3.132.0.10", {(u8 *)"\x06\x05\x2B\x81\x04\x00\x0A", 7}, 256, SC_ALGORITHM_EC}, + + /* OpenPGP extensions by Yubikey and GNUK are not defined in RFCs but we know the oid written to card */ + + {"edwards25519", "1.3.6.1.4.1.11591.15.1", {(u8 *)"\x06\x09\x2B\x06\x01\x04\x01\xDA\x47\x0F\x01", 11}, 255, SC_ALGORITHM_EDDSA}, + {"curve25519", "1.3.6.1.4.1.3029.1.5.1", {(u8 *)"\x06\x0A\x2B\x06\x01\x04\x01\x97\x55\x01\x05\x01", 12}, 255, SC_ALGORITHM_XEDDSA}, + + /* RFC 8410 defined curves */ + {"X25519", "1.3.101.110", {(u8 *)"\x06\x03\x2b\x65\x6e", 5}, 255, SC_ALGORITHM_XEDDSA}, + {"X448", "1.3.101.111", {(u8 *)"\x06\x03\x2b\x65\x6f", 5}, 448, SC_ALGORITHM_XEDDSA}, + {"Ed25519", "1.3.101.112", {(u8 *)"\x06\x03\x2b\x65\x70", 5}, 255, SC_ALGORITHM_EDDSA}, + {"Ed448", "1.3.101.113", {(u8 *)"\x06\x03\x2b\x65\x71", 5}, 448, SC_ALGORITHM_EDDSA}, + /* GnuPG openpgp curves as used in gnupg-card are equivalent to RFC8410 OIDs */ + {"cv25519", "1.3.101.110", {(u8 *)"\x06\x03\x2b\x65\x6e", 5}, 255, SC_ALGORITHM_XEDDSA}, + {"ed25519", "1.3.101.112", {(u8 *)"\x06\x03\x2b\x65\x70", 5}, 255, SC_ALGORITHM_EDDSA}, + + {NULL, NULL, {NULL, 0}, 0, 0}, /* Do not touch this */ }; - +// clang-format on int sc_pkcs15_fix_ec_parameters(struct sc_context *ctx, struct sc_ec_parameters *ecparams) { int rv, ii; + int mapped_string = 0; /* der is printable string that can be replaced with der of OID */ LOG_FUNC_CALLED(ctx); /* In PKCS#11 EC parameters arrives in DER encoded form */ - if (ecparams->der.value && ecparams->der.len) { - for (ii=0; ec_curve_infos[ii].name; ii++) { - struct sc_object_id id; - unsigned char *buf = NULL; - size_t len = 0; + if (ecparams->der.value && ecparams->der.len && ecparams->der.len > 2) { - sc_format_oid(&id, ec_curve_infos[ii].oid_str); - sc_encode_oid (ctx, &id, &buf, &len); + switch (ecparams->der.value[0]) { + case 0x06: /* der.value is an OID */ + for (ii = 0; ec_curve_infos[ii].name; ii++) { + size_t len = ec_curve_infos[ii].oid_der.len; - if (ecparams->der.len == len && !memcmp(ecparams->der.value, buf, len)) { - free(buf); - break; + if (ecparams->der.len == len && + memcmp(ecparams->der.value, ec_curve_infos[ii].oid_der.value, len) == 0) + break; /* found ec_curve_infos[ii] */ } + break; + + case 0x13: + /* printable string as per PKCS11 V 3.0 for experimental curves */ + { + int r_tag; + const u8 *body = ecparams->der.value; + size_t len = ecparams->der.len; + unsigned int cla_out, tag_out; + size_t bodylen; + + r_tag = sc_asn1_read_tag(&body, len, &cla_out, &tag_out, &bodylen); + if (r_tag != SC_SUCCESS || tag_out != 0x13) { + sc_log(ctx, "Invalid printable string"); + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + } + for (ii = 0; ec_curve_infos[ii].name; ii++) { + size_t len = strlen(ec_curve_infos[ii].name); + if (bodylen != len || memcmp(ec_curve_infos[ii].name, body, len) != 0) + continue; + /* found replacement of printable string to OID */ + mapped_string = 1; + break; + } + } + break; - free(buf); + default: + sc_log(ctx, "Unsupported ec params"); + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } - /* TODO: support of explicit EC parameters form */ - if (!ec_curve_infos[ii].name) + if (ec_curve_infos[ii].name == NULL) /* end of ec_curve_info */ LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported named curve"); + /* ii points to entry with matching oid_der or a mapped entry with replacement oid_der */ sc_log(ctx, "Found known curve '%s'", ec_curve_infos[ii].name); - if (!ecparams->named_curve) { + if (mapped_string) { /* free previous name if any replace below with new name */ + free(ecparams->named_curve); + ecparams->named_curve = NULL; + } + + if (!ecparams->named_curve) { /* if present,keep the name as some curves have multiple names */ ecparams->named_curve = strdup(ec_curve_infos[ii].name); if (!ecparams->named_curve) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); @@ -1531,21 +1630,33 @@ sc_pkcs15_fix_ec_parameters(struct sc_context *ctx, struct sc_ec_parameters *ecp sc_log(ctx, "Curve name: '%s'", ecparams->named_curve); } - if (!sc_valid_oid(&ecparams->id)) - sc_format_oid(&ecparams->id, ec_curve_infos[ii].oid_str); + /* fill in object_id based on oid_der */ + sc_format_oid(&ecparams->id, ec_curve_infos[ii].oid_str); ecparams->field_length = ec_curve_infos[ii].size; - sc_log(ctx, "Curve length %"SC_FORMAT_LEN_SIZE_T"u", - ecparams->field_length); - } - else if (ecparams->named_curve) { /* it can be name of curve or OID in ASCII form */ - for (ii=0; ec_curve_infos[ii].name; ii++) { + ecparams->key_type = ec_curve_infos[ii].key_type; + sc_log(ctx, "Curve length %" SC_FORMAT_LEN_SIZE_T "u key_type %d", + ecparams->field_length, ecparams->key_type); + if (mapped_string) { + /* replace the printable string version with the oid */ + if (ecparams->der.value) + free(ecparams->der.value); + ecparams->der.len = ec_curve_infos[ii].oid_der.len; + ecparams->der.value = malloc(ecparams->der.len); + if (ecparams->der.value == NULL) { + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + } + memcpy(ecparams->der.value, ec_curve_infos[ii].oid_der.value, ecparams->der.len); + } + } else if (ecparams->named_curve) { /* it can be name of curve or OID in ASCII form */ + /* caller did not provide an OID, look for a name or oid_string */ + for (ii = 0; ec_curve_infos[ii].name; ii++) { if (!strcmp(ec_curve_infos[ii].name, ecparams->named_curve)) break; if (!strcmp(ec_curve_infos[ii].oid_str, ecparams->named_curve)) break; } - if (!ec_curve_infos[ii].name) { + if (!ec_curve_infos[ii].name) { sc_log(ctx, "Named curve '%s' not supported", ecparams->named_curve); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } @@ -1554,15 +1665,20 @@ sc_pkcs15_fix_ec_parameters(struct sc_context *ctx, struct sc_ec_parameters *ecp LOG_TEST_RET(ctx, rv, "Invalid OID format"); ecparams->field_length = ec_curve_infos[ii].size; - - if (!ecparams->der.value || !ecparams->der.len) { + ecparams->key_type = ec_curve_infos[ii].key_type; + sc_log(ctx, "Curve length %" SC_FORMAT_LEN_SIZE_T "u key_type %d", + ecparams->field_length, ecparams->key_type); + + if (ecparams->der.value == NULL || ecparams->der.len == 0) { + free(ecparams->der.value); /* just in case */ + ecparams->der.value = NULL; + ecparams->der.len = 0; + /* if caller did not provide valid der OID, fill in */ rv = sc_encode_oid (ctx, &ecparams->id, &ecparams->der.value, &ecparams->der.len); LOG_TEST_RET(ctx, rv, "Cannot encode object ID"); } - } - else if (sc_valid_oid(&ecparams->id)) { + } else LOG_TEST_RET(ctx, SC_ERROR_NOT_IMPLEMENTED, "EC parameters has to be presented as a named curve or explicit data"); - } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -1579,50 +1695,100 @@ sc_pkcs15_convert_pubkey(struct sc_pkcs15_pubkey *pkcs15_key, void *evp_key) switch (pk_type) { case EVP_PKEY_RSA: { struct sc_pkcs15_pubkey_rsa *dst = &pkcs15_key->u.rsa; - RSA *src = EVP_PKEY_get1_RSA(pk); + /* Get parameters */ +#if OPENSSL_VERSION_NUMBER < 0x30000000L const BIGNUM *src_n, *src_e; - + RSA *src = NULL; + if (!(src = EVP_PKEY_get1_RSA(pk))) + return SC_ERROR_INCOMPATIBLE_KEY; RSA_get0_key(src, &src_n, &src_e, NULL); - + if (!src_n || !src_e) { + RSA_free(src); + return SC_ERROR_INTERNAL; + } +#else + BIGNUM *src_n = NULL, *src_e = NULL; + if (EVP_PKEY_get_bn_param(pk, OSSL_PKEY_PARAM_RSA_N, &src_n) != 1 || + EVP_PKEY_get_bn_param(pk, OSSL_PKEY_PARAM_RSA_E, &src_e) != 1) { + BN_free(src_n); + return SC_ERROR_INTERNAL; + } +#endif + /* Convert */ pkcs15_key->algorithm = SC_ALGORITHM_RSA; - if (!sc_pkcs15_convert_bignum(&dst->modulus, src_n) || !sc_pkcs15_convert_bignum(&dst->exponent, src_e)) + if (!sc_pkcs15_convert_bignum(&dst->modulus, src_n) || + !sc_pkcs15_convert_bignum(&dst->exponent, src_e)) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA_free(src); +#else + BN_free(src_n); BN_free(src_e); +#endif return SC_ERROR_INVALID_DATA; + } + +#if OPENSSL_VERSION_NUMBER < 0x30000000L RSA_free(src); +#else + BN_free(src_n); BN_free(src_e); +#endif break; } - case EVP_PKEY_DSA: { - struct sc_pkcs15_pubkey_dsa *dst = &pkcs15_key->u.dsa; - DSA *src = EVP_PKEY_get1_DSA(pk); - const BIGNUM *src_pub_key, *src_priv_key, *src_p, *src_q, *src_g; - - DSA_get0_key(src, &src_pub_key, &src_priv_key); - DSA_get0_pqg(src, &src_p, &src_q, &src_g); - - pkcs15_key->algorithm = SC_ALGORITHM_DSA; - sc_pkcs15_convert_bignum(&dst->pub, src_pub_key); - sc_pkcs15_convert_bignum(&dst->p, src_p); - sc_pkcs15_convert_bignum(&dst->q, src_q); - sc_pkcs15_convert_bignum(&dst->g, src_g); - DSA_free(src); - break; - } -#if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) +#if !defined(OPENSSL_NO_EC) case NID_id_GostR3410_2001: { struct sc_pkcs15_pubkey_gostr3410 *dst = &pkcs15_key->u.gostr3410; - EC_KEY *eckey = EVP_PKEY_get0(pk); - const EC_POINT *point; BIGNUM *X, *Y; int r = 0; - - assert(eckey); - point = EC_KEY_get0_public_key(eckey); - if (!point) +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const EC_KEY *eckey = NULL; + const EC_POINT *point = NULL; + const EC_GROUP *group = NULL; + if (!(eckey = EVP_PKEY_get0(pk))) + return SC_ERROR_INCOMPATIBLE_KEY; + if (!(point = EC_KEY_get0_public_key(eckey)) || + !(group = EC_KEY_get0_group(eckey))) return SC_ERROR_INTERNAL; +#else + EC_POINT *point = NULL; + EC_GROUP *group = NULL; + int nid = 0; + unsigned char *pub = NULL; + size_t pub_len = 0; + char *group_name = NULL; + size_t group_name_len = 0; + + if (EVP_PKEY_get_octet_string_param(pk, OSSL_PKEY_PARAM_PUB_KEY, NULL, 0, &pub_len) != 1) { + return SC_ERROR_INTERNAL; + } + if (EVP_PKEY_get_group_name(pk, NULL, 0, &group_name_len) != 1) { + return SC_ERROR_INTERNAL; + } + if (!(pub = malloc(pub_len)) || !(group_name = malloc(group_name_len))) { + free(pub); + return SC_ERROR_OUT_OF_MEMORY; + } + if (EVP_PKEY_get_octet_string_param(pk, OSSL_PKEY_PARAM_PUB_KEY, pub, pub_len, NULL) != 1 || + EVP_PKEY_get_group_name(pk, group_name, group_name_len, NULL) != 1) { + free(pub); + free(group_name); + return SC_ERROR_INTERNAL; + } + if ((nid = OBJ_sn2nid(group_name) == 0) || + !(group = EC_GROUP_new_by_curve_name(nid)) || + !(point = EC_POINT_new(group)) || + EC_POINT_oct2point(group, point, pub, pub_len, NULL) != 1) { + free(pub); + free(group_name); + EC_POINT_free(point); + EC_GROUP_free(group); + return SC_ERROR_INTERNAL; + } + free(pub); + free(group_name); +#endif X = BN_new(); Y = BN_new(); - if (X && Y && EC_KEY_get0_group(eckey)) - r = EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(eckey), - point, X, Y, NULL); + if (X && Y && group) + r = EC_POINT_get_affine_coordinates(group, point, X, Y, NULL); if (r == 1) { dst->xy.len = BN_num_bytes(X) + BN_num_bytes(Y); dst->xy.data = malloc(dst->xy.len); @@ -1639,38 +1805,56 @@ sc_pkcs15_convert_pubkey(struct sc_pkcs15_pubkey *pkcs15_key, void *evp_key) } BN_free(X); BN_free(Y); + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EC_GROUP_free(group); + EC_POINT_free(point); +#endif if (r != 1) return SC_ERROR_INTERNAL; break; } case EVP_PKEY_EC: { struct sc_pkcs15_pubkey_ec *dst = &pkcs15_key->u.ec; - EC_KEY *src = NULL; + pkcs15_key->algorithm = SC_ALGORITHM_EC; + unsigned char buf[255]; size_t buflen = 255; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const EC_KEY *src = NULL; const EC_GROUP *grp = NULL; - unsigned char buf[255]; - size_t buflen = 255; - int nid; + const EC_POINT *point = NULL; + int nid = 0; - src = EVP_PKEY_get0(pk); - assert(src); - assert(EC_KEY_get0_public_key(src)); - - pkcs15_key->algorithm = SC_ALGORITHM_EC; - grp = EC_KEY_get0_group(src); - if(grp == 0) + if (!(src = EVP_PKEY_get0_EC_KEY(pk))) return SC_ERROR_INCOMPATIBLE_KEY; + if (!(point = EC_KEY_get0_public_key(src)) || + !(grp = EC_KEY_get0_group(src))) { + return SC_ERROR_INCOMPATIBLE_KEY; + } /* Decode EC_POINT from a octet string */ - buflen = EC_POINT_point2oct(grp, (const EC_POINT *) EC_KEY_get0_public_key(src), + buflen = EC_POINT_point2oct(grp, point, POINT_CONVERSION_UNCOMPRESSED, buf, buflen, NULL); /* get curve name */ nid = EC_GROUP_get_curve_name(grp); if(nid != 0) { - const char *name = OBJ_nid2sn(nid); - if(sizeof(name) > 0) - dst->params.named_curve = strdup(name); + const char *group_name = OBJ_nid2sn(nid); + if (group_name) + dst->params.named_curve = strdup(group_name); } +#else + char group_name[256]; + if (EVP_PKEY_get_octet_string_param(pk, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, buf, buflen, NULL) != 1) + return SC_ERROR_INTERNAL; + if (EVP_PKEY_get_group_name(pk, group_name, sizeof(group_name), NULL) != 1) + return SC_ERROR_INTERNAL; + dst->params.named_curve = strdup(group_name); + + /* Decode EC_POINT from a octet string */ + if (EVP_PKEY_get_octet_string_param(pk, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, buf, buflen, &buflen) != 1) { + return SC_ERROR_INCOMPATIBLE_KEY; + } +#endif /* copy the public key */ if (buflen > 0) { @@ -1687,7 +1871,13 @@ sc_pkcs15_convert_pubkey(struct sc_pkcs15_pubkey *pkcs15_key, void *evp_key) break; } -#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) */ +#endif /* !defined(OPENSSL_NO_EC) */ +#ifdef EVP_PKEY_ED25519 + case EVP_PKEY_ED25519: { + /* TODO */ + break; + } +#endif /* EVP_PKEY_ED25519 */ default: return SC_ERROR_NOT_SUPPORTED; } diff --git a/src/libopensc/pkcs15-sc-hsm.c b/src/libopensc/pkcs15-sc-hsm.c index 26eaa05858..614c43cbad 100644 --- a/src/libopensc/pkcs15-sc-hsm.c +++ b/src/libopensc/pkcs15-sc-hsm.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -40,8 +40,7 @@ extern struct sc_aid sc_hsm_aid; void sc_hsm_set_serialnr(sc_card_t *card, char *serial); - - +// clang-format off static struct ec_curve curves[] = { { { (unsigned char *) "\x2A\x86\x48\xCE\x3D\x03\x01\x01", 8}, // secp192r1 aka prime192r1 @@ -61,6 +60,24 @@ static struct ec_curve curves[] = { { (unsigned char *) "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xBC\xE6\xFA\xAD\xA7\x17\x9E\x84\xF3\xB9\xCA\xC2\xFC\x63\x25\x51", 32}, { (unsigned char *) "\x01", 1} }, + { + { (unsigned char *) "\x2B\x81\x04\x00\x22", 5}, // secp384r1 + { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF", 48}, + { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFC", 48}, + { (unsigned char *) "\xB3\x31\x2F\xA7\xE2\x3E\xE7\xE4\x98\x8E\x05\x6B\xE3\xF8\x2D\x19\x18\x1D\x9C\x6E\xFE\x81\x41\x12\x03\x14\x08\x8F\x50\x13\x87\x5A\xC6\x56\x39\x8D\x8A\x2E\xD1\x9D\x2A\x85\xC8\xED\xD3\xEC\x2A\xEF", 48}, + { (unsigned char *) "\x04\xAA\x87\xCA\x22\xBE\x8B\x05\x37\x8E\xB1\xC7\x1E\xF3\x20\xAD\x74\x6E\x1D\x3B\x62\x8B\xA7\x9B\x98\x59\xF7\x41\xE0\x82\x54\x2A\x38\x55\x02\xF2\x5D\xBF\x55\x29\x6C\x3A\x54\x5E\x38\x72\x76\x0A\xB7\x36\x17\xDE\x4A\x96\x26\x2C\x6F\x5D\x9E\x98\xBF\x92\x92\xDC\x29\xF8\xF4\x1D\xBD\x28\x9A\x14\x7C\xE9\xDA\x31\x13\xB5\xF0\xB8\xC0\x0A\x60\xB1\xCE\x1D\x7E\x81\x9D\x7A\x43\x1D\x7C\x90\xEA\x0E\x5F", 97}, + { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC7\x63\x4D\x81\xF4\x37\x2D\xDF\x58\x1A\x0D\xB2\x48\xB0\xA7\x7A\xEC\xEC\x19\x6A\xCC\xC5\x29\x73", 48}, + { (unsigned char *) "\x01", 1} + }, + { + { (unsigned char *) "\x2B\x81\x04\x00\x23", 5}, // secp521r1 + { (unsigned char *) "\x01\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 66}, + { (unsigned char *) "\x01\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC", 66}, + { (unsigned char *) "\x00\x51\x95\x3E\xB9\x61\x8E\x1C\x9A\x1F\x92\x9A\x21\xA0\xB6\x85\x40\xEE\xA2\xDA\x72\x5B\x99\xB3\x15\xF3\xB8\xB4\x89\x91\x8E\xF1\x09\xE1\x56\x19\x39\x51\xEC\x7E\x93\x7B\x16\x52\xC0\xBD\x3B\xB1\xBF\x07\x35\x73\xDF\x88\x3D\x2C\x34\xF1\xEF\x45\x1F\xD4\x6B\x50\x3F\x00", 66}, + { (unsigned char *) "\x04\x00\xC6\x85\x8E\x06\xB7\x04\x04\xE9\xCD\x9E\x3E\xCB\x66\x23\x95\xB4\x42\x9C\x64\x81\x39\x05\x3F\xB5\x21\xF8\x28\xAF\x60\x6B\x4D\x3D\xBA\xA1\x4B\x5E\x77\xEF\xE7\x59\x28\xFE\x1D\xC1\x27\xA2\xFF\xA8\xDE\x33\x48\xB3\xC1\x85\x6A\x42\x9B\xF9\x7E\x7E\x31\xC2\xE5\xBD\x66\x01\x18\x39\x29\x6A\x78\x9A\x3B\xC0\x04\x5C\x8A\x5F\xB4\x2C\x7D\x1B\xD9\x98\xF5\x44\x49\x57\x9B\x44\x68\x17\xAF\xBD\x17\x27\x3E\x66\x2C\x97\xEE\x72\x99\x5E\xF4\x26\x40\xC5\x50\xB9\x01\x3F\xAD\x07\x61\x35\x3C\x70\x86\xA2\x72\xC2\x40\x88\xBE\x94\x76\x9F\xD1\x66\x50", 133}, + { (unsigned char *) "\x01\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFA\x51\x86\x87\x83\xBF\x2F\x96\x6B\x7F\xCC\x01\x48\xF7\x09\xA5\xD0\x3B\xB5\xC9\xB8\x89\x9C\x47\xAE\xBB\x6F\xB7\x1E\x91\x38\x64\x09", 66}, + { (unsigned char *) "\x01", 1} + }, { { (unsigned char *) "\x2B\x24\x03\x03\x02\x08\x01\x01\x03", 9}, // brainpoolP192r1 { (unsigned char *) "\xC3\x02\xF4\x1D\x93\x2A\x36\xCD\xA7\xA3\x46\x30\x93\xD1\x8D\xB7\x8F\xCE\x47\x6D\xE1\xA8\x62\x97", 24}, @@ -97,6 +114,78 @@ static struct ec_curve curves[] = { { (unsigned char *) "\xD3\x5E\x47\x20\x36\xBC\x4F\xB7\xE1\x3C\x78\x5E\xD2\x01\xE0\x65\xF9\x8F\xCF\xA5\xB6\x8F\x12\xA3\x2D\x48\x2E\xC7\xEE\x86\x58\xE9\x86\x91\x55\x5B\x44\xC5\x93\x11", 40}, { (unsigned char *) "\x01", 1} }, + { + { (unsigned char *) "\x2B\x24\x03\x03\x02\x08\x01\x01\x0B", 9}, // brainpoolP384r1 + { (unsigned char *) "\x8C\xB9\x1E\x82\xA3\x38\x6D\x28\x0F\x5D\x6F\x7E\x50\xE6\x41\xDF\x15\x2F\x71\x09\xED\x54\x56\xB4\x12\xB1\xDA\x19\x7F\xB7\x11\x23\xAC\xD3\xA7\x29\x90\x1D\x1A\x71\x87\x47\x00\x13\x31\x07\xEC\x53", 48}, + { (unsigned char *) "\x7B\xC3\x82\xC6\x3D\x8C\x15\x0C\x3C\x72\x08\x0A\xCE\x05\xAF\xA0\xC2\xBE\xA2\x8E\x4F\xB2\x27\x87\x13\x91\x65\xEF\xBA\x91\xF9\x0F\x8A\xA5\x81\x4A\x50\x3A\xD4\xEB\x04\xA8\xC7\xDD\x22\xCE\x28\x26", 48}, + { (unsigned char *) "\x04\xA8\xC7\xDD\x22\xCE\x28\x26\x8B\x39\xB5\x54\x16\xF0\x44\x7C\x2F\xB7\x7D\xE1\x07\xDC\xD2\xA6\x2E\x88\x0E\xA5\x3E\xEB\x62\xD5\x7C\xB4\x39\x02\x95\xDB\xC9\x94\x3A\xB7\x86\x96\xFA\x50\x4C\x11", 48}, + { (unsigned char *) "\x04\x1D\x1C\x64\xF0\x68\xCF\x45\xFF\xA2\xA6\x3A\x81\xB7\xC1\x3F\x6B\x88\x47\xA3\xE7\x7E\xF1\x4F\xE3\xDB\x7F\xCA\xFE\x0C\xBD\x10\xE8\xE8\x26\xE0\x34\x36\xD6\x46\xAA\xEF\x87\xB2\xE2\x47\xD4\xAF\x1E\x8A\xBE\x1D\x75\x20\xF9\xC2\xA4\x5C\xB1\xEB\x8E\x95\xCF\xD5\x52\x62\xB7\x0B\x29\xFE\xEC\x58\x64\xE1\x9C\x05\x4F\xF9\x91\x29\x28\x0E\x46\x46\x21\x77\x91\x81\x11\x42\x82\x03\x41\x26\x3C\x53\x15", 97}, + { (unsigned char *) "\x8C\xB9\x1E\x82\xA3\x38\x6D\x28\x0F\x5D\x6F\x7E\x50\xE6\x41\xDF\x15\x2F\x71\x09\xED\x54\x56\xB3\x1F\x16\x6E\x6C\xAC\x04\x25\xA7\xCF\x3A\xB6\xAF\x6B\x7F\xC3\x10\x3B\x88\x32\x02\xE9\x04\x65\x65", 48}, + { (unsigned char *) "\x01", 1} + }, + { + { (unsigned char *) "\x2B\x24\x03\x03\x02\x08\x01\x01\x0D", 9}, // brainpoolP512r1 + { (unsigned char *) "\xAA\xDD\x9D\xB8\xDB\xE9\xC4\x8B\x3F\xD4\xE6\xAE\x33\xC9\xFC\x07\xCB\x30\x8D\xB3\xB3\xC9\xD2\x0E\xD6\x63\x9C\xCA\x70\x33\x08\x71\x7D\x4D\x9B\x00\x9B\xC6\x68\x42\xAE\xCD\xA1\x2A\xE6\xA3\x80\xE6\x28\x81\xFF\x2F\x2D\x82\xC6\x85\x28\xAA\x60\x56\x58\x3A\x48\xF3", 64}, + { (unsigned char *) "\x78\x30\xA3\x31\x8B\x60\x3B\x89\xE2\x32\x71\x45\xAC\x23\x4C\xC5\x94\xCB\xDD\x8D\x3D\xF9\x16\x10\xA8\x34\x41\xCA\xEA\x98\x63\xBC\x2D\xED\x5D\x5A\xA8\x25\x3A\xA1\x0A\x2E\xF1\xC9\x8B\x9A\xC8\xB5\x7F\x11\x17\xA7\x2B\xF2\xC7\xB9\xE7\xC1\xAC\x4D\x77\xFC\x94\xCA", 64}, + { (unsigned char *) "\x3D\xF9\x16\x10\xA8\x34\x41\xCA\xEA\x98\x63\xBC\x2D\xED\x5D\x5A\xA8\x25\x3A\xA1\x0A\x2E\xF1\xC9\x8B\x9A\xC8\xB5\x7F\x11\x17\xA7\x2B\xF2\xC7\xB9\xE7\xC1\xAC\x4D\x77\xFC\x94\xCA\xDC\x08\x3E\x67\x98\x40\x50\xB7\x5E\xBA\xE5\xDD\x28\x09\xBD\x63\x80\x16\xF7\x23", 64}, + { (unsigned char *) "\x04\x81\xAE\xE4\xBD\xD8\x2E\xD9\x64\x5A\x21\x32\x2E\x9C\x4C\x6A\x93\x85\xED\x9F\x70\xB5\xD9\x16\xC1\xB4\x3B\x62\xEE\xF4\xD0\x09\x8E\xFF\x3B\x1F\x78\xE2\xD0\xD4\x8D\x50\xD1\x68\x7B\x93\xB9\x7D\x5F\x7C\x6D\x50\x47\x40\x6A\x5E\x68\x8B\x35\x22\x09\xBC\xB9\xF8\x22\x7D\xDE\x38\x5D\x56\x63\x32\xEC\xC0\xEA\xBF\xA9\xCF\x78\x22\xFD\xF2\x09\xF7\x00\x24\xA5\x7B\x1A\xA0\x00\xC5\x5B\x88\x1F\x81\x11\xB2\xDC\xDE\x49\x4A\x5F\x48\x5E\x5B\xCA\x4B\xD8\x8A\x27\x63\xAE\xD1\xCA\x2B\x2F\xA8\xF0\x54\x06\x78\xCD\x1E\x0F\x3A\xD8\x08\x92", 129}, + { (unsigned char *) "\xAA\xDD\x9D\xB8\xDB\xE9\xC4\x8B\x3F\xD4\xE6\xAE\x33\xC9\xFC\x07\xCB\x30\x8D\xB3\xB3\xC9\xD2\x0E\xD6\x63\x9C\xCA\x70\x33\x08\x70\x55\x3E\x5C\x41\x4C\xA9\x26\x19\x41\x86\x61\x19\x7F\xAC\x10\x47\x1D\xB1\xD3\x81\x08\x5D\xDA\xDD\xB5\x87\x96\x82\x9C\xA9\x00\x69", 64}, + { (unsigned char *) "\x01", 1} + }, + { + { (unsigned char *) "\x2B\x24\x03\x03\x02\x08\x01\x01\x04", 9}, // brainpoolP192t1 + { (unsigned char *) "\xC3\x02\xF4\x1D\x93\x2A\x36\xCD\xA7\xA3\x46\x30\x93\xD1\x8D\xB7\x8F\xCE\x47\x6D\xE1\xA8\x62\x97", 24}, + { (unsigned char *) "\xC3\x02\xF4\x1D\x93\x2A\x36\xCD\xA7\xA3\x46\x30\x93\xD1\x8D\xB7\x8F\xCE\x47\x6D\xE1\xA8\x62\x94", 24}, + { (unsigned char *) "\x13\xD5\x6F\xFA\xEC\x78\x68\x1E\x68\xF9\xDE\xB4\x3B\x35\xBE\xC2\xFB\x68\x54\x2E\x27\x89\x7B\x79", 24}, + { (unsigned char *) "\x04\x3A\xE9\xE5\x8C\x82\xF6\x3C\x30\x28\x2E\x1F\xE7\xBB\xF4\x3F\xA7\x2C\x44\x6A\xF6\xF4\x61\x81\x29\x09\x7E\x2C\x56\x67\xC2\x22\x3A\x90\x2A\xB5\xCA\x44\x9D\x00\x84\xB7\xE5\xB3\xDE\x7C\xCC\x01\xC9", 49}, + { (unsigned char *) "\xC3\x02\xF4\x1D\x93\x2A\x36\xCD\xA7\xA3\x46\x2F\x9E\x9E\x91\x6B\x5B\xE8\xF1\x02\x9A\xC4\xAC\xC1", 24}, + { (unsigned char *) "\x01", 1} + }, + { + { (unsigned char *) "\x2B\x24\x03\x03\x02\x08\x01\x01\x06", 9}, // brainpoolP224t1 + { (unsigned char *) "\xD7\xC1\x34\xAA\x26\x43\x66\x86\x2A\x18\x30\x25\x75\xD1\xD7\x87\xB0\x9F\x07\x57\x97\xDA\x89\xF5\x7E\xC8\xC0\xFF", 28}, + { (unsigned char *) "\xD7\xC1\x34\xAA\x26\x43\x66\x86\x2A\x18\x30\x25\x75\xD1\xD7\x87\xB0\x9F\x07\x57\x97\xDA\x89\xF5\x7E\xC8\xC0\xFC", 28}, + { (unsigned char *) "\x4B\x33\x7D\x93\x41\x04\xCD\x7B\xEF\x27\x1B\xF6\x0C\xED\x1E\xD2\x0D\xA1\x4C\x08\xB3\xBB\x64\xF1\x8A\x60\x88\x8D", 28}, + { (unsigned char *) "\x04\x6A\xB1\xE3\x44\xCE\x25\xFF\x38\x96\x42\x4E\x7F\xFE\x14\x76\x2E\xCB\x49\xF8\x92\x8A\xC0\xC7\x60\x29\xB4\xD5\x80\x03\x74\xE9\xF5\x14\x3E\x56\x8C\xD2\x3F\x3F\x4D\x7C\x0D\x4B\x1E\x41\xC8\xCC\x0D\x1C\x6A\xBD\x5F\x1A\x46\xDB\x4C", 57}, + { (unsigned char *) "\xD7\xC1\x34\xAA\x26\x43\x66\x86\x2A\x18\x30\x25\x75\xD0\xFB\x98\xD1\x16\xBC\x4B\x6D\xDE\xBC\xA3\xA5\xA7\x93\x9F", 28}, + { (unsigned char *) "\x01", 1} + }, + { + { (unsigned char *) "\x2B\x24\x03\x03\x02\x08\x01\x01\x08", 9}, // brainpoolP256t1 + { (unsigned char *) "\xA9\xFB\x57\xDB\xA1\xEE\xA9\xBC\x3E\x66\x0A\x90\x9D\x83\x8D\x72\x6E\x3B\xF6\x23\xD5\x26\x20\x28\x20\x13\x48\x1D\x1F\x6E\x53\x77", 32}, + { (unsigned char *) "\xA9\xFB\x57\xDB\xA1\xEE\xA9\xBC\x3E\x66\x0A\x90\x9D\x83\x8D\x72\x6E\x3B\xF6\x23\xD5\x26\x20\x28\x20\x13\x48\x1D\x1F\x6E\x53\x74", 32}, + { (unsigned char *) "\x66\x2C\x61\xC4\x30\xD8\x4E\xA4\xFE\x66\xA7\x73\x3D\x0B\x76\xB7\xBF\x93\xEB\xC4\xAF\x2F\x49\x25\x6A\xE5\x81\x01\xFE\xE9\x2B\x04", 32}, + { (unsigned char *) "\x04\xA3\xE8\xEB\x3C\xC1\xCF\xE7\xB7\x73\x22\x13\xB2\x3A\x65\x61\x49\xAF\xA1\x42\xC4\x7A\xAF\xBC\x2B\x79\xA1\x91\x56\x2E\x13\x05\xF4\x2D\x99\x6C\x82\x34\x39\xC5\x6D\x7F\x7B\x22\xE1\x46\x44\x41\x7E\x69\xBC\xB6\xDE\x39\xD0\x27\x00\x1D\xAB\xE8\xF3\x5B\x25\xC9\xBE", 65}, + { (unsigned char *) "\xA9\xFB\x57\xDB\xA1\xEE\xA9\xBC\x3E\x66\x0A\x90\x9D\x83\x8D\x71\x8C\x39\x7A\xA3\xB5\x61\xA6\xF7\x90\x1E\x0E\x82\x97\x48\x56\xA7", 32}, + { (unsigned char *) "\x01", 1} + }, + { + { (unsigned char *) "\x2B\x24\x03\x03\x02\x08\x01\x01\x0A", 9}, // brainpoolP320t1 + { (unsigned char *) "\xD3\x5E\x47\x20\x36\xBC\x4F\xB7\xE1\x3C\x78\x5E\xD2\x01\xE0\x65\xF9\x8F\xCF\xA6\xF6\xF4\x0D\xEF\x4F\x92\xB9\xEC\x78\x93\xEC\x28\xFC\xD4\x12\xB1\xF1\xB3\x2E\x27", 40}, + { (unsigned char *) "\xD3\x5E\x47\x20\x36\xBC\x4F\xB7\xE1\x3C\x78\x5E\xD2\x01\xE0\x65\xF9\x8F\xCF\xA6\xF6\xF4\x0D\xEF\x4F\x92\xB9\xEC\x78\x93\xEC\x28\xFC\xD4\x12\xB1\xF1\xB3\x2E\x24", 40}, + { (unsigned char *) "\xA7\xF5\x61\xE0\x38\xEB\x1E\xD5\x60\xB3\xD1\x47\xDB\x78\x20\x13\x06\x4C\x19\xF2\x7E\xD2\x7C\x67\x80\xAA\xF7\x7F\xB8\xA5\x47\xCE\xB5\xB4\xFE\xF4\x22\x34\x03\x53", 40}, + { (unsigned char *) "\x04\x92\x5B\xE9\xFB\x01\xAF\xC6\xFB\x4D\x3E\x7D\x49\x90\x01\x0F\x81\x34\x08\xAB\x10\x6C\x4F\x09\xCB\x7E\xE0\x78\x68\xCC\x13\x6F\xFF\x33\x57\xF6\x24\xA2\x1B\xED\x52\x63\xBA\x3A\x7A\x27\x48\x3E\xBF\x66\x71\xDB\xEF\x7A\xBB\x30\xEB\xEE\x08\x4E\x58\xA0\xB0\x77\xAD\x42\xA5\xA0\x98\x9D\x1E\xE7\x1B\x1B\x9B\xC0\x45\x5F\xB0\xD2\xC3", 81}, + { (unsigned char *) "\xD3\x5E\x47\x20\x36\xBC\x4F\xB7\xE1\x3C\x78\x5E\xD2\x01\xE0\x65\xF9\x8F\xCF\xA5\xB6\x8F\x12\xA3\x2D\x48\x2E\xC7\xEE\x86\x58\xE9\x86\x91\x55\x5B\x44\xC5\x93\x11", 40}, + { (unsigned char *) "\x01", 1} + }, + { + { (unsigned char *) "\x2B\x24\x03\x03\x02\x08\x01\x01\x0C", 9}, // brainpoolP384t1 + { (unsigned char *) "\x8C\xB9\x1E\x82\xA3\x38\x6D\x28\x0F\x5D\x6F\x7E\x50\xE6\x41\xDF\x15\x2F\x71\x09\xED\x54\x56\xB4\x12\xB1\xDA\x19\x7F\xB7\x11\x23\xAC\xD3\xA7\x29\x90\x1D\x1A\x71\x87\x47\x00\x13\x31\x07\xEC\x53", 48}, + { (unsigned char *) "\x8C\xB9\x1E\x82\xA3\x38\x6D\x28\x0F\x5D\x6F\x7E\x50\xE6\x41\xDF\x15\x2F\x71\x09\xED\x54\x56\xB4\x12\xB1\xDA\x19\x7F\xB7\x11\x23\xAC\xD3\xA7\x29\x90\x1D\x1A\x71\x87\x47\x00\x13\x31\x07\xEC\x50", 48}, + { (unsigned char *) "\x7F\x51\x9E\xAD\xA7\xBD\xA8\x1B\xD8\x26\xDB\xA6\x47\x91\x0F\x8C\x4B\x93\x46\xED\x8C\xCD\xC6\x4E\x4B\x1A\xBD\x11\x75\x6D\xCE\x1D\x20\x74\xAA\x26\x3B\x88\x80\x5C\xED\x70\x35\x5A\x33\xB4\x71\xEE", 48}, + { (unsigned char *) "\x04\x18\xDE\x98\xB0\x2D\xB9\xA3\x06\xF2\xAF\xCD\x72\x35\xF7\x2A\x81\x9B\x80\xAB\x12\xEB\xD6\x53\x17\x24\x76\xFE\xCD\x46\x2A\xAB\xFF\xC4\xFF\x19\x1B\x94\x6A\x5F\x54\xD8\xD0\xAA\x2F\x41\x88\x08\xCC\x25\xAB\x05\x69\x62\xD3\x06\x51\xA1\x14\xAF\xD2\x75\x5A\xD3\x36\x74\x7F\x93\x47\x5B\x7A\x1F\xCA\x3B\x88\xF2\xB6\xA2\x08\xCC\xFE\x46\x94\x08\x58\x4D\xC2\xB2\x91\x26\x75\xBF\x5B\x9E\x58\x29\x28", 97}, + { (unsigned char *) "\x8C\xB9\x1E\x82\xA3\x38\x6D\x28\x0F\x5D\x6F\x7E\x50\xE6\x41\xDF\x15\x2F\x71\x09\xED\x54\x56\xB3\x1F\x16\x6E\x6C\xAC\x04\x25\xA7\xCF\x3A\xB6\xAF\x6B\x7F\xC3\x10\x3B\x88\x32\x02\xE9\x04\x65\x65", 48}, + { (unsigned char *) "\x01", 1} + }, + { + { (unsigned char *) "\x2B\x24\x03\x03\x02\x08\x01\x01\x0E", 9}, // brainpoolP512t1 + { (unsigned char *) "\xAA\xDD\x9D\xB8\xDB\xE9\xC4\x8B\x3F\xD4\xE6\xAE\x33\xC9\xFC\x07\xCB\x30\x8D\xB3\xB3\xC9\xD2\x0E\xD6\x63\x9C\xCA\x70\x33\x08\x71\x7D\x4D\x9B\x00\x9B\xC6\x68\x42\xAE\xCD\xA1\x2A\xE6\xA3\x80\xE6\x28\x81\xFF\x2F\x2D\x82\xC6\x85\x28\xAA\x60\x56\x58\x3A\x48\xF3", 64}, + { (unsigned char *) "\xAA\xDD\x9D\xB8\xDB\xE9\xC4\x8B\x3F\xD4\xE6\xAE\x33\xC9\xFC\x07\xCB\x30\x8D\xB3\xB3\xC9\xD2\x0E\xD6\x63\x9C\xCA\x70\x33\x08\x71\x7D\x4D\x9B\x00\x9B\xC6\x68\x42\xAE\xCD\xA1\x2A\xE6\xA3\x80\xE6\x28\x81\xFF\x2F\x2D\x82\xC6\x85\x28\xAA\x60\x56\x58\x3A\x48\xF0", 64}, + { (unsigned char *) "\x7C\xBB\xBC\xF9\x44\x1C\xFA\xB7\x6E\x18\x90\xE4\x68\x84\xEA\xE3\x21\xF7\x0C\x0B\xCB\x49\x81\x52\x78\x97\x50\x4B\xEC\x3E\x36\xA6\x2B\xCD\xFA\x23\x04\x97\x65\x40\xF6\x45\x00\x85\xF2\xDA\xE1\x45\xC2\x25\x53\xB4\x65\x76\x36\x89\x18\x0E\xA2\x57\x18\x67\x42\x3E", 64}, + { (unsigned char *) "\x04\x64\x0E\xCE\x5C\x12\x78\x87\x17\xB9\xC1\xBA\x06\xCB\xC2\xA6\xFE\xBA\x85\x84\x24\x58\xC5\x6D\xDE\x9D\xB1\x75\x8D\x39\xC0\x31\x3D\x82\xBA\x51\x73\x5C\xDB\x3E\xA4\x99\xAA\x77\xA7\xD6\x94\x3A\x64\xF7\xA3\xF2\x5F\xE2\x6F\x06\xB5\x1B\xAA\x26\x96\xFA\x90\x35\xDA\x5B\x53\x4B\xD5\x95\xF5\xAF\x0F\xA2\xC8\x92\x37\x6C\x84\xAC\xE1\xBB\x4E\x30\x19\xB7\x16\x34\xC0\x11\x31\x15\x9C\xAE\x03\xCE\xE9\xD9\x93\x21\x84\xBE\xEF\x21\x6B\xD7\x1D\xF2\xDA\xDF\x86\xA6\x27\x30\x6E\xCF\xF9\x6D\xBB\x8B\xAC\xE1\x98\xB6\x1E\x00\xF8\xB3\x32", 129}, + { (unsigned char *) "\xAA\xDD\x9D\xB8\xDB\xE9\xC4\x8B\x3F\xD4\xE6\xAE\x33\xC9\xFC\x07\xCB\x30\x8D\xB3\xB3\xC9\xD2\x0E\xD6\x63\x9C\xCA\x70\x33\x08\x70\x55\x3E\x5C\x41\x4C\xA9\x26\x19\x41\x86\x61\x19\x7F\xAC\x10\x47\x1D\xB1\xD3\x81\x08\x5D\xDA\xDD\xB5\x87\x96\x82\x9C\xA9\x00\x69", 64}, + { (unsigned char *) "\x01", 1} + }, { { (unsigned char *) "\x2B\x81\x04\x00\x1F", 5}, // secp192k1 { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xEE\x37", 24}, @@ -125,8 +214,7 @@ static struct ec_curve curves[] = { { NULL, 0} } }; - - +// clang-format on #define C_ASN1_CVC_PUBKEY_SIZE 10 static const struct sc_asn1_entry c_asn1_cvc_pubkey[C_ASN1_CVC_PUBKEY_SIZE] = { @@ -145,7 +233,7 @@ static const struct sc_asn1_entry c_asn1_cvc_pubkey[C_ASN1_CVC_PUBKEY_SIZE] = { #define C_ASN1_CVC_BODY_SIZE 5 static const struct sc_asn1_entry c_asn1_cvc_body[C_ASN1_CVC_BODY_SIZE] = { { "certificateProfileIdentifier", SC_ASN1_INTEGER, SC_ASN1_APP | 0x1F29, 0, NULL, NULL }, - { "certificationAuthorityReference", SC_ASN1_PRINTABLESTRING, SC_ASN1_APP | 2, 0, NULL, NULL }, + { "certificationAuthorityReference", SC_ASN1_PRINTABLESTRING, SC_ASN1_APP | 2, SC_ASN1_OPTIONAL, NULL, NULL }, { "publicKey", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 0x1F49, 0, NULL, NULL }, { "certificateHolderReference", SC_ASN1_PRINTABLESTRING, SC_ASN1_APP | 0x1F20, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } @@ -178,6 +266,26 @@ static const struct sc_asn1_entry c_asn1_req[C_ASN1_REQ_SIZE] = { { NULL, 0, 0, 0, NULL, NULL } }; +struct sc_object_id sc_hsm_public_key_oid = { + {1, 3, 6, 1, 4, 1, 24991, 4, 3, 1, -1} +}; + +#define C_ASN1_SC_HSM_PKA_NEW_SIZE 5 +static const struct sc_asn1_entry c_asn1_sc_hsm_pka_new_format[C_ASN1_SC_HSM_PKA_NEW_SIZE] = { + { "oid", SC_ASN1_OBJECT, SC_ASN1_TAG_UNIVERSAL | SC_ASN1_TAG_OBJECT, 0, NULL, NULL }, + { "dicaCVC", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 1, 0, NULL, NULL }, + { "deviceCVC", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 2, 0, NULL, NULL }, + { "publicKey", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 3, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } +}; + +#define C_ASN1_SC_HSM_PKA_OLD_SIZE 4 +static const struct sc_asn1_entry c_asn1_sc_hsm_pka_old_format[C_ASN1_SC_HSM_PKA_OLD_SIZE] = { + { "publicKey", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 7, 0, NULL, NULL }, + { "deviceCVCert", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 0x1F21, 0, NULL, NULL }, + { "dicaCVCert", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 0x1F21, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } +}; static int read_file(sc_pkcs15_card_t * p15card, u8 fid[2], @@ -191,7 +299,7 @@ static int read_file(sc_pkcs15_card_t * p15card, u8 fid[2], path.aid = sc_hsm_aid; /* we don't have a pre-known size of the file */ path.count = -1; - if (!p15card->opts.use_file_cache + if (!p15card->opts.use_file_cache || !efbin || SC_SUCCESS != sc_pkcs15_read_cached_file(p15card, &path, &efbin, len)) { /* avoid re-selection of SC-HSM */ path.aid.len = 0; @@ -225,14 +333,103 @@ static int read_file(sc_pkcs15_card_t * p15card, u8 fid[2], return SC_SUCCESS; } +static void fixup_cvc_printable_string_lengths(sc_cvc_t *cvc) +{ + /* SC_ASN1_PRINTABLESTRING adds 1 for the null-terminator */ + if (cvc->chrLen > 0) { + cvc->chrLen--; + } + if (cvc->carLen > 0) { + cvc->carLen--; + } + if (cvc->outerCARLen > 0) { + cvc->outerCARLen--; + } +} +/* + * Sets up asn1_cvcert to point to asn1_cvc_body, asn1_cvc_pubkey, and + * cvc. When sc_asn1_decode is called on asn1_cvcert, it will populate fields + * in cvc. + * + * @param asn1_cvcert: unpopulated array with len matching c_asn1_cvcert + * @param asn1_cvc_cert: unpopulated array with len matching c_asn1_cvc_body + * @param asn1_cvc_pubkey: unpopulated array matching c_asn1_cvc_pubkey + * @param cvc: non NULL cvc struct + */ +static int sc_pkcs15emu_sc_hsm_format_asn1_cvcert( + struct sc_asn1_entry *asn1_cvcert, size_t asn1_cvcert_len, + struct sc_asn1_entry *asn1_cvc_body, size_t asn1_cvc_body_len, + struct sc_asn1_entry *asn1_cvc_pubkey, size_t asn1_cvc_pubkey_len, + sc_cvc_t *cvc) +{ + if ((asn1_cvc_pubkey_len < C_ASN1_CVC_PUBKEY_SIZE) || + (asn1_cvc_body_len < C_ASN1_CVC_BODY_SIZE) || + (asn1_cvcert_len < C_ASN1_CVCERT_SIZE)) { + return SC_ERROR_BUFFER_TOO_SMALL; + } + + sc_copy_asn1_entry(c_asn1_cvc_pubkey, asn1_cvc_pubkey); + sc_copy_asn1_entry(c_asn1_cvc_body, asn1_cvc_body); + sc_copy_asn1_entry(c_asn1_cvcert, asn1_cvcert); + + sc_format_asn1_entry(asn1_cvc_pubkey , &cvc->pukoid, NULL, 0); + sc_format_asn1_entry(asn1_cvc_pubkey + 1, &cvc->primeOrModulus, &cvc->primeOrModuluslen, 0); + sc_format_asn1_entry(asn1_cvc_pubkey + 2, &cvc->coefficientAorExponent, &cvc->coefficientAorExponentlen, 0); + sc_format_asn1_entry(asn1_cvc_pubkey + 3, &cvc->coefficientB, &cvc->coefficientBlen, 0); + sc_format_asn1_entry(asn1_cvc_pubkey + 4, &cvc->basePointG, &cvc->basePointGlen, 0); + sc_format_asn1_entry(asn1_cvc_pubkey + 5, &cvc->order, &cvc->orderlen, 0); + sc_format_asn1_entry(asn1_cvc_pubkey + 6, &cvc->publicPoint, &cvc->publicPointlen, 0); + sc_format_asn1_entry(asn1_cvc_pubkey + 7, &cvc->cofactor, &cvc->cofactorlen, 0); + sc_format_asn1_entry(asn1_cvc_pubkey + 8, &cvc->modulusSize, NULL, 0); + + sc_format_asn1_entry(asn1_cvc_body , &cvc->cpi, NULL, 0); + cvc->carLen = sizeof(cvc->car); + sc_format_asn1_entry(asn1_cvc_body + 1, &cvc->car, &cvc->carLen, 0); + sc_format_asn1_entry(asn1_cvc_body + 2, asn1_cvc_pubkey, NULL, 0); + cvc->chrLen = sizeof(cvc->chr); + sc_format_asn1_entry(asn1_cvc_body + 3, &cvc->chr, &cvc->chrLen, 0); + + sc_format_asn1_entry(asn1_cvcert , asn1_cvc_body, NULL, 0); + sc_format_asn1_entry(asn1_cvcert + 1, &cvc->signature, &cvc->signatureLen, 0); + return SC_SUCCESS; +} + +/* + * Sets up asn1_req to point to asn1_authreq, which points to asn1_cvcert and + * cvc + * When sc_asn1_decode is called on asn1_authreq, it will populate fields + * in cvc and asn1_cvcert + * + * @param asn1_authreq: unpopulated array with len matching c_asn1_req + * @param asn1_authreq: unpopulated array with len matching c_asn1_authreq + * @param asn1_cvcert: already-initialized array matching c_asn1_cvcert + * + */ +static int sc_pkcs15emu_sc_hsm_format_asn1_req( + struct sc_asn1_entry *asn1_authreq, size_t asn1_authreq_len, + struct sc_asn1_entry *asn1_cvcert, + sc_cvc_t *cvc) +{ + if (asn1_authreq_len < C_ASN1_AUTHREQ_SIZE) { + return SC_ERROR_BUFFER_TOO_SMALL; + } + + sc_copy_asn1_entry(c_asn1_authreq, asn1_authreq); + + sc_format_asn1_entry(asn1_authreq , asn1_cvcert, NULL, 0); + cvc->outerCARLen = sizeof(cvc->outer_car); + sc_format_asn1_entry(asn1_authreq + 1, &cvc->outer_car, &cvc->outerCARLen, 0); + sc_format_asn1_entry(asn1_authreq + 2, &cvc->outerSignature, &cvc->outerSignatureLen, 0); + return SC_SUCCESS; +} /* * Decode a card verifiable certificate as defined in TR-03110. */ int sc_pkcs15emu_sc_hsm_decode_cvc(sc_pkcs15_card_t * p15card, - const u8 ** buf, size_t *buflen, - sc_cvc_t *cvc) + const u8 ** buf, size_t *buflen, + sc_cvc_t *cvc) { sc_card_t *card = p15card->card; struct sc_asn1_entry asn1_req[C_ASN1_REQ_SIZE]; @@ -241,47 +438,30 @@ int sc_pkcs15emu_sc_hsm_decode_cvc(sc_pkcs15_card_t * p15card, struct sc_asn1_entry asn1_cvcert[C_ASN1_CVCERT_SIZE]; struct sc_asn1_entry asn1_cvc_body[C_ASN1_CVC_BODY_SIZE]; struct sc_asn1_entry asn1_cvc_pubkey[C_ASN1_CVC_PUBKEY_SIZE]; - unsigned int cla,tag; + unsigned int cla = 0, tag = 0; size_t taglen; - size_t lenchr = sizeof(cvc->chr); - size_t lencar = sizeof(cvc->car); - size_t lenoutercar = sizeof(cvc->outer_car); const u8 *tbuf; int r; memset(cvc, 0, sizeof(*cvc)); sc_copy_asn1_entry(c_asn1_req, asn1_req); - sc_copy_asn1_entry(c_asn1_authreq, asn1_authreq); sc_copy_asn1_entry(c_asn1_cvc, asn1_cvc); - sc_copy_asn1_entry(c_asn1_cvcert, asn1_cvcert); - sc_copy_asn1_entry(c_asn1_cvc_body, asn1_cvc_body); - sc_copy_asn1_entry(c_asn1_cvc_pubkey, asn1_cvc_pubkey); - - sc_format_asn1_entry(asn1_cvc_pubkey , &cvc->pukoid, NULL, 0); - sc_format_asn1_entry(asn1_cvc_pubkey + 1, &cvc->primeOrModulus, &cvc->primeOrModuluslen, 0); - sc_format_asn1_entry(asn1_cvc_pubkey + 2, &cvc->coefficientAorExponent, &cvc->coefficientAorExponentlen, 0); - sc_format_asn1_entry(asn1_cvc_pubkey + 3, &cvc->coefficientB, &cvc->coefficientBlen, 0); - sc_format_asn1_entry(asn1_cvc_pubkey + 4, &cvc->basePointG, &cvc->basePointGlen, 0); - sc_format_asn1_entry(asn1_cvc_pubkey + 5, &cvc->order, &cvc->orderlen, 0); - sc_format_asn1_entry(asn1_cvc_pubkey + 6, &cvc->publicPoint, &cvc->publicPointlen, 0); - sc_format_asn1_entry(asn1_cvc_pubkey + 7, &cvc->cofactor, &cvc->cofactorlen, 0); - sc_format_asn1_entry(asn1_cvc_pubkey + 8, &cvc->modulusSize, NULL, 0); - sc_format_asn1_entry(asn1_cvc_body , &cvc->cpi, NULL, 0); - sc_format_asn1_entry(asn1_cvc_body + 1, &cvc->car, &lencar, 0); - sc_format_asn1_entry(asn1_cvc_body + 2, &asn1_cvc_pubkey, NULL, 0); - sc_format_asn1_entry(asn1_cvc_body + 3, &cvc->chr, &lenchr, 0); - - sc_format_asn1_entry(asn1_cvcert , &asn1_cvc_body, NULL, 0); - sc_format_asn1_entry(asn1_cvcert + 1, &cvc->signature, &cvc->signatureLen, 0); + r = sc_pkcs15emu_sc_hsm_format_asn1_cvcert( + asn1_cvcert, C_ASN1_CVCERT_SIZE, + asn1_cvc_body, C_ASN1_CVC_BODY_SIZE, + asn1_cvc_pubkey, C_ASN1_CVC_PUBKEY_SIZE, + cvc); + LOG_TEST_RET(card->ctx, r, "sc_asn1_entry array too small"); - sc_format_asn1_entry(asn1_cvc , &asn1_cvcert, NULL, 0); + sc_format_asn1_entry(asn1_cvc, asn1_cvcert, NULL, 0); - sc_format_asn1_entry(asn1_authreq , &asn1_cvcert, NULL, 0); - sc_format_asn1_entry(asn1_authreq + 1, &cvc->outer_car, &lenoutercar, 0); - sc_format_asn1_entry(asn1_authreq + 2, &cvc->outerSignature, &cvc->outerSignatureLen, 0); + r = sc_pkcs15emu_sc_hsm_format_asn1_req( + asn1_authreq, C_ASN1_AUTHREQ_SIZE, + asn1_cvcert, cvc); + LOG_TEST_RET(card->ctx, r, "sc_asn1_entry array too small"); - sc_format_asn1_entry(asn1_req , &asn1_authreq, NULL, 0); + sc_format_asn1_entry(asn1_req, asn1_authreq, NULL, 0); /* sc_asn1_print_tags(*buf, *buflen); */ @@ -298,10 +478,337 @@ int sc_pkcs15emu_sc_hsm_decode_cvc(sc_pkcs15_card_t * p15card, LOG_TEST_RET(card->ctx, r, "Could not decode card verifiable certificate"); + fixup_cvc_printable_string_lengths(cvc); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } +struct sc_asn1_cvc_format { + struct sc_asn1_entry asn1_cvc[C_ASN1_CVC_SIZE]; + struct sc_asn1_entry asn1_cvcert[C_ASN1_CVCERT_SIZE]; + struct sc_asn1_entry asn1_cvc_body[C_ASN1_CVC_BODY_SIZE]; + struct sc_asn1_entry asn1_cvc_pubkey[C_ASN1_CVC_PUBKEY_SIZE]; +}; + +struct sc_asn1_sc_hsm_pka_callback_arg { + sc_context_t *ctx; + struct sc_asn1_entry *next_entry; + sc_cvc_pka_component_t *component; +}; + + +struct sc_asn1_sc_hsm_pka_data { + struct sc_asn1_entry asn1_public_key_req[C_ASN1_REQ_SIZE]; + struct sc_asn1_entry asn1_public_key_authreq[C_ASN1_AUTHREQ_SIZE]; + struct sc_asn1_cvc_format asn1_public_key_cvc; + struct sc_asn1_cvc_format asn1_device_cvc; + struct sc_asn1_cvc_format asn1_dica_cvc; + struct sc_asn1_sc_hsm_pka_callback_arg public_key_req_arg; + struct sc_asn1_sc_hsm_pka_callback_arg device_arg; + struct sc_asn1_sc_hsm_pka_callback_arg dica_arg; +}; + +struct sc_asn1_sc_hsm_pka_new_format { + struct sc_asn1_entry seq[C_ASN1_SC_HSM_PKA_NEW_SIZE]; + struct sc_asn1_sc_hsm_pka_data data; + struct sc_object_id oid; +}; + +struct sc_asn1_sc_hsm_pka_old_format { + struct sc_asn1_entry seq[C_ASN1_SC_HSM_PKA_OLD_SIZE]; + struct sc_asn1_sc_hsm_pka_data data; +}; + +/* + * Saves the current pointer then continues to decode + */ +static int sc_asn1_sc_hsm_pka_set_ptr_callback( + sc_context_t *nctx, void *arg, const u8 *obj, + size_t objlen, int depth) +{ + struct sc_asn1_sc_hsm_pka_callback_arg *carg = arg; + + carg->component->ptr = obj; + carg->component->len = objlen; + + return sc_asn1_decode(carg->ctx, carg->next_entry, obj, objlen, NULL, NULL); +} + +static int sc_asn1_sc_hsm_pka_data_init(sc_context_t *ctx, + struct sc_asn1_sc_hsm_pka_data *data, + sc_cvc_pka_t *pka) +{ + int r; + + data->public_key_req_arg.ctx = ctx; + data->device_arg.ctx = ctx; + data->dica_arg.ctx = ctx; + + /* public key info is in an authenticatedrequest (0x67) */ + r = sc_pkcs15emu_sc_hsm_format_asn1_cvcert( + data->asn1_public_key_cvc.asn1_cvcert, C_ASN1_CVCERT_SIZE, + data->asn1_public_key_cvc.asn1_cvc_body, C_ASN1_CVC_BODY_SIZE, + data->asn1_public_key_cvc.asn1_cvc_pubkey, C_ASN1_CVC_PUBKEY_SIZE, + &pka->public_key_req.cvc); + LOG_TEST_RET(ctx, r, "sc_asn1_entry too small"); + + r = sc_pkcs15emu_sc_hsm_format_asn1_req( + data->asn1_public_key_authreq, C_ASN1_AUTHREQ_SIZE, + data->asn1_public_key_cvc.asn1_cvcert, + &pka->public_key_req.cvc); + LOG_TEST_RET(ctx, r, "sc_asn1_entry too small"); + + /* + * insert a callback between req and authreq + * the HSM expects the contents of the 0x67 authenticatedrequest tag (not + * including the 0x67 tag itself) + */ + sc_copy_asn1_entry(c_asn1_req, data->asn1_public_key_req); + data->asn1_public_key_req[0].type = SC_ASN1_CALLBACK; + data->public_key_req_arg.component = &pka->public_key_req; + data->public_key_req_arg.next_entry = data->asn1_public_key_authreq; + sc_format_asn1_entry(data->asn1_public_key_req, + sc_asn1_sc_hsm_pka_set_ptr_callback, + &data->public_key_req_arg, 0); + + /* device CVC is a certificate (0x7F21) */ + r = sc_pkcs15emu_sc_hsm_format_asn1_cvcert( + data->asn1_device_cvc.asn1_cvcert, C_ASN1_CVCERT_SIZE, + data->asn1_device_cvc.asn1_cvc_body, C_ASN1_CVC_BODY_SIZE, + data->asn1_device_cvc.asn1_cvc_pubkey, C_ASN1_CVC_PUBKEY_SIZE, + &pka->device.cvc); + LOG_TEST_RET(ctx, r, "sc_asn1_entry too small"); + + /* + * insert a callback between asn1_cvc and asn1_cvcert + * the HSM expects the contents of the 0x7F21 CVC tag (not including the + * 0x7F21 tag itself) + */ + sc_copy_asn1_entry(c_asn1_cvc, data->asn1_device_cvc.asn1_cvc); + data->asn1_device_cvc.asn1_cvc[0].type = SC_ASN1_CALLBACK; + data->device_arg.component = &pka->device; + data->device_arg.next_entry = data->asn1_device_cvc.asn1_cvcert; + sc_format_asn1_entry(data->asn1_device_cvc.asn1_cvc, + sc_asn1_sc_hsm_pka_set_ptr_callback, + &data->device_arg, 0); + + /* device issuer CA CVC is a certificate (0x7F21) */ + r = sc_pkcs15emu_sc_hsm_format_asn1_cvcert( + data->asn1_dica_cvc.asn1_cvcert, C_ASN1_CVCERT_SIZE, + data->asn1_dica_cvc.asn1_cvc_body, C_ASN1_CVC_BODY_SIZE, + data->asn1_dica_cvc.asn1_cvc_pubkey, C_ASN1_CVC_PUBKEY_SIZE, + &pka->dica.cvc); + LOG_TEST_RET(ctx, r, "sc_asn1_entry too small"); + /* + * insert a callback between asn1_cvc and asn1_cvcert + * the HSM expects the contents of the 0x7F21 CVC tag (not including the + * 0x7F21 tag itself) + */ + sc_copy_asn1_entry(c_asn1_cvc, data->asn1_dica_cvc.asn1_cvc); + data->asn1_dica_cvc.asn1_cvc[0].type = SC_ASN1_CALLBACK; + data->dica_arg.component = &pka->dica; + data->dica_arg.next_entry = data->asn1_dica_cvc.asn1_cvcert; + sc_format_asn1_entry(data->asn1_dica_cvc.asn1_cvc, + sc_asn1_sc_hsm_pka_set_ptr_callback, + &data->dica_arg, 0); + return SC_SUCCESS; +} + +/* + * For SmartCard HSMs, this is the older format for registering a public key + * for public key authentication. + * + * @param buf: *buf should point to the first tag in the sequence + * + * SEQUENCE (0x30) + * authenticatedrequest for public key details (0x67) + * device CVC (0x7F21) + * device issuer CA CVC (0x7F21) + */ +static int decode_pka_old_format(sc_pkcs15_card_t *p15card, + const u8 **buf, size_t *buflen, + sc_cvc_pka_t *pka) +{ + int r; + sc_card_t *card; + struct sc_asn1_sc_hsm_pka_old_format *format = NULL; + + card = p15card->card; + + format = calloc(1, sizeof(*format)); + if (format == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + + r = sc_asn1_sc_hsm_pka_data_init(card->ctx, &format->data, pka); + LOG_TEST_GOTO_ERR(card->ctx, r, "sc_asn1_entry array too small"); + + sc_copy_asn1_entry(c_asn1_sc_hsm_pka_old_format, format->seq); + format->seq[0] = format->data.asn1_public_key_req[0]; + format->seq[1] = format->data.asn1_device_cvc.asn1_cvc[0]; + format->seq[2] = format->data.asn1_dica_cvc.asn1_cvc[0]; + + r = sc_asn1_decode(p15card->card->ctx, format->seq, *buf, *buflen, + buf, buflen); + LOG_TEST_GOTO_ERR(card->ctx, r, + "Could not decode ASN.1 for public key file's old format"); + + r = SC_SUCCESS; + /* fall-through */ + +err: + free(format); + format = NULL; + return r; +} + +/* + * For SmartCard HSMs, this is the newer format for registering a public key + * for public key authentication. + * + * @param buf: *buf should point to the first tag after the sequence tag + * + * 1.3.6.1.4.1.24991 is the CardContact organization + * The 4.3.1 part is from inspecting their exported public key, but it doesn't + * seem to be publicly registered. + * + * SEQUENCE (0x30) + * OID (0x6) 1.3.6.1.4.1.24991.4.3.1 + * Application 1 (0x61) + * device CVC (0x7F21) + * Application 2 (0x62) + * device issuer CA CVC (0x7F21) + * Application 3 (0x63) + * authenticatedrequest for public key details (0x67) + */ +static int decode_pka_new_format(sc_pkcs15_card_t *p15card, + const u8 **buf, size_t *buflen, + sc_cvc_pka_t *pka) +{ + int r; + sc_card_t *card; + struct sc_asn1_sc_hsm_pka_new_format *format = NULL; + + card = p15card->card; + + format = calloc(1, sizeof(*format)); + if (format == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + + r = sc_asn1_sc_hsm_pka_data_init(card->ctx, &format->data, pka); + LOG_TEST_GOTO_ERR(card->ctx, r, "sc_asn1_entry array too small"); + + sc_copy_asn1_entry(c_asn1_sc_hsm_pka_new_format, format->seq); + sc_format_asn1_entry(&format->seq[0], &format->oid, NULL, 0); + sc_format_asn1_entry(&format->seq[1], + format->data.asn1_dica_cvc.asn1_cvc, NULL, 0); + sc_format_asn1_entry(&format->seq[2], + format->data.asn1_device_cvc.asn1_cvc, NULL, 0); + sc_format_asn1_entry(&format->seq[3], + format->data.asn1_public_key_req, NULL, 0); + + r = sc_asn1_decode(p15card->card->ctx, format->seq, *buf, *buflen, + buf, buflen); + LOG_TEST_GOTO_ERR(card->ctx, r, + "Could not decode ASN.1 for public key file's new format"); + + if (sc_compare_oid(&format->oid, &sc_hsm_public_key_oid) == 0) { + /* sc_dump_oid uses static memory */ + sc_log(p15card->card->ctx, "OID %s did not match expected value", + sc_dump_oid(&format->oid)); + r = -1; + goto err; + } + + r = SC_SUCCESS; + /* fall-through */ + +err: + free(format); + format = NULL; + return r; +} + +/* + * @param pka: will be overwritten, should be uninitialized or memset to 0 + */ +int sc_pkcs15emu_sc_hsm_decode_pka(sc_pkcs15_card_t *p15card, + const u8 **buf, size_t *buflen, + sc_cvc_pka_t *pka) +{ + int r; + const u8 *curr; + const u8 *peek; + unsigned int cla, tag; + size_t taglen; + size_t currlen; + sc_card_t *card; + + memset(pka, 0, sizeof(*pka)); + + card = p15card->card; + curr = *buf; + currlen = *buflen; + + /* first tag should be sequence */ + r = sc_asn1_read_tag(&curr, currlen, &cla, &tag, &taglen); + LOG_TEST_GOTO_ERR(card->ctx, r, + "Could not decode first sequence tag for public key file"); + currlen = *buflen - (curr - *buf); + + if ((cla != (SC_ASN1_TAG_UNIVERSAL|SC_ASN1_TAG_CONSTRUCTED)) || + (tag != SC_ASN1_TAG_SEQUENCE)) { + sc_log(card->ctx, + "Expected sequence tag, but got tag %u class 0x%x", tag, cla); + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto err; + } + + /* next tag is either OID (new format) or 0x67 (old format) */ + peek = curr; + r = sc_asn1_read_tag(&peek, currlen, &cla, &tag, &taglen); + LOG_TEST_GOTO_ERR(card->ctx, r, + "Could not decode first sequence element tag for public key file"); + + if (tag == SC_ASN1_TAG_OBJECT) { + /* OID means it's the new format */ + r = decode_pka_new_format(p15card, &curr, &currlen, pka); + LOG_TEST_GOTO_ERR(card->ctx, r, + "Could not decode public key file new format"); + } else if ((cla == (SC_ASN1_TAG_APPLICATION|SC_ASN1_TAG_CONSTRUCTED)) && + (tag == 7)) { + /* + * if it's authenticatedrequest (Application 7 / 0x67), then attempt + * to parse the old format + */ + r = decode_pka_old_format(p15card, &curr, &currlen, pka); + LOG_TEST_GOTO_ERR(card->ctx, r, + "Could not decode authenticatedrequest for public key file"); + } else { + sc_log(card->ctx, + "Unexpected tag %u class 0x%x for first element of sequence", + tag, cla); + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto err; + } + + fixup_cvc_printable_string_lengths(&pka->public_key_req.cvc); + fixup_cvc_printable_string_lengths(&pka->device.cvc); + fixup_cvc_printable_string_lengths(&pka->dica.cvc); + + *buf = curr; + *buflen = *buflen - (curr - *buf); + + return SC_SUCCESS; + +err: + sc_pkcs15emu_sc_hsm_free_cvc_pka(pka); + return r; +} /* * Encode a card verifiable certificate as defined in TR-03110. @@ -315,8 +822,6 @@ int sc_pkcs15emu_sc_hsm_encode_cvc(sc_pkcs15_card_t * p15card, struct sc_asn1_entry asn1_cvcert[C_ASN1_CVCERT_SIZE]; struct sc_asn1_entry asn1_cvc_body[C_ASN1_CVC_BODY_SIZE]; struct sc_asn1_entry asn1_cvc_pubkey[C_ASN1_CVC_PUBKEY_SIZE]; - size_t lenchr; - size_t lencar; int r; sc_copy_asn1_entry(c_asn1_cvc, asn1_cvc); @@ -346,11 +851,9 @@ int sc_pkcs15emu_sc_hsm_encode_cvc(sc_pkcs15_card_t * p15card, } sc_format_asn1_entry(asn1_cvc_body , &cvc->cpi, NULL, 1); - lencar = strnlen(cvc->car, sizeof cvc->car); - sc_format_asn1_entry(asn1_cvc_body + 1, &cvc->car, &lencar, 1); + sc_format_asn1_entry(asn1_cvc_body + 1, &cvc->car, &cvc->carLen, 1); sc_format_asn1_entry(asn1_cvc_body + 2, &asn1_cvc_pubkey, NULL, 1); - lenchr = strnlen(cvc->chr, sizeof cvc->chr); - sc_format_asn1_entry(asn1_cvc_body + 3, &cvc->chr, &lenchr, 1); + sc_format_asn1_entry(asn1_cvc_body + 3, &cvc->chr, &cvc->chrLen, 1); sc_format_asn1_entry(asn1_cvcert , &asn1_cvc_body, NULL, 1); if (cvc->signature && (cvc->signatureLen > 0)) { @@ -387,7 +890,11 @@ int sc_pkcs15emu_sc_hsm_get_curve_oid(sc_cvc_t *cvc, const struct sc_lv_data **o int i; for (i = 0; curves[i].oid.value; i++) { - if ((curves[i].prime.len == cvc->primeOrModuluslen) && !memcmp(curves[i].prime.value, cvc->primeOrModulus, cvc->primeOrModuluslen)) { + if ((curves[i].prime.len == cvc->primeOrModuluslen) && + !memcmp(curves[i].prime.value, cvc->primeOrModulus, cvc->primeOrModuluslen) && + (curves[i].coefficientA.len == cvc->coefficientAorExponentlen) && + !memcmp(curves[i].coefficientA.value, + cvc->coefficientAorExponent, cvc->coefficientAorExponentlen)) { *oid = &curves[i].oid; return SC_SUCCESS; } @@ -411,8 +918,12 @@ static int sc_pkcs15emu_sc_hsm_get_rsa_public_key(struct sc_context *ctx, sc_cvc pubkey->u.rsa.modulus.data = malloc(pubkey->u.rsa.modulus.len); pubkey->u.rsa.exponent.len = cvc->coefficientAorExponentlen; pubkey->u.rsa.exponent.data = malloc(pubkey->u.rsa.exponent.len); - if (!pubkey->u.rsa.modulus.data || !pubkey->u.rsa.exponent.data) + if (!pubkey->u.rsa.modulus.data || !pubkey->u.rsa.exponent.data) { + free(pubkey->u.rsa.modulus.data); + free(pubkey->u.rsa.exponent.data); + free(pubkey->alg_id); return SC_ERROR_OUT_OF_MEMORY; + } memcpy(pubkey->u.rsa.exponent.data, cvc->coefficientAorExponent, pubkey->u.rsa.exponent.len); memcpy(pubkey->u.rsa.modulus.data, cvc->primeOrModulus, pubkey->u.rsa.modulus.len); @@ -439,7 +950,7 @@ static int sc_pkcs15emu_sc_hsm_get_ec_public_key(struct sc_context *ctx, sc_cvc_ return SC_ERROR_OUT_OF_MEMORY; ecp->der.len = oid->len + 2; - ecp->der.value = calloc(ecp->der.len, 1); + ecp->der.value = calloc(1, ecp->der.len); if (!ecp->der.value) { free(ecp); return SC_ERROR_OUT_OF_MEMORY; @@ -461,14 +972,23 @@ static int sc_pkcs15emu_sc_hsm_get_ec_public_key(struct sc_context *ctx, sc_cvc_ pubkey->alg_id->params = ecp; pubkey->u.ec.ecpointQ.value = malloc(cvc->publicPointlen); - if (!pubkey->u.ec.ecpointQ.value) + if (!pubkey->u.ec.ecpointQ.value) { + free(pubkey->alg_id); + free(ecp->der.value); + free(ecp); return SC_ERROR_OUT_OF_MEMORY; + } memcpy(pubkey->u.ec.ecpointQ.value, cvc->publicPoint, cvc->publicPointlen); pubkey->u.ec.ecpointQ.len = cvc->publicPointlen; pubkey->u.ec.params.der.value = malloc(ecp->der.len); - if (!pubkey->u.ec.params.der.value) + if (!pubkey->u.ec.params.der.value) { + free(pubkey->u.ec.ecpointQ.value); + free(pubkey->alg_id); + free(ecp->der.value); + free(ecp); return SC_ERROR_OUT_OF_MEMORY; + } memcpy(pubkey->u.ec.params.der.value, ecp->der.value, ecp->der.len); pubkey->u.ec.params.der.len = ecp->der.len; @@ -493,6 +1013,10 @@ int sc_pkcs15emu_sc_hsm_get_public_key(struct sc_context *ctx, sc_cvc_t *cvc, st void sc_pkcs15emu_sc_hsm_free_cvc(sc_cvc_t *cvc) { + if (cvc->outerSignature) { + free(cvc->outerSignature); + cvc->outerSignature = NULL; + } if (cvc->signature) { free(cvc->signature); cvc->signature = NULL; @@ -527,7 +1051,13 @@ void sc_pkcs15emu_sc_hsm_free_cvc(sc_cvc_t *cvc) } } - +void sc_pkcs15emu_sc_hsm_free_cvc_pka(sc_cvc_pka_t *pka) +{ + sc_pkcs15emu_sc_hsm_free_cvc(&pka->public_key_req.cvc); + sc_pkcs15emu_sc_hsm_free_cvc(&pka->device.cvc); + sc_pkcs15emu_sc_hsm_free_cvc(&pka->dica.cvc); + memset(pka, 0, sizeof(*pka)); +} static int sc_pkcs15emu_sc_hsm_add_pubkey(sc_pkcs15_card_t *p15card, u8 *efbin, size_t len, sc_pkcs15_prkey_info_t *key_info, char *label) { @@ -554,11 +1084,20 @@ static int sc_pkcs15emu_sc_hsm_add_pubkey(sc_pkcs15_card_t *p15card, u8 *efbin, memset(&pubkey_obj, 0, sizeof(pubkey_obj)); r = sc_pkcs15_encode_pubkey(ctx, &pubkey, &pubkey_obj.content.value, &pubkey_obj.content.len); - LOG_TEST_RET(ctx, r, "Could not encode public key"); + if (r != SC_SUCCESS) { + sc_pkcs15_erase_pubkey(&pubkey); + LOG_TEST_RET(ctx, r, "Could not encode public key"); + } r = sc_pkcs15_encode_pubkey(ctx, &pubkey, &pubkey_info.direct.raw.value, &pubkey_info.direct.raw.len); - LOG_TEST_RET(ctx, r, "Could not encode public key"); + if (r != SC_SUCCESS) { + sc_pkcs15_erase_pubkey(&pubkey); + LOG_TEST_RET(ctx, r, "Could not encode public key"); + } r = sc_pkcs15_encode_pubkey_as_spki(ctx, &pubkey, &pubkey_info.direct.spki.value, &pubkey_info.direct.spki.len); - LOG_TEST_RET(ctx, r, "Could not encode public key"); + if (r != SC_SUCCESS) { + sc_pkcs15_erase_pubkey(&pubkey); + LOG_TEST_RET(ctx, r, "Could not encode public key"); + } pubkey_info.id = key_info->id; strlcpy(pubkey_obj.label, label, sizeof(pubkey_obj.label)); @@ -570,9 +1109,11 @@ static int sc_pkcs15emu_sc_hsm_add_pubkey(sc_pkcs15_card_t *p15card, u8 *efbin, } else { /* TODO fix if support of non multiple of 8 curves are added */ pubkey_info.field_length = cvc.primeOrModuluslen << 3; - pubkey_info.usage = SC_PKCS15_PRKEY_USAGE_VERIFY; + pubkey_info.usage = SC_PKCS15_PRKEY_USAGE_VERIFY|SC_PKCS15_PRKEY_USAGE_DERIVE; r = sc_pkcs15emu_add_ec_pubkey(p15card, &pubkey_obj, &pubkey_info); } + if (r < 0) + free(pubkey_info.direct.spki.value); LOG_TEST_RET(ctx, r, "Could not add public key"); sc_pkcs15emu_sc_hsm_free_cvc(&cvc); @@ -600,19 +1141,24 @@ static int sc_pkcs15emu_sc_hsm_add_prkd(sc_pkcs15_card_t * p15card, u8 keyid) { size_t len; int r; + if (keyid == 0) { + // Device authentication key does not have PKCS#15 meta data + return SC_SUCCESS; + } + fid[0] = PRKD_PREFIX; fid[1] = keyid; /* Try to select a related EF containing the PKCS#15 description of the key */ len = sizeof efbin; r = read_file(p15card, fid, efbin, &len, 1); - LOG_TEST_RET(card->ctx, r, "Could not read EF.PRKD"); + LOG_TEST_RET(card->ctx, r, "Skipping optional EF.PRKD"); ptr = efbin; memset(&prkd, 0, sizeof(prkd)); r = sc_pkcs15_decode_prkdf_entry(p15card, &prkd, (const u8 **)&ptr, &len); - LOG_TEST_RET(card->ctx, r, "Could not decode EF.PRKD"); + LOG_TEST_RET(card->ctx, r, "Skipping optional EF.PRKD"); /* All keys require user PIN authentication */ prkd.auth_id.len = 1; @@ -630,6 +1176,10 @@ static int sc_pkcs15emu_sc_hsm_add_prkd(sc_pkcs15_card_t * p15card, u8 keyid) { if (prkd.type == SC_PKCS15_TYPE_PRKEY_RSA) { r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkd, key_info); } else { + if (key_info->field_length == 528) { + // Fix a bug for secp521 key generated with OpenSCDP + key_info->field_length = 521; + } r = sc_pkcs15emu_add_ec_prkey(p15card, &prkd, key_info); } @@ -642,8 +1192,6 @@ static int sc_pkcs15emu_sc_hsm_add_prkd(sc_pkcs15_card_t * p15card, u8 keyid) { r = read_file(p15card, fid, efbin, &len, 0); LOG_TEST_RET(card->ctx, r, "Could not read EF"); - LOG_TEST_RET(card->ctx, r, "Could not read EF"); - if (efbin[0] == 0x67) { /* Decode CSR and create public key object */ sc_pkcs15emu_sc_hsm_add_pubkey(p15card, efbin, len, key_info, prkd.label); free(key_info); @@ -702,13 +1250,13 @@ static int sc_pkcs15emu_sc_hsm_add_dcod(sc_pkcs15_card_t * p15card, u8 id) { /* Try to select a related EF containing the PKCS#15 description of the data */ len = sizeof efbin; r = read_file(p15card, fid, efbin, &len, 1); - LOG_TEST_RET(card->ctx, r, "Could not read EF.DCOD"); + LOG_TEST_RET(card->ctx, r, "Skipping optional EF.DCOD"); ptr = efbin; memset(&data_obj, 0, sizeof(data_obj)); r = sc_pkcs15_decode_dodf_entry(p15card, &data_obj, &ptr, &len); - LOG_TEST_RET(card->ctx, r, "Could not decode EF.DCOD"); + LOG_TEST_RET(card->ctx, r, "Could not decode optional EF.DCOD"); data_info = (sc_pkcs15_data_info_t *)data_obj.data; @@ -741,13 +1289,15 @@ static int sc_pkcs15emu_sc_hsm_add_cd(sc_pkcs15_card_t * p15card, u8 id) { /* Try to select a related EF containing the PKCS#15 description of the data */ len = sizeof efbin; r = read_file(p15card, fid, efbin, &len, 1); - LOG_TEST_RET(card->ctx, r, "Could not read EF.DCOD"); + LOG_TEST_RET(card->ctx, r, "Skipping optional EF.CDF"); ptr = efbin; memset(&obj, 0, sizeof(obj)); r = sc_pkcs15_decode_cdf_entry(p15card, &obj, &ptr, &len); - LOG_TEST_RET(card->ctx, r, "Could not decode EF.CD"); + if (obj.data == NULL && r >= SC_SUCCESS) + r = SC_ERROR_OBJECT_NOT_FOUND; + LOG_TEST_RET(card->ctx, r, "Skipping optional EF.CDF"); cert_info = (sc_pkcs15_cert_info_t *)obj.data; @@ -772,10 +1322,10 @@ static int sc_pkcs15emu_sc_hsm_read_tokeninfo (sc_pkcs15_card_t * p15card) /* Read token info */ len = sizeof efbin; r = read_file(p15card, (u8 *) "\x2F\x03", efbin, &len, 1); - LOG_TEST_RET(card->ctx, r, "Could not read EF.TokenInfo"); + LOG_TEST_RET(card->ctx, r, "Skipping optional EF.TokenInfo"); r = sc_pkcs15_parse_tokeninfo(card->ctx, p15card->tokeninfo, efbin, len); - LOG_TEST_RET(card->ctx, r, "Could not decode EF.TokenInfo"); + LOG_TEST_RET(card->ctx, r, "Skipping optional EF.TokenInfo"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -823,8 +1373,10 @@ static int sc_pkcs15emu_sc_hsm_init (sc_pkcs15_card_t * p15card) p15card->card->version.hw_major = 24; /* JCOP 2.4.1r3 */ p15card->card->version.hw_minor = 13; - p15card->card->version.fw_major = file->prop_attr[file->prop_attr_len - 2]; - p15card->card->version.fw_minor = file->prop_attr[file->prop_attr_len - 1]; + if (file && file->prop_attr && file->prop_attr_len >= 2) { + p15card->card->version.fw_major = file->prop_attr[file->prop_attr_len - 2]; + p15card->card->version.fw_minor = file->prop_attr[file->prop_attr_len - 1]; + } sc_file_free(file); @@ -835,14 +1387,16 @@ static int sc_pkcs15emu_sc_hsm_init (sc_pkcs15_card_t * p15card) } else { len = sizeof efbin; r = read_file(p15card, (u8 *) "\x2F\x02", efbin, &len, 1); - LOG_TEST_RET(card->ctx, r, "Could not select EF.C_DevAut"); - - /* save EF_C_DevAut for further use */ - ptr = realloc(priv->EF_C_DevAut, len); - if (ptr) { - memcpy(ptr, efbin, len); - priv->EF_C_DevAut = ptr; - priv->EF_C_DevAut_len = len; + LOG_TEST_RET(card->ctx, r, "Skipping optional EF.C_DevAut"); + + if (len > 0) { + /* save EF_C_DevAut for further use */ + ptr = realloc(priv->EF_C_DevAut, len); + if (ptr) { + memcpy(ptr, efbin, len); + priv->EF_C_DevAut = ptr; + priv->EF_C_DevAut_len = len; + } } ptr = efbin; @@ -855,7 +1409,12 @@ static int sc_pkcs15emu_sc_hsm_init (sc_pkcs15_card_t * p15card) sc_pkcs15emu_sc_hsm_read_tokeninfo(p15card); if (p15card->tokeninfo->label == NULL) { - p15card->tokeninfo->label = strdup("SmartCard-HSM"); + if (p15card->card->type == SC_CARD_TYPE_SC_HSM_GOID + || p15card->card->type == SC_CARD_TYPE_SC_HSM_SOC) { + p15card->tokeninfo->label = strdup("GoID"); + } else { + p15card->tokeninfo->label = strdup("SmartCard-HSM"); + } if (p15card->tokeninfo->label == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } @@ -866,22 +1425,35 @@ static int sc_pkcs15emu_sc_hsm_init (sc_pkcs15_card_t * p15card) } if (p15card->tokeninfo->manufacturer_id == NULL) { - p15card->tokeninfo->manufacturer_id = strdup("www.CardContact.de"); - if (p15card->tokeninfo->manufacturer_id == NULL) + if (p15card->card->type == SC_CARD_TYPE_SC_HSM_GOID + || p15card->card->type == SC_CARD_TYPE_SC_HSM_SOC) { + p15card->tokeninfo->manufacturer_id = strdup("Bundesdruckerei GmbH"); + } else { + p15card->tokeninfo->manufacturer_id = strdup("www.CardContact.de"); + } + if (p15card->tokeninfo->manufacturer_id == NULL) { + sc_pkcs15_card_clear(p15card); LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } } appinfo->label = strdup(p15card->tokeninfo->label); - if (appinfo->label == NULL) + if (appinfo->label == NULL) { + sc_pkcs15_card_clear(p15card); LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } len = strnlen(devcert.chr, sizeof devcert.chr); /* Strip last 5 digit sequence number from CHR */ - assert(len >= 8); + if (len < 8) + return SC_ERROR_INTERNAL; len -= 5; - p15card->tokeninfo->serial_number = calloc(len + 1, 1); - if (p15card->tokeninfo->serial_number == NULL) + free(p15card->tokeninfo->serial_number); + p15card->tokeninfo->serial_number = calloc(1, len + 1); + if (p15card->tokeninfo->serial_number == NULL) { + sc_pkcs15_card_clear(p15card); LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } memcpy(p15card->tokeninfo->serial_number, devcert.chr, len); *(p15card->tokeninfo->serial_number + len) = 0; @@ -897,6 +1469,7 @@ static int sc_pkcs15emu_sc_hsm_init (sc_pkcs15_card_t * p15card) pin_info.auth_id.value[0] = 1; pin_info.path.aid = sc_hsm_aid; pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; + pin_info.auth_method = SC_AC_CHV; pin_info.attrs.pin.reference = 0x81; pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_LOCAL|SC_PKCS15_PIN_FLAG_INITIALIZED|SC_PKCS15_PIN_FLAG_EXCHANGE_REF_DATA; pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; @@ -912,9 +1485,22 @@ static int sc_pkcs15emu_sc_hsm_init (sc_pkcs15_card_t * p15card) strlcpy(pin_obj.label, "UserPIN", sizeof(pin_obj.label)); pin_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE|SC_PKCS15_CO_FLAG_MODIFIABLE; - r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); - if (r < 0) - LOG_FUNC_RETURN(card->ctx, r); + pin_obj.data = &pin_info; + + r = sc_pkcs15_get_pin_info(p15card, &pin_obj); + + if (r != SC_ERROR_DATA_OBJECT_NOT_FOUND) { + if (r < 0) { + sc_pkcs15_card_clear(p15card); + LOG_FUNC_RETURN(card->ctx, r); + } + + r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); + if (r < 0) { + sc_pkcs15_card_clear(p15card); + LOG_FUNC_RETURN(card->ctx, r); + } + } memset(&pin_info, 0, sizeof(pin_info)); memset(&pin_obj, 0, sizeof(pin_obj)); @@ -923,8 +1509,9 @@ static int sc_pkcs15emu_sc_hsm_init (sc_pkcs15_card_t * p15card) pin_info.auth_id.value[0] = 2; pin_info.path.aid = sc_hsm_aid; pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; + pin_info.auth_method = SC_AC_CHV; pin_info.attrs.pin.reference = 0x88; - pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_LOCAL|SC_PKCS15_PIN_FLAG_INITIALIZED|SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED|SC_PKCS15_PIN_FLAG_SO_PIN; + pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_LOCAL|SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED|SC_PKCS15_PIN_FLAG_SO_PIN; pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_BCD; pin_info.attrs.pin.min_length = 16; pin_info.attrs.pin.stored_length = 0; @@ -936,12 +1523,30 @@ static int sc_pkcs15emu_sc_hsm_init (sc_pkcs15_card_t * p15card) strlcpy(pin_obj.label, "SOPIN", sizeof(pin_obj.label)); pin_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; + pin_obj.data = &pin_info; + + r = sc_pkcs15_get_pin_info(p15card, &pin_obj); + + if (r != SC_ERROR_DATA_OBJECT_NOT_FOUND) { + pin_info.attrs.pin.flags |= SC_PKCS15_PIN_FLAG_INITIALIZED; + } else { + r = SC_SUCCESS; + } + + if (r < 0) { + sc_pkcs15_card_clear(p15card); + LOG_FUNC_RETURN(card->ctx, r); + } + r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); - if (r < 0) + if (r < 0) { + sc_pkcs15_card_clear(p15card); LOG_FUNC_RETURN(card->ctx, r); + } - if (card->type == SC_CARD_TYPE_SC_HSM_SOC) { + if (card->type == SC_CARD_TYPE_SC_HSM_SOC + || card->type == SC_CARD_TYPE_SC_HSM_GOID) { /* SC-HSM of this type always has a PIN-Pad */ r = SC_SUCCESS; } else { @@ -950,7 +1555,7 @@ static int sc_pkcs15emu_sc_hsm_init (sc_pkcs15_card_t * p15card) pindata.pin_type = SC_AC_CHV; pindata.pin_reference = 0x85; - r = sc_pin_cmd(card, &pindata, NULL); + r = sc_pin_cmd(card, &pindata); } if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) { memset(&pindata, 0, sizeof(pindata)); @@ -958,14 +1563,16 @@ static int sc_pkcs15emu_sc_hsm_init (sc_pkcs15_card_t * p15card) pindata.pin_type = SC_AC_CHV; pindata.pin_reference = 0x86; - r = sc_pin_cmd(card, &pindata, NULL); + r = sc_pin_cmd(card, &pindata); } - if ((r != SC_ERROR_DATA_OBJECT_NOT_FOUND) && (r != SC_ERROR_INCORRECT_PARAMETERS)) + if ((r != SC_ERROR_DATA_OBJECT_NOT_FOUND) && (r != SC_ERROR_INCORRECT_PARAMETERS) && (r != SC_ERROR_REF_DATA_NOT_USABLE)) card->caps |= SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH; filelistlength = sc_list_files(card, filelist, sizeof(filelist)); + if (filelistlength < 0) + sc_pkcs15_card_clear(p15card); LOG_TEST_RET(card->ctx, filelistlength, "Could not enumerate file and key identifier"); for (i = 0; i < filelistlength; i += 2) { @@ -991,16 +1598,12 @@ static int sc_pkcs15emu_sc_hsm_init (sc_pkcs15_card_t * p15card) int sc_pkcs15emu_sc_hsm_init_ex(sc_pkcs15_card_t *p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t *opts) + struct sc_aid *aid) { - if (opts && (opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)) { - return sc_pkcs15emu_sc_hsm_init(p15card); - } else { - if (p15card->card->type != SC_CARD_TYPE_SC_HSM - && p15card->card->type != SC_CARD_TYPE_SC_HSM_SOC) { - return SC_ERROR_WRONG_CARD; - } - return sc_pkcs15emu_sc_hsm_init(p15card); + if (p15card->card->type != SC_CARD_TYPE_SC_HSM + && p15card->card->type != SC_CARD_TYPE_SC_HSM_SOC + && p15card->card->type != SC_CARD_TYPE_SC_HSM_GOID) { + return SC_ERROR_WRONG_CARD; } + return sc_pkcs15emu_sc_hsm_init(p15card); } diff --git a/src/libopensc/pkcs15-sec.c b/src/libopensc/pkcs15-sec.c index fbdd7cf096..835a75e2a9 100644 --- a/src/libopensc/pkcs15-sec.c +++ b/src/libopensc/pkcs15-sec.c @@ -2,7 +2,7 @@ * pkcs15-sec.c: PKCS#15 cryptography functions * * Copyright (C) 2001, 2002 Juha Yrjölä - * Copyrigth (C) 2007 Nils Larsch + * Copyright (C) 2007 Nils Larsch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -16,10 +16,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -32,39 +32,82 @@ #include "internal.h" #include "pkcs15.h" +#include "pkcs11/pkcs11.h" + +static int sec_env_add_param(sc_security_env_t* se, const sc_sec_env_param_t* p) +{ + size_t i; + + if (!se || !p) + return SC_ERROR_INCORRECT_PARAMETERS; + + for (i = 0; i < SC_SEC_ENV_MAX_PARAMS; i++) { + if (se->params[i].value == NULL) { + se->params[i] = *p; + + return SC_SUCCESS; + } + } + + return SC_ERROR_TOO_MANY_OBJECTS; +} + + +static int get_file_path(const struct sc_pkcs15_object* obj, sc_path_t* path) +{ + if (!path) + return SC_ERROR_INCORRECT_PARAMETERS; + + const struct sc_pkcs15_prkey_info *prkey = (const struct sc_pkcs15_prkey_info *) obj->data; + const struct sc_pkcs15_skey_info *skey = (const struct sc_pkcs15_skey_info *) obj->data; + + if ((obj->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY) { + *path = prkey->path; + } + else if ((obj->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_SKEY) { + *path = skey->path; + } + else + return SC_ERROR_INCORRECT_PARAMETERS; + + return SC_SUCCESS; +} + static int select_key_file(struct sc_pkcs15_card *p15card, - const struct sc_pkcs15_prkey_info *prkey, + const struct sc_pkcs15_object *key, sc_security_env_t *senv) { sc_context_t *ctx = p15card->card->ctx; + sc_path_t orig_path; sc_path_t path, file_id; int r; LOG_FUNC_CALLED(ctx); + LOG_TEST_RET(ctx, get_file_path(key, &orig_path), "Could not get key file path."); memset(&path, 0, sizeof(sc_path_t)); memset(&file_id, 0, sizeof(sc_path_t)); /* TODO: Why file_app may be NULL -- at least 3F00 has to be present? * Check validity of the following assumption. */ /* For pkcs15-emulated cards, the file_app may be NULL, - * in that case we allways assume an absolute path */ - if (!prkey->path.len && prkey->path.aid.len) { + * in that case we always assume an absolute path */ + if (!orig_path.len && orig_path.aid.len) { /* Private key is a SDO allocated in application DF */ - path = prkey->path; + path = orig_path; } - else if (prkey->path.len == 2 && p15card->file_app != NULL) { + else if (orig_path.len == 2 && p15card->file_app != NULL) { /* Path is relative to app. DF */ path = p15card->file_app->path; - file_id = prkey->path; + file_id = orig_path; sc_append_path(&path, &file_id); senv->file_ref = file_id; senv->flags |= SC_SEC_ENV_FILE_REF_PRESENT; } - else if (prkey->path.len > 2) { - path = prkey->path; - memcpy(file_id.value, prkey->path.value + prkey->path.len - 2, 2); + else if (orig_path.len > 2) { + path = orig_path; + memcpy(file_id.value, orig_path.value + orig_path.len - 2, 2); file_id.len = 2; file_id.type = SC_PATH_TYPE_FILE_ID; senv->file_ref = file_id; @@ -90,14 +133,15 @@ static int use_key(struct sc_pkcs15_card *p15card, { int r = SC_SUCCESS; int revalidated_cached_pin = 0; - const struct sc_pkcs15_prkey_info *prkey = (const struct sc_pkcs15_prkey_info *) obj->data; + sc_path_t path; + LOG_TEST_RET(p15card->card->ctx, get_file_path(obj, &path), "Failed to get key file path."); r = sc_lock(p15card->card); LOG_TEST_RET(p15card->card->ctx, r, "sc_lock() failed"); do { - if (prkey->path.len != 0 || prkey->path.aid.len != 0) { - r = select_key_file(p15card, prkey, senv); + if (path.len != 0 || path.aid.len != 0) { + r = select_key_file(p15card, obj, senv); if (r < 0) { sc_log(p15card->card->ctx, "Unable to select private key file"); @@ -122,7 +166,7 @@ static int use_key(struct sc_pkcs15_card *p15card, sc_unlock(p15card->card); - return r; + LOG_FUNC_RETURN(p15card->card->ctx, r); } static int format_senv(struct sc_pkcs15_card *p15card, @@ -131,6 +175,7 @@ static int format_senv(struct sc_pkcs15_card *p15card, { sc_context_t *ctx = p15card->card->ctx; const struct sc_pkcs15_prkey_info *prkey = (const struct sc_pkcs15_prkey_info *) obj->data; + const struct sc_pkcs15_skey_info *skey = (const struct sc_pkcs15_skey_info *) obj->data; memset(senv_out, 0, sizeof(*senv_out)); @@ -138,8 +183,8 @@ static int format_senv(struct sc_pkcs15_card *p15card, * it can get value of card specific 'AlgorithmInfo::algRef'. */ memcpy(senv_out->supported_algos, &p15card->tokeninfo->supported_algos, sizeof(senv_out->supported_algos)); - if ((obj->type & SC_PKCS15_TYPE_CLASS_MASK) != SC_PKCS15_TYPE_PRKEY) - LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "This is not a private key"); + if (!((obj->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY || (obj->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_SKEY)) + LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "This is not a private or secret key"); /* If the key is not native, we can't operate with it. */ if (!prkey->native) @@ -155,6 +200,7 @@ static int format_senv(struct sc_pkcs15_card *p15card, LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } senv_out->algorithm = SC_ALGORITHM_RSA; + senv_out->key_size_bits = prkey->modulus_length; break; case SC_PKCS15_TYPE_PRKEY_GOSTR3410: @@ -166,6 +212,31 @@ static int format_senv(struct sc_pkcs15_card *p15card, LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } senv_out->algorithm = SC_ALGORITHM_GOSTR3410; + senv_out->key_size_bits = prkey->modulus_length; + break; + + case SC_PKCS15_TYPE_PRKEY_EDDSA: + *alg_info_out = sc_card_find_eddsa_alg(p15card->card, prkey->field_length, NULL); + if (*alg_info_out == NULL) { + sc_log(ctx, + "Card does not support EDDSA with field_size %"SC_FORMAT_LEN_SIZE_T"u", + prkey->field_length); + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + } + senv_out->algorithm = SC_ALGORITHM_EDDSA; + senv_out->key_size_bits = prkey->field_length; + break; + + case SC_PKCS15_TYPE_PRKEY_XEDDSA: + *alg_info_out = sc_card_find_xeddsa_alg(p15card->card, prkey->field_length, NULL); + if (*alg_info_out == NULL) { + sc_log(ctx, + "Card does not support XEDDSA with field_size %"SC_FORMAT_LEN_SIZE_T"u", + prkey->field_length); + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + } + senv_out->algorithm = SC_ALGORITHM_XEDDSA; + senv_out->key_size_bits = prkey->field_length; break; case SC_PKCS15_TYPE_PRKEY_EC: @@ -177,10 +248,25 @@ static int format_senv(struct sc_pkcs15_card *p15card, LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } senv_out->algorithm = SC_ALGORITHM_EC; + senv_out->key_size_bits = prkey->field_length; senv_out->flags |= SC_SEC_ENV_ALG_REF_PRESENT; senv_out->algorithm_ref = prkey->field_length; break; + case SC_PKCS15_TYPE_SKEY_GENERIC: + if (skey->key_type != CKK_AES) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Key type not supported"); + *alg_info_out = sc_card_find_alg(p15card->card, SC_ALGORITHM_AES, + skey->value_len, NULL); + if (*alg_info_out == NULL) { + sc_log(ctx, + "Card does not support AES with key length %"SC_FORMAT_LEN_SIZE_T"u", + skey->value_len); + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + } + senv_out->algorithm = SC_ALGORITHM_AES; + senv_out->key_size_bits = skey->value_len; + break; /* add other crypto types here */ default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Key type not supported"); @@ -196,11 +282,11 @@ static int format_senv(struct sc_pkcs15_card *p15card, return SC_SUCCESS; } - + int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *obj, unsigned long flags, - const u8 * in, size_t inlen, u8 *out, size_t outlen) + const u8 * in, size_t inlen, u8 *out, size_t outlen, void *pMechanism) { sc_context_t *ctx = p15card->card->ctx; int r; @@ -210,7 +296,7 @@ int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card, unsigned long pad_flags = 0, sec_flags = 0; LOG_FUNC_CALLED(ctx); - + if (!(prkey->usage & (SC_PKCS15_PRKEY_USAGE_DECRYPT|SC_PKCS15_PRKEY_USAGE_UNWRAP))) LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "This key cannot be used for decryption"); @@ -227,13 +313,33 @@ int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card, LOG_TEST_RET(ctx, r, "use_key() failed"); /* Strip any padding */ - if (pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { + if (pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02) { + unsigned int s = r; + unsigned int key_size = (unsigned int)alg_info->key_length; + r = sc_pkcs1_strip_02_padding_constant_time(ctx, key_size / 8, out, s, out, &s); + /* for keeping PKCS#1 v1.5 depadding constant-time, do not log error here */ + } +#ifdef ENABLE_OPENSSL + else if (pad_flags & SC_ALGORITHM_RSA_PAD_OAEP) { size_t s = r; - r = sc_pkcs1_strip_02_padding(ctx, out, s, out, &s); - LOG_TEST_RET(ctx, r, "Invalid PKCS#1 padding"); + uint8_t *param = NULL; + size_t paramlen = 0; + if (pMechanism != NULL) { + CK_MECHANISM *mech = (CK_MECHANISM *)pMechanism; + if (mech->pParameter && sizeof(CK_RSA_PKCS_OAEP_PARAMS) == mech->ulParameterLen) { + CK_RSA_PKCS_OAEP_PARAMS * oaep_params = mech->pParameter; + if (oaep_params->source == CKZ_DATA_SPECIFIED) { + param = oaep_params->pSourceData; + paramlen = (size_t)oaep_params->ulSourceDataLen; + } + } + } + r = sc_pkcs1_strip_oaep_padding(ctx, out, s, flags, param, paramlen); + LOG_TEST_RET(ctx, r, "Invalid OAEP padding"); } - - LOG_FUNC_RETURN(ctx, r); +#endif + /* do not log error code to prevent side channel attack */ + return r; } /* derive one key from another. RSA can use decipher, so this is for only ECDH @@ -246,7 +352,7 @@ int sc_pkcs15_derive(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *obj, unsigned long flags, const u8 * in, size_t inlen, u8 *out, - unsigned long *poutlen) + size_t *poutlen) { sc_context_t *ctx = p15card->card->ctx; int r; @@ -262,8 +368,9 @@ int sc_pkcs15_derive(struct sc_pkcs15_card *p15card, switch (obj->type) { case SC_PKCS15_TYPE_PRKEY_EC: - if (out == NULL || *poutlen < (prkey->field_length + 7) / 8) { - *poutlen = (prkey->field_length + 7) / 8; + case SC_PKCS15_TYPE_PRKEY_XEDDSA: + if (out == NULL || *poutlen < BYTES4BITS(prkey->field_length)) { + *poutlen = BYTES4BITS(prkey->field_length); r = 0; /* say no data to return */ LOG_FUNC_RETURN(ctx, r); } @@ -284,19 +391,206 @@ int sc_pkcs15_derive(struct sc_pkcs15_card *p15card, *poutlen); LOG_TEST_RET(ctx, r, "use_key() failed"); - /* Strip any padding */ - if (pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { - size_t s = r; - r = sc_pkcs1_strip_02_padding(ctx, out, s, out, &s); - LOG_TEST_RET(ctx, r, "Invalid PKCS#1 padding"); - } - /* If card stores derived key on card, then no data is returned * and the key must be used on the card. */ *poutlen = r; LOG_FUNC_RETURN(ctx, r); } +/* + * Unwrap a key into a key object on card. + * in holds the wrapped key data + * the target file that target_key points to must be created before calling this function + * Use pkcs15init to perform the complete unwrapping operation and create the pkcs#15 object for the new key. + */ +int sc_pkcs15_unwrap(struct sc_pkcs15_card *p15card, + const struct sc_pkcs15_object *key, + struct sc_pkcs15_object *target_key, + unsigned long flags, + const u8 * in, size_t inlen, + const u8 * param, size_t paramlen) +{ + sc_context_t *ctx = p15card->card->ctx; + int r; + sc_algorithm_info_t *alg_info = NULL; + sc_security_env_t senv; + const struct sc_pkcs15_prkey_info *src_prkey = (const struct sc_pkcs15_prkey_info *) key->data; + const struct sc_pkcs15_skey_info *src_skey = (const struct sc_pkcs15_skey_info *) key->data; + const struct sc_pkcs15_skey_info *tkey = (const struct sc_pkcs15_skey_info *) target_key->data; + unsigned long pad_flags = 0, sec_flags = 0; + u8 *out = 0; + size_t poutlen = 0; + sc_path_t path, target_file_id; + sc_sec_env_param_t senv_param; + + LOG_FUNC_CALLED(ctx); + + if (key->type == SC_PKCS15_TYPE_PRKEY_RSA) { + if (!(src_prkey->usage & (SC_PKCS15_PRKEY_USAGE_UNWRAP))) + LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "This key cannot be used for unwrapping"); + } + else if ((key->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_SKEY) { + if (!(src_skey->usage & (SC_PKCS15_PRKEY_USAGE_UNWRAP))) + LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "This key cannot be used for unwrapping"); + } + else + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Key type not supported"); + + r = format_senv(p15card, key, &senv, &alg_info); + LOG_TEST_RET(ctx, r, "Could not initialize security environment"); + senv.operation = SC_SEC_OPERATION_UNWRAP; + + memset(&path, 0, sizeof(sc_path_t)); + memset(&target_file_id, 0, sizeof(sc_path_t)); + + if (!tkey->path.len && tkey->path.aid.len) { + /* Target key is a SDO allocated in application DF */ + target_file_id = tkey->path; + } + else if (tkey->path.len == 2 && p15card->file_app != NULL) { + /* Path is relative to app. DF */ + path = p15card->file_app->path; + target_file_id = tkey->path; + sc_append_path(&path, &target_file_id); + target_file_id = path; + } + else if (tkey->path.len > 2) { + path = tkey->path; + memcpy(target_file_id.value, tkey->path.value + tkey->path.len - 2, 2); + target_file_id.len = 2; + target_file_id.type = SC_PATH_TYPE_FILE_ID; + } + else { + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid unwrapping target key path"); + } + + senv_param = (sc_sec_env_param_t) { SC_SEC_ENV_PARAM_TARGET_FILE, &target_file_id, sizeof(target_file_id)}; + LOG_TEST_RET(ctx, sec_env_add_param(&senv, &senv_param), "failed to add target file path to security environment"); + + r = sc_get_encoding_flags(ctx, flags, alg_info->flags, &pad_flags, &sec_flags); + LOG_TEST_RET(ctx, r, "cannot encode security operation flags"); + senv.algorithm_flags = sec_flags; + + if ((sec_flags & (SC_ALGORITHM_AES_CBC | SC_ALGORITHM_AES_CBC_PAD)) > 0) { + senv_param = (sc_sec_env_param_t) { SC_SEC_ENV_PARAM_IV, (void*) param, paramlen }; + LOG_TEST_RET(ctx, sec_env_add_param(&senv, &senv_param), "failed to add IV to security environment"); + } + + r = use_key(p15card, key, &senv, sc_unwrap, in, inlen, out, + poutlen); + LOG_TEST_RET(ctx, r, "use_key() failed"); + + LOG_FUNC_RETURN(ctx, r); +} + +/* + * Wrap a key and return a cryptogram + * is the wrapping key + * is the key to be wrapped + * wrapped data is returned in + */ +int sc_pkcs15_wrap(struct sc_pkcs15_card *p15card, + const struct sc_pkcs15_object *key, + struct sc_pkcs15_object *target_key, + unsigned long flags, + u8 *cryptogram, size_t *crgram_len, + const u8 *param, size_t paramlen) { + sc_context_t *ctx = p15card->card->ctx; + int r; + sc_algorithm_info_t *alg_info = NULL; + sc_security_env_t senv; + const struct sc_pkcs15_prkey_info *src_prkey = (const struct sc_pkcs15_prkey_info *) key->data; + const struct sc_pkcs15_skey_info *src_skey = (const struct sc_pkcs15_skey_info *) key->data; + const struct sc_pkcs15_prkey_info *target_prkey = (const struct sc_pkcs15_prkey_info *) target_key->data; + const struct sc_pkcs15_skey_info *target_skey = (const struct sc_pkcs15_skey_info *) target_key->data; + unsigned long pad_flags = 0, sec_flags = 0; + sc_path_t tkey_path; + sc_path_t path, target_file_id; + sc_sec_env_param_t senv_param; + + LOG_FUNC_CALLED(ctx); + + switch (key->type) { + case SC_PKCS15_TYPE_PRKEY_RSA: + if (!(src_prkey->usage & (SC_PKCS15_PRKEY_USAGE_WRAP))) + LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "This key cannot be used for wrapping"); + break; + case SC_PKCS15_TYPE_SKEY_DES: + case SC_PKCS15_TYPE_SKEY_3DES: + case SC_PKCS15_TYPE_SKEY_GENERIC: + if (!(src_skey->usage & (SC_PKCS15_PRKEY_USAGE_WRAP))) + LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "This key cannot be used for wrapping"); + break; + default: + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Wrapping key type not supported"); + } + + if (!(target_key->type == SC_PKCS15_TYPE_PRKEY_RSA || + (target_key->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_SKEY)) { + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Target key type not supported"); + } + + r = format_senv(p15card, key, &senv, &alg_info); + LOG_TEST_RET(ctx, r, "Could not initialize security environment"); + senv.operation = SC_SEC_OPERATION_WRAP; + + memset(&path, 0, sizeof (sc_path_t)); + memset(&target_file_id, 0, sizeof (sc_path_t)); + + switch (target_key->type) { + case SC_PKCS15_TYPE_PRKEY_RSA: + tkey_path = target_prkey->path; + break; + default: /* we already know it is a secret key */ + tkey_path = target_skey->path; + break; + } + + if (!tkey_path.len && tkey_path.aid.len) { + /* Target key is a SDO allocated in application DF */ + target_file_id = tkey_path; + } else if (tkey_path.len == 2 && p15card->file_app != NULL) { + /* Path is relative to app. DF */ + path = p15card->file_app->path; + target_file_id = tkey_path; + sc_append_path(&path, &target_file_id); + target_file_id = path; + } else if (tkey_path.len > 2) { + /*path = tkey_path;*/ + memcpy(target_file_id.value, tkey_path.value + tkey_path.len - 2, 2); + target_file_id.len = 2; + target_file_id.type = SC_PATH_TYPE_FILE_ID; + } + else { + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid unwrapping target key path"); + } + senv_param = (sc_sec_env_param_t) { SC_SEC_ENV_PARAM_TARGET_FILE, &target_file_id, sizeof(target_file_id)}; + LOG_TEST_RET(ctx, sec_env_add_param(&senv, &senv_param), "failed to add target file path to security environment"); + + r = sc_get_encoding_flags(ctx, flags, alg_info->flags, &pad_flags, &sec_flags); + LOG_TEST_RET(ctx, r, "cannot encode security operation flags"); + senv.algorithm_flags = sec_flags; + + if ((sec_flags & (SC_ALGORITHM_AES_CBC | SC_ALGORITHM_AES_CBC_PAD)) > 0) { + senv_param = (sc_sec_env_param_t) { SC_SEC_ENV_PARAM_IV, (void*) param, paramlen }; + LOG_TEST_RET(ctx, sec_env_add_param(&senv, &senv_param), "failed to add IV to security environment"); + } + + r = use_key(p15card, key, &senv, sc_wrap, NULL, 0, cryptogram, crgram_len ? *crgram_len : 0); + + if (r > -1 && crgram_len) { + if (*crgram_len < (size_t) r) { + *crgram_len = r; + if (cryptogram != NULL) /* if NULL, return success and required buffer length by PKCS#11 convention */ + LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Buffer too small to hold the wrapped key."); + } + *crgram_len = r; + } + + LOG_FUNC_RETURN(ctx, r); +} + + /* copied from pkcs15-cardos.c */ #define USAGE_ANY_SIGN (SC_PKCS15_PRKEY_USAGE_SIGN|\ SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) @@ -306,15 +600,15 @@ int sc_pkcs15_derive(struct sc_pkcs15_card *p15card, int sc_pkcs15_compute_signature(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *obj, unsigned long flags, const u8 *in, size_t inlen, - u8 *out, size_t outlen) + u8 *out, size_t outlen, void *pMechanism) { sc_context_t *ctx = p15card->card->ctx; int r; sc_security_env_t senv; sc_algorithm_info_t *alg_info; const struct sc_pkcs15_prkey_info *prkey = (const struct sc_pkcs15_prkey_info *) obj->data; - u8 buf[1024], *tmp; - size_t modlen; + u8 *buf = NULL, *tmp; + size_t modlen = 0, buflen = 0; unsigned long pad_flags = 0, sec_flags = 0; LOG_FUNC_CALLED(ctx); @@ -329,30 +623,36 @@ int sc_pkcs15_compute_signature(struct sc_pkcs15_card *p15card, switch (obj->type) { case SC_PKCS15_TYPE_PRKEY_RSA: - modlen = prkey->modulus_length / 8; + modlen = BYTES4BITS(prkey->modulus_length); break; case SC_PKCS15_TYPE_PRKEY_GOSTR3410: - modlen = (prkey->modulus_length + 7) / 8 * 2; + modlen = BYTES4BITS(prkey->modulus_length) * 2; break; case SC_PKCS15_TYPE_PRKEY_EC: - modlen = ((prkey->field_length +7) / 8) * 2; /* 2*nLen */ + case SC_PKCS15_TYPE_PRKEY_EDDSA: + case SC_PKCS15_TYPE_PRKEY_XEDDSA: + modlen = BYTES4BITS(prkey->field_length) * 2; /* 2*nLen */ break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Key type not supported"); } /* Probably never happens, but better make sure */ - if (inlen > sizeof(buf) || outlen < modlen) + if (outlen < modlen) LOG_FUNC_RETURN(ctx, SC_ERROR_BUFFER_TOO_SMALL); + buflen = inlen + modlen; + buf = sc_mem_secure_alloc(buflen); + if (buf == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(buf, in, inlen); /* revert data to sign when signing with the GOST key. * TODO: can it be confirmed by the GOST standard? * TODO: tested with RuTokenECP, has to be validated for RuToken. */ - if (obj->type == SC_PKCS15_TYPE_PRKEY_GOSTR3410) { + if (obj->type == SC_PKCS15_TYPE_PRKEY_GOSTR3410) { r = sc_mem_reverse(buf, inlen); - LOG_TEST_RET(ctx, r, "Reverse memory error"); + LOG_TEST_GOTO_ERR(ctx, r, "Reverse memory error"); } tmp = buf; @@ -363,76 +663,99 @@ int sc_pkcs15_compute_signature(struct sc_pkcs15_card *p15card, /* if the card has SC_ALGORITHM_NEED_USAGE set, and the * key is for signing and decryption, we need to emulate signing */ - /* TODO: -DEE assume only RSA keys will ever use _NEED_USAGE */ - - sc_log(ctx, "supported algorithm flags 0x%X, private key usage 0x%X", alg_info->flags, prkey->usage); - if ((alg_info->flags & SC_ALGORITHM_NEED_USAGE) && - ((prkey->usage & USAGE_ANY_SIGN) && - (prkey->usage & USAGE_ANY_DECIPHER)) ) { - size_t tmplen = sizeof(buf); - if (flags & SC_ALGORITHM_RSA_RAW) { - r = sc_pkcs15_decipher(p15card, obj,flags, in, inlen, out, outlen); - LOG_FUNC_RETURN(ctx, r); - } - if (modlen > tmplen) - LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "Buffer too small, needs recompile!"); - r = sc_pkcs1_encode(ctx, flags, in, inlen, buf, &tmplen, modlen); + sc_log(ctx, "supported algorithm flags 0x%lX, private key usage 0x%X", alg_info->flags, prkey->usage); + if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA) { + if ((alg_info->flags & SC_ALGORITHM_NEED_USAGE) && + ((prkey->usage & USAGE_ANY_SIGN) && + (prkey->usage & USAGE_ANY_DECIPHER)) ) { + size_t tmplen = buflen; + if (flags & SC_ALGORITHM_RSA_RAW) { + r = sc_pkcs15_decipher(p15card, obj, flags, in, inlen, out, outlen, NULL); + goto err; + } + if (modlen > tmplen) + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_NOT_ALLOWED, "Buffer too small, needs recompile!"); + + /* XXX Assuming RSA key here */ + r = sc_pkcs1_encode(ctx, flags, in, inlen, buf, &tmplen, prkey->modulus_length, pMechanism); - /* no padding needed - already done */ - flags &= ~SC_ALGORITHM_RSA_PADS; - /* instead use raw rsa */ - flags |= SC_ALGORITHM_RSA_RAW; + /* no padding needed - already done */ + flags &= ~SC_ALGORITHM_RSA_PADS; + /* instead use raw rsa */ + flags |= SC_ALGORITHM_RSA_RAW; - LOG_TEST_RET(ctx, r, "Unable to add padding"); + LOG_TEST_GOTO_ERR(ctx, r, "Unable to add padding"); + + r = sc_pkcs15_decipher(p15card, obj, flags, buf, modlen, out, outlen, NULL); + goto err; + } - r = sc_pkcs15_decipher(p15card, obj,flags, buf, modlen, out, outlen); - LOG_FUNC_RETURN(ctx, r); - } + /* If the card doesn't support the requested algorithm, we normally add the + * padding here in software and ask the card to do a raw signature. There's + * one exception to that, where we might be able to get the signature to + * succeed by stripping padding if the card only offers higher-level + * signature operations. The only thing we can strip is the DigestInfo + * block from PKCS1 padding. */ + if ((flags == (SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_HASH_NONE)) && + !(alg_info->flags & SC_ALGORITHM_RSA_RAW) && + !(alg_info->flags & SC_ALGORITHM_RSA_HASH_NONE) && + (alg_info->flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01)) { + unsigned int algo; + size_t tmplen = buflen; + + r = sc_pkcs1_strip_digest_info_prefix(&algo, tmp, inlen, tmp, &tmplen); + if (r != SC_SUCCESS || algo == SC_ALGORITHM_RSA_HASH_NONE) { + r = SC_ERROR_INVALID_DATA; + goto err; + } + flags &= ~SC_ALGORITHM_RSA_HASH_NONE; + flags |= algo; + inlen = tmplen; + } + } - /* If the card doesn't support the requested algorithm, see if we - * can strip the input so a more restrictive algo can be used */ - if ((flags == (SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE)) && - !(alg_info->flags & (SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASH_NONE))) { - unsigned int algo; - size_t tmplen = sizeof(buf); - r = sc_pkcs1_strip_digest_info_prefix(&algo, tmp, inlen, tmp, &tmplen); - if (r != SC_SUCCESS || algo == SC_ALGORITHM_RSA_HASH_NONE) { - sc_mem_clear(buf, sizeof(buf)); - LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + /* ECDSA software hash has already been done, or is not needed, or card will do hash */ + /* if card can not do the hash, will use SC_ALGORITHM_ECDSA_RAW */ + if (obj->type == SC_PKCS15_TYPE_PRKEY_EC) { + if ((alg_info->flags & SC_ALGORITHM_ECDSA_RAW) + && !(flags & SC_ALGORITHM_ECDSA_HASHES & alg_info->flags)) { + sc_log(ctx, "ECDSA using SC_ALGORITHM_ECDSA_RAW flags before 0x%8.8lx", flags); + flags |= SC_ALGORITHM_ECDSA_RAW; + flags &= ~SC_ALGORITHM_ECDSA_HASHES; } - flags &= ~SC_ALGORITHM_RSA_HASH_NONE; - flags |= algo; - inlen = tmplen; } r = sc_get_encoding_flags(ctx, flags, alg_info->flags, &pad_flags, &sec_flags); if (r != SC_SUCCESS) { - sc_mem_clear(buf, sizeof(buf)); - LOG_FUNC_RETURN(ctx, r); + goto err; } + /* senv now has flags card or driver will do */ senv.algorithm_flags = sec_flags; - sc_log(ctx, "DEE flags:0x%8.8lx alg_info->flags:0x%8.8x pad:0x%8.8lx sec:0x%8.8lx", + sc_log(ctx, "DEE flags:0x%8.8lx alg_info->flags:0x%8.8lx pad:0x%8.8lx sec:0x%8.8lx", flags, alg_info->flags, pad_flags, sec_flags); /* add the padding bytes (if necessary) */ if (pad_flags != 0) { - size_t tmplen = sizeof(buf); - - r = sc_pkcs1_encode(ctx, pad_flags, tmp, inlen, tmp, &tmplen, modlen); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to add padding"); + size_t tmplen = buflen; + /* XXX Assuming RSA key here */ + r = sc_pkcs1_encode(ctx, pad_flags, tmp, inlen, tmp, &tmplen, + prkey->modulus_length, pMechanism); + LOG_TEST_GOTO_ERR(ctx, r, "Unable to add padding"); inlen = tmplen; } else if ( senv.algorithm == SC_ALGORITHM_RSA && - (flags & SC_ALGORITHM_RSA_PADS) == SC_ALGORITHM_RSA_PAD_NONE) { + (flags & SC_ALGORITHM_RSA_PADS) == SC_ALGORITHM_RSA_PAD_NONE) { /* Add zero-padding if input is shorter than the modulus */ if (inlen < modlen) { - if (modlen > sizeof(buf)) - return SC_ERROR_BUFFER_TOO_SMALL; + if (modlen > buflen) { + r = SC_ERROR_BUFFER_TOO_SMALL; + goto err; + } memmove(tmp+modlen-inlen, tmp, inlen); memset(tmp, 0, modlen-inlen); } @@ -442,17 +765,202 @@ int sc_pkcs15_compute_signature(struct sc_pkcs15_card *p15card, * If the length of the hash value is larger than the bit length of n, only * the leftmost bits of the hash up to the length of n will be used. Any * truncation is done by the token. + * But if card is going to do the hash, pass in all the data */ else if (senv.algorithm == SC_ALGORITHM_EC && - (flags & SC_ALGORITHM_ECDSA_HASH_NONE) != 0) { - inlen = MIN(inlen, (prkey->field_length+7)/8); + (senv.algorithm_flags & SC_ALGORITHM_ECDSA_HASHES) == 0) { + inlen = MIN(inlen, BYTES4BITS(prkey->field_length)); } r = use_key(p15card, obj, &senv, sc_compute_signature, tmp, inlen, out, outlen); - LOG_TEST_RET(ctx, r, "use_key() failed"); - sc_mem_clear(buf, sizeof(buf)); + LOG_TEST_GOTO_ERR(ctx, r, "use_key() failed"); + + /* Some cards may return RSA signature as integer without leading zero bytes */ + /* Already know outlen >= modlen and r >= 0 */ + if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA && (unsigned)r < modlen) { + memmove(out + modlen - r, out, r); + memset(out, 0, modlen - r); + r = (int)modlen; + } + +err: + sc_mem_secure_clear_free(buf, buflen); + + LOG_FUNC_RETURN(ctx, r); +} + +int +sc_pkcs15_encrypt_sym(struct sc_pkcs15_card *p15card, + const struct sc_pkcs15_object *obj, + unsigned long flags, + const u8 *in, size_t inlen, u8 *out, size_t *outlen, + const u8 *param, size_t paramlen) +{ + + sc_context_t *ctx = p15card->card->ctx; + + int i, r; + sc_algorithm_info_t *alg_info = NULL; + sc_security_env_t senv; + sc_sec_env_param_t senv_param; + const struct sc_pkcs15_skey_info *skey; + unsigned long pad_flags = 0, sec_flags = 0; + int revalidated_cached_pin = 0; + sc_path_t path; + + sc_log(ctx, "called with flags 0x%lX", flags); + + skey = (const struct sc_pkcs15_skey_info *)obj->data; + if (!(skey->usage & SC_PKCS15_PRKEY_USAGE_ENCRYPT)) + LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "This key cannot be used for encryption"); + + r = format_senv(p15card, obj, &senv, &alg_info); + LOG_TEST_RET(ctx, r, "Could not initialize security environment"); + senv.operation = SC_SEC_OPERATION_ENCRYPT_SYM; + + r = sc_get_encoding_flags(ctx, flags, alg_info->flags, &pad_flags, &sec_flags); + LOG_TEST_RET(ctx, r, "cannot encode security operation flags"); + senv.algorithm_flags = sec_flags; + + for (i = 0; i < SC_MAX_SUPPORTED_ALGORITHMS && senv.supported_algos[i].reference; i++) { + if ((senv.supported_algos[i].mechanism == CKM_AES_ECB && sec_flags == SC_ALGORITHM_AES_ECB) || + (senv.supported_algos[i].mechanism == CKM_AES_CBC && sec_flags == SC_ALGORITHM_AES_CBC) || + (senv.supported_algos[i].mechanism == CKM_AES_CBC_PAD && sec_flags == SC_ALGORITHM_AES_CBC_PAD)) { + senv.algorithm_ref = senv.supported_algos[i].algo_ref; + senv.flags |= SC_SEC_ENV_ALG_REF_PRESENT; + break; + } + } + + if ((sec_flags & (SC_ALGORITHM_AES_CBC | SC_ALGORITHM_AES_CBC_PAD)) > 0) { + senv_param = (sc_sec_env_param_t){ + SC_SEC_ENV_PARAM_IV, (void *)param, paramlen}; + LOG_TEST_RET(ctx, sec_env_add_param(&senv, &senv_param), "failed to add IV to security environment"); + } + + LOG_TEST_RET(p15card->card->ctx, get_file_path(obj, &path), "Failed to get key file path."); + + LOG_TEST_RET(p15card->card->ctx, r, "sc_lock() failed"); + + do { + r = SC_SUCCESS; + if (outlen == NULL) { + /* C_EncryptInit */ + /* select key file and set sec env */ + if (path.len != 0 || path.aid.len != 0) { + r = select_key_file(p15card, obj, &senv); + if (r < 0) + sc_log(p15card->card->ctx, "Unable to select key file"); + } + if (r == SC_SUCCESS) { + r = sc_set_security_env(p15card->card, &senv, 0); + if (r < 0) + sc_log(p15card->card->ctx, "Unable to set security env"); + } + } + + if (r == SC_SUCCESS) + r = sc_encrypt_sym(p15card->card, in, inlen, out, outlen); + + if (revalidated_cached_pin) + /* only re-validate once */ + break; + if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { + r = sc_pkcs15_pincache_revalidate(p15card, obj); + if (r < 0) + break; + revalidated_cached_pin = 1; + } + } while (revalidated_cached_pin); + + LOG_FUNC_RETURN(ctx, r); +} + +int +sc_pkcs15_decrypt_sym(struct sc_pkcs15_card *p15card, + const struct sc_pkcs15_object *obj, + unsigned long flags, + const u8 *in, size_t inlen, u8 *out, size_t *outlen, + const u8 *param, size_t paramlen) +{ + + sc_context_t *ctx = p15card->card->ctx; + + int i, r; + sc_algorithm_info_t *alg_info = NULL; + sc_security_env_t senv; + sc_sec_env_param_t senv_param; + const struct sc_pkcs15_skey_info *skey; + unsigned long pad_flags = 0, sec_flags = 0; + int revalidated_cached_pin = 0; + sc_path_t path; + + sc_log(ctx, "called with flags 0x%lX", flags); + + skey = (const struct sc_pkcs15_skey_info *)obj->data; + if (!(skey->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT)) + LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "This key cannot be used for encryption"); + + r = format_senv(p15card, obj, &senv, &alg_info); + LOG_TEST_RET(ctx, r, "Could not initialize security environment"); + senv.operation = SC_SEC_OPERATION_DECRYPT_SYM; + + r = sc_get_encoding_flags(ctx, flags, alg_info->flags, &pad_flags, &sec_flags); + LOG_TEST_RET(ctx, r, "cannot encode security operation flags"); + senv.algorithm_flags = sec_flags; + + for (i = 0; i < SC_MAX_SUPPORTED_ALGORITHMS && senv.supported_algos[i].reference; i++) { + if ((senv.supported_algos[i].mechanism == CKM_AES_ECB && sec_flags == SC_ALGORITHM_AES_ECB) || + (senv.supported_algos[i].mechanism == CKM_AES_CBC && sec_flags == SC_ALGORITHM_AES_CBC) || + (senv.supported_algos[i].mechanism == CKM_AES_CBC_PAD && sec_flags == SC_ALGORITHM_AES_CBC_PAD)) { + senv.algorithm_ref = senv.supported_algos[i].algo_ref; + senv.flags |= SC_SEC_ENV_ALG_REF_PRESENT; + break; + } + } + + if ((sec_flags & (SC_ALGORITHM_AES_CBC | SC_ALGORITHM_AES_CBC_PAD)) > 0) { + senv_param = (sc_sec_env_param_t){ + SC_SEC_ENV_PARAM_IV, (void *)param, paramlen}; + LOG_TEST_RET(ctx, sec_env_add_param(&senv, &senv_param), "failed to add IV to security environment"); + } + + LOG_TEST_RET(p15card->card->ctx, get_file_path(obj, &path), "Failed to get key file path."); + + LOG_TEST_RET(p15card->card->ctx, r, "sc_lock() failed"); + + do { + r = SC_SUCCESS; + if (outlen == NULL) { + /* C_DecryptInit */ + /* select key file and set sec env */ + if (path.len != 0 || path.aid.len != 0) { + r = select_key_file(p15card, obj, &senv); + if (r < 0) + sc_log(p15card->card->ctx, "Unable to select key file"); + } + if (r == SC_SUCCESS) { + r = sc_set_security_env(p15card->card, &senv, 0); + if (r < 0) + sc_log(p15card->card->ctx, "Unable to set security env"); + } + } + + if (r == SC_SUCCESS) + r = sc_decrypt_sym(p15card->card, in, inlen, out, outlen); + + if (revalidated_cached_pin) + /* only re-validate once */ + break; + if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { + r = sc_pkcs15_pincache_revalidate(p15card, obj); + if (r < 0) + break; + revalidated_cached_pin = 1; + } + } while (revalidated_cached_pin); LOG_FUNC_RETURN(ctx, r); } diff --git a/src/libopensc/pkcs15-skeid.c b/src/libopensc/pkcs15-skeid.c new file mode 100644 index 0000000000..cd802f95b8 --- /dev/null +++ b/src/libopensc/pkcs15-skeid.c @@ -0,0 +1,189 @@ +/* + * PKCS15 emulation layer for Slovak eID card + * + * Copyright (C) 2022 Juraj Å arinay + * + * 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 + * + * based on the PKCS15 emulation layer for EstEID card by Martin Paljak + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "common/compat_strlcpy.h" + +#include "internal.h" +#include "log.h" +#include "pkcs15.h" + +static const struct sc_aid skeid_aid_qes = {{0xE8, 0x28, 0xBD, 0x08, 0x0F, 0xA0, 0x00, 0x00, 0x08, 0x51, 0x00, 0x00, 0x11}, 13}; + +static int sc_pkcs15emu_skeid_init(sc_pkcs15_card_t * p15card) +{ + int r; + int i; + size_t sn_len; + char *buf; + + set_string(&p15card->tokeninfo->label, "eID karta"); + set_string(&p15card->tokeninfo->manufacturer_id, "Atos Information Technology GmbH"); + + sn_len = p15card->card->serialnr.len; + if (sn_len > 0) { + buf = malloc(2 * sn_len + 1); + if (!buf) return SC_ERROR_OUT_OF_MEMORY; + sc_bin_to_hex(p15card->card->serialnr.value, sn_len, buf, + 2 * sn_len + 1, 0); + p15card->tokeninfo->serial_number = buf; + } + + p15card->tokeninfo->flags = SC_PKCS15_TOKEN_PRN_GENERATION | SC_PKCS15_TOKEN_READONLY; + + /* add certificates */ + const char *skeid_cert_names[3] = { + "Kvalifikovany certifikat pre elektronicky podpis", + "Certifikat pre elektronicky podpis", + "Sifrovaci certifikat" + }; + + const char *skeid_cert_paths[3] = { + "3f0001030201", + "3f0001030202", + "3f0001030203" + }; + + for (i = 0; i < 3; i++) { + struct sc_pkcs15_cert_info cert_info; + struct sc_pkcs15_object cert_obj; + + memset(&cert_info, 0, sizeof(cert_info)); + memset(&cert_obj, 0, sizeof(cert_obj)); + + cert_info.id.value[0] = i + 1; + cert_info.id.len = 1; + + sc_format_path(skeid_cert_paths[i], &cert_info.path); + strlcpy(cert_obj.label, skeid_cert_names[i], sizeof(cert_obj.label)); + + r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); + + LOG_TEST_RET(p15card->card->ctx, r, "Error adding certificate."); + } + + /* add pins */ + const char *skeid_pin_names[2] = { + "BOK", + "Podpisovy PIN" + }; + + const unsigned int skeid_pin_max_length[2] = {6, 10}; + const unsigned int skeid_pin_max_tries[2] = {5, 3}; + const int skeid_pin_ref[2] = {0x03, 0x87}; + const char *skeid_pin_paths[2] = {"3F00", "3F000101"}; + + const unsigned int skeid_pin_flags[2] = {SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_EXCHANGE_REF_DATA | SC_PKCS15_PIN_FLAG_INITIALIZED, + SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_EXCHANGE_REF_DATA | SC_PKCS15_PIN_FLAG_INITIALIZED}; + + for (i = 0; i < 2; i++) { + struct sc_pkcs15_auth_info pin_info; + struct sc_pkcs15_object pin_obj; + + memset(&pin_info, 0, sizeof(pin_info)); + memset(&pin_obj, 0, sizeof(pin_obj)); + + pin_info.auth_id.len = 1; + pin_info.auth_id.value[0] = i + 1; + pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; + pin_info.attrs.pin.reference = skeid_pin_ref[i]; + pin_info.attrs.pin.flags = skeid_pin_flags[i]; + pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; + pin_info.attrs.pin.min_length = 6; + pin_info.attrs.pin.max_length = skeid_pin_max_length[i]; + pin_info.max_tries = skeid_pin_max_tries[i]; + + strlcpy(pin_obj.label, skeid_pin_names[i], sizeof(pin_obj.label)); + pin_obj.flags = skeid_pin_flags[i]; + + sc_format_path(skeid_pin_paths[i], &pin_info.path); + + r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); + + LOG_TEST_RET(p15card->card->ctx, r, "Error adding PIN."); + } + + /* add private keys */ + const u8 skeid_prkey_pin[3] = {2, 1, 1}; + + /* store seIdentifier rather than keyReference */ + const int skeid_prkey_ref[3] = {0x01, 0x34, 0x44}; + const int skeid_prkey_usage[3] = + { SC_PKCS15_PRKEY_USAGE_NONREPUDIATION | SC_PKCS15_PRKEY_USAGE_SIGN, + SC_PKCS15_PRKEY_USAGE_SIGN, + SC_PKCS15_PRKEY_USAGE_DECRYPT + }; + + const char *skeid_prkey_paths[3] = {"3F000101", "3F000102", "3F000102"}; + + const char *skeid_prkey_name[3] = { + "Podpisovy kluc (KEP)", + "Podpisovy kluc", + "Sifrovaci kluc", + }; + + for (i = 0; i < 3; i++) { + struct sc_pkcs15_prkey_info prkey_info; + struct sc_pkcs15_object prkey_obj; + + memset(&prkey_info, 0, sizeof(prkey_info)); + memset(&prkey_obj, 0, sizeof(prkey_obj)); + + prkey_info.id.len = 1; + prkey_info.id.value[0] = i + 1; + prkey_info.native = 1; + prkey_info.key_reference = skeid_prkey_ref[i]; + prkey_info.modulus_length = 3072; + sc_format_path(skeid_prkey_paths[i], &prkey_info.path); + + prkey_info.usage = skeid_prkey_usage[i]; + + strlcpy(prkey_obj.label, skeid_prkey_name[i], sizeof(prkey_obj.label)); + prkey_obj.auth_id.len = 1; + prkey_obj.auth_id.value[0] = skeid_prkey_pin[i]; + if (i == 0) prkey_obj.user_consent = 1; + + prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; + + r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); + + LOG_TEST_RET(p15card->card->ctx, r, "Error adding private key."); + } + LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); +} + +int sc_pkcs15emu_skeid_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid) +{ + int r = SC_ERROR_WRONG_CARD; + + if (p15card->card->type == SC_CARD_TYPE_SKEID_V3 + && (aid == NULL || (aid->len == skeid_aid_qes.len && !memcmp(aid->value, &skeid_aid_qes.value, skeid_aid_qes.len)))) + r = sc_pkcs15emu_skeid_init(p15card); + + return r; +} diff --git a/src/libopensc/pkcs15-skey.c b/src/libopensc/pkcs15-skey.c index 006dfa1100..f9aff56008 100644 --- a/src/libopensc/pkcs15-skey.c +++ b/src/libopensc/pkcs15-skey.c @@ -1,7 +1,7 @@ /* * pkcs15-skey.c: PKCS #15 secret key functions * - * Copyright (C) 2002 Juha Yrjölä + * Copyright (C) 2002 Juha Yrjölä * Copyright (C) 2011 Viktor Tarasov * * This library is free software; you can redistribute it and/or @@ -16,10 +16,11 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "internal.h" #include "pkcs15.h" +#include "pkcs11/pkcs11.h" #include "asn1.h" #include #include @@ -27,10 +28,18 @@ #include /* - * in src/libopensc/types.h SC_MAX_SUPPORTED_ALGORITHMS defined as 8 + * in src/libopensc/types.h SC_MAX_SUPPORTED_ALGORITHMS defined as 16 */ #define C_ASN1_SUPPORTED_ALGORITHMS_SIZE (SC_MAX_SUPPORTED_ALGORITHMS + 1) static const struct sc_asn1_entry c_asn1_supported_algorithms[C_ASN1_SUPPORTED_ALGORITHMS_SIZE] = { + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "algorithmReference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, @@ -93,7 +102,7 @@ sc_pkcs15_decode_skdf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_obj { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_skey_info info; - int r, i; + int r, i, ii; size_t usage_len = sizeof(info.usage); size_t af_len = sizeof(info.access_flags); struct sc_asn1_entry asn1_com_key_attr[C_ASN1_COM_KEY_ATTR_SIZE]; @@ -106,6 +115,8 @@ sc_pkcs15_decode_skdf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_obj struct sc_asn1_pkcs15_object skey_des_obj = { obj, asn1_com_key_attr, asn1_com_skey_attr, asn1_generic_skey_attr }; + static const struct sc_object_id id_aes = { { 2, 16, 840, 1, 101, 3, 4, 1, -1 } }; + struct sc_object_id temp_oid; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_ASN1); @@ -140,13 +151,32 @@ sc_pkcs15_decode_skdf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_obj /* Fill in defaults */ memset(&info, 0, sizeof(info)); + info.native = 1; r = sc_asn1_decode(ctx, asn1_skey, *buf, *buflen, buf, buflen); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) return r; LOG_TEST_RET(ctx, r, "ASN.1 decoding failed"); - if (asn1_skey_choice[0].flags & SC_ASN1_PRESENT) + if (asn1_skey_choice[0].flags & SC_ASN1_PRESENT) { obj->type = SC_PKCS15_TYPE_SKEY_GENERIC; + + /* Check key type. framework-pkcs15 recognizes one type per key, and AES is the only algorithm supported for + * SKEY_GENERIC type keys, so just check if this key is AES compatible. */ + + for (i = 0; i < SC_MAX_SUPPORTED_ALGORITHMS && info.algo_refs[i] != 0 && info.key_type == 0; i++) { + for (ii = 0; ii < SC_MAX_SUPPORTED_ALGORITHMS && p15card->tokeninfo != 0; ii++) { + if (info.algo_refs[i] == p15card->tokeninfo->supported_algos[ii].reference) { + temp_oid = p15card->tokeninfo->supported_algos[ii].algo_id; + temp_oid.value[8] = -1; /* strip off AES subtype octet*/ + + if (sc_compare_oid(&id_aes, &temp_oid)) { + info.key_type = CKK_AES; + break; + } + } + } + } + } else if (asn1_skey_choice[1].flags & SC_ASN1_PRESENT) obj->type = SC_PKCS15_TYPE_SKEY_DES; else if (asn1_skey_choice[2].flags & SC_ASN1_PRESENT) @@ -239,3 +269,12 @@ int sc_pkcs15_encode_skdf_entry(struct sc_context *ctx, sc_log(ctx, "Key path %s", sc_print_path(&skey->path)); LOG_FUNC_RETURN(ctx, r); } + +void +sc_pkcs15_free_skey_info(sc_pkcs15_skey_info_t *info) +{ + if (info) { + free(info->data.value); + free(info); + } +} diff --git a/src/libopensc/pkcs15-srbeid.c b/src/libopensc/pkcs15-srbeid.c new file mode 100644 index 0000000000..7846d51505 --- /dev/null +++ b/src/libopensc/pkcs15-srbeid.c @@ -0,0 +1,577 @@ +/* + * pkcs15-srbeid.c: PKCS#15 emulation for Serbian cards using the + * CardEdge PKI applet. + * + * Copyright (C) 2026 LibreSCRS contributors + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#ifdef ENABLE_ZLIB +#include "compression.h" +#endif +#include "card-srbeid.h" +#include "internal.h" +#include "log.h" +#include "pkcs15.h" + +/* CardEdge cmapfile constants. */ +#define CE_CMAP_RECORD_SIZE 86u +#define CE_CMAP_FLAGS_OFFSET 80u +#define CE_CMAP_SIG_SIZE_OFFSET 82u +#define CE_CMAP_KX_SIZE_OFFSET 84u +#define CE_CMAP_VALID_CONTAINER 0x01u +#define CE_KEY_KIND_PRIVATE 1u +#define CE_AT_KEYEXCHANGE 1u +#define CE_AT_SIGNATURE 2u +#define CE_PKI_ROOT_DIR_FID 0x7000u +#define CE_DIR_HEADER_SIZE 10u +#define CE_DIR_ENTRY_SIZE 12u + +/* CardEdge PIN constants */ +#define CE_PIN_REFERENCE 0x80u +#define CE_PIN_MAX_LENGTH 8u + +/* Private key FID formula. */ +static unsigned int +ce_private_key_fid(unsigned int cont_idx, + unsigned int key_pair_id) +{ + return CE_KEYS_BASE_FID | ((cont_idx << 4) & 0x0FF0u) | ((key_pair_id << 2) & 0x000Cu) | CE_KEY_KIND_PRIVATE; +} + +/* + * Select FID and read the entire file into a malloc'd buffer. + * Uses sc_select_file() (dispatched to card driver's select_file which + * handles CardEdge's proprietary FCI) and sc_read_binary(). + * + * *out_len receives the byte count; caller must free() the buffer. + * Returns SC_SUCCESS or a negative SC_ERROR_* code. + */ +static int +srbeid_read_file(sc_card_t *card, unsigned int fid, + u8 **buf_out, size_t *out_len) +{ + sc_path_t path = {0}; + sc_file_t *file = NULL; + u8 *buf; + int r; + + *buf_out = NULL; + *out_len = 0; + + path.value[0] = (u8)((fid >> 8) & 0xFF); + path.value[1] = (u8)(fid & 0xFF); + path.len = 2; + path.type = SC_PATH_TYPE_FILE_ID; + + r = sc_select_file(card, &path, &file); + if (r < 0) + return r; + + if (!file || file->size == 0) { + sc_file_free(file); + return SC_SUCCESS; + } + + if (file->size > 65536) { + sc_file_free(file); + return SC_ERROR_INVALID_DATA; + } + + buf = malloc(file->size); + if (!buf) { + sc_file_free(file); + return SC_ERROR_OUT_OF_MEMORY; + } + + r = sc_read_binary(card, 0, buf, file->size, 0); + sc_file_free(file); + if (r < 0) { + free(buf); + return r; + } + + *buf_out = buf; + *out_len = (size_t)r; + return SC_SUCCESS; +} + +/* One entry from a CardEdge directory file. */ +typedef struct ce_dir_entry { + char name[9]; /* 8-char name + NUL */ + unsigned fid; + int is_dir; +} ce_dir_entry_t; + +/* + * Parse a CardEdge directory file into an array of ce_dir_entry_t. + * + * CardEdge directories use a proprietary binary format: + * [10-byte header] [12-byte entries...] + * This is NOT ISO 7816-4 EF.DIR (ASN.1 BER-TLV application templates), + * so standard sc_enum_apps() / iso7816_read_ef_dir() cannot be used. + * + * *entries_out: caller must free(). Returns entry count or -1 on error. + */ +static int +ce_parse_dir(const u8 *data, size_t len, ce_dir_entry_t **entries_out) +{ + size_t count, i; + ce_dir_entry_t *entries; + + *entries_out = NULL; + if (len < CE_DIR_HEADER_SIZE) + return -1; + + count = (size_t)data[6] | ((size_t)data[7] << 8); + if (count == 0) + return 0; + + /* Bound count against buffer size before allocation. */ + if (count > (len - CE_DIR_HEADER_SIZE) / CE_DIR_ENTRY_SIZE) + return -1; + + entries = calloc(count, sizeof(ce_dir_entry_t)); + if (!entries) + return -1; + + for (i = 0; i < count; i++) { + size_t off = CE_DIR_HEADER_SIZE + i * CE_DIR_ENTRY_SIZE; + int k; + + /* Name: up to 8 ASCII chars, may not be NUL-terminated on card. */ + memcpy(entries[i].name, data + off, 8); + entries[i].name[8] = '\0'; + /* Strip trailing spaces/NULs. */ + k = 7; + while (k >= 0 && (entries[i].name[k] == ' ' || entries[i].name[k] == '\0')) + entries[i].name[k--] = '\0'; + entries[i].fid = (unsigned)data[off + 8] | ((unsigned)data[off + 9] << 8); + entries[i].is_dir = (data[off + 10] != 0); + } + + *entries_out = entries; + return (int)count; +} + +typedef struct cert_entry { + char label[32]; + unsigned cert_fid; + unsigned key_fid; + unsigned key_size_bits; + unsigned cont_id; + unsigned key_pair_id; /* CE_AT_KEYEXCHANGE or CE_AT_SIGNATURE */ +} cert_entry_t; + +/* Select AID_PKCS15 and enumerate certificates from mscp/cmapfile. + * *certs_out: caller must free(). Returns cert count or negative error. */ +static int +srbeid_enum_certs(sc_card_t *card, cert_entry_t **certs_out) +{ + u8 *dir_buf = NULL, *mscp_buf = NULL, *cmap_buf = NULL; + size_t dir_len = 0, mscp_len = 0, cmap_len = 0; + ce_dir_entry_t *root_entries = NULL, *mscp_entries = NULL; + int root_count = 0, mscp_count = 0; + unsigned mscp_fid = 0, cmap_fid = 0; + cert_entry_t *certs = NULL; + int ncerts = 0, cap = 8; + int r, i; + size_t cmap_offset = 0, cmap_nrec = 0; + + *certs_out = NULL; + + /* Select PKI applet. */ + if (iso7816_select_aid(card, AID_PKCS15, AID_PKCS15_LEN, NULL, NULL) != SC_SUCCESS) { + r = SC_ERROR_CARD_CMD_FAILED; + goto out; + } + + /* Read root directory (FID 0x7000). */ + r = srbeid_read_file(card, CE_PKI_ROOT_DIR_FID, &dir_buf, &dir_len); + if (r < 0) + goto out; + + root_count = ce_parse_dir(dir_buf, dir_len, &root_entries); + if (root_count < 0) { + r = SC_ERROR_INVALID_DATA; + goto out; + } + + for (i = 0; i < root_count; i++) { + if (root_entries[i].is_dir && strcmp(root_entries[i].name, "mscp") == 0) { + mscp_fid = root_entries[i].fid; + break; + } + } + if (mscp_fid == 0) { + r = SC_ERROR_FILE_NOT_FOUND; + goto out; + } + + /* Read mscp directory. */ + r = srbeid_read_file(card, mscp_fid, &mscp_buf, &mscp_len); + if (r < 0) + goto out; + + mscp_count = ce_parse_dir(mscp_buf, mscp_len, &mscp_entries); + if (mscp_count < 0) { + r = SC_ERROR_INVALID_DATA; + goto out; + } + + certs = calloc((size_t)cap, sizeof(cert_entry_t)); + if (!certs) { + r = SC_ERROR_OUT_OF_MEMORY; + goto out; + } + + for (i = 0; i < mscp_count; i++) { + ce_dir_entry_t *e = &mscp_entries[i]; + if (e->is_dir) + continue; + + if (strcmp(e->name, "cmapfile") == 0) { + cmap_fid = e->fid; + } else if (strlen(e->name) == 5) { + unsigned kp_id; + const char *lbl; + + if (strncmp(e->name, "kxc", 3) == 0) { + kp_id = CE_AT_KEYEXCHANGE; + lbl = "Key Exchange Certificate"; + } else if (strncmp(e->name, "ksc", 3) == 0) { + kp_id = CE_AT_SIGNATURE; + lbl = "Digital Signature Certificate"; + } else { + continue; + } + + if (ncerts >= cap) { + cert_entry_t *tmp = realloc(certs, + (size_t)(cap * 2) * sizeof(cert_entry_t)); + if (!tmp) { + r = SC_ERROR_OUT_OF_MEMORY; + goto out; + } + certs = tmp; + cap *= 2; + } + + certs[ncerts].cont_id = (unsigned)(e->name[3] - '0') * 10 + (unsigned)(e->name[4] - '0'); + certs[ncerts].cert_fid = e->fid; + certs[ncerts].key_pair_id = kp_id; + snprintf(certs[ncerts].label, sizeof(certs[ncerts].label), "%s", lbl); + ncerts++; + } + } + + /* Read cmapfile and resolve key FIDs. */ + if (cmap_fid != 0) { + r = srbeid_read_file(card, cmap_fid, &cmap_buf, &cmap_len); + if (r == SC_SUCCESS) { + /* Optional 2-byte prefix present when (len-2) is a multiple of 86. */ + if (cmap_len >= 2 && (cmap_len - 2) % CE_CMAP_RECORD_SIZE == 0) + cmap_offset = 2; + cmap_nrec = (cmap_len - cmap_offset) / CE_CMAP_RECORD_SIZE; + } + } + + for (i = 0; i < ncerts; i++) { + unsigned ci = certs[i].cont_id; + + if (cmap_buf && ci < cmap_nrec) { + size_t rec = cmap_offset + (size_t)ci * CE_CMAP_RECORD_SIZE; + u8 flags = cmap_buf[rec + CE_CMAP_FLAGS_OFFSET]; + + if (flags & CE_CMAP_VALID_CONTAINER) { + size_t sz_off = (certs[i].key_pair_id == CE_AT_KEYEXCHANGE) + ? rec + CE_CMAP_KX_SIZE_OFFSET + : rec + CE_CMAP_SIG_SIZE_OFFSET; + unsigned kbits = (unsigned)cmap_buf[sz_off] | ((unsigned)cmap_buf[sz_off + 1] << 8); + if (kbits != 0) { + certs[i].key_size_bits = kbits; + certs[i].key_fid = ce_private_key_fid(ci, certs[i].key_pair_id); + } + } + } + sc_log(card->ctx, + "srbeid: cert[%d] \"%s\" cert_fid=0x%04x key_fid=0x%04x key_size=%u", + i, certs[i].label, certs[i].cert_fid, + certs[i].key_fid, certs[i].key_size_bits); + } + + *certs_out = certs; + certs = NULL; + r = ncerts; + +out: + free(dir_buf); + free(mscp_buf); + free(cmap_buf); + free(root_entries); + free(mscp_entries); + free(certs); + return r; +} + +/* + * Read the raw (possibly zlib-compressed) cert file and return DER bytes. + * + * CardEdge cert file layout: + * [CardFS len prefix: 2 bytes LE] + * [0x01 0x00] [uncompressed len: 2 bytes LE] [zlib data] — compressed + * OR [0x30 ...] — raw DER + */ +static int +srbeid_read_cert_der(sc_card_t *card, unsigned cert_fid, + u8 **der_out, size_t *der_len_out) +{ + u8 *raw = NULL; + size_t raw_len = 0; + const u8 *data; + size_t dlen; + int r; + + *der_out = NULL; + *der_len_out = 0; + + r = srbeid_read_file(card, cert_fid, &raw, &raw_len); + if (r < 0) + return r; + + if (raw_len < 6) { + free(raw); + return SC_ERROR_INVALID_DATA; + } + + /* Skip 2-byte CardFS length prefix. */ + data = raw + 2; + dlen = raw_len - 2; + + if (dlen >= 4 && data[0] == 0x01 && data[1] == 0x00) { + /* zlib-compressed DER */ +#ifdef ENABLE_ZLIB + size_t uncompressed_len = (size_t)data[2] | ((size_t)data[3] << 8); + u8 *der = NULL; + + r = sc_decompress_alloc(&der, &uncompressed_len, + data + 4, dlen - 4, COMPRESSION_ZLIB); + if (r != SC_SUCCESS) { + sc_log(card->ctx, "srbeid: zlib decompress failed (ret=%d)", r); + free(raw); + return SC_ERROR_INVALID_DATA; + } + *der_out = der; + *der_len_out = uncompressed_len; +#else + sc_log(card->ctx, "srbeid: cert is zlib-compressed but zlib not available"); + free(raw); + return SC_ERROR_NOT_SUPPORTED; +#endif + } else if (dlen >= 1 && data[0] == 0x30) { + /* Uncompressed DER (ASN.1 SEQUENCE tag). */ + u8 *der = malloc(dlen); + if (!der) { + free(raw); + return SC_ERROR_OUT_OF_MEMORY; + } + memcpy(der, data, dlen); + *der_out = der; + *der_len_out = dlen; + } else { + sc_log(card->ctx, + "srbeid: cert FID 0x%04x: unknown format (byte0=0x%02x)", + cert_fid, data[0]); + free(raw); + return SC_ERROR_INVALID_DATA; + } + + free(raw); + return SC_SUCCESS; +} + +static int +sc_pkcs15emu_srbeid_init(sc_pkcs15_card_t *p15card) +{ + sc_card_t *card = p15card->card; + cert_entry_t *certs = NULL; + int ncerts, i, r = SC_SUCCESS; + + sc_log(card->ctx, "srbeid: pkcs15 bind"); + + ncerts = srbeid_enum_certs(card, &certs); + if (ncerts < 0) { + sc_log(card->ctx, "srbeid: cert enumeration failed: %d", ncerts); + return ncerts; + } + if (ncerts == 0) { + sc_log(card->ctx, "srbeid: no certificates found"); + goto out; + } + + /* Set card label and manufacturer. */ + set_string(&p15card->tokeninfo->label, "Serbian CardEdge"); + set_string(&p15card->tokeninfo->manufacturer_id, "CardEdge"); + + /* Query PIN tries_left via card driver's pin_cmd. */ + { + struct sc_pin_cmd_data pin_data = {0}; + + pin_data.cmd = SC_PIN_CMD_GET_INFO; + pin_data.pin_type = SC_AC_CHV; + pin_data.pin_reference = CE_PIN_REFERENCE; + pin_data.pin1.tries_left = -1; + + /* Best-effort: failure to query PIN status is not fatal. */ + sc_pin_cmd(card, &pin_data); + sc_log(card->ctx, "srbeid: PIN tries_left=%d", pin_data.pin1.tries_left); + + /* ---- PIN auth object ---- + * Must be registered before private keys so auth_id links work. */ + { + sc_pkcs15_auth_info_t auth_info = {0}; + sc_pkcs15_object_t auth_obj = {0}; + + auth_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; + auth_info.auth_method = SC_AC_CHV; + auth_info.tries_left = pin_data.pin1.tries_left; + auth_info.attrs.pin.reference = CE_PIN_REFERENCE; + auth_info.attrs.pin.min_length = 4; + auth_info.attrs.pin.max_length = CE_PIN_MAX_LENGTH; + auth_info.attrs.pin.stored_length = CE_PIN_MAX_LENGTH; + auth_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; + auth_info.attrs.pin.pad_char = 0x00; + auth_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_NEEDS_PADDING; + auth_info.path.aid.len = AID_PKCS15_LEN; + memcpy(auth_info.path.aid.value, AID_PKCS15, AID_PKCS15_LEN); + auth_info.auth_id.len = 1; + auth_info.auth_id.value[0] = 1; + + strncpy(auth_obj.label, "User PIN", sizeof(auth_obj.label) - 1); + auth_obj.auth_id.len = 0; + auth_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; + + r = sc_pkcs15emu_add_pin_obj(p15card, &auth_obj, &auth_info); + if (r < 0) { + sc_log(card->ctx, "srbeid: add PIN obj failed: %d", r); + goto out; + } + } + } + + for (i = 0; i < ncerts; i++) { + sc_pkcs15_prkey_info_t key_info = {0}; + sc_pkcs15_object_t key_obj = {0}; + sc_pkcs15_cert_info_t cert_info = {0}; + sc_pkcs15_object_t cert_obj = {0}; + u8 *der = NULL; + size_t der_len = 0; + int is_kxc = (certs[i].key_pair_id == CE_AT_KEYEXCHANGE); + + /* ---- Private key object ---- */ + + key_info.id.len = 1; + key_info.id.value[0] = (u8)(i + 1); + key_info.native = 1; + key_info.key_reference = (int)certs[i].key_fid; + key_info.modulus_length = certs[i].key_size_bits + ? certs[i].key_size_bits + : 2048; + + /* + * Key usage flags by type: + * kxc (AT_KEYEXCHANGE) — encryption / key wrapping / decryption + signing + * (TLS client auth uses the key exchange cert for signing) + * ksc (AT_SIGNATURE) — digital signature / non-repudiation only + */ + if (is_kxc) { + key_info.usage = SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_WRAP | SC_PKCS15_PRKEY_USAGE_UNWRAP | SC_PKCS15_PRKEY_USAGE_SIGN; + } else { + key_info.usage = SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; + } + + /* + * Set only the AID on key_info.path (path.len stays 0). + * This makes select_key_file() select the PKI applet via AID + * before calling set_security_env(), without appending a file + * path that would fail on CardEdge's non-TLV FCI. + * + * The key FID is passed via key_info.key_reference and + * reconstructed in set_security_env() from the low byte. + */ + key_info.path.aid.len = AID_PKCS15_LEN; + memcpy(key_info.path.aid.value, AID_PKCS15, AID_PKCS15_LEN); + + strncpy(key_obj.label, certs[i].label, sizeof(key_obj.label) - 1); + key_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; + key_obj.auth_id.len = 1; + key_obj.auth_id.value[0] = 1; + + r = sc_pkcs15emu_add_rsa_prkey(p15card, &key_obj, &key_info); + if (r < 0) { + sc_log(card->ctx, "srbeid: add prkey[%d] failed: %d", i, r); + goto out; + } + + /* ---- Certificate object ---- */ + if (srbeid_read_cert_der(card, certs[i].cert_fid, &der, &der_len) < 0) { + sc_log(card->ctx, "srbeid: could not read cert[%d] DER", i); + continue; + } + + cert_info.id.len = 1; + cert_info.id.value[0] = (u8)(i + 1); + cert_info.authority = 0; + + /* Store DER directly in the PKCS#15 value buffer. */ + cert_info.value.value = der; /* ownership transferred */ + cert_info.value.len = der_len; + + strncpy(cert_obj.label, certs[i].label, sizeof(cert_obj.label) - 1); + + r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); + if (r < 0) { + sc_log(card->ctx, "srbeid: add cert[%d] failed: %d", i, r); + free(der); + goto out; + } + /* der ownership now belongs to p15card; do not free. */ + } + + sc_log(card->ctx, "srbeid: pkcs15 bind OK (%d certs)", ncerts); + +out: + free(certs); + return r; +} + +int +sc_pkcs15emu_srbeid_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid) +{ + (void)aid; + + if (p15card->card->type != SC_CARD_TYPE_SRBEID_BASE) + return SC_ERROR_WRONG_CARD; + + return sc_pkcs15emu_srbeid_init(p15card); +} diff --git a/src/libopensc/pkcs15-starcert.c b/src/libopensc/pkcs15-starcert.c index 68f2eeeddf..8aafb1dfab 100644 --- a/src/libopensc/pkcs15-starcert.c +++ b/src/libopensc/pkcs15-starcert.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -26,6 +26,7 @@ #include #include +#include "internal.h" #include "common/compat_strlcpy.h" #include "pkcs15.h" #include "cardctl.h" @@ -33,8 +34,6 @@ #define MANU_ID "Giesecke & Devrient GmbH" #define STARCERT "StarCertV2201" -int sc_pkcs15emu_starcert_init_ex(sc_pkcs15_card_t *, struct sc_aid *,sc_pkcs15emu_opt_t *); - typedef struct cdata_st { const char *label; int authority; @@ -52,11 +51,11 @@ typedef struct pdata_st { unsigned int maxlen; unsigned int minlen; unsigned int storedlen; - int flags; + int flags; int tries_left; const char pad_char; int obj_flags; -} pindata; +} pindata; typedef struct prdata_st { const char *id; @@ -89,14 +88,14 @@ static int get_cert_len(sc_card_t *card, sc_path_t *path) if (r < 0) return 0; r = sc_read_binary(card, 0, buf, sizeof(buf), 0); - if (r < 0) + if (r < 4) return 0; if (buf[0] != 0x30 || buf[1] != 0x82) return 0; path->index = 0; - path->count = ((((size_t) buf[2]) << 8) | buf[3]) + 4; + path->count = ((buf[2] << 8) | buf[3]) + 4; return 1; -} +} static int starcert_detect_card(sc_pkcs15_card_t *p15card) { @@ -106,7 +105,7 @@ static int starcert_detect_card(sc_pkcs15_card_t *p15card) sc_card_t *card = p15card->card; /* check if we have the correct card OS */ - if (strcmp(card->name, "STARCOS SPK 2.3")) + if (strcmp(card->name, "STARCOS")) return SC_ERROR_WRONG_CARD; /* read EF_Info file */ sc_format_path("3F00FE13", &path); @@ -116,7 +115,7 @@ static int starcert_detect_card(sc_pkcs15_card_t *p15card) r = sc_read_binary(card, 0, buf, 64, 0); if (r != 64) return SC_ERROR_WRONG_CARD; - if (memcmp(buf + 24, STARCERT, strlen(STARCERT))) + if (memcmp(buf + 24, STARCERT, strlen(STARCERT))) return SC_ERROR_WRONG_CARD; return SC_SUCCESS; @@ -164,22 +163,19 @@ static int sc_pkcs15emu_starcert_init(sc_pkcs15_card_t *p15card) /* get serial number */ r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); + if (r != SC_SUCCESS) + return SC_ERROR_INTERNAL; r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0); if (r != SC_SUCCESS) return SC_ERROR_INTERNAL; - if (p15card->tokeninfo->serial_number) - free(p15card->tokeninfo->serial_number); - p15card->tokeninfo->serial_number = malloc(strlen(buf) + 1); + + set_string(&p15card->tokeninfo->serial_number, buf); if (!p15card->tokeninfo->serial_number) return SC_ERROR_INTERNAL; - strcpy(p15card->tokeninfo->serial_number, buf); /* the manufacturer ID, in this case Giesecke & Devrient GmbH */ - if (p15card->tokeninfo->manufacturer_id) - free(p15card->tokeninfo->manufacturer_id); - p15card->tokeninfo->manufacturer_id = malloc(strlen(MANU_ID) + 1); + set_string(&p15card->tokeninfo->manufacturer_id, MANU_ID); if (!p15card->tokeninfo->manufacturer_id) - return SC_ERROR_INTERNAL; - strcpy(p15card->tokeninfo->manufacturer_id, MANU_ID); + goto err; /* set certs */ for (i = 0; certs[i].label; i++) { @@ -201,7 +197,7 @@ static int sc_pkcs15emu_starcert_init(sc_pkcs15_card_t *p15card) r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) - return SC_ERROR_INTERNAL; + goto err; } /* set pins */ for (i = 0; pins[i].label; i++) { @@ -230,7 +226,7 @@ static int sc_pkcs15emu_starcert_init(sc_pkcs15_card_t *p15card) r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if (r < 0) - return SC_ERROR_INTERNAL; + goto err; } /* set private keys */ for (i = 0; prkeys[i].label; i++) { @@ -254,33 +250,29 @@ static int sc_pkcs15emu_starcert_init(sc_pkcs15_card_t *p15card) r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); if (r < 0) - return SC_ERROR_INTERNAL; + goto err; } - + /* select the application DF */ sc_format_path("3F00DF01", &path); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS || !file) - return SC_ERROR_INTERNAL; + goto err; /* set the application DF */ - if (p15card->file_app) - free(p15card->file_app); + sc_file_free(p15card->file_app); p15card->file_app = file; return SC_SUCCESS; + +err: + sc_pkcs15_card_clear(p15card); + return SC_ERROR_INTERNAL; } int sc_pkcs15emu_starcert_init_ex(sc_pkcs15_card_t *p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t *opts) + struct sc_aid *aid) { - - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - return sc_pkcs15emu_starcert_init(p15card); - else { - int r = starcert_detect_card(p15card); - if (r) - return SC_ERROR_WRONG_CARD; - return sc_pkcs15emu_starcert_init(p15card); - } + if (starcert_detect_card(p15card)) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_starcert_init(p15card); } diff --git a/src/libopensc/pkcs15-starcos-esign.c b/src/libopensc/pkcs15-starcos-esign.c new file mode 100644 index 0000000000..9af8de4ab1 --- /dev/null +++ b/src/libopensc/pkcs15-starcos-esign.c @@ -0,0 +1,363 @@ +/** + * PKCS15 emulation layer for Giesecke & Devrient StarCOS 3.x cards + * with eSign application + * + * Copyright (C) 2022, jozsefd + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "common/compat_strlcpy.h" +#include "internal.h" +#include "log.h" +#include "pkcs15.h" +#include "cards.h" + +#include +#include + +/* compile time option: define ENABLE_ESIGN_ISSUER_CONTAINERS to enable containers holding the issuer certificates */ + +static const char name_Card[] = "ESIGN"; +static const char name_Vendor[] = "Giesecke & Devrient"; +static const char name_ESign[] = "ESIGN"; +static const unsigned char aid_ESIGN[] = {0xA0, 0x00, 0x00, 0x02, 0x45, 0x53, 0x69, 0x67, 0x6E}; + +typedef struct cdata_st { + const char *label; + int authority; + const char *path; + const char *id; + int obj_flags; +} cdata, *pcdata; + +typedef struct pdata_st { + const char *id; + const char *label; + const char *path; + int ref; + int type; + unsigned int maxlen; + unsigned int minlen; + unsigned int storedlen; + int flags; + int tries_left; + int max_tries; + const char pad_char; + int obj_flags; +} pindata, *ppindata; + +typedef struct prdata_st { + const char *id; + const char *label; + unsigned int modulus_len; + int usage; + const char *path; + int ref; + const char *auth_id; + int obj_flags; +} prdata, *pprdata; + +typedef struct container_st { + const char *id; + const pcdata certdata; + const ppindata pindata; + const pprdata prdata; +} container; + +#define USAGE_NONREP SC_PKCS15_PRKEY_USAGE_NONREPUDIATION +#define USAGE_KE SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ + SC_PKCS15_PRKEY_USAGE_DECRYPT | \ + SC_PKCS15_PRKEY_USAGE_WRAP | \ + SC_PKCS15_PRKEY_USAGE_UNWRAP +#define USAGE_AUT SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ + SC_PKCS15_PRKEY_USAGE_DECRYPT | \ + SC_PKCS15_PRKEY_USAGE_WRAP | \ + SC_PKCS15_PRKEY_USAGE_UNWRAP | \ + SC_PKCS15_PRKEY_USAGE_SIGN +#define USER_PIN SC_PKCS15_PIN_FLAG_INITIALIZED | \ + SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | \ + SC_PKCS15_PIN_TYPE_FLAGS_PIN_GLOBAL | \ + SC_PKCS15_PIN_AUTH_TYPE_PIN + +static int +get_cert_size(sc_card_t *card, sc_path_t *path, size_t *psize) +{ + int r; + sc_file_t *file = NULL; + + r = sc_select_file(card, path, &file); + LOG_TEST_RET(card->ctx, r, "Failed to select EF certificate"); + + *psize = file->size; + sc_file_free(file); + + return SC_SUCCESS; +} + +static int +add_app(sc_pkcs15_card_t *p15card, const container *containers, int container_count) +{ + int i, containers_added = 0, r = SC_SUCCESS; + ppindata installed_pins[4]; + size_t installed_pin_count = 0; + sc_card_t *card = p15card->card; + + LOG_FUNC_CALLED(card->ctx); + + for (i = 0; i < container_count; i++) { + struct sc_pkcs15_cert_info cert_info; + struct sc_pkcs15_object cert_obj; + size_t cert_size; + + memset(&cert_info, 0, sizeof(cert_info)); + memset(&cert_obj, 0, sizeof(cert_obj)); + + sc_pkcs15_format_id(containers[i].id, &cert_info.id); + cert_info.authority = containers[i].certdata->authority; + sc_format_path(containers[i].certdata->path, &cert_info.path); + + r = get_cert_size(card, &cert_info.path, &cert_size); + if ( r != SC_SUCCESS ) { + sc_log(card->ctx, "Failed to determine size of certificate %s, ignoring container", containers[i].certdata->label); + continue; + } + + strlcpy(cert_obj.label, containers[i].certdata->label, sizeof(cert_obj.label)); + cert_obj.flags = containers[i].certdata->obj_flags; + + r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); + LOG_TEST_RET(card->ctx, r, "Failed to add certificate"); + + if (containers[i].pindata != 0) { + size_t j; + int is_pin_installed = 0; + + /* A pin object could be used by more than 1 container, ensure it is added only once */ + for (j = 0; j < installed_pin_count; j++) { + if (installed_pins[j] == containers[i].pindata) { + is_pin_installed = 1; + break; + } + } + + if (!is_pin_installed) { + struct sc_pkcs15_auth_info pin_info; + struct sc_pkcs15_object pin_obj; + + if (installed_pin_count < (int)(sizeof(installed_pins) / sizeof(ppindata))) { + installed_pins[installed_pin_count++] = containers[i].pindata; + } else { + sc_log(card->ctx, "Warning: cannot add more than 4 pins"); + continue; + } + + memset(&pin_info, 0, sizeof(pin_info)); + memset(&pin_obj, 0, sizeof(pin_obj)); + + sc_pkcs15_format_id(containers[i].pindata->id, &pin_info.auth_id); + pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; + pin_info.attrs.pin.reference = containers[i].pindata->ref; + pin_info.attrs.pin.flags = containers[i].pindata->flags; + pin_info.attrs.pin.type = containers[i].pindata->type; + pin_info.attrs.pin.min_length = containers[i].pindata->minlen; + pin_info.attrs.pin.stored_length = containers[i].pindata->storedlen; + pin_info.attrs.pin.max_length = containers[i].pindata->maxlen; + pin_info.attrs.pin.pad_char = containers[i].pindata->pad_char; + if (containers[i].pindata->path != NULL) + sc_format_path(containers[i].pindata->path, &pin_info.path); + pin_info.tries_left = -1; + pin_info.max_tries = containers[i].pindata->max_tries; + + strlcpy(pin_obj.label, containers[i].pindata->label, sizeof(pin_obj.label)); + pin_obj.flags = containers[i].pindata->obj_flags; + + r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); + LOG_TEST_RET(card->ctx, r, "Failed to add PIN object"); + } + } + + if (containers[i].prdata != 0) { + struct sc_pkcs15_prkey_info prkey_info; + struct sc_pkcs15_object prkey_obj; + int modulus_len = containers[i].prdata->modulus_len; + memset(&prkey_info, 0, sizeof(prkey_info)); + memset(&prkey_obj, 0, sizeof(prkey_obj)); + + sc_pkcs15_format_id(containers[i].id, &prkey_info.id); + prkey_info.usage = containers[i].prdata->usage; + prkey_info.native = 1; + prkey_info.key_reference = containers[i].prdata->ref; + prkey_info.modulus_length = modulus_len; + sc_format_path(containers[i].prdata->path, &prkey_info.path); + + strlcpy(prkey_obj.label, containers[i].prdata->label, sizeof(prkey_obj.label)); + prkey_obj.flags = containers[i].prdata->obj_flags; + if (containers[i].prdata->auth_id) { + sc_pkcs15_format_id(containers[i].prdata->auth_id, &prkey_obj.auth_id); + } + + r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); + LOG_TEST_RET(card->ctx, r, "Failed to add RSA prkey"); + } + + containers_added++; + } + + if (containers_added == 0) { + r = SC_ERROR_INVALID_CARD; + } else { + r = SC_SUCCESS; + } + + LOG_FUNC_RETURN(card->ctx, r); +} + +/* + * adds the PKCS15 objects of the ESIGN application. The app may contain + * 1) Authentication container + * - 2048-bit RSA key + * - CH certificate + * 2) Encryption container + * - 2048-bit RSA key + * - CH certificate + * 3) Authentication Issuer container + * - issuer certificate + * 3) Encryption Issuer container + * - issuer certificate + * Depending on the card profile, some containers may be missing. + * Both RSA keys are protected with the UserPIN. The app may have a PUK, not + * supported by this emulator. + * + * The issuer certificates are not included by default, define ENABLE_ESIGN_ISSUER_CONTAINERS + * to enable them. + */ +static int +starcos_add_esign_app(sc_pkcs15_card_t *p15card) +{ + static cdata auth_cert = {"C.CH.AUT", 0, "3F00060843F1", "1", 0}; + static cdata encr_cert = {"C.CH.ENC", 0, "3F0006084301", "2", 0}; +#ifdef ENABLE_ESIGN_ISSUER_CONTAINERS + const cdata auth_root_cert = { "C.RootCA_Auth", 1, "3F00060843F0", "3", 0 }; + const cdata encr_root_cert = { "C.RootCA_Enc", 1, "3F0006084300", "4", 0 }; +#endif + + static prdata auth_key = {"1", "PrK.CH.AUT", 2048, USAGE_AUT, "3F000608", 0x81, "1", SC_PKCS15_CO_FLAG_PRIVATE}; + static prdata encr_key = {"2", "PrK.CH.ENC", 2048, USAGE_KE, "3F000608", 0x83, "1", SC_PKCS15_CO_FLAG_PRIVATE}; + + static pindata auth_pin = {"1", "UserPIN", "3F00", 0x01, SC_PKCS15_PIN_TYPE_UTF8, 16, 6, 0, + USER_PIN, -1, 3, 0x00, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE}; + + static pindata auth_pin_v35 = {"1", "UserPIN", "3F00", 0x06, SC_PKCS15_PIN_TYPE_UTF8, 16, 6, 0, + USER_PIN, -1, 3, 0x00, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE}; + + ppindata auth = (p15card->card->type == SC_CARD_TYPE_STARCOS_V3_5_ESIGN) ? &auth_pin_v35 : &auth_pin; + + const container containers[] = { + {"1", &auth_cert, auth, &auth_key}, + {"2", &encr_cert, auth, &encr_key}, +#ifdef ENABLE_ESIGN_ISSUER_CONTAINERS + { "3", &auth_root_cert, 0, 0 }, + { "4", &encr_root_cert, 0, 0 }, +#endif + }; + + return add_app(p15card, containers, sizeof(containers) / sizeof(container)); +} + +static int +starcos_esign_init(sc_pkcs15_card_t *p15card, struct sc_aid *aid) +{ + sc_card_t *card = p15card->card; + sc_context_t *ctx = card->ctx; + const char *label = name_Card; + int r; + int apps_added = 0; + + SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + + if (card->type != SC_CARD_TYPE_STARCOS_V3_4_ESIGN && card->type != SC_CARD_TYPE_STARCOS_V3_5_ESIGN) { + return SC_ERROR_WRONG_CARD; + } + + if (aid == NULL) { + // no aid: in this case all emulated apps are added, currently only the esign_app + r = starcos_add_esign_app(p15card); + if (r == SC_SUCCESS) + apps_added++; + } else { + // aid specified: only the matching app is added + if (aid->len == sizeof(aid_ESIGN) && memcmp(aid->value, aid_ESIGN, sizeof(aid_ESIGN)) == 0) { + r = starcos_add_esign_app(p15card); + if (r == SC_SUCCESS) { + label = name_ESign; + apps_added++; + } + } + + if (apps_added > 0) { + // pkcs11 requires the file_app + struct sc_path path; + struct sc_file *file = NULL; + sc_path_set(&path, SC_PATH_TYPE_DF_NAME, aid->value, aid->len, 0, 0); + r = sc_select_file(card, &path, &file); + if (r != SC_SUCCESS || !file) + return SC_ERROR_INTERNAL; + sc_file_free(p15card->file_app); + p15card->file_app = file; + } + } + + if (apps_added == 0) { + LOG_TEST_RET(ctx, SC_ERROR_WRONG_CARD, "No supported app found on this card"); + } + + sc_pkcs15_free_tokeninfo(p15card->tokeninfo); + + p15card->tokeninfo = sc_pkcs15_tokeninfo_new(); + if (!p15card->tokeninfo) { + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "unable to create tokeninfo struct"); + } else { + sc_serial_number_t serial; + char serial_hex[SC_MAX_SERIALNR * 2 + 2]; + r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); + LOG_TEST_RET(ctx, r, "Failed to query card serial number"); + + r = sc_bin_to_hex(serial.value, serial.len, serial_hex, sizeof serial_hex, 0); + LOG_TEST_RET(ctx, r, "Failed to convert S/N to hex"); + p15card->tokeninfo->serial_number = strdup(serial_hex); + p15card->tokeninfo->label = strdup(label); + p15card->tokeninfo->manufacturer_id = strdup(name_Vendor); + p15card->tokeninfo->flags = SC_PKCS15_TOKEN_READONLY; + } + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + +int +sc_pkcs15emu_starcos_esign_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid) +{ + int r = SC_ERROR_WRONG_CARD; + + if (!p15card || !p15card->card || !p15card->card->ctx) + return SC_ERROR_INVALID_ARGUMENTS; + + r = starcos_esign_init(p15card, aid); + LOG_FUNC_RETURN(p15card->card->ctx, r); +} diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c index d72fc313ba..375c3e7f3e 100644 --- a/src/libopensc/pkcs15-syn.c +++ b/src/libopensc/pkcs15-syn.c @@ -16,10 +16,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -33,35 +33,49 @@ #include "asn1.h" #include "pkcs15.h" #include "pkcs15-syn.h" +#include "pkcs15-emulator-filter.h" +// clang-format off struct sc_pkcs15_emulator_handler builtin_emulators[] = { - { "westcos", sc_pkcs15emu_westcos_init_ex }, { "openpgp", sc_pkcs15emu_openpgp_init_ex }, - { "infocamere", sc_pkcs15emu_infocamere_init_ex }, { "starcert", sc_pkcs15emu_starcert_init_ex }, { "tcos", sc_pkcs15emu_tcos_init_ex }, - { "esteid", sc_pkcs15emu_esteid_init_ex }, { "itacns", sc_pkcs15emu_itacns_init_ex }, - { "postecert", sc_pkcs15emu_postecert_init_ex }, { "PIV-II", sc_pkcs15emu_piv_init_ex }, { "cac", sc_pkcs15emu_cac_init_ex }, - { "gemsafeGPK", sc_pkcs15emu_gemsafeGPK_init_ex }, + { "idprime", sc_pkcs15emu_idprime_init_ex }, { "gemsafeV1", sc_pkcs15emu_gemsafeV1_init_ex }, - { "actalis", sc_pkcs15emu_actalis_init_ex }, - { "atrust-acos",sc_pkcs15emu_atrust_acos_init_ex}, - { "tccardos", sc_pkcs15emu_tccardos_init_ex }, - { "entersafe", sc_pkcs15emu_entersafe_init_ex }, + { "entersafe", sc_pkcs15emu_entersafe_init_ex }, { "pteid", sc_pkcs15emu_pteid_init_ex }, { "oberthur", sc_pkcs15emu_oberthur_init_ex }, { "sc-hsm", sc_pkcs15emu_sc_hsm_init_ex }, - { "dnie", sc_pkcs15emu_dnie_init_ex }, - { "gids", sc_pkcs15emu_gids_init_ex }, - { "iasecc", sc_pkcs15emu_iasecc_init_ex }, - { "jpki", sc_pkcs15emu_jpki_init_ex }, + { "dnie", sc_pkcs15emu_dnie_init_ex }, + { "gids", sc_pkcs15emu_gids_init_ex }, + { "iasecc", sc_pkcs15emu_iasecc_init_ex }, + { "jpki", sc_pkcs15emu_jpki_init_ex }, { "coolkey", sc_pkcs15emu_coolkey_init_ex }, + { "din66291", sc_pkcs15emu_din_66291_init_ex }, + { "esteid2018", sc_pkcs15emu_esteid2018_init_ex }, + { "esteid2025", sc_pkcs15emu_esteid2025_init_ex }, + { "skeid", sc_pkcs15emu_skeid_init_ex }, + { "cardos", sc_pkcs15emu_cardos_init_ex }, + { "nqapplet", sc_pkcs15emu_nqapplet_init_ex }, + { "esign", sc_pkcs15emu_starcos_esign_init_ex }, + { "eOI", sc_pkcs15emu_eoi_init_ex }, + { "dtrust", sc_pkcs15emu_dtrust_init_ex }, + { "lteid", sc_pkcs15emu_lteid_init_ex }, + { "srbeid", sc_pkcs15emu_srbeid_init_ex }, { NULL, NULL } }; +struct sc_pkcs15_emulator_handler old_emulators[] = { + { "atrust-acos",sc_pkcs15emu_atrust_acos_init_ex}, + { "actalis", sc_pkcs15emu_actalis_init_ex }, + { "tccardos", sc_pkcs15emu_tccardos_init_ex }, + { NULL, NULL } +}; +// clang-format on + static int parse_emu_block(sc_pkcs15_card_t *, struct sc_aid *, scconf_block *); static sc_pkcs15_df_t * sc_pkcs15emu_get_df(sc_pkcs15_card_t *p15card, unsigned int type); @@ -70,17 +84,15 @@ static const char *builtin_name = "builtin"; static const char *func_name = "sc_pkcs15_init_func"; static const char *exfunc_name = "sc_pkcs15_init_func_ex"; - +// FIXME: have a flag in card->flags to indicate the same int sc_pkcs15_is_emulation_only(sc_card_t *card) { switch (card->type) { - case SC_CARD_TYPE_MCRD_ESTEID_V10: - case SC_CARD_TYPE_MCRD_ESTEID_V11: - case SC_CARD_TYPE_MCRD_ESTEID_V30: case SC_CARD_TYPE_GEMSAFEV1_PTEID: case SC_CARD_TYPE_OPENPGP_V1: case SC_CARD_TYPE_OPENPGP_V2: case SC_CARD_TYPE_OPENPGP_GNUK: + case SC_CARD_TYPE_OPENPGP_V3: case SC_CARD_TYPE_SC_HSM: case SC_CARD_TYPE_SC_HSM_SOC: case SC_CARD_TYPE_DNIE_BASE: @@ -89,6 +101,34 @@ int sc_pkcs15_is_emulation_only(sc_card_t *card) case SC_CARD_TYPE_DNIE_USER: case SC_CARD_TYPE_DNIE_TERMINATED: case SC_CARD_TYPE_IASECC_GEMALTO: + case SC_CARD_TYPE_IASECC_CPX: + case SC_CARD_TYPE_IASECC_CPXCL: + case SC_CARD_TYPE_PIV_II_GENERIC: + case SC_CARD_TYPE_PIV_II_HIST: + case SC_CARD_TYPE_PIV_II_NEO: + case SC_CARD_TYPE_PIV_II_YUBIKEY4: + case SC_CARD_TYPE_PIV_II_SWISSBIT: + case SC_CARD_TYPE_ESTEID_2018: + case SC_CARD_TYPE_CARDOS_V5_0: + case SC_CARD_TYPE_CARDOS_V5_3: + case SC_CARD_TYPE_NQ_APPLET: + case SC_CARD_TYPE_NQ_APPLET_RFID: + case SC_CARD_TYPE_STARCOS_V3_4_ESIGN: + case SC_CARD_TYPE_STARCOS_V3_5_ESIGN: + case SC_CARD_TYPE_SKEID_V3: + case SC_CARD_TYPE_EOI: + case SC_CARD_TYPE_EOI_CONTACTLESS: + case SC_CARD_TYPE_DTRUST_V4_1_STD: + case SC_CARD_TYPE_DTRUST_V4_4_STD: + case SC_CARD_TYPE_DTRUST_V4_1_MULTI: + case SC_CARD_TYPE_DTRUST_V4_1_M100: + case SC_CARD_TYPE_DTRUST_V4_4_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_STD: + case SC_CARD_TYPE_DTRUST_V5_4_STD: + case SC_CARD_TYPE_DTRUST_V5_1_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_M100: + case SC_CARD_TYPE_DTRUST_V5_4_MULTI: + case SC_CARD_TYPE_LTEID: return 1; default: return 0; @@ -100,21 +140,19 @@ sc_pkcs15_bind_synthetic(sc_pkcs15_card_t *p15card, struct sc_aid *aid) { sc_context_t *ctx = p15card->card->ctx; scconf_block *conf_block, **blocks, *blk; - sc_pkcs15emu_opt_t opts; int i, r = SC_ERROR_WRONG_CARD; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); - memset(&opts, 0, sizeof(opts)); conf_block = NULL; conf_block = sc_get_conf_block(ctx, "framework", "pkcs15", 1); if (!conf_block) { - /* no conf file found => try bultin drivers */ + /* no conf file found => try builtin drivers */ sc_log(ctx, "no conf file (or section), trying all builtin emulators"); for (i = 0; builtin_emulators[i].name; i++) { sc_log(ctx, "trying %s", builtin_emulators[i].name); - r = builtin_emulators[i].handler(p15card, aid, &opts); + r = builtin_emulators[i].handler(p15card, aid); if (r == SC_SUCCESS) /* we got a hit */ goto out; @@ -122,32 +160,41 @@ sc_pkcs15_bind_synthetic(sc_pkcs15_card_t *p15card, struct sc_aid *aid) } else { /* we have a conf file => let's use it */ int builtin_enabled; - const scconf_list *list, *item; + const scconf_list *list; builtin_enabled = scconf_get_bool(conf_block, "enable_builtin_emulation", 1); list = scconf_find_list(conf_block, "builtin_emulators"); /* FIXME: rename to enabled_emulators */ if (builtin_enabled && list) { - /* get the list of enabled emulation drivers */ - for (item = list; item; item = item->next) { - /* go through the list of builtin drivers */ - const char *name = item->data; - - sc_log(ctx, "trying %s", name); - for (i = 0; builtin_emulators[i].name; i++) - if (!strcmp(builtin_emulators[i].name, name)) { - r = builtin_emulators[i].handler(p15card, aid, &opts); - if (r == SC_SUCCESS) - /* we got a hit */ - goto out; - } + /* filter enabled emulation drivers from conf file */ + struct _sc_pkcs15_emulators filtered_emulators; + struct sc_pkcs15_emulator_handler** lst; + int ret; + + filtered_emulators.ccount = 0; + ret = set_emulators(ctx, &filtered_emulators, list, builtin_emulators, old_emulators); + if (ret == SC_SUCCESS || ret == SC_ERROR_TOO_MANY_OBJECTS) { + lst = filtered_emulators.list_of_handlers; + + if (ret == SC_ERROR_TOO_MANY_OBJECTS) + sc_log(ctx, "trying first %d emulators from conf file", SC_MAX_PKCS15_EMULATORS); + + for (i = 0; lst[i]; i++) { + sc_log(ctx, "trying %s", lst[i]->name); + r = lst[i]->handler(p15card, aid); + if (r == SC_SUCCESS) + /* we got a hit */ + goto out; + } + } else { + sc_log(ctx, "failed to filter enabled card emulators: %s", sc_strerror(ret)); } } else if (builtin_enabled) { sc_log(ctx, "no emulator list in config file, trying all builtin emulators"); for (i = 0; builtin_emulators[i].name; i++) { sc_log(ctx, "trying %s", builtin_emulators[i].name); - r = builtin_emulators[i].handler(p15card, aid, &opts); + r = builtin_emulators[i].handler(p15card, aid); if (r == SC_SUCCESS) /* we got a hit */ goto out; @@ -188,10 +235,9 @@ static int parse_emu_block(sc_pkcs15_card_t *p15card, struct sc_aid *aid, scconf { sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; - sc_pkcs15emu_opt_t opts; void *handle = NULL; int (*init_func)(sc_pkcs15_card_t *); - int (*init_func_ex)(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); + int (*init_func_ex)(sc_pkcs15_card_t *, struct sc_aid *); int r; const char *driver, *module_name; @@ -200,9 +246,6 @@ static int parse_emu_block(sc_pkcs15_card_t *p15card, struct sc_aid *aid, scconf init_func = NULL; init_func_ex = NULL; - memset(&opts, 0, sizeof(opts)); - opts.blk = conf; - module_name = scconf_get_str(conf, "module", builtin_name); if (!strcmp(module_name, "builtin")) { int i; @@ -216,6 +259,14 @@ static int parse_emu_block(sc_pkcs15_card_t *p15card, struct sc_aid *aid, scconf break; } } + if (init_func_ex == NULL) { + for (i = 0; old_emulators[i].name; i++) { + if (!strcmp(old_emulators[i].name, module_name)) { + init_func_ex = old_emulators[i].handler; + break; + } + } + } } else { const char *(*get_version)(void); const char *name = NULL; @@ -257,12 +308,12 @@ static int parse_emu_block(sc_pkcs15_card_t *p15card, struct sc_aid *aid, scconf address = sc_dlsym(handle, name); if (address) - init_func_ex = (int (*)(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *)) address; + init_func_ex = (int (*)(sc_pkcs15_card_t *, struct sc_aid *)) address; } } /* try to initialize the pkcs15 structures */ if (init_func_ex) - r = init_func_ex(p15card, aid, &opts); + r = init_func_ex(p15card, aid); else if (init_func) r = init_func(p15card); else @@ -298,7 +349,8 @@ static sc_pkcs15_df_t * sc_pkcs15emu_get_df(sc_pkcs15_card_t *p15card, } } - assert(created == 0); + if (created != 0) + return NULL; file = sc_file_new(); if (!file) @@ -360,6 +412,7 @@ int sc_pkcs15emu_add_ec_prkey(sc_pkcs15_card_t *p15card, return sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_PRKEY_EC, obj, &key); } + int sc_pkcs15emu_add_ec_pubkey(sc_pkcs15_card_t *p15card, const sc_pkcs15_object_t *obj, const sc_pkcs15_pubkey_info_t *in_key) { @@ -371,6 +424,56 @@ int sc_pkcs15emu_add_ec_pubkey(sc_pkcs15_card_t *p15card, return sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_PUBKEY_EC, obj, &key); } +int sc_pkcs15emu_add_eddsa_prkey(sc_pkcs15_card_t *p15card, + const sc_pkcs15_object_t *obj, const sc_pkcs15_prkey_info_t *in_key) +{ + sc_pkcs15_prkey_info_t key = *in_key; + + if (key.access_flags == 0) + key.access_flags = SC_PKCS15_PRKEY_ACCESS_SENSITIVE + | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE + | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE + | SC_PKCS15_PRKEY_ACCESS_LOCAL; + + return sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_PRKEY_EDDSA, obj, &key); +} + +int sc_pkcs15emu_add_eddsa_pubkey(sc_pkcs15_card_t *p15card, + const sc_pkcs15_object_t *obj, const sc_pkcs15_pubkey_info_t *in_key) +{ + sc_pkcs15_pubkey_info_t key = *in_key; + + if (key.access_flags == 0) + key.access_flags = SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE; + + return sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_PUBKEY_EDDSA, obj, &key); +} + +int sc_pkcs15emu_add_xeddsa_prkey(sc_pkcs15_card_t *p15card, + const sc_pkcs15_object_t *obj, const sc_pkcs15_prkey_info_t *in_key) +{ + sc_pkcs15_prkey_info_t key = *in_key; + + if (key.access_flags == 0) + key.access_flags = SC_PKCS15_PRKEY_ACCESS_SENSITIVE + | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE + | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE + | SC_PKCS15_PRKEY_ACCESS_LOCAL; + + return sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_PRKEY_XEDDSA, obj, &key); +} + +int sc_pkcs15emu_add_xeddsa_pubkey(sc_pkcs15_card_t *p15card, + const sc_pkcs15_object_t *obj, const sc_pkcs15_pubkey_info_t *in_key) +{ + sc_pkcs15_pubkey_info_t key = *in_key; + + if (key.access_flags == 0) + key.access_flags = SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE; + + return sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_PUBKEY_XEDDSA, obj, &key); +} + int sc_pkcs15emu_add_x509_cert(sc_pkcs15_card_t *p15card, const sc_pkcs15_object_t *obj, const sc_pkcs15_cert_info_t *cert) { @@ -390,11 +493,15 @@ int sc_pkcs15emu_object_add(sc_pkcs15_card_t *p15card, unsigned int type, unsigned int df_type; size_t data_len; + SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); + obj = calloc(1, sizeof(*obj)); - if (!obj) - return SC_ERROR_OUT_OF_MEMORY; + if (!obj) { + LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + memcpy(obj, in_obj, sizeof(*obj)); - obj->type = type; + obj->type = type; switch (type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_AUTH: @@ -420,19 +527,19 @@ int sc_pkcs15emu_object_add(sc_pkcs15_card_t *p15card, unsigned int type, default: sc_log(p15card->card->ctx, "Unknown PKCS15 object type %d", type); free(obj); - return SC_ERROR_INVALID_ARGUMENTS; + LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_INVALID_ARGUMENTS); } obj->data = calloc(1, data_len); if (obj->data == NULL) { free(obj); - return SC_ERROR_OUT_OF_MEMORY; + LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(obj->data, data, data_len); obj->df = sc_pkcs15emu_get_df(p15card, df_type); sc_pkcs15_add_object(p15card, obj); - return SC_SUCCESS; + LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } diff --git a/src/libopensc/pkcs15-syn.h b/src/libopensc/pkcs15-syn.h index 22db061bec..fcbf861823 100644 --- a/src/libopensc/pkcs15-syn.h +++ b/src/libopensc/pkcs15-syn.h @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PKCS15_SYN_H @@ -29,34 +29,41 @@ extern "C" { #include #include -int sc_pkcs15emu_westcos_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *, sc_pkcs15emu_opt_t *opts); -int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_infocamere_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_starcert_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_tcos_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_esteid_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_postecert_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_piv_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *, sc_pkcs15emu_opt_t *opts); -int sc_pkcs15emu_cac_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *, sc_pkcs15emu_opt_t *opts); -int sc_pkcs15emu_gemsafeGPK_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *, sc_pkcs15emu_opt_t *opts); -int sc_pkcs15emu_gemsafeV1_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *, sc_pkcs15emu_opt_t *opts); -int sc_pkcs15emu_actalis_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *, sc_pkcs15emu_opt_t *opts); -int sc_pkcs15emu_atrust_acos_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *, sc_pkcs15emu_opt_t *opts); -int sc_pkcs15emu_tccardos_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_entersafe_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_pteid_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_oberthur_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_itacns_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_sc_hsm_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_dnie_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_gids_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_iasecc_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_jpki_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); -int sc_pkcs15emu_coolkey_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *, sc_pkcs15emu_opt_t *opts); +int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_starcert_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_tcos_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_esteid2018_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_esteid2025_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_piv_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_cac_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_gemsafeV1_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_actalis_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_atrust_acos_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_tccardos_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_entersafe_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_pteid_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_oberthur_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_itacns_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_sc_hsm_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_dnie_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_gids_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_iasecc_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_jpki_init_ex(sc_pkcs15_card_t *, struct sc_aid *); +int sc_pkcs15emu_coolkey_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_din_66291_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_idprime_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_cardos_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_nqapplet_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_starcos_esign_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_skeid_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_eoi_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_dtrust_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_lteid_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_srbeid_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); struct sc_pkcs15_emulator_handler { const char *name; - int (*handler)(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); + int (*handler)(sc_pkcs15_card_t *, struct sc_aid *); }; #ifdef __cplusplus diff --git a/src/libopensc/pkcs15-tccardos.c b/src/libopensc/pkcs15-tccardos.c index b1779b5ec6..4ffe8b1539 100644 --- a/src/libopensc/pkcs15-tccardos.c +++ b/src/libopensc/pkcs15-tccardos.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -41,10 +41,6 @@ #define TC_CARDOS_GLOBALPIN 0x3000 #define TC_CARDOS_PIN_MASK 0x3000 -int sc_pkcs15emu_tccardos_init_ex(sc_pkcs15_card_t *p15card, - struct sc_aid *, - sc_pkcs15emu_opt_t *opts); - static int read_file(struct sc_card *card, const char *file, u8 *buf, size_t *len) { @@ -54,12 +50,14 @@ static int read_file(struct sc_card *card, const char *file, u8 *buf, sc_format_path(file, &path); r = sc_select_file(card, &path, &fid); - if (r != SC_SUCCESS || !fid) + if (r != SC_SUCCESS) return r; + if (!fid) + return SC_ERROR_INTERNAL; if (fid->size < *len) *len = fid->size; r = sc_read_binary(card, 0, buf, *len, 0); - free(fid); + sc_file_free(fid); if ((size_t)r < *len) return SC_ERROR_INTERNAL; @@ -215,11 +213,11 @@ static int parse_EF_CardInfo(sc_pkcs15_card_t *p15card) size_t i; unsigned int key_num; struct sc_context *ctx = p15card->card->ctx; - size_t offset; + size_t offset, available_space; /* read EF_CardInfo1 */ r = read_file(p15card->card, "3F001003b200", info1, &info1_len); - if (r != SC_SUCCESS) + if (r != SC_SUCCESS || info1_len < 4) return SC_ERROR_WRONG_CARD; /* read EF_CardInfo2 */ r = read_file(p15card->card, "3F001003b201", info2, &info2_len); @@ -230,18 +228,30 @@ static int parse_EF_CardInfo(sc_pkcs15_card_t *p15card) | (((unsigned int) info1[info1_len-2]) << 8) | (((unsigned int) info1[info1_len-3]) << 16) | (((unsigned int) info1[info1_len-4]) << 24); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "found %d private keys\n", (int)key_num); - /* set p1 to the address of the first key descriptor */ - offset = info1_len - 4 - key_num * 2; - if (offset >= sizeof info1) + sc_log(ctx, "found %u private keys", key_num); + available_space = info1_len - 4; + + /* Verify bounds for the following loop + * - the info1 has 2 bytes for each key + * - the info2 has at least 14 bytes for each key (16 bytes at some cases -- checked later) + */ + if (key_num > available_space / 2 || key_num > info2_len / 14) { return SC_ERROR_INVALID_DATA; + } + offset = available_space - key_num * 2; + /* set p1 to the address of the first key descriptor */ p1 = info1 + offset; p2 = info2; + for (i=0; i info2_len) { + return SC_ERROR_INVALID_DATA; + } /* evaluate CertInfo2 */ cert_count = *p2++; p2 += 2; /* ignore cert DF (it's always 1002) */ @@ -256,6 +266,9 @@ static int parse_EF_CardInfo(sc_pkcs15_card_t *p15card) r1_cert = (p2[0] << 8) | p2[1]; p2 += 2; if (cert_count == 4) { + if ((size_t)(p2 - info2) + 2 > info2_len) { + return SC_ERROR_INVALID_DATA; + } r2_cert = (p2[0] << 8) | p2[1]; p2 += 2; } @@ -304,10 +317,10 @@ static int sc_pkcs15_tccardos_init_func(sc_pkcs15_card_t *p15card) int r; struct sc_path path; struct sc_file *file = NULL; - u8 gdo[MAX_INFO1_SIZE]; char hex_buf[256]; - size_t gdo_len = MAX_INFO1_SIZE; struct sc_card *card = p15card->card; + sc_serial_number_t iccsn; + iccsn.len = sizeof iccsn.value; /* check if we have the correct card OS */ if (strcmp(card->name, "CardOS M4")) @@ -317,41 +330,47 @@ static int sc_pkcs15_tccardos_init_func(sc_pkcs15_card_t *p15card) if (r != SC_SUCCESS) return r; /* set card label */ - if (p15card->tokeninfo->label != NULL) - free(p15card->tokeninfo->label); - p15card->tokeninfo->label = strdup(TC_CARDOS_LABEL); + set_string(&p15card->tokeninfo->label, TC_CARDOS_LABEL); if (p15card->tokeninfo->label == NULL) return SC_ERROR_OUT_OF_MEMORY; /* set the manufacturer ID */ - if (p15card->tokeninfo->manufacturer_id != NULL) - free(p15card->tokeninfo->manufacturer_id); - p15card->tokeninfo->manufacturer_id = strdup(MANU_ID); - if (p15card->tokeninfo->manufacturer_id == NULL) - return SC_ERROR_OUT_OF_MEMORY; + set_string(&p15card->tokeninfo->manufacturer_id, MANU_ID); + if (p15card->tokeninfo->manufacturer_id == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } /* set the serial number */ - r = read_file(p15card->card, "3F002F02", gdo, &gdo_len); - if (r != SC_SUCCESS) - return SC_ERROR_INTERNAL; - sc_bin_to_hex(gdo + 7, 8, hex_buf, sizeof(hex_buf), 0); - p15card->tokeninfo->serial_number = strdup(hex_buf); - if (p15card->tokeninfo->serial_number == NULL) - return SC_ERROR_OUT_OF_MEMORY; + r = sc_parse_ef_gdo(card, iccsn.value, &iccsn.len, NULL, 0); + if (r != SC_SUCCESS || iccsn.len < 5+8) { + r = SC_ERROR_INTERNAL; + goto err; + } + sc_bin_to_hex(iccsn.value + 5, 8, hex_buf, sizeof(hex_buf), 0); + set_string(&p15card->tokeninfo->serial_number, hex_buf); + if (p15card->tokeninfo->serial_number == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } /* select the application DF */ sc_format_path(TC_CARDOS_APP_DF, &path); r = sc_select_file(card, &path, &file); - if (r != SC_SUCCESS || file == NULL) - return SC_ERROR_INTERNAL; + if (r != SC_SUCCESS || file == NULL) { + r = SC_ERROR_INTERNAL; + goto err; + } /* set the application DF */ - if (p15card->file_app) - free(p15card->file_app); + sc_file_free(p15card->file_app); p15card->file_app = file; return SC_SUCCESS; + +err: + sc_pkcs15_card_clear(p15card); + return r; } int sc_pkcs15emu_tccardos_init_ex(sc_pkcs15_card_t *p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t *opts) + struct sc_aid *aid) { return sc_pkcs15_tccardos_init_func(p15card); } diff --git a/src/libopensc/pkcs15-tcos.c b/src/libopensc/pkcs15-tcos.c index 74bb32a6fd..24e8cee4a1 100644 --- a/src/libopensc/pkcs15-tcos.c +++ b/src/libopensc/pkcs15-tcos.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -33,11 +33,6 @@ #include "cardctl.h" #include "log.h" -int sc_pkcs15emu_tcos_init_ex( - sc_pkcs15_card_t *p15card, - struct sc_aid *, - sc_pkcs15emu_opt_t *opts); - static int insert_cert( sc_pkcs15_card_t *p15card, const char *path, @@ -50,6 +45,7 @@ static int insert_cert( struct sc_pkcs15_cert_info cert_info; struct sc_pkcs15_object cert_obj; unsigned char cert[20]; + size_t cert_len = 0; int r; memset(&cert_info, 0, sizeof(cert_info)); @@ -62,24 +58,33 @@ static int insert_cert( strlcpy(cert_obj.label, label, sizeof(cert_obj.label)); cert_obj.flags = writable ? SC_PKCS15_CO_FLAG_MODIFIABLE : 0; - if(sc_select_file(card, &cert_info.path, NULL)!=SC_SUCCESS){ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "Select(%s) failed\n", path); + if (sc_select_file(card, &cert_info.path, NULL) != SC_SUCCESS) { + sc_log(ctx, "Select(%s) failed", path); return 1; } - if(sc_read_binary(card, 0, cert, sizeof(cert), 0)<0){ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "ReadBinary(%s) failed\n", path); + r = sc_read_binary(card, 0, cert, sizeof(cert), 0); + if (r <= 0) { + sc_log(ctx, "ReadBinary(%s) failed\n", path); return 2; } - if(cert[0]!=0x30 || cert[1]!=0x82){ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "Invalid Cert: %02X:%02X:...\n", cert[0], cert[1]); + cert_len = r; /* actual number of read bytes */ + if (cert_len < 4) { + sc_log(ctx, "Invalid certificate length"); + return 3; + } + if (cert[0] != 0x30 || cert[1] != 0x82) { + sc_log(ctx, "Invalid Cert: %02X:%02X:...\n", cert[0], cert[1]); return 3; } /* some certificates are prefixed by an OID */ - if(cert[4]==0x06 && cert[5]<10 && cert[6+cert[5]]==0x30 && cert[7+cert[5]]==0x82){ + if (cert_len > 9 && (size_t)(7 + cert[5]) <= cert_len && + cert[4] == 0x06 && cert[5] < 10 && cert[6 + cert[5]] == 0x30 && + cert[7 + cert[5]] == 0x82) { + if ((size_t)(9 + cert[5]) > cert_len) { + sc_log(ctx, "Invalid certificate length"); + return 3; + } cert_info.path.index=6+cert[5]; cert_info.path.count=(cert[8+cert[5]]<<8) + cert[9+cert[5]] + 4; } else { @@ -87,12 +92,12 @@ static int insert_cert( cert_info.path.count=(cert[2]<<8) + cert[3] + 4; } - r=sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); - if(r!=SC_SUCCESS){ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "sc_pkcs15emu_add_x509_cert(%s) failed\n", path); + r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); + if (r != SC_SUCCESS) { + sc_log(ctx, "sc_pkcs15emu_add_x509_cert(%s) failed", path); return 4; } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "%s: OK, Index=%d, Count=%d\n", path, cert_info.path.index, cert_info.path.count); + sc_log(ctx, "%s: OK, Index=%d, Count=%d", path, cert_info.path.index, cert_info.path.count); return 0; } @@ -104,9 +109,10 @@ static int insert_key( int key_length, unsigned char auth_id, const char *label -){ - sc_card_t *card=p15card->card; - sc_context_t *ctx=p15card->card->ctx; +) +{ + sc_card_t *card = p15card->card; + sc_context_t *ctx = p15card->card->ctx; sc_file_t *f; struct sc_pkcs15_prkey_info prkey_info; struct sc_pkcs15_object prkey_obj; @@ -126,57 +132,64 @@ static int insert_key( prkey_obj.auth_id.len = 1; prkey_obj.auth_id.value[0] = auth_id; - can_sign=can_crypt=0; - if(card->type==SC_CARD_TYPE_TCOS_V3){ + can_sign = can_crypt = 0; + if (card->type == SC_CARD_TYPE_TCOS_V3) { unsigned char buf[256]; - int i, rec_no=0; - if(prkey_info.path.len>=2) prkey_info.path.len-=2; + int i, rec_no = 0; + if (prkey_info.path.len >= 2) + prkey_info.path.len -= 2; sc_append_file_id(&prkey_info.path, 0x5349); - if(sc_select_file(card, &prkey_info.path, NULL)!=SC_SUCCESS){ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "Select(%s) failed\n", - sc_print_path(&prkey_info.path)); + if (sc_select_file(card, &prkey_info.path, NULL) != SC_SUCCESS) { + sc_log(ctx, "Select(%s) failed", sc_print_path(&prkey_info.path)); return 1; } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "Searching for Key-Ref %02X\n", key_reference); - while((r=sc_read_record(card, ++rec_no, buf, sizeof(buf), SC_RECORD_BY_REC_NR))>0){ - int found=0; - if(buf[0]!=0xA0) continue; - for(i=2;i 0) { + int found = 0; + if (buf[0] != 0xA0 || r < 2) + continue; + for (i = 2; i < buf[1] + 2 && i < r - 2; i += 2 + buf[i + 1]) { + if (buf[i] == 0x83 && buf[i + 1] == 1 && buf[i + 2] == key_reference) + ++found; } - if(found) break; + if (found) + break; } - if(r<=0){ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL,"No EF_KEYD-Record found\n"); + if (r <= 0) { + sc_log(ctx, "No EF_KEYD-Record found"); return 1; } - for(i=0;iprop_attr || f->prop_attr_len < 2){ + sc_log(ctx, "Select(%s) failed", sc_print_path(&prkey_info.path)); + sc_file_free(f); return 1; } - if (f->prop_attr[1] & 0x04) can_crypt=1; - if (f->prop_attr[1] & 0x08) can_sign=1; + if (f->prop_attr[1] & 0x04) + can_crypt = 1; + if (f->prop_attr[1] & 0x08) + can_sign = 1; sc_file_free(f); } - prkey_info.usage= SC_PKCS15_PRKEY_USAGE_SIGN; - if(can_crypt) prkey_info.usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT|SC_PKCS15_PRKEY_USAGE_DECRYPT; - if(can_sign) prkey_info.usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; - - r=sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); - if(r!=SC_SUCCESS){ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "sc_pkcs15emu_add_rsa_prkey(%s) failed\n", path); + prkey_info.usage = SC_PKCS15_PRKEY_USAGE_SIGN; + if (can_crypt) + prkey_info.usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT; + if (can_sign) + prkey_info.usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; + + r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); + if(r != SC_SUCCESS) { + sc_log(ctx, "sc_pkcs15emu_add_rsa_prkey(%s) failed\n", path); return 4; } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "%s: OK%s%s\n", path, can_sign ? ", Sign" : "", can_crypt ? ", Crypt" : ""); + sc_log(ctx, "%s: OK%s%s\n", path, can_sign ? ", Sign" : "", can_crypt ? ", Crypt" : ""); return 0; } @@ -192,7 +205,7 @@ static int insert_pin( ){ sc_card_t *card=p15card->card; sc_context_t *ctx=p15card->card->ctx; - sc_file_t *f; + sc_file_t *f = NULL; struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; int r; @@ -217,48 +230,56 @@ static int insert_pin( pin_obj.auth_id.len = auth_id ? 0 : 1; pin_obj.auth_id.value[0] = auth_id; - if(card->type==SC_CARD_TYPE_TCOS_V3){ + if(card->type == SC_CARD_TYPE_TCOS_V3) { unsigned char buf[256]; int i, rec_no=0; - if(pin_info.path.len>=2) pin_info.path.len-=2; + if (pin_info.path.len >= 2) { + pin_info.path.len -= 2; + } sc_append_file_id(&pin_info.path, 0x5049); - if(sc_select_file(card, &pin_info.path, NULL)!=SC_SUCCESS){ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "Select(%s) failed\n", - sc_print_path(&pin_info.path)); + if (sc_select_file(card, &pin_info.path, NULL) != SC_SUCCESS) { + sc_log(ctx, "Select(%s) failed", sc_print_path(&pin_info.path)); return 1; } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "Searching for PIN-Ref %02X\n", pin_reference); - while((r=sc_read_record(card, ++rec_no, buf, sizeof(buf), SC_RECORD_BY_REC_NR))>0){ - int found=0, fbz=-1; - if(buf[0]!=0xA0) continue; - for(i=2;i 0) { + int found = 0, fbz = -1; + if (r < 2 || buf[0] != 0xA0) + continue; + for (i = 2; i < buf[1] + 2 && (i + 2) < r; i += 2 + buf[i + 1]) { + if (buf[i] == 0x83 && buf[i + 1] == 1 && buf[i + 2] == pin_reference) { + ++found; + } + if (buf[i] == 0x90 && (i + 1 + buf[i + 1]) < r) { + fbz = buf[i + 1 + buf[i + 1]]; + } + } + if (found) { + pin_info.tries_left = fbz; + break; } - if(found) pin_info.tries_left=fbz; - if(found) break; } - if(r<=0){ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL,"No EF_PWDD-Record found\n"); + if (r <= 0) { + sc_log(ctx, "No EF_PWDD-Record found\n"); return 1; } } else { - if(sc_select_file(card, &pin_info.path, &f)!=SC_SUCCESS){ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL,"Select(%s) failed\n", path); + if (sc_select_file(card, &pin_info.path, &f) != SC_SUCCESS + || !f->prop_attr || f->prop_attr_len < 4){ + sc_log(ctx, "Select(%s) failed\n", path); + sc_file_free(f); return 1; } - pin_info.tries_left=f->prop_attr[3]; + pin_info.tries_left = f->prop_attr[3]; sc_file_free(f); } r=sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if(r!=SC_SUCCESS){ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "sc_pkcs15emu_add_pin_obj(%s) failed\n", path); + sc_log(ctx, "sc_pkcs15emu_add_pin_obj(%s) failed\n", path); return 4; } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "%s: OK, FBZ=%d\n", path, pin_info.tries_left); + sc_log(ctx, "%s: OK, FBZ=%d\n", path, pin_info.tries_left); return 0; } @@ -288,8 +309,8 @@ static int detect_netkey( sprintf(dir,"%04X", f->id); sc_file_free(f); - p15card->tokeninfo->manufacturer_id = strdup("TeleSec GmbH"); - p15card->tokeninfo->label = strdup(card->type==SC_CARD_TYPE_TCOS_V3 ? "NetKey V3 Card" : "NetKey Card"); + set_string(&p15card->tokeninfo->manufacturer_id, "TeleSec GmbH"); + set_string(&p15card->tokeninfo->label, card->type==SC_CARD_TYPE_TCOS_V3 ? "NetKey V3 Card" : "NetKey Card"); keylen= card->type==SC_CARD_TYPE_TCOS_V3 ? 2048 : 1024; c_auth= card->type==SC_CARD_TYPE_TCOS_V3 ? "C500" : "C100"; @@ -342,7 +363,7 @@ static int detect_netkey( insert_cert(p15card, dirpath(dir,"C000"), 0x49, 1, "SigG Zertifikat 1"); insert_cert(p15card, dirpath(dir,"4331"), 0x49, 1, "SigG Zertifikat 2"); insert_cert(p15card, dirpath(dir,"4332"), 0x49, 1, "SigG Zertifikat 3"); - + if(card->type==SC_CARD_TYPE_TCOS_V3){ insert_key(p15card, dirpath(dir,"0000"), 0x49, 0x84, 2048, 5, "SigG Schluessel"); } else { @@ -376,12 +397,16 @@ static int detect_idkey( memcpy(p.value, "\xD2\x76\x00\x00\x03\x0C\x01", p.len=7); if (sc_select_file(card,&p,NULL)!=SC_SUCCESS) return 1; - p15card->tokeninfo->manufacturer_id = strdup("TeleSec GmbH"); - p15card->tokeninfo->label = strdup("IDKey Card"); + set_string(&p15card->tokeninfo->manufacturer_id, "TeleSec GmbH"); + set_string(&p15card->tokeninfo->label, "IDKey Card"); insert_cert(p15card, "DF074331", 0x45, 1, "Signatur Zertifikat 1"); - insert_cert(p15card, "DF074332", 0x45, 1, "Signatur Zertifikat 2"); - insert_cert(p15card, "DF074333", 0x45, 1, "Signatur Zertifikat 3"); + insert_cert(p15card, "DF074332", 0x46, 1, "Signatur Zertifikat 2"); + insert_cert(p15card, "DF074333", 0x47, 1, "Signatur Zertifikat 3"); + insert_cert(p15card, "DF084331", 0x4B, 1, "Verschluesselungs Zertifikat 1"); + insert_cert(p15card, "DF084332", 0x4C, 1, "Verschluesselungs Zertifikat 2"); + insert_cert(p15card, "DF084333", 0x4D, 1, "Verschluesselungs Zertifikat 3"); + /* TODO should others come here too? */ insert_key(p15card, "DF074E03", 0x45, 0x84, 2048, 1, "IDKey1"); insert_key(p15card, "DF074E04", 0x46, 0x85, 2048, 1, "IDKey2"); @@ -389,6 +414,9 @@ static int detect_idkey( insert_key(p15card, "DF074E06", 0x48, 0x87, 2048, 1, "IDKey4"); insert_key(p15card, "DF074E07", 0x49, 0x88, 2048, 1, "IDKey5"); insert_key(p15card, "DF074E08", 0x4A, 0x89, 2048, 1, "IDKey6"); + insert_key(p15card, "DF084E01", 0x4B, 0x81, 2048, 1, "IDKey7"); + insert_key(p15card, "DF084E02", 0x4C, 0x82, 2048, 1, "IDKey8"); + insert_key(p15card, "DF084E03", 0x4D, 0x83, 2048, 1, "IDKey9"); insert_pin(p15card, "5000", 1, 2, 0x00, 6, "PIN", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED @@ -405,8 +433,8 @@ static int detect_signtrust( sc_pkcs15_card_t *p15card ){ if(insert_cert(p15card,"8000DF01C000", 0x45, 1, "Signatur Zertifikat")) return 1; - p15card->tokeninfo->manufacturer_id = strdup("Deutsche Post"); - p15card->tokeninfo->label = strdup("SignTrust Card"); + set_string(&p15card->tokeninfo->manufacturer_id, "Deutsche Post"); + set_string(&p15card->tokeninfo->label, "SignTrust Card"); insert_cert(p15card,"800082008220", 0x46, 1, "Verschluesselungs Zertifikat"); insert_cert(p15card,"800083008320", 0x47, 1, "Authentifizierungs Zertifikat"); @@ -435,8 +463,8 @@ static int detect_datev( sc_pkcs15_card_t *p15card ){ if(insert_cert(p15card,"3000C500", 0x45, 0, "Signatur Zertifikat")) return 1; - p15card->tokeninfo->manufacturer_id = strdup("DATEV"); - p15card->tokeninfo->label = strdup("DATEV Classic"); + set_string(&p15card->tokeninfo->manufacturer_id, "DATEV"); + set_string(&p15card->tokeninfo->label, "DATEV Classic"); insert_cert(p15card,"DF02C200", 0x46, 0, "Verschluesselungs Zertifikat"); insert_cert(p15card,"DF02C500", 0x47, 0, "Authentifizierungs Zertifikat"); @@ -456,8 +484,8 @@ static int detect_unicard( sc_pkcs15_card_t *p15card ){ if(!insert_cert(p15card,"41004352", 0x45, 1, "Zertifikat 1")){ - p15card->tokeninfo->manufacturer_id = strdup("JLU Giessen"); - p15card->tokeninfo->label = strdup("JLU Giessen Card"); + set_string(&p15card->tokeninfo->manufacturer_id, "JLU Giessen"); + set_string(&p15card->tokeninfo->label, "JLU Giessen Card"); insert_cert(p15card,"41004353", 0x46, 1, "Zertifikat 2"); insert_cert(p15card,"41004354", 0x47, 1, "Zertifikat 3"); @@ -466,8 +494,8 @@ static int detect_unicard( insert_key(p15card,"41005105", 0x47, 0x85, 1024, 1, "Schluessel 3"); } else if(!insert_cert(p15card,"41014352", 0x45, 1, "Zertifikat 1")){ - p15card->tokeninfo->manufacturer_id = strdup("TU Darmstadt"); - p15card->tokeninfo->label = strdup("TUD Card"); + set_string(&p15card->tokeninfo->manufacturer_id, "TU Darmstadt"); + set_string(&p15card->tokeninfo->label, "TUD Card"); insert_cert(p15card,"41014353", 0x46, 1, "Zertifikat 2"); insert_cert(p15card,"41014354", 0x47, 1, "Zertifikat 3"); @@ -490,28 +518,31 @@ static int detect_unicard( int sc_pkcs15emu_tcos_init_ex( sc_pkcs15_card_t *p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t *opts + struct sc_aid *aid ){ sc_card_t *card = p15card->card; sc_context_t *ctx = p15card->card->ctx; sc_serial_number_t serialnr; char serial[30]; - int i, r; + int r; /* check if we have the correct card OS unless SC_PKCS15EMU_FLAGS_NO_CHECK */ - i=(opts && (opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)); - if (!i && card->type!=SC_CARD_TYPE_TCOS_V2 && card->type!=SC_CARD_TYPE_TCOS_V3) return SC_ERROR_WRONG_CARD; + if (card->type!=SC_CARD_TYPE_TCOS_V2 && card->type!=SC_CARD_TYPE_TCOS_V3) return SC_ERROR_WRONG_CARD; /* get the card serial number */ r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serialnr); if (r < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "unable to get ICCSN\n"); + sc_log(ctx, "unable to get ICCSN"); return SC_ERROR_WRONG_CARD; } - sc_bin_to_hex(serialnr.value, serialnr.len , serial, sizeof(serial), 0); + r = sc_bin_to_hex(serialnr.value, serialnr.len, serial, sizeof(serial), 0); + if (r != SC_SUCCESS) { + sc_log(ctx, "serial number invalid"); + return SC_ERROR_INTERNAL; + } + serial[19] = '\0'; - p15card->tokeninfo->serial_number = strdup(serial); + set_string(&p15card->tokeninfo->serial_number, serial); if(!detect_netkey(p15card)) return SC_SUCCESS; if(!detect_idkey(p15card)) return SC_SUCCESS; @@ -519,5 +550,6 @@ int sc_pkcs15emu_tcos_init_ex( if(!detect_signtrust(p15card)) return SC_SUCCESS; if(!detect_datev(p15card)) return SC_SUCCESS; + sc_pkcs15_card_clear(p15card); return SC_ERROR_INTERNAL; } diff --git a/src/libopensc/pkcs15-westcos.c b/src/libopensc/pkcs15-westcos.c deleted file mode 100644 index 424ca0cd38..0000000000 --- a/src/libopensc/pkcs15-westcos.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * pkcs15-westcos.c: pkcs15 emulation for westcos card - * - * Copyright (C) 2009 francois.leblanc@cev-sa.com - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#include "internal.h" -#include "pkcs15.h" -#include "cardctl.h" -#include "common/compat_strlcpy.h" - -int sc_pkcs15emu_westcos_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); - -static int sc_pkcs15emu_westcos_init(sc_pkcs15_card_t * p15card) -{ - int i, r; - int modulus_length = 0; - char buf[256]; - sc_card_t *card = p15card->card; - sc_serial_number_t serial; - sc_path_t path; - sc_format_path("3F00", &path); - r = sc_select_file(card, &path, NULL); - if (r) - goto out; - if (p15card->tokeninfo->label != NULL) - free(p15card->tokeninfo->label); - p15card->tokeninfo->label = strdup("westcos"); - if (p15card->tokeninfo->manufacturer_id != NULL) - free(p15card->tokeninfo->manufacturer_id); - p15card->tokeninfo->manufacturer_id = strdup("CEV"); - - /* get serial number */ - r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); - r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0); - if (r) - goto out; - if (p15card->tokeninfo->serial_number != NULL) - free(p15card->tokeninfo->serial_number); - p15card->tokeninfo->serial_number = strdup(buf); - sc_format_path("AAAA", &path); - r = sc_select_file(card, &path, NULL); - if (r) - { - goto out; - } - else - { - for (i = 0; i <= 1; i++) { - unsigned int flags; - struct sc_pkcs15_auth_info pin_info; - struct sc_pkcs15_object pin_obj; - memset(&pin_info, 0, sizeof(pin_info)); - memset(&pin_obj, 0, sizeof(pin_obj)); - flags = SC_PKCS15_PIN_FLAG_INITIALIZED; - if (i == 1) { - flags |= - SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED | - SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN; - } - pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; - pin_info.auth_id.len = 1; - pin_info.auth_id.value[0] = i + 1; - pin_info.attrs.pin.reference = i; - pin_info.attrs.pin.flags = flags; - pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_BCD; - pin_info.attrs.pin.min_length = 4; - pin_info.attrs.pin.stored_length = 8; - pin_info.attrs.pin.max_length = 8; - pin_info.attrs.pin.pad_char = 0xff; - pin_info.path = path; - pin_info.tries_left = -1; - pin_info.logged_in = SC_PIN_STATE_UNKNOWN; - if (i == 1) - strlcpy(pin_obj.label, "Unblock", - sizeof(pin_obj.label)); - - else { - strlcpy(pin_obj.label, "User", - sizeof(pin_obj.label)); - pin_obj.auth_id.len = 1; - pin_obj.auth_id.value[0] = 2; - } - pin_obj.flags = - SC_PKCS15_CO_FLAG_MODIFIABLE | - SC_PKCS15_CO_FLAG_PRIVATE; - r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, - &pin_info); - if (r) - goto out; - } - } - - sc_format_path("0002", &path); - r = sc_select_file(card, &path, NULL); - if (r) - { - goto out; - } - else - { - /* certificate file */ - struct sc_pkcs15_cert_info cert_info; - struct sc_pkcs15_object cert_obj; - struct sc_pkcs15_pubkey_info pubkey_info; - struct sc_pkcs15_object pubkey_obj; - struct sc_pkcs15_pubkey *pkey = NULL; - memset(&cert_info, 0, sizeof(cert_info)); - memset(&cert_obj, 0, sizeof(cert_obj)); - cert_info.id.len = 1; - cert_info.id.value[0] = 0x45; - cert_info.authority = 0; - cert_info.path = path; - r = sc_pkcs15_read_certificate(p15card, &cert_info, - (sc_pkcs15_cert_t - **) (&cert_obj.data)); - if (!r) { - sc_pkcs15_cert_t *cert = - (sc_pkcs15_cert_t *) (cert_obj.data); - strlcpy(cert_obj.label, "User certificat", - sizeof(cert_obj.label)); - cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; - r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, - &cert_info); - if (r) - goto out; - pkey = cert->key; - - if (pkey->algorithm == SC_ALGORITHM_RSA) { - modulus_length = (int)(pkey->u.rsa.modulus.len * 8); - } - - } - else - { - /* or public key */ - memset(&pubkey_info, 0, sizeof(pubkey_info)); - memset(&pubkey_obj, 0, sizeof(pubkey_obj)); - pubkey_info.id.len = 1; - pubkey_info.id.value[0] = 0x45; - pubkey_info.modulus_length = modulus_length; - pubkey_info.key_reference = 1; - pubkey_info.native = 1; - pubkey_info.usage = - SC_PKCS15_PRKEY_USAGE_VERIFY | - SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER | - SC_PKCS15_PRKEY_USAGE_ENCRYPT | - SC_PKCS15_PRKEY_USAGE_WRAP; - pubkey_info.path = path; - strlcpy(pubkey_obj.label, "Public Key", - sizeof(pubkey_obj.label)); - pubkey_obj.auth_id.len = 1; - pubkey_obj.auth_id.value[0] = 1; - pubkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; - pubkey_obj.type = SC_PKCS15_TYPE_PUBKEY_RSA; - if (pkey == NULL) { - pubkey_obj.data = &pubkey_info; - r = sc_pkcs15_read_pubkey(p15card, &pubkey_obj, &pkey); - if (r) - goto out; - /* not sure if necessary */ - pubkey_obj.flags = 0; - } - if (pkey->algorithm == SC_ALGORITHM_RSA) { - modulus_length = (int)(pkey->u.rsa.modulus.len * 8); - } - pubkey_info.modulus_length = modulus_length; - pubkey_obj.data = pkey; - r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, - &pubkey_info); - if (r < 0) - goto out; - } - } - sc_format_path("0001", &path); - r = sc_select_file(card, &path, NULL); - if (r) - { - goto out; - } - else - { - struct sc_pkcs15_prkey_info prkey_info; - struct sc_pkcs15_object prkey_obj; - memset(&prkey_info, 0, sizeof(prkey_info)); - memset(&prkey_obj, 0, sizeof(prkey_obj)); - prkey_info.id.len = 1; - prkey_info.id.value[0] = 0x45; - prkey_info.usage = - SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_DECRYPT - | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; - prkey_info.native = 1; - prkey_info.key_reference = 1; - prkey_info.modulus_length = modulus_length; - prkey_info.path = path; - strlcpy(prkey_obj.label, "Private Key", - sizeof(prkey_obj.label)); - prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; - prkey_obj.auth_id.len = 1; - prkey_obj.auth_id.value[0] = 1; - r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, - &prkey_info); - if (r < 0) - goto out; - } - r = 0; -out: - return r; -} - -static int westcos_detect_card(sc_pkcs15_card_t * p15card) -{ - sc_card_t *card = p15card->card; - sc_context_t *ctx = card->ctx; - const char *name = "WESTCOS"; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "westcos_detect_card (%s)", card->name); - if (strncmp(card->name, name, strlen(name))) - return SC_ERROR_WRONG_CARD; - return SC_SUCCESS; -} - -int sc_pkcs15emu_westcos_init_ex(sc_pkcs15_card_t * p15card, - struct sc_aid *aid, - sc_pkcs15emu_opt_t * opts) -{ - int r; - sc_card_t *card = p15card->card; - sc_context_t *ctx = card->ctx; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "sc_pkcs15_init_func_ex westcos\n"); - if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) - return sc_pkcs15emu_westcos_init(p15card); - r = westcos_detect_card(p15card); - if (r) - return SC_ERROR_WRONG_CARD; - return sc_pkcs15emu_westcos_init(p15card); -} diff --git a/src/libopensc/pkcs15.c b/src/libopensc/pkcs15.c index 2ce34b6c02..2dc91843df 100644 --- a/src/libopensc/pkcs15.c +++ b/src/libopensc/pkcs15.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H @@ -33,6 +33,8 @@ #include "pkcs15.h" #include "asn1.h" #include "common/libscdl.h" +#include "common/compat_strlcat.h" +#include "common/compat_strlcpy.h" #ifdef ENABLE_OPENSSL #include @@ -42,25 +44,45 @@ #include #endif +#ifdef ENABLE_ZLIB +#include "compression.h" +#endif + static const struct sc_asn1_entry c_asn1_twlabel[] = { { "twlabel", SC_ASN1_UTF8STRING, SC_ASN1_TAG_UTF8STRING, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; +// clang-format off static const struct sc_asn1_entry c_asn1_algorithm_info[7] = { - { "reference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, - { "algorithmPKCS#11", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, - { "parameters", SC_ASN1_NULL, SC_ASN1_TAG_NULL, 0, NULL, NULL }, - { "supportedOperations",SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, + { "reference", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "algorithmPKCS#11", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "parameters", SC_ASN1_CHOICE, 0, SC_ASN1_OPTIONAL, NULL, NULL }, + { "supportedOperations",SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, { "objId", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, SC_ASN1_OPTIONAL, NULL, NULL }, { "algRef", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; +// clang-format on + +static const struct sc_asn1_entry c_asn1_algorithm_info_parameters[3] = { + { "PKCS15RSAParameters",SC_ASN1_NULL, SC_ASN1_TAG_NULL, 0, NULL, NULL }, + { "PKCS15ECParameters", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } +}; /* - * in src/libopensc/types.h SC_MAX_SUPPORTED_ALGORITHMS defined as 8 + * in src/libopensc/types.h SC_MAX_SUPPORTED_ALGORITHMS defined as 16 */ static const struct sc_asn1_entry c_asn1_supported_algorithms[SC_MAX_SUPPORTED_ALGORITHMS + 1] = { + { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, + { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "algorithmInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, @@ -116,6 +138,7 @@ static void sc_pkcs15_remove_dfs(struct sc_pkcs15_card *); static void sc_pkcs15_remove_objects(struct sc_pkcs15_card *); static int sc_pkcs15_aux_get_md_guid(struct sc_pkcs15_card *, const struct sc_pkcs15_object *, unsigned, unsigned char *, size_t *); +static void sc_pkcs15_clear_tokeninfo(struct sc_pkcs15_tokeninfo *tokeninfo); int sc_pkcs15_parse_tokeninfo(sc_context_t *ctx, sc_pkcs15_tokeninfo_t *ti, const u8 *buf, size_t blen) @@ -125,18 +148,20 @@ int sc_pkcs15_parse_tokeninfo(sc_context_t *ctx, u8 serial[128]; size_t serial_len = sizeof(serial); u8 mnfid[SC_PKCS15_MAX_LABEL_SIZE]; - size_t mnfid_len = sizeof(mnfid); + size_t mnfid_len = sizeof(mnfid) - 1; u8 label[SC_PKCS15_MAX_LABEL_SIZE]; - size_t label_len = sizeof(label); + size_t label_len = sizeof(label) - 1; u8 last_update[32], profile_indication[SC_PKCS15_MAX_LABEL_SIZE]; size_t lupdate_len = sizeof(last_update) - 1, pi_len = sizeof(profile_indication) - 1; size_t flags_len = sizeof(ti->flags); u8 preferred_language[3]; size_t lang_length = sizeof(preferred_language); struct sc_asn1_entry asn1_supported_algorithms[SC_MAX_SUPPORTED_ALGORITHMS + 1], - asn1_algo_infos[SC_MAX_SUPPORTED_ALGORITHMS][7]; + asn1_algo_infos[SC_MAX_SUPPORTED_ALGORITHMS][7], + asn1_algo_infos_parameters[SC_MAX_SUPPORTED_ALGORITHMS][3]; size_t reference_len = sizeof(ti->supported_algos[0].reference); size_t mechanism_len = sizeof(ti->supported_algos[0].mechanism); + size_t parameter_len = sizeof(ti->supported_algos[0].parameters); size_t operations_len = sizeof(ti->supported_algos[0].operations); size_t algo_ref_len = sizeof(ti->supported_algos[0].algo_ref); @@ -145,6 +170,10 @@ int sc_pkcs15_parse_tokeninfo(sc_context_t *ctx, struct sc_asn1_entry asn1_toki_attrs[C_ASN1_TOKI_ATTRS_SIZE], asn1_tokeninfo[3], asn1_twlabel[3]; memset(last_update, 0, sizeof(last_update)); + memset(label, 0, sizeof(label)); + memset(profile_indication, 0, sizeof(profile_indication)); + memset(mnfid, 0, sizeof(mnfid)); + sc_copy_asn1_entry(c_asn1_twlabel, asn1_twlabel); sc_copy_asn1_entry(c_asn1_toki_attrs, asn1_toki_attrs); sc_copy_asn1_entry(c_asn1_tokeninfo, asn1_tokeninfo); @@ -152,14 +181,22 @@ int sc_pkcs15_parse_tokeninfo(sc_context_t *ctx, sc_format_asn1_entry(asn1_twlabel, label, &label_len, 0); sc_copy_asn1_entry(c_asn1_profile_indication, asn1_profile_indication); - for (ii=0; iisupported_algos[ii].reference, &reference_len, 0); sc_format_asn1_entry(asn1_algo_infos[ii] + 1, &ti->supported_algos[ii].mechanism, &mechanism_len, 0); - sc_format_asn1_entry(asn1_algo_infos[ii] + 2, NULL, NULL, 0); + sc_format_asn1_entry(asn1_algo_infos[ii] + 2, + asn1_algo_infos_parameters[ii], NULL, 0); + sc_format_asn1_entry(asn1_algo_infos_parameters[ii] + 0, + NULL, NULL, 0); + sc_format_asn1_entry(asn1_algo_infos_parameters[ii] + 1, + &ti->supported_algos[ii].parameters, ¶meter_len, 0); sc_format_asn1_entry(asn1_algo_infos[ii] + 3, &ti->supported_algos[ii].operations, &operations_len, 0); sc_format_asn1_entry(asn1_algo_infos[ii] + 4, &ti->supported_algos[ii].algo_id, NULL, 1); sc_format_asn1_entry(asn1_algo_infos[ii] + 5, &ti->supported_algos[ii].algo_ref, &algo_ref_len, 0); @@ -189,20 +226,18 @@ int sc_pkcs15_parse_tokeninfo(sc_context_t *ctx, sc_format_asn1_entry(asn1_tokeninfo, asn1_toki_attrs, NULL, 0); r = sc_asn1_decode(ctx, asn1_tokeninfo, buf, blen, NULL, NULL); - LOG_TEST_RET(ctx, r, "ASN.1 parsing of EF(TokenInfo) failed"); + if (r != SC_SUCCESS) { + /* The decoding could have allocated something we need to free */ + sc_pkcs15_clear_tokeninfo(ti); + LOG_TEST_RET(ctx, r, "ASN.1 parsing of EF(TokenInfo) failed"); + } - if (asn1_toki_attrs[1].flags & SC_ASN1_PRESENT) { + if (asn1_toki_attrs[1].flags & SC_ASN1_PRESENT && serial_len > 0) { + free(ti->serial_number); ti->serial_number = malloc(serial_len * 2 + 1); if (ti->serial_number == NULL) return SC_ERROR_OUT_OF_MEMORY; - - ti->serial_number[0] = 0; - for (ii = 0; ii < serial_len; ii++) { - char byte[3]; - - sprintf(byte, "%02X", serial[ii]); - strcat(ti->serial_number, byte); - } + sc_bin_to_hex(serial, serial_len, ti->serial_number, serial_len * 2 + 1, 0); sc_log(ctx, "TokenInfo.serialNunmber '%s'", ti->serial_number); } @@ -270,27 +305,42 @@ sc_pkcs15_encode_tokeninfo(sc_context_t *ctx, sc_pkcs15_tokeninfo_t *ti, struct sc_asn1_entry asn1_toki_attrs[C_ASN1_TOKI_ATTRS_SIZE]; struct sc_asn1_entry asn1_tokeninfo[2]; struct sc_asn1_entry asn1_supported_algorithms[SC_MAX_SUPPORTED_ALGORITHMS + 1], - asn1_algo_infos[SC_MAX_SUPPORTED_ALGORITHMS][7]; + asn1_algo_infos[SC_MAX_SUPPORTED_ALGORITHMS][7], + asn1_algo_infos_parameters[SC_MAX_SUPPORTED_ALGORITHMS][3]; size_t reference_len = sizeof(ti->supported_algos[0].reference); size_t mechanism_len = sizeof(ti->supported_algos[0].mechanism); + size_t parameter_len = sizeof(ti->supported_algos[0].parameters); size_t operations_len = sizeof(ti->supported_algos[0].operations); size_t algo_ref_len = sizeof(ti->supported_algos[0].algo_ref); struct sc_asn1_entry asn1_last_update[C_ASN1_LAST_UPDATE_SIZE]; struct sc_asn1_entry asn1_profile_indication[C_ASN1_PROFILE_INDICATION_SIZE]; + u8 serial[128]; sc_copy_asn1_entry(c_asn1_toki_attrs, asn1_toki_attrs); sc_copy_asn1_entry(c_asn1_tokeninfo, asn1_tokeninfo); sc_copy_asn1_entry(c_asn1_last_update, asn1_last_update); sc_copy_asn1_entry(c_asn1_profile_indication, asn1_profile_indication); - for (ii=0; iisupported_algos[ii].reference; ii++) + for (ii=0; iisupported_algos[ii].reference; ii++) { sc_copy_asn1_entry(c_asn1_algorithm_info, asn1_algo_infos[ii]); + sc_copy_asn1_entry(c_asn1_algorithm_info_parameters, + asn1_algo_infos_parameters[ii]); + } sc_copy_asn1_entry(c_asn1_supported_algorithms, asn1_supported_algorithms); for (ii=0; iisupported_algos[ii].reference; ii++) { sc_format_asn1_entry(asn1_algo_infos[ii] + 0, &ti->supported_algos[ii].reference, &reference_len, 1); sc_format_asn1_entry(asn1_algo_infos[ii] + 1, &ti->supported_algos[ii].mechanism, &mechanism_len, 1); - sc_format_asn1_entry(asn1_algo_infos[ii] + 2, NULL, NULL, 0); + sc_format_asn1_entry(asn1_algo_infos[ii] + 2, + asn1_algo_infos_parameters[ii], NULL, 1); + if (!sc_valid_oid(&ti->supported_algos[ii].parameters)) { + sc_format_asn1_entry(asn1_algo_infos_parameters[ii] + 0, + NULL, NULL, 1); + } + else { + sc_format_asn1_entry(asn1_algo_infos_parameters[ii] + 1, + &ti->supported_algos[ii].parameters, ¶meter_len, 0); + } sc_format_asn1_entry(asn1_algo_infos[ii] + 3, &ti->supported_algos[ii].operations, &operations_len, 1); sc_format_asn1_entry(asn1_algo_infos[ii] + 4, &ti->supported_algos[ii].algo_id, NULL, 1); sc_format_asn1_entry(asn1_algo_infos[ii] + 5, &ti->supported_algos[ii].algo_ref, &algo_ref_len, 1); @@ -299,7 +349,6 @@ sc_pkcs15_encode_tokeninfo(sc_context_t *ctx, sc_pkcs15_tokeninfo_t *ti, sc_format_asn1_entry(asn1_toki_attrs + 0, &ti->version, NULL, 1); if (ti->serial_number != NULL) { - u8 serial[128]; serial_len = 0; if (strlen(ti->serial_number)/2 > sizeof(serial)) return SC_ERROR_BUFFER_TOO_SMALL; @@ -413,52 +462,6 @@ fix_authentic_ddo(struct sc_pkcs15_card *p15card) } } - -static void -fix_starcos_pkcs15_card(struct sc_pkcs15_card *p15card) -{ - struct sc_context *ctx = p15card->card->ctx; - - /* set special flags based on card meta data */ - if (strcmp(p15card->card->driver->short_name,"cardos") == 0 - && p15card->tokeninfo && p15card->tokeninfo->label) { - - /* D-Trust cards (D-TRUST, D-SIGN) */ - if (strstr(p15card->tokeninfo->label,"D-TRUST") != NULL - || strstr(p15card->tokeninfo->label,"D-SIGN") != NULL) { - - /* D-TRUST Card 2.0 2cc (standard cards, which always add - * SHA1 prefix itself */ - if (strstr(p15card->tokeninfo->label, "2cc") != NULL) { - p15card->card->caps |= SC_CARD_CAP_ONLY_RAW_HASH_STRIPPED; - sc_log(ctx, "D-TRUST 2cc card detected, only SHA1 works with this card"); - /* XXX: add detection when other hash than SHA1 is used with - * such a card, as this produces invalid signatures. - */ - } - - /* D-SIGN multicard 2.0 2ca (cards working with all types of hashes - * and no addition of prefix) */ - else if (strstr(p15card->tokeninfo->label, "2ca") != NULL) { - p15card->card->caps |= SC_CARD_CAP_ONLY_RAW_HASH; - sc_log(ctx, "D-TRUST 2ca card detected"); - } - - /* D-TRUST card 2.4 2ce (cards working with all types of hashes - * and no addition of prefix) */ - else if (strstr(p15card->tokeninfo->label, "2ce") != NULL) { - p15card->card->caps |= SC_CARD_CAP_ONLY_RAW_HASH; - sc_log(ctx, "D-TRUST 2ce card detected"); - } - - /* XXX: probably there are more D-Trust card in the wild, - * which also need these flags to produce valid signatures - */ - } - } -} - - static int parse_ddo(struct sc_pkcs15_card *p15card, const u8 * buf, size_t buflen) { @@ -485,18 +488,21 @@ parse_ddo(struct sc_pkcs15_card *p15card, const u8 * buf, size_t buflen) LOG_TEST_RET(ctx, r, "DDO parsing failed"); if (asn1_ddo[1].flags & SC_ASN1_PRESENT) { + sc_file_free(p15card->file_odf); p15card->file_odf = sc_file_new(); if (p15card->file_odf == NULL) goto mem_err; p15card->file_odf->path = odf_path; } if (asn1_ddo[2].flags & SC_ASN1_PRESENT) { + sc_file_free(p15card->file_tokeninfo); p15card->file_tokeninfo = sc_file_new(); if (p15card->file_tokeninfo == NULL) goto mem_err; p15card->file_tokeninfo->path = ti_path; } if (asn1_ddo[3].flags & SC_ASN1_PRESENT) { + sc_file_free(p15card->file_unusedspace); p15card->file_unusedspace = sc_file_new(); if (p15card->file_unusedspace == NULL) goto mem_err; @@ -530,7 +536,7 @@ sc_pkcs15_get_lastupdate(struct sc_pkcs15_card *p15card) struct sc_context *ctx = p15card->card->ctx; struct sc_file *file = NULL; struct sc_asn1_entry asn1_last_update[C_ASN1_LAST_UPDATE_SIZE]; - unsigned char *content, last_update[32]; + unsigned char *content, last_update[32] = {0}; size_t lupdate_len = sizeof(last_update) - 1; int r, content_len; size_t size; @@ -546,18 +552,19 @@ sc_pkcs15_get_lastupdate(struct sc_pkcs15_card *p15card) return NULL; size = file->size ? file->size : 1024; + sc_file_free(file); - content = calloc(size, 1); + content = calloc(1, size); if (!content) return NULL; r = sc_read_binary(p15card->card, 0, content, size, 0); - if (r < 0) + if (r < 0) { + free(content); return NULL; + } content_len = r; - sc_file_free(file); - sc_copy_asn1_entry(c_asn1_last_update, asn1_last_update); sc_format_asn1_entry(asn1_last_update + 0, last_update, &lupdate_len, 0); @@ -566,9 +573,11 @@ sc_pkcs15_get_lastupdate(struct sc_pkcs15_card *p15card) if (r < 0) return NULL; - p15card->tokeninfo->last_update.gtime = strdup((char *)last_update); - if (!p15card->tokeninfo->last_update.gtime) - return NULL; + if (asn1_last_update[0].flags & SC_ASN1_PRESENT) { + p15card->tokeninfo->last_update.gtime = strdup((char *)last_update); + if (!p15card->tokeninfo->last_update.gtime) + return NULL; + } done: sc_log(ctx, "lastUpdate.gtime '%s'", p15card->tokeninfo->last_update.gtime); return p15card->tokeninfo->last_update.gtime; @@ -624,12 +633,14 @@ parse_odf(const unsigned char * buf, size_t buflen, struct sc_pkcs15_card *p15ca if (r < 0) return r; type = r; - r = sc_pkcs15_make_absolute_path(&p15card->file_app->path, &path); - if (r < 0) - return r; - r = sc_pkcs15_add_df(p15card, odf_indexes[type], &path); - if (r) - return r; + if (p15card->file_app) { + r = sc_pkcs15_make_absolute_path(&p15card->file_app->path, &path); + if (r < 0) + return r; + r = sc_pkcs15_add_df(p15card, odf_indexes[type], &path); + if (r) + return r; + } } return 0; } @@ -712,55 +723,72 @@ sc_pkcs15_card_new(void) return NULL; } - sc_init_oid(&p15card->tokeninfo->profile_indication.oid); - p15card->magic = SC_PKCS15_CARD_MAGIC; return p15card; } -void -sc_pkcs15_free_tokeninfo(struct sc_pkcs15_card *p15card) +struct sc_pkcs15_tokeninfo * +sc_pkcs15_tokeninfo_new(void) { - if (!p15card || !p15card->tokeninfo) - return; + struct sc_pkcs15_tokeninfo *tokeninfo; - if (p15card->tokeninfo->label != NULL) - free(p15card->tokeninfo->label); - if (p15card->tokeninfo->serial_number != NULL) - free(p15card->tokeninfo->serial_number); - if (p15card->tokeninfo->manufacturer_id != NULL) - free(p15card->tokeninfo->manufacturer_id); - if (p15card->tokeninfo->last_update.gtime != NULL) - free(p15card->tokeninfo->last_update.gtime); - if (p15card->tokeninfo->preferred_language != NULL) - free(p15card->tokeninfo->preferred_language); - if (p15card->tokeninfo->profile_indication.name != NULL) - free(p15card->tokeninfo->profile_indication.name); - if (p15card->tokeninfo->seInfo != NULL) { - unsigned i; - for (i = 0; i < p15card->tokeninfo->num_seInfo; i++) - free(p15card->tokeninfo->seInfo[i]); - free(p15card->tokeninfo->seInfo); + tokeninfo = calloc(1, sizeof(struct sc_pkcs15_tokeninfo)); + if (tokeninfo == NULL) { + return NULL; } - free(p15card->tokeninfo); - p15card->tokeninfo = NULL; + sc_init_oid(&tokeninfo->profile_indication.oid); + + return tokeninfo; } +static void +sc_pkcs15_clear_tokeninfo(struct sc_pkcs15_tokeninfo *tokeninfo) +{ + if (!tokeninfo) + return; + + free(tokeninfo->label); + tokeninfo->label = NULL; + free(tokeninfo->serial_number); + tokeninfo->serial_number = NULL; + free(tokeninfo->manufacturer_id); + tokeninfo->manufacturer_id = NULL; + free(tokeninfo->last_update.gtime); + tokeninfo->last_update.gtime = NULL; + free(tokeninfo->preferred_language); + tokeninfo->preferred_language = NULL; + free(tokeninfo->profile_indication.name); + tokeninfo->profile_indication.name = NULL; + if (tokeninfo->seInfo != NULL) { + unsigned i; + for (i = 0; i < tokeninfo->num_seInfo; i++) + free(tokeninfo->seInfo[i]); + free(tokeninfo->seInfo); + tokeninfo->seInfo = NULL; + } +} void -sc_pkcs15_free_app(struct sc_pkcs15_card *p15card) +sc_pkcs15_free_tokeninfo(struct sc_pkcs15_tokeninfo *tokeninfo) { - if (!p15card || !p15card->app) + if (!tokeninfo) return; - if (p15card->app->label) + sc_pkcs15_clear_tokeninfo(tokeninfo); + free(tokeninfo); +} + +void +sc_pkcs15_free_app(struct sc_pkcs15_card *p15card) +{ + if (p15card && p15card->app) { free(p15card->app->label); - if (p15card->app->ddo.value) free(p15card->app->ddo.value); - free(p15card->app); - p15card->app = NULL; + free(p15card->app); + p15card->app = NULL; + } } @@ -778,6 +806,7 @@ sc_pkcs15_card_free(struct sc_pkcs15_card *p15card) if (p15card->md_data) free(p15card->md_data); + sc_pkcs15_free_app(p15card); sc_pkcs15_remove_objects(p15card); sc_pkcs15_remove_dfs(p15card); sc_pkcs15_free_unusedspace(p15card); @@ -789,7 +818,7 @@ sc_pkcs15_card_free(struct sc_pkcs15_card *p15card) sc_file_free(p15card->file_unusedspace); p15card->magic = 0; - sc_pkcs15_free_tokeninfo(p15card); + sc_pkcs15_free_tokeninfo(p15card->tokeninfo); sc_pkcs15_free_app(p15card); free(p15card); } @@ -820,30 +849,19 @@ sc_pkcs15_card_clear(struct sc_pkcs15_card *p15card) p15card->file_odf = NULL; sc_file_free(p15card->file_unusedspace); p15card->file_unusedspace = NULL; - if (p15card->tokeninfo->label != NULL) { - free(p15card->tokeninfo->label); - p15card->tokeninfo->label = NULL; - } - if (p15card->tokeninfo->serial_number != NULL) { - free(p15card->tokeninfo->serial_number); - p15card->tokeninfo->serial_number = NULL; - } - if (p15card->tokeninfo->manufacturer_id != NULL) { - free(p15card->tokeninfo->manufacturer_id); - p15card->tokeninfo->manufacturer_id = NULL; - } - if (p15card->tokeninfo->last_update.gtime != NULL) { - free(p15card->tokeninfo->last_update.gtime); - p15card->tokeninfo->last_update.gtime = NULL; - } - if (p15card->tokeninfo->preferred_language != NULL) { - free(p15card->tokeninfo->preferred_language); - p15card->tokeninfo->preferred_language = NULL; - } - if (p15card->tokeninfo->profile_indication.name != NULL) { - free(p15card->tokeninfo->profile_indication.name); - p15card->tokeninfo->profile_indication.name = NULL; - } + + free(p15card->tokeninfo->label); + p15card->tokeninfo->label = NULL; + free(p15card->tokeninfo->serial_number); + p15card->tokeninfo->serial_number = NULL; + free(p15card->tokeninfo->manufacturer_id); + p15card->tokeninfo->manufacturer_id = NULL; + free(p15card->tokeninfo->last_update.gtime); + p15card->tokeninfo->last_update.gtime = NULL; + free(p15card->tokeninfo->preferred_language); + p15card->tokeninfo->preferred_language = NULL; + free(p15card->tokeninfo->profile_indication.name); + p15card->tokeninfo->profile_indication.name = NULL; if (p15card->tokeninfo->seInfo != NULL) { size_t i; for (i = 0; i < p15card->tokeninfo->num_seInfo; i++) @@ -852,6 +870,8 @@ sc_pkcs15_card_clear(struct sc_pkcs15_card *p15card) p15card->tokeninfo->seInfo = NULL; p15card->tokeninfo->num_seInfo = 0; } + + sc_pkcs15_free_app(p15card); } @@ -903,6 +923,7 @@ sc_dup_app_info(const struct sc_app_info *info) return NULL; } memcpy(out->ddo.value, info->ddo.value, info->ddo.len); + out->ddo.len = info->ddo.len; return out; } @@ -972,6 +993,7 @@ sc_pkcs15_bind_internal(struct sc_pkcs15_card *p15card, struct sc_aid *aid) if (err != SC_SUCCESS) sc_log(ctx, "unable to enumerate apps: %s", sc_strerror(err)); } + sc_file_free(p15card->file_app); p15card->file_app = sc_file_new(); if (p15card->file_app == NULL) { err = SC_ERROR_OUT_OF_MEMORY; @@ -983,6 +1005,7 @@ sc_pkcs15_bind_internal(struct sc_pkcs15_card *p15card, struct sc_aid *aid) info = sc_find_app(card, aid); if (info) { sc_log(ctx, "bind to application('%s',aid:'%s')", info->label, sc_dump_hex(info->aid.value, info->aid.len)); + sc_pkcs15_free_app(p15card); p15card->app = sc_dup_app_info(info); if (!p15card->app) { err = SC_ERROR_OUT_OF_MEMORY; @@ -1047,27 +1070,35 @@ sc_pkcs15_bind_internal(struct sc_pkcs15_card *p15card, struct sc_aid *aid) sc_log(ctx, "EF(ODF) is empty"); goto end; } + if (len > MAX_FILE_SIZE) { + sc_log(ctx, "EF(ODF) too large"); + goto end; + } buf = malloc(len); - if(buf == NULL) - LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + if(buf == NULL) { + err = SC_ERROR_OUT_OF_MEMORY; + goto end; + } err = -1; /* file state: not in cache */ if (p15card->opts.use_file_cache) { err = sc_pkcs15_read_cached_file(p15card, &tmppath, &buf, &len); if (err == SC_SUCCESS) - err = len; + err = (int)len; } if (err < 0) { err = sc_read_binary(card, 0, buf, len, 0); - if (err < 0) { - sc_log(ctx, "read EF(ODF) file error: %s", sc_strerror(err)); - goto end; - } - else if (err < 2) { - err = SC_ERROR_PKCS15_APP_NOT_FOUND; - sc_log(ctx, "Invalid content of EF(ODF): %s", sc_strerror(err)); + if (err < 2) { + if (err < 0) { + sc_log(ctx, "read EF(ODF) file error: %s", sc_strerror(err)); + } else { + err = SC_ERROR_PKCS15_APP_NOT_FOUND; + sc_log(ctx, "Invalid content of EF(ODF): %s", sc_strerror(err)); + } goto end; } + /* sc_read_binary may return less than requested */ + len = err; if (p15card->opts.use_file_cache) { sc_pkcs15_cache_file(p15card, &tmppath, buf, len); @@ -1113,32 +1144,39 @@ sc_pkcs15_bind_internal(struct sc_pkcs15_card *p15card, struct sc_aid *aid) sc_log(ctx, "EF(TokenInfo) is empty"); goto end; } + if (len > MAX_FILE_SIZE) { + sc_log(ctx, "EF(TokenInfo) too large"); + goto end; + } buf = malloc(len); - if(buf == NULL) - LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + if(buf == NULL) { + err = SC_ERROR_OUT_OF_MEMORY; + goto end; + } err = -1; /* file state: not in cache */ if (p15card->opts.use_file_cache) { err = sc_pkcs15_read_cached_file(p15card, &tmppath, &buf, &len); if (err == SC_SUCCESS) - err = len; + err = (int)len; } if (err < 0) { err = sc_read_binary(card, 0, buf, len, 0); - if (err < 0) { - sc_log(ctx, "read EF(TokenInfo) file error: %s", sc_strerror(err)); - goto end; - } if (err <= 2) { - err = SC_ERROR_PKCS15_APP_NOT_FOUND; - sc_log(ctx, "Invalid content of EF(TokenInfo): %s", sc_strerror(err)); + if (err < 0) { + sc_log(ctx, "read EF(TokenInfo) file error: %s", sc_strerror(err)); + } else { + err = SC_ERROR_PKCS15_APP_NOT_FOUND; + sc_log(ctx, "Invalid content of EF(TokenInfo): %s", sc_strerror(err)); + } goto end; } + /* sc_read_binary may return less than requested */ + len = err; if (p15card->opts.use_file_cache) { sc_pkcs15_cache_file(p15card, &tmppath, buf, len); } - err = len; } memset(&tokeninfo, 0, sizeof(tokeninfo)); @@ -1148,8 +1186,13 @@ sc_pkcs15_bind_internal(struct sc_pkcs15_card *p15card, struct sc_aid *aid) goto end; } + sc_pkcs15_clear_tokeninfo(p15card->tokeninfo); *(p15card->tokeninfo) = tokeninfo; + if (!p15card->tokeninfo->serial_number && 0 == card->serialnr.len) { + sc_card_ctl(p15card->card, SC_CARDCTL_GET_SERIALNR, &card->serialnr); + } + if (!p15card->tokeninfo->serial_number && card->serialnr.len) { char *serial = calloc(1, card->serialnr.len*2 + 1); size_t ii; @@ -1180,43 +1223,118 @@ sc_pkcs15_bind_internal(struct sc_pkcs15_card *p15card, struct sc_aid *aid) } +const char *pkcs15_get_default_use_file_cache(struct sc_card *card) +{ + /* enable file caching by default for cards with static content to avoid + * synchronization problems. + * + * The following list was initialized with the cards that can't be modified + * with OpenSC i.e. which don't have a profile/driver for pkcs15-init. */ + const char *card_drivers_with_file_cache[] = { + "atrust-acos", + "belpic", + "cac1", + "cac", + "coolkey", + "edo", + "esteid2018", + "esteid2025", + "flex", + "cyberflex", + "gemsafeV1", + "idprime", + "itacns", + "jpki", + "MaskTech", + "mcrd", + "myeid", + "npa", + "nqapplet", + "tcos", + "dtrust", + "lteid", + }; + + if (NULL == card || NULL == card->driver || NULL == card->driver->short_name) + return "no"; + for (size_t i = 0; i < (sizeof card_drivers_with_file_cache / sizeof *card_drivers_with_file_cache); i++) { + if (0 == strcmp(card->driver->short_name, card_drivers_with_file_cache[i])) + return "public"; + } + + return "no"; +} + int sc_pkcs15_bind(struct sc_card *card, struct sc_aid *aid, struct sc_pkcs15_card **p15card_out) { struct sc_pkcs15_card *p15card = NULL; - struct sc_context *ctx = card->ctx; + struct sc_context *ctx; scconf_block *conf_block = NULL; int r, emu_first, enable_emu; + const char *use_file_cache; + const char *pin_protected_certificate, *private_certificate; + + if (card == NULL || p15card_out == NULL) { + return SC_ERROR_INVALID_ARGUMENTS; + } + ctx = card->ctx; LOG_FUNC_CALLED(ctx); sc_log(ctx, "application(aid:'%s')", aid ? sc_dump_hex(aid->value, aid->len) : "empty"); - if (p15card_out == NULL) { - return SC_ERROR_INVALID_ARGUMENTS; - } p15card = sc_pkcs15_card_new(); if (p15card == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); p15card->card = card; - p15card->opts.use_file_cache = 0; + p15card->opts.use_file_cache = SC_PKCS15_OPTS_CACHE_NO_FILES; + use_file_cache = pkcs15_get_default_use_file_cache(card); p15card->opts.use_pin_cache = 1; p15card->opts.pin_cache_counter = 10; p15card->opts.pin_cache_ignore_user_consent = 0; + pin_protected_certificate = "protect"; + private_certificate = ""; conf_block = sc_get_conf_block(ctx, "framework", "pkcs15", 1); - if (conf_block) { - p15card->opts.use_file_cache = scconf_get_bool(conf_block, "use_file_caching", p15card->opts.use_file_cache); + use_file_cache = scconf_get_str(conf_block, "use_file_caching", use_file_cache); p15card->opts.use_pin_cache = scconf_get_bool(conf_block, "use_pin_caching", p15card->opts.use_pin_cache); p15card->opts.pin_cache_counter = scconf_get_int(conf_block, "pin_cache_counter", p15card->opts.pin_cache_counter); - p15card->opts.pin_cache_ignore_user_consent = scconf_get_bool(conf_block, "pin_cache_ignore_user_consent", + p15card->opts.pin_cache_ignore_user_consent = scconf_get_bool(conf_block, "pin_cache_ignore_user_consent", p15card->opts.pin_cache_ignore_user_consent); - } - sc_log(ctx, "PKCS#15 options: use_file_cache=%d use_pin_cache=%d pin_cache_counter=%d pin_cache_ignore_user_consent=%d", - p15card->opts.use_file_cache, p15card->opts.use_pin_cache,p15card->opts.pin_cache_counter, - p15card->opts.pin_cache_ignore_user_consent); + pin_protected_certificate = scconf_get_str(conf_block, "pin_protected_certificate", pin_protected_certificate); + /* read also the old value to keep backward compatibility */ + private_certificate = scconf_get_str(conf_block, "private_certificate", private_certificate); + } + + if (0 == strcmp(use_file_cache, "yes")) { + p15card->opts.use_file_cache = SC_PKCS15_OPTS_CACHE_ALL_FILES; + } else if (0 == strcmp(use_file_cache, "public")) { + p15card->opts.use_file_cache = SC_PKCS15_OPTS_CACHE_PUBLIC_FILES; + } else if (0 == strcmp(use_file_cache, "no")) { + p15card->opts.use_file_cache = SC_PKCS15_OPTS_CACHE_NO_FILES; + } + + if (0 == strcmp(pin_protected_certificate, "protect")) { + p15card->opts.pin_protected_certificate = SC_PKCS15_CARD_OPTS_PRIV_CERT_PROTECT; + } else if (0 == strcmp(pin_protected_certificate, "ignore")) { + p15card->opts.pin_protected_certificate = SC_PKCS15_CARD_OPTS_PRIV_CERT_IGNORE; + } else if (0 == strcmp(pin_protected_certificate, "declassify")) { + p15card->opts.pin_protected_certificate = SC_PKCS15_CARD_OPTS_PRIV_CERT_DECLASSIFY; + } + /* overwrite pin_protected_certificate when private_certificate set */ + if (0 == strcmp(private_certificate, "protect")) { + p15card->opts.pin_protected_certificate = SC_PKCS15_CARD_OPTS_PRIV_CERT_PROTECT; + } else if (0 == strcmp(private_certificate, "ignore")) { + p15card->opts.pin_protected_certificate = SC_PKCS15_CARD_OPTS_PRIV_CERT_IGNORE; + } else if (0 == strcmp(private_certificate, "declassify")) { + p15card->opts.pin_protected_certificate = SC_PKCS15_CARD_OPTS_PRIV_CERT_DECLASSIFY; + } + sc_log(ctx, "PKCS#15 options: use_file_cache=%d use_pin_cache=%d pin_cache_counter=%d pin_cache_ignore_user_consent=%d pin_protected_certificate=%d", + p15card->opts.use_file_cache, p15card->opts.use_pin_cache, p15card->opts.pin_cache_counter, + p15card->opts.pin_cache_ignore_user_consent, p15card->opts.pin_protected_certificate); r = sc_lock(card); if (r) { @@ -1251,8 +1369,6 @@ sc_pkcs15_bind(struct sc_card *card, struct sc_aid *aid, goto error; } done: - fix_starcos_pkcs15_card(p15card); - *p15card_out = p15card; sc_unlock(card); LOG_FUNC_RETURN(ctx, SC_SUCCESS); @@ -1295,12 +1411,13 @@ __sc_pkcs15_search_objects(struct sc_pkcs15_card *p15card, unsigned int class_ma /* Make sure the class mask we have makes sense */ if (class_mask == 0 - || (class_mask & ~(SC_PKCS15_SEARCH_CLASS_PRKEY | - SC_PKCS15_SEARCH_CLASS_PUBKEY | - SC_PKCS15_SEARCH_CLASS_SKEY | - SC_PKCS15_SEARCH_CLASS_CERT | - SC_PKCS15_SEARCH_CLASS_DATA | - SC_PKCS15_SEARCH_CLASS_AUTH))) { + || (class_mask & ~( + SC_PKCS15_SEARCH_CLASS_PRKEY | + SC_PKCS15_SEARCH_CLASS_PUBKEY | + SC_PKCS15_SEARCH_CLASS_SKEY | + SC_PKCS15_SEARCH_CLASS_CERT | + SC_PKCS15_SEARCH_CLASS_DATA | + SC_PKCS15_SEARCH_CLASS_AUTH))) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_INVALID_ARGUMENTS); } @@ -1356,7 +1473,7 @@ __sc_pkcs15_search_objects(struct sc_pkcs15_card *p15card, unsigned int class_ma break; } - return match_count; + return (int)match_count; } @@ -1869,11 +1986,19 @@ sc_pkcs15_free_object(struct sc_pkcs15_object *obj) sc_pkcs15_free_prkey_info((sc_pkcs15_prkey_info_t *)obj->data); break; case SC_PKCS15_TYPE_PUBKEY: + /* This is normally passed to framework-pkcs15, + * but if something fails on the way, it would not get freed */ + if (obj->emulated) { + sc_pkcs15_free_pubkey(obj->emulated); + } sc_pkcs15_free_pubkey_info((sc_pkcs15_pubkey_info_t *)obj->data); break; case SC_PKCS15_TYPE_CERT: sc_pkcs15_free_cert_info((sc_pkcs15_cert_info_t *)obj->data); break; + case SC_PKCS15_TYPE_SKEY: + sc_pkcs15_free_skey_info((sc_pkcs15_skey_info_t *)obj->data); + break; case SC_PKCS15_TYPE_DATA_OBJECT: sc_pkcs15_free_data_info((sc_pkcs15_data_info_t *)obj->data); break; @@ -1985,6 +2110,8 @@ sc_pkcs15_encode_df(struct sc_context *ctx, struct sc_pkcs15_card *p15card, stru free(buf); return r; } + if (!tmpsize) + continue; p = (u8 *) realloc(buf, bufsize + tmpsize); if (!p) { free(tmp); @@ -1994,6 +2121,7 @@ sc_pkcs15_encode_df(struct sc_context *ctx, struct sc_pkcs15_card *p15card, stru buf = p; memcpy(buf + bufsize, tmp, tmpsize); free(tmp); + tmp = NULL; bufsize += tmpsize; } *buf_out = buf; @@ -2046,7 +2174,7 @@ sc_pkcs15_parse_df(struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df) sc_log(ctx, "unknown DF type: %d", df->type); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } - r = sc_pkcs15_read_file(p15card, &df->path, &buf, &bufsize); + r = sc_pkcs15_read_file(p15card, &df->path, &buf, &bufsize, 0); LOG_TEST_RET(ctx, r, "pkcs15 read file failed"); p = buf; @@ -2077,6 +2205,10 @@ sc_pkcs15_parse_df(struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df) sc_log(ctx, "%s: Error adding object", sc_strerror(r)); goto ret; } + while (bufsize > 0 && *p == 00) { + bufsize--; + p++; + } }; if (r > 0) @@ -2240,7 +2372,7 @@ sc_pkcs15_parse_unusedspace(const unsigned char *buf, size_t buflen, struct sc_p const unsigned char *p = buf; size_t left = buflen; int r; - struct sc_path path, dummy_path; + struct sc_path path; struct sc_pkcs15_id auth_id; struct sc_asn1_entry asn1_unusedspace[] = { { "UnusedSpace", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, @@ -2255,9 +2387,6 @@ sc_pkcs15_parse_unusedspace(const unsigned char *buf, size_t buflen, struct sc_p /* Clean the list if already present */ sc_pkcs15_free_unusedspace(p15card); - sc_format_path("3F00", &dummy_path); - dummy_path.index = dummy_path.count = 0; - sc_format_asn1_entry(asn1_unusedspace, asn1_unusedspace_values, NULL, 1); sc_format_asn1_entry(asn1_unusedspace_values, &path, NULL, 1); sc_format_asn1_entry(asn1_unusedspace_values+1, &auth_id, NULL, 0); @@ -2272,7 +2401,7 @@ sc_pkcs15_parse_unusedspace(const unsigned char *buf, size_t buflen, struct sc_p /* If the path length is 0, it's a dummy path then don't add it. * If the path length isn't included (-1) then it's against the standard * but we'll just ignore it instead of returning an error. */ - if (path.count > 0) { + if (path.count > 0 && p15card->file_app) { r = sc_pkcs15_make_absolute_path(&p15card->file_app->path, &path); if (r < 0) return r; @@ -2287,10 +2416,59 @@ sc_pkcs15_parse_unusedspace(const unsigned char *buf, size_t buflen, struct sc_p return 0; } +static int decompress_file(sc_card_t *card, unsigned char *buf, size_t buflen, + unsigned char **out, size_t *outlen, unsigned long flags) +{ + LOG_FUNC_CALLED(card->ctx); +#ifdef ENABLE_ZLIB + int rv = SC_SUCCESS; + int method = 0; + + if (flags & SC_FILE_FLAG_COMPRESSED_GZIP) { + method = COMPRESSION_GZIP; + } else if (flags & SC_FILE_FLAG_COMPRESSED_ZLIB) { + method = COMPRESSION_ZLIB; + } else { + method = COMPRESSION_AUTO; + } + + rv = sc_decompress_alloc(out, outlen, buf, buflen, method); + if (rv != SC_SUCCESS) { + sc_log(card->ctx, "Decompression failed: %d", rv); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); + } + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +#else + sc_log(card->ctx, "Compression not supported, no zlib"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); +#endif +} + +int +sc_decode_do53(sc_context_t *ctx, u8 **data, size_t *data_len, + const u8 *buf, size_t buflen) +{ + struct sc_asn1_entry c_asn1_do53[] = { + { "do53", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x13, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + struct sc_asn1_entry asn1_do53[2]; + int r; + + LOG_FUNC_CALLED(ctx); + sc_copy_asn1_entry(c_asn1_do53, asn1_do53); + sc_format_asn1_entry(asn1_do53, data, data_len, 0); + + r = sc_asn1_decode(ctx, asn1_do53, buf, buflen, NULL, NULL); + LOG_TEST_RET(ctx, r, "ASN.1 parsing of do-53 failed"); + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + int sc_pkcs15_read_file(struct sc_pkcs15_card *p15card, const struct sc_path *in_path, - unsigned char **buf, size_t *buflen) + unsigned char **buf, size_t *buflen, int private_data) { struct sc_context *ctx; struct sc_file *file = NULL; @@ -2307,7 +2485,8 @@ sc_pkcs15_read_file(struct sc_pkcs15_card *p15card, const struct sc_path *in_pat sc_log(ctx, "path=%s, index=%u, count=%d", sc_print_path(in_path), in_path->index, in_path->count); r = -1; /* file state: not in cache */ - if (p15card->opts.use_file_cache) { + if (p15card->opts.use_file_cache + && ((p15card->opts.use_file_cache & SC_PKCS15_OPTS_CACHE_ALL_FILES) || !private_data)) { r = sc_pkcs15_read_cached_file(p15card, in_path, &data, &len); if (!r && in_path->aid.len > 0 && in_path->len >= 2) { @@ -2321,29 +2500,58 @@ sc_pkcs15_read_file(struct sc_pkcs15_card *p15card, const struct sc_path *in_pat if (r) { r = sc_lock(p15card->card); - LOG_TEST_RET(ctx, r, "sc_lock() failed"); + if (r) + goto fail; r = sc_select_file(p15card->card, in_path, &file); if (r) goto fail_unlock; /* Handle the case where the ASN.1 Path object specified * index and length values */ - if (in_path->count < 0) { - if (file->size) - len = file->size; - else - len = 1024; - offset = 0; - } - else { - offset = in_path->index; - len = in_path->count; - /* Make sure we're within proper bounds */ - if (offset >= file->size || offset + len > file->size) { - r = SC_ERROR_INVALID_ASN1_OBJECT; + + if (file->ef_structure == SC_FILE_EF_LINEAR_VARIABLE) { + + // in_path->index: record_no + // in_path->count: ignored! + + if(file->record_length > 0) { + if(file->record_length > MAX_FILE_SIZE) { + len = MAX_FILE_SIZE; + sc_log(ctx, " record size truncated, encoded length: %"SC_FORMAT_LEN_SIZE_T"u", file->record_length); + } else { + len = file->record_length; + } + } else { + len = MAX_FILE_SIZE; + } + + if ((in_path->index <= 0) || (in_path->index > (int)(file->record_count))) { + sc_log(ctx, " record number out of bounds: %d", in_path->index); + r = SC_ERROR_RECORD_NOT_FOUND; goto fail_unlock; } + + } else { + + if (in_path->count < 0) { + if (file->size) + len = (file->size > MAX_FILE_SIZE)? MAX_FILE_SIZE:file->size; + else + len = 1024; + offset = 0; + } + else { + offset = in_path->index; + len = in_path->count; + /* Make sure we're within proper bounds */ + if (offset >= file->size || offset + len > file->size) { + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto fail_unlock; + } + } } + + free(data); data = malloc(len); if (data == NULL) { r = SC_ERROR_OUT_OF_MEMORY; @@ -2351,14 +2559,16 @@ sc_pkcs15_read_file(struct sc_pkcs15_card *p15card, const struct sc_path *in_pat } if (file->ef_structure == SC_FILE_EF_LINEAR_VARIABLE_TLV) { - int i; + unsigned int i; size_t l, record_len; unsigned char *head = data; - for (i=1; ; i++) { + for (i=1; ; i++) { l = len - (head - data); - if (l > 256) { l = 256; } - r = sc_read_record(p15card->card, i, head, l, SC_RECORD_BY_REC_NR); + if (l > 256) { + l = 256; + } + r = sc_read_record(p15card->card, i, 0, head, l, SC_RECORD_BY_REC_NR); if (r == SC_ERROR_RECORD_NOT_FOUND) break; if (r < 0) { @@ -2368,42 +2578,71 @@ sc_pkcs15_read_file(struct sc_pkcs15_card *p15card, const struct sc_path *in_pat break; record_len = head[1]; if (record_len != 0xff) { - memmove(head,head+2,r-2); + memmove(head, head+2, r-2); head += (r-2); } else { if (r < 4) break; - memmove(head,head+4,r-4); + memmove(head, head+4, r-4); head += (r-4); } } len = head-data; } + else if (file->ef_structure == SC_FILE_EF_LINEAR_VARIABLE) { + r = sc_read_record(p15card->card, in_path->index, (unsigned)offset, data, len, SC_RECORD_BY_REC_NR); + if (r < 0) { + goto fail_unlock; + } + /* sc_read_record may return less than requested */ + len = r; + } else { - r = sc_read_binary(p15card->card, offset, data, len, 0); + unsigned long flags = 0; + r = sc_read_binary(p15card->card, (unsigned)offset, data, len, &flags); if (r < 0) { goto fail_unlock; } /* sc_read_binary may return less than requested */ len = r; + + if (flags & SC_FILE_FLAG_COMPRESSED_AUTO + || flags & SC_FILE_FLAG_COMPRESSED_ZLIB + || flags & SC_FILE_FLAG_COMPRESSED_GZIP) { + unsigned char *decompressed_buf = NULL; + size_t decompressed_len = 0; + r = decompress_file(p15card->card, data, len, &decompressed_buf, &decompressed_len, flags); + if (r != SC_SUCCESS) { + goto fail_unlock; + } + free(data); + data = decompressed_buf; + len = decompressed_len; + } } sc_unlock(p15card->card); sc_file_free(file); - if (p15card->opts.use_file_cache) { + if (len && p15card->opts.use_file_cache + && ((p15card->opts.use_file_cache & SC_PKCS15_OPTS_CACHE_ALL_FILES) || !private_data)) { sc_pkcs15_cache_file(p15card, in_path, data, len); } + if (len == 0) { + free(data); + data = NULL; + } } *buf = data; *buflen = len; LOG_FUNC_RETURN(ctx, SC_SUCCESS); fail_unlock: + sc_unlock(p15card->card); +fail: free(data); sc_file_free(file); - sc_unlock(p15card->card); LOG_FUNC_RETURN(ctx, r); } @@ -2499,9 +2738,12 @@ sc_pkcs15_make_absolute_path(const struct sc_path *parent, struct sc_path *child void sc_pkcs15_free_object_content(struct sc_pkcs15_object *obj) { - if (obj->content.value && obj->content.len) { - sc_mem_clear(obj->content.value, obj->content.len); - free(obj->content.value); + if (obj->content.value && obj->content.len) { + if (obj->content_free) { + obj->content_free(obj->content.value, obj->content.len); + } else { + free(obj->content.value); + } } obj->content.value = NULL; obj->content.len = 0; @@ -2525,7 +2767,14 @@ sc_pkcs15_allocate_object_content(struct sc_context *ctx, struct sc_pkcs15_objec /* Need to pass by temporary variable, * because 'value' and 'content.value' pointers can be the sames. */ - tmp_buf = (unsigned char *)sc_mem_alloc_secure(ctx, len); + if (SC_PKCS15_TYPE_AUTH & obj->type + || SC_PKCS15_TYPE_SKEY & obj->type + || SC_PKCS15_TYPE_PRKEY & obj->type) { + tmp_buf = sc_mem_secure_alloc(len); + obj->content_free = sc_mem_secure_free; + } else { + tmp_buf = malloc(len); + } if (!tmp_buf) return SC_ERROR_OUT_OF_MEMORY; @@ -2561,13 +2810,38 @@ sc_pkcs15_get_supported_algo(struct sc_pkcs15_card *p15card, unsigned operation, return info; } +struct sc_supported_algo_info * +sc_pkcs15_get_specific_supported_algo(struct sc_pkcs15_card *p15card, unsigned operation, unsigned mechanism, const struct sc_object_id *algo_oid) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_supported_algo_info *info = NULL; + int ii; + + if (algo_oid == NULL) + return NULL; + + for (ii=0;iitokeninfo->supported_algos[ii].reference; ii++) + if ((p15card->tokeninfo->supported_algos[ii].operations & operation) + && (p15card->tokeninfo->supported_algos[ii].mechanism == mechanism) + && sc_compare_oid(algo_oid, &p15card->tokeninfo->supported_algos[ii].algo_id) == 1) + break; + + if (ii < SC_MAX_SUPPORTED_ALGORITHMS && p15card->tokeninfo->supported_algos[ii].reference) { + info = &p15card->tokeninfo->supported_algos[ii]; + sc_log(ctx, "found supported algorithm (ref:%X,mech:%X,ops:%X,algo_ref:%X)", + info->reference, info->mechanism, info->operations, info->algo_ref); + } + + return info; +} + int sc_pkcs15_get_generalized_time(struct sc_context *ctx, char **out) { #ifdef HAVE_GETTIMEOFDAY struct timeval tv; #endif - struct tm *tm_time; + struct tm tm; time_t t; if (!ctx || !out) @@ -2580,16 +2854,21 @@ sc_pkcs15_get_generalized_time(struct sc_context *ctx, char **out) #else t = time(NULL); #endif - tm_time = gmtime(&t); - if (!tm_time) - LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "gmtime failed"); + +#ifdef _WIN32 + if (0 != gmtime_s(&tm, &t)) + LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); +#else + if (NULL == gmtime_r(&t, &tm)) + LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); +#endif *out = calloc(1, 16); if (*out == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "memory failure"); /* print time in generalized time format */ - if (!strftime(*out, 16, "%Y%m%d%H%M%SZ", tm_time)) { + if (!strftime(*out, 16, "%Y%m%d%H%M%SZ", &tm)) { free(*out); LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "strftime failed"); } @@ -2687,19 +2966,19 @@ sc_pkcs15_serialize_guid(unsigned char *in, size_t in_size, unsigned flags, *out = '\0'; if (!flags) - strcpy(out, "{"); + strlcpy(out, "{", out_size); for (ii=0; ii<4; ii++) sprintf(out + strlen(out), "%02x", *(in + offs++)); for (jj=0; jj<3; jj++) { - strcat(out, "-"); + strlcat(out, "-", out_size); for (ii=0; ii<2; ii++) sprintf(out + strlen(out), "%02x", *(in + offs++)); } - strcat(out, "-"); + strlcat(out, "-", out_size); for (ii=0; ii<6; ii++) sprintf(out + strlen(out), "%02x", *(in + offs++)); if (!flags) - strcat(out, "}"); + strlcat(out, "}", out_size); return SC_SUCCESS; } @@ -2713,7 +2992,8 @@ sc_pkcs15_get_object_guid(struct sc_pkcs15_card *p15card, const struct sc_pkcs15 struct sc_serial_number serialnr; struct sc_pkcs15_id id; unsigned char guid_bin[SC_PKCS15_MAX_ID_SIZE + SC_MAX_SERIALNR]; - int rv, guid_bin_size; + int rv; + size_t guid_bin_size; LOG_FUNC_CALLED(ctx); if(!out || !out_size) diff --git a/src/libopensc/pkcs15.h b/src/libopensc/pkcs15.h index 7d922c397d..361569e952 100644 --- a/src/libopensc/pkcs15.h +++ b/src/libopensc/pkcs15.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_PKCS15_H @@ -192,24 +192,6 @@ struct sc_pkcs15_prkey_rsa { sc_pkcs15_bignum_t dmq1; }; -struct sc_pkcs15_pubkey_dsa { - sc_pkcs15_bignum_t pub; - sc_pkcs15_bignum_t p; - sc_pkcs15_bignum_t q; - sc_pkcs15_bignum_t g; -}; - -struct sc_pkcs15_prkey_dsa { - /* public components */ - sc_pkcs15_bignum_t pub; - sc_pkcs15_bignum_t p; - sc_pkcs15_bignum_t q; - sc_pkcs15_bignum_t g; - - /* private key */ - sc_pkcs15_bignum_t priv; -}; - struct sc_pkcs15_gost_parameters { struct sc_object_id key; struct sc_object_id hash; @@ -223,8 +205,8 @@ struct sc_pkcs15_pubkey_ec { struct sc_pkcs15_prkey_ec { struct sc_ec_parameters params; - sc_pkcs15_bignum_t privateD; /* note this is bignum */ - struct sc_pkcs15_u8 ecpointQ; /* This is NOT DER, just value and length */ + struct sc_pkcs15_u8 ecpointQ; /* This is NOT DER, just value and length */ + sc_pkcs15_bignum_t privateD; /* note this is bignum */ }; struct sc_pkcs15_pubkey_gostr3410 { @@ -238,13 +220,12 @@ struct sc_pkcs15_prkey_gostr3410 { }; struct sc_pkcs15_pubkey { - int algorithm; + unsigned long algorithm; struct sc_algorithm_id * alg_id; /* Decoded key */ union { struct sc_pkcs15_pubkey_rsa rsa; - struct sc_pkcs15_pubkey_dsa dsa; struct sc_pkcs15_pubkey_ec ec; struct sc_pkcs15_pubkey_gostr3410 gostr3410; } u; @@ -252,12 +233,11 @@ struct sc_pkcs15_pubkey { typedef struct sc_pkcs15_pubkey sc_pkcs15_pubkey_t; struct sc_pkcs15_prkey { - unsigned int algorithm; + unsigned long algorithm; /* TODO do we need: struct sc_algorithm_id * alg_id; */ union { struct sc_pkcs15_prkey_rsa rsa; - struct sc_pkcs15_prkey_dsa dsa; struct sc_pkcs15_prkey_ec ec; struct sc_pkcs15_prkey_gostr3410 gostr3410; struct sc_pkcs15_skey secret; @@ -382,7 +362,7 @@ struct sc_pkcs15_prkey_info { unsigned int usage, access_flags; int native, key_reference; /* convert to union if other types are supported */ - size_t modulus_length; /* RSA */ + size_t modulus_length; /* RSA, in bits */ size_t field_length; /* EC in bits */ unsigned int algo_refs[SC_MAX_SUPPORTED_ALGORITHMS]; @@ -437,15 +417,17 @@ typedef struct sc_pkcs15_skey_info sc_pkcs15_skey_info_t; #define SC_PKCS15_TYPE_PRKEY 0x100 #define SC_PKCS15_TYPE_PRKEY_RSA 0x101 -#define SC_PKCS15_TYPE_PRKEY_DSA 0x102 #define SC_PKCS15_TYPE_PRKEY_GOSTR3410 0x103 #define SC_PKCS15_TYPE_PRKEY_EC 0x104 +#define SC_PKCS15_TYPE_PRKEY_EDDSA 0x105 +#define SC_PKCS15_TYPE_PRKEY_XEDDSA 0x106 #define SC_PKCS15_TYPE_PUBKEY 0x200 #define SC_PKCS15_TYPE_PUBKEY_RSA 0x201 -#define SC_PKCS15_TYPE_PUBKEY_DSA 0x202 #define SC_PKCS15_TYPE_PUBKEY_GOSTR3410 0x203 #define SC_PKCS15_TYPE_PUBKEY_EC 0x204 +#define SC_PKCS15_TYPE_PUBKEY_EDDSA 0x205 +#define SC_PKCS15_TYPE_PUBKEY_XEDDSA 0x206 #define SC_PKCS15_TYPE_SKEY 0x300 #define SC_PKCS15_TYPE_SKEY_GENERIC 0x301 @@ -493,6 +475,12 @@ struct sc_pkcs15_object { struct sc_pkcs15_object *next, *prev; /* used only internally */ struct sc_pkcs15_der content; + /* Method for deallocating the object's content.value. + * If no specific function for deallocation is given, then free() is used + * to release content.value */ + void (*content_free)(void *, size_t); + + int session_object; /* used internally. if nonzero, object is a session object. */ }; typedef struct sc_pkcs15_object sc_pkcs15_object_t; @@ -591,6 +579,7 @@ typedef struct sc_pkcs15_card { int use_pin_cache; int pin_cache_counter; int pin_cache_ignore_user_consent; + int pin_protected_certificate; } opts; unsigned int magic; @@ -611,7 +600,17 @@ typedef struct sc_pkcs15_card { /* flags suitable for struct sc_pkcs15_card */ #define SC_PKCS15_CARD_FLAG_EMULATED 0x02000000 -/* X509 bits for certificate usage extansion */ +/* suitable for struct sc_pkcs15_card.opts.use_file_cache */ +#define SC_PKCS15_OPTS_CACHE_NO_FILES 0 +#define SC_PKCS15_OPTS_CACHE_PUBLIC_FILES 1 +#define SC_PKCS15_OPTS_CACHE_ALL_FILES 2 + +/* suitable for struct sc_pkcs15_card.opts.pin_protected_certificate */ +#define SC_PKCS15_CARD_OPTS_PRIV_CERT_PROTECT 0 +#define SC_PKCS15_CARD_OPTS_PRIV_CERT_IGNORE 1 +#define SC_PKCS15_CARD_OPTS_PRIV_CERT_DECLASSIFY 2 + +/* X509 bits for certificate usage extension */ #define SC_X509_DIGITAL_SIGNATURE 0x0001UL #define SC_X509_NON_REPUDIATION 0x0002UL #define SC_X509_KEY_ENCIPHERMENT 0x0004UL @@ -647,21 +646,49 @@ int sc_pkcs15_find_object_by_id(struct sc_pkcs15_card *, unsigned int, struct sc_pkcs15_card * sc_pkcs15_card_new(void); void sc_pkcs15_card_free(struct sc_pkcs15_card *p15card); void sc_pkcs15_card_clear(struct sc_pkcs15_card *p15card); +struct sc_pkcs15_tokeninfo * sc_pkcs15_tokeninfo_new(void); +void sc_pkcs15_free_tokeninfo(struct sc_pkcs15_tokeninfo *tokeninfo); int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *prkey_obj, unsigned long flags, - const u8 *in, size_t inlen, u8 *out, size_t outlen); + const u8 *in, size_t inlen, u8 *out, size_t outlen, void *pMechanism); int sc_pkcs15_derive(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *prkey_obj, unsigned long flags, - const u8 *in, size_t inlen, u8 *out, unsigned long *poutlen); + const u8 *in, size_t inlen, u8 *out, size_t *poutlen); + +int sc_pkcs15_unwrap(struct sc_pkcs15_card *p15card, + const struct sc_pkcs15_object *key, + struct sc_pkcs15_object *target_key, + unsigned long flags, + const u8 * in, size_t inlen, + const u8 * param, size_t paramlen); + +int sc_pkcs15_wrap(struct sc_pkcs15_card *p15card, + const struct sc_pkcs15_object *key, + struct sc_pkcs15_object *target_key, + unsigned long flags, + u8 * cryptogram, size_t* crgram_len, + const u8 * param, size_t paramlen); int sc_pkcs15_compute_signature(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *prkey_obj, unsigned long alg_flags, const u8 *in, - size_t inlen, u8 *out, size_t outlen); + size_t inlen, u8 *out, size_t outlen, void *pMechanism); + +int sc_pkcs15_encrypt_sym(struct sc_pkcs15_card *p15card, + const struct sc_pkcs15_object *obj, + unsigned long flags, + const u8 *in, size_t inlen, u8 *out, size_t *outlen, + const u8 *param, size_t paramlen); + +int sc_pkcs15_decrypt_sym(struct sc_pkcs15_card *p15card, + const struct sc_pkcs15_object *obj, + unsigned long flags, + const u8 *in, size_t inlen, u8 *out, size_t *outlen, + const u8 *param, size_t paramlen); int sc_pkcs15_read_pubkey(struct sc_pkcs15_card *, const struct sc_pkcs15_object *, struct sc_pkcs15_pubkey **); @@ -669,10 +696,6 @@ int sc_pkcs15_decode_pubkey_rsa(struct sc_context *, struct sc_pkcs15_pubkey_rsa *, const u8 *, size_t); int sc_pkcs15_encode_pubkey_rsa(struct sc_context *, struct sc_pkcs15_pubkey_rsa *, u8 **, size_t *); -int sc_pkcs15_decode_pubkey_dsa(struct sc_context *, - struct sc_pkcs15_pubkey_dsa *, const u8 *, size_t); -int sc_pkcs15_encode_pubkey_dsa(struct sc_context *, - struct sc_pkcs15_pubkey_dsa *, u8 **, size_t *); int sc_pkcs15_decode_pubkey_gostr3410(struct sc_context *, struct sc_pkcs15_pubkey_gostr3410 *, const u8 *, size_t); int sc_pkcs15_encode_pubkey_gostr3410(struct sc_context *, @@ -681,11 +704,13 @@ int sc_pkcs15_decode_pubkey_ec(struct sc_context *, struct sc_pkcs15_pubkey_ec *, const u8 *, size_t); int sc_pkcs15_encode_pubkey_ec(struct sc_context *, struct sc_pkcs15_pubkey_ec *, u8 **, size_t *); +int sc_pkcs15_encode_pubkey_eddsa(struct sc_context *, + struct sc_pkcs15_pubkey_ec *, u8 **, size_t *); int sc_pkcs15_decode_pubkey(struct sc_context *, struct sc_pkcs15_pubkey *, const u8 *, size_t); int sc_pkcs15_encode_pubkey(struct sc_context *, struct sc_pkcs15_pubkey *, u8 **, size_t *); -int sc_pkcs15_encode_pubkey_as_spki(struct sc_context *, +int sc_pkcs15_encode_pubkey_as_spki(struct sc_context *, struct sc_pkcs15_pubkey *, u8 **, size_t *); void sc_pkcs15_erase_pubkey(struct sc_pkcs15_pubkey *); void sc_pkcs15_free_pubkey(struct sc_pkcs15_pubkey *); @@ -702,10 +727,12 @@ int sc_pkcs15_pubkey_from_spki_fields(struct sc_context *, int sc_pkcs15_encode_prkey(struct sc_context *, struct sc_pkcs15_prkey *, u8 **, size_t *); void sc_pkcs15_free_prkey(struct sc_pkcs15_prkey *prkey); +void sc_pkcs15_erase_prkey(struct sc_pkcs15_prkey *prkey); void sc_pkcs15_free_key_params(struct sc_pkcs15_key_params *params); int sc_pkcs15_read_data_object(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_data_info *info, + int private_obj, struct sc_pkcs15_data **data_object_out); int sc_pkcs15_find_data_object_by_id(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_id *id, @@ -721,6 +748,7 @@ void sc_pkcs15_free_data_object(struct sc_pkcs15_data *data_object); int sc_pkcs15_read_certificate(struct sc_pkcs15_card *card, const struct sc_pkcs15_cert_info *info, + int private_obj, struct sc_pkcs15_cert **cert); void sc_pkcs15_free_certificate(struct sc_pkcs15_cert *cert); int sc_pkcs15_find_cert_by_id(struct sc_pkcs15_card *card, @@ -730,6 +758,9 @@ int sc_pkcs15_get_name_from_dn(struct sc_context *ctx, const u8 *dn, size_t dn_len, const struct sc_object_id *type, u8 **name, size_t *name_len); +int sc_pkcs15_map_usage(unsigned int cert_usage, unsigned long algorithm, + unsigned int *pub_usage_ptr, unsigned int *pr_usage_ptr, + int allow_nonrepudiation); int sc_pkcs15_get_extension(struct sc_context *ctx, struct sc_pkcs15_cert *cert, const struct sc_object_id *type, @@ -738,7 +769,7 @@ int sc_pkcs15_get_extension(struct sc_context *ctx, int sc_pkcs15_get_bitstring_extension(struct sc_context *ctx, struct sc_pkcs15_cert *cert, const struct sc_object_id *type, - unsigned long long *value, + unsigned int *value, int *is_critical); /* sc_pkcs15_create_cdf: Creates a new certificate DF on a card pointed * by . Information about the file, such as the file ID, is read @@ -746,7 +777,6 @@ int sc_pkcs15_get_bitstring_extension(struct sc_context *ctx, int sc_pkcs15_create_cdf(struct sc_pkcs15_card *card, struct sc_file *file, const struct sc_pkcs15_cert_info **certs); -int sc_pkcs15_create(struct sc_pkcs15_card *p15card, struct sc_card *card); int sc_pkcs15_find_prkey_by_id(struct sc_pkcs15_card *card, const struct sc_pkcs15_id *id, @@ -862,13 +892,6 @@ int sc_pkcs15_decode_skdf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, const u8 **buf, size_t *bufsize); -int sc_pkcs15_decode_enveloped_data(struct sc_context *ctx, - struct sc_pkcs15_enveloped_data *result, - const u8 *buf, size_t buflen); -int sc_pkcs15_encode_enveloped_data(struct sc_context *ctx, - struct sc_pkcs15_enveloped_data *data, - u8 **buf, size_t *buflen); - int sc_pkcs15_add_object(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj); void sc_pkcs15_remove_object(struct sc_pkcs15_card *p15card, @@ -883,7 +906,7 @@ int sc_pkcs15_encode_unusedspace(struct sc_context *ctx, struct sc_pkcs15_card *p15card, u8 **buf, size_t *buflen); -/* Deduce private key attributes from cerresponding certificate */ +/* Deduce private key attributes from corresponding certificate */ int sc_pkcs15_prkey_attrs_from_cert(struct sc_pkcs15_card *, struct sc_pkcs15_object *, struct sc_pkcs15_object **); @@ -892,12 +915,13 @@ void sc_pkcs15_free_pubkey_info(sc_pkcs15_pubkey_info_t *key); void sc_pkcs15_free_cert_info(sc_pkcs15_cert_info_t *cert); void sc_pkcs15_free_data_info(sc_pkcs15_data_info_t *data); void sc_pkcs15_free_auth_info(sc_pkcs15_auth_info_t *auth_info); +void sc_pkcs15_free_skey_info(sc_pkcs15_skey_info_t *key); void sc_pkcs15_free_object(struct sc_pkcs15_object *obj); /* Generic file i/o */ int sc_pkcs15_read_file(struct sc_pkcs15_card *p15card, const struct sc_path *path, - u8 **buf, size_t *buflen); + u8 **buf, size_t *buflen, int private_data); /* Caching functions */ int sc_pkcs15_read_cached_file(struct sc_pkcs15_card *p15card, @@ -934,8 +958,14 @@ void sc_pkcs15_free_object_content(struct sc_pkcs15_object *); int sc_pkcs15_allocate_object_content(struct sc_context *, struct sc_pkcs15_object *, const unsigned char *, size_t); +/* find algorithm from card's supported algorithms by operation and mechanism */ struct sc_supported_algo_info *sc_pkcs15_get_supported_algo(struct sc_pkcs15_card *, - unsigned, unsigned); + unsigned operation, unsigned mechanism); + +/* find algorithm from card's supported algorithms by operation, mechanism and object_id */ +struct sc_supported_algo_info *sc_pkcs15_get_specific_supported_algo(struct sc_pkcs15_card *, + unsigned operation, unsigned mechanism, const struct sc_object_id *algo_oid); + int sc_pkcs15_add_supported_algo_ref(struct sc_pkcs15_object *, struct sc_supported_algo_info *); @@ -973,14 +1003,6 @@ typedef struct sc_pkcs15_search_key { int sc_pkcs15_search_objects(struct sc_pkcs15_card *, sc_pkcs15_search_key_t *, struct sc_pkcs15_object **, size_t); -/* This structure is passed to the new sc_pkcs15emu_*_init functions */ -typedef struct sc_pkcs15emu_opt { - scconf_block *blk; - unsigned int flags; -} sc_pkcs15emu_opt_t; - -#define SC_PKCS15EMU_FLAGS_NO_CHECK 0x00000001 - extern int sc_pkcs15_bind_synthetic(struct sc_pkcs15_card *, struct sc_aid *); extern int sc_pkcs15_is_emulation_only(sc_card_t *); @@ -997,6 +1019,14 @@ int sc_pkcs15emu_add_ec_prkey(struct sc_pkcs15_card *, const struct sc_pkcs15_object *, const sc_pkcs15_prkey_info_t *); int sc_pkcs15emu_add_ec_pubkey(struct sc_pkcs15_card *, const struct sc_pkcs15_object *, const sc_pkcs15_pubkey_info_t *); +int sc_pkcs15emu_add_eddsa_prkey(struct sc_pkcs15_card *, + const struct sc_pkcs15_object *, const sc_pkcs15_prkey_info_t *); +int sc_pkcs15emu_add_eddsa_pubkey(struct sc_pkcs15_card *, + const struct sc_pkcs15_object *, const sc_pkcs15_pubkey_info_t *); +int sc_pkcs15emu_add_xeddsa_prkey(struct sc_pkcs15_card *, + const struct sc_pkcs15_object *, const sc_pkcs15_prkey_info_t *); +int sc_pkcs15emu_add_xeddsa_pubkey(struct sc_pkcs15_card *, + const struct sc_pkcs15_object *, const sc_pkcs15_pubkey_info_t *); int sc_pkcs15emu_add_x509_cert(struct sc_pkcs15_card *, const struct sc_pkcs15_object *, const sc_pkcs15_cert_info_t *); int sc_pkcs15emu_add_data_object(struct sc_pkcs15_card *, diff --git a/src/libopensc/reader-cryptotokenkit.m b/src/libopensc/reader-cryptotokenkit.m index cead041c71..f963a1710c 100644 --- a/src/libopensc/reader-cryptotokenkit.m +++ b/src/libopensc/reader-cryptotokenkit.m @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -43,6 +43,30 @@ NULL }; +static int convertError(NSError *error) +{ + switch (error.code) { + case TKErrorCodeNotImplemented: + return SC_ERROR_NOT_IMPLEMENTED; + case TKErrorCodeCommunicationError: + return SC_ERROR_TRANSMIT_FAILED; + case TKErrorCodeCorruptedData: + return SC_ERROR_CORRUPTED_DATA; + case TKErrorCodeCanceledByUser: + return SC_ERROR_KEYPAD_CANCELLED; + case TKErrorCodeAuthenticationFailed: + return SC_ERROR_PIN_CODE_INCORRECT; + case TKErrorCodeObjectNotFound: + return SC_ERROR_OBJECT_NOT_FOUND; + case TKErrorCodeTokenNotFound: + return SC_ERROR_CARD_REMOVED; + case TKErrorCodeBadParameter: + return SC_ERROR_INVALID_ARGUMENTS; + default: + return SC_ERROR_UNKNOWN; + } +} + static int cryptotokenkit_init(sc_context_t *ctx) { return SC_SUCCESS; @@ -155,8 +179,12 @@ static int cryptotokenkit_connect(sc_reader_t *reader) priv->tksmartcard = [priv->tksmartcardslot makeSmartCard]; } - if (!priv->tksmartcard || ![priv->tksmartcard valid]) - return SC_ERROR_CARD_NOT_PRESENT; + if (!priv->tksmartcard || !priv->tksmartcard.valid) + return SC_ERROR_CARD_REMOVED; + + /* if tksmartcard.context is set to nil, we know that the card has been + * reset or acquired by a different session */ + priv->tksmartcard.context = @(YES); /* attempt to detect protocol in use T0/T1/RAW */ ctk_set_proto(reader); @@ -184,11 +212,18 @@ static int cryptotokenkit_lock(sc_reader_t *reader) if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) goto err; + if (!priv->tksmartcard) + return SC_ERROR_CARD_REMOVED; + + if (priv->tksmartcard.context == nil) { + r = SC_ERROR_CARD_RESET; + priv->tksmartcard.context = @(YES); + goto err; + } [priv->tksmartcard beginSessionWithReply:^(BOOL success, NSError *error) { if (success != TRUE) { - NSLog(@"Error locking card <%@>", error); - r = SC_ERROR_UNKNOWN; + r = convertError(error); } else { r = SC_SUCCESS; } @@ -206,8 +241,11 @@ static int cryptotokenkit_unlock(sc_reader_t *reader) LOG_FUNC_CALLED(reader->ctx); + if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; + if (!priv->tksmartcard) + return SC_ERROR_CARD_REMOVED; [priv->tksmartcard endSession]; @@ -226,13 +264,16 @@ static int cryptotokenkit_transmit(sc_reader_t *reader, sc_apdu_t *apdu) LOG_FUNC_CALLED(reader->ctx); + if (!priv->tksmartcard) + return SC_ERROR_CARD_REMOVED; + r = sc_apdu_get_octets(reader->ctx, apdu, &sbuf, &ssize, reader->active_protocol); if (r != SC_SUCCESS) goto err; if (reader->name) sc_log(reader->ctx, "reader '%s'", reader->name); - sc_apdu_log(reader->ctx, SC_LOG_DEBUG_NORMAL, sbuf, ssize, 1); + sc_apdu_log(reader->ctx, sbuf, ssize, 1); [priv->tksmartcard transmitRequest: [NSData dataWithBytes: sbuf length: ssize] @@ -240,18 +281,14 @@ static int cryptotokenkit_transmit(sc_reader_t *reader, sc_apdu_t *apdu) if (response) { rsize = [response length]; rbuf = malloc(rsize); - if (!rbuf) + if (!rbuf) { r = SC_ERROR_OUT_OF_MEMORY; - else - memcpy(rbuf, (unsigned char*) [response bytes], rsize); - } - if (r == SC_SUCCESS) { - if (error) { - NSLog(@"Error transmitting to card <%@>", error); - r = SC_ERROR_TRANSMIT_FAILED; } else { + memcpy(rbuf, (unsigned char*) [response bytes], rsize); r = SC_SUCCESS; } + } else { + r = convertError(error); } dispatch_semaphore_signal(sema); }]; @@ -259,7 +296,7 @@ static int cryptotokenkit_transmit(sc_reader_t *reader, sc_apdu_t *apdu) if (r != SC_SUCCESS) goto err; - sc_apdu_log(reader->ctx, SC_LOG_DEBUG_NORMAL, rbuf, rsize, 0); + sc_apdu_log(reader->ctx, rbuf, rsize, 0); r = sc_apdu_set_resp(reader->ctx, apdu, rbuf, rsize); err: @@ -275,6 +312,165 @@ static int cryptotokenkit_transmit(sc_reader_t *reader, sc_apdu_t *apdu) LOG_FUNC_RETURN(reader->ctx, r); } +TKSmartCardPINFormat *getPINFormat(struct sc_pin_cmd_pin *pin) +{ + TKSmartCardPINFormat *format = [[TKSmartCardPINFormat alloc] init]; + switch (pin->encoding) { + case SC_PIN_ENCODING_GLP: + /* GLP PIN length is encoded in 4 bits and block size is always 8 bytes */ + format.PINLengthBitSize = 4; + format.PINBlockByteLength = 8; + /* fall through */ + case SC_PIN_ENCODING_BCD: + format.encoding = TKSmartCardPINEncodingBCD; + break; + case SC_PIN_ENCODING_ASCII: + format.encoding = TKSmartCardPINEncodingASCII; + format.PINBlockByteLength = pin->pad_length; + break; + default: + return nil; + } + format.minPINLength = pin->min_length; + format.maxPINLength = pin->max_length; + + return format; +} + +int cryptotokenkit_perform_verify(struct sc_reader *reader, struct sc_pin_cmd_data *data) +{ + u8 template[SC_MAX_APDU_BUFFER_SIZE]; + __block int r; + __block UInt16 sw; + size_t ssize = 0; + u8 *sbuf = NULL, *rbuf = NULL; + struct cryptotokenkit_private_data *priv = reader->drv_data; + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + + LOG_FUNC_CALLED(reader->ctx); + + if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) + return SC_ERROR_NOT_ALLOWED; + if (!priv->tksmartcard) + return SC_ERROR_CARD_REMOVED; + + /* The APDU must be provided by the card driver */ + if (!data->apdu) { + return SC_ERROR_NOT_SUPPORTED; + } + + r = sc_apdu_get_octets(reader->ctx, data->apdu, &sbuf, &ssize, reader->active_protocol); + LOG_TEST_GOTO_ERR(reader->ctx, r, "Could not encode APDU template"); + + NSData *apdu = [NSData dataWithBytes:sbuf length:ssize]; + TKSmartCardPINFormat *format; + struct sc_pin_cmd_pin *pin_ref = &data->pin1; + TKSmartCardUserInteractionForPINOperation *interaction; + switch (data->cmd) { + case SC_PIN_CMD_VERIFY: + format = getPINFormat(pin_ref); + NSInteger offset; + if (data->pin1.offset >= 5) { + offset = data->pin1.offset - 5; + } else { + offset = 0; + } + interaction = [priv->tksmartcard userInteractionForSecurePINVerificationWithPINFormat:format APDU:apdu PINByteOffset:offset]; + break; + case SC_PIN_CMD_CHANGE: + case SC_PIN_CMD_UNBLOCK: + if (data->flags & SC_PIN_CMD_IMPLICIT_CHANGE) { + pin_ref = &data->pin2; + } + /* TODO: set confirmation and text */ + format = getPINFormat(pin_ref); + NSInteger oldOffset, newOffset; + + /* Set offsets if available, otherwise default to 0 */ + oldOffset = (data->pin1.offset >= 5 ? data->pin1.offset - 5 : 0); + newOffset = (data->pin2.offset >= 5 ? data->pin2.offset - 5 : 0); + interaction = [priv->tksmartcard userInteractionForSecurePINChangeWithPINFormat:format APDU:apdu currentPINByteOffset:oldOffset newPINByteOffset:newOffset]; + break; + default: + sc_log(reader->ctx, "Unknown PIN command %d", data->cmd); + r = SC_ERROR_NOT_SUPPORTED; + goto err; + } + if (nil == interaction) { + r = SC_ERROR_NOT_SUPPORTED; + goto err; + } + + [interaction runWithReply:^(BOOL success, NSError *error) { + if (success) { + NSData *response = interaction.resultData; + if (nil != response) { + data->apdu->resplen = response.length; + memcpy(data->apdu->resp, (unsigned char *) response.bytes, response.length); + } else { + data->apdu->resplen = 0; + } + sw = interaction.resultSW; + r = SC_SUCCESS; + } else { + r = convertError(error); + } + dispatch_semaphore_signal(sema); + }]; + dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + + if (r != SC_SUCCESS) { + goto err; + } + + data->apdu->sw1 = sw >> 8; + data->apdu->sw2 = sw & 0xFF; + + switch (sw) { + case 0x6400: + /* Input timed out */ + r = SC_ERROR_KEYPAD_TIMEOUT; + break; + case 0x6401: + /* Input cancelled */ + r = SC_ERROR_KEYPAD_CANCELLED; + break; + case 0x6402: + /* PINs don't match */ + r = SC_ERROR_KEYPAD_PIN_MISMATCH; + break; + case 0x6403: + /* Entered PIN is not in length limits */ + r = SC_ERROR_INVALID_PIN_LENGTH; /* XXX: designed to be returned when PIN is in API call */ + break; + case 0x6B80: + /* Wrong data in the buffer, rejected by firmware */ + r = SC_ERROR_READER; + break; + } + +err: + if (sbuf != NULL) { + sc_mem_clear(sbuf, ssize); + free(sbuf); + } + + LOG_FUNC_RETURN(reader->ctx, r); +} + +void cryptotokenkit_detect_reader_features(struct sc_reader *reader, TKSmartCard* tksmartcard) +{ + if (tksmartcard) { + const u8 template[] = {tksmartcard.cla, 0x20, 0x00, 0x80, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + NSData *data = [NSData dataWithBytes:template length:sizeof template]; + TKSmartCardPINFormat *PINFormat = [[TKSmartCardPINFormat alloc] init]; + PINFormat.PINBitOffset = 0; + + if (nil != [tksmartcard userInteractionForSecurePINVerificationWithPINFormat:PINFormat APDU:data PINByteOffset:0]) + reader->capabilities |= SC_READER_CAP_PIN_PAD; + } +} + int cryptotokenkit_use_reader(sc_context_t *ctx, void *pcsc_context_handle, void *pcsc_card_handle) { int r; @@ -329,6 +525,10 @@ int cryptotokenkit_use_reader(sc_context_t *ctx, void *pcsc_context_handle, void /* attempt to detect protocol in use T0/T1/RAW */ ctk_set_proto(reader); + cryptotokenkit_detect_card_presence(reader); + + cryptotokenkit_detect_reader_features(reader, tksmartcard); + r = _sc_add_reader(ctx, reader); err: @@ -347,8 +547,10 @@ int cryptotokenkit_use_reader(sc_context_t *ctx, void *pcsc_context_handle, void static int cryptotokenkit_detect_readers(sc_context_t *ctx) { size_t i; + NSUInteger j; int r; TKSmartCardSlotManager *mngr = [TKSmartCardSlotManager defaultManager]; + NSMutableArray *slotNames; LOG_FUNC_CALLED(ctx); @@ -358,38 +560,38 @@ static int cryptotokenkit_detect_readers(sc_context_t *ctx) goto err; } - /* temporarily mark all readers as removed */ - for (i=0; i < sc_ctx_get_reader_count(ctx); i++) { - sc_reader_t *reader = sc_ctx_get_reader(ctx, i); - reader->flags |= SC_READER_REMOVED; - } - sc_log(ctx, "Probing CryptoTokenKit readers"); - for (NSString *slotName in [mngr slotNames]) { - sc_reader_t *old_reader; - int found = 0; - const char *reader_name = [slotName UTF8String]; - dispatch_semaphore_t sema = dispatch_semaphore_create(0); + slotNames = [[mngr slotNames] mutableCopy]; - for (i=0; i < sc_ctx_get_reader_count(ctx) && !found; i++) { - old_reader = sc_ctx_get_reader(ctx, i); - if (old_reader == NULL) { - r = SC_ERROR_INTERNAL; - goto err; - } - if (!strcmp(old_reader->name, reader_name)) { - found = 1; - } + /* check if existing readers were returned in the list */ + for (i = 0; i < sc_ctx_get_reader_count(ctx); i++) { + sc_reader_t *reader = sc_ctx_get_reader(ctx, i); + + if (reader == NULL) { + r = SC_ERROR_INTERNAL; + goto err; } - /* Reader already available, skip */ - if (found) { - old_reader->flags &= ~SC_READER_REMOVED; - continue; + for (j = 0; j < [slotNames count]; j++) { + if (!strcmp(reader->name, [slotNames[j] UTF8String])) + break; } - sc_log(ctx, "Found new CryptoTokenKit reader '%s'", reader_name); + if (j < [slotNames count]) { + /* existing reader found; remove it from the list */ + [slotNames removeObjectAtIndex:j]; + reader->flags &= ~SC_READER_REMOVED; + } else { + /* existing reader not found */ + reader->flags |= SC_READER_REMOVED; + } + } + + /* add readers remaining in the list */ + for (NSString *slotName in slotNames) { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + sc_log(ctx, "Found new CryptoTokenKit reader '%s'", [slotName UTF8String]); [mngr getSlotWithName:slotName reply:^(TKSmartCardSlot *slot) { cryptotokenkit_use_reader(ctx, slot, NULL); dispatch_semaphore_signal(sema); @@ -414,7 +616,7 @@ static int cryptotokenkit_detect_readers(sc_context_t *ctx) cryptotokenkit_ops.lock = cryptotokenkit_lock; cryptotokenkit_ops.unlock = cryptotokenkit_unlock; cryptotokenkit_ops.transmit = cryptotokenkit_transmit; - cryptotokenkit_ops.perform_verify = NULL; + cryptotokenkit_ops.perform_verify = cryptotokenkit_perform_verify; cryptotokenkit_ops.perform_pace = NULL; cryptotokenkit_ops.use_reader = cryptotokenkit_use_reader; cryptotokenkit_ops.detect_readers = cryptotokenkit_detect_readers; diff --git a/src/libopensc/reader-ctapi.c b/src/libopensc/reader-ctapi.c index 3c1db600fd..10f3d335c3 100644 --- a/src/libopensc/reader-ctapi.c +++ b/src/libopensc/reader-ctapi.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -141,7 +141,7 @@ static int refresh_attributes(sc_reader_t *reader) } else { if (rbuf[0] != CTBCS_P2_STATUS_ICC) { /* Should we be more tolerant here? I do not think so... */ - sc_log(reader->ctx, "Invalid data object returnd on CTBCS_P2_STATUS_ICC: 0x%x", rbuf[0]); + sc_log(reader->ctx, "Invalid data object returned on CTBCS_P2_STATUS_ICC: 0x%x", rbuf[0]); return SC_ERROR_TRANSMIT_FAILED; } /* Fixme - should not be reached */ @@ -199,7 +199,7 @@ static int ctapi_transmit(sc_reader_t *reader, sc_apdu_t *apdu) r = sc_apdu_get_octets(reader->ctx, apdu, &sbuf, &ssize, SC_PROTO_RAW); if (r != SC_SUCCESS) goto out; - sc_apdu_log(reader->ctx, SC_LOG_DEBUG_NORMAL, sbuf, ssize, 1); + sc_apdu_log(reader->ctx, sbuf, ssize, 1); r = ctapi_internal_transmit(reader, sbuf, ssize, rbuf, &rsize, apdu->control); if (r < 0) { @@ -207,7 +207,7 @@ static int ctapi_transmit(sc_reader_t *reader, sc_apdu_t *apdu) sc_log(reader->ctx, "unable to transmit"); goto out; } - sc_apdu_log(reader->ctx, SC_LOG_DEBUG_NORMAL, rbuf, rsize, 0); + sc_apdu_log(reader->ctx, rbuf, rsize, 0); /* set response */ r = sc_apdu_set_resp(reader->ctx, apdu, rbuf, rsize); out: @@ -239,7 +239,6 @@ static int ctapi_connect(sc_reader_t *reader) char rv; u8 cmd[9], rbuf[256], sad, dad; unsigned short lr; - int r; if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; @@ -259,13 +258,13 @@ static int ctapi_connect(sc_reader_t *reader) return SC_ERROR_TRANSMIT_FAILED; } if (lr < 2) - SC_FUNC_RETURN(reader->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(reader->ctx, SC_ERROR_INTERNAL); lr -= 2; if (lr > SC_MAX_ATR_SIZE) return SC_ERROR_INTERNAL; reader->atr.len = lr; memcpy(reader->atr.value, rbuf, lr); - r = _sc_parse_atr(reader); + _sc_parse_atr(reader); return 0; } @@ -435,11 +434,11 @@ static int ctapi_load_module(sc_context_t *ctx, if (rbuf[0] != CTBCS_P2_STATUS_TFU) { /* Number of slots might also detected by using CTBCS_P2_STATUS_ICC. If you think that's important please do it... ;) */ - sc_log(reader->ctx, "Invalid data object returnd on CTBCS_P2_STATUS_TFU: 0x%x", rbuf[0]); + sc_log(reader->ctx, "Invalid data object returned on CTBCS_P2_STATUS_TFU: 0x%x", rbuf[0]); } NumUnits = rbuf[1]; if (NumUnits + 4 > lr) { - sc_log(reader->ctx, "Invalid data returnd: %d functional units, size %d", NumUnits, rv); + sc_log(reader->ctx, "Invalid data returned: %d functional units, size %d", NumUnits, rv); } priv->ctapi_functional_units = 0; for(i = 0; i < NumUnits; i++) { diff --git a/src/libopensc/reader-empty.c b/src/libopensc/reader-empty.c new file mode 100644 index 0000000000..4e7c04c452 --- /dev/null +++ b/src/libopensc/reader-empty.c @@ -0,0 +1,159 @@ +/* + * reader-none.c: Reader driver stub with no functionality + * + * Copyright (C) 2026 Vyacheslav Yurkov + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "internal.h" + +static int +empty_transmit(sc_reader_t *reader, sc_apdu_t *apdu) +{ + return 0; +} + +static int +empty_detect_card_presence(sc_reader_t *reader) +{ + return SC_SUCCESS; +} + +static int +empty_connect(sc_reader_t *reader) +{ + return SC_SUCCESS; +} + +static int +empty_disconnect(sc_reader_t * reader) +{ + return SC_SUCCESS; +} + +static int +empty_pin_cmd(sc_reader_t *reader, struct sc_pin_cmd_data *data) +{ + return SC_SUCCESS; +} + +static int +empty_lock(sc_reader_t *reader) +{ + return SC_SUCCESS; +} + +static int +empty_unlock(sc_reader_t *reader) +{ + return SC_SUCCESS; +} + +static int +empty_release(sc_reader_t *reader) +{ + return SC_SUCCESS; +} + +static int +empty_reset(sc_reader_t *reader, int do_cold_reset) +{ + return SC_SUCCESS; +} + +static int +empty_cancel(sc_context_t *ctx) +{ + return SC_SUCCESS; +} + +static int +empty_init(sc_context_t *ctx) +{ + return SC_SUCCESS; +} + +static int +empty_finish(sc_context_t *ctx) +{ + return SC_SUCCESS; +} + +static int +empty_detect_readers(sc_context_t *ctx) +{ + return SC_SUCCESS; +} + +static int +empty_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_reader_t **event_reader, unsigned int *event, + int timeout, void **reader_states) +{ + return SC_SUCCESS; +} + +int +empty_perform_pace(struct sc_reader *reader, void *input_pace, void *output_pace) +{ + return SC_SUCCESS; +} + +int +empty_use_reader(sc_context_t *ctx, void *empty_context_handle, void *empty_card_handle) +{ + return SC_SUCCESS; +} + +static struct sc_reader_operations empty_ops; + +static struct sc_reader_driver empty_drv = { + "Empty reader", + "none", + &empty_ops, + NULL +}; + +struct sc_reader_driver * +sc_get_empty_driver(void) +{ + empty_ops.init = empty_init; + empty_ops.finish = empty_finish; + empty_ops.detect_readers = empty_detect_readers; + empty_ops.transmit = empty_transmit; + empty_ops.detect_card_presence = empty_detect_card_presence; + empty_ops.lock = empty_lock; + empty_ops.unlock = empty_unlock; + empty_ops.release = empty_release; + empty_ops.connect = empty_connect; + empty_ops.disconnect = empty_disconnect; + empty_ops.perform_verify = empty_pin_cmd; + empty_ops.wait_for_event = empty_wait_for_event; + empty_ops.cancel = empty_cancel; + empty_ops.reset = empty_reset; + empty_ops.use_reader = empty_use_reader; + empty_ops.perform_pace = empty_perform_pace; + + return &empty_drv; +} diff --git a/src/libopensc/reader-openct.c b/src/libopensc/reader-openct.c index 7f586e902a..7910b3ce2a 100644 --- a/src/libopensc/reader-openct.c +++ b/src/libopensc/reader-openct.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -229,21 +229,21 @@ openct_reader_connect(sc_reader_t *reader) ct_reader_disconnect(data->h); if (!(data->h = ct_reader_connect(data->num))) { - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "ct_reader_connect socket failed\n"); + sc_log(reader->ctx, "ct_reader_connect socket failed\n"); return SC_ERROR_CARD_NOT_PRESENT; } rc = ct_card_request(data->h, data->slot, 0, NULL, reader->atr.value, sizeof(reader->atr.value)); if (rc < 0) { - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(reader->ctx, "openct_reader_connect read failed: %s\n", ct_strerror(rc)); return SC_ERROR_CARD_NOT_PRESENT; } if (rc == 0) { - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "openct_reader_connect recved no data\n"); + sc_log(reader->ctx, "openct_reader_connect received no data\n"); return SC_ERROR_READER; } @@ -323,15 +323,15 @@ static int openct_reader_transmit(sc_reader_t *reader, sc_apdu_t *apdu) r = sc_apdu_get_octets(reader->ctx, apdu, &sbuf, &ssize, SC_PROTO_RAW); if (r != SC_SUCCESS) goto out; - sc_apdu_log(reader->ctx, SC_LOG_DEBUG_NORMAL, sbuf, ssize, 1); + sc_apdu_log(reader->ctx, sbuf, ssize, 1); r = openct_reader_internal_transmit(reader, sbuf, ssize, rbuf, &rsize, apdu->control); if (r < 0) { /* unable to transmit ... most likely a reader problem */ - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "unable to transmit"); + sc_log(reader->ctx, "unable to transmit"); goto out; } - sc_apdu_log(reader->ctx, SC_LOG_DEBUG_NORMAL, rbuf, rsize, 0); + sc_apdu_log(reader->ctx, rbuf, rsize, 0); /* set response */ r = sc_apdu_set_resp(reader->ctx, apdu, rbuf, rsize); out: diff --git a/src/libopensc/reader-pcsc.c b/src/libopensc/reader-pcsc.c index d970fa83f9..0cc7f781d1 100644 --- a/src/libopensc/reader-pcsc.c +++ b/src/libopensc/reader-pcsc.c @@ -16,10 +16,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -39,9 +39,14 @@ #include "common/libscdl.h" #include "internal.h" #include "internal-winscard.h" +#include "card-sc-hsm.h" #include "pace.h" +#ifndef MAXDWORD +#define MAXDWORD UINT32_MAX +#endif + #ifdef HAVE_PCSCLITE_H #if !defined (__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED < 101000 #define HAVE_PCSCLITE 1 @@ -77,11 +82,42 @@ #define PCSC_TRACE(reader, desc, rv) do { sc_log(reader->ctx, "%s:" desc ": 0x%08lx\n", reader->name, (unsigned long)((ULONG)rv)); } while (0) #define PCSC_LOG(ctx, desc, rv) do { sc_log(ctx, desc ": 0x%08lx\n", (unsigned long)((ULONG)rv)); } while (0) +/* #define APDU_LOG_FILE "apdulog" */ +#ifdef APDU_LOG_FILE +void APDU_LOG(u8 *rbuf, uint16_t rsize) +{ + static FILE *fd = NULL; + u8 *lenb = (u8*)&rsize; + + if (fd == NULL) { + fd = fopen(APDU_LOG_FILE, "w"); + } + /* First two bytes denote the length */ + (void) fwrite(lenb, 2, 1, fd); + (void) fwrite(rbuf, rsize, 1, fd); + fflush(fd); +} +#else +#define APDU_LOG(rbuf, rsize) +#endif + +#define MAX_PCSC_EVENT_LISTENERS +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + struct pcsc_global_private_data { int cardmod; SCARDCONTEXT pcsc_ctx; - SCARDCONTEXT pcsc_wait_ctx; + /** `SCARDCONTEXT`s dedicated for detecting reader events. Each calling + * thread will get one free slot assigned. We use global tracking of all + * slots to allow pcsc_cancel to abort each blocking operation. PC/SC + * events are tied to the SCARDCONTEXT, which is why we cannot reuse + * pcsc_ctx for watching events as that may interfere with its usage of + * in `refresh_attributes()`, that may swallow events from a different + * waiting thread. For PKCS#11 we need at least 16 contexts for each + * virtual slot. */ + SCARDCONTEXT pcsc_wait_ctx[32]; int enable_pinpad; + int fixed_pinlength; int enable_pace; size_t force_max_recv_size; size_t force_max_send_size; @@ -106,6 +142,9 @@ struct pcsc_global_private_data { SCardTransmit_t SCardTransmit; SCardListReaders_t SCardListReaders; SCardGetAttrib_t SCardGetAttrib; + + sc_reader_t *attached_reader; + sc_reader_t *removed_reader; }; struct pcsc_private_data { @@ -130,6 +169,8 @@ struct pcsc_private_data { }; static int pcsc_detect_card_presence(sc_reader_t *reader); +static int pcsc_reconnect(sc_reader_t * reader, DWORD action); +static int pcsc_connect(sc_reader_t *reader); static DWORD pcsc_reset_action(const char *str) { @@ -156,9 +197,15 @@ static int pcsc_to_opensc_error(LONG rv) return SC_ERROR_CARD_UNRESPONSIVE; case SCARD_E_SHARING_VIOLATION: return SC_ERROR_READER_LOCKED; +#ifdef SCARD_E_NO_READERS_AVAILABLE case SCARD_E_NO_READERS_AVAILABLE: return SC_ERROR_NO_READERS_FOUND; +#endif + case SCARD_E_UNKNOWN_READER: + return SC_ERROR_READER_DETACHED; + case SCARD_E_NO_SERVICE: + case SCARD_E_SERVICE_STOPPED: /* If the service is (auto)started, there could be readers later */ return SC_ERROR_NO_READERS_FOUND; case SCARD_E_NO_SMARTCARD: @@ -206,12 +253,18 @@ static int pcsc_internal_transmit(sc_reader_t *reader, struct pcsc_private_data *priv = reader->drv_data; SCARD_IO_REQUEST sSendPci, sRecvPci; DWORD dwSendLength, dwRecvLength; - LONG rv; + LONG rv = SCARD_E_INVALID_VALUE; + int r; SCARDHANDLE card; LOG_FUNC_CALLED(reader->ctx); card = priv->pcsc_card; + // Ensure send buffer fits into a DWORD + if (sendsize > MAXDWORD) + return SC_ERROR_INVALID_ARGUMENTS; + if (*recvsize > MAXDWORD) + return SC_ERROR_INVALID_ARGUMENTS; if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; @@ -220,20 +273,19 @@ static int pcsc_internal_transmit(sc_reader_t *reader, sRecvPci.dwProtocol = opensc_proto_to_pcsc(reader->active_protocol); sRecvPci.cbPciLength = sizeof(sRecvPci); - dwSendLength = sendsize; - dwRecvLength = *recvsize; + dwSendLength = (DWORD)sendsize; + dwRecvLength = (DWORD)*recvsize; if (!control) { rv = priv->gpriv->SCardTransmit(card, &sSendPci, sendbuf, dwSendLength, &sRecvPci, recvbuf, &dwRecvLength); } else { - if (priv->gpriv->SCardControlOLD != NULL) { + if (priv->gpriv->SCardControlOLD) { rv = priv->gpriv->SCardControlOLD(card, sendbuf, dwSendLength, recvbuf, &dwRecvLength); - } - else { - rv = priv->gpriv->SCardControl(card, (DWORD) control, sendbuf, dwSendLength, - recvbuf, dwRecvLength, &dwRecvLength); + } else if (priv->gpriv->SCardControl) { + rv = priv->gpriv->SCardControl(card, (DWORD)control, sendbuf, dwSendLength, + recvbuf, dwRecvLength, &dwRecvLength); } } @@ -242,6 +294,18 @@ static int pcsc_internal_transmit(sc_reader_t *reader, switch (rv) { case SCARD_W_REMOVED_CARD: return SC_ERROR_CARD_REMOVED; + case SCARD_E_INVALID_HANDLE: + case SCARD_E_INVALID_VALUE: + case SCARD_E_READER_UNAVAILABLE: + r = pcsc_connect(reader); + LOG_TEST_RET(reader->ctx, r, "Could not connect to card after reattached reader."); + /* return failure so that upper layers will be notified */ + return SC_ERROR_READER_REATTACHED; + case SCARD_W_RESET_CARD: + r = pcsc_reconnect(reader, SCARD_LEAVE_CARD); + LOG_TEST_RET(reader->ctx, r, "Could not reconnect to card after reattached reader."); + /* return failure so that upper layers will be notified */ + return SC_ERROR_CARD_RESET; default: /* Translate strange errors from card removal to a proper return code */ pcsc_detect_card_presence(reader); @@ -269,7 +333,7 @@ static int pcsc_transmit(sc_reader_t *reader, sc_apdu_t *apdu) * The buffer for the returned data needs to be at least 2 bytes * larger than the expected data length to store SW1 and SW2. */ rsize = rbuflen = apdu->resplen <= 256 ? 258 : apdu->resplen + 2; - rbuf = malloc(rbuflen); + rbuf = malloc(rbuflen); if (rbuf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto out; @@ -281,7 +345,7 @@ static int pcsc_transmit(sc_reader_t *reader, sc_apdu_t *apdu) goto out; if (reader->name) sc_log(reader->ctx, "reader '%s'", reader->name); - sc_apdu_log(reader->ctx, SC_LOG_DEBUG_NORMAL, sbuf, ssize, 1); + sc_apdu_log(reader->ctx, sbuf, ssize, 1); r = pcsc_internal_transmit(reader, sbuf, ssize, rbuf, &rsize, apdu->control); @@ -290,7 +354,8 @@ static int pcsc_transmit(sc_reader_t *reader, sc_apdu_t *apdu) sc_log(reader->ctx, "unable to transmit"); goto out; } - sc_apdu_log(reader->ctx, SC_LOG_DEBUG_NORMAL, rbuf, rsize, 0); + sc_apdu_log(reader->ctx, rbuf, rsize, 0); + APDU_LOG(rbuf, (uint16_t)rsize); /* set response */ r = sc_apdu_set_resp(reader->ctx, apdu, rbuf, rsize); @@ -312,7 +377,7 @@ static int pcsc_transmit(sc_reader_t *reader, sc_apdu_t *apdu) static int refresh_attributes(sc_reader_t *reader) { struct pcsc_private_data *priv = reader->drv_data; - int old_flags = reader->flags; + unsigned long old_flags = reader->flags; DWORD state, prev_state; LONG rv; @@ -321,7 +386,7 @@ static int refresh_attributes(sc_reader_t *reader) if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; - if (priv->reader_state.szReader == NULL) { + if (priv->reader_state.szReader == NULL || reader->flags & SC_READER_REMOVED) { priv->reader_state.szReader = reader->name; priv->reader_state.dwCurrentState = SCARD_STATE_UNAWARE; priv->reader_state.dwEventState = SCARD_STATE_UNAWARE; @@ -333,11 +398,49 @@ static int refresh_attributes(sc_reader_t *reader) if (rv != SCARD_S_SUCCESS) { if (rv == (LONG)SCARD_E_TIMEOUT) { - /* Timeout, no change from previous recorded state. Make sure that - * changed flag is not set. */ + unsigned char atr[SC_MAX_ATR_SIZE]; + + /* Make sure to preserve the CARD_PRESENT flag if the reader was + * reattached and we called the refresh_attributes too recently */ + if (priv->reader_state.dwEventState & SCARD_STATE_PRESENT) { + reader->flags |= SC_READER_CARD_PRESENT; + } + + /* Timeout should denote no change from previous recorded state, + * but check if the card handle is valid */ reader->flags &= ~SC_READER_CARD_CHANGED; + if (priv->pcsc_card != 0) { + DWORD readers_len = 0, cstate = 0, prot, atr_len = SC_MAX_ATR_SIZE; + /* When reader is removed between two subsequent calls to refresh_attributes, + * SCardGetStatusChange does not notice the change, test the card handle with SCardStatus */ + rv = priv->gpriv->SCardStatus(priv->pcsc_card, NULL, &readers_len, &cstate, &prot, atr, &atr_len); + if (rv != (LONG)SCARD_S_SUCCESS) + reader->flags |= SC_READER_CARD_CHANGED; + /* If this happens, card must be reconnected, otherwise SCardGetStatusChange() will still return timeout + * and card handle will be invalid. */ + } + LOG_FUNC_RETURN(reader->ctx, SC_SUCCESS); } + + /* the system could not detect the reader. It means, the previously attached reader is disconnected. */ + if (rv == (LONG)SCARD_E_UNKNOWN_READER +#ifdef SCARD_E_NO_READERS_AVAILABLE + || rv == (LONG)SCARD_E_NO_READERS_AVAILABLE +#endif + || rv == (LONG)SCARD_E_SERVICE_STOPPED) { + /* Set flag when state changes */ + if (reader->flags & SC_READER_REMOVED) { + reader->flags &= ~SC_READER_CARD_CHANGED; + } else { + reader->flags |= SC_READER_CARD_CHANGED; + } + reader->flags &= ~SC_READER_CARD_PRESENT; + reader->flags |= SC_READER_REMOVED; + priv->gpriv->removed_reader = reader; + SC_FUNC_RETURN(reader->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); + } + PCSC_TRACE(reader, "SCardGetStatusChange failed", rv); return pcsc_to_opensc_error(rv); } @@ -353,10 +456,11 @@ static int refresh_attributes(sc_reader_t *reader) * XXX: We'll hit it again, as no readers are removed currently. */ reader->flags &= ~(SC_READER_CARD_PRESENT); - return SC_ERROR_READER_DETACHED; + sc_log(reader->ctx, "Reader unknown: %s", sc_strerror(SC_ERROR_READER_DETACHED)); + SC_FUNC_RETURN(reader->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } - reader->flags &= ~(SC_READER_CARD_CHANGED|SC_READER_CARD_INUSE|SC_READER_CARD_EXCLUSIVE); + reader->flags &= ~(SC_READER_CARD_CHANGED | SC_READER_CARD_INUSE | SC_READER_CARD_EXCLUSIVE); if (state & SCARD_STATE_PRESENT) { reader->flags |= SC_READER_CARD_PRESENT; @@ -364,10 +468,11 @@ static int refresh_attributes(sc_reader_t *reader) if (priv->reader_state.cbAtr > SC_MAX_ATR_SIZE) return SC_ERROR_INTERNAL; - /* Some cards have a different cold (after a powerup) and warm (after a reset) ATR */ + /* Some cards have a different cold (after a powerup) and warm (after a reset) ATR */ if (memcmp(priv->reader_state.rgbAtr, reader->atr.value, priv->reader_state.cbAtr) != 0) { reader->atr.len = priv->reader_state.cbAtr; memcpy(reader->atr.value, priv->reader_state.rgbAtr, reader->atr.len); + APDU_LOG(reader->atr.value, (uint16_t) reader->atr.len); } /* Is the reader in use by some other application ? */ @@ -387,7 +492,7 @@ static int refresh_attributes(sc_reader_t *reader) unsigned char atr[SC_MAX_ATR_SIZE]; rv = priv->gpriv->SCardStatus(priv->pcsc_card, NULL, &readers_len, &cstate, &prot, atr, &atr_len); - if (rv == (LONG)SCARD_W_REMOVED_CARD) + if (rv == (LONG)SCARD_W_REMOVED_CARD || rv == (LONG)SCARD_E_INVALID_VALUE) reader->flags |= SC_READER_CARD_CHANGED; } } else { @@ -395,8 +500,6 @@ static int refresh_attributes(sc_reader_t *reader) } } else { reader->flags &= ~SC_READER_CARD_PRESENT; - if (old_flags & SC_READER_CARD_PRESENT) - reader->flags |= SC_READER_CARD_CHANGED; } sc_log(reader->ctx, "card %s%s", reader->flags & SC_READER_CARD_PRESENT ? "present" : "absent", @@ -413,13 +516,18 @@ static int pcsc_detect_card_presence(sc_reader_t *reader) rv = refresh_attributes(reader); if (rv != SC_SUCCESS) LOG_FUNC_RETURN(reader->ctx, rv); - LOG_FUNC_RETURN(reader->ctx, reader->flags); + + // Return 0 if the card is not present + if (reader->flags & SC_READER_CARD_PRESENT) + LOG_FUNC_RETURN(reader->ctx, (int)reader->flags); + else + LOG_FUNC_RETURN(reader->ctx, 0); } static int check_forced_protocol(sc_reader_t *reader, DWORD *protocol) { scconf_block *atrblock = NULL; - int ok = 0; + int forced = 0; atrblock = _sc_match_atr_block(reader->ctx, NULL, &reader->atr); if (atrblock != NULL) { @@ -428,26 +536,37 @@ static int check_forced_protocol(sc_reader_t *reader, DWORD *protocol) forcestr = scconf_get_str(atrblock, "force_protocol", "unknown"); if (!strcmp(forcestr, "t0")) { *protocol = SCARD_PROTOCOL_T0; - ok = 1; + forced = 1; } else if (!strcmp(forcestr, "t1")) { *protocol = SCARD_PROTOCOL_T1; - ok = 1; + forced = 1; } else if (!strcmp(forcestr, "raw")) { *protocol = SCARD_PROTOCOL_RAW; - ok = 1; + forced = 1; } - if (ok) + if (forced) sc_log(reader->ctx, "force_protocol: %s", forcestr); } - if (!ok && reader->uid.len) { + if (!forced && reader->uid.len) { /* We identify contactless cards by their UID. Communication * defined by ISO/IEC 14443 is identical to T=1. */ *protocol = SCARD_PROTOCOL_T1; - ok = 1; + forced = 1; + } + + if (!forced) { + sc_card_t card; + memset(&card, 0, sizeof card); + card.ctx = reader->ctx; + card.atr = reader->atr; + if (0 <= _sc_match_atr(&card, sc_hsm_atrs, NULL)) { + *protocol = SCARD_PROTOCOL_T1; + forced = 1; + } } - return ok; + return forced; } @@ -481,7 +600,7 @@ static int pcsc_reconnect(sc_reader_t * reader, DWORD action) priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED, protocol, action, &active_proto); - + PCSC_TRACE(reader, "SCardReconnect returned", rv); if (rv != SCARD_S_SUCCESS) { PCSC_TRACE(reader, "SCardReconnect failed", rv); @@ -511,13 +630,14 @@ static void initialize_uid(sc_reader_t *reader) apdu.resplen = sizeof rbuf; if (SC_SUCCESS == pcsc_transmit(reader, &apdu) - && apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + && apdu.sw1 == 0x90 && apdu.sw2 == 0x00 + && 0 < apdu.resplen && apdu.resplen <= SC_MAX_UID_SIZE) { reader->uid.len = apdu.resplen; memcpy(reader->uid.value, apdu.resp, reader->uid.len); - sc_debug_hex(reader->ctx, SC_LOG_DEBUG_NORMAL, "UID", + sc_log_hex(reader->ctx, "UID", reader->uid.value, reader->uid.len); } else { - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "unable to get UID"); + sc_log(reader->ctx, "unable to get UID"); } } } @@ -544,14 +664,6 @@ static int pcsc_connect(sc_reader_t *reader) rv = priv->gpriv->SCardConnect(priv->gpriv->pcsc_ctx, reader->name, priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED, protocol, &card_handle, &active_proto); -#ifdef __APPLE__ - if (rv == (LONG)SCARD_E_SHARING_VIOLATION) { - sleep(1); /* Try again to compete with Tokend probes */ - rv = priv->gpriv->SCardConnect(priv->gpriv->pcsc_ctx, reader->name, - priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED, - protocol, &card_handle, &active_proto); - } -#endif if (rv != SCARD_S_SUCCESS) { PCSC_TRACE(reader, "SCardConnect failed", rv); return pcsc_to_opensc_error(rv); @@ -595,8 +707,12 @@ static int pcsc_disconnect(sc_reader_t * reader) if (!priv->gpriv->cardmod && !(reader->ctx->flags & SC_CTX_FLAG_TERMINATE)) { LONG rv = priv->gpriv->SCardDisconnect(priv->pcsc_card, priv->gpriv->disconnect_action); PCSC_TRACE(reader, "SCardDisconnect returned", rv); + if (rv == SCARD_S_SUCCESS) { + // Card was successfully disconnected, reset the card handle + priv->pcsc_card = 0; + } } - reader->flags = 0; + reader->flags &= SC_READER_REMOVED; return SC_SUCCESS; } @@ -621,25 +737,17 @@ static int pcsc_lock(sc_reader_t *reader) PCSC_TRACE(reader, "SCardBeginTransaction returned", rv); switch (rv) { + case SCARD_E_INVALID_VALUE: + /* This is returned in case of the same reader was re-attached */ case SCARD_E_INVALID_HANDLE: case SCARD_E_READER_UNAVAILABLE: r = pcsc_connect(reader); - if (r != SC_SUCCESS) { - sc_log(reader->ctx, "pcsc_connect failed (%d)", - r); - return r; - } + LOG_TEST_RET(reader->ctx, r, "Could not connect to card after reattached reader."); /* return failure so that upper layers will be notified and try to lock again */ return SC_ERROR_READER_REATTACHED; case SCARD_W_RESET_CARD: - /* try to reconnect if the card was reset by some other application */ - PCSC_TRACE(reader, "SCardBeginTransaction calling pcsc_reconnect", rv); r = pcsc_reconnect(reader, SCARD_LEAVE_CARD); - if (r != SC_SUCCESS) { - sc_log(reader->ctx, - "pcsc_reconnect failed (%d)", r); - return r; - } + LOG_TEST_RET(reader->ctx, r, "Could not reconnect to card after reattached reader."); /* return failure so that upper layers will be notified and try to lock again */ return SC_ERROR_CARD_RESET; case SCARD_S_SUCCESS: @@ -703,9 +811,9 @@ static int pcsc_reset(sc_reader_t *reader, int do_cold_reset) return r; } - static int pcsc_cancel(sc_context_t *ctx) { + size_t i; LONG rv = SCARD_S_SUCCESS; struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *)ctx->reader_drv_data; @@ -714,18 +822,16 @@ static int pcsc_cancel(sc_context_t *ctx) if (ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; -#ifndef _WIN32 - if (gpriv->pcsc_wait_ctx != -1) { - rv = gpriv->SCardCancel(gpriv->pcsc_wait_ctx); - if (rv == SCARD_S_SUCCESS) { - /* Also close and clear the waiting context */ - rv = gpriv->SCardReleaseContext(gpriv->pcsc_wait_ctx); - gpriv->pcsc_wait_ctx = -1; + for (i = 0; i < ARRAY_SIZE(gpriv->pcsc_wait_ctx); i++) { + if (gpriv->pcsc_wait_ctx[i] != (SCARDCONTEXT)-1) { + rv = gpriv->SCardCancel(gpriv->pcsc_wait_ctx[i]); + if (rv == SCARD_S_SUCCESS) { + /* Also close and clear the waiting context */ + rv = gpriv->SCardReleaseContext(gpriv->pcsc_wait_ctx[i]); + gpriv->pcsc_wait_ctx[i] = -1; + } } } -#else - rv = gpriv->SCardCancel(gpriv->pcsc_ctx); -#endif if (rv != SCARD_S_SUCCESS) { PCSC_LOG(ctx, "SCardCancel/SCardReleaseContext failed", rv); return pcsc_to_opensc_error(rv); @@ -747,7 +853,7 @@ static int pcsc_init(sc_context_t *ctx) struct pcsc_global_private_data *gpriv; scconf_block *conf_block = NULL; int ret = SC_ERROR_INTERNAL; - + size_t i; gpriv = calloc(1, sizeof(struct pcsc_global_private_data)); if (gpriv == NULL) { @@ -762,13 +868,16 @@ static int pcsc_init(sc_context_t *ctx) /* PC/SC Defaults */ gpriv->provider_library = DEFAULT_PCSC_PROVIDER; gpriv->connect_exclusive = 0; - gpriv->disconnect_action = SCARD_RESET_CARD; + gpriv->disconnect_action = SCARD_LEAVE_CARD; gpriv->transaction_end_action = SCARD_LEAVE_CARD; gpriv->reconnect_action = SCARD_LEAVE_CARD; gpriv->enable_pinpad = 1; + gpriv->fixed_pinlength = 0; gpriv->enable_pace = 1; gpriv->pcsc_ctx = -1; - gpriv->pcsc_wait_ctx = -1; + for (i = 0; i < ARRAY_SIZE(gpriv->pcsc_wait_ctx); i++) { + gpriv->pcsc_wait_ctx[i] = -1; + } /* max send/receive sizes: if exist in configuration these options overwrite * the values by default and values declared by reader */ gpriv->force_max_send_size = 0; @@ -781,19 +890,21 @@ static int pcsc_init(sc_context_t *ctx) gpriv->connect_exclusive = scconf_get_bool(conf_block, "connect_exclusive", gpriv->connect_exclusive); gpriv->disconnect_action = - pcsc_reset_action(scconf_get_str(conf_block, "disconnect_action", "reset")); + pcsc_reset_action(scconf_get_str(conf_block, "disconnect_action", "leave")); gpriv->transaction_end_action = pcsc_reset_action(scconf_get_str(conf_block, "transaction_end_action", "leave")); gpriv->reconnect_action = pcsc_reset_action(scconf_get_str(conf_block, "reconnect_action", "leave")); gpriv->enable_pinpad = scconf_get_bool(conf_block, "enable_pinpad", gpriv->enable_pinpad); + gpriv->fixed_pinlength = scconf_get_bool(conf_block, "fixed_pinlength", + gpriv->fixed_pinlength); gpriv->enable_pace = scconf_get_bool(conf_block, "enable_pace", gpriv->enable_pace); gpriv->force_max_send_size = scconf_get_int(conf_block, - "max_send_size", gpriv->force_max_send_size); + "max_send_size", (int)gpriv->force_max_send_size); gpriv->force_max_recv_size = scconf_get_int(conf_block, - "max_recv_size", gpriv->force_max_recv_size); + "max_recv_size", (int)gpriv->force_max_recv_size); } if (gpriv->cardmod) { @@ -933,7 +1044,7 @@ static unsigned long part10_detect_pace_capabilities(sc_reader_t *reader, SCARDH if (!priv) goto err; - if (priv->pace_ioctl && priv->gpriv) { + if (priv->pace_ioctl && priv->gpriv && priv->gpriv->SCardControl) { if (SCARD_S_SUCCESS != priv->gpriv->SCardControl(card_handle, priv->pace_ioctl, pace_capabilities_buf, sizeof pace_capabilities_buf, rbuf, sizeof(rbuf), @@ -968,7 +1079,7 @@ static unsigned long part10_detect_pace_capabilities(sc_reader_t *reader, SCARDH } static int -part10_find_property_by_tag(unsigned char buffer[], int length, +part10_find_property_by_tag(unsigned char buffer[], DWORD length, int tag_searched); /** * @brief Detects reader's maximum data size @@ -992,7 +1103,7 @@ static size_t part10_detect_max_data(sc_reader_t *reader, SCARDHANDLE card_handl if (!priv) goto err; - if (priv->get_tlv_properties && priv->gpriv) { + if (priv->get_tlv_properties && priv->gpriv && priv->gpriv->SCardControl) { if (SCARD_S_SUCCESS != priv->gpriv->SCardControl(card_handle, priv->get_tlv_properties, NULL, 0, rbuf, sizeof(rbuf), &rcount)) { sc_log(reader->ctx, "PC/SC v2 part 10: Get TLV properties failed!"); @@ -1025,7 +1136,7 @@ static int part10_get_vendor_product(struct sc_reader *reader, if (!priv) return SC_ERROR_INVALID_ARGUMENTS; - if (priv->get_tlv_properties && priv->gpriv) { + if (priv->get_tlv_properties && priv->gpriv && priv->gpriv->SCardControl) { if (SCARD_S_SUCCESS != priv->gpriv->SCardControl(card_handle, priv->get_tlv_properties, NULL, 0, rbuf, sizeof(rbuf), &rcount)) { @@ -1054,56 +1165,53 @@ static void detect_reader_features(sc_reader_t *reader, SCARDHANDLE card_handle) sc_context_t *ctx = reader->ctx; struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *) ctx->reader_drv_data; struct pcsc_private_data *priv = reader->drv_data; - u8 feature_buf[256], rbuf[SC_MAX_APDU_BUFFER_SIZE]; - DWORD rcount, feature_len, i; - PCSC_TLV_STRUCTURE *pcsc_tlv; + DWORD rcount = 0, i; + u8 buf[256]; LONG rv; const char *log_disabled = "but it's disabled in configuration file"; + int id_vendor = 0, id_product = 0; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Requesting reader features ... "); - if (gpriv->SCardControl == NULL) - return; - - rv = gpriv->SCardControl(card_handle, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0, feature_buf, sizeof(feature_buf), &feature_len); - if (rv != (LONG)SCARD_S_SUCCESS) { - PCSC_TRACE(reader, "SCardControl failed", rv); - return; + if (gpriv->SCardControl) { + rv = gpriv->SCardControl(card_handle, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0, buf, sizeof(buf), &rcount); + if (rv != SCARD_S_SUCCESS) { + PCSC_TRACE(reader, "SCardControl failed", rv); + return; + } } - if ((feature_len % sizeof(PCSC_TLV_STRUCTURE)) != 0) { + if ((rcount % sizeof(PCSC_TLV_STRUCTURE)) != 0 + || rcount > sizeof buf) { sc_log(ctx, "Inconsistent TLV from reader!"); return; } - /* get the number of elements instead of the complete size */ - feature_len /= sizeof(PCSC_TLV_STRUCTURE); - - pcsc_tlv = (PCSC_TLV_STRUCTURE *)feature_buf; - for (i = 0; i < feature_len; i++) { - sc_log(ctx, "Reader feature %02x found", pcsc_tlv[i].tag); - if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_DIRECT) { - priv->verify_ioctl = ntohl(pcsc_tlv[i].value); - } else if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_START) { - priv->verify_ioctl_start = ntohl(pcsc_tlv[i].value); - } else if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_FINISH) { - priv->verify_ioctl_finish = ntohl(pcsc_tlv[i].value); - } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_DIRECT) { - priv->modify_ioctl = ntohl(pcsc_tlv[i].value); - } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_START) { - priv->modify_ioctl_start = ntohl(pcsc_tlv[i].value); - } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_FINISH) { - priv->modify_ioctl_finish = ntohl(pcsc_tlv[i].value); - } else if (pcsc_tlv[i].tag == FEATURE_IFD_PIN_PROPERTIES) { - priv->pin_properties_ioctl = ntohl(pcsc_tlv[i].value); - } else if (pcsc_tlv[i].tag == FEATURE_GET_TLV_PROPERTIES) { - priv->get_tlv_properties = ntohl(pcsc_tlv[i].value); - } else if (pcsc_tlv[i].tag == FEATURE_EXECUTE_PACE) { - priv->pace_ioctl = ntohl(pcsc_tlv[i].value); + for (i = 0; i < rcount; i += sizeof(PCSC_TLV_STRUCTURE)) { + PCSC_TLV_STRUCTURE *pcsc_tlv = (PCSC_TLV_STRUCTURE *)(buf+i); + sc_log(ctx, "Reader feature %02x found", pcsc_tlv->tag); + if (pcsc_tlv->tag == FEATURE_VERIFY_PIN_DIRECT) { + priv->verify_ioctl = ntohl(pcsc_tlv->value); + } else if (pcsc_tlv->tag == FEATURE_VERIFY_PIN_START) { + priv->verify_ioctl_start = ntohl(pcsc_tlv->value); + } else if (pcsc_tlv->tag == FEATURE_VERIFY_PIN_FINISH) { + priv->verify_ioctl_finish = ntohl(pcsc_tlv->value); + } else if (pcsc_tlv->tag == FEATURE_MODIFY_PIN_DIRECT) { + priv->modify_ioctl = ntohl(pcsc_tlv->value); + } else if (pcsc_tlv->tag == FEATURE_MODIFY_PIN_START) { + priv->modify_ioctl_start = ntohl(pcsc_tlv->value); + } else if (pcsc_tlv->tag == FEATURE_MODIFY_PIN_FINISH) { + priv->modify_ioctl_finish = ntohl(pcsc_tlv->value); + } else if (pcsc_tlv->tag == FEATURE_IFD_PIN_PROPERTIES) { + priv->pin_properties_ioctl = ntohl(pcsc_tlv->value); + } else if (pcsc_tlv->tag == FEATURE_GET_TLV_PROPERTIES) { + priv->get_tlv_properties = ntohl(pcsc_tlv->value); + } else if (pcsc_tlv->tag == FEATURE_EXECUTE_PACE) { + priv->pace_ioctl = ntohl(pcsc_tlv->value); } else { - sc_log(ctx, "Reader feature %02x is not supported", pcsc_tlv[i].tag); + sc_log(ctx, "Reader feature %02x is not supported", pcsc_tlv->tag); } } @@ -1128,14 +1236,26 @@ static void detect_reader_features(sc_reader_t *reader, SCARDHANDLE card_handle) } } + /* Some readers claim to have PinPAD support even if they have not */ + if ((reader->capabilities & SC_READER_CAP_PIN_PAD) && + part10_get_vendor_product(reader, card_handle, &id_vendor, &id_product) == SC_SUCCESS) { + /* HID Global OMNIKEY 3x21/6121 Smart Card Reader, fixed in libccid 1.4.29 (remove when last supported OS is using 1.4.29) */ + if ((id_vendor == 0x076B && id_product == 0x3031) || + (id_vendor == 0x076B && id_product == 0x6632)) { + sc_log(ctx, "%s is not pinpad reader, ignoring", reader->name); + reader->capabilities &= ~SC_READER_CAP_PIN_PAD; + } + } + /* Detect display */ - if (priv->pin_properties_ioctl) { - rcount = sizeof(rbuf); - rv = gpriv->SCardControl(card_handle, priv->pin_properties_ioctl, NULL, 0, rbuf, sizeof(rbuf), &rcount); + if (priv->pin_properties_ioctl && gpriv->SCardControl) { + rcount = sizeof(buf); + rv = gpriv->SCardControl(card_handle, priv->pin_properties_ioctl, + NULL, 0, buf, sizeof(buf), &rcount); if (rv == SCARD_S_SUCCESS) { #ifdef PIN_PROPERTIES_v5 if (rcount == sizeof(PIN_PROPERTIES_STRUCTURE_v5)) { - PIN_PROPERTIES_STRUCTURE_v5 *caps = (PIN_PROPERTIES_STRUCTURE_v5 *)rbuf; + PIN_PROPERTIES_STRUCTURE_v5 *caps = (PIN_PROPERTIES_STRUCTURE_v5 *) &buf; if (caps->wLcdLayout > 0) { sc_log(ctx, "Reader has a display: %04X", caps->wLcdLayout); reader->capabilities |= SC_READER_CAP_DISPLAY; @@ -1144,16 +1264,16 @@ static void detect_reader_features(sc_reader_t *reader, SCARDHANDLE card_handle) } #endif if (rcount == sizeof(PIN_PROPERTIES_STRUCTURE)) { - PIN_PROPERTIES_STRUCTURE *caps = (PIN_PROPERTIES_STRUCTURE *)rbuf; + PIN_PROPERTIES_STRUCTURE *caps = (PIN_PROPERTIES_STRUCTURE *) buf; if (caps->wLcdLayout > 0) { sc_log(ctx, "Reader has a display: %04X", caps->wLcdLayout); reader->capabilities |= SC_READER_CAP_DISPLAY; } - else { + else { sc_log(ctx, "Reader does not have a display."); } } - else { + else { sc_log(ctx, "Returned PIN properties structure has bad length (%lu/%"SC_FORMAT_LEN_SIZE_T"u)", (unsigned long)rcount, @@ -1175,55 +1295,68 @@ static void detect_reader_features(sc_reader_t *reader, SCARDHANDLE card_handle) } } - /* max send/receive sizes: with default values only short APDU supported */ - reader->max_send_size = priv->gpriv->force_max_send_size ? - priv->gpriv->force_max_send_size : - SC_READER_SHORT_APDU_MAX_SEND_SIZE; - reader->max_recv_size = priv->gpriv->force_max_recv_size ? - priv->gpriv->force_max_recv_size : - SC_READER_SHORT_APDU_MAX_RECV_SIZE; + size_t max_send_size = 0; + size_t max_recv_size = 0; if (priv->get_tlv_properties) { /* Try to set reader max_send_size and max_recv_size based on * detected max_data */ - int max_data = part10_detect_max_data(reader, card_handle); - - if (max_data > 0) { - sc_log(ctx, "Reader supports transceiving %d bytes of data", - max_data); - if (priv->gpriv->force_max_send_size) - reader->max_send_size = max_data; - else - sc_log(ctx, "Sending is limited to %"SC_FORMAT_LEN_SIZE_T"u bytes of data" - " in configuration file", reader->max_send_size); - if (!priv->gpriv->force_max_recv_size) - reader->max_recv_size = max_data; - else - sc_log(ctx, "Receiving is limited to %"SC_FORMAT_LEN_SIZE_T"u bytes of data" - " in configuration file", reader->max_recv_size); - } else { - sc_log(ctx, "Assuming that the reader supports transceiving " - "short length APDUs only"); - } + max_send_size = max_recv_size = part10_detect_max_data(reader, card_handle); /* debug the product and vendor ID of the reader */ part10_get_vendor_product(reader, card_handle, NULL, NULL); } + else { + /* Try to set default limits based on device name */ + if (!strncmp("REINER SCT cyberJack", reader->name, 20)) { + max_send_size = 1014; + max_recv_size = 1014; + } + else if (!strncmp("Secure Flash Card", reader->name, 17)) { + max_send_size = 478; + max_recv_size = 506; + } + } - if(gpriv->SCardGetAttrib != NULL) { - rcount = sizeof(rbuf); + if (max_send_size > 0) { + sc_log(ctx, "Reader supports sending %"SC_FORMAT_LEN_SIZE_T"u bytes of data", + max_send_size); + if (!priv->gpriv->force_max_send_size) + reader->max_send_size = max_send_size; + else + sc_log(ctx, "Sending is limited to %"SC_FORMAT_LEN_SIZE_T"u bytes of data" + " in configuration file", reader->max_send_size); + } else { + sc_log(ctx, "Assuming that the reader supports sending " + "short length APDUs only"); + } + + if (max_recv_size > 0) { + sc_log(ctx, "Reader supports receiving %"SC_FORMAT_LEN_SIZE_T"u bytes of data", + max_recv_size); + if (!priv->gpriv->force_max_recv_size) + reader->max_recv_size = max_recv_size; + else + sc_log(ctx, "Receiving is limited to %"SC_FORMAT_LEN_SIZE_T"u bytes of data" + " in configuration file", reader->max_recv_size); + } else { + sc_log(ctx, "Assuming that the reader supports receiving " + "short length APDUs only"); + } + + if (gpriv->SCardGetAttrib != NULL) { + rcount = sizeof(buf); if (gpriv->SCardGetAttrib(card_handle, SCARD_ATTR_VENDOR_NAME, - rbuf, &rcount) == SCARD_S_SUCCESS + buf, &rcount) == SCARD_S_SUCCESS && rcount > 0) { /* add NUL termination, just in case... */ - rbuf[(sizeof rbuf)-1] = '\0'; - reader->vendor = strdup((char *) rbuf); + buf[(sizeof buf)-1] = '\0'; + reader->vendor = strdup((char *) buf); } - rcount = sizeof rbuf; - if(gpriv->SCardGetAttrib(card_handle, SCARD_ATTR_VENDOR_IFD_VERSION, - rbuf, &rcount) == SCARD_S_SUCCESS - && rcount == 4) { - i = *(DWORD *) rbuf; + rcount = sizeof i; + if (gpriv->SCardGetAttrib(card_handle, SCARD_ATTR_VENDOR_IFD_VERSION, + (u8 *) &i, &rcount) == SCARD_S_SUCCESS + && rcount == sizeof i) { reader->version_major = (i >> 24) & 0xFF; reader->version_minor = (i >> 16) & 0xFF; } @@ -1231,7 +1364,7 @@ static void detect_reader_features(sc_reader_t *reader, SCARDHANDLE card_handle) } int pcsc_add_reader(sc_context_t *ctx, - char *reader_name, size_t reader_name_len, + char *reader_name, size_t reader_name_len, sc_reader_t **out_reader) { int ret = SC_ERROR_INTERNAL; @@ -1261,9 +1394,19 @@ int pcsc_add_reader(sc_context_t *ctx, goto err1; } + /* max send/receive sizes: with default values only short APDU supported */ + reader->max_send_size = priv->gpriv->force_max_send_size ? + priv->gpriv->force_max_send_size : + SC_READER_SHORT_APDU_MAX_SEND_SIZE; + reader->max_recv_size = priv->gpriv->force_max_recv_size ? + priv->gpriv->force_max_recv_size : + SC_READER_SHORT_APDU_MAX_RECV_SIZE; + ret = _sc_add_reader(ctx, reader); - refresh_attributes(reader); + if (ret == SC_SUCCESS) { + refresh_attributes(reader); + } err1: return ret; @@ -1278,7 +1421,7 @@ static int pcsc_detect_readers(sc_context_t *ctx) char *reader_buf = NULL, *reader_name; const char *mszGroups = NULL; int ret = SC_ERROR_INTERNAL; - size_t i; + unsigned int i; LOG_FUNC_CALLED(ctx); @@ -1293,13 +1436,9 @@ static int pcsc_detect_readers(sc_context_t *ctx) goto out; } - /* temporarily mark all readers as removed */ - for (i=0;i < sc_ctx_get_reader_count(ctx);i++) { - sc_reader_t *reader = sc_ctx_get_reader(ctx, i); - reader->flags |= SC_READER_REMOVED; - } - sc_log(ctx, "Probing PC/SC readers"); + gpriv->attached_reader = NULL; + gpriv->removed_reader = NULL; do { if (gpriv->pcsc_ctx == (SCARDCONTEXT)-1) { @@ -1312,10 +1451,35 @@ static int pcsc_detect_readers(sc_context_t *ctx) } else { rv = gpriv->SCardListReaders(gpriv->pcsc_ctx, NULL, NULL, (LPDWORD) &reader_buf_size); - if (rv == (LONG)SCARD_E_NO_SERVICE) { + + /* + * All readers have disappeared, so mark them as + * such so we don't keep polling them over and over. + */ + if (rv == (LONG)SCARD_E_NO_SERVICE +#ifdef SCARD_E_NO_READERS_AVAILABLE + || rv == (LONG)SCARD_E_NO_READERS_AVAILABLE +#endif + || rv == (LONG)SCARD_E_SERVICE_STOPPED) { + for (i = 0; i < sc_ctx_get_reader_count(ctx); i++) { + sc_reader_t *reader = sc_ctx_get_reader(ctx, i); + + if (!reader) { + ret = SC_ERROR_INTERNAL; + goto out; + } + + reader->flags |= SC_READER_REMOVED; + gpriv->removed_reader = reader; + } + } + + if ((rv == (LONG)SCARD_E_NO_SERVICE) || (rv == (LONG)SCARD_E_SERVICE_STOPPED)) { gpriv->SCardReleaseContext(gpriv->pcsc_ctx); - gpriv->pcsc_ctx = 0; - gpriv->pcsc_wait_ctx = -1; + gpriv->pcsc_ctx = -1; + for (i = 0; i < ARRAY_SIZE(gpriv->pcsc_wait_ctx); i++) { + gpriv->pcsc_wait_ctx[i] = -1; + } /* reconnecting below may may restart PC/SC service */ rv = SCARD_E_INVALID_HANDLE; } @@ -1331,6 +1495,7 @@ static int pcsc_detect_readers(sc_context_t *ctx) rv = gpriv->SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &gpriv->pcsc_ctx); if (rv != SCARD_S_SUCCESS) { + gpriv->pcsc_ctx = -1; PCSC_LOG(ctx, "SCardEstablishContext failed", rv); ret = pcsc_to_opensc_error(rv); goto out; @@ -1341,7 +1506,8 @@ static int pcsc_detect_readers(sc_context_t *ctx) } } while (rv != SCARD_S_SUCCESS); - reader_buf = malloc(sizeof(char) * reader_buf_size); + /* The +2 below is to make sure we have zero terminators, in case we get invalid data */ + reader_buf = calloc(reader_buf_size+2, sizeof(char)); if (!reader_buf) { ret = SC_ERROR_OUT_OF_MEMORY; goto out; @@ -1354,34 +1520,55 @@ static int pcsc_detect_readers(sc_context_t *ctx) goto out; } - for (reader_name = reader_buf; *reader_name != '\x0'; - reader_name += strlen(reader_name) + 1) { - sc_reader_t *reader = NULL, *old_reader = NULL; - struct pcsc_private_data *priv = NULL; - int found = 0; + /* check if existing readers were returned in the list */ + for (i = 0; i < sc_ctx_get_reader_count(ctx); i++) { + sc_reader_t *reader = sc_ctx_get_reader(ctx, i); - for (i=0;i < sc_ctx_get_reader_count(ctx) && !found;i++) { - old_reader = sc_ctx_get_reader(ctx, i); - if (old_reader == NULL) { - ret = SC_ERROR_INTERNAL; - goto out; - } - if (!strcmp(old_reader->name, reader_name)) { - found = 1; + if (!reader) { + ret = SC_ERROR_INTERNAL; + goto out; + } + + for (reader_name = reader_buf; *reader_name != '\x0'; + reader_name += strlen(reader_name) + 1) { + if (!strcmp(reader->name, reader_name)) { + if (reader->flags & SC_READER_REMOVED) { + reader->flags &= ~SC_READER_REMOVED; + gpriv->attached_reader = reader; + refresh_attributes(reader); + } + break; } } - /* Reader already available, skip */ - if (found) { - old_reader->flags &= ~SC_READER_REMOVED; - continue; + if (*reader_name != '\x0') { + /* existing reader found; remove it from the list */ + char *next_reader_name = reader_name + strlen(reader_name) + 1; + + memmove(reader_name, next_reader_name, + (reader_buf + reader_buf_size) - next_reader_name); + reader_buf_size -= (next_reader_name - reader_name); + } else { + if (!(reader->flags & SC_READER_REMOVED)) { + /* existing reader not found */ + reader->flags |= SC_READER_REMOVED; + gpriv->removed_reader = reader; + } } + } + + /* add readers remaining in the list */ + for (reader_name = reader_buf; *reader_name != '\x0'; + reader_name += strlen(reader_name) + 1) { + sc_reader_t *reader = NULL; + struct pcsc_private_data *priv = NULL; ret = pcsc_add_reader(ctx, reader_name, strlen(reader_name), &reader); if (ret != SC_SUCCESS) { _sc_delete_reader(ctx, reader); continue; } + gpriv->attached_reader = reader; /* check for pinpad support early, to allow opensc-tool -l display accurate information */ priv = reader->drv_data; @@ -1400,7 +1587,7 @@ static int pcsc_detect_readers(sc_context_t *ctx) } if (rv == (LONG)SCARD_E_SHARING_VIOLATION) { /* Assume that there is a card in the reader in shared mode if - * direct communcation failed */ + * direct communication failed */ rv = gpriv->SCardConnect(gpriv->pcsc_ctx, reader->name, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, &card_handle, @@ -1423,83 +1610,171 @@ static int pcsc_detect_readers(sc_context_t *ctx) LOG_FUNC_RETURN(ctx, ret); } +struct pcsc_reader_states { + size_t pcsc_wait_ctx_index; + SCARD_READERSTATE *reader_states; +}; + +static void +pcsc_reader_states_free(sc_context_t *ctx, struct pcsc_reader_states *states) +{ + if (states) { + if (ctx) { + struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *)ctx->reader_drv_data; + if (states->pcsc_wait_ctx_index < ARRAY_SIZE(gpriv->pcsc_wait_ctx)) { + gpriv->SCardReleaseContext(gpriv->pcsc_wait_ctx[states->pcsc_wait_ctx_index]); + gpriv->pcsc_wait_ctx[states->pcsc_wait_ctx_index] = -1; + } + } + free(states->reader_states); + free(states); + } +} /* Wait for an event to occur. */ +/** Summary Table + +| PC/SC State Change | Condition | Returned Event | Return Code | +|------------------------------|----------------------------|-----------------------------------------------------------------|-----------------------------| +| Card inserted | `PRESENT ↑` | `SC_EVENT_CARD_INSERTED` | `SC_SUCCESS` | +| Card removed | `PRESENT ↓` | `SC_EVENT_CARD_REMOVED` | `SC_SUCCESS` | +| Reader attached | `UNKNOWN ↓` | `SC_EVENT_READER_ATTACHED` | `SC_SUCCESS` | +| Reader detached | `UNKNOWN ↑` or `IGNORE ↑` | `SC_EVENT_READER_DETACHED` | `SC_SUCCESS` | +| PnP hotplug attach | via pseudo-reader | `SC_EVENT_READER_ATTACHED` (+ optional `SC_EVENT_CARD_INSERTED`) | `SC_SUCCESS` | +| PnP hotplug detach (Windows) | via pseudo-reader | `SC_EVENT_READER_DETACHED` | `SC_SUCCESS` | +| Timeout | `SCARD_E_TIMEOUT` | none | `SC_ERROR_EVENT_TIMEOUT` | +| Cancel | `SCARD_E_CANCELLED` | none | `SC_ERROR_EVENT_TIMEOUT` | +| PC/SC error | `rv != SCARD_S_SUCCESS` | none | `pcsc_to_opensc_error(rv)` | +| No readers | `num_watch == 0` | none | `SC_ERROR_NO_READERS_FOUND` | + */ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_reader_t **event_reader, unsigned int *event, int timeout, void **reader_states) { struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *)ctx->reader_drv_data; LONG rv; - SCARD_READERSTATE *rgReaderStates; - size_t i; - unsigned int num_watch; - int r = SC_ERROR_INTERNAL; + unsigned int num_watch, count, i; + int r = SC_ERROR_INTERNAL, detect_readers = 0, detected_hotplug = 0; DWORD dwtimeout; + struct pcsc_reader_states *states; LOG_FUNC_CALLED(ctx); - if (!event_reader && !event && reader_states) { + if (!event_reader && !event && reader_states) { + /* delete reader states and release all associated resources */ sc_log(ctx, "free allocated reader states"); - free(*reader_states); + pcsc_reader_states_free(ctx, *reader_states); *reader_states = NULL; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } if (reader_states == NULL || *reader_states == NULL) { - rgReaderStates = calloc(sc_ctx_get_reader_count(ctx) + 2, sizeof(SCARD_READERSTATE)); - if (!rgReaderStates) + /* initialize new reader states */ + states = calloc(1, sizeof *states); + if (!states) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + /* find an empty slot for the new listener */ + for (states->pcsc_wait_ctx_index = 0; states->pcsc_wait_ctx_index < ARRAY_SIZE(gpriv->pcsc_wait_ctx); states->pcsc_wait_ctx_index++) { + if (gpriv->pcsc_wait_ctx[states->pcsc_wait_ctx_index] == (SCARDCONTEXT)-1) + /* empty slot found */ + break; + } + if (states->pcsc_wait_ctx_index == ARRAY_SIZE(gpriv->pcsc_wait_ctx)) { + sc_log(ctx, "too many listeners active already"); + r = SC_ERROR_INTERNAL; + goto out; + } else { + sc_log(ctx, "PC/SC event listener %" SC_FORMAT_LEN_SIZE_T "d", states->pcsc_wait_ctx_index); + } + + count = sc_ctx_get_reader_count(ctx); + + states->reader_states = calloc(count + 2, sizeof(SCARD_READERSTATE)); + if (!states->reader_states) { + r = SC_ERROR_NOT_ENOUGH_MEMORY; + goto out; + } + /* Find out the current status */ - num_watch = sc_ctx_get_reader_count(ctx); - sc_log(ctx, "Trying to watch %d readers", num_watch); - for (i = 0; i < num_watch; i++) { - rgReaderStates[i].szReader = sc_ctx_get_reader(ctx, i)->name; - rgReaderStates[i].dwCurrentState = SCARD_STATE_UNAWARE; - rgReaderStates[i].dwEventState = SCARD_STATE_UNAWARE; - } -#ifndef __APPLE__ /* OS X 10.6.2 does not support PnP notification */ - if (event_mask & SC_EVENT_READER_ATTACHED) { - rgReaderStates[i].szReader = "\\\\?PnP?\\Notification"; - rgReaderStates[i].dwCurrentState = SCARD_STATE_UNAWARE; - rgReaderStates[i].dwEventState = SCARD_STATE_UNAWARE; + num_watch = 0; + for (i = 0; i < count; i++) { + sc_reader_t *reader = sc_ctx_get_reader(ctx, i); + if (reader->flags & SC_READER_REMOVED) + continue; + struct pcsc_private_data *priv = reader->drv_data; + states->reader_states[num_watch].szReader = reader->name; + if (priv->reader_state.szReader == NULL) { + states->reader_states[num_watch].dwCurrentState = SCARD_STATE_UNAWARE; + } else { + states->reader_states[num_watch].dwCurrentState = priv->reader_state.dwEventState; + } + states->reader_states[num_watch].dwEventState = SCARD_STATE_UNAWARE; num_watch++; } + sc_log(ctx, "Trying to watch %d reader%s", num_watch, num_watch == 1 ? "" : "s"); + if (event_mask & SC_EVENT_READER_ATTACHED) { +#ifdef __APPLE__ + /* OS X 10.6.2 - 10.12.6 do not support PnP notification */ + sc_log(ctx, "PnP notification not supported"); + /* Always check on new readers as if a hotplug + * event was detected. This overwrites a + * SC_ERROR_EVENT_TIMEOUT if a new reader is + * detected with SC_SUCCESS. */ + detect_readers = 1; + detected_hotplug = 1; +#else + states->reader_states[num_watch].szReader = "\\\\?PnP?\\Notification"; + states->reader_states[num_watch].dwCurrentState = SCARD_STATE_UNAWARE; +#ifdef _WIN32 + /* Windows expects number of readers in HiWord of dwCurrentState. + * See https://stackoverflow.com/questions/16370909. */ + states->reader_states[num_watch].dwCurrentState |= (num_watch << 16); +#endif + states->reader_states[num_watch].dwEventState = SCARD_STATE_UNAWARE; + num_watch++; + sc_log(ctx, "Trying to detect new readers"); #endif + } + } else { + /* reader states already initialized */ + states = *reader_states; + + if (states->pcsc_wait_ctx_index >= ARRAY_SIZE(gpriv->pcsc_wait_ctx)) { + sc_log(ctx, "PC/SC event listener %" SC_FORMAT_LEN_SIZE_T "d not available", states->pcsc_wait_ctx_index); + r = SC_ERROR_INTERNAL; + goto out; + } + + for (num_watch = 0; states->reader_states[num_watch].szReader; num_watch++) + sc_log(ctx, "reuse reader '%s'", states->reader_states[num_watch].szReader); } - else { - rgReaderStates = (SCARD_READERSTATE *)(*reader_states); - for (num_watch = 0; rgReaderStates[num_watch].szReader; num_watch++) - sc_log(ctx, "re-use reader '%s'", rgReaderStates[num_watch].szReader); - } -#ifndef _WIN32 - /* Establish a new context, assuming that it is called from a different thread with pcsc-lite */ - if (gpriv->pcsc_wait_ctx == -1) { - rv = gpriv->SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &gpriv->pcsc_wait_ctx); + + if (gpriv->pcsc_wait_ctx[states->pcsc_wait_ctx_index] == (SCARDCONTEXT)-1) { + rv = gpriv->SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &gpriv->pcsc_wait_ctx[states->pcsc_wait_ctx_index]); if (rv != SCARD_S_SUCCESS) { + gpriv->pcsc_wait_ctx[states->pcsc_wait_ctx_index] = -1; PCSC_LOG(ctx, "SCardEstablishContext(wait) failed", rv); r = pcsc_to_opensc_error(rv); goto out; } } -#else - gpriv->pcsc_wait_ctx = gpriv->pcsc_ctx; -#endif - if (!event_reader || !event) - { + + if (!event_reader || !event) { r = SC_ERROR_INTERNAL; goto out; } + *event_reader = NULL; + *event = 0; + if (num_watch == 0) { - sc_log(ctx, "No readers available, PnP notification not supported"); - *event_reader = NULL; + sc_log(ctx, "No readers available to be watched"); r = SC_ERROR_NO_READERS_FOUND; goto out; } - rv = gpriv->SCardGetStatusChange(gpriv->pcsc_wait_ctx, 0, rgReaderStates, num_watch); + rv = gpriv->SCardGetStatusChange(gpriv->pcsc_wait_ctx[states->pcsc_wait_ctx_index], 0, states->reader_states, num_watch); if (rv != SCARD_S_SUCCESS) { if (rv != (LONG)SCARD_E_TIMEOUT) { PCSC_LOG(ctx, "SCardGetStatusChange(1) failed", rv); @@ -1516,8 +1791,7 @@ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_re /* Scan the current state of all readers to see if they * match any of the events we're polling for */ - *event = 0; - for (i = 0, rsp = rgReaderStates; i < num_watch; i++, rsp++) { + for (i = 0, rsp = states->reader_states; i < num_watch; i++, rsp++) { DWORD state, prev_state; sc_log(ctx, "'%s' before=0x%08X now=0x%08X", rsp->szReader, @@ -1526,50 +1800,76 @@ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_re prev_state = rsp->dwCurrentState; state = rsp->dwEventState; rsp->dwCurrentState = rsp->dwEventState; +#ifdef _WIN32 + if (!strcmp(rsp->szReader, "\\\\?PnP?\\Notification")) { + /* Windows expects number of readers in HiWord of dwCurrentState. + * See https://stackoverflow.com/questions/16370909. */ + rsp->dwCurrentState |= ((num_watch - 1) << 16); + } +#endif if (state & SCARD_STATE_CHANGED) { - - /* check for hotplug events */ - if (!strcmp(rgReaderStates[i].szReader, "\\\\?PnP?\\Notification")) { + /* check for hotplug events */ + if (!strcmp(rsp->szReader, "\\\\?PnP?\\Notification")) { sc_log(ctx, "detected hotplug event"); - *event |= SC_EVENT_READER_ATTACHED; - *event_reader = NULL; - } - - if ((state & SCARD_STATE_PRESENT) && !(prev_state & SCARD_STATE_PRESENT)) { - sc_log(ctx, "card inserted event"); - *event |= SC_EVENT_CARD_INSERTED; - } - - if ((prev_state & SCARD_STATE_PRESENT) && !(state & SCARD_STATE_PRESENT)) { - sc_log(ctx, "card removed event"); - *event |= SC_EVENT_CARD_REMOVED; - } - - if ((state & SCARD_STATE_UNKNOWN) && !(prev_state & SCARD_STATE_UNKNOWN)) { - sc_log(ctx, "reader detached event"); - *event |= SC_EVENT_READER_DETACHED; - } - - if ((prev_state & SCARD_STATE_UNKNOWN) && !(state & SCARD_STATE_UNKNOWN)) { - sc_log(ctx, "reader re-attached event"); - *event |= SC_EVENT_READER_ATTACHED; - } - - if (*event & event_mask) { - sc_log(ctx, "Matching event 0x%02X in reader %s", *event, rsp->szReader); - *event_reader = sc_ctx_get_reader_by_name(ctx, rsp->szReader); - r = SC_SUCCESS; - goto out; + /* Windows sends hotplug event on both, attaching and + * detaching a reader. pcscd only sends it in case of + * attaching a reader. We'll detect later in which case we + * are. */ + detect_readers = 1; + detected_hotplug = 1; + + /* Windows wants us to manually reset the changed state */ + rsp->dwEventState &= ~SCARD_STATE_CHANGED; + } else { + sc_reader_t *reader = sc_ctx_get_reader_by_name(ctx, rsp->szReader); + + if ((state & SCARD_STATE_PRESENT) && !(prev_state & SCARD_STATE_PRESENT)) { + sc_log(ctx, "card inserted event"); + *event |= SC_EVENT_CARD_INSERTED; + } + + if ((prev_state & SCARD_STATE_PRESENT) && !(state & SCARD_STATE_PRESENT)) { + sc_log(ctx, "card removed event"); + *event |= SC_EVENT_CARD_REMOVED; + } + + if ((state & SCARD_STATE_UNKNOWN) && !(prev_state & SCARD_STATE_UNKNOWN)) { + sc_log(ctx, "reader detached event"); + *event |= SC_EVENT_READER_DETACHED; + detect_readers = 1; + } + + if ((state & SCARD_STATE_IGNORE) && !(prev_state & SCARD_STATE_IGNORE)) { + sc_log(ctx, "reader detached event"); + *event |= SC_EVENT_READER_DETACHED; + detect_readers = 1; + } + + if ((prev_state & SCARD_STATE_UNKNOWN) && !(state & SCARD_STATE_UNKNOWN)) { + sc_log(ctx, "reader re-attached event"); + *event |= SC_EVENT_READER_ATTACHED; + detect_readers = 1; + } + + if (*event & event_mask) { + sc_log(ctx, "Matching event 0x%02X in reader %s", *event, rsp->szReader); + *event_reader = reader; + r = SC_SUCCESS; + goto out; + } else { + *event = 0; + } } - } - - /* No match - copy the state so pcscd knows - * what to watch out for */ - /* rsp->dwCurrentState = rsp->dwEventState; */ } - if (timeout == 0) { + /* if a reader was detected, we need to create a new list of readers */ + if (detected_hotplug) { + /* By default, ignore a hotplug event as if a timeout + * occurred, since it may be an unrequested removal or + * false alarm. Just continue to loop and check at the end + * of this function whether we need to return the attached + * reader or not. */ r = SC_ERROR_EVENT_TIMEOUT; goto out; } @@ -1581,15 +1881,15 @@ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_re else dwtimeout = timeout; - rv = gpriv->SCardGetStatusChange(gpriv->pcsc_wait_ctx, dwtimeout, rgReaderStates, num_watch); + rv = gpriv->SCardGetStatusChange(gpriv->pcsc_wait_ctx[states->pcsc_wait_ctx_index], dwtimeout, states->reader_states, num_watch); - if (rv == (LONG) SCARD_E_CANCELLED) { - /* C_Finalize was called, events don't matter */ + if (rv == (LONG)SCARD_E_CANCELLED) { + /* pcsc_cancel was called, events don't matter */ r = SC_ERROR_EVENT_TIMEOUT; goto out; } - if (rv == (LONG) SCARD_E_TIMEOUT) { + if (rv == (LONG)SCARD_E_TIMEOUT) { r = SC_ERROR_EVENT_TIMEOUT; goto out; } @@ -1600,13 +1900,61 @@ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_re goto out; } } + out: - if (!reader_states) { - free(rgReaderStates); + /* in case of an error re-detect all readers */ + if (r < 0 && r != SC_ERROR_EVENT_TIMEOUT) + detect_readers = 1; + + if (detect_readers) { + pcsc_detect_readers(ctx); + } + + if (detected_hotplug) { + if (gpriv->attached_reader) { + if (event_reader && event && !*event) { + /* no other event has been detected, yet */ + *event_reader = gpriv->attached_reader; + *event = SC_EVENT_READER_ATTACHED; + /* If the card is present in the reader, report also this event */ + if (gpriv->attached_reader->flags & SC_READER_CARD_PRESENT) { + *event |= SC_EVENT_CARD_INSERTED; + } + r = SC_SUCCESS; + } + gpriv->attached_reader = NULL; + } else if (gpriv->removed_reader) { + /* Normally, we only check the hotplug event for attached readers. + * However, Windows also notifies on removal. Check, if the latter + * was requested by the caller. */ + if (event_mask & SC_EVENT_READER_DETACHED + && event_reader && event && !*event) { + /* no other event has been detected, yet */ + *event_reader = gpriv->removed_reader; + *event = SC_EVENT_READER_DETACHED; + r = SC_SUCCESS; + } + gpriv->removed_reader = NULL; + } else { + /* false alarm, there was no reader attached or removed, + * avoid re-initialize the reader states by resetting detect_readers */ + detect_readers = 0; + } } - else if (*reader_states == NULL) { - sc_log(ctx, "return allocated 'reader states'"); - *reader_states = rgReaderStates; + + if (detect_readers) { + pcsc_cancel(ctx); + pcsc_reader_states_free(ctx, states); + if (reader_states) { + *reader_states = NULL; + } + } else { + if (!reader_states) { + pcsc_reader_states_free(ctx, states); + } else if (*reader_states == NULL) { + sc_log(ctx, "return allocated reader states"); + *reader_states = states; + } } LOG_FUNC_RETURN(ctx, r); @@ -1635,8 +1983,9 @@ static int part10_build_verify_pin_block(struct sc_reader *reader, u8 * buf, siz int offset = 0, count = 0; sc_apdu_t *apdu = data->apdu; u8 tmp; - unsigned int tmp16; - PIN_VERIFY_STRUCTURE *pin_verify = (PIN_VERIFY_STRUCTURE *)buf; + uint16_t tmp16; + size_t off; + PIN_VERIFY_STRUCTURE *pin_verify = (PIN_VERIFY_STRUCTURE *)buf; /* PIN verification control message */ pin_verify->bTimerOut = SC_CCID_PIN_TIMEOUT; @@ -1647,10 +1996,13 @@ static int part10_build_verify_pin_block(struct sc_reader *reader, u8 * buf, siz if (data->pin1.encoding == SC_PIN_ENCODING_ASCII) { tmp |= SC_CCID_PIN_ENCODING_ASCII; - /* if the effective PIN length offset is specified, use it */ - if (data->pin1.length_offset > 4) { + /* If the PIN offset is specified, use it, as long as it fits in 4 bits */ + if (data->pin1.offset >= 5) { + off = data->pin1.offset - 5; + if (off > 15) + return SC_ERROR_NOT_SUPPORTED; tmp |= SC_CCID_PIN_UNITS_BYTES; - tmp |= (data->pin1.length_offset - 5) << 3; + tmp |= off << 3; } } else if (data->pin1.encoding == SC_PIN_ENCODING_BCD) { tmp |= SC_CCID_PIN_ENCODING_BCD; @@ -1670,7 +2022,13 @@ static int part10_build_verify_pin_block(struct sc_reader *reader, u8 * buf, siz /* GLP PIN length is encoded in 4 bits and block size is always 8 bytes */ tmp |= 0x40 | 0x08; } else if (data->pin1.encoding == SC_PIN_ENCODING_ASCII && data->flags & SC_PIN_CMD_NEED_PADDING) { - tmp |= data->pin1.pad_length; + /* + * Use fixed-size PIN frame if pad length is below the limit. If not, leave the + * PIN frame size at 0, hoping for adaptive support by the reader (which includes + * both a variable-length frame when Lc is 0 or a pre-padded frame when Lc != 0). + */ + if (data->pin1.pad_length <= 15) + tmp |= data->pin1.pad_length; } pin_verify->bmPINBlockString = tmp; @@ -1685,7 +2043,7 @@ static int part10_build_verify_pin_block(struct sc_reader *reader, u8 * buf, siz if (!data->pin1.min_length || !data->pin1.max_length) return SC_ERROR_INVALID_ARGUMENTS; - tmp16 = (data->pin1.min_length << 8 ) + data->pin1.max_length; + tmp16 = (((data->pin1.min_length << 8) & 0xFF00) + (data->pin1.max_length & 0xFF)); pin_verify->wPINMaxExtraDigit = HOST_TO_CCID_16(tmp16); /* Min Max */ pin_verify->bEntryValidationCondition = 0x02; /* Keypress only */ @@ -1703,17 +2061,12 @@ static int part10_build_verify_pin_block(struct sc_reader *reader, u8 * buf, siz pin_verify->bTeoPrologue[2] = 0x00; /* APDU itself */ - pin_verify->abData[offset++] = apdu->cla; - pin_verify->abData[offset++] = apdu->ins; - pin_verify->abData[offset++] = apdu->p1; - pin_verify->abData[offset++] = apdu->p2; - - /* Copy data if not Case 1 */ - if (data->pin1.length_offset != 4) { - pin_verify->abData[offset++] = apdu->lc; - memcpy(&pin_verify->abData[offset], apdu->data, apdu->datalen); - offset += apdu->datalen; - } + LOG_TEST_RET(reader->ctx, + sc_apdu2bytes(reader->ctx, apdu, + reader->active_protocol, pin_verify->abData, + SC_MAX_APDU_BUFFER_SIZE), + "Could not encode PIN APDU"); + offset += sc_apdu_get_length(apdu, reader->active_protocol); pin_verify->ulDataLength = HOST_TO_CCID_32(offset); /* APDU size */ @@ -1730,8 +2083,8 @@ static int part10_build_modify_pin_block(struct sc_reader *reader, u8 * buf, siz int offset = 0, count = 0; sc_apdu_t *apdu = data->apdu; u8 tmp; - unsigned int tmp16; - PIN_MODIFY_STRUCTURE *pin_modify = (PIN_MODIFY_STRUCTURE *)buf; + uint16_t tmp16; + PIN_MODIFY_STRUCTURE *pin_modify = (PIN_MODIFY_STRUCTURE *)buf; struct sc_pin_cmd_pin *pin_ref = data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ? &data->pin2 : &data->pin1; @@ -1744,12 +2097,6 @@ static int part10_build_modify_pin_block(struct sc_reader *reader, u8 * buf, siz tmp = 0x00; if (pin_ref->encoding == SC_PIN_ENCODING_ASCII) { tmp |= SC_CCID_PIN_ENCODING_ASCII; - - /* if the effective PIN length offset is specified, use it */ - if (pin_ref->length_offset > 4) { - tmp |= SC_CCID_PIN_UNITS_BYTES; - tmp |= (pin_ref->length_offset - 5) << 3; - } } else if (pin_ref->encoding == SC_PIN_ENCODING_BCD) { tmp |= SC_CCID_PIN_ENCODING_BCD; tmp |= SC_CCID_PIN_UNITS_BYTES; @@ -1768,7 +2115,8 @@ static int part10_build_modify_pin_block(struct sc_reader *reader, u8 * buf, siz /* GLP PIN length is encoded in 4 bits and block size is always 8 bytes */ tmp |= 0x40 | 0x08; } else if (pin_ref->encoding == SC_PIN_ENCODING_ASCII && pin_ref->pad_length) { - tmp |= pin_ref->pad_length; + if (pin_ref->pad_length <= 15) + tmp |= pin_ref->pad_length; } pin_modify->bmPINBlockString = tmp; /* bmPINBlockString */ @@ -1780,19 +2128,14 @@ static int part10_build_modify_pin_block(struct sc_reader *reader, u8 * buf, siz } pin_modify->bmPINLengthFormat = tmp; /* bmPINLengthFormat */ - /* Set offsets if not Case 1 APDU */ - if (pin_ref->length_offset != 4) { - pin_modify->bInsertionOffsetOld = data->pin1.offset - 5; - pin_modify->bInsertionOffsetNew = data->pin2.offset - 5; - } else { - pin_modify->bInsertionOffsetOld = 0x00; - pin_modify->bInsertionOffsetNew = 0x00; - } + /* Set offsets if available, otherwise default to 0 */ + pin_modify->bInsertionOffsetOld = (data->pin1.offset >= 5 ? data->pin1.offset - 5 : 0); + pin_modify->bInsertionOffsetNew = (data->pin2.offset >= 5 ? data->pin2.offset - 5 : 0); if (!pin_ref->min_length || !pin_ref->max_length) return SC_ERROR_INVALID_ARGUMENTS; - tmp16 = (pin_ref->min_length << 8 ) + pin_ref->max_length; + tmp16 = (uint16_t)(((pin_ref->min_length << 8) & 0xFF00) + (pin_ref->max_length & 0xFF)); pin_modify->wPINMaxExtraDigit = HOST_TO_CCID_16(tmp16); /* Min Max */ /* bConfirmPIN flags @@ -1822,17 +2165,12 @@ static int part10_build_modify_pin_block(struct sc_reader *reader, u8 * buf, siz pin_modify->bTeoPrologue[2] = 0x00; /* APDU itself */ - pin_modify->abData[offset++] = apdu->cla; - pin_modify->abData[offset++] = apdu->ins; - pin_modify->abData[offset++] = apdu->p1; - pin_modify->abData[offset++] = apdu->p2; - - /* Copy data if not Case 1 */ - if (pin_ref->length_offset != 4) { - pin_modify->abData[offset++] = apdu->lc; - memcpy(&pin_modify->abData[offset], apdu->data, apdu->datalen); - offset += apdu->datalen; - } + LOG_TEST_RET(reader->ctx, + sc_apdu2bytes(reader->ctx, apdu, + reader->active_protocol, pin_modify->abData, + SC_MAX_APDU_BUFFER_SIZE), + "Could not encode PIN APDU"); + offset += sc_apdu_get_length(apdu, reader->active_protocol); pin_modify->ulDataLength = HOST_TO_CCID_32(offset); /* APDU size */ @@ -1843,17 +2181,15 @@ static int part10_build_modify_pin_block(struct sc_reader *reader, u8 * buf, siz /* Find a given PCSC v2 part 10 property */ static int -part10_find_property_by_tag(unsigned char buffer[], int length, - int tag_searched) +part10_find_property_by_tag(unsigned char buffer[], DWORD length, + int tag_searched) { unsigned char *p; int found = 0, len, value = -1; p = buffer; - while (p-buffer < length) - { - if (*p++ == tag_searched) - { + while (p - buffer < (long)length) { + if (*p++ == tag_searched) { found = 1; break; } @@ -1863,12 +2199,11 @@ part10_find_property_by_tag(unsigned char buffer[], int length, p += len; } - if (found) - { + if (found && p - buffer < (long)length) { len = *p++; - switch(len) - { + if (p - buffer + len <= (long)length) { + switch (len) { case 1: value = *p; break; @@ -1880,11 +2215,12 @@ part10_find_property_by_tag(unsigned char buffer[], int length, break; default: value = -1; + } } } return value; -} /* part10_find_property_by_tag */ +} /* Make sure the pin min and max are supported by the reader * and fix the values if needed */ @@ -1895,9 +2231,16 @@ part10_check_pin_min_max(sc_reader_t *reader, struct sc_pin_cmd_data *data) unsigned char buffer[256]; size_t length = sizeof buffer; struct pcsc_private_data *priv = reader->drv_data; + struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *) reader->ctx->reader_drv_data; struct sc_pin_cmd_pin *pin_ref = data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ? - &data->pin1 : &data->pin2; + &data->pin2 : &data->pin1; + + if (gpriv->fixed_pinlength != 0) { + pin_ref->min_length = gpriv->fixed_pinlength; + pin_ref->max_length = gpriv->fixed_pinlength; + return 0; + } if (!priv->get_tlv_properties) return 0; @@ -1921,11 +2264,11 @@ part10_check_pin_min_max(sc_reader_t *reader, struct sc_pin_cmd_data *data) /* maximum pin size */ r = part10_find_property_by_tag(buffer, length, PCSCv2_PART10_PROPERTY_bMaxPINSize); - if (r >= 0) + if (r > 0) { unsigned int value = r; - if (pin_ref->max_length > value) + if (!pin_ref->max_length || pin_ref->max_length > value) pin_ref->max_length = r; } @@ -1990,7 +2333,7 @@ pcsc_pin_cmd(sc_reader_t *reader, struct sc_pin_cmd_data *data) /* If PIN block building failed, we fail too */ LOG_TEST_RET(reader->ctx, r, "PC/SC v2 pinpad block building failed!"); /* If not, debug it, just for fun */ - sc_debug_hex(reader->ctx, SC_LOG_DEBUG_NORMAL, "PC/SC v2 pinpad block", sbuf, scount); + sc_log_hex(reader->ctx, "PC/SC v2 pinpad block", sbuf, scount); r = pcsc_internal_transmit(reader, sbuf, scount, rbuf, &rcount, ioctl); @@ -2181,6 +2524,8 @@ static int transform_pace_output(u8 *rbuf, size_t rbuflen, parsed += ui8; /* length_CARprev */ + if (parsed + 1 > rbuflen) + return SC_ERROR_UNKNOWN_DATA_RECEIVED; ui8 = rbuf[parsed]; /* do not just yet copy ui8 to pace_output->previous_car_length */ parsed += 1; @@ -2283,6 +2628,31 @@ static void detect_protocol(sc_reader_t *reader, SCARDHANDLE card_handle) reader->active_protocol = pcsc_proto_to_opensc(prot); } +int +pcsc_check_reader_handles(sc_context_t *ctx, sc_reader_t *reader, void * pcsc_context_handle, void * pcsc_card_handle) +{ + char reader_name[128]; + DWORD reader_name_size = sizeof(reader_name); + + if (NULL == reader) + return 1; + + struct pcsc_private_data *priv = reader->drv_data; + memset(reader_name, 0, sizeof(reader_name)); + + /* check if new handles are for the same reader as old handles */ + if (SCARD_S_SUCCESS != priv->gpriv->SCardGetAttrib(*(SCARDHANDLE *)pcsc_card_handle, + SCARD_ATTR_DEVICE_SYSTEM_NAME_A, (LPBYTE) + reader_name, &reader_name_size) + || strcmp(reader_name, reader->name) != 0) { + sc_log(ctx, "Reader name changed from \"%s\" to \"%s\"", reader->name, reader_name); + + return 1; + } + + return 0; +} + int pcsc_use_reader(sc_context_t *ctx, void * pcsc_context_handle, void * pcsc_card_handle) { SCARDHANDLE card_handle; @@ -2298,17 +2668,34 @@ int pcsc_use_reader(sc_context_t *ctx, void * pcsc_context_handle, void * pcsc_c goto out; } - /* if we already had a reader, delete it */ + if (!gpriv->cardmod) { + ret = SC_ERROR_INTERNAL; + goto out; + } + + /* Only minidriver calls this and only uses one reader */ + /* if we already have a reader, update it */ if (sc_ctx_get_reader_count(ctx) > 0) { - sc_reader_t *oldrdr = list_extract_at(&ctx->readers, 0); - if (oldrdr) - _sc_delete_reader(ctx, oldrdr); + sc_log(ctx, "Reusing the reader"); + sc_reader_t *reader = list_get_at(&ctx->readers, 0); + + if (reader) { + struct pcsc_private_data *priv = reader->drv_data; + priv->pcsc_card =*(SCARDHANDLE *)pcsc_card_handle; + gpriv->pcsc_ctx = *(SCARDCONTEXT *)pcsc_context_handle; + ret = SC_SUCCESS; + goto out; + } else { + ret = SC_ERROR_INTERNAL; + goto out; + } } sc_log(ctx, "Probing PC/SC reader"); + gpriv->attached_reader = NULL; gpriv->pcsc_ctx = *(SCARDCONTEXT *)pcsc_context_handle; - card_handle = *(SCARDHANDLE *)pcsc_card_handle; + card_handle = *(SCARDHANDLE *)pcsc_card_handle; if(SCARD_S_SUCCESS == gpriv->SCardGetAttrib(card_handle, SCARD_ATTR_DEVICE_SYSTEM_NAME_A, (LPBYTE) @@ -2321,6 +2708,7 @@ int pcsc_use_reader(sc_context_t *ctx, void * pcsc_context_handle, void * pcsc_c priv->pcsc_card = card_handle; detect_protocol(reader, card_handle); detect_reader_features(reader, card_handle); + gpriv->attached_reader = reader; } else { _sc_delete_reader(ctx, reader); } diff --git a/src/libopensc/reader-tr03119.c b/src/libopensc/reader-tr03119.c index 7a9d4a4500..178e546264 100644 --- a/src/libopensc/reader-tr03119.c +++ b/src/libopensc/reader-tr03119.c @@ -1,7 +1,7 @@ /* * reader-escape.c: implementation related to escape commands with pseudo APDUs * - * Copyright (C) 2013-2015 Frank Morgner + * Copyright (C) 2013-2018 Frank Morgner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -15,9 +15,13 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "reader-tr03119.h" #include "ccid-types.h" #include "internal.h" @@ -40,23 +44,39 @@ #ifndef _BSD_SOURCE #define _BSD_SOURCE /* See feature_test_macros(7) */ #endif +#ifdef HAVE_SYS_ENDIAN_H +#include +#endif +#ifdef HAVE_ENDIAN_H #include #endif +#endif + +int get_pace_capabilities(u8 *bitmap) +{ + if (!bitmap) + return SC_ERROR_INVALID_ARGUMENTS; -static const u8 escape_cla = 0xff; -static const u8 escape_ins = 0x9a; + /* BitMap */ + *bitmap = EAC_BITMAP_PACE|EAC_BITMAP_EID|EAC_BITMAP_ESIGN; -static const u8 escape_p1_PIN = 0x04; -static const u8 escape_p2_GetReaderPACECapabilities = 0x01; -static const u8 escape_p2_EstablishPACEChannel = 0x02; -/*static const u8 escape_p2_DestroyPACEChannel = 0x03;*/ -static const u8 escape_p2_PC_to_RDR_Secure = 0x10; + return SC_SUCCESS; +} + +const u8 escape_cla = 0xff; +const u8 escape_ins = 0x9a; -static const u8 escape_p1_IFD = 0x01; -static const u8 escape_p2_vendor = 0x01; -/*static const u8 escape_p2_product = 0x03;*/ -static const u8 escape_p2_version_firmware = 0x06; -/*static const u8 escape_p2_version_driver = 0x07;*/ +const u8 escape_p1_PIN = 0x04; +const u8 escape_p2_GetReaderPACECapabilities = 0x01; +const u8 escape_p2_EstablishPACEChannel = 0x02; +/*const u8 escape_p2_DestroyPACEChannel = 0x03;*/ +const u8 escape_p2_PC_to_RDR_Secure = 0x10; + +const u8 escape_p1_IFD = 0x01; +const u8 escape_p2_vendor = 0x01; +/*const u8 escape_p2_product = 0x03;*/ +const u8 escape_p2_version_firmware = 0x06; +/*const u8 escape_p2_version_driver = 0x07;*/ struct sc_asn1_entry g_boolean[] = { { "boolean", @@ -79,7 +99,7 @@ struct sc_asn1_entry g_numeric_string_as_octet_string[] = { { NULL , 0 , 0 , 0 , NULL , NULL } }; -static const struct sc_asn1_entry g_EstablishPACEChannelInput_data[] = { +const struct sc_asn1_entry g_EstablishPACEChannelInput_data[] = { { "passwordID", /* use an OCTET STRING to avoid a conversion to int */ SC_ASN1_STRUCT, SC_ASN1_CTX|0x01|SC_ASN1_CONS, 0, NULL, NULL }, @@ -94,7 +114,7 @@ static const struct sc_asn1_entry g_EstablishPACEChannelInput_data[] = { SC_ASN1_STRUCT, SC_ASN1_CTX|0x05|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } }; -static const struct sc_asn1_entry g_EstablishPACEChannelOutput_data[] = { +const struct sc_asn1_entry g_EstablishPACEChannelOutput_data[] = { { "errorCode", SC_ASN1_STRUCT, SC_ASN1_CTX|0x01|SC_ASN1_CONS, 0, NULL, NULL }, { "statusMSESetAT", @@ -109,7 +129,7 @@ static const struct sc_asn1_entry g_EstablishPACEChannelOutput_data[] = { SC_ASN1_STRUCT, SC_ASN1_CTX|0x06|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } }; -static const struct sc_asn1_entry g_EstablishPACEChannel[] = { +const struct sc_asn1_entry g_EstablishPACEChannel[] = { { "EstablishPACEChannel", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE|SC_ASN1_CONS, 0, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } @@ -494,7 +514,7 @@ static int escape_pin_cmd_to_buf(sc_context_t *ctx, /* GLP PIN length is encoded in 4 bits and block size is always 8 bytes */ bmPINBlockString = 0x40 | 0x08; } else if (pin_ref->encoding == SC_PIN_ENCODING_ASCII && data->flags & SC_PIN_CMD_NEED_PADDING) { - bmPINBlockString = pin_ref->pad_length; + bmPINBlockString = (uint8_t) pin_ref->pad_length; } else { bmPINBlockString = 0x00; } @@ -566,11 +586,11 @@ static int escape_pin_cmd_to_buf(sc_context_t *ctx, modify->bmPINLengthFormat = bmPINLengthFormat; if (!(data->flags & SC_PIN_CMD_IMPLICIT_CHANGE) && data->pin1.offset) { - modify->bInsertionOffsetOld = data->pin1.offset - 5; + modify->bInsertionOffsetOld = (uint8_t) data->pin1.offset - 5; } else { modify->bInsertionOffsetOld = 0; } - modify->bInsertionOffsetNew = data->pin2.offset ? data->pin2.offset - 5 : 0; + modify->bInsertionOffsetNew = data->pin2.offset ? (uint8_t) data->pin2.offset - 5 : 0; modify->wPINMaxExtraDigit = wPINMaxExtraDigit; modify->bConfirmPIN = CCID_PIN_CONFIRM_NEW | (data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ? 0 : CCID_PIN_INSERT_OLD); @@ -653,23 +673,21 @@ static int escape_perform_verify(struct sc_reader *reader, r = escape_pin_cmd_to_buf(reader->ctx, data, (unsigned char **) &apdu.data, &apdu.datalen); if (r < 0) { - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(reader->ctx, "Error encoding PC_to_RDR_Secure"); goto err; } apdu.lc = apdu.datalen; - r = SC_SUCCESS; - r = reader->ops->transmit(reader, &apdu); if (r < 0) { - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(reader->ctx, "Error performing PC_to_RDR_Secure"); goto err; } if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) { - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(reader->ctx, "Error decoding PC_to_RDR_Secure"); r = SC_ERROR_NOT_SUPPORTED; goto err; @@ -714,7 +732,7 @@ static int escape_perform_pace(struct sc_reader *reader, r = escape_pace_input_to_buf(reader->ctx, input, (unsigned char **) &apdu.data, &apdu.datalen); if (r < 0) { - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(reader->ctx, "Error encoding EstablishPACEChannel"); goto err; } @@ -722,13 +740,13 @@ static int escape_perform_pace(struct sc_reader *reader, r = reader->ops->transmit(reader, &apdu); if (r < 0) { - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(reader->ctx, "Error performing EstablishPACEChannel"); goto err; } if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) { - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(reader->ctx, "Error decoding EstablishPACEChannel"); r = SC_ERROR_NOT_SUPPORTED; goto err; @@ -910,10 +928,11 @@ int escape_pace_capabilities_to_buf(sc_context_t *ctx, return sc_asn1_encode(ctx, PACECapabilities, asn1, asn1_len); } +#define MAX_RESPLEN 0xFF void sc_detect_escape_cmds(sc_reader_t *reader) { int error = 0; - u8 rbuf[0xff+1]; + u8 rbuf[MAX_RESPLEN + 1]; sc_apdu_t apdu; unsigned long capabilities; @@ -925,8 +944,8 @@ void sc_detect_escape_cmds(sc_reader_t *reader) apdu.p1 = escape_p1_PIN; apdu.p2 = escape_p2_GetReaderPACECapabilities; apdu.resp = rbuf; - apdu.resplen = sizeof rbuf; - apdu.le = sizeof rbuf; + apdu.resplen = MAX_RESPLEN; + apdu.le = MAX_RESPLEN; if (reader->ops->transmit(reader, &apdu) == SC_SUCCESS && apdu.sw1 == 0x90 && apdu.sw2 == 0x00 @@ -936,7 +955,7 @@ void sc_detect_escape_cmds(sc_reader_t *reader) && !(reader->capabilities & SC_READER_CAP_PIN_PAD)) { ((struct sc_reader_operations *) reader->ops)->perform_verify = escape_perform_verify; - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(reader->ctx, "Added escape command wrappers for PIN verification/modification to '%s'", reader->name); } @@ -944,20 +963,20 @@ void sc_detect_escape_cmds(sc_reader_t *reader) && !(reader->capabilities & SC_READER_CAP_PACE_GENERIC)) { ((struct sc_reader_operations *) reader->ops)->perform_pace = escape_perform_pace; - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(reader->ctx, "Added escape command wrappers for PACE to '%s'", reader->name); } reader->capabilities |= capabilities; } else { error++; - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(reader->ctx, "%s does not support escape commands", reader->name); } apdu.p1 = escape_p1_IFD; apdu.p2 = escape_p2_vendor; - apdu.resplen = sizeof rbuf; + apdu.resplen = MAX_RESPLEN; if (reader->ops->transmit(reader, &apdu) == SC_SUCCESS && apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { if (!reader->vendor) { @@ -971,7 +990,7 @@ void sc_detect_escape_cmds(sc_reader_t *reader) apdu.p1 = escape_p1_IFD; apdu.p2 = escape_p2_version_firmware; - apdu.resplen = sizeof rbuf; + apdu.resplen = MAX_RESPLEN; if (reader->ops->transmit(reader, &apdu) == SC_SUCCESS && apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { if (!reader->version_major && !reader->version_minor) { @@ -988,7 +1007,7 @@ void sc_detect_escape_cmds(sc_reader_t *reader) } if (error && reader) { - sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(reader->ctx, "%d escape command%s failed, need to reset the card", error, error == 1 ? "" : "s"); if (reader->ops && reader->ops->transmit) { diff --git a/src/libopensc/reader-tr03119.h b/src/libopensc/reader-tr03119.h index 089074fb97..8c8db27eb5 100644 --- a/src/libopensc/reader-tr03119.h +++ b/src/libopensc/reader-tr03119.h @@ -1,7 +1,7 @@ /* * reader-tr03119.h: interface related to escape commands with pseudo APDUs * - * Copyright (C) 2013-2015 Frank Morgner + * Copyright (C) 2013-2018 Frank Morgner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _READER_TR03119_H @@ -28,6 +28,60 @@ extern "C" { #endif +/** @brief NPA capabilities (TR-03119): PACE */ +#define EAC_BITMAP_PACE 0x40 +/** @brief NPA capabilities (TR-03119): EPA: eID */ +#define EAC_BITMAP_EID 0x20 +/** @brief NPA capabilities (TR-03119): EPA: eSign */ +#define EAC_BITMAP_ESIGN 0x10 + +/** + * @brief Get the PACE capabilities + * + * @param[in,out] bitmap where to store capabilities bitmap + * @note Since this code offers no support for terminal certificate, the bitmap is always \c PACE_BITMAP_PACE|PACE_BITMAP_EID + * + * @return \c SC_SUCCESS or error code if an error occurred + */ +int get_pace_capabilities(u8 *bitmap); + +/** @brief NPA result (TR-03119): Kein Fehler */ +#define EAC_SUCCESS 0x00000000 +/** @brief NPA result (TR-03119): Längen im Input sind inkonsistent */ +#define EAC_ERROR_LENGTH_INCONSISTENT 0xD0000001 +/** @brief NPA result (TR-03119): Unerwartete Daten im Input */ +#define EAC_ERROR_UNEXPECTED_DATA 0xD0000002 +/** @brief NPA result (TR-03119): Unerwartete Kombination von Daten im Input */ +#define EAC_ERROR_UNEXPECTED_DATA_COMBINATION 0xD0000003 +/** @brief NPA result (TR-03119): Die Karte unterstützt das PACE – Verfahren nicht. (Unerwartete Struktur in Antwortdaten der Karte) */ +#define EAC_ERROR_CARD_NOT_SUPPORTED 0xE0000001 +/** @brief NPA result (TR-03119): Der Kartenleser unterstützt den angeforderten bzw. den ermittelten Algorithmus nicht. */ +#define EAC_ERROR_ALGORITH_NOT_SUPPORTED 0xE0000002 +/** @brief NPA result (TR-03119): Der Kartenleser kennt die PIN – ID nicht. */ +#define EAC_ERROR_PINID_NOT_SUPPORTED 0xE0000003 +/** @brief NPA result (TR-03119): Negative Antwort der Karte auf Select EF_CardAccess (needs to be OR-ed with SW1|SW2) */ +#define EAC_ERROR_SELECT_EF_CARDACCESS 0xF0000000 +/** @brief NPA result (TR-03119): Negative Antwort der Karte auf Read Binary (needs to be OR-ed with SW1|SW2) */ +#define EAC_ERROR_READ_BINARY 0xF0010000 +/** @brief NPA result (TR-03119): Negative Antwort der Karte auf MSE: Set AT (needs to be OR-ed with SW1|SW2) */ +#define EAC_ERROR_MSE_SET_AT 0xF0020000 +/** @brief NPA result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 1 (needs to be OR-ed with SW1|SW2) */ +#define EAC_ERROR_GENERAL_AUTHENTICATE_1 0xF0030000 +/** @brief NPA result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 2 (needs to be OR-ed with SW1|SW2) */ +#define EAC_ERROR_GENERAL_AUTHENTICATE_2 0xF0040000 +/** @brief NPA result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 3 (needs to be OR-ed with SW1|SW2) */ +#define EAC_ERROR_GENERAL_AUTHENTICATE_3 0xF0050000 +/** @brief NPA result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 4 (needs to be OR-ed with SW1|SW2) */ +#define EAC_ERROR_GENERAL_AUTHENTICATE_4 0xF0060000 +/** @brief NPA result (TR-03119): Kommunikationsabbruch mit Karte. */ +#define EAC_ERROR_COMMUNICATION 0xF0100001 +/** @brief NPA result (TR-03119): Keine Karte im Feld. */ +#define EAC_ERROR_NO_CARD 0xF0100002 +/** @brief NPA result (TR-03119): Benutzerabbruch. */ +#define EAC_ERROR_ABORTED 0xF0200001 +/** @brief NPA result (TR-03119): Benutzer – Timeout */ +#define EAC_ERROR_TIMEOUT 0xF0200002 + void sc_detect_escape_cmds(sc_reader_t *reader); int escape_pace_input_to_buf(sc_context_t *ctx, diff --git a/src/libopensc/sc-ossl-compat.h b/src/libopensc/sc-ossl-compat.h index 2c853bed91..96ec4bd736 100644 --- a/src/libopensc/sc-ossl-compat.h +++ b/src/libopensc/sc-ossl-compat.h @@ -1,5 +1,5 @@ /* - * sc-ossl-compat.h: OpenSC ecompatability for older OpenSSL versions + * sc-ossl-compat.h: OpenSC compatibility for older OpenSSL versions * * Copyright (C) 2016 Douglas E. Engert * @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SC_OSSL_COMPAT_H @@ -30,216 +30,119 @@ extern "C" { #include #include /* - * Provide backward compatability to older versions of OpenSSL - * while using most of OpenSSL 1.1 API + * Provide compatibility OpenSSL 1.1.1, 3.0.1 and LibreSSL 3.4.2 * * LibreSSL is a fork of OpenSSL from 2014 * In its version of openssl/opensslv.h it defines: * OPENSSL_VERSION_NUMBER 0x20000000L (Will not change) - * LIBRESSL_VERSION_NUMBER 0x2050000fL (changes with its versions. - * The LibreSSL appears to follow the OpenSSL-1.0.1 API - * - */ - -/* - * EVP_CIPHER_CTX functions: - * EVP_CIPHER_CTX_new not in 0.9.7 - * EVP_CIPHER_CTX_free not in 0.9.7 - * EVP_CIPHER_CTX_init in 0.9.7 to 1.0.2. defined in 1.1 as EVP_CIPHER_CTX_reset - * EVP_CIPHER_CTX_cleanup in 0.9.7 to 1.0.2, defined in 1.1 as EVP_CIPHER_CTX_reset - * EVP_CIPHER_CTX_reset only in 1.1 - * - * EVP_CIPHER_CTX_new does a EVP_CIPHER_CTX_init - * EVP_CIPHER_CTX_free does a EVP_CIPHER_CTX_cleanup - * EVP_CIPHER_CTX_cleanup does equivelent of a EVP_CIPHER_CTX_init - * Use EVP_CIPHER_CTX_new, EVP_CIPHER_CTX_free, and EVP_CIPHER_CTX_cleanup between operations + * LIBRESSL_VERSION_NUMBER 0x3040200fL (changes with its versions) */ -#if OPENSSL_VERSION_NUMBER <= 0x009070dfL - -/* in 0.9.7 EVP_CIPHER_CTX was always allocated inline or in other structures */ - -#define EVP_CIPHER_CTX_new() ({ \ - EVP_CIPHER_CTX * tmp = NULL; \ - tmp = OPENSSL_malloc(sizeof(struct evp_cipher_ctx_st)); \ - if (tmp) { \ - EVP_CIPHER_CTX_init(tmp); \ - } \ - tmp; \ - }) - -#define EVP_CIPHER_CTX_free(x) ({ \ - if (x) { \ - EVP_CIPHER_CTX_cleanup(x); \ - OPENSSL_free(x); \ - } \ - }) -#endif /* OPENSSL_VERSION_NUMBER =< 0x00907000L */ - -/* - * 1.1 renames RSA_PKCS1_SSLeay to RSA_PKCS1_OpenSSL - * use RSA_PKCS1_OpenSSL - * Previous versions are missing a number of functions to access - * some hidden structures. Define them here: - */ - -/* EVP_PKEY_base_id introduced in 1.0.1 */ -#if OPENSSL_VERSION_NUMBER < 0x10001000L -#define EVP_PKEY_base_id(x) (x->type) -#endif - -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) -#define RSA_PKCS1_OpenSSL RSA_PKCS1_SSLeay -#define OPENSSL_malloc_init CRYPTO_malloc_init - -#define EVP_PKEY_get0_RSA(x) (x->pkey.rsa) -#define EVP_PKEY_get0_DSA(x) (x->pkey.dsa) +#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x30500000L #define X509_get_extension_flags(x) (x->ex_flags) #define X509_get_key_usage(x) (x->ex_kusage) #define X509_get_extended_key_usage(x) (x->ex_xkusage) -#define EVP_PKEY_up_ref(user_key) CRYPTO_add(&user_key->references, 1, CRYPTO_LOCK_EVP_PKEY) -#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER < 0x2050300fL -#define X509_up_ref(cert) CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509) -#endif +#define EVP_MD_CTX_md_data(x) (x->md_data) #endif -/* - * OpenSSL-1.1.0-pre5 has hidden the RSA and DSA structures - * One can no longer use statements like rsa->n = ... - * Macros and defines don't work on all systems, so use inline versions - * If that is not good enough, versions could be added to libopensc - */ +#if defined(LIBRESSL_VERSION_NUMBER) +#define OPENSSL_malloc_init() while(0) continue +#if LIBRESSL_VERSION_NUMBER < 0x30500000L +#define FIPS_mode() (0) +#endif +/* OpenSSL 1.1.1 has EVP_sha3_* */ +#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x30800000L +#define EVP_sha3_224() (NULL) +#define EVP_sha3_256() (NULL) +#define EVP_sha3_384() (NULL) +#define EVP_sha3_512() (NULL) +#endif +#if LIBRESSL_VERSION_NUMBER < 0x3070000fL +#define EVP_PKEY_new_raw_public_key(t, e, p, l) (NULL) +#define EVP_PKEY_get_raw_public_key(p, pu, l) (0) +#endif +#endif -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) -/* based on OpenSSL-1.1.0 e_os2.h */ -/* sc_ossl_inline: portable inline definition usable in public headers */ -# if !defined(inline) && !defined(__cplusplus) -# if defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L - /* just use inline */ -# define sc_ossl_inline inline -# elif defined(__GNUC__) && __GNUC__>=2 -# define sc_ossl_inline __inline__ -# elif defined(_MSC_VER) -# define sc_ossl_inline __inline -# else -# define sc_ossl_inline -# endif -# else -# define sc_ossl_inline inline -# endif +/* OpenSSL 1.1.1 has FIPS_mode function */ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#define FIPS_mode() EVP_default_properties_is_fips_enabled(NULL) #endif -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER >= 0x30000000L -#define RSA_bits(R) (BN_num_bits(R->n)) +#define USE_OPENSSL3_LIBCTX -#include -#ifndef OPENSSL_NO_RSA -#include -#endif -#ifndef OPENSSL_NO_DSA -#include -#endif +#include +#include +#include + +typedef struct ossl3ctx { + OSSL_LIB_CTX *libctx; + OSSL_PROVIDER *defprov; + OSSL_PROVIDER *legacyprov; +} ossl3ctx_t; -#ifndef OPENSSL_NO_RSA -static sc_ossl_inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) +static inline EVP_MD *_sc_evp_md(ossl3ctx_t *ctx, const char *algorithm) { - /* d is the private component and may be NULL */ - if (n == NULL || e == NULL) - return 0; - - BN_free(r->n); - BN_free(r->e); - BN_free(r->d); - r->n = n; - r->e = e; - r->d = d; - - return 1; + return EVP_MD_fetch(ctx->libctx, algorithm, NULL); } +#define sc_evp_md(ctx, alg) _sc_evp_md((ctx)->ossl3ctx, alg) -static sc_ossl_inline int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) +static inline void sc_evp_md_free(EVP_MD *md) { - if (p == NULL || q == NULL) - return 0; + EVP_MD_free(md); +} - BN_free(r->p); - BN_free(r->q); - r->p = p; - r->q = q; +static inline EVP_PKEY_CTX *_sc_evp_pkey_ctx_new(ossl3ctx_t *ctx, + EVP_PKEY *pkey) +{ + return EVP_PKEY_CTX_new_from_pkey(ctx->libctx, pkey, NULL); +} +#define sc_evp_pkey_ctx_new(ctx, pkey) \ + _sc_evp_pkey_ctx_new((ctx)->ossl3ctx, pkey) - return 1; +static inline EVP_CIPHER *_sc_evp_cipher(ossl3ctx_t *ctx, const char *algorithm) +{ + return EVP_CIPHER_fetch(ctx->libctx, algorithm, NULL); } +#define sc_evp_cipher(ctx, alg) _sc_evp_cipher((ctx)->ossl3ctx, alg) -static sc_ossl_inline int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) +static inline void sc_evp_cipher_free(EVP_CIPHER *cipher) { - if (dmp1 == NULL || dmq1 == NULL || iqmp == NULL) - return 0; + EVP_CIPHER_free(cipher); +} - BN_free(r->dmp1); - BN_free(r->dmq1); - BN_free(r->iqmp); - r->dmp1 = dmp1; - r->dmq1 = dmq1; - r->iqmp = iqmp; +#else /* OPENSSL < 3 */ - return 1; -} +#include -static sc_ossl_inline void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) +static inline EVP_MD *sc_evp_md(void *unused, const char *algorithm) { - if (n != NULL) - *n = r->n; - if (e != NULL) - *e = r->e; - if (d != NULL) - *d = r->d; + return (EVP_MD *)EVP_get_digestbyname(algorithm); } -static sc_ossl_inline void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) +static inline void sc_evp_md_free(EVP_MD *md) { - if (p != NULL) - *p = r->p; - if (q != NULL) - *q = r->q; + return; } -static sc_ossl_inline void RSA_get0_crt_params(const RSA *r, - const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp) +static inline EVP_PKEY_CTX *sc_evp_pkey_ctx_new(void *unused, EVP_PKEY *pkey) { - if (dmp1 != NULL) - *dmp1 = r->dmp1; - if (dmq1 != NULL) - *dmq1 = r->dmq1; - if (iqmp != NULL) - *iqmp = r->iqmp; + return EVP_PKEY_CTX_new(pkey, NULL); } -#endif /* OPENSSL_NO_RSA */ - -#ifndef OPENSSL_NO_DSA -static sc_ossl_inline void DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) +static inline EVP_CIPHER *sc_evp_cipher(void *unused, const char *algorithm) { - if (p != NULL) - *p = d->p; - if (q != NULL) - *q = d->q; - if (g != NULL) - *g = d->g; + return (EVP_CIPHER *)EVP_get_cipherbyname(algorithm); } -static sc_ossl_inline void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key) +static inline void sc_evp_cipher_free(EVP_CIPHER *cipher) { - if (pub_key != NULL) - *pub_key = d->pub_key; - if (priv_key != NULL) - *priv_key = d->priv_key; + return; } -/* NOTE: DSA_set0_* functions not defined because they are not currently used in OpenSC */ -#endif /* OPENSSL_NO_DSA */ +#endif -#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ #ifdef __cplusplus } diff --git a/src/libopensc/sc.c b/src/libopensc/sc.c index 051fcf69d9..075259be79 100644 --- a/src/libopensc/sc.c +++ b/src/libopensc/sc.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -34,6 +34,7 @@ #include /* for OPENSSL_cleanse */ #endif + #include "internal.h" #ifdef PACKAGE_VERSION @@ -42,6 +43,18 @@ static const char *sc_version = PACKAGE_VERSION; static const char *sc_version = "(undef)"; #endif +#ifdef _WIN32 +#include +#define PAGESIZE 0 +#else +#include +#include +#include +#ifndef PAGESIZE +#define PAGESIZE 0 +#endif +#endif + const char *sc_get_version(void) { return sc_version; @@ -49,77 +62,103 @@ const char *sc_get_version(void) int sc_hex_to_bin(const char *in, u8 *out, size_t *outlen) { - int err = SC_SUCCESS; - size_t left, count = 0, in_len; - + const char *sc_hex_to_bin_separators = " :"; if (in == NULL || out == NULL || outlen == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } - left = *outlen; - in_len = strlen(in); - while (*in != '\0') { - int byte = 0, nybbles = 2; - - while (nybbles-- && *in && *in != ':' && *in != ' ') { - char c; - byte <<= 4; - c = *in++; - if ('0' <= c && c <= '9') - c -= '0'; - else - if ('a' <= c && c <= 'f') - c = c - 'a' + 10; - else - if ('A' <= c && c <= 'F') - c = c - 'A' + 10; - else { - err = SC_ERROR_INVALID_ARGUMENTS; - goto out; + int byte_needs_nibble = 0; + int r = SC_SUCCESS; + size_t left = *outlen; + u8 byte = 0; + while (*in != '\0' && 0 != left) { + char c = *in++; + u8 nibble; + if ('0' <= c && c <= '9') + nibble = c - '0'; + else if ('a' <= c && c <= 'f') + nibble = c - 'a' + 10; + else if ('A' <= c && c <= 'F') + nibble = c - 'A' + 10; + else { + if (strchr(sc_hex_to_bin_separators, (int) c)) { + if (byte_needs_nibble) { + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } + continue; } - byte |= c; + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; } - /* Detect premature end of string before byte is complete */ - if (in_len > 1 && *in == '\0' && nybbles >= 0) { - err = SC_ERROR_INVALID_ARGUMENTS; - break; + if (byte_needs_nibble) { + byte |= nibble; + *out++ = (u8) byte; + left--; + byte_needs_nibble = 0; + } else { + byte = nibble << 4; + byte_needs_nibble = 1; } + } - if (*in == ':' || *in == ' ') - in++; - if (left <= 0) { - err = SC_ERROR_BUFFER_TOO_SMALL; - break; - } - out[count++] = (u8) byte; + if (left == *outlen && 1 == byte_needs_nibble && 0 != left) { + /* no output written so far, but we have a valid nibble in the upper + * bits. Allow this special case. */ + *out = (u8) byte>>4; left--; + byte_needs_nibble = 0; } -out: - *outlen = count; - return err; + /* for ease of implementation we only accept completely hexed bytes. */ + if (byte_needs_nibble) { + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } + + /* skip all trailing separators to see if we missed something */ + while (*in != '\0') { + if (NULL == strchr(sc_hex_to_bin_separators, (int) *in)) + break; + in++; + } + if (*in != '\0') { + r = SC_ERROR_BUFFER_TOO_SMALL; + goto err; + } + +err: + *outlen -= left; + return r; } int sc_bin_to_hex(const u8 *in, size_t in_len, char *out, size_t out_len, - int in_sep) + int in_sep) { - unsigned int n, sep_len; - char *pos, *end, sep; - - sep = (char)in_sep; - sep_len = sep > 0 ? 1 : 0; - pos = out; - end = out + out_len; - for (n = 0; n < in_len; n++) { - if (pos + 3 + sep_len >= end) + if (in == NULL || out == NULL) { + return SC_ERROR_INVALID_ARGUMENTS; + } + + if (in_sep > 0) { + if (out_len < in_len*3 || out_len < 1) + return SC_ERROR_BUFFER_TOO_SMALL; + } else { + if (out_len < in_len*2 + 1) return SC_ERROR_BUFFER_TOO_SMALL; - if (n && sep_len) - *pos++ = sep; - sprintf(pos, "%02x", in[n]); - pos += 2; } - *pos = '\0'; + + const char hex[] = "0123456789abcdef"; + while (in_len) { + unsigned char value = *in++; + *out++ = hex[(value >> 4) & 0xF]; + *out++ = hex[ value & 0xF]; + in_len--; + if (in_len && in_sep > 0) + *out++ = (char)in_sep; + } + *out = '\0'; + return SC_SUCCESS; } @@ -170,21 +209,46 @@ unsigned long bebytes2ulong(const u8 *buf) { if (buf == NULL) return 0UL; - return (unsigned long) (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]); + return (unsigned long)buf[0] << 24 + | (unsigned long)buf[1] << 16 + | (unsigned long)buf[2] << 8 + | (unsigned long)buf[3]; } unsigned short bebytes2ushort(const u8 *buf) { if (buf == NULL) return 0U; - return (unsigned short) (buf[0] << 8 | buf[1]); + return (unsigned short)buf[0] << 8 + | (unsigned short)buf[1]; } unsigned short lebytes2ushort(const u8 *buf) { if (buf == NULL) return 0U; - return (unsigned short)buf[1] << 8 | (unsigned short)buf[0]; + return (unsigned short)buf[1] << 8 + | (unsigned short)buf[0]; +} + +unsigned long lebytes2ulong(const u8 *buf) +{ + if (buf == NULL) + return 0UL; + return (unsigned long)buf[3] << 24 + | (unsigned long)buf[2] << 16 + | (unsigned long)buf[1] << 8 + | (unsigned long)buf[0]; +} + +void set_string(char **strp, const char *value) +{ + if (strp == NULL) { + return; + } + + free(*strp); + *strp = value ? strdup(value) : NULL; } void sc_init_oid(struct sc_object_id *oid) @@ -210,11 +274,11 @@ int sc_format_oid(struct sc_object_id *oid, const char *in) p = in; for (ii=0; ii < SC_MAX_OBJECT_ID_OCTETS; ii++) { - oid->value[ii] = strtol(p, &q, 10); + oid->value[ii] = (int)strtol(p, &q, 10); if (!*q) break; - if (!(q[0] == '.' && isdigit(q[1]))) + if (!(q[0] == '.' && isdigit((unsigned char)q[1]))) goto out; p = q + 1; @@ -277,6 +341,12 @@ int sc_detect_card_presence(sc_reader_t *reader) LOG_FUNC_RETURN(reader->ctx, SC_ERROR_NOT_SUPPORTED); r = reader->ops->detect_card_presence(reader); + + // Check that we get sane return value from backend + // detect_card_presence should return 0 if no card is present. + if (r && !(r & SC_READER_CARD_PRESENT)) + LOG_FUNC_RETURN(reader->ctx, SC_ERROR_INTERNAL); + LOG_FUNC_RETURN(reader->ctx, r); } @@ -382,7 +452,7 @@ int sc_path_print(char *buf, size_t buflen, const sc_path_t *path) if (buf == NULL || path == NULL) return SC_ERROR_INVALID_ARGUMENTS; - if (buflen < path->len * 2 + path->aid.len * 2 + 1) + if (buflen < path->len * 2 + path->aid.len * 2 + 3) return SC_ERROR_BUFFER_TOO_SMALL; buf[0] = '\0'; @@ -475,7 +545,7 @@ int sc_file_add_acl_entry(sc_file_t *file, unsigned int operation, if (_new == NULL) return SC_ERROR_OUT_OF_MEMORY; _new->method = method; - _new->key_ref = key_ref; + _new->key_ref = (unsigned)key_ref; _new->next = NULL; p = file->acl[operation]; @@ -495,13 +565,13 @@ const sc_acl_entry_t * sc_file_get_acl_entry(const sc_file_t *file, { sc_acl_entry_t *p; static const sc_acl_entry_t e_never = { - SC_AC_NEVER, SC_AC_KEY_REF_NONE, {{0, 0, 0, {0}}}, NULL + SC_AC_NEVER, SC_AC_KEY_REF_NONE, NULL }; static const sc_acl_entry_t e_none = { - SC_AC_NONE, SC_AC_KEY_REF_NONE, {{0, 0, 0, {0}}}, NULL + SC_AC_NONE, SC_AC_KEY_REF_NONE, NULL }; static const sc_acl_entry_t e_unknown = { - SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE, {{0, 0, 0, {0}}}, NULL + SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE, NULL }; if (file == NULL || operation >= SC_MAX_AC_OPS) { @@ -578,9 +648,9 @@ void sc_file_dup(sc_file_t **dest, const sc_file_t *src) const sc_acl_entry_t *e; unsigned int op; + *dest = NULL; if (!sc_file_valid(src)) return; - *dest = NULL; newf = sc_file_new(); if (newf == NULL) return; @@ -628,7 +698,7 @@ int sc_file_set_sec_attr(sc_file_t *file, const u8 *sec_attr, return SC_ERROR_INVALID_ARGUMENTS; } - if (sec_attr == NULL) { + if (sec_attr == NULL || sec_attr_len == 0) { if (file->sec_attr != NULL) free(file->sec_attr); file->sec_attr = NULL; @@ -658,7 +728,7 @@ int sc_file_set_prop_attr(sc_file_t *file, const u8 *prop_attr, return SC_ERROR_INVALID_ARGUMENTS; } - if (prop_attr == NULL) { + if (prop_attr == NULL || prop_attr_len == 0) { if (file->prop_attr != NULL) free(file->prop_attr); file->prop_attr = NULL; @@ -688,7 +758,7 @@ int sc_file_set_type_attr(sc_file_t *file, const u8 *type_attr, return SC_ERROR_INVALID_ARGUMENTS; } - if (type_attr == NULL) { + if (type_attr == NULL || type_attr_len == 0) { if (file->type_attr != NULL) free(file->type_attr); file->type_attr = NULL; @@ -719,7 +789,7 @@ int sc_file_set_content(sc_file_t *file, const u8 *content, return SC_ERROR_INVALID_ARGUMENTS; } - if (content == NULL) { + if (content == NULL || content_len == 0) { if (file->encoded_content != NULL) free(file->encoded_content); file->encoded_content = NULL; @@ -826,42 +896,46 @@ int _sc_parse_atr(sc_reader_t *reader) return SC_SUCCESS; } -void *sc_mem_alloc_secure(sc_context_t *ctx, size_t len) +void *sc_mem_secure_alloc(size_t len) { - void *pointer; - int locked = 0; + void *p; - pointer = calloc(len, sizeof(unsigned char)); - if (!pointer) - return NULL; -#ifdef HAVE_SYS_MMAN_H - /* TODO mprotect */ - /* Do not swap the memory */ - if (mlock(pointer, len) >= 0) - locked = 1; +#ifdef _WIN32 + p = VirtualAlloc(NULL, len, MEM_COMMIT, PAGE_READWRITE); + if (p != NULL) { + VirtualLock(p, len); + } +#else + p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (p != NULL) { + mlock(p, len); + } #endif + + return p; +} + +void sc_mem_secure_free(void *ptr, size_t len) +{ #ifdef _WIN32 - /* Do not swap the memory */ - if (VirtualLock(pointer, len) != 0) - locked = 1; + VirtualUnlock(ptr, len); + VirtualFree(ptr, 0, MEM_RELEASE); +#else + munlock(ptr, len); + munmap(ptr, len); #endif - if (!locked) { - if (ctx->flags & SC_CTX_FLAG_PARANOID_MEMORY) { - sc_do_log (ctx, 0, NULL, 0, NULL, "cannot lock memory, failing allocation because paranoid set"); - free (pointer); - pointer = NULL; - } else { - sc_do_log (ctx, 0, NULL, 0, NULL, "cannot lock memory, sensitive data may be paged to disk"); - } - } - return pointer; } void sc_mem_clear(void *ptr, size_t len) { - /* FIXME: Bug in 1.0.0-beta series crashes with 0 length */ if (len > 0) { -#ifdef ENABLE_OPENSSL +#ifdef HAVE_MEMSET_S + memset_s(ptr, len, 0, len); +#elif _WIN32 + SecureZeroMemory(ptr, len); +#elif HAVE_EXPLICIT_BZERO + explicit_bzero(ptr, len); +#elif ENABLE_OPENSSL OPENSSL_cleanse(ptr, len); #else memset(ptr, 0, len); @@ -949,7 +1023,7 @@ void sc_remote_data_init(struct sc_remote_data *rdata) static unsigned long sc_CRC_tab32[256]; static int sc_CRC_tab32_initialized = 0; -unsigned sc_crc32(unsigned char *value, size_t len) +unsigned sc_crc32(const unsigned char *value, size_t len) { size_t ii, jj; unsigned long crc; @@ -980,6 +1054,28 @@ unsigned sc_crc32(unsigned char *value, size_t len) return crc%0xffff; } +const u8 *sc_compacttlv_find_tag(const u8 *buf, size_t len, u8 tag, size_t *outlen) +{ + if (buf != NULL) { + size_t idx; + u8 plain_tag = tag & 0xF0; + size_t expected_len = tag & 0x0F; + + for (idx = 0; idx < len; idx++) { + u8 ctag = buf[idx] & 0xF0; + size_t ctag_len = buf[idx] & 0x0F; + if (ctag == plain_tag && idx + ctag_len < len && + (expected_len == 0 || expected_len == ctag_len)) { + if (outlen != NULL) + *outlen = ctag_len; + return buf + (idx + 1); + } + idx += ctag_len; + } + } + return NULL; +} + /**************************** mutex functions ************************/ int sc_mutex_create(const sc_context_t *ctx, void **mutex) @@ -1030,3 +1126,8 @@ unsigned long sc_thread_id(const sc_context_t *ctx) else return ctx->thread_ctx->thread_id(); } + +void sc_free(void *p) +{ + free(p); +} diff --git a/src/libopensc/sec.c b/src/libopensc/sec.c index 024b21628d..c2719161c4 100644 --- a/src/libopensc/sec.c +++ b/src/libopensc/sec.c @@ -15,16 +15,17 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include +#include #ifdef HAVE_UNISTD_H #include #endif @@ -36,10 +37,13 @@ int sc_decipher(sc_card_t *card, { int r; - if (card == NULL || crgram == NULL || out == NULL) { + if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + if (crgram == NULL || out == NULL) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + LOG_FUNC_CALLED(card->ctx); if (card->ops->decipher == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); r = card->ops->decipher(card, crgram, crgram_len, out, outlen); @@ -55,13 +59,43 @@ int sc_compute_signature(sc_card_t *card, if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); if (card->ops->compute_signature == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); r = card->ops->compute_signature(card, data, datalen, out, outlen); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } +int sc_unwrap(sc_card_t *card, + const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) +{ + int r; + + if (card == NULL || crgram == NULL) { + return SC_ERROR_INVALID_ARGUMENTS; + } + LOG_FUNC_CALLED(card->ctx); + if (card->ops->unwrap == NULL) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); + r = card->ops->unwrap(card, crgram, crgram_len); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); +} + +int sc_wrap(sc_card_t *card, + const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) +{ + int r; + + if (card == NULL) { + return SC_ERROR_INVALID_ARGUMENTS; + } + LOG_FUNC_CALLED(card->ctx); + if (card->ops->wrap == NULL) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); + r = card->ops->wrap(card, out, outlen); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); +} + int sc_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) @@ -71,7 +105,7 @@ int sc_set_security_env(sc_card_t *card, if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); if (card->ops->set_security_env == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); r = card->ops->set_security_env(card, env, se_num); @@ -85,14 +119,14 @@ int sc_restore_security_env(sc_card_t *card, int se_num) if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); if (card->ops->restore_security_env == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); r = card->ops->restore_security_env(card, se_num); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } -int sc_verify(sc_card_t *card, unsigned int type, int ref, +int sc_verify(sc_card_t *card, unsigned int type, int ref, const u8 *pin, size_t pinlen, int *tries_left) { struct sc_pin_cmd_data data; @@ -104,7 +138,11 @@ int sc_verify(sc_card_t *card, unsigned int type, int ref, data.pin1.data = pin; data.pin1.len = pinlen; - return sc_pin_cmd(card, &data, tries_left); + int r = sc_pin_cmd(card, &data); + if (tries_left) + *tries_left = data.pin1.tries_left; + + return r; } int sc_logout(sc_card_t *card) @@ -130,7 +168,11 @@ int sc_change_reference_data(sc_card_t *card, unsigned int type, data.pin2.data = newref; data.pin2.len = newlen; - return sc_pin_cmd(card, &data, tries_left); + int r = sc_pin_cmd(card, &data); + if (tries_left) + *tries_left = data.pin1.tries_left; + + return r; } int sc_reset_retry_counter(sc_card_t *card, unsigned int type, int ref, @@ -148,7 +190,7 @@ int sc_reset_retry_counter(sc_card_t *card, unsigned int type, int ref, data.pin2.data = newref; data.pin2.len = newlen; - return sc_pin_cmd(card, &data, NULL); + return sc_pin_cmd(card, &data); } /* @@ -158,17 +200,23 @@ int sc_reset_retry_counter(sc_card_t *card, unsigned int type, int ref, * send this PIN to the card. If no PIN was given, the driver should * ask the reader to obtain the pin(s) via the pin pad */ -int sc_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, - int *tries_left) +int sc_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data) { - int r; + int r, debug; if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(card->ctx); + + debug = card->ctx->debug; + if (data->cmd != SC_PIN_CMD_GET_INFO + && card->ctx->debug < SC_LOG_DEBUG_PIN) { + card->ctx->debug = 0; + } + if (card->ops->pin_cmd) { - r = card->ops->pin_cmd(card, data, tries_left); + r = card->ops->pin_cmd(card, data); } else if (!(data->flags & SC_PIN_CMD_USE_PINPAD)) { /* Card driver doesn't support new style pin_cmd, fall * back to old interface */ @@ -182,7 +230,7 @@ int sc_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, data->pin_reference, data->pin1.data, (size_t) data->pin1.len, - tries_left); + &data->pin1.tries_left); break; case SC_PIN_CMD_CHANGE: if (card->ops->change_reference_data != NULL) @@ -193,7 +241,7 @@ int sc_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, (size_t) data->pin1.len, data->pin2.data, (size_t) data->pin2.len, - tries_left); + &data->pin1.tries_left); break; case SC_PIN_CMD_UNBLOCK: if (card->ops->reset_retry_counter != NULL) @@ -207,12 +255,14 @@ int sc_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, break; } if (r == SC_ERROR_NOT_SUPPORTED) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unsupported PIN operation (%d)", + sc_log(card->ctx, "unsupported PIN operation (%d)", data->cmd); } else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Use of pin pad not supported by card driver"); + sc_log(card->ctx, "Use of pin pad not supported by card driver"); r = SC_ERROR_NOT_SUPPORTED; } + card->ctx->debug = debug; + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } @@ -220,10 +270,10 @@ int sc_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, * This function will copy a PIN, convert and pad it as required * * Note about the SC_PIN_ENCODING_GLP encoding: - * PIN buffers are allways 16 nibbles (8 bytes) and look like this: + * PIN buffers are always 16 nibbles (8 bytes) and look like this: * 0x2 + len + pin_in_BCD + paddingnibbles * in which the paddingnibble = 0xF - * E.g. if PIN = 12345, then sbuf = {0x24, 0x12, 0x34, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF} + * E.g. if PIN = 12345, then sbuf = {0x25, 0x12, 0x34, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF} * E.g. if PIN = 123456789012, then sbuf = {0x2C, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0xFF} * Reference: Global Platform - Card Specification - version 2.0.1' - April 7, 2000 */ @@ -243,7 +293,7 @@ int sc_build_pin(u8 *buf, size_t buflen, struct sc_pin_cmd_pin *pin, int pad) if (pin->data[i] < '0' || pin->data[i] > '9') return SC_ERROR_INVALID_ARGUMENTS; } - buf[0] = 0x20 | pin_len; + buf[0] = 0x20 | (u8) pin_len; buf++; buflen--; } @@ -258,6 +308,9 @@ int sc_build_pin(u8 *buf, size_t buflen, struct sc_pin_cmd_pin *pin, int pad) if (pin_len > 2 * buflen) return SC_ERROR_BUFFER_TOO_SMALL; for (i = j = 0; j < pin_len; j++) { + if (!isdigit(pin->data[j])) { + return SC_ERROR_INVALID_DATA; + } buf[i] <<= 4; buf[i] |= pin->data[j] & 0xf; if (j & 1) @@ -289,5 +342,37 @@ int sc_build_pin(u8 *buf, size_t buflen, struct sc_pin_cmd_pin *pin, int pad) } } - return i; + return (int)i; +} + +int +sc_encrypt_sym(struct sc_card *card, const u8 *plaintext, size_t plaintext_len, + u8 *out, size_t *outlen) +{ + int r; + + if (card == NULL) + return SC_ERROR_INVALID_ARGUMENTS; + + LOG_FUNC_CALLED(card->ctx); + if (card->ops->encrypt_sym == NULL) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); + r = card->ops->encrypt_sym(card, plaintext, plaintext_len, out, outlen); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); +} + +int +sc_decrypt_sym(struct sc_card *card, const u8 *data, size_t data_len, + u8 *out, size_t *outlen) +{ + int r; + + if (card == NULL) + return SC_ERROR_INVALID_ARGUMENTS; + + LOG_FUNC_CALLED(card->ctx); + if (card->ops->decrypt_sym == NULL) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); + r = card->ops->decrypt_sym(card, data, data_len, out, outlen); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } diff --git a/src/libopensc/simpletlv.c b/src/libopensc/simpletlv.c index f526a1cd71..09a269af9e 100644 --- a/src/libopensc/simpletlv.c +++ b/src/libopensc/simpletlv.c @@ -18,10 +18,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -49,19 +49,20 @@ sc_simpletlv_put_tag(u8 tag, size_t datalen, u8 *out, size_t outlen, u8 **ptr) /* tag is just number between 0x01 and 0xFE */ if (tag == 0x00 || tag == 0xff) return SC_ERROR_INVALID_ARGUMENTS; + if (datalen > 0xffff) { + /* we can't store more than two bytes in Simple TLV */ + return SC_ERROR_WRONG_LENGTH; + } *p++ = tag; /* tag is single byte */ if (datalen < 0xff) { /* short value up to 255 */ *p++ = (u8)datalen; /* is in the second byte */ - } else if (datalen < 0xffff) { + } else { /* longer values up to 65535 */ *p++ = (u8)0xff; /* first byte is 0xff */ *p++ = (u8)datalen & 0xff; *p++ = (u8)(datalen >> 8) & 0xff; /* LE */ - } else { - /* we can't store more than two bytes in Simple TLV */ - return SC_ERROR_WRONG_LENGTH; } if (ptr != NULL) *ptr = p; @@ -72,28 +73,40 @@ sc_simpletlv_put_tag(u8 tag, size_t datalen, u8 *out, size_t outlen, u8 **ptr) * content. */ int -sc_simpletlv_read_tag(u8 **buf, size_t buflen, u8 *tag_out, size_t *taglen) +sc_simpletlv_read_tag(const u8 **buf, size_t buflen, u8 *tag_out, size_t *taglen) { - size_t len; - u8 *p = *buf; + u8 tag; + size_t left = buflen, len; + const u8 *p = *buf; - if (buflen < 2) { - *buf = p+buflen; - return SC_ERROR_INVALID_ARGUMENTS; + *buf = NULL; + + if (left < 2) { + return SC_ERROR_INVALID_TLV_OBJECT; } + tag = *p; + p++; + len = *p; + p++; + left -= 2; - *tag_out = *p++; - len = *p++; if (len == 0xff) { /* don't crash on bad data */ - if (buflen < 4) { - *taglen = 0; - return SC_ERROR_INVALID_ARGUMENTS; + if (left < 2) { + return SC_ERROR_INVALID_TLV_OBJECT; } + /* skip two bytes (the size) */ len = lebytes2ushort(p); - p++; + p += 2; + left -= 2; } + + *tag_out = tag; *taglen = len; *buf = p; + + if (len > left) + return SC_ERROR_TLV_END_OF_CONTENTS; + return SC_SUCCESS; } diff --git a/src/libopensc/simpletlv.h b/src/libopensc/simpletlv.h index c207441fa0..df094d2f40 100644 --- a/src/libopensc/simpletlv.h +++ b/src/libopensc/simpletlv.h @@ -18,7 +18,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_SIMPLETLV_H @@ -29,7 +29,6 @@ extern "C" { #endif #include "libopensc/opensc.h" -#include "libopensc/pkcs15.h" /* * Create a tag/length file in Simple TLV based on the val_len content length @@ -49,6 +48,6 @@ int sc_simpletlv_put_tag(u8 tag, size_t datalen, u8 *out, size_t outlen, u8 **pt * @param taglen The length of the V record * @return SC_SUCCESS on valid input */ -int sc_simpletlv_read_tag(u8 **buf, size_t buflen, u8 *tag_out, size_t *taglen); +int sc_simpletlv_read_tag(const u8 **buf, size_t buflen, u8 *tag_out, size_t *taglen); #endif diff --git a/src/libopensc/sm.c b/src/libopensc/sm.c index a5f53de16d..062efc5054 100644 --- a/src/libopensc/sm.c +++ b/src/libopensc/sm.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -31,6 +31,7 @@ #include "asn1.h" #include "sm.h" +#ifdef ENABLE_SM static const struct sc_asn1_entry c_asn1_sm_response[4] = { { "encryptedData", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 7, SC_ASN1_OPTIONAL, NULL, NULL }, { "statusWord", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 0x19, 0, NULL, NULL }, @@ -38,7 +39,6 @@ static const struct sc_asn1_entry c_asn1_sm_response[4] = { { NULL, 0, 0, 0, NULL, NULL } }; -#ifdef ENABLE_SM int sc_sm_parse_answer(struct sc_card *card, unsigned char *resp_data, size_t resp_len, struct sm_card_response *out) @@ -157,7 +157,7 @@ sc_sm_single_transmit(struct sc_card *card, struct sc_apdu *apdu) } /* send APDU flagged as NO_SM */ - sm_apdu->flags |= SC_APDU_FLAGS_NO_SM; + sm_apdu->flags |= SC_APDU_FLAGS_NO_SM | SC_APDU_FLAGS_NO_RETRY_WL; rv = sc_transmit_apdu(card, sm_apdu); if (rv < 0) { card->sm_ctx.ops.free_sm_apdu(card, apdu, &sm_apdu); diff --git a/src/libopensc/sm.h b/src/libopensc/sm.h index e27725f45b..a49dcac7e9 100644 --- a/src/libopensc/sm.h +++ b/src/libopensc/sm.h @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SM_H @@ -35,18 +35,15 @@ extern "C" { #include #include -#ifndef SHA_DIGEST_LENGTH -#define SHA_DIGEST_LENGTH 20 -#define SHA1_DIGEST_LENGTH 20 -#define SHA256_DIGEST_LENGTH 32 -#endif - #define SM_TYPE_GP_SCP01 0x100 #define SM_TYPE_CWA14890 0x400 #define SM_TYPE_DH_RSA 0x500 +/** don't use SM */ #define SM_MODE_NONE 0x0 +/** let the card driver decide when to use SM, possibly based on the card's ACLs */ #define SM_MODE_ACL 0x100 +/** use SM for all commands */ #define SM_MODE_TRANSMIT 0x200 #define SM_CMD_INITIALIZE 0x10 @@ -91,6 +88,10 @@ extern "C" { #define SM_GP_SECURITY_MAC 0x01 #define SM_GP_SECURITY_ENC 0x03 +/* As in OpenSSL include/openssl/des.h */ +typedef unsigned char sm_des_cblock[8]; +typedef /* const */ unsigned char sm_const_des_cblock[8]; + /* Global Platform (SCP01) data types */ /* * @struct sm_type_params_gp @@ -300,7 +301,7 @@ struct sm_card_operations { /* * @struct sm_module_operations * API to use external SM modules: - * - 'initiliaze' - get APDU(s) to initialize SM session; + * - 'initialize' - get APDU(s) to initialize SM session; * - 'get apdus' - get secured APDUs to execute particular command; * - 'finalize' - get APDU(s) to finalize SM session; * - 'module init' - initialize external module (allocate data, read configuration, ...); @@ -354,12 +355,12 @@ int sc_sm_update_apdu_response(struct sc_card *, unsigned char *, size_t, int, s int sc_sm_single_transmit(struct sc_card *, struct sc_apdu *); /** - * @brief Stops SM and frees allocated ressources. + * @brief Stops SM and frees allocated resources. * * Calls \a card->sm_ctx.ops.close() if available and \c card->sm_ctx.sm_mode * is \c SM_MODE_TRANSMIT * - * @param[in] card + * @param[in] card card * * @return \c SC_SUCCESS or error code if an error occurred */ diff --git a/src/libopensc/types.h b/src/libopensc/types.h index e665c54b2d..304725a674 100644 --- a/src/libopensc/types.h +++ b/src/libopensc/types.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_TYPES_H @@ -32,7 +32,11 @@ typedef unsigned char u8; #define SC_MAX_CARD_DRIVER_SNAME_SIZE 16 #define SC_MAX_CARD_APPS 8 #define SC_MAX_APDU_BUFFER_SIZE 261 /* takes account of: CLA INS P1 P2 Lc [255 byte of data] Le */ +#define SC_MAX_APDU_DATA_SIZE 0xFF +#define SC_MAX_APDU_RESP_SIZE (0xFF+1) #define SC_MAX_EXT_APDU_BUFFER_SIZE 65538 +#define SC_MAX_EXT_APDU_DATA_SIZE 0xFFFF +#define SC_MAX_EXT_APDU_RESP_SIZE (0xFFFF+1) #define SC_MAX_PIN_SIZE 256 /* OpenPGP card has 254 max */ #define SC_MAX_ATR_SIZE 33 #define SC_MAX_UID_SIZE 10 @@ -45,12 +49,16 @@ typedef unsigned char u8; #define SC_MAX_SDO_ACLS 8 #define SC_MAX_CRTS_IN_SE 12 #define SC_MAX_SE_NUM 8 +#define SC_MAX_PKCS15_EMULATORS 48 +#define SC_MAX_KEYREF_SIZE 8 /* When changing this value, pay attention to the initialization of the ASN1 * static variables that use this macro, like, for example, - * 'c_asn1_supported_algorithms' in src/libopensc/pkcs15.c + * 'c_asn1_supported_algorithms' in src/libopensc/pkcs15.c, + * src/libopensc/pkcs15-prkey.c and src/libopensc/pkcs15-skey.c + * `grep "src/libopensc/types.h SC_MAX_SUPPORTED_ALGORITHMS defined as"' */ -#define SC_MAX_SUPPORTED_ALGORITHMS 8 +#define SC_MAX_SUPPORTED_ALGORITHMS 16 struct sc_lv_data { unsigned char *value; @@ -149,6 +157,7 @@ struct sc_crt { #define SC_AC_SCB 0x00000040 /* IAS/ECC SCB byte. */ #define SC_AC_IDA 0x00000080 /* PKCS#15 authentication ID */ #define SC_AC_SESSION 0x00000100 /* Session PIN */ +#define SC_AC_CONTEXT_SPECIFIC 0x00000200 /* Context specific login */ #define SC_AC_UNKNOWN 0xFFFFFFFE #define SC_AC_NEVER 0xFFFFFFFF @@ -197,13 +206,11 @@ struct sc_crt { typedef struct sc_acl_entry { unsigned int method; /* See SC_AC_* */ unsigned int key_ref; /* SC_AC_KEY_REF_NONE or an integer */ - - struct sc_crt crts[SC_MAX_CRTS_IN_SE]; - struct sc_acl_entry *next; } sc_acl_entry_t; /* File types */ +#define SC_FILE_TYPE_UNKNOWN 0x00 #define SC_FILE_TYPE_DF 0x04 #define SC_FILE_TYPE_INTERNAL_EF 0x03 #define SC_FILE_TYPE_WORKING_EF 0x01 @@ -219,11 +226,33 @@ typedef struct sc_acl_entry { #define SC_FILE_EF_CYCLIC 0x06 #define SC_FILE_EF_CYCLIC_TLV 0x07 +/* File flags */ +#define SC_FILE_FLAG_COMPRESSED_AUTO 0x01 +#define SC_FILE_FLAG_COMPRESSED_ZLIB 0x02 +#define SC_FILE_FLAG_COMPRESSED_GZIP 0x04 + /* File status flags */ -#define SC_FILE_STATUS_ACTIVATED 0x00 -#define SC_FILE_STATUS_INVALIDATED 0x01 -#define SC_FILE_STATUS_CREATION 0x02 /* Full access in this state, - (at least for SetCOS 4.4 */ +/* ISO7816-4: Unless otherwise specified, the security attributes are valid for the operational state.*/ +#define SC_FILE_STATUS_ACTIVATED 0x00 /* ISO7816-4: Operational state (activated) (5, 7) */ +#define SC_FILE_STATUS_INVALIDATED 0x01 /* ISO7816-4: Operational state (deactivated) (4, 6) */ + +/* Full access in this state, (at least for SetCOS 4.4 ) */ +#define SC_FILE_STATUS_CREATION 0x02 /* ISO7816-4: Creation state, (1) */ + +#define SC_FILE_STATUS_INITIALISATION 0x03 /* ISO7816-4: Initialisation state, (3) */ +#define SC_FILE_STATUS_NO_INFO 0x04 /* ISO7816-4: No information given, (0) */ +#define SC_FILE_STATUS_TERMINATION 0x0c /* ISO7816-4: Termination state (12,13,14,15) */ +#define SC_FILE_STATUS_PROPRIETARY 0xf0 /* ISO7816-4: codes > 15 */ + +/* reserved for future use by ISO/IEC */ +#define SC_FILE_STATUS_RFU_2 0x07 /* ISO7816-4: (0x02) */ +#define SC_FILE_STATUS_RFU_8 0x08 /* ISO7816-4: (0x08) */ +#define SC_FILE_STATUS_RFU_9 0x09 /* ISO7816-4: (0x09) */ +#define SC_FILE_STATUS_RFU_10 0x0a /* ISO7816-4: (0x0a) */ +#define SC_FILE_STATUS_RFU_11 0x0b /* ISO7816-4: (0x0b) */ + +#define SC_FILE_STATUS_UNKNOWN 0xff /* if tag 0x8A is missing, there is no information about LCSB */ + typedef struct sc_file { struct sc_path path; unsigned char name[16]; /* DF name */ @@ -235,9 +264,10 @@ typedef struct sc_file { int id; /* file identifier (2 bytes) */ int sid; /* short EF identifier (1 byte) */ struct sc_acl_entry *acl[SC_MAX_AC_OPS]; /* Access Control List */ + int acl_inactive; /* if set, the card access control mechanism is not active */ - int record_length; /* In case of fixed-length or cyclic EF */ - int record_count; /* Valid, if not transparent EF or DF */ + size_t record_length; /* max. length in case of record-oriented EF */ + size_t record_count; /* Valid, if not transparent EF or DF */ unsigned char *sec_attr; /* security data in proprietary format. tag '86' */ size_t sec_attr_len; @@ -282,6 +312,8 @@ typedef struct sc_file { #define SC_APDU_FLAGS_NO_RETRY_WL 0x00000004UL /* APDU is from Secure Messaging */ #define SC_APDU_FLAGS_NO_SM 0x00000008UL +/* let SM do the command chaining */ +#define SC_APDU_FLAGS_SM_CHAINING 0x00000010UL #define SC_APDU_ALLOCATE_FLAG 0x01 #define SC_APDU_ALLOCATE_FLAG_DATA 0x02 diff --git a/src/minidriver/Makefile.am b/src/minidriver/Makefile.am index 6448e7aeea..d116784ef9 100644 --- a/src/minidriver/Makefile.am +++ b/src/minidriver/Makefile.am @@ -1,30 +1,33 @@ include $(top_srcdir)/win32/ltrc.inc -MAINTAINERCLEANFILES = $(srcdir)/Makefile.in $(srcdir)/versioninfo-minidriver.rc -EXTRA_DIST = Makefile.mak versioninfo-minidriver.rc.in +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +EXTRA_DIST = Makefile.mak versioninfo-minidriver.rc if ENABLE_MINIDRIVER lib_LTLIBRARIES = opensc-minidriver@LIBRARY_BITNESS@.la # Do we need this on bin? Why can't we # put it in dedicated directory -dist_sbin_SCRIPTS = opensc-minidriver.inf minidriver-westcos.reg minidriver-sc-hsm.reg minidriver-feitian.reg +dist_sbin_SCRIPTS = opensc-minidriver.inf else -dist_noinst_DATA = opensc-minidriver.inf minidriver-westcos.reg minidriver-sc-hsm.reg minidriver-feitian.reg +dist_noinst_DATA = opensc-minidriver.inf endif -AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) +AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) AM_CPPFLAGS = -I$(top_srcdir)/src -opensc_minidriver@LIBRARY_BITNESS@_la_SOURCES = minidriver.c minidriver.exports versioninfo-minidriver.rc +opensc_minidriver@LIBRARY_BITNESS@_la_SOURCES = minidriver.c opensc-minidriver.exports versioninfo-minidriver.rc opensc_minidriver@LIBRARY_BITNESS@_la_LIBADD = \ $(top_builddir)/src/libopensc/libopensc_static.la \ $(OPTIONAL_OPENSSL_LIBS) \ - -lbcrypt -lcrypt32 -lrpcrt4 + -lbcrypt -lcrypt32 -lrpcrt4 -lwinmm opensc_minidriver@LIBRARY_BITNESS@_la_LDFLAGS = $(AM_LDFLAGS) \ - -export-symbols "$(srcdir)/minidriver.exports" \ + -export-symbols "$(srcdir)/opensc-minidriver.exports" \ -module -avoid-version -no-undefined if ENABLE_MINIDRIVER install-exec-hook: mv "$(DESTDIR)$(libdir)/opensc-minidriver@LIBRARY_BITNESS@.dll" "$(DESTDIR)$(bindir)/" + +uninstall-hook: + rm -f "$(DESTDIR)$(bindir)/opensc-minidriver@LIBRARY_BITNESS@.dll" endif diff --git a/src/minidriver/Makefile.mak b/src/minidriver/Makefile.mak index 5ce80ef533..2130c62b32 100644 --- a/src/minidriver/Makefile.mak +++ b/src/minidriver/Makefile.mak @@ -3,16 +3,18 @@ TOPDIR = ..\.. TARGET = opensc-minidriver.dll OBJECTS = minidriver.obj versioninfo-minidriver.res LIBS = $(TOPDIR)\src\libopensc\opensc_a.lib \ - $(TOPDIR)\src\pkcs15init\pkcs15init.lib \ - $(TOPDIR)\src\common\libscdl.lib + $(TOPDIR)\src\scconf\scconf.lib \ + $(TOPDIR)\src\common\common.lib \ + $(TOPDIR)\src\common\libscdl.lib \ + $(TOPDIR)\src\ui\strings.lib \ + $(TOPDIR)\src\ui\notify.lib \ + $(TOPDIR)\src\sm\libsmiso.lib \ + $(TOPDIR)\src\sm\libsmeac.lib \ + $(TOPDIR)\src\pkcs15init\pkcs15init.lib all: $(TARGET) !INCLUDE $(TOPDIR)\win32\Make.rules.mak -$(TARGET): $(OBJECTS) $(LIBS) - echo LIBRARY $* > $*.def - echo EXPORTS >> $*.def - type minidriver.exports >> $*.def - link /dll $(LINKFLAGS) /def:$*.def /out:$(TARGET) $(OBJECTS) $(LIBS) $(ZLIB_LIB) $(OPENPACE_LIB) $(OPENSSL_LIB) ws2_32.lib gdi32.lib advapi32.lib Crypt32.lib User32.lib bcrypt.lib DelayImp.lib Rpcrt4.lib /DELAYLOAD:bcrypt.dll - if EXIST $(TARGET).manifest mt -manifest $(TARGET).manifest -outputresource:$(TARGET);2 +$(TARGET): $(OBJECTS) $(LIBS) $*.def + link /dll $(LINKFLAGS) /out:$@ /def:$*.def $(OBJECTS) $(LIBS) $(ZLIB_LIB) $(OPENPACE_LIB) $(OPENSSL_LIB) ws2_32.lib gdi32.lib Comctl32.lib advapi32.lib Crypt32.lib User32.lib bcrypt.lib DelayImp.lib Rpcrt4.lib Shell32.lib Comctl32.lib Winmm.lib shlwapi.lib /DELAYLOAD:bcrypt.dll diff --git a/src/minidriver/cardmod-mingw-compat.h b/src/minidriver/cardmod-mingw-compat.h index 2bf01a7f37..b64b97e9ea 100644 --- a/src/minidriver/cardmod-mingw-compat.h +++ b/src/minidriver/cardmod-mingw-compat.h @@ -14,7 +14,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __deref @@ -22,21 +22,14 @@ #define __deref_opt_out_bcount(x) #define __deref_out_bcount(x) #define __deref_out_bcount_opt(x) -#define __deref_out_ecount(x) #define __in -#define __in_bcount(x) #define __in_bcount_opt(x) -#define __in_ecount(x) #define __in_opt #define __inout -#define __inout_bcount_full(x) #define __inout_bcount_opt(x) #define __out -#define __out_bcount(x) -#define __out_bcount_full(x) #define __out_bcount_part_opt(x,y) #define __out_bcount_part_opt(x,y) -#define __out_ecount(x) #define __out_opt #define __struct_bcount(x) #define __success(x) diff --git a/src/minidriver/minidriver-feitian.reg b/src/minidriver/minidriver-feitian.reg deleted file mode 100644 index 446ef1cfef..0000000000 --- a/src/minidriver/minidriver-feitian.reg +++ /dev/null @@ -1,30 +0,0 @@ -Windows Registry Editor Version 5.00 - -[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\ePass2003] -"ATR"=hex:3b,9f,95,81,31,fe,9f,00,66,46,53,05,01,00,11,71,df,00,00,03,6a,82,f8 -"ATRMask"=hex,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff -"Crypto Provider"="Microsoft Base Smart Card Crypto Provider" -"Smart Card Key Storage Provider"="Microsoft Smart Card Key Storage Provider" -"80000001"="opensc-minidriver.dll" - -[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Cryptography\Calais\SmartCards\ePass2003] -"ATR"=hex:3b,9f,95,81,31,fe,9f,00,66,46,53,05,01,00,11,71,df,00,00,03,6a,82,f8 -"ATRMask"=hex:ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,00,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff -"Crypto Provider"="Microsoft Base Smart Card Crypto Provider" -"Smart Card Key Storage Provider"="Microsoft Smart Card Key Storage Provider" -"80000001"="opensc-minidriver.dll" - - -[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\FTCOS/PK-01C] -"ATR"=hex:3b,9f,95,81,31,fe,9f,00,65,46,53,05,00,06,71,df,00,00,00,00,00,00,00 -"ATRMask"=hex:ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,00,ff,ff,ff,ff,ff,ff,00,00,00,00 -"Crypto Provider"="Microsoft Base Smart Card Crypto Provider" -"Smart Card Key Storage Provider"="Microsoft Smart Card Key Storage Provider" -"80000001"="opensc-minidriver.dll" - -[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Cryptography\Calais\SmartCards\FTCOS/PK-01C] -"ATR"=hex:3b,9f,95,81,31,fe,9f,00,65,46,53,05,00,06,71,df,00,00,00,00,00,00,00 -"ATRMask"=hex:ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,00,ff,ff,ff,ff,ff,ff,00,00,00,00 -"Crypto Provider"="Microsoft Base Smart Card Crypto Provider" -"Smart Card Key Storage Provider"="Microsoft Smart Card Key Storage Provider" -"80000001"="opensc-minidriver.dll" diff --git a/src/minidriver/minidriver-italian-cns.reg b/src/minidriver/minidriver-italian-cns.reg deleted file mode 100644 index 3bca8d4ad8..0000000000 --- a/src/minidriver/minidriver-italian-cns.reg +++ /dev/null @@ -1,33 +0,0 @@ -Windows Registry Editor Version 5.00 - -[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Cryptography\Calais\SmartCards\CPS] -"Crypto Provider"="Microsoft Base Smart Card Crypto Provider" -"Smart Card Key Storage Provider"="Microsoft Smart Card Key Storage Provider" -"80000001"="opensc-minidriver.dll" -"ATR"=hex:3b,ff,18,00,ff,c1,0a,31,fe,55,00,6b,05,08,c8,0c,01,11,01,43,4e,53,10,\ - 31,80,05 -"ATRMask"=hex:ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,\ - ff,ff,ff,ff,ff - -[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Cryptography\Calais\SmartCards\CPS-Athena] -"Crypto Provider"="Microsoft Base Smart Card Crypto Provider" -"Smart Card Key Storage Provider"="Microsoft Smart Card Key Storage Provider" -"80000001"="opensc-minidriver.dll" -"ATR"=hex:3b,df,18,00,81,31,fe,7d,00,6b,02,0c,01,82,01,11,01,43,4e,53,10,31,80,fc -"ATRMask"=hex:ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff - -[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\CPS] -"Crypto Provider"="Microsoft Base Smart Card Crypto Provider" -"Smart Card Key Storage Provider"="Microsoft Smart Card Key Storage Provider" -"80000001"="opensc-minidriver.dll" -"ATR"=hex:3b,ff,18,00,ff,c1,0a,31,fe,55,00,6b,05,08,c8,0c,01,11,01,43,4e,53,10,\ - 31,80,05 -"ATRMask"=hex:ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,\ - ff,ff,ff,ff,ff - -[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\CPS-Athena] -"Crypto Provider"="Microsoft Base Smart Card Crypto Provider" -"Smart Card Key Storage Provider"="Microsoft Smart Card Key Storage Provider" -"80000001"="opensc-minidriver.dll" -"ATR"=hex:3b,df,18,00,81,31,fe,7d,00,6b,02,0c,01,82,01,11,01,43,4e,53,10,31,80,fc -"ATRMask"=hex:ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff diff --git a/src/minidriver/minidriver-sc-hsm.reg b/src/minidriver/minidriver-sc-hsm.reg deleted file mode 100644 index 38598b2cd2..0000000000 Binary files a/src/minidriver/minidriver-sc-hsm.reg and /dev/null differ diff --git a/src/minidriver/minidriver-westcos.reg b/src/minidriver/minidriver-westcos.reg deleted file mode 100644 index 840daee683..0000000000 --- a/src/minidriver/minidriver-westcos.reg +++ /dev/null @@ -1,7 +0,0 @@ -Windows Registry Editor Version 5.00 - -[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\CEV WESTCOS] -"80000001"="opensc-minidriver.dll" -"ATR"=hex:3f,69,00,00,00,64,01,00,00,00,80,90,00 -"ATRMask"=hex:ff,ff,ff,ff,ff,ff,ff,00,00,00,f0,ff,ff -"Crypto Provider"="Microsoft Base Smart Card Crypto Provider" diff --git a/src/minidriver/minidriver.c b/src/minidriver/minidriver.c index 0e6ab7869f..1d397cf46d 100644 --- a/src/minidriver/minidriver.c +++ b/src/minidriver/minidriver.c @@ -1,4 +1,4 @@ -/* +/* * minidriver.c: OpenSC minidriver * * Copyright (C) 2009,2010 francois.leblanc@cev-sa.com @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* @@ -32,11 +32,19 @@ #include #include +#include #include #include +#include +#ifdef __MINGW32__ +#include +#else +#include +#endif #include "common/compat_strlcpy.h" +#include "common/constant-time.h" #include "libopensc/asn1.h" #include "libopensc/cardctl.h" #include "libopensc/opensc.h" @@ -44,13 +52,20 @@ #include "libopensc/log.h" #include "libopensc/internal.h" #include "libopensc/aux-data.h" +#include "libopensc/sc-ossl-compat.h" +#include "ui/notify.h" +#include "ui/strings.h" +#include "ui/wchar_from_char_str.h" #include "pkcs15init/pkcs15-init.h" #ifdef ENABLE_OPENSSL #include -#if OPENSSL_VERSION_NUMBER >= 0x10000000L #include +#include #endif + +#ifdef ENABLE_OPENPACE +#include #endif #if defined(__MINGW32__) @@ -59,6 +74,25 @@ #include "cardmod.h" +#define MD_FUNC_CALLED(pCardData, level) do { \ + logprintf(pCardData, level, "MD_Function:%s:%d called\n",__FUNCTION__, __LINE__); \ + } while(0) + + +#define MD_FUNC_CALLED_EXT(pCardData, level) do { \ + logprintf(pCardData, level, "\nP:%lu T:%lu MD_Function:%s:%d called\n", \ + (unsigned long)GetCurrentProcessId(),\ + (unsigned long)GetCurrentThreadId(), \ + __FUNCTION__, __LINE__); \ + } while(0) + +#define MD_FUNC_RETURN(pCardData, level, ...) do { \ + DWORD _ret = __VA_ARGS__; \ + logprintf(pCardData, level,\ + "MD_Function:%s:%d returning with: 0x%08X\n", __FUNCTION__, __LINE__, (unsigned)_ret); \ + return _ret; \ + } while(0) + /* store the instance given at DllMain when attached to access internal resources */ HINSTANCE g_inst; @@ -68,14 +102,17 @@ HINSTANCE g_inst; #define NULLSTR(a) (a == NULL ? "" : a) #define NULLWSTR(a) (a == NULL ? L"" : a) -#define MD_MAX_KEY_CONTAINERS 12 +#define MD_MAX_KEY_CONTAINERS 32 #define MD_CARDID_SIZE 16 -#define MD_UTC_TIME_LENGTH_MAX 16 -#define MD_CARDCF_LENGTH (sizeof(CARD_CACHE_FILE_FORMAT)) +#define MD_ROLE_USER_SIGN (ROLE_ADMIN + 1) +/* + * must be higher than MD_ROLE_USER_SIGN and + * less than or equal MAX_PINS + */ +#define MD_MAX_PINS MAX_PINS -#define MD_DATA_APPLICAITON_NAME "CSP" -#define MD_DATA_DEFAULT_CONT_LABEL "Default Key Container" +#define MD_CARDCF_LENGTH (sizeof(CARD_CACHE_FILE_FORMAT)) #define MD_KEY_USAGE_KEYEXCHANGE \ SC_PKCS15INIT_X509_KEY_ENCIPHERMENT | \ @@ -111,10 +148,9 @@ HINSTANCE g_inst; #endif /* defined twice: in versioninfo-minidriver.rc.in and in minidriver.c */ -#define IDD_PINPAD 101 -#define IDI_LOGO 102 -#define IDC_PINPAD_TEXT 1001 -#define IDC_PINPAD_ICON 1000 +#define IDI_SMARTCARD 102 + +#define SUBKEY_ENABLE_CANCEL "Software\\" OPENSC_VS_FF_COMPANY_NAME "\\OpenSC" OPENSC_ARCH_SUFFIX "\\md_pinpad_dlg_enable_cancel" /* magic to determine previous pinpad authentication */ #define MAGIC_SESSION_PIN "opensc-minidriver" @@ -172,7 +208,9 @@ struct md_guid_conversion md_static_conversions[MD_MAX_CONVERSIONS] = {0}; typedef struct _VENDOR_SPECIFIC { - struct sc_pkcs15_object *obj_user_pin, *obj_sopin; + BOOL initialized; + + struct sc_pkcs15_object *pin_objs[MD_MAX_PINS]; struct sc_context *ctx; struct sc_reader *reader; @@ -194,60 +232,20 @@ typedef struct _VENDOR_SPECIFIC /* these will be used to store intermediate dh agreements results */ struct md_dh_agreement* dh_agreements; BYTE allocatedAgreements; -}VENDOR_SPECIFIC; -/* - * Windows (ex. Vista) may access the card from more than one thread. - * The following data type and static data is an attempt to resolve - * some of the encountered multi-thread issues of OpenSC - * on the minidriver side. - * - * TODO: resolve multi-thread issues on the OpenSC side - */ -#define MD_STATIC_FLAG_READ_ONLY 1 -#define MD_STATIC_FLAG_SUPPORTS_X509_ENROLLMENT 2 -#define MD_STATIC_FLAG_CONTEXT_DELETED 4 -#define MD_STATIC_FLAG_GUID_AS_ID 8 -#define MD_STATIC_FLAG_GUID_AS_LABEL 16 -#define MD_STATIC_FLAG_CREATE_CONTAINER_KEY_IMPORT 32 -#define MD_STATIC_FLAG_CREATE_CONTAINER_KEY_GEN 64 -#define MD_STATIC_FLAG_IGNORE_PIN_LENGTH 128 - -#define MD_STATIC_PROCESS_ATTACHED 0xA11AC4EDL -struct md_opensc_static_data { - unsigned flags, flags_checked; - unsigned long attach_check; -}; -static struct md_opensc_static_data md_static_data; - - -#define C_ASN1_MD_CONTAINER_ATTRS_SIZE 7 -static const struct sc_asn1_entry c_asn1_md_container_attrs[C_ASN1_MD_CONTAINER_ATTRS_SIZE] = { - { "index", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, - { "id", SC_ASN1_PKCS15_ID, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_EMPTY_ALLOWED, NULL, NULL }, - { "guid", SC_ASN1_UTF8STRING, SC_ASN1_TAG_UTF8STRING, SC_ASN1_EMPTY_ALLOWED, NULL, NULL }, - { "flags", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, - { "sizeKeyExchange", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, - { "sizeSign", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, - { NULL, 0, 0, 0, NULL, NULL } -}; + /* if any key used with the MD_ROLE_USER_SIGN has user_consent set PinCacheAlwaysPrompt */ + int need_pin_always; -#define C_ASN1_MD_CONTAINER_SIZE 2 -static const struct sc_asn1_entry c_asn1_md_container[C_ASN1_MD_CONTAINER_SIZE] = { - { "mdContainer", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, - { NULL, 0, 0, 0, NULL, NULL } -}; + CRITICAL_SECTION hScard_lock; +} VENDOR_SPECIFIC; static DWORD md_translate_OpenSC_to_Windows_error(int OpenSCerror, DWORD dwDefaulCode); -static int associate_card(PCARD_DATA pCardData); -static int disassociate_card(PCARD_DATA pCardData); +static DWORD associate_card(PCARD_DATA pCardData); +static void disassociate_card(PCARD_DATA pCardData); static DWORD md_pkcs15_delete_object(PCARD_DATA pCardData, struct sc_pkcs15_object *obj); static DWORD md_fs_init(PCARD_DATA pCardData); - -#if defined(_MSC_VER) && _MSC_VER < 1900 -#define snprintf _snprintf -#endif +static void md_fs_finalize(PCARD_DATA pCardData); #if defined(__GNUC__) static void logprintf(PCARD_DATA pCardData, int level, const char* format, ...) @@ -258,11 +256,11 @@ static void logprintf(PCARD_DATA pCardData, int level, _Printf_format_string_ co { va_list arg; VENDOR_SPECIFIC *vs; -/* Use a simplied log to get all messages including messages +/* Use a simplified log to get all messages including messages * before opensc is loaded. The file must be modifiable by all * users as we maybe called under lsa or user. Note data from * multiple process and threads may get intermingled. - * flush to get last message before ann crash + * flush to get last message before any crash * close so as the file is not left open during any wait. */ DWORD md_debug = 0; @@ -270,7 +268,7 @@ static void logprintf(PCARD_DATA pCardData, int level, _Printf_format_string_ co int rv; rv = sc_ctx_win32_get_config_value("CARDMOD_LOW_LEVEL_DEBUG", - "MiniDriverDebug", "Software\\OpenSC Project\\OpenSC", + "MiniDriverDebug", "Software\\" OPENSC_VS_FF_COMPANY_NAME "\\OpenSC" OPENSC_ARCH_SUFFIX, (char *)(&md_debug), &sz); if (rv == SC_SUCCESS && md_debug != 0) { FILE *lldebugfp = fopen("C:\\tmp\\md.log","a+"); @@ -330,125 +328,410 @@ static void loghex(PCARD_DATA pCardData, int level, PBYTE data, size_t len) logprintf(pCardData, level, " %04X %s\n", a, line); } +static DWORD reinit_card(PCARD_DATA pCardData) +{ + VENDOR_SPECIFIC *vs; + DWORD r; + + if (!pCardData) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + vs = (VENDOR_SPECIFIC *)(pCardData->pvVendorSpecific); + if (!vs) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + if (vs->initialized) { + disassociate_card(pCardData); + md_fs_finalize(pCardData); + } + + r = associate_card(pCardData); + if (r != SCARD_S_SUCCESS) + MD_FUNC_RETURN(pCardData, 1, r); + + r = md_fs_init(pCardData); + if (r != SCARD_S_SUCCESS) { + logprintf(pCardData, 1, + "reinit_card md_fs_init failed, r = 0x%lX\n", + (unsigned long)r); + disassociate_card(pCardData); + MD_FUNC_RETURN(pCardData, 1, r); + } + + MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); +} + +static BOOL lock(PCARD_DATA pCardData) +{ + if (pCardData) { + VENDOR_SPECIFIC *vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (vs) { + EnterCriticalSection(&vs->hScard_lock); + return TRUE; + } + } + + return FALSE; +} + +static void unlock(PCARD_DATA pCardData) +{ + if (pCardData) { + VENDOR_SPECIFIC *vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (vs) { + LeaveCriticalSection(&vs->hScard_lock); + } + } +} + +static DWORD reinit_card_for(PCARD_DATA pCardData, const char *name) +{ + DWORD r; + + r = reinit_card(pCardData); + if (r != SCARD_S_SUCCESS) + logprintf(pCardData, 1, + "%s was called, but unable to initialize card, r = %u\n", + name, (unsigned int)r); + + MD_FUNC_RETURN(pCardData, 1, r); +} + +static DWORD check_card_status(PCARD_DATA pCardData, const char *name) +{ + VENDOR_SPECIFIC *vs; + + MD_FUNC_CALLED(pCardData, 3); + + if (!pCardData) + MD_FUNC_RETURN(pCardData, 3, SCARD_E_INVALID_PARAMETER); + + vs = (VENDOR_SPECIFIC *)(pCardData->pvVendorSpecific); + if (!vs) + MD_FUNC_RETURN(pCardData, 3, SCARD_E_INVALID_PARAMETER); + + if (vs->initialized) + MD_FUNC_RETURN(pCardData, 3, SCARD_S_SUCCESS); + + MD_FUNC_RETURN(pCardData, 3, reinit_card_for(pCardData, name)); +} + /* - * check if the card has been removed, or the + * check if the card is OK, has been removed, or the * caller has changed the handles. - * if so, then free up all previous card info - * and reestablish + * if so, then try to reinit card + * or if different handles but same reader, just use the handles */ -static int -check_reader_status(PCARD_DATA pCardData) +static DWORD +check_card_reader_status(PCARD_DATA pCardData, const char *name) { - int r = SCARD_S_SUCCESS; VENDOR_SPECIFIC *vs = NULL; + DWORD dwRet; + int r; - logprintf(pCardData, 4, "check_reader_status\n"); + logprintf(pCardData, 1, "check_reader_status for %s\n", name); if(!pCardData) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + dwRet = check_card_status(pCardData, name); + if (dwRet != SCARD_S_SUCCESS) + MD_FUNC_RETURN(pCardData, 3, dwRet); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if(!vs) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 3, SCARD_E_INVALID_PARAMETER); - logprintf(pCardData, 7, "pCardData->hSCardCtx:0x%08X hScard:0x%08X\n", - (unsigned int)pCardData->hSCardCtx, - (unsigned int)pCardData->hScard); + logprintf(pCardData, 7, "sizeof(size_t):%u sizeof(ULONG_PTR):%u sizeof(__int3264):%u sizeof pCardData->hSCardCtx:%u\n", + (unsigned)sizeof(size_t), (unsigned)sizeof(ULONG_PTR), (unsigned)sizeof(__int3264), (unsigned)sizeof(pCardData->hSCardCtx)); + + logprintf(pCardData, 1, "pCardData->hSCardCtx:0x%08"SC_FORMAT_LEN_SIZE_T"X hScard:0x%08"SC_FORMAT_LEN_SIZE_T"X\n", + (size_t)pCardData->hSCardCtx, + (size_t)pCardData->hScard); if (pCardData->hSCardCtx != vs->hSCardCtx || pCardData->hScard != vs->hScard) { - logprintf(pCardData, 1, "HANDLES CHANGED from 0x%08X 0x%08X\n", - (unsigned int)vs->hSCardCtx, - (unsigned int)vs->hScard); - - /* Basically a mini AcquireContext */ - r = disassociate_card(pCardData); - logprintf(pCardData, 1, "disassociate_card r = 0x%08X\n", r); - r = associate_card(pCardData); /* need to check return codes */ - if (r != SCARD_S_SUCCESS) - return r; - logprintf(pCardData, 1, "associate_card r = 0x%08X\n", r); - /* Rebuild 'soft' fs - in case changed */ - r = md_fs_init(pCardData); - logprintf(pCardData, 1, "md_fs_init r = 0x%08X\n", r); - } - else if (vs->reader) { - /* This should always work, as BaseCSP should be checking for removal too */ - r = sc_detect_card_presence(vs->reader); - logprintf(pCardData, 2, - "check_reader_status r=%d flags 0x%08X\n", r, - (unsigned int)vs->reader->flags); + logprintf(pCardData, 1, "HANDLES CHANGED from vs->hSCardCtx:0x%08"SC_FORMAT_LEN_SIZE_T"X vs->hScard:0x%08"SC_FORMAT_LEN_SIZE_T"X\n", + (size_t)vs->hSCardCtx, + (size_t)vs->hScard); + if (vs->ctx) { + if (1 == pcsc_check_reader_handles(vs->ctx, vs->reader, &pCardData->hSCardCtx, &pCardData->hScard)) { + _sc_delete_reader(vs->ctx, vs->reader); + MD_FUNC_RETURN(pCardData, 1, reinit_card_for(pCardData, name)); + } else { + vs->hScard = pCardData->hScard; + vs->hSCardCtx = pCardData->hSCardCtx; + r = sc_ctx_use_reader(vs->ctx, &vs->hSCardCtx, &vs->hScard); + logprintf(pCardData, 1, "sc_ctx_use_reader returned %d\n", r); + if (r) + MD_FUNC_RETURN(pCardData, 1, SCARD_F_INTERNAL_ERROR); + } + } + } + + /* This should always work, as BaseCSP should be checking for removal too */ + r = sc_detect_card_presence(vs->reader); + logprintf(pCardData, 2, + "check_reader_status r=%d flags 0x%08X\n", r, + (unsigned int)vs->reader->flags); + if (r < 0) + MD_FUNC_RETURN(pCardData, 1, + md_translate_OpenSC_to_Windows_error(r, SCARD_F_INTERNAL_ERROR)); + + if (!(r & SC_READER_CARD_PRESENT)) { + /* + * if there is really no card present it may not make sense to + * try initializing the card but since it won't hurt let's try + * it anyway for completeness + */ + logprintf(pCardData, 1, "no card present? trying to reinit\n"); + MD_FUNC_RETURN(pCardData, 1, reinit_card_for(pCardData, name)); + } + + MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); +} + +static DWORD +get_pin_by_name(PCARD_DATA pCardData, struct sc_pkcs15_card *p15card, int role, struct sc_pkcs15_object **ret_obj) +{ + scconf_block *conf_block = NULL; + scconf_block **blocks = NULL; + char *pin_type; + char str_path[SC_MAX_AID_STRING_SIZE]; + char *pin = NULL; + struct sc_pkcs15_id id; + + MD_FUNC_CALLED(pCardData, 1); + + switch (role) { + case ROLE_USER: + pin_type = "user_pin"; + break; + case MD_ROLE_USER_SIGN: + pin_type = "sign_pin"; + break; + default: + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + } + + conf_block = sc_get_conf_block(p15card->card->ctx, "framework", "pkcs15", 1); + if (!conf_block) + MD_FUNC_RETURN(pCardData, 1, SCARD_F_INTERNAL_ERROR); + + if (p15card->app != NULL) { + memset(str_path, 0, sizeof(str_path)); + sc_bin_to_hex(p15card->app->path.value, p15card->app->path.len, str_path, sizeof(str_path), 0); + blocks = scconf_find_blocks(p15card->card->ctx->conf, conf_block, "application", str_path); } - return r; + if (blocks) { + if (blocks[0]) { + pin = (char *)scconf_get_str(blocks[0], pin_type, NULL); + } + free(blocks); + } + if (!pin) + MD_FUNC_RETURN(pCardData, 1, SCARD_F_INTERNAL_ERROR); + + strncpy((char*)id.value, pin, sizeof(id.value) - 1); + id.len = strlen(pin); + if (id.len > sizeof(id.value)) + id.len = sizeof(id.value); + MD_FUNC_RETURN(pCardData, 1, sc_pkcs15_find_pin_by_auth_id(p15card, &id, ret_obj) ? SCARD_F_INTERNAL_ERROR : SCARD_S_SUCCESS); } static DWORD md_get_pin_by_role(PCARD_DATA pCardData, PIN_ID role, struct sc_pkcs15_object **ret_obj) { VENDOR_SPECIFIC *vs; - int rv = SC_SUCCESS; + int rv; + + MD_FUNC_CALLED(pCardData, 1); if (!pCardData) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if (!ret_obj) - return SCARD_E_INVALID_PARAMETER; - - *ret_obj = NULL; - - if (role == ROLE_USER) { - if (!vs->obj_user_pin) { - /* Get 'global' User PIN; if no, get the 'local' one */ - rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_GLOBAL, - SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, &vs->obj_user_pin); - if (rv) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + rv = get_pin_by_name(pCardData, vs->p15card, role, ret_obj); + if (!rv) + goto out; + + /* please keep me in sync with _get_auth_object_by_name() in pkcs11/framework-pkcs15.c */ + if (role == ROLE_USER) { + /* Get 'global' User PIN; if no, get the 'local' one */ + rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_GLOBAL, + SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, ret_obj); + if (rv) + rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_LOCAL, + SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, ret_obj); + } + else if (role == MD_ROLE_USER_SIGN) { + int idx = 0; + + /* Get the 'global' user PIN */ + rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_GLOBAL, + SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, ret_obj); + if (!rv) { + /* Global (user) PIN exists, get the local one -- sign PIN */ + rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_LOCAL, + SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, ret_obj); + } + else { + /* No global PIN, try to get first local one -- user PIN */ + rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_LOCAL, + SC_PKCS15_PIN_TYPE_FLAGS_MASK, &idx, ret_obj); + if (!rv) { + /* User PIN is local, try to get the second local -- sign PIN */ + idx++; rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_LOCAL, - SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, &vs->obj_user_pin); + SC_PKCS15_PIN_TYPE_FLAGS_MASK, &idx, ret_obj); + } } - - *ret_obj = vs->obj_user_pin; } - else if (role == ROLE_ADMIN) { + else if (role == ROLE_ADMIN) { /* Get SO PIN; if no, get the 'global' PUK; if no get the 'local' one */ - if (!vs->obj_sopin) { - rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_SOPIN, - SC_PKCS15_PIN_TYPE_FLAGS_SOPIN, NULL, &vs->obj_sopin); - if (rv) - rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PUK_GLOBAL, - SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, &vs->obj_sopin); - if (rv) - rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PUK_LOCAL, - SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, &vs->obj_sopin); - } - - *ret_obj = vs->obj_sopin; + rv = sc_pkcs15_find_so_pin(vs->p15card, ret_obj); + if (rv) + rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PUK_GLOBAL, + SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, ret_obj); + if (rv) + rv = sc_pkcs15_find_pin_by_flags(vs->p15card, SC_PKCS15_PIN_TYPE_FLAGS_PUK_LOCAL, + SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, ret_obj); } else { - logprintf(pCardData, 2, "cannot get PIN object: unsupported role\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + logprintf(pCardData, 2, + "cannot get PIN object: unsupported role %u\n", + (unsigned int)role); + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } - return (rv == SC_SUCCESS) ? SCARD_S_SUCCESS : SCARD_E_UNSUPPORTED_FEATURE; +out: + if (rv) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); + + if (*ret_obj) + logprintf(pCardData, 7, "Returning PIN '%.*s' for role %u\n", + (int) sizeof (*ret_obj)->label, (*ret_obj)->label, + (unsigned int)role); + + MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } +static const char * +md_get_config_str(PCARD_DATA pCardData, enum ui_str id) +{ + VENDOR_SPECIFIC *vs; + const char *ret = NULL; -static BOOL -md_get_config_bool(PCARD_DATA pCardData, char *flag_name, unsigned flag, BOOL ret_default) + if (!pCardData) + return ret; + + vs = (VENDOR_SPECIFIC*) pCardData->pvVendorSpecific; + if (vs->ctx && vs->reader) { + struct sc_atr atr; + atr.len = pCardData->cbAtr; + memcpy(atr.value, pCardData->pbAtr, atr.len); + ret = ui_get_str(vs->ctx, &atr, vs->p15card, id); + } + + return ret; +} + + +static HICON +md_get_config_icon(PCARD_DATA pCardData, char *flag_name, HICON ret_default) { VENDOR_SPECIFIC *vs; - BOOL ret = ret_default; + HICON ret = ret_default; if (!pCardData) return ret; logprintf(pCardData, 2, "Get '%s' option\n", flag_name); - if (md_static_data.flags_checked & flag) { - ret = (md_static_data.flags & flag) ? TRUE : FALSE; - logprintf(pCardData, 2, "Returns checked flag: %s\n", ret ? "TRUE" : "FALSE"); + + vs = (VENDOR_SPECIFIC*) pCardData->pvVendorSpecific; + if (vs->ctx && vs->reader) { + struct sc_atr atr; + scconf_block *atrblock; + atr.len = pCardData->cbAtr; + memcpy(atr.value, pCardData->pbAtr, atr.len); + atrblock = _sc_match_atr_block(vs->ctx, NULL, &atr); + logprintf(pCardData, 2, "Match ATR:\n"); + loghex(pCardData, 3, atr.value, atr.len); + + if (atrblock) { + const char *filename = scconf_get_str(atrblock, flag_name, NULL); + if (filename) { + ret = (HICON) LoadImage(g_inst, filename, IMAGE_ICON, 0, 0, + LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_SHARED); + } + if (!ret) + ret = ret_default; + } + } + + + return ret; +} + + +static HICON +md_get_pinpad_dlg_icon(PCARD_DATA pCardData) +{ + return md_get_config_icon(pCardData, "md_pinpad_dlg_icon", NULL); +} + + +static int +md_get_config_int(PCARD_DATA pCardData, char *flag_name, int ret_default) +{ + VENDOR_SPECIFIC *vs; + int ret = ret_default; + + if (!pCardData) return ret; + + logprintf(pCardData, 2, "Get '%s' option\n", flag_name); + + vs = (VENDOR_SPECIFIC*) pCardData->pvVendorSpecific; + if (vs->ctx && vs->reader) { + struct sc_atr atr; + scconf_block *atrblock; + atr.len = pCardData->cbAtr; + memcpy(atr.value, pCardData->pbAtr, atr.len); + atrblock = _sc_match_atr_block(vs->ctx, NULL, &atr); + logprintf(pCardData, 2, "Match ATR:\n"); + loghex(pCardData, 3, atr.value, atr.len); + + if (atrblock) + ret = scconf_get_int(atrblock, flag_name, ret_default); } + return ret; +} + + +static int +md_get_pinpad_dlg_timeout(PCARD_DATA pCardData) +{ + return md_get_config_int(pCardData, "md_pinpad_dlg_timeout", 30); +} + + +static BOOL +md_get_config_bool(PCARD_DATA pCardData, char *flag_name, BOOL ret_default) +{ + VENDOR_SPECIFIC *vs; + BOOL ret = ret_default; + + if (!pCardData) + return ret; + vs = (VENDOR_SPECIFIC*) pCardData->pvVendorSpecific; + if (!vs) + return ret; + if (vs->ctx && vs->reader) { struct sc_atr atr; scconf_block *atrblock; @@ -462,25 +745,60 @@ md_get_config_bool(PCARD_DATA pCardData, char *flag_name, unsigned flag, BOOL re ret = scconf_get_bool(atrblock, flag_name, ret_default) ? TRUE : FALSE; } - md_static_data.flags_checked |= flag; - if (ret == TRUE) - md_static_data.flags |= flag; - else - md_static_data.flags &= ~flag; - - logprintf(pCardData, 2, "Returns '%s' flag '%s', static flags/checked %X/%X\n", - flag_name, ret ? "TRUE" : "FALSE", - md_static_data.flags, md_static_data.flags_checked); + logprintf(pCardData, 2, "flag_name:%s:%s\n", flag_name, ret ? "TRUE": "FALSE"); return ret; } +/* 'cancellation' mode can be enabled from the OpenSC configuration file*/ +static BOOL +md_is_pinpad_dlg_enable_cancel(PCARD_DATA pCardData) +{ + VENDOR_SPECIFIC *vs; + + logprintf(pCardData, 2, "Is cancelling the PIN pad dialog enabled?\n"); + + vs = (VENDOR_SPECIFIC*) pCardData->pvVendorSpecific; + if (vs && vs->ctx && vs->ctx->exe_path) { + DWORD enable_cancel; + size_t sz = sizeof enable_cancel; + + if (SC_SUCCESS == sc_ctx_win32_get_config_value(NULL, vs->ctx->exe_path, + SUBKEY_ENABLE_CANCEL, + (char *)(&enable_cancel), &sz)) { + switch (enable_cancel) { + case 0: + return FALSE; + case 1: + return TRUE; + } + } + } + + return md_get_config_bool(pCardData, "md_pinpad_dlg_enable_cancel", FALSE); +} + + /* 'Write' mode can be enabled from the OpenSC configuration file*/ static BOOL md_is_read_only(PCARD_DATA pCardData) { + BOOL ret = TRUE; + logprintf(pCardData, 2, "Is read-only?\n"); - return md_get_config_bool(pCardData, "md_read_only", MD_STATIC_FLAG_READ_ONLY, TRUE); + + if (pCardData && pCardData->pvVendorSpecific) { + VENDOR_SPECIFIC *vs = (VENDOR_SPECIFIC*) pCardData->pvVendorSpecific; + if (vs->p15card && vs->p15card->tokeninfo) { + if (vs->p15card->tokeninfo->flags & SC_PKCS15_TOKEN_READONLY) { + ret = TRUE; + } else { + ret = FALSE; + } + } + } + + return md_get_config_bool(pCardData, "read_only", ret); } @@ -490,7 +808,7 @@ md_is_supports_X509_enrollment(PCARD_DATA pCardData) { BOOL defaultvalue = !md_is_read_only(pCardData); logprintf(pCardData, 2, "Is supports X509 enrollment?\n"); - return md_get_config_bool(pCardData, "md_supports_X509_enrollment", MD_STATIC_FLAG_SUPPORTS_X509_ENROLLMENT, defaultvalue); + return md_get_config_bool(pCardData, "md_supports_X509_enrollment", defaultvalue); } @@ -499,7 +817,7 @@ static BOOL md_is_guid_as_id(PCARD_DATA pCardData) { logprintf(pCardData, 2, "Is GUID has to be used as ID of crypto objects?\n"); - return md_get_config_bool(pCardData, "md_guid_as_id", MD_STATIC_FLAG_GUID_AS_ID, FALSE); + return md_get_config_bool(pCardData, "md_guid_as_id", FALSE); } @@ -508,7 +826,7 @@ static BOOL md_is_guid_as_label(PCARD_DATA pCardData) { logprintf(pCardData, 2, "Is GUID has to be used as label of crypto objects?\n"); - return md_get_config_bool(pCardData, "md_guid_as_label", MD_STATIC_FLAG_GUID_AS_LABEL, FALSE); + return md_get_config_bool(pCardData, "md_guid_as_label", FALSE); } @@ -517,7 +835,7 @@ static BOOL md_is_supports_container_key_gen(PCARD_DATA pCardData) { logprintf(pCardData, 2, "Is supports 'key generation' create_container mechanism?\n"); - return md_get_config_bool(pCardData, "md_supports_container_key_gen", MD_STATIC_FLAG_CREATE_CONTAINER_KEY_GEN, TRUE); + return md_get_config_bool(pCardData, "md_supports_container_key_gen", TRUE); } @@ -526,7 +844,7 @@ static BOOL md_is_supports_container_key_import(PCARD_DATA pCardData) { logprintf(pCardData, 2, "Is supports 'key import' create container mechanism?\n"); - return md_get_config_bool(pCardData, "md_supports_container_key_import", MD_STATIC_FLAG_CREATE_CONTAINER_KEY_IMPORT, TRUE); + return md_get_config_bool(pCardData, "md_supports_container_key_import", TRUE); } /* generate unique key label (GUID)*/ @@ -549,6 +867,9 @@ md_contguid_get_guid_from_card(PCARD_DATA pCardData, struct sc_pkcs15_object *pr size_t guid_len = MAX_CONTAINER_NAME_LEN+1; vs = (VENDOR_SPECIFIC*) pCardData->pvVendorSpecific; + if (!vs) + return SCARD_E_INVALID_PARAMETER; + rv = sc_pkcs15_get_object_guid(vs->p15card, prkey, 1, (unsigned char*) szGuid, &guid_len); if (rv) { logprintf(pCardData, 2, "md_contguid_get_guid_from_card(): error %d\n", rv); @@ -581,11 +902,11 @@ md_contguid_add_conversion(PCARD_DATA pCardData, struct sc_pkcs15_object *prkey, strlcpy(md_static_conversions[i].szOpenSCGuid, szOpenSCGuid, MAX_CONTAINER_NAME_LEN + 1); logprintf(pCardData, 0, "md_contguid_add_conversion(): Registering conversion '%s' '%s'\n", szWindowsGuid, szOpenSCGuid); - return SCARD_S_SUCCESS;; + return SCARD_S_SUCCESS; } } logprintf(pCardData, 0, "md_contguid_add_conversion(): Unable to add a new conversion with guid %s.\n", szWindowsGuid); - return SCARD_F_INTERNAL_ERROR;; + return SCARD_F_INTERNAL_ERROR; } /* remove an entry in the guid conversion table*/ @@ -629,7 +950,7 @@ md_contguid_build_cont_guid_from_key(PCARD_DATA pCardData, struct sc_pkcs15_obje DWORD dwret = SCARD_S_SUCCESS; szGuid[0] = '\0'; - /* priorize the use of the key id over the key label as a container name */ + /* prioritize the use of the key id over the key label as a container name */ if (md_is_guid_as_id(pCardData) && prkey_info->id.len > 0 && prkey_info->id.len <= MAX_CONTAINER_NAME_LEN) { memcpy(szGuid, prkey_info->id.value, prkey_info->id.len); szGuid[prkey_info->id.len] = 0; @@ -651,6 +972,9 @@ md_cont_flags_from_key(PCARD_DATA pCardData, struct sc_pkcs15_object *key_obj, u int rv; vs = (VENDOR_SPECIFIC*) pCardData->pvVendorSpecific; + if (!vs) + return SCARD_E_INVALID_PARAMETER; + prkey_info = (struct sc_pkcs15_prkey_info *)key_obj->data; *cont_flags = CONTAINER_MAP_VALID_CONTAINER; @@ -678,6 +1002,9 @@ md_fs_find_directory(PCARD_DATA pCardData, struct md_directory *parent, char *na return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; + if (!vs) + return SCARD_E_INVALID_PARAMETER; + if (!parent) parent = &vs->root; @@ -707,7 +1034,7 @@ md_fs_find_directory(PCARD_DATA pCardData, struct md_directory *parent, char *na static DWORD md_fs_add_directory(PCARD_DATA pCardData, struct md_directory **head, char *name, - CARD_FILE_ACCESS_CONDITION acl, + CARD_DIRECTORY_ACCESS_CONDITION acl, struct md_directory **out) { struct md_directory *new_dir = NULL; @@ -857,6 +1184,8 @@ md_fs_delete_file(PCARD_DATA pCardData, char *parent, char *name) return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; + if (!vs) + return SCARD_E_INVALID_PARAMETER; dwret = md_fs_find_directory(pCardData, NULL, parent, &dir); if (dwret != SCARD_S_SUCCESS) { @@ -917,7 +1246,7 @@ md_fs_delete_file(PCARD_DATA pCardData, char *parent, char *name) return dwret; } -static DWORD +static void md_fs_finalize(PCARD_DATA pCardData) { VENDOR_SPECIFIC *vs; @@ -925,9 +1254,11 @@ md_fs_finalize(PCARD_DATA pCardData) struct md_directory *dir = NULL, *dir_to_rm; if (!pCardData) - return SCARD_E_INVALID_PARAMETER; + return; vs = pCardData->pvVendorSpecific; + if (!vs) + return; file = vs->root.files; while (file != NULL) { @@ -935,6 +1266,7 @@ md_fs_finalize(PCARD_DATA pCardData) file = file->next; md_fs_free_file(pCardData, file_to_rm); } + vs->root.files = NULL; dir = vs->root.subdirs; while(dir) { @@ -948,7 +1280,7 @@ md_fs_finalize(PCARD_DATA pCardData) dir = dir->next; pCardData->pfnCspFree(dir_to_rm); } - return 0; + vs->root.subdirs = NULL; } /* @@ -966,6 +1298,8 @@ md_pkcs15_update_containers(PCARD_DATA pCardData, unsigned char *blob, size_t si return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; + if (!vs) + return SCARD_E_INVALID_PARAMETER; nn_records = (int) size/sizeof(CONTAINER_MAP_RECORD); if (nn_records > MD_MAX_KEY_CONTAINERS) @@ -1012,6 +1346,9 @@ md_pkcs15_delete_object(PCARD_DATA pCardData, struct sc_pkcs15_object *obj) if (!pCardData) return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; + if (!vs) + return SCARD_E_INVALID_PARAMETER; + card = vs->p15card->card; if (!obj) @@ -1092,6 +1429,9 @@ md_set_cardid(PCARD_DATA pCardData, struct md_file *file) return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; + if (!vs) + return SCARD_E_INVALID_PARAMETER; + if (vs->p15card->tokeninfo && vs->p15card->tokeninfo->serial_number) { unsigned char sn_bin[SC_MAX_SERIALNR]; unsigned char cardid_bin[MD_CARDID_SIZE]; @@ -1107,12 +1447,16 @@ md_set_cardid(PCARD_DATA pCardData, struct md_file *file) memcpy(sn_bin, vs->p15card->tokeninfo->serial_number, sn_len); } - for (offs=0; offs < MD_CARDID_SIZE; ) { - wr = MD_CARDID_SIZE - offs; - if (wr > sn_len) - wr = sn_len; - memcpy(cardid_bin + offs, sn_bin, wr); - offs += wr; + if (sn_len > 0) { + for (offs=0; offs < MD_CARDID_SIZE; ) { + wr = MD_CARDID_SIZE - offs; + if (wr > sn_len) + wr = sn_len; + memcpy(cardid_bin + offs, sn_bin, wr); + offs += wr; + } + } else { + memset(cardid_bin, 0, MD_CARDID_SIZE); } dwret = md_fs_set_content(pCardData, file, cardid_bin, MD_CARDID_SIZE); @@ -1158,9 +1502,11 @@ md_fs_read_msroots_file(PCARD_DATA pCardData, struct md_file *file) for(ii = 0; ii < cert_num; ii++) { struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) prkey_objs[ii]->data; struct sc_pkcs15_cert *cert = NULL; + int private_obj; PCCERT_CONTEXT wincert = NULL; if (cert_info->authority) { - rv = sc_pkcs15_read_certificate(vs->p15card, cert_info, &cert); + private_obj = prkey_objs[ii]->flags & SC_PKCS15_CO_FLAG_PRIVATE; + rv = sc_pkcs15_read_certificate(vs->p15card, cert_info, private_obj, &cert); if(rv) { logprintf(pCardData, 2, "Cannot read certificate idx:%i: sc-error %d\n", ii, rv); continue; @@ -1260,8 +1606,9 @@ md_fs_read_content(PCARD_DATA pCardData, char *parent, struct md_file *file) struct sc_pkcs15_cert *cert = NULL; struct sc_pkcs15_object *cert_obj = vs->p15_containers[idx].cert_obj; struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *)cert_obj->data; + int private_obj = cert_obj->flags & SC_PKCS15_CO_FLAG_PRIVATE; - rv = sc_pkcs15_read_certificate(vs->p15card, cert_info, &cert); + rv = sc_pkcs15_read_certificate(vs->p15card, cert_info, private_obj, &cert); if(rv) { logprintf(pCardData, 2, "Cannot read certificate idx:%i: sc-error %d\n", idx, rv); logprintf(pCardData, 2, "set cardcf from 'DATA' pkcs#15 object\n"); @@ -1343,6 +1690,8 @@ md_fs_add_msroots(PCARD_DATA pCardData, struct md_file **head) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC *) pCardData->pvVendorSpecific; + if (!vs) + return SCARD_E_INVALID_PARAMETER; rv = sc_pkcs15_get_objects(vs->p15card, SC_PKCS15_TYPE_CERT_X509, prkey_objs, MD_MAX_KEY_CONTAINERS); if (rv < 0) { @@ -1371,11 +1720,19 @@ md_fs_add_msroots(PCARD_DATA pCardData, struct md_file **head) * 2b. Change the index of internal p15_container according to the index from 'DATA' file. * Records from 'DATA' file are ignored is they do not have * the corresponding PKCS#15 private key object. - * 3. Initalize the content of the 'soft' 'cmapfile' from the inernal p15-containers. + * 3. Initialize the content of the 'soft' 'cmapfile' from the internal p15-containers. */ static DWORD md_set_cmapfile(PCARD_DATA pCardData, struct md_file *file) { + typedef enum { SCF_NONE, + SCF_NONDEFAULT_SIGN_PIN, + SCF_NONDEFAULT_OTHER_PIN, + SCF_NONDEFAULT_USER_PIN, + SCF_DEFAULT_SIGN_PIN, + SCF_DEFAULT_OTHER_PIN, + SCF_DEFAULT_USER_PIN + } pin_mode_t; VENDOR_SPECIFIC *vs; PCONTAINER_MAP_RECORD p; unsigned char *cmap_buf = NULL; @@ -1384,20 +1741,43 @@ md_set_cmapfile(PCARD_DATA pCardData, struct md_file *file) int ii, rv, conts_num, found_default = 0; /* struct sc_pkcs15_data *data_object; */ struct sc_pkcs15_object *prkey_objs[MD_MAX_KEY_CONTAINERS]; + pin_mode_t pin_mode = SCF_NONE; + int pin_cont_idx = -1; if (!pCardData || !file) return SCARD_E_INVALID_PARAMETER; logprintf(pCardData, 2, "set 'cmapfile'\n"); vs = pCardData->pvVendorSpecific; - cmap_len = MD_MAX_KEY_CONTAINERS*sizeof(CONTAINER_MAP_RECORD); - cmap_buf = pCardData->pfnCspAlloc(cmap_len); - if(!cmap_buf) - return SCARD_E_NO_MEMORY; - memset(cmap_buf, 0, cmap_len); + if (!vs) + return SCARD_E_INVALID_PARAMETER; - rv = sc_pkcs15_get_objects(vs->p15card, SC_PKCS15_TYPE_PRKEY, prkey_objs, MD_MAX_KEY_CONTAINERS); - if (rv < 0) { + dwret = md_get_pin_by_role(pCardData, ROLE_USER, &vs->pin_objs[ROLE_USER]); + if (dwret != SCARD_S_SUCCESS) { + logprintf(pCardData, 2, "Cannot get User PIN object\n"); + return dwret; + } + + dwret = md_get_pin_by_role(pCardData, MD_ROLE_USER_SIGN, &vs->pin_objs[MD_ROLE_USER_SIGN]); + if (dwret != SCARD_S_SUCCESS) { + logprintf(pCardData, 2, "Cannot get Sign PIN object -- ignored\n"); + vs->pin_objs[MD_ROLE_USER_SIGN] = NULL; + } + + dwret = md_get_pin_by_role(pCardData, ROLE_ADMIN, &vs->pin_objs[ROLE_ADMIN]); + if (dwret != SCARD_S_SUCCESS) { + logprintf(pCardData, 2, "Cannot get Admin PIN object -- ignored\n"); + vs->pin_objs[ROLE_ADMIN] = NULL; + } + + cmap_len = MD_MAX_KEY_CONTAINERS*sizeof(CONTAINER_MAP_RECORD); + cmap_buf = pCardData->pfnCspAlloc(cmap_len); + if(!cmap_buf) + return SCARD_E_NO_MEMORY; + memset(cmap_buf, 0, cmap_len); + + rv = sc_pkcs15_get_objects(vs->p15card, SC_PKCS15_TYPE_PRKEY, prkey_objs, MD_MAX_KEY_CONTAINERS); + if (rv < 0) { logprintf(pCardData, 0, "Private key enumeration failed: %s\n", sc_strerror(rv)); return SCARD_F_UNKNOWN_ERROR; } @@ -1431,7 +1811,123 @@ md_set_cmapfile(PCARD_DATA pCardData, struct md_file *file) dwret = md_cont_flags_from_key(pCardData, key_obj, &cont->flags); if (dwret != SCARD_S_SUCCESS) return dwret; - if (cont->flags & CONTAINER_MAP_DEFAULT_CONTAINER) + + logprintf(pCardData, 7, "Container[%i] is '%.*s' guid=%.*s\n", ii, + (int) sizeof key_obj->label, key_obj->label, + (int) sizeof cont->guid, cont->guid); + + if (cont->flags & CONTAINER_MAP_VALID_CONTAINER && + key_obj->auth_id.len > 0) { + struct sc_pkcs15_object *keypin_obj; + struct sc_pkcs15_auth_info *userpin_info = + (struct sc_pkcs15_auth_info *)vs->pin_objs[ROLE_USER]->data; + struct sc_pkcs15_auth_info *signpin_info = + vs->pin_objs[MD_ROLE_USER_SIGN] ? + (struct sc_pkcs15_auth_info *)vs->pin_objs[MD_ROLE_USER_SIGN]->data : + NULL; + struct sc_pkcs15_auth_info *adminpin_info = + vs->pin_objs[ROLE_ADMIN] ? + (struct sc_pkcs15_auth_info *)vs->pin_objs[ROLE_ADMIN]->data : + NULL; + + if (sc_pkcs15_find_pin_by_auth_id(vs->p15card, &key_obj->auth_id, &keypin_obj)) + logprintf(pCardData, 2, + "Container[%i] has an unknown auth id, might not work properly\n", + ii); + else { + size_t pinidx; + size_t pinidxempty = MD_MAX_PINS; + for (pinidx = 0; pinidx < MD_MAX_PINS; pinidx++) { + struct sc_pkcs15_auth_info *pin_info; + + if (!vs->pin_objs[pinidx]) { + if (pinidxempty >= MD_MAX_PINS) + pinidxempty = pinidx; + + continue; + } + + pin_info = + (struct sc_pkcs15_auth_info *)vs->pin_objs[pinidx]->data; + + if (sc_pkcs15_compare_id(&key_obj->auth_id, + &pin_info->auth_id)) + break; + } + + if (pinidx >= MD_MAX_PINS) { + if (pinidxempty >= MD_MAX_PINS) + logprintf(pCardData, 2, + "no free slot for container[%i] auth id, might not work properly\n", + ii); + else + vs->pin_objs[pinidxempty] = keypin_obj; + } + + if (sc_pkcs15_compare_id(&key_obj->auth_id, &userpin_info->auth_id)) { + pin_mode_t pin_mode_n = + cont->flags & CONTAINER_MAP_DEFAULT_CONTAINER ? + SCF_DEFAULT_USER_PIN : SCF_NONDEFAULT_USER_PIN; + + logprintf(pCardData, 7, + "Container[%i]%s is secured by User PIN\n", + ii, + cont->flags & CONTAINER_MAP_DEFAULT_CONTAINER ? + " (default)" : ""); + + if (pin_mode < pin_mode_n) { + pin_mode = pin_mode_n; + pin_cont_idx = ii; + } + } else if (signpin_info != NULL && + sc_pkcs15_compare_id(&key_obj->auth_id, &signpin_info->auth_id)) { + pin_mode_t pin_mode_n = + cont->flags & CONTAINER_MAP_DEFAULT_CONTAINER ? + SCF_DEFAULT_SIGN_PIN : SCF_NONDEFAULT_SIGN_PIN; + + logprintf(pCardData, 7, + "Container[%i]%s is secured by Sign PIN\n", + ii, + cont->flags & CONTAINER_MAP_DEFAULT_CONTAINER ? + " (default)" : ""); + + /* set flag that at least one key that uses the sign key needs PinCacheAlwaysPrompt */ + logprintf(pCardData, 7, "key_obj->user_consent: %d\n", (int) key_obj->user_consent); + if (key_obj->user_consent) { + vs->need_pin_always = 1; + logprintf(pCardData, 7, "vs->need_pin_always %d\n", (int) vs->need_pin_always); + } + + if (pin_mode < pin_mode_n) { + pin_mode = pin_mode_n; + pin_cont_idx = ii; + } + } else if (adminpin_info != NULL && + sc_pkcs15_compare_id(&key_obj->auth_id, &adminpin_info->auth_id)) { + logprintf(pCardData, 2, + "Container[%i] is secured by Admin PIN, might not work properly\n", + ii); + } else { + pin_mode_t pin_mode_n = + cont->flags & CONTAINER_MAP_DEFAULT_CONTAINER ? + SCF_DEFAULT_OTHER_PIN : SCF_NONDEFAULT_OTHER_PIN; + + logprintf(pCardData, 7, + "Container[%i]%s is secured by other PIN\n", + ii, + cont->flags & CONTAINER_MAP_DEFAULT_CONTAINER ? + " (default)" : ""); + + if (pin_mode < pin_mode_n) { + pin_mode = pin_mode_n; + pin_cont_idx = ii; + } + } + } + } + + if (cont->flags & CONTAINER_MAP_VALID_CONTAINER && + cont->flags & CONTAINER_MAP_DEFAULT_CONTAINER) found_default = 1; /* AT_KEYEXCHANGE is more general key usage, @@ -1455,7 +1951,6 @@ md_set_cmapfile(PCARD_DATA pCardData, struct md_file *file) cont->size_key_exchange = prkey_info->field_length; } - logprintf(pCardData, 7, "Container[%i]'s guid=%.*s\n", ii, (int) sizeof cont->guid, cont->guid); logprintf(pCardData, 7, "Container[%i]'s key-exchange:%"SC_FORMAT_LEN_SIZE_T"u, sign:%"SC_FORMAT_LEN_SIZE_T"u\n", ii, cont->size_key_exchange, cont->size_sign); @@ -1463,7 +1958,7 @@ md_set_cmapfile(PCARD_DATA pCardData, struct md_file *file) cont->id = prkey_info->id; cont->prkey_obj = prkey_objs[ii]; - /* Try to find the friend objects: certficate and public key */ + /* Try to find the friend objects: certificate and public key */ if (!sc_pkcs15_find_cert_by_id(vs->p15card, &cont->id, &cont->cert_obj)) logprintf(pCardData, 2, "found certificate friend '%.*s'\n", (int) sizeof cont->cert_obj->label, cont->cert_obj->label); @@ -1489,11 +1984,11 @@ md_set_cmapfile(PCARD_DATA pCardData, struct md_file *file) for (ii=0;iidata; - if (strcmp(dinfo->app_label, MD_DATA_APPLICAITON_NAME)) + if (strcmp(dinfo->app_label, "CSP")) continue; logprintf(pCardData, 2, "Found 'DATA' object '%.*s'\n", (int) sizeof dobjs[ii]->label, dobjs[ii]->label); - if (!strncmp(dobjs[ii]->label, MD_DATA_DEFAULT_CONT_LABEL, sizeof dobjs[ii]->label)) { + if (!strncmp(dobjs[ii]->label, "Default Key Container", sizeof dobjs[ii]->label)) { default_cont = dobjs[ii]; continue; } @@ -1513,6 +2008,108 @@ md_set_cmapfile(PCARD_DATA pCardData, struct md_file *file) } } #endif + + /* if no default container was found promote the best one (PIN-wise) to default */ + if (!found_default && (pin_mode == SCF_NONDEFAULT_SIGN_PIN || + pin_mode == SCF_NONDEFAULT_OTHER_PIN || + pin_mode == SCF_NONDEFAULT_USER_PIN)) { + struct md_pkcs15_container *cont = + &vs->p15_containers[pin_cont_idx]; + cont->flags |= CONTAINER_MAP_DEFAULT_CONTAINER; + + found_default = 1; + + logprintf(pCardData, 7, + "Container[%i] promoted to default\n", + pin_cont_idx); + + if (pin_mode == SCF_NONDEFAULT_SIGN_PIN) + pin_mode = SCF_DEFAULT_SIGN_PIN; + else if (pin_mode == SCF_NONDEFAULT_OTHER_PIN) + pin_mode = SCF_DEFAULT_OTHER_PIN; + else + pin_mode = SCF_DEFAULT_USER_PIN; + } + + /* if all containers use non-user PINs we need to make the best container PIN the user (primary) one */ + if (pin_mode == SCF_NONDEFAULT_SIGN_PIN || + pin_mode == SCF_DEFAULT_SIGN_PIN || + pin_mode == SCF_NONDEFAULT_OTHER_PIN || + pin_mode == SCF_DEFAULT_OTHER_PIN) { + struct sc_pkcs15_object *user_pin_old = + vs->pin_objs[ROLE_USER]; + struct sc_pkcs15_object *user_pin_new = + NULL; + + if (pin_mode == SCF_NONDEFAULT_SIGN_PIN || + pin_mode == SCF_DEFAULT_SIGN_PIN) { + user_pin_new = vs->pin_objs[MD_ROLE_USER_SIGN]; + vs->pin_objs[MD_ROLE_USER_SIGN] = NULL; + + logprintf(pCardData, 7, + "Sign PIN%s promoted to user one\n", + pin_mode == SCF_DEFAULT_SIGN_PIN ? + " (from default container)" : ""); + } else { + struct sc_pkcs15_object *key_obj = + vs->p15_containers[pin_cont_idx].prkey_obj; + struct sc_pkcs15_object *keypin_obj; + + if (sc_pkcs15_find_pin_by_auth_id(vs->p15card, &key_obj->auth_id, &keypin_obj)) + logprintf(pCardData, 2, + "Cannot find container[%i] auth id again, might not work properly\n", + pin_cont_idx); + else { + size_t pinidx; + + logprintf(pCardData, 7, + "Container[%i]%s PIN will be made the user one\n", + pin_cont_idx, + pin_mode == SCF_DEFAULT_OTHER_PIN ? + " (default)" : ""); + + for (pinidx = 0; pinidx < MD_MAX_PINS; pinidx++) { + struct sc_pkcs15_auth_info *pin_info; + + if (!vs->pin_objs[pinidx]) + continue; + + pin_info = + (struct sc_pkcs15_auth_info *)vs->pin_objs[pinidx]->data; + + if (sc_pkcs15_compare_id(&key_obj->auth_id, + &pin_info->auth_id)) { + vs->pin_objs[pinidx] = NULL; + break; + } + } + + user_pin_new = keypin_obj; + } + } + + if (user_pin_new) { + size_t pinidx; + + vs->pin_objs[ROLE_USER] = user_pin_new; + + for (pinidx = 0; pinidx < MD_MAX_PINS; pinidx++) { + if (vs->pin_objs[pinidx]) + continue; + + vs->pin_objs[pinidx] = user_pin_old; + break; + } + + if (pinidx >= MD_MAX_PINS) { + logprintf(pCardData, 2, + "no free slot for previous User PIN, replacing last one\n"); + + vs->pin_objs[MD_MAX_PINS - 1] = user_pin_old; + } + } + } + /* Initialize 'CMAPFILE' content from the P15 containers */ p = (PCONTAINER_MAP_RECORD)cmap_buf; for (ii=0; iiroot.files), "cardcf", EveryoneReadUserWriteAc, NULL, 0, &cardcf); if (dwret != SCARD_S_SUCCESS) - return dwret; + goto ret_cleanup; dwret = md_set_cardcf(pCardData, cardcf); if (dwret != SCARD_S_SUCCESS) - return dwret; + goto ret_cleanup; dwret = md_fs_add_file(pCardData, &(vs->root.files), "cardapps", EveryoneReadAdminWriteAc, NULL, 0, &cardapps); if (dwret != SCARD_S_SUCCESS) - return dwret; + goto ret_cleanup; dwret = md_set_cardapps(pCardData, cardapps); if (dwret != SCARD_S_SUCCESS) - return dwret; + goto ret_cleanup; - dwret = md_fs_add_directory(pCardData, &(vs->root.subdirs), "mscp", UserCreateDeleteDirAc, &mscp); + dwret = md_fs_add_directory(pCardData, &(vs->root.subdirs), "mscp", (CARD_DIRECTORY_ACCESS_CONDITION)UserCreateDeleteDirAc, &mscp); if (dwret != SCARD_S_SUCCESS) - return dwret; + goto ret_cleanup; dwret = md_fs_add_file(pCardData, &(mscp->files), "cmapfile", EveryoneReadUserWriteAc, NULL, 0, &cmapfile); if (dwret != SCARD_S_SUCCESS) - return dwret; + goto ret_cleanup; dwret = md_set_cmapfile(pCardData, cmapfile); if (dwret != SCARD_S_SUCCESS) - return dwret; + goto ret_cleanup; #ifdef OPENSSL_VERSION_NUMBER logprintf(pCardData, 3, @@ -1625,6 +2222,10 @@ md_fs_init(PCARD_DATA pCardData) "MD virtual file system initialized; Without OPENSSL\n"); #endif return SCARD_S_SUCCESS; + +ret_cleanup: + md_fs_finalize(pCardData); + return dwret; } /* Create SC context */ @@ -1684,6 +2285,8 @@ md_free_space(PCARD_DATA pCardData, PCARD_FREE_SPACE_INFO pCardFreeSpaceInfo) return ERROR_REVISION_MISMATCH; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) + return SCARD_E_INVALID_PARAMETER; /* Count free containers */ for (idx=0, count=0; idxpvVendorSpecific); + if (!vs) + return SCARD_E_INVALID_PARAMETER; if (flags & CARD_CREATE_CONTAINER_KEY_IMPORT) { if (key_algo == SC_ALGORITHM_RSA) { @@ -1833,7 +2438,7 @@ md_check_key_compatibility(PCARD_DATA pCardData, DWORD flags, DWORD key_type, static DWORD -md_pkcs15_generate_key(PCARD_DATA pCardData, DWORD idx, DWORD key_type, DWORD key_size) +md_pkcs15_generate_key(PCARD_DATA pCardData, DWORD idx, DWORD key_type, DWORD key_size, PIN_ID PinId) { VENDOR_SPECIFIC *vs; struct sc_card *card = NULL; @@ -1845,13 +2450,19 @@ md_pkcs15_generate_key(PCARD_DATA pCardData, DWORD idx, DWORD key_type, DWORD ke struct sc_pkcs15init_pubkeyargs pub_args; struct md_pkcs15_container *cont = NULL; int rv; - DWORD dw, dwret = SCARD_F_INTERNAL_ERROR; + DWORD dwret = SCARD_F_INTERNAL_ERROR; CHAR szGuid[MAX_CONTAINER_NAME_LEN +1] = "Default key label"; if (!pCardData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) + return SCARD_E_INVALID_PARAMETER; + + if (PinId >= MD_MAX_PINS || !vs->pin_objs[PinId]) + return SCARD_E_INVALID_PARAMETER; + card = vs->p15card->card; memset(&pub_args, 0, sizeof(pub_args)); @@ -1904,12 +2515,7 @@ md_pkcs15_generate_key(PCARD_DATA pCardData, DWORD idx, DWORD key_type, DWORD ke keygen_args.prkey_args.access_flags = MD_KEY_ACCESS; - dw = md_get_pin_by_role(pCardData, ROLE_USER, &pin_obj); - if (dw != SCARD_S_SUCCESS) { - logprintf(pCardData, 2, "MdGenerateKey(): cannot get User PIN object"); - return dw; - } - + pin_obj = vs->pin_objs[PinId]; auth_info = (struct sc_pkcs15_auth_info *) pin_obj->data; keygen_args.prkey_args.auth_id = pub_args.auth_id = auth_info->auth_id; @@ -1946,6 +2552,7 @@ md_pkcs15_generate_key(PCARD_DATA pCardData, DWORD idx, DWORD key_type, DWORD ke keygen_args.pubkey_label = keygen_args.prkey_args.label; rv = sc_pkcs15init_generate_key(vs->p15card, profile, &keygen_args, key_size, &cont->prkey_obj); + sc_pkcs15_erase_prkey(&keygen_args.prkey_args.key); if (rv < 0) { logprintf(pCardData, 3, "MdGenerateKey(): key generation failed: sc-error %i\n", rv); goto done; @@ -1970,11 +2577,10 @@ md_pkcs15_generate_key(PCARD_DATA pCardData, DWORD idx, DWORD key_type, DWORD ke return dwret; } - static DWORD -md_pkcs15_store_key(PCARD_DATA pCardData, DWORD idx, DWORD key_type, BYTE *blob, DWORD blob_size) +md_pkcs15_store_key(PCARD_DATA pCardData, DWORD idx, DWORD key_type, BYTE *blob, DWORD blob_size, PIN_ID PinId) { -#if OPENSSL_VERSION_NUMBER >= 0x10000000L +#ifdef ENABLE_OPENSSL VENDOR_SPECIFIC *vs; struct sc_card *card = NULL; struct sc_profile *profile = NULL; @@ -1986,13 +2592,19 @@ md_pkcs15_store_key(PCARD_DATA pCardData, DWORD idx, DWORD key_type, BYTE *blob, BYTE *ptr = blob; EVP_PKEY *pkey=NULL; int rv; - DWORD dw, dwret = SCARD_F_INTERNAL_ERROR; + DWORD dwret = SCARD_F_INTERNAL_ERROR; CHAR szGuid[MAX_CONTAINER_NAME_LEN +1] = "Default key label"; if (!pCardData) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) + return SCARD_E_INVALID_PARAMETER; + + if (PinId >= MD_MAX_PINS || !vs->pin_objs[PinId]) + return SCARD_E_INVALID_PARAMETER; + card = vs->p15card->card; pkey = b2i_PrivateKey((const unsigned char **)&ptr, blob_size); @@ -2032,12 +2644,7 @@ md_pkcs15_store_key(PCARD_DATA pCardData, DWORD idx, DWORD key_type, BYTE *blob, prkey_args.access_flags = MD_KEY_ACCESS; - dw = md_get_pin_by_role(pCardData, ROLE_USER, &pin_obj); - if (dw != SCARD_S_SUCCESS) { - logprintf(pCardData, 2, "MdStoreKey(): cannot get User PIN object"); - return dw; - } - + pin_obj = vs->pin_objs[PinId]; prkey_args.auth_id = ((struct sc_pkcs15_auth_info *) pin_obj->data)->auth_id; rv = sc_lock(card); @@ -2108,7 +2715,7 @@ md_pkcs15_store_key(PCARD_DATA pCardData, DWORD idx, DWORD key_type, BYTE *blob, #else logprintf(pCardData, 1, "MD store key not supported\n"); return SCARD_E_UNSUPPORTED_FEATURE; -#endif +#endif /* ENABLE_OPENSSL */ } @@ -2130,6 +2737,9 @@ md_pkcs15_store_certificate(PCARD_DATA pCardData, char *file_name, unsigned char logprintf(pCardData, 1, "MdStoreCert(): store certificate '%s'\n", file_name); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) + return SCARD_E_INVALID_PARAMETER; + card = vs->p15card->card; memset(&args, 0, sizeof(args)); @@ -2199,6 +2809,9 @@ md_query_key_sizes(PCARD_DATA pCardData, DWORD dwKeySpec, CARD_KEY_SIZES *pKeySi logprintf(pCardData, 1, "md_query_key_sizes: store dwKeySpec '%lu'\n", (unsigned long)dwKeySpec); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) + return SCARD_E_INVALID_PARAMETER; + count = vs->p15card->card->algorithm_count; pKeySizes->dwVersion = CARD_KEY_SIZES_CURRENT_VERSION; @@ -2207,7 +2820,7 @@ md_query_key_sizes(PCARD_DATA pCardData, DWORD dwKeySpec, CARD_KEY_SIZES *pKeySi pKeySizes->dwMaximumBitlen = 0; pKeySizes->dwIncrementalBitlen = 0; - /* dwKeySpec=0 is a special value when the key size is queried without specifing the algorithm. + /* dwKeySpec=0 is a special value when the key size is queried without specifying the algorithm. Used on old minidriver version. In this case, it is RSA */ if ((dwKeySpec == 0) || (dwKeySpec == AT_KEYEXCHANGE) || (dwKeySpec == AT_SIGNATURE)) { for (i = 0; i < count; i++) { @@ -2243,7 +2856,8 @@ md_query_key_sizes(PCARD_DATA pCardData, DWORD dwKeySpec, CARD_KEY_SIZES *pKeySi for (i = 0; i < count; i++) { algo_info = vs->p15card->card->algorithms + i; if (algo_info->algorithm == SC_ALGORITHM_EC) { - flag = SC_ALGORITHM_ECDH_CDH_RAW | SC_ALGORITHM_EXT_EC_NAMEDCURVE; + flag = SC_ALGORITHM_ECDH_CDH_RAW; + /* TODO check if namedCurve matches Windows supported curves */ /* ECDHE */ if ((dwKeySpec == AT_ECDHE_P256) && (algo_info->key_length == 256) && (algo_info->flags & flag)) { keysize = 256; @@ -2258,11 +2872,12 @@ md_query_key_sizes(PCARD_DATA pCardData, DWORD dwKeySpec, CARD_KEY_SIZES *pKeySi break; } /* ECDSA */ - flag = SC_ALGORITHM_ECDSA_HASH_NONE| + flag = SC_ALGORITHM_ECDSA_RAW| + SC_ALGORITHM_ECDSA_HASH_NONE| SC_ALGORITHM_ECDSA_HASH_SHA1| SC_ALGORITHM_ECDSA_HASH_SHA224| - SC_ALGORITHM_ECDSA_HASH_SHA256| - SC_ALGORITHM_EXT_EC_NAMEDCURVE; + SC_ALGORITHM_ECDSA_HASH_SHA256; + /* TODO check if namedCurve matches Windows supported curves */ if ((dwKeySpec == AT_ECDSA_P256) && (algo_info->key_length == 256) && (algo_info->flags & flag)) { keysize = 256; break; @@ -2276,17 +2891,17 @@ md_query_key_sizes(PCARD_DATA pCardData, DWORD dwKeySpec, CARD_KEY_SIZES *pKeySi break; } } - if (keysize) { - pKeySizes->dwMinimumBitlen = keysize; - pKeySizes->dwDefaultBitlen = keysize; - pKeySizes->dwMaximumBitlen = keysize; - pKeySizes->dwIncrementalBitlen = 1; - } else { - logprintf(pCardData, 0, - "No ECC key found (keyspec=%lu)\n", - (unsigned long)dwKeySpec); - return SCARD_E_INVALID_PARAMETER; - } + } + if (keysize) { + pKeySizes->dwMinimumBitlen = keysize; + pKeySizes->dwDefaultBitlen = keysize; + pKeySizes->dwMaximumBitlen = keysize; + pKeySizes->dwIncrementalBitlen = 1; + } else { + logprintf(pCardData, 0, + "No ECC key found (keyspec=%lu)\n", + (unsigned long)dwKeySpec); + return SCARD_E_INVALID_PARAMETER; } } @@ -2302,39 +2917,6 @@ md_query_key_sizes(PCARD_DATA pCardData, DWORD dwKeySpec, CARD_KEY_SIZES *pKeySi return SCARD_S_SUCCESS; } -static VOID CenterWindow(HWND hwndWindow, HWND hwndParent) -{ - RECT rectWindow, rectParent; - int nWidth,nHeight, nScreenWidth, nScreenHeight; - int nX, nY; - GetWindowRect(hwndWindow, &rectWindow); - - nWidth = rectWindow.right - rectWindow.left; - nHeight = rectWindow.bottom - rectWindow.top; - - nScreenWidth = GetSystemMetrics(SM_CXSCREEN); - nScreenHeight = GetSystemMetrics(SM_CYSCREEN); - - // make the window relative to its parent - if (hwndParent != NULL) { - GetWindowRect(hwndParent, &rectParent); - nX = ((rectParent.right - rectParent.left) - nWidth) / 2 + rectParent.left; - nY = ((rectParent.bottom - rectParent.top) - nHeight) / 2 + rectParent.top; - } - else { - nX = (nScreenWidth - nWidth) /2; - nY = (nScreenHeight - nHeight) /2; - } - // make sure that the dialog box never moves outside of the screen - if (nX < 0) nX = 0; - if (nY < 0) nY = 0; - if (nX + nWidth > nScreenWidth) nX = nScreenWidth - nWidth; - if (nY + nHeight > nScreenHeight) nY = nScreenHeight - nHeight; - - MoveWindow(hwndWindow, nX, nY, nWidth, nHeight, TRUE); -} - - static DWORD WINAPI md_dialog_perform_pin_operation_thread(PVOID lpParameter) { @@ -2366,70 +2948,135 @@ md_dialog_perform_pin_operation_thread(PVOID lpParameter) rv = (DWORD) ERROR_INVALID_PARAMETER; break; } - if (parameter[9] != 0) { - EndDialog((HWND) parameter[9], rv); + if (parameter[10] != 0) { + EndDialog((HWND) parameter[10], rv); } return (DWORD) rv; } -static INT_PTR CALLBACK md_dialog_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +static const char *md_get_ui_str(PCARD_DATA pCardData, enum ui_str id) { - UNREFERENCED_PARAMETER(wParam); - switch (message) - { - case WM_INITDIALOG: - { - HICON hIcon = NULL; - PCARD_DATA pCardData = (PCARD_DATA) (((LONG_PTR*)lParam)[7]); - VENDOR_SPECIFIC* vs = (VENDOR_SPECIFIC*) pCardData->pvVendorSpecific; - /* store parameter like pCardData for further use if needed */ - SetWindowLongPtr(hWnd, GWLP_USERDATA, lParam); - /* change the text shown on the screen */ - if (vs->wszPinContext ) { - SetWindowTextW(GetDlgItem(hWnd, IDC_PINPAD_TEXT), vs->wszPinContext ); - } - CenterWindow(hWnd, vs->hwndParent); - /* load the information icon */ - hIcon = (HICON) LoadImage(0, IDI_INFORMATION, IMAGE_ICON, 0, 0, LR_SHARED); - SendMessage(GetDlgItem(hWnd, IDC_PINPAD_ICON),STM_SETIMAGE,IMAGE_ICON, (LPARAM) hIcon); - /* change the icon */ - hIcon = LoadIcon(g_inst, MAKEINTRESOURCE(IDI_LOGO)); - if (hIcon) + const char *str = md_get_config_str(pCardData, id); + + if (str && *str == '\0') { + /* if the user used an empty string, remove the field by setting it to NULL */ + str = NULL; + } + + return str; +} + +static HRESULT CALLBACK md_dialog_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LONG_PTR dwRefData) +{ + LONG_PTR param; + + UNREFERENCED_PARAMETER(lParam); + switch (message) { + case TDN_CREATED: { - SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIcon); - SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIcon); + PCARD_DATA pCardData = (PCARD_DATA)((LONG_PTR*)dwRefData)[7]; + DWORD now = timeGetTime(); + + /* remove the icon from the window title */ + SendMessage(hWnd, WM_SETICON, (LPARAM) ICON_BIG, (LONG_PTR) NULL); + SendMessage(hWnd, WM_SETICON, (LPARAM) ICON_SMALL, (LONG_PTR) NULL); + + /* store parameter like pCardData for further use if needed */ + ((LONG_PTR*)dwRefData)[11] = (LONG_PTR) now; + SetWindowLongPtr(hWnd, GWLP_USERDATA, dwRefData); + ((LONG_PTR*)dwRefData)[10] = (LONG_PTR) hWnd; + + if (!md_is_pinpad_dlg_enable_cancel(pCardData)) { + int timeout = md_get_pinpad_dlg_timeout(pCardData); + if (timeout > 0) { + SendMessage(hWnd, TDM_SET_PROGRESS_BAR_RANGE, 0, MAKELPARAM(0, timeout*1000)); + } + + /* disable "Close" */ + SendMessage(hWnd, TDM_ENABLE_BUTTON, IDCLOSE, 0); + + /* launch the function in another thread context store the thread handle */ + ((LONG_PTR*)dwRefData)[9] = (LONG_PTR) CreateThread(NULL, 0, md_dialog_perform_pin_operation_thread, (LPVOID) dwRefData, 0, NULL); + } else { + int timeout = md_get_pinpad_dlg_timeout(pCardData); + if (timeout > 0) { + SendMessage(hWnd, TDM_SET_PROGRESS_BAR_RANGE, 0, 0); + SendMessage(hWnd, TDM_SET_PROGRESS_BAR_STATE, PBST_PAUSED, 0); + } + } } - /* launch the function in another thread context store the thread handle */ - ((LONG_PTR*)lParam)[9] = (LONG_PTR) hWnd; - ((LONG_PTR*)lParam)[8] = (LONG_PTR) CreateThread(NULL, 0, md_dialog_perform_pin_operation_thread, (PVOID) lParam, 0, NULL); - } - return TRUE; - case WM_DESTROY: - { + return S_OK; + + case TDN_TIMER: + SendMessage(hWnd, TDM_SET_PROGRESS_BAR_POS, wParam, 0L); + return S_OK; + + case TDN_BUTTON_CLICKED: + switch(LOWORD(wParam)) { + case IDCANCEL: + DestroyWindow(hWnd); + break; + + case IDOK: + param = GetWindowLongPtr(hWnd, GWLP_USERDATA); + if (param) { + PCARD_DATA pCardData = (PCARD_DATA)((LONG_PTR*)param)[7]; + + int timeout = md_get_pinpad_dlg_timeout(pCardData); + if (timeout > 0) { + DWORD start = (DWORD)((LONG_PTR*)dwRefData)[11]; + DWORD delta = timeGetTime() - start; + SendMessage(hWnd, TDM_SET_PROGRESS_BAR_RANGE, 0, MAKELPARAM(delta, delta + timeout*1000)); + SendMessage(hWnd, TDM_SET_PROGRESS_BAR_STATE, PBST_NORMAL, 0); + } + + /* disable "OK" and "Cancel" */ + SendMessage(hWnd, TDM_ENABLE_BUTTON, IDOK, 0); + SendMessage(hWnd, TDM_ENABLE_BUTTON, IDCANCEL, 0); + + /* disable "x" */ + HMENU menu = GetSystemMenu(hWnd, FALSE); + if (menu) { + EnableMenuItem(menu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED); + } + + /* launch the function in another thread context store the thread handle */ + ((LONG_PTR*)dwRefData)[9] = (LONG_PTR) CreateThread(NULL, 0, md_dialog_perform_pin_operation_thread, (LPVOID) dwRefData, 0, NULL); + } + break; + + default: + return S_FALSE; + } + break; + + case TDN_DESTROYED: /* clean resources used */ - LPARAM param = GetWindowLongPtr(hWnd, GWLP_USERDATA); + param = GetWindowLongPtr(hWnd, GWLP_USERDATA); if (param) { - HANDLE hThread = (HANDLE)((LONG_PTR*)param)[8]; + HANDLE hThread = (HANDLE)((LONG_PTR*)param)[9]; CloseHandle(hThread); } - } - break; + break; } - return FALSE; -} - + /* don't close the Task Dialog */ + return S_FALSE; +} -static int +static int md_dialog_perform_pin_operation(PCARD_DATA pCardData, int operation, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *pin_obj, const u8 *pin1, size_t pin1len, - const u8 *pin2, size_t *pin2len, BOOL displayUI) + const u8 *pin2, size_t *pin2len, BOOL displayUI, DWORD role) { - LONG_PTR parameter[10]; + LONG_PTR parameter[12]; INT_PTR result = 0; + TASKDIALOGCONFIG tc = {0}; int rv = 0; + BOOL checked, user_checked; VENDOR_SPECIFIC* pv = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + /* stack the parameters */ parameter[0] = (LONG_PTR)operation; parameter[1] = (LONG_PTR)p15card; @@ -2439,18 +3086,118 @@ md_dialog_perform_pin_operation(PCARD_DATA pCardData, int operation, struct sc_p parameter[5] = (LONG_PTR)pin2; parameter[6] = (LONG_PTR)pin2len; parameter[7] = (LONG_PTR)pCardData; - parameter[8] = 0; /* place holder for thread handle */ - parameter[9] = 0; /* place holder for window handle */ + parameter[8] = (LONG_PTR)role; + parameter[9] = 0; /* place holder for thread handle */ + parameter[10] = 0; /* place holder for window handle */ + parameter[11] = 0; /* place holder for end of timer */ + /* launch the function to perform in the same thread context */ if (!displayUI) { rv = md_dialog_perform_pin_operation_thread(parameter); SecureZeroMemory(parameter, sizeof(parameter)); return rv; } - /* launch the UI in the same thread context than the parent and the function to perform in another thread context + + /* launch the UI in the same thread context than the parent and the function to perform in another thread context this is the only way to display a modal dialog attached to a parent (hwndParent != 0) */ - result = DialogBoxParam(g_inst, MAKEINTRESOURCE(IDD_PINPAD), pv->hwndParent, md_dialog_proc, (LPARAM) parameter); + tc.hwndParent = pv->hwndParent; + tc.hInstance = g_inst; + + tc.pszWindowTitle = wchar_from_char_str(md_get_ui_str(pCardData, + MD_PINPAD_DLG_TITLE)); + tc.pszMainInstruction = wchar_from_char_str(md_get_ui_str(pCardData, + MD_PINPAD_DLG_MAIN)); + tc.pszExpandedControlText = wchar_from_char_str(md_get_ui_str(pCardData, + MD_PINPAD_DLG_CONTROL_EXPANDED)); + tc.pszCollapsedControlText = wchar_from_char_str(md_get_ui_str(pCardData, + MD_PINPAD_DLG_CONTROL_COLLAPSED)); + tc.pszExpandedInformation = wchar_from_char_str(md_get_ui_str(pCardData, + MD_PINPAD_DLG_EXPANDED)); + tc.pszVerificationText = wchar_from_char_str(md_get_ui_str(pCardData, + MD_PINPAD_DLG_VERIFICATION)); + switch (role) { + case ROLE_ADMIN: + tc.pszContent = wchar_from_char_str(md_get_ui_str(pCardData, + MD_PINPAD_DLG_CONTENT_ADMIN)); + break; + case MD_ROLE_USER_SIGN: + tc.pszContent = wchar_from_char_str(md_get_ui_str(pCardData, + MD_PINPAD_DLG_CONTENT_USER_SIGN)); + break; + case ROLE_USER: + default: + tc.pszContent = wchar_from_char_str(md_get_ui_str(pCardData, + MD_PINPAD_DLG_CONTENT_USER)); + break; + } + + if (pv->wszPinContext) { + /* overwrite the main instruction with the application's information if + * possible */ + tc.pszMainInstruction = pv->wszPinContext; + } + + tc.dwFlags = TDF_POSITION_RELATIVE_TO_WINDOW; + if (tc.pszExpandedInformation != NULL) { + tc.dwFlags |= TDF_EXPAND_FOOTER_AREA; + } + if (md_get_pinpad_dlg_timeout(pCardData) > 0) { + tc.dwFlags |= TDF_SHOW_PROGRESS_BAR | TDF_CALLBACK_TIMER; + } + + checked = !md_is_pinpad_dlg_enable_cancel(pCardData); + if (checked) { + tc.dwFlags |= TDF_VERIFICATION_FLAG_CHECKED; + /* can't use TDCBF_CANCEL_BUTTON since this would implicitly set TDF_ALLOW_DIALOG_CANCELLATION */ + tc.dwCommonButtons = TDCBF_CLOSE_BUTTON; + } else { + tc.dwFlags |= TDF_ALLOW_DIALOG_CANCELLATION; + tc.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_OK_BUTTON; + } + + tc.hMainIcon = md_get_pinpad_dlg_icon(pCardData); + if (tc.hMainIcon) { + tc.dwFlags |= TDF_USE_HICON_MAIN; + } else { + tc.pszMainIcon = MAKEINTRESOURCEW(IDI_SMARTCARD); + } + tc.pfCallback = md_dialog_proc; + tc.lpCallbackData = (LONG_PTR)parameter; + tc.cbSize = sizeof(tc); + + result = TaskDialogIndirect(&tc, NULL, NULL, &user_checked); + + if (user_checked != checked) { + if (pv && pv->ctx && pv->ctx->exe_path) { + HKEY hKey; + LSTATUS lstatus = RegOpenKeyExA(HKEY_CURRENT_USER, + SUBKEY_ENABLE_CANCEL, 0, KEY_WRITE, &hKey); + if (ERROR_SUCCESS != lstatus) { + lstatus = RegCreateKeyExA(HKEY_CURRENT_USER, + SUBKEY_ENABLE_CANCEL, 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_WRITE, NULL, &hKey, NULL); + } + if (ERROR_SUCCESS == lstatus) { + DWORD enable_cancel = 0; + if (user_checked == FALSE) { + enable_cancel = 1; + } + lstatus = RegSetValueEx(hKey, pv->ctx->exe_path, 0, REG_DWORD, + (const BYTE*)&enable_cancel, sizeof(enable_cancel)); + RegCloseKey(hKey); + } + } + } + + LocalFree((WCHAR *) tc.pszWindowTitle); + LocalFree((WCHAR *) tc.pszMainInstruction); + LocalFree((WCHAR *) tc.pszExpandedControlText); + LocalFree((WCHAR *) tc.pszCollapsedControlText); + LocalFree((WCHAR *) tc.pszExpandedInformation); + LocalFree((WCHAR *) tc.pszContent); + SecureZeroMemory(parameter, sizeof(parameter)); + return (int) result; } @@ -2522,40 +3269,45 @@ static DWORD md_translate_OpenSC_to_Windows_error(int OpenSCerror, DWORD WINAPI CardDeleteContext(__inout PCARD_DATA pCardData) { VENDOR_SPECIFIC *vs = NULL; + CRITICAL_SECTION hScard_lock; - if (md_static_data.attach_check != MD_STATIC_PROCESS_ATTACHED) - return SCARD_S_SUCCESS; + MD_FUNC_CALLED(pCardData, 1); if(!pCardData) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); logprintf(pCardData, 1, - "\nP:%lu T:%lu pCardData:%p hScard=0x%08X hSCardCtx=0x%08X CardDeleteContext\n", + "\nP:%lu T:%lu pCardData:%p hScard=0x%08"SC_FORMAT_LEN_SIZE_T"X hSCardCtx=0x%08"SC_FORMAT_LEN_SIZE_T"X CardDeleteContext\n", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData, - (unsigned int)pCardData->hScard, - (unsigned int)pCardData->hSCardCtx); + (size_t)pCardData->hScard, + (size_t)pCardData->hSCardCtx); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if(!vs) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + hScard_lock = vs->hScard_lock; + EnterCriticalSection(&hScard_lock); disassociate_card(pCardData); + md_fs_finalize(pCardData); if(vs->ctx) { logprintf(pCardData, 6, "release context\n"); sc_release_context(vs->ctx); - md_static_data.flags |= MD_STATIC_FLAG_CONTEXT_DELETED; vs->ctx = NULL; } logprintf(pCardData, 1, "**********************************************************************\n"); - md_fs_finalize(pCardData); pCardData->pfnCspFree(pCardData->pvVendorSpecific); pCardData->pvVendorSpecific = NULL; - return SCARD_S_SUCCESS; + LeaveCriticalSection(&hScard_lock); + DeleteCriticalSection(&hScard_lock); + + MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } DWORD WINAPI CardQueryCapabilities(__in PCARD_DATA pCardData, @@ -2563,21 +3315,30 @@ DWORD WINAPI CardQueryCapabilities(__in PCARD_DATA pCardData, { DWORD dwret; + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "pCardCapabilities=%p\n", pCardCapabilities); - if (!pCardData || !pCardCapabilities) - return SCARD_E_INVALID_PARAMETER; + if (!pCardData || !pCardCapabilities || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + dwret = check_card_status(pCardData, "CardQueryCapabilities"); + if (dwret != SCARD_S_SUCCESS) { + goto err; + } dwret = md_card_capabilities(pCardData, pCardCapabilities); - if (dwret != SCARD_S_SUCCESS) - return dwret; + if (dwret != SCARD_S_SUCCESS) { + goto err; + } - check_reader_status(pCardData); +err: + unlock(pCardData); - return SCARD_S_SUCCESS; + MD_FUNC_RETURN(pCardData, 1, dwret); } DWORD WINAPI CardDeleteContainer(__in PCARD_DATA pCardData, @@ -2587,67 +3348,106 @@ DWORD WINAPI CardDeleteContainer(__in PCARD_DATA pCardData, VENDOR_SPECIFIC *vs = NULL; DWORD dwret; struct md_pkcs15_container* cont; + + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardDeleteContainer(idx:%u)\n", (unsigned int)bContainerIndex); - if (!pCardData) - return SCARD_E_INVALID_PARAMETER; + if (!pCardData || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); - if (bContainerIndex >= MD_MAX_KEY_CONTAINERS) - return SCARD_E_INVALID_PARAMETER; + dwret = check_card_reader_status(pCardData, "CardDeleteContainer"); + if (dwret != SCARD_S_SUCCESS) { + goto err; + } + + if (bContainerIndex >= MD_MAX_KEY_CONTAINERS) { + dwret = SCARD_E_INVALID_PARAMETER; + goto err; + } if (!md_is_supports_container_key_gen(pCardData)) { logprintf(pCardData, 1, "Denied 'deletion' mechanism to delete container.\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + dwret = SCARD_E_UNSUPPORTED_FEATURE; + goto err; } vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); - if(!vs) - return SCARD_E_INVALID_PARAMETER; + if(!vs) { + dwret = SCARD_E_INVALID_PARAMETER; + goto err; + } cont = &(vs->p15_containers[bContainerIndex]); dwret = md_pkcs15_delete_object(pCardData, cont->prkey_obj); - if (dwret != SCARD_S_SUCCESS) { + if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "private key deletion failed\n"); - return dwret; + goto err; } dwret = md_pkcs15_delete_object(pCardData, cont->pubkey_obj); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "public key deletion failed\n"); - return dwret; + goto err; } ZeroMemory(cont, sizeof(struct md_pkcs15_container)); logprintf(pCardData, 1, "key deleted\n"); - return SCARD_S_SUCCESS; -} +err: + unlock(pCardData); -DWORD WINAPI CardCreateContainer(__in PCARD_DATA pCardData, - __in BYTE bContainerIndex, - __in DWORD dwFlags, - __in DWORD dwKeySpec, - __in DWORD dwKeySize, - __in PBYTE pbKeyData) + MD_FUNC_RETURN(pCardData, 1, dwret); +} + +/** The CardCreateContainerEx function creates a new key container that the +container index identifies and the bContainerIndex parameter specifies. The function +associates the key container with the PIN that the PinId parameter specified. +This function is useful if the card-edge does not allow for changing the key attributes +after the key container is created. This function replaces the need to call +CardSetContainerProperty to set the CCP_PIN_IDENTIFIER property CardCreateContainer +is called. +The caller of this function can provide the key material that the card imports. +This is useful in those situations in which the card either does not support internal +key generation or the caller requests that the key be archived in the card.*/ +DWORD WINAPI CardCreateContainerEx(__in PCARD_DATA pCardData, + __in BYTE bContainerIndex, + __in DWORD dwFlags, + __in DWORD dwKeySpec, + __in DWORD dwKeySize, + __in PBYTE pbKeyData, + __in PIN_ID PinId) { DWORD dwret; - if (!pCardData) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_CALLED(pCardData, 1); + + if (!pCardData || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + if (PinId == ROLE_ADMIN) { + dwret = SCARD_W_SECURITY_VIOLATION; + goto err; + } + + dwret = check_card_reader_status(pCardData, "CardCreateContainerEx"); + if (dwret != SCARD_S_SUCCESS) + goto err; logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, - "CardCreateContainerEx(idx:%u,flags:%lX,type:%lX,size:%lu,data:%p)\n", + "CardCreateContainerEx(idx:%u,flags:%lX,type:%lX,size:%lu,data:%p,pin:%u)\n", (unsigned int)bContainerIndex, (unsigned long)dwFlags, - (unsigned long)dwKeySpec, (unsigned long)dwKeySize, pbKeyData); + (unsigned long)dwKeySpec, (unsigned long)dwKeySize, pbKeyData, + (unsigned int)PinId); if (pbKeyData) { logprintf(pCardData, 7, "Key data\n"); @@ -2657,7 +3457,7 @@ DWORD WINAPI CardCreateContainer(__in PCARD_DATA pCardData, dwret = md_check_key_compatibility(pCardData, dwFlags, dwKeySpec, dwKeySize, pbKeyData); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "check key compatibility failed\n"); - return dwret; + goto err; } if (!md_is_supports_container_key_gen(pCardData)) { @@ -2672,34 +3472,56 @@ DWORD WINAPI CardCreateContainer(__in PCARD_DATA pCardData, if (!dwFlags) { logprintf(pCardData, 1, "Unsupported create container mechanism.\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + dwret = SCARD_E_UNSUPPORTED_FEATURE; + goto err; } if (dwFlags & CARD_CREATE_CONTAINER_KEY_GEN) { - dwret = md_pkcs15_generate_key(pCardData, bContainerIndex, dwKeySpec, dwKeySize); + dwret = md_pkcs15_generate_key(pCardData, bContainerIndex, dwKeySpec, dwKeySize, PinId); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "key generation failed\n"); - return dwret; + goto err; } logprintf(pCardData, 1, "key generated\n"); } else if ((dwFlags & CARD_CREATE_CONTAINER_KEY_IMPORT) && (pbKeyData != NULL)) { - dwret = md_pkcs15_store_key(pCardData, bContainerIndex, dwKeySpec, pbKeyData, dwKeySize); + dwret = md_pkcs15_store_key(pCardData, bContainerIndex, dwKeySpec, pbKeyData, dwKeySize, PinId); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "key store failed\n"); - return dwret; + goto err; } logprintf(pCardData, 1, "key imported\n"); } else { logprintf(pCardData, 1, "Invalid dwFlags value: 0x%lX\n", (unsigned long)dwFlags); - return SCARD_E_INVALID_PARAMETER; + dwret = SCARD_E_INVALID_PARAMETER; + goto err; } - return SCARD_S_SUCCESS; +err: + unlock(pCardData); + + MD_FUNC_RETURN(pCardData, 1, dwret); } +DWORD WINAPI CardCreateContainer(__in PCARD_DATA pCardData, + __in BYTE bContainerIndex, + __in DWORD dwFlags, + __in DWORD dwKeySpec, + __in DWORD dwKeySize, + __in PBYTE pbKeyData) +{ + DWORD dwret; + + MD_FUNC_CALLED(pCardData, 1); + + dwret = CardCreateContainerEx(pCardData, bContainerIndex, dwFlags, + dwKeySpec, dwKeySize, pbKeyData, + ROLE_USER); + + MD_FUNC_RETURN(pCardData, 1, dwret); +} typedef struct { PUBLICKEYSTRUC publickeystruc; @@ -2711,16 +3533,24 @@ DWORD WINAPI CardGetContainerInfo(__in PCARD_DATA pCardData, __in BYTE bContaine { VENDOR_SPECIFIC *vs = NULL; DWORD sz = 0; - DWORD ret = SCARD_F_UNKNOWN_ERROR; + DWORD ret; struct md_pkcs15_container *cont = NULL; struct sc_pkcs15_der pubkey_der; struct sc_pkcs15_prkey_info *prkey_info = NULL; int rv; + pubkey_der.value = NULL; + pubkey_der.len = 0; + + MD_FUNC_CALLED(pCardData, 1); if(!pCardData) - return SCARD_E_INVALID_PARAMETER; - if (!pContainerInfo) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + if (!pContainerInfo || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + ret = check_card_reader_status(pCardData, "CardGetContainerInfo"); + if (ret != SCARD_S_SUCCESS) + goto err; logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), @@ -2732,34 +3562,39 @@ DWORD WINAPI CardGetContainerInfo(__in PCARD_DATA pCardData, __in BYTE bContaine (unsigned long)pContainerInfo->cbSigPublicKey, (unsigned long)pContainerInfo->cbKeyExPublicKey); - if (dwFlags) - return SCARD_E_INVALID_PARAMETER; - if (bContainerIndex >= MD_MAX_KEY_CONTAINERS) - return SCARD_E_NO_KEY_CONTAINER; - if (pContainerInfo->dwVersion > CONTAINER_INFO_CURRENT_VERSION) - return ERROR_REVISION_MISMATCH; + if (dwFlags) { + ret = SCARD_E_INVALID_PARAMETER; + goto err; + } + if (bContainerIndex >= MD_MAX_KEY_CONTAINERS) { + ret = SCARD_E_NO_KEY_CONTAINER; + goto err; + } + if (pContainerInfo->dwVersion > CONTAINER_INFO_CURRENT_VERSION) { + ret = ERROR_REVISION_MISMATCH; + goto err; + } pContainerInfo->dwVersion = CONTAINER_INFO_CURRENT_VERSION; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) { + ret = SCARD_E_INVALID_PARAMETER; + goto err; + } + cont = &vs->p15_containers[bContainerIndex]; if (!cont->prkey_obj) { logprintf(pCardData, 7, "Container %u is empty\n", (unsigned int)bContainerIndex); - return SCARD_E_NO_KEY_CONTAINER; - } - - if (vs->p15card == NULL) { - return SCARD_F_INTERNAL_ERROR; + ret = SCARD_E_NO_KEY_CONTAINER; + goto err; } + ret = SCARD_F_UNKNOWN_ERROR; prkey_info = (struct sc_pkcs15_prkey_info *)cont->prkey_obj->data; - check_reader_status(pCardData); - pubkey_der.value = NULL; - pubkey_der.len = 0; - if ((cont->prkey_obj->content.value != NULL) && (cont->prkey_obj->content.len > 0)) { sc_der_copy(&pubkey_der, &cont->prkey_obj->content); ret = SCARD_S_SUCCESS; @@ -2791,9 +3626,10 @@ DWORD WINAPI CardGetContainerInfo(__in PCARD_DATA pCardData, __in BYTE bContaine if (!pubkey_der.value && cont->cert_obj) { struct sc_pkcs15_cert *cert = NULL; + int private_obj = cont->cert_obj->flags & SC_PKCS15_CO_FLAG_PRIVATE; logprintf(pCardData, 1, "now read certificate '%.*s'\n", (int) sizeof cont->cert_obj->label, cont->cert_obj->label); - rv = sc_pkcs15_read_certificate(vs->p15card, (struct sc_pkcs15_cert_info *)(cont->cert_obj->data), &cert); + rv = sc_pkcs15_read_certificate(vs->p15card, (struct sc_pkcs15_cert_info *)(cont->cert_obj->data), private_obj, &cert); if(!rv) { rv = sc_pkcs15_encode_pubkey(vs->ctx, cert->key, &pubkey_der.value, &pubkey_der.len); if (rv) { @@ -2817,14 +3653,15 @@ DWORD WINAPI CardGetContainerInfo(__in PCARD_DATA pCardData, __in BYTE bContaine if (!pubkey_der.value && (cont->size_sign || cont->size_key_exchange)) { logprintf(pCardData, 2, "cannot find public key\n"); - return SCARD_F_INTERNAL_ERROR; + ret = SCARD_F_INTERNAL_ERROR; + goto err; } if (ret != SCARD_S_SUCCESS) { logprintf(pCardData, 7, "GetContainerInfo(idx:%u) failed; error %lX", (unsigned int)bContainerIndex, (unsigned long)ret); - return ret; + goto err; } logprintf(pCardData, 7, "SubjectPublicKeyInfo:\n"); @@ -2839,8 +3676,10 @@ DWORD WINAPI CardGetContainerInfo(__in PCARD_DATA pCardData, __in BYTE bContaine if (cont->size_sign) { PUBRSAKEYSTRUCT_BASE *publicKey = (PUBRSAKEYSTRUCT_BASE *)pCardData->pfnCspAlloc(sz); - if (!publicKey) - return SCARD_E_NO_MEMORY; + if (!publicKey) { + ret = SCARD_E_NO_MEMORY; + goto err; + } CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, pubkey_der.value, (DWORD) pubkey_der.len, 0, publicKey, &sz); @@ -2856,8 +3695,10 @@ DWORD WINAPI CardGetContainerInfo(__in PCARD_DATA pCardData, __in BYTE bContaine if (cont->size_key_exchange) { PUBRSAKEYSTRUCT_BASE *publicKey = (PUBRSAKEYSTRUCT_BASE*)pCardData->pfnCspAlloc(sz); - if (!publicKey) - return SCARD_E_NO_MEMORY; + if (!publicKey) { + ret = SCARD_E_NO_MEMORY; + goto err; + } CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, pubkey_der.value, (DWORD) pubkey_der.len, 0, publicKey, &sz); @@ -2895,12 +3736,15 @@ DWORD WINAPI CardGetContainerInfo(__in PCARD_DATA pCardData, __in BYTE bContaine logprintf(pCardData, 3, "Unable to match the ECC public size to one of Microsoft algorithm %"SC_FORMAT_LEN_SIZE_T"u\n", cont->size_sign); - return SCARD_F_INTERNAL_ERROR; + ret = SCARD_F_INTERNAL_ERROR; + goto err; } publicKey = (BCRYPT_ECCKEY_BLOB *)pCardData->pfnCspAlloc(sz); - if (!publicKey) - return SCARD_E_NO_MEMORY; + if (!publicKey) { + ret = SCARD_E_NO_MEMORY; + goto err; + } publicKey->cbKey = (DWORD)(pubkey_der.len -3) /2; publicKey->dwMagic = dwMagic; @@ -2910,8 +3754,10 @@ DWORD WINAPI CardGetContainerInfo(__in PCARD_DATA pCardData, __in BYTE bContaine memcpy(((PBYTE)publicKey) + sizeof(BCRYPT_ECCKEY_BLOB), pubkey_der.value + 3, pubkey_der.len -3); logprintf(pCardData, 3, - "return info on ECC SIGN_CONTAINER_INDEX %u\n", - (unsigned int)bContainerIndex); + "return info on ECC SIGN_CONTAINER_INDEX %u cbKey:%u dwMagic:%u\n", + (unsigned int)bContainerIndex, + (unsigned int)publicKey->cbKey, + (unsigned int)publicKey->dwMagic); } if (cont->size_key_exchange) { sz = (DWORD) (sizeof(BCRYPT_ECCKEY_BLOB) + pubkey_der.len -3); @@ -2931,12 +3777,15 @@ DWORD WINAPI CardGetContainerInfo(__in PCARD_DATA pCardData, __in BYTE bContaine logprintf(pCardData, 3, "Unable to match the ECC public size to one of Microsoft algorithm %"SC_FORMAT_LEN_SIZE_T"u\n", cont->size_key_exchange); - return SCARD_F_INTERNAL_ERROR; + ret = SCARD_F_INTERNAL_ERROR; + goto err; } publicKey = (BCRYPT_ECCKEY_BLOB *)pCardData->pfnCspAlloc(sz); - if (!publicKey) - return SCARD_E_NO_MEMORY; + if (!publicKey) { + ret = SCARD_E_NO_MEMORY; + goto err; + } publicKey->cbKey = (DWORD)(pubkey_der.len -3) /2; publicKey->dwMagic = dwMagic; @@ -2946,15 +3795,28 @@ DWORD WINAPI CardGetContainerInfo(__in PCARD_DATA pCardData, __in BYTE bContaine memcpy(((PBYTE)publicKey) + sizeof(BCRYPT_ECCKEY_BLOB), pubkey_der.value + 3, pubkey_der.len -3); logprintf(pCardData, 3, - "return info on ECC KEYX_CONTAINER_INDEX %u\n", - (unsigned int)bContainerIndex); + "return info on ECC KEYX_CONTAINER_INDEX %u cbKey:%u dwMagic:%u\n", + (unsigned int)bContainerIndex, + (unsigned int)publicKey->cbKey, + (unsigned int)publicKey->dwMagic); } } } - - logprintf(pCardData, 7, "returns container(idx:%u) info", + logprintf(pCardData, 7, "returns container(idx:%u) info\n", (unsigned int)bContainerIndex); - return SCARD_S_SUCCESS; + + logprintf(pCardData, 1, + "CardGetContainerInfo bContainerIndex=%u, dwFlags=0x%08X, dwVersion=%lu, cbSigPublicKey=%lu, cbKeyExPublicKey=%lu\n", + (unsigned int)bContainerIndex, (unsigned int)dwFlags, + (unsigned long)pContainerInfo->dwVersion, + (unsigned long)pContainerInfo->cbSigPublicKey, + (unsigned long)pContainerInfo->cbKeyExPublicKey); + +err: + free(pubkey_der.value); + unlock(pCardData); + + MD_FUNC_RETURN(pCardData, 1, ret); } DWORD WINAPI CardAuthenticatePin(__in PCARD_DATA pCardData, @@ -2964,6 +3826,9 @@ DWORD WINAPI CardAuthenticatePin(__in PCARD_DATA pCardData, __out_opt PDWORD pcAttemptsRemaining) { PIN_ID PinId = 0; + + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); @@ -2977,12 +3842,12 @@ DWORD WINAPI CardAuthenticatePin(__in PCARD_DATA pCardData, PinId = ROLE_ADMIN; } else { - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } if (pbPin == NULL) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); - return CardAuthenticateEx(pCardData, PinId, CARD_PIN_SILENT_CONTEXT, pbPin, cbPin, NULL, NULL, pcAttemptsRemaining); + MD_FUNC_RETURN(pCardData, 1, CardAuthenticateEx(pCardData, PinId, CARD_PIN_SILENT_CONTEXT, pbPin, cbPin, NULL, NULL, pcAttemptsRemaining)); } @@ -2991,40 +3856,57 @@ DWORD WINAPI CardGetChallenge(__in PCARD_DATA pCardData, __out PDWORD pcbChallengeData) { VENDOR_SPECIFIC *vs; + DWORD dwret; int rv; + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardGetChallenge\n"); if(!pCardData) - return SCARD_E_INVALID_PARAMETER; - if (!ppbChallengeData || !pcbChallengeData) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + if (!ppbChallengeData || !pcbChallengeData || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); - vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + dwret = check_card_reader_status(pCardData, "CardGetChallenge"); + if (dwret != SCARD_S_SUCCESS) { + goto err; + } - check_reader_status(pCardData); + vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) { + dwret = SCARD_E_INVALID_PARAMETER; + goto err; + } *pcbChallengeData = 8; *ppbChallengeData = (PBYTE) pCardData->pfnCspAlloc(8); - if (!*ppbChallengeData) - return SCARD_E_NO_MEMORY; + if (!*ppbChallengeData) { + dwret = SCARD_E_NO_MEMORY; + goto err; + } rv = sc_get_challenge(vs->p15card->card, *ppbChallengeData, 8); - if (rv) { + if (rv < 0) { logprintf(pCardData, 1, "Get challenge failed: %s\n", sc_strerror(rv)); pCardData->pfnCspFree(*ppbChallengeData); *ppbChallengeData = NULL; - return SCARD_E_UNEXPECTED; + dwret = SCARD_E_UNEXPECTED; + goto err; } + dwret = SCARD_S_SUCCESS; logprintf(pCardData, 7, "returns %lu bytes:\n", (unsigned long)*pcbChallengeData); loghex(pCardData, 7, *ppbChallengeData, *pcbChallengeData); - return SCARD_S_SUCCESS; + +err: + unlock(pCardData); + MD_FUNC_RETURN(pCardData, 1, dwret); } @@ -3033,11 +3915,14 @@ DWORD WINAPI CardAuthenticateChallenge(__in PCARD_DATA pCardData, __in DWORD cbResponseData, __out_opt PDWORD pcAttemptsRemaining) { + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardAuthenticateChallenge - unsupported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } @@ -3050,8 +3935,12 @@ DWORD WINAPI CardUnblockPin(__in PCARD_DATA pCardData, __in DWORD cRetryCount, __in DWORD dwFlags) { - if(!pCardData) - return SCARD_E_INVALID_PARAMETER; + DWORD r = SCARD_S_SUCCESS; + + MD_FUNC_CALLED(pCardData, 1); + + if(!pCardData || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), @@ -3060,43 +3949,52 @@ DWORD WINAPI CardUnblockPin(__in PCARD_DATA pCardData, if (pwszUserId == NULL) { logprintf(pCardData, 1, "no user ID\n"); - return SCARD_E_INVALID_PARAMETER; + r = SCARD_E_INVALID_PARAMETER; + goto err; } if (wcscmp(wszCARD_USER_USER, pwszUserId) != 0 && wcscmp(wszCARD_USER_ADMIN,pwszUserId) != 0) { logprintf(pCardData, 1, "unknown user ID %S\n", pwszUserId); - return SCARD_E_INVALID_PARAMETER; + r = SCARD_E_INVALID_PARAMETER; + goto err; } if (wcscmp(wszCARD_USER_ADMIN, pwszUserId) == 0) { logprintf(pCardData, 1, "unlocking admin not supported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + r = SCARD_E_UNSUPPORTED_FEATURE; + goto err; } if (dwFlags & CARD_AUTHENTICATE_PIN_CHALLENGE_RESPONSE) { logprintf(pCardData, 1, - "challenge / response not supported, we'll treat response as a PUK\n"); + "challenge / response not supported, we'll treat response as a PUK\n"); logprintf(pCardData, 1, - "note that you'll need to type PUK in hex (replace every PUK digit X with '3X') in Win CAD unblock dialog response field\n"); + "note that you'll need to type PUK in hex (replace every PUK digit X with '3X') in Win CAD unblock dialog response field\n"); dwFlags &= ~CARD_AUTHENTICATE_PIN_CHALLENGE_RESPONSE; } if (dwFlags) { logprintf(pCardData, 1, "flags of %x not supported\n", - (unsigned int)dwFlags); - return SCARD_E_INVALID_PARAMETER; + (unsigned int)dwFlags); + r = SCARD_E_INVALID_PARAMETER; + goto err; } logprintf(pCardData, 1, - "UserID('%S'), AuthData(%p, %lu), NewPIN(%p, %lu), Retry(%lu), dwFlags(0x%lX)\n", - pwszUserId, pbAuthenticationData, - (unsigned long)cbAuthenticationData, pbNewPinData, - (unsigned long)cbNewPinData, (unsigned long)cRetryCount, - (unsigned long)dwFlags); - - return CardChangeAuthenticatorEx(pCardData, - PIN_CHANGE_FLAG_UNBLOCK | - CARD_PIN_SILENT_CONTEXT, - ROLE_ADMIN, pbAuthenticationData, - cbAuthenticationData, ROLE_USER, - pbNewPinData, cbNewPinData, - cRetryCount, NULL); + "UserID('%S'), AuthData(%p, %lu), NewPIN(%p, %lu), Retry(%lu), dwFlags(0x%lX)\n", + pwszUserId, pbAuthenticationData, + (unsigned long)cbAuthenticationData, pbNewPinData, + (unsigned long)cbNewPinData, (unsigned long)cRetryCount, + (unsigned long)dwFlags); + + r = CardChangeAuthenticatorEx(pCardData, + PIN_CHANGE_FLAG_UNBLOCK | + CARD_PIN_SILENT_CONTEXT, + ROLE_ADMIN, pbAuthenticationData, + cbAuthenticationData, ROLE_USER, + pbNewPinData, cbNewPinData, + cRetryCount, NULL); + +err: + unlock(pCardData); + + MD_FUNC_RETURN(pCardData, 1, r); } @@ -3110,99 +4008,137 @@ DWORD WINAPI CardChangeAuthenticator(__in PCARD_DATA pCardData, __in DWORD dwFlags, __out_opt PDWORD pcAttemptsRemaining) { + DWORD r = SCARD_S_SUCCESS; PIN_ID pinid; - if(!pCardData) - return SCARD_E_INVALID_PARAMETER; + + MD_FUNC_CALLED(pCardData, 1); + + if(!pCardData || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", - (unsigned long)GetCurrentProcessId(), - (unsigned long)GetCurrentThreadId(), pCardData); + (unsigned long)GetCurrentProcessId(), + (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardChangeAuthenticator\n"); - if (pwszUserId == NULL) - return SCARD_E_INVALID_PARAMETER; + if (pwszUserId == NULL) { + r = SCARD_E_INVALID_PARAMETER; + goto err; + } if (dwFlags == CARD_AUTHENTICATE_PIN_CHALLENGE_RESPONSE) { logprintf(pCardData, 1, "Other then 'authentication' the PIN are not supported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + r = SCARD_E_UNSUPPORTED_FEATURE; + goto err; } else if (dwFlags != CARD_AUTHENTICATE_PIN_PIN){ - return SCARD_E_INVALID_PARAMETER; + r = SCARD_E_INVALID_PARAMETER; + goto err; } - if (wcscmp(wszCARD_USER_USER, pwszUserId) != 0 && wcscmp(wszCARD_USER_ADMIN, pwszUserId) != 0) - return SCARD_E_INVALID_PARAMETER; + if (wcscmp(wszCARD_USER_USER, pwszUserId) != 0 && wcscmp(wszCARD_USER_ADMIN, pwszUserId) != 0) { + r = SCARD_E_INVALID_PARAMETER; + goto err; + } logprintf(pCardData, 1, - "UserID('%S'), CurrentPIN(%p, %lu), NewPIN(%p, %lu), Retry(%lu), dwFlags(0x%lX)\n", - pwszUserId, pbCurrentAuthenticator, - (unsigned long)cbCurrentAuthenticator, pbNewAuthenticator, - (unsigned long)cbNewAuthenticator, (unsigned long)cRetryCount, - (unsigned long)dwFlags); + "UserID('%S'), CurrentPIN(%p, %lu), NewPIN(%p, %lu), Retry(%lu), dwFlags(0x%lX)\n", + pwszUserId, pbCurrentAuthenticator, + (unsigned long)cbCurrentAuthenticator, pbNewAuthenticator, + (unsigned long)cbNewAuthenticator, (unsigned long)cRetryCount, + (unsigned long)dwFlags); if (wcscmp(wszCARD_USER_USER, pwszUserId) == 0) pinid = ROLE_USER; else pinid = ROLE_ADMIN; - return CardChangeAuthenticatorEx(pCardData, PIN_CHANGE_FLAG_CHANGEPIN | - CARD_PIN_SILENT_CONTEXT, pinid, - pbCurrentAuthenticator, - cbCurrentAuthenticator, pinid, - pbNewAuthenticator, cbNewAuthenticator, - cRetryCount, pcAttemptsRemaining); + r = CardChangeAuthenticatorEx(pCardData, PIN_CHANGE_FLAG_CHANGEPIN | + CARD_PIN_SILENT_CONTEXT, pinid, + pbCurrentAuthenticator, + cbCurrentAuthenticator, pinid, + pbNewAuthenticator, cbNewAuthenticator, + cRetryCount, pcAttemptsRemaining); + +err: + unlock(pCardData); + MD_FUNC_RETURN(pCardData, 1, r); } -/* this function is not called on purpose. -If a deauthentication is not possible, it should be set to NULL in CardAcquireContext. -Because this function do nothing - it is not called. -Note: the PIN freshnesh will be managed by the Base CSP*/ +/* Note: the PIN freshness will be managed by the Base CSP */ DWORD WINAPI CardDeauthenticate(__in PCARD_DATA pCardData, __in LPWSTR pwszUserId, __in DWORD dwFlags) { + DWORD dwret; VENDOR_SPECIFIC* vs = NULL; int rv; + + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%ld T:%ld pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardDeauthenticate(%S) %lu\n", NULLWSTR(pwszUserId), (unsigned long)dwFlags); - if(!pCardData) - return SCARD_E_INVALID_PARAMETER; + if(!pCardData || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + dwret = check_card_reader_status(pCardData, "CardDeauthenticate"); + if (dwret != SCARD_S_SUCCESS) + goto err; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) { + dwret = SCARD_E_INVALID_PARAMETER; + goto err; + } sc_pkcs15_pincache_clear(vs->p15card); rv = sc_logout(vs->p15card->card); - if (rv != SC_SUCCESS) - return SCARD_E_UNSUPPORTED_FEATURE; - /* force a reset of a card - SCARD_S_SUCCESS do not lead to the reset of the card and leave it still authenticated */ - return SCARD_S_SUCCESS; + if (rv != SC_SUCCESS) { + /* force a reset of a card - SCARD_S_SUCCESS do not lead to the reset + * of the card and leave it still authenticated */ + dwret = SCARD_E_UNSUPPORTED_FEATURE; + goto err; + } + + dwret = SCARD_S_SUCCESS; + +err: + unlock(pCardData); + + MD_FUNC_RETURN(pCardData, 1, dwret); } DWORD WINAPI CardCreateDirectory(__in PCARD_DATA pCardData, __in LPSTR pszDirectoryName, __in CARD_DIRECTORY_ACCESS_CONDITION AccessCondition) { + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardCreateDirectory - unsupported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } DWORD WINAPI CardDeleteDirectory(__in PCARD_DATA pCardData, __in LPSTR pszDirectoryName) { + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardDeleteDirectory(%s) - unsupported\n", NULLSTR(pszDirectoryName)); - return SCARD_E_UNSUPPORTED_FEATURE; + + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } DWORD WINAPI CardCreateFile(__in PCARD_DATA pCardData, @@ -3214,6 +4150,8 @@ DWORD WINAPI CardCreateFile(__in PCARD_DATA pCardData, struct md_directory *dir = NULL; DWORD dwret; + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); @@ -3222,17 +4160,27 @@ DWORD WINAPI CardCreateFile(__in PCARD_DATA pCardData, NULLSTR(pszDirectoryName), NULLSTR(pszFileName), (unsigned long)cbInitialCreationSize, AccessCondition); + if (!lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + dwret = check_card_status(pCardData, "CardCreateFile"); + if (dwret != SCARD_S_SUCCESS) + goto err; + dwret = md_fs_find_directory(pCardData, NULL, pszDirectoryName, &dir); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "CardCreateFile() cannot find parent directory '%s'", NULLSTR(pszDirectoryName)); - return dwret; + goto err; } dwret = md_fs_add_file(pCardData, &dir->files, pszFileName, AccessCondition, NULL, cbInitialCreationSize, NULL); if (dwret != SCARD_S_SUCCESS) - return dwret; + goto err; - return SCARD_S_SUCCESS; +err: + unlock(pCardData); + + MD_FUNC_RETURN(pCardData, 1, dwret); } @@ -3244,45 +4192,51 @@ DWORD WINAPI CardReadFile(__in PCARD_DATA pCardData, __out PDWORD pcbData) { struct md_file *file = NULL; + DWORD dwret; + + MD_FUNC_CALLED(pCardData, 1); logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardReadFile\n"); - if(!pCardData) - return SCARD_E_INVALID_PARAMETER; + if(!pCardData || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); logprintf(pCardData, 2, "pszDirectoryName = %s, pszFileName = %s, dwFlags = %lX, pcbData=%p, ppbData=%p\n", NULLSTR(pszDirectoryName), NULLSTR(pszFileName), (unsigned long)dwFlags, pcbData, ppbData); - if (!pszFileName || !strlen(pszFileName)) - return SCARD_E_INVALID_PARAMETER; - if (dwFlags) - return SCARD_E_INVALID_PARAMETER; + if (!pszFileName || !strlen(pszFileName) || dwFlags) { + dwret = SCARD_E_INVALID_PARAMETER; + goto err; + } - check_reader_status(pCardData); + dwret = check_card_reader_status(pCardData, "CardReadFile"); + if (dwret != SCARD_S_SUCCESS) + goto err; md_fs_find_file(pCardData, pszDirectoryName, pszFileName, &file); if (!file) { logprintf(pCardData, 2, "CardReadFile(): file '%s' not found in '%s'\n", NULLSTR(pszFileName), NULLSTR(pszDirectoryName)); - return SCARD_E_FILE_NOT_FOUND; + dwret = SCARD_E_FILE_NOT_FOUND; + goto err; } if (!file->blob) { - DWORD dwret; - dwret = md_fs_read_content(pCardData, pszDirectoryName, file); if (dwret != SCARD_S_SUCCESS) - return dwret; + goto err; } if (ppbData) { *ppbData = pCardData->pfnCspAlloc(file->size); - if(!*ppbData) - return SCARD_E_NO_MEMORY; + if(!*ppbData) { + dwret = SCARD_E_NO_MEMORY; + goto err; + } memcpy(*ppbData, file->blob, file->size); } @@ -3292,7 +4246,11 @@ DWORD WINAPI CardReadFile(__in PCARD_DATA pCardData, logprintf(pCardData, 7, "returns '%s' content:\n", NULLSTR(pszFileName)); loghex(pCardData, 7, file->blob, file->size); - return SCARD_S_SUCCESS; + +err: + unlock(pCardData); + + MD_FUNC_RETURN(pCardData, 1, dwret); } @@ -3306,15 +4264,19 @@ DWORD WINAPI CardWriteFile(__in PCARD_DATA pCardData, struct md_file *file = NULL; DWORD dwret; - if(!pCardData) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_CALLED(pCardData, 1); + + if(!pCardData || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardWriteFile() dirName:'%s', fileName:'%s' \n", NULLSTR(pszDirectoryName), NULLSTR(pszFileName)); - check_reader_status(pCardData); + dwret = check_card_reader_status(pCardData, "CardWriteFile"); + if (dwret != SCARD_S_SUCCESS) + goto err; if (pbData && cbData) { logprintf(pCardData, 1, "CardWriteFile try to write (%lu):\n", @@ -3325,7 +4287,8 @@ DWORD WINAPI CardWriteFile(__in PCARD_DATA pCardData, md_fs_find_file(pCardData, pszDirectoryName, pszFileName, &file); if (!file) { logprintf(pCardData, 2, "CardWriteFile(): file '%s' not found in '%s'\n", NULLSTR(pszFileName), NULLSTR(pszDirectoryName)); - return SCARD_E_FILE_NOT_FOUND; + dwret = SCARD_E_FILE_NOT_FOUND; + goto err; } logprintf(pCardData, 7, "set content of '%s' to:\n", NULLSTR(pszFileName)); @@ -3335,20 +4298,23 @@ DWORD WINAPI CardWriteFile(__in PCARD_DATA pCardData, if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "cannot set file content: %lu\n", (unsigned long)dwret); - return dwret; + goto err; } if (pszDirectoryName && !strcmp(pszDirectoryName, "mscp")) { if ((strstr(pszFileName, "kxc") == pszFileName) || (strstr(pszFileName, "ksc") == pszFileName)) { dwret = md_pkcs15_store_certificate(pCardData, pszFileName, pbData, cbData); if (dwret != SCARD_S_SUCCESS) - return dwret; + goto err; logprintf(pCardData, 2, "md_pkcs15_store_certificate() OK\n"); } } logprintf(pCardData, 2, "write '%s' ok.\n", NULLSTR(pszFileName)); - return SCARD_S_SUCCESS; + +err: + unlock(pCardData); + MD_FUNC_RETURN(pCardData, 1, dwret); } DWORD WINAPI CardDeleteFile(__in PCARD_DATA pCardData, @@ -3358,25 +4324,31 @@ DWORD WINAPI CardDeleteFile(__in PCARD_DATA pCardData, { DWORD dwret; + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardDeleteFile(%s, %s) called\n", NULLSTR(pszDirectoryName), NULLSTR(pszFileName)); - if(!pCardData) - return SCARD_E_INVALID_PARAMETER; + if(!pCardData || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); - check_reader_status(pCardData); + dwret = check_card_reader_status(pCardData, "CardDeleteFile"); + if (dwret != SCARD_S_SUCCESS) + goto err; dwret = md_fs_delete_file(pCardData, pszDirectoryName, pszFileName); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 2, "CardDeleteFile(): delete file error: %lX\n", (unsigned long)dwret); - return dwret; + goto err; } - return SCARD_S_SUCCESS; +err: + unlock(pCardData); + MD_FUNC_RETURN(pCardData, 1, dwret); } @@ -3387,28 +4359,38 @@ DWORD WINAPI CardEnumFiles(__in PCARD_DATA pCardData, __in DWORD dwFlags) { VENDOR_SPECIFIC *vs = NULL; + DWORD dwret; char mstr[0x100]; struct md_directory *dir = NULL; struct md_file *file = NULL; size_t offs; + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardEnumFiles() directory '%s'\n", NULLSTR(pszDirectoryName)); - if (!pCardData) - return SCARD_E_INVALID_PARAMETER; - if (!pmszFileNames || !pdwcbFileName) - return SCARD_E_INVALID_PARAMETER; + if (!pCardData || !pmszFileNames || !pdwcbFileName || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (dwFlags) { logprintf(pCardData, 1, "CardEnumFiles() dwFlags not 'zero' -- %lX\n", (unsigned long)dwFlags); - return SCARD_E_INVALID_PARAMETER; + dwret = SCARD_E_INVALID_PARAMETER; + goto err; } + dwret = check_card_status(pCardData, "CardEnumFiles"); + if (dwret != SCARD_S_SUCCESS) + goto err; + vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) { + dwret = SCARD_E_INVALID_PARAMETER; + goto err; + } memset(mstr, 0, sizeof(mstr)); @@ -3418,7 +4400,8 @@ DWORD WINAPI CardEnumFiles(__in PCARD_DATA pCardData, md_fs_find_directory(pCardData, NULL, pszDirectoryName, &dir); if (!dir) { logprintf(pCardData, 2, "enum files() failed: directory '%s' not found\n", NULLSTR(pszDirectoryName)); - return SCARD_E_FILE_NOT_FOUND; + dwret = SCARD_E_FILE_NOT_FOUND; + goto err; } file = dir->files; @@ -3432,12 +4415,17 @@ DWORD WINAPI CardEnumFiles(__in PCARD_DATA pCardData, offs += 1; *pmszFileNames = (LPSTR)(*pCardData->pfnCspAlloc)(offs); - if (*pmszFileNames == NULL) - return SCARD_E_NO_MEMORY; + if (*pmszFileNames == NULL) { + dwret = SCARD_E_NO_MEMORY; + goto err; + } CopyMemory(*pmszFileNames, mstr, offs); *pdwcbFileName = (DWORD) offs; - return SCARD_S_SUCCESS; + +err: + unlock(pCardData); + MD_FUNC_RETURN(pCardData, 1, dwret); } @@ -3446,24 +4434,37 @@ DWORD WINAPI CardGetFileInfo(__in PCARD_DATA pCardData, __in LPSTR pszFileName, __inout PCARD_FILE_INFO pCardFileInfo) { + DWORD dwret; struct md_file *file = NULL; + MD_FUNC_CALLED(pCardData, 1); + + if(!pCardData || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardGetFileInfo(dirName:'%s',fileName:'%s', out %p)\n", NULLSTR(pszDirectoryName), NULLSTR(pszFileName), pCardFileInfo); + dwret = check_card_status(pCardData, "CardGetFileInfo"); + if (dwret != SCARD_S_SUCCESS) + goto err; + md_fs_find_file(pCardData, pszDirectoryName, pszFileName, &file); if (!file) { logprintf(pCardData, 2, "CardWriteFile(): file '%s' not found in '%s'\n", NULLSTR(pszFileName), NULLSTR(pszDirectoryName)); - return SCARD_E_FILE_NOT_FOUND; + dwret = SCARD_E_FILE_NOT_FOUND; + goto err; } pCardFileInfo->dwVersion = CARD_FILE_INFO_CURRENT_VERSION; pCardFileInfo->cbFileSize = (DWORD) file->size; pCardFileInfo->AccessCondition = file->acl; - return SCARD_S_SUCCESS; +err: + unlock(pCardData); + MD_FUNC_RETURN(pCardData, 1, dwret); } @@ -3472,6 +4473,8 @@ DWORD WINAPI CardQueryFreeSpace(__in PCARD_DATA pCardData, __in DWORD dwFlags, { DWORD dwret; + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); @@ -3480,20 +4483,25 @@ DWORD WINAPI CardQueryFreeSpace(__in PCARD_DATA pCardData, __in DWORD dwFlags, pCardFreeSpaceInfo, (unsigned long)dwFlags, (unsigned long)pCardFreeSpaceInfo->dwVersion); - if (!pCardData) - return SCARD_E_INVALID_PARAMETER; + if (!pCardData || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); - check_reader_status(pCardData); + dwret = check_card_status(pCardData, "CardQueryFreeSpace"); + if (dwret != SCARD_S_SUCCESS) + goto err; dwret = md_free_space(pCardData, pCardFreeSpaceInfo); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "CardQueryFreeSpace() md free space error"); - return dwret; + goto err; } logprintf(pCardData, 7, "FreeSpace:\n"); loghex(pCardData, 7, (BYTE *)pCardFreeSpaceInfo, sizeof(*pCardFreeSpaceInfo)); - return SCARD_S_SUCCESS; + +err: + unlock(pCardData); + MD_FUNC_RETURN(pCardData, 1, dwret); } @@ -3504,6 +4512,8 @@ DWORD WINAPI CardQueryKeySizes(__in PCARD_DATA pCardData, { DWORD dwret; + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); @@ -3512,20 +4522,23 @@ DWORD WINAPI CardQueryKeySizes(__in PCARD_DATA pCardData, (unsigned long)dwKeySpec, (unsigned long)dwFlags, pKeySizes ? (unsigned long)pKeySizes->dwVersion : 0); - if (!pCardData) - return SCARD_E_INVALID_PARAMETER; - if ( dwFlags != 0 ) - return SCARD_E_INVALID_PARAMETER; - if ( dwKeySpec == 0 ) - return SCARD_E_INVALID_PARAMETER; + if (!pCardData || dwFlags != 0 || dwKeySpec == 0 || !lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + dwret = check_card_status(pCardData, "CardQueryKeySizes"); + if (dwret != SCARD_S_SUCCESS) + goto err; dwret = md_query_key_sizes(pCardData, dwKeySpec, pKeySizes); if (dwret != SCARD_S_SUCCESS) - return dwret; + goto err; logprintf(pCardData, 7, "pKeySizes:\n"); loghex(pCardData, 7, (BYTE *)pKeySizes, sizeof(*pKeySizes)); - return SCARD_S_SUCCESS; + +err: + unlock(pCardData); + MD_FUNC_RETURN(pCardData, 1, dwret); } @@ -3533,39 +4546,52 @@ DWORD WINAPI CardRSADecrypt(__in PCARD_DATA pCardData, __inout PCARD_RSA_DECRYPT_INFO pInfo) { - int r, opt_crypt_flags = 0; + DWORD dwret; + int r, opt_crypt_flags = 0, good = 0; unsigned ui; VENDOR_SPECIFIC *vs; struct sc_pkcs15_prkey_info *prkey_info; BYTE *pbuf = NULL, *pbuf2 = NULL; struct sc_pkcs15_object *pkey = NULL; struct sc_algorithm_info *alg_info = NULL; + unsigned int wrong_padding = 0; + unsigned int pbufLen = 0; + + MD_FUNC_CALLED(pCardData, 1); logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardRSADecrypt\n"); - if (!pCardData) - return SCARD_E_INVALID_PARAMETER; - if (!pInfo) - return SCARD_E_INVALID_PARAMETER; - if ( pInfo->pbData == NULL ) - return SCARD_E_INVALID_PARAMETER; + + if (!pCardData || !pInfo || pInfo->pbData == NULL) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (pInfo->dwVersion > CARD_RSA_KEY_DECRYPT_INFO_CURRENT_VERSION) - return ERROR_REVISION_MISMATCH; + MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); if ( pInfo->dwVersion < CARD_RSA_KEY_DECRYPT_INFO_CURRENT_VERSION && pCardData->dwVersion == CARD_DATA_CURRENT_VERSION) - return ERROR_REVISION_MISMATCH; + MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); if (pInfo->dwKeySpec != AT_KEYEXCHANGE) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + if (!lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + dwret = check_card_reader_status(pCardData, "CardRSADecrypt"); + if (dwret != SCARD_S_SUCCESS) + goto err; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) { + dwret = SCARD_E_INVALID_PARAMETER; + goto err; + } /* check if the container exists */ - if (pInfo->bContainerIndex >= MD_MAX_KEY_CONTAINERS) - return SCARD_E_NO_KEY_CONTAINER; - - check_reader_status(pCardData); + if (pInfo->bContainerIndex >= MD_MAX_KEY_CONTAINERS) { + dwret = SCARD_E_NO_KEY_CONTAINER; + goto err; + } logprintf(pCardData, 2, "CardRSADecrypt dwVersion=%lu, bContainerIndex=%u, dwKeySpec=%lu pbData=%p, cbData=%lu\n", @@ -3583,18 +4609,22 @@ DWORD WINAPI CardRSADecrypt(__in PCARD_DATA pCardData, pkey = vs->p15_containers[pInfo->bContainerIndex].prkey_obj; if (!pkey) { logprintf(pCardData, 2, "CardRSADecrypt prkey not found\n"); - return SCARD_E_NO_KEY_CONTAINER; + dwret = SCARD_E_NO_KEY_CONTAINER; + goto err; } /* input and output buffers are always the same size */ pbuf = pCardData->pfnCspAlloc(pInfo->cbData); - if (!pbuf) - return SCARD_E_NO_MEMORY; + if (!pbuf) { + dwret = SCARD_E_NO_MEMORY; + goto err; + } pbuf2 = pCardData->pfnCspAlloc(pInfo->cbData); if (!pbuf2) { pCardData->pfnCspFree(pbuf); - return SCARD_E_NO_MEMORY; + dwret = SCARD_E_NO_MEMORY; + goto err; } /*inversion donnees*/ @@ -3611,24 +4641,26 @@ DWORD WINAPI CardRSADecrypt(__in PCARD_DATA pCardData, prkey_info->modulus_length); pCardData->pfnCspFree(pbuf); pCardData->pfnCspFree(pbuf2); - return SCARD_F_INTERNAL_ERROR; + dwret = SCARD_F_INTERNAL_ERROR; + goto err; } - /* filter boggus input: the data to decrypt is shorter than the RSA key ? */ + /* filter bogus input: the data to decrypt is shorter than the RSA key ? */ if ( pInfo->cbData < prkey_info->modulus_length / 8) { /* according to the minidriver specs, this is the error code to return (instead of invalid parameter when the call is forwarded to the card implementation) */ pCardData->pfnCspFree(pbuf); pCardData->pfnCspFree(pbuf2); - return SCARD_E_INSUFFICIENT_BUFFER; + dwret = SCARD_E_INSUFFICIENT_BUFFER; + goto err; } + pbufLen = pInfo->cbData; if (alg_info->flags & SC_ALGORITHM_RSA_RAW) { logprintf(pCardData, 2, "sc_pkcs15_decipher: using RSA-RAW mechanism\n"); - r = sc_pkcs15_decipher(vs->p15card, pkey, opt_crypt_flags, pbuf, pInfo->cbData, pbuf2, pInfo->cbData); - logprintf(pCardData, 2, "sc_pkcs15_decipher returned %d\n", r); - + r = sc_pkcs15_decipher(vs->p15card, pkey, opt_crypt_flags | SC_ALGORITHM_RSA_RAW, pbuf, pInfo->cbData, pbuf2, pInfo->cbData, NULL); + /* do not log return value to not leak it */ if (r > 0) { /* Need to handle padding */ if (pInfo->dwVersion >= CARD_RSA_KEY_DECRYPT_INFO_VERSION_TWO) { @@ -3636,31 +4668,29 @@ DWORD WINAPI CardRSADecrypt(__in PCARD_DATA pCardData, "sc_pkcs15_decipher: DECRYPT-INFO dwVersion=%lu\n", (unsigned long)pInfo->dwVersion); if (pInfo->dwPaddingType == CARD_PADDING_PKCS1) { - size_t temp = pInfo->cbData; + unsigned int temp = pInfo->cbData; logprintf(pCardData, 2, "sc_pkcs15_decipher: stripping PKCS1 padding\n"); - r = sc_pkcs1_strip_02_padding(vs->ctx, pbuf2, pInfo->cbData, pbuf2, &temp); + r = sc_pkcs1_strip_02_padding_constant_time(vs->ctx, prkey_info->modulus_length / 8, pbuf2, pInfo->cbData, pbuf2, &temp); pInfo->cbData = (DWORD) temp; - if (r < 0) { - logprintf(pCardData, 2, "Cannot strip PKCS1 padding: %i\n", r); - pCardData->pfnCspFree(pbuf); - pCardData->pfnCspFree(pbuf2); - return SCARD_F_INTERNAL_ERROR; - } + wrong_padding = constant_time_eq_i(r, SC_ERROR_WRONG_PADDING); + /* continue without returning error to not leak that padding is wrong + to prevent time side-channel leak for Marvin attack*/ } else if (pInfo->dwPaddingType == CARD_PADDING_OAEP) { /* TODO: Handle OAEP padding if present - can call PFN_CSP_UNPAD_DATA */ logprintf(pCardData, 2, "OAEP padding not implemented\n"); pCardData->pfnCspFree(pbuf); pCardData->pfnCspFree(pbuf2); - return SCARD_F_INTERNAL_ERROR; + dwret = SCARD_F_INTERNAL_ERROR; + goto err; } } } } - else if (alg_info->flags & SC_ALGORITHM_RSA_PAD_PKCS1) { + else if (alg_info->flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02) { logprintf(pCardData, 2, "sc_pkcs15_decipher: using RSA_PAD_PKCS1 mechanism\n"); - r = sc_pkcs15_decipher(vs->p15card, pkey, opt_crypt_flags | SC_ALGORITHM_RSA_PAD_PKCS1, - pbuf, pInfo->cbData, pbuf2, pInfo->cbData); + r = sc_pkcs15_decipher(vs->p15card, pkey, opt_crypt_flags | SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02, + pbuf, pInfo->cbData, pbuf2, pInfo->cbData, NULL); logprintf(pCardData, 2, "sc_pkcs15_decipher returned %d\n", r); if (r > 0) { /* No padding info, or padding info none */ @@ -3688,52 +4718,68 @@ DWORD WINAPI CardRSADecrypt(__in PCARD_DATA pCardData, logprintf(pCardData, 2, "CardRSADecrypt: no usable RSA algorithm\n"); pCardData->pfnCspFree(pbuf); pCardData->pfnCspFree(pbuf2); - return SCARD_E_INVALID_PARAMETER; + dwret = SCARD_E_INVALID_PARAMETER; + goto err; } - if ( r < 0) { + good = constant_time_ge(r, 1); + /* if no error or padding error, do not return here to prevent Marvin attack */ + if (!(good | wrong_padding) && r < 0) { logprintf(pCardData, 2, "sc_pkcs15_decipher error(%i): %s\n", r, sc_strerror(r)); pCardData->pfnCspFree(pbuf); pCardData->pfnCspFree(pbuf2); - return md_translate_OpenSC_to_Windows_error(r, SCARD_E_INVALID_VALUE); + dwret = md_translate_OpenSC_to_Windows_error(r, SCARD_E_INVALID_VALUE); + goto err; } - - logprintf(pCardData, 2, "decrypted data(%lu):\n", - (unsigned long)pInfo->cbData); - loghex(pCardData, 7, pbuf2, pInfo->cbData); + dwret = constant_time_select_s(good, SCARD_S_SUCCESS, SCARD_F_INTERNAL_ERROR); /*inversion donnees */ - for(ui = 0; ui < pInfo->cbData; ui++) - pInfo->pbData[ui] = pbuf2[pInfo->cbData-ui-1]; + /* copy data in constant-time way to prevent leak */ + for (ui = 0; ui < pbufLen; ui++) { + unsigned int mask, inv_ui; + unsigned char msg_byte, orig_byte; + mask = good & constant_time_lt_s(ui, pInfo->cbData); /* ui should be in bounds of decrypted message */ + inv_ui = pInfo->cbData - ui - 1; /* compute inversed ui index */ + msg_byte = pbuf2[constant_time_select(mask, inv_ui, 0)]; /* if in range of decrypted message, read on inversed index otherwise read some bogus value */ + orig_byte = pInfo->pbData[ui]; + pInfo->pbData[ui] = constant_time_select_s(mask, msg_byte, orig_byte); /* store message byte only if in correct range */ + } pCardData->pfnCspFree(pbuf); pCardData->pfnCspFree(pbuf2); - return SCARD_S_SUCCESS; + +err: + unlock(pCardData); + /* do not log return value to not leak it */ + return dwret; } DWORD WINAPI CardSignData(__in PCARD_DATA pCardData, __inout PCARD_SIGNING_INFO pInfo) { + DWORD dwret; VENDOR_SPECIFIC *vs; ALG_ID hashAlg; sc_pkcs15_prkey_info_t *prkey_info; BYTE dataToSign[0x200]; - int r, opt_crypt_flags = 0, opt_hash_flags = 0; + int opt_crypt_flags = 0; size_t dataToSignLen = sizeof(dataToSign); sc_pkcs15_object_t *pkey; + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardSignData\n"); if (!pCardData || !pInfo) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if ( ( pInfo->dwVersion != CARD_SIGNING_INFO_BASIC_VERSION ) && ( pInfo->dwVersion != CARD_SIGNING_INFO_CURRENT_VERSION ) ) - return ERROR_REVISION_MISMATCH; + MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); if ( pInfo->pbData == NULL ) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); switch(pInfo->dwKeySpec) { case AT_SIGNATURE: @@ -3746,11 +4792,18 @@ DWORD WINAPI CardSignData(__in PCARD_DATA pCardData, __inout PCARD_SIGNING_INFO case AT_ECDHE_P521: break; default: - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } if (pInfo->dwSigningFlags & ~(CARD_PADDING_INFO_PRESENT | CARD_PADDING_NONE | CARD_BUFFER_SIZE_ONLY | CARD_PADDING_PKCS1 | CARD_PADDING_PSS | CARD_PADDING_OAEP)) return SCARD_E_INVALID_PARAMETER; + if (!lock(pCardData)) + return SCARD_E_INVALID_PARAMETER; + + dwret = check_card_reader_status(pCardData, "CardSignData"); + if (dwret != SCARD_S_SUCCESS) + goto err; + logprintf(pCardData, 2, "CardSignData dwVersion=%lu, bContainerIndex=%u, dwKeySpec=%lu, dwSigningFlags=0x%08X, aiHashAlg=0x%08X\n", (unsigned long)pInfo->dwVersion, @@ -3766,115 +4819,136 @@ DWORD WINAPI CardSignData(__in PCARD_DATA pCardData, __inout PCARD_SIGNING_INFO hashAlg = pInfo->aiHashAlg; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); - if (pInfo->bContainerIndex >= MD_MAX_KEY_CONTAINERS) - return SCARD_E_NO_KEY_CONTAINER; + if (!vs) { + dwret = SCARD_E_INVALID_PARAMETER; + goto err; + } + + if (pInfo->bContainerIndex >= MD_MAX_KEY_CONTAINERS) { + dwret = SCARD_E_NO_KEY_CONTAINER; + goto err; + } pkey = vs->p15_containers[pInfo->bContainerIndex].prkey_obj; - if (!pkey) - return SCARD_E_NO_KEY_CONTAINER; + if (!pkey) { + dwret = SCARD_E_NO_KEY_CONTAINER; + goto err; + } prkey_info = (struct sc_pkcs15_prkey_info *)(pkey->data); - check_reader_status(pCardData); - logprintf(pCardData, 2, "pInfo->dwVersion = %lu\n", (unsigned long)pInfo->dwVersion); - if (dataToSignLen < pInfo->cbData) - return SCARD_E_INSUFFICIENT_BUFFER; + if (dataToSignLen < pInfo->cbData) { + dwret = SCARD_E_INSUFFICIENT_BUFFER; + goto err; + } memcpy(dataToSign, pInfo->pbData, pInfo->cbData); dataToSignLen = pInfo->cbData; - if (CARD_PADDING_INFO_PRESENT & pInfo->dwSigningFlags) { - BCRYPT_PKCS1_PADDING_INFO *pinf = (BCRYPT_PKCS1_PADDING_INFO *)pInfo->pPaddingInfo; - if (CARD_PADDING_PSS == pInfo->dwPaddingType) { - logprintf(pCardData, 0, "unsupported paddingtype CARD_PADDING_PSS\n"); - return SCARD_E_UNSUPPORTED_FEATURE; - } - else if (CARD_PADDING_PKCS1 != pInfo->dwPaddingType) { - logprintf(pCardData, 0, "unsupported paddingtype\n"); - return SCARD_E_INVALID_PARAMETER; - } - - if (!pinf->pszAlgId) { - /* hashAlg = CALG_SSL3_SHAMD5; */ - logprintf(pCardData, 3, "Using CALG_SSL3_SHAMD5 hashAlg\n"); - opt_hash_flags = SC_ALGORITHM_RSA_HASH_MD5_SHA1; - } - else { - if (wcscmp(pinf->pszAlgId, L"MD5") == 0) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_MD5; - else if (wcscmp(pinf->pszAlgId, L"SHA1") == 0) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_SHA1; - else if (wcscmp(pinf->pszAlgId, L"SHAMD5") == 0) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_MD5_SHA1; - else if (wcscmp(pinf->pszAlgId, L"SHA224") == 0) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_SHA224; - else if (wcscmp(pinf->pszAlgId, L"SHA256") == 0) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_SHA256; - else if (wcscmp(pinf->pszAlgId, L"SHA384") == 0) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_SHA384; - else if (wcscmp(pinf->pszAlgId, L"SHA512") == 0) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_SHA512; - else if (wcscmp(pinf->pszAlgId, L"RIPEMD160") == 0) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_RIPEMD160; - else - { - logprintf(pCardData, 0,"unknown AlgId %S\n",NULLWSTR(pinf->pszAlgId)); - return SCARD_E_UNSUPPORTED_FEATURE; - } - } - } - else { + if (0 == (CARD_PADDING_INFO_PRESENT & pInfo->dwSigningFlags)) { + /* When CARD_PADDING_INFO_PRESENT is not set in dwSigningFlags, this is + * the basic version of the signing structure. (If this is not the + * basic version of the signing structure, the minidriver should return + * ERROR_REVISION_MISMATCH.) The minidriver should only do PKCS1 + * padding and use the value in aiHashAlg. */ logprintf(pCardData, 3, "CARD_PADDING_INFO_PRESENT not set\n"); + opt_crypt_flags = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01; /* turn off later if key is EC */ if (hashAlg == CALG_MD5) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_MD5; + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_MD5; else if (hashAlg == CALG_SHA1) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_SHA1; + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA1; else if (hashAlg == CALG_SSL3_SHAMD5) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_MD5_SHA1; + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_MD5_SHA1; else if (hashAlg == CALG_SHA_256) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_SHA256; + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA256; else if (hashAlg == CALG_SHA_384) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_SHA384; + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA384; else if (hashAlg == CALG_SHA_512) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_SHA512; + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA512; else if (hashAlg == (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_RIPEMD160)) - opt_hash_flags = SC_ALGORITHM_RSA_HASH_RIPEMD160; + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_RIPEMD160; else if (hashAlg !=0) { logprintf(pCardData, 0, "bogus aiHashAlg %i\n", hashAlg); - return SCARD_E_UNSUPPORTED_FEATURE; + dwret = SCARD_E_UNSUPPORTED_FEATURE; + goto err; } - } - - if (pInfo->dwSigningFlags & CARD_PADDING_NONE) - { - /* do not add the digest info when called from CryptSignHash(CRYPT_NOHASHOID) - - Note: SC_ALGORITHM_RSA_HASH_MD5_SHA1 aka CALG_SSL3_SHAMD5 do not have a digest info to be added - CryptSignHash(CALG_SSL3_SHAMD5,CRYPT_NOHASHOID) is the same than CryptSignHash(CALG_SSL3_SHAMD5) - */ - opt_hash_flags = 0; - } - - /* From sc-minidriver_specs_v7.docx pp.76: - * 'The Base CSP/KSP performs the hashing operation on the data before passing it - * to CardSignData for signature.' - * So, the SC_ALGORITHM_RSA_HASH_* flags should not be passed to pkcs15 library - * when calculating the signature . - * - * From sc-minidriver_specs_v7.docx pp.76: - * 'If the aiHashAlg member is nonzero, it specifies the hash algorithm’s object identifier (OID) - * that is encoded in the PKCS padding.' - * So, the digest info has be included into the data to be signed. - * */ - if (opt_hash_flags) { - logprintf(pCardData, 2, "include digest info of the algorithm 0x%08X\n", opt_hash_flags); - dataToSignLen = sizeof(dataToSign); - r = sc_pkcs1_encode(vs->ctx, opt_hash_flags, pInfo->pbData, pInfo->cbData, dataToSign, &dataToSignLen, 0); - if (r) { - logprintf(pCardData, 2, "PKCS#1 encode error %s\n", sc_strerror(r)); - return SCARD_E_INVALID_VALUE; + } else { + switch (pInfo->dwPaddingType) { + case CARD_PADDING_NONE: + opt_crypt_flags = SC_ALGORITHM_RSA_PAD_NONE; + break; + + case CARD_PADDING_PKCS1: + opt_crypt_flags = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01; + BCRYPT_PKCS1_PADDING_INFO *pkcs1_pinf = (BCRYPT_PKCS1_PADDING_INFO *)pInfo->pPaddingInfo; + + if (!pkcs1_pinf->pszAlgId) + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_NONE; + else if (wcscmp(pkcs1_pinf->pszAlgId, L"SHAMD5") == 0) + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_MD5_SHA1; + else if (wcscmp(pkcs1_pinf->pszAlgId, BCRYPT_MD5_ALGORITHM) == 0) + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_MD5; + else if (wcscmp(pkcs1_pinf->pszAlgId, BCRYPT_SHA1_ALGORITHM) == 0) + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA1; + else if (wcscmp(pkcs1_pinf->pszAlgId, L"SHA224") == 0) + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA224; + else if (wcscmp(pkcs1_pinf->pszAlgId, BCRYPT_SHA256_ALGORITHM) == 0) + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA256; + else if (wcscmp(pkcs1_pinf->pszAlgId, BCRYPT_SHA384_ALGORITHM) == 0) + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA384; + else if (wcscmp(pkcs1_pinf->pszAlgId, BCRYPT_SHA512_ALGORITHM) == 0) + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA512; + else if (wcscmp(pkcs1_pinf->pszAlgId, L"RIPEMD160") == 0) + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_RIPEMD160; + else { + logprintf(pCardData, 0,"unknown AlgId %S\n",NULLWSTR(pkcs1_pinf->pszAlgId)); + dwret = SCARD_E_UNSUPPORTED_FEATURE; + goto err; + } + break; + + case CARD_PADDING_PSS: + opt_crypt_flags = SC_ALGORITHM_RSA_PAD_PSS; + BCRYPT_PSS_PADDING_INFO *pss_pinf = (BCRYPT_PSS_PADDING_INFO *)pInfo->pPaddingInfo; + ULONG expected_salt_len; + + if (!pss_pinf->pszAlgId || wcscmp(pss_pinf->pszAlgId, BCRYPT_SHA1_ALGORITHM) == 0) { + /* hashAlg = CALG_SHA1; */ + logprintf(pCardData, 3, "Using CALG_SHA1 hashAlg\n"); + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA1 | SC_ALGORITHM_MGF1_SHA1; + expected_salt_len = 160; + } else if (wcscmp(pss_pinf->pszAlgId, L"SHA224") == 0) { + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA224 | SC_ALGORITHM_MGF1_SHA224; + expected_salt_len = 224; + } else if (wcscmp(pss_pinf->pszAlgId, BCRYPT_SHA256_ALGORITHM) == 0) { + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA256 | SC_ALGORITHM_MGF1_SHA256; + expected_salt_len = 256; + } else if (wcscmp(pss_pinf->pszAlgId, BCRYPT_SHA384_ALGORITHM) == 0) { + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA384 | SC_ALGORITHM_MGF1_SHA384; + expected_salt_len = 384; + } else if (wcscmp(pss_pinf->pszAlgId, BCRYPT_SHA512_ALGORITHM) == 0) { + opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA512 | SC_ALGORITHM_MGF1_SHA512; + expected_salt_len = 512; + } else { + logprintf(pCardData, 0,"unknown AlgId %S\n",NULLWSTR(pss_pinf->pszAlgId)); + dwret = SCARD_E_UNSUPPORTED_FEATURE; + goto err; + } + /* We're strict, and only do PSS signatures with a salt length that + * matches the digest length (any shorter is rubbish, any longer + * is useless). */ + if (pss_pinf->cbSalt != expected_salt_len / 8) { + dwret = SCARD_E_INVALID_PARAMETER; + goto err; + } + break; + + default: + logprintf(pCardData, 0, "unsupported paddingtype\n"); + dwret = SCARD_E_INVALID_PARAMETER; + goto err; } } @@ -3882,9 +4956,7 @@ DWORD WINAPI CardSignData(__in PCARD_DATA pCardData, __inout PCARD_SIGNING_INFO if ( prkey_info->modulus_length > 0) { /* RSA */ pInfo->cbSignedData = (DWORD) prkey_info->modulus_length / 8; - opt_crypt_flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE; } else if ( prkey_info->field_length > 0) { - opt_crypt_flags = SC_ALGORITHM_ECDSA_HASH_NONE; switch(prkey_info->field_length) { case 256: /* ECDSA_P256 */ @@ -3902,11 +4974,14 @@ DWORD WINAPI CardSignData(__in PCARD_DATA pCardData, __inout PCARD_SIGNING_INFO logprintf(pCardData, 0, "unknown ECC key size %"SC_FORMAT_LEN_SIZE_T"u\n", prkey_info->field_length); - return SCARD_E_INVALID_VALUE; + dwret = SCARD_E_INVALID_VALUE; + goto err; } + opt_crypt_flags &= ~SC_ALGORITHM_RSA_PADS; /* EC does not use this */ } else { logprintf(pCardData, 0, "invalid private key\n"); - return SCARD_E_INVALID_VALUE; + dwret = SCARD_E_INVALID_VALUE; + goto err; } logprintf(pCardData, 3, "pInfo->cbSignedData = %lu\n", @@ -3920,8 +4995,10 @@ DWORD WINAPI CardSignData(__in PCARD_DATA pCardData, __inout PCARD_SIGNING_INFO lg = pInfo->cbSignedData; logprintf(pCardData, 3, "lg = %lu\n", (unsigned long)lg); pbuf = pCardData->pfnCspAlloc(lg); - if (!pbuf) - return SCARD_E_NO_MEMORY; + if (!pbuf) { + dwret = SCARD_E_NO_MEMORY; + goto err; + } logprintf(pCardData, 7, "Data to sign: "); loghex(pCardData, 7, dataToSign, dataToSignLen); @@ -3929,20 +5006,21 @@ DWORD WINAPI CardSignData(__in PCARD_DATA pCardData, __inout PCARD_SIGNING_INFO pInfo->pbSignedData = (PBYTE) pCardData->pfnCspAlloc(pInfo->cbSignedData); if (!pInfo->pbSignedData) { pCardData->pfnCspFree(pbuf); - return SCARD_E_NO_MEMORY; + dwret = SCARD_E_NO_MEMORY; + goto err; } - r = sc_pkcs15_compute_signature(vs->p15card, pkey, opt_crypt_flags, dataToSign, dataToSignLen, pbuf, lg); + r = sc_pkcs15_compute_signature(vs->p15card, pkey, opt_crypt_flags, dataToSign, dataToSignLen, pbuf, lg, NULL); logprintf(pCardData, 2, "sc_pkcs15_compute_signature return %d\n", r); if(r < 0) { logprintf(pCardData, 2, "sc_pkcs15_compute_signature error %s\n", sc_strerror(r)); pCardData->pfnCspFree(pbuf); - return md_translate_OpenSC_to_Windows_error(r, SCARD_F_INTERNAL_ERROR); + dwret = md_translate_OpenSC_to_Windows_error(r, SCARD_F_INTERNAL_ERROR); + goto err; } pInfo->cbSignedData = r; - /*revert data only for RSA (Microsoft uses the big endian version while everyone is using little endian*/ if ( prkey_info->modulus_length > 0) { for(i = 0; i < r; i++) @@ -3959,53 +5037,69 @@ DWORD WINAPI CardSignData(__in PCARD_DATA pCardData, __inout PCARD_SIGNING_INFO } logprintf(pCardData, 3, - "CardSignData, dwVersion=%lu, name=%S, hScard=0x%08X, hSCardCtx=0x%08X\n", + "CardSignData, dwVersion=%lu, name=%S, hScard=0x%08"SC_FORMAT_LEN_SIZE_T"X, hSCardCtx=0x%08"SC_FORMAT_LEN_SIZE_T"X\n", (unsigned long)pCardData->dwVersion, NULLWSTR(pCardData->pwszCardName), - (unsigned int)pCardData->hScard, - (unsigned int)pCardData->hSCardCtx); + (size_t)pCardData->hScard, + (size_t)pCardData->hSCardCtx); - return SCARD_S_SUCCESS; +err: + unlock(pCardData); + MD_FUNC_RETURN(pCardData, 1, dwret); } DWORD WINAPI CardConstructDHAgreement(__in PCARD_DATA pCardData, __inout PCARD_DH_AGREEMENT_INFO pAgreementInfo) { + DWORD dwret; VENDOR_SPECIFIC *vs; struct sc_pkcs15_object *pkey = NULL; - int r, opt_derive_flags = 0; + int r, opt_derive_flags = SC_ALGORITHM_ECDH_CDH_RAW; u8* out = 0; - unsigned long outlen = 0; + size_t outlen = 0; PBYTE pbPublicKey = NULL; - DWORD dwPublicKeySize = 0; + size_t publicKeySize = 0; struct md_dh_agreement* dh_agreement = NULL; struct md_dh_agreement* temp = NULL; BYTE i; + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardConstructDHAgreement\n"); if (!pCardData) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (!pAgreementInfo) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if ( pAgreementInfo->pbPublicKey == NULL ) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (pAgreementInfo->dwVersion > CARD_DH_AGREEMENT_INFO_VERSION) - return ERROR_REVISION_MISMATCH; + MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); if ( pAgreementInfo->dwVersion < CARD_DH_AGREEMENT_INFO_VERSION && pCardData->dwVersion == CARD_DATA_CURRENT_VERSION) - return ERROR_REVISION_MISMATCH; + MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); + + if (!lock(pCardData)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + dwret = check_card_reader_status(pCardData, "CardConstructDHAgreement"); + if (dwret != SCARD_S_SUCCESS) + goto err; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) { + dwret = SCARD_E_INVALID_PARAMETER; + goto err; + } /* check if the container exists */ - if (pAgreementInfo->bContainerIndex >= MD_MAX_KEY_CONTAINERS) - return SCARD_E_NO_KEY_CONTAINER; - - check_reader_status(pCardData); + if (pAgreementInfo->bContainerIndex >= MD_MAX_KEY_CONTAINERS) { + dwret = SCARD_E_NO_KEY_CONTAINER; + goto err; + } logprintf(pCardData, 2, "CardConstructDHAgreement dwVersion=%lu, dwKeySpec=%u pbData=%p, cbData=%lu\n", (unsigned long)pAgreementInfo->dwVersion, @@ -4016,36 +5110,40 @@ DWORD WINAPI CardConstructDHAgreement(__in PCARD_DATA pCardData, pkey = vs->p15_containers[pAgreementInfo->bContainerIndex].prkey_obj; if (!pkey) { logprintf(pCardData, 2, "CardConstructDHAgreement prkey not found\n"); - return SCARD_E_NO_KEY_CONTAINER; + dwret = SCARD_E_NO_KEY_CONTAINER; + goto err; } /* convert the Windows public key into an OpenSC public key */ - dwPublicKeySize = pAgreementInfo->dwPublicKey - sizeof(BCRYPT_ECCKEY_BLOB) + 1; - pbPublicKey = (PBYTE) pCardData->pfnCspAlloc(dwPublicKeySize); + publicKeySize = pAgreementInfo->dwPublicKey - sizeof(BCRYPT_ECCKEY_BLOB) + 1; + pbPublicKey = (PBYTE) pCardData->pfnCspAlloc(publicKeySize); if (!pbPublicKey) { - return ERROR_OUTOFMEMORY; + dwret = ERROR_OUTOFMEMORY; + goto err; } pbPublicKey[0] = 4; - memcpy(pbPublicKey+1, pAgreementInfo->pbPublicKey + sizeof(BCRYPT_ECCKEY_BLOB), dwPublicKeySize-1); + memcpy(pbPublicKey+1, pAgreementInfo->pbPublicKey + sizeof(BCRYPT_ECCKEY_BLOB), publicKeySize-1); /* derive the key using the OpenSC functions */ - r = sc_pkcs15_derive(vs->p15card, pkey, opt_derive_flags, pbPublicKey, dwPublicKeySize, out, &outlen ); + r = sc_pkcs15_derive(vs->p15card, pkey, opt_derive_flags, pbPublicKey, publicKeySize, out, &outlen ); logprintf(pCardData, 2, "sc_pkcs15_derive returned %d\n", r); if ( r < 0) { logprintf(pCardData, 2, "sc_pkcs15_derive error(%i): %s\n", r, sc_strerror(r)); pCardData->pfnCspFree(pbPublicKey); - return md_translate_OpenSC_to_Windows_error(r, SCARD_E_INVALID_VALUE); + dwret = md_translate_OpenSC_to_Windows_error(r, SCARD_E_INVALID_VALUE); + goto err; } out = pCardData->pfnCspAlloc(outlen); if (!out) { - return ERROR_OUTOFMEMORY; + dwret = ERROR_OUTOFMEMORY; + goto err; } - r = sc_pkcs15_derive(vs->p15card, pkey, opt_derive_flags, pbPublicKey, dwPublicKeySize, out, &outlen ); + r = sc_pkcs15_derive(vs->p15card, pkey, opt_derive_flags, pbPublicKey, publicKeySize, out, &outlen ); logprintf(pCardData, 2, "sc_pkcs15_derive returned %d\n", r); pCardData->pfnCspFree(pbPublicKey); @@ -4053,7 +5151,8 @@ DWORD WINAPI CardConstructDHAgreement(__in PCARD_DATA pCardData, if ( r < 0) { logprintf(pCardData, 2, "sc_pkcs15_derive error(%i): %s\n", r, sc_strerror(r)); pCardData->pfnCspFree(out); - return md_translate_OpenSC_to_Windows_error(r, SCARD_E_INVALID_VALUE); + dwret = md_translate_OpenSC_to_Windows_error(r, SCARD_E_INVALID_VALUE); + goto err; } /* save the dh agreement for later use */ @@ -4065,14 +5164,16 @@ DWORD WINAPI CardConstructDHAgreement(__in PCARD_DATA pCardData, pAgreementInfo->bSecretAgreementIndex = i; dh_agreement->pbAgreement = out; dh_agreement->dwSize = outlen; - return SCARD_S_SUCCESS; + dwret = SCARD_S_SUCCESS; + goto err; } } /* no empty space => need to allocate memory */ temp = (struct md_dh_agreement*) pCardData->pfnCspAlloc((vs->allocatedAgreements+1) * sizeof(struct md_dh_agreement)); if (!temp) { pCardData->pfnCspFree(out); - return SCARD_E_NO_MEMORY; + dwret = SCARD_E_NO_MEMORY; + goto err; } if ((vs->allocatedAgreements) > 0) { memcpy(temp, vs->dh_agreements, sizeof(struct md_dh_agreement) * (vs->allocatedAgreements)); @@ -4084,15 +5185,17 @@ DWORD WINAPI CardConstructDHAgreement(__in PCARD_DATA pCardData, dh_agreement->pbAgreement = out; dh_agreement->dwSize = outlen; vs->allocatedAgreements++; - return SCARD_S_SUCCESS; -} +err: + unlock(pCardData); + MD_FUNC_RETURN(pCardData, 1, dwret); +} DWORD WINAPI CardDeriveHashOrHMAC(__in PCARD_DATA pCardData, __inout PCARD_DERIVE_KEY pAgreementInfo, __in struct md_dh_agreement* agreement, __in PWSTR szAlgorithm, - __in PBYTE pbHmacKey, __in DWORD dwHmacKeySize + __in PBYTE pbHmacKey, __in DWORD dwHmacKeySize ) { DWORD dwReturn = 0; @@ -4105,6 +5208,8 @@ DWORD WINAPI CardDeriveHashOrHMAC(__in PCARD_DATA pCardData, ULONG i; NCryptBufferDesc* parameters = NULL; + MD_FUNC_CALLED(pCardData, 1); + dwReturn = BCryptOpenAlgorithmProvider(&hAlgorithm, szAlgorithm, NULL, (pbHmacKey?BCRYPT_ALG_HANDLE_HMAC_FLAG:0)); if (dwReturn) { logprintf(pCardData, 0, @@ -4211,16 +5316,16 @@ DWORD WINAPI CardDeriveHashOrHMAC(__in PCARD_DATA pCardData, LocalFree(pbBuffer); if (hAlgorithm) BCryptCloseAlgorithmProvider(hAlgorithm, 0); - return dwReturn; + MD_FUNC_RETURN(pCardData, 1, dwReturn); } /* Generic function to perform hash. Could have been OpenSSL but used BCrypt* functions. BCrypt is loaded as a delay load library. The dll can be loaded into Windows XP until this code is called. Hopefully, ECC is not available in Windows XP and BCrypt functions are not called */ -DWORD HashDataWithBCrypt(__in PCARD_DATA pCardData, BCRYPT_ALG_HANDLE hAlgorithm, - PBYTE pbOuput, DWORD dwOutputSize, PBYTE pbSecret, DWORD dwSecretSize, +DWORD HashDataWithBCrypt(__in PCARD_DATA pCardData, BCRYPT_ALG_HANDLE hAlgorithm, + PBYTE pbOuput, DWORD dwOutputSize, PBYTE pbSecret, DWORD dwSecretSize, PBYTE pbData1, DWORD dwDataSize1, - PBYTE pbData2, DWORD dwDataSize2, + PBYTE pbData2, DWORD dwDataSize2, PBYTE pbData3, DWORD dwDataSize3 ) { DWORD dwReturn, dwSize, dwBufferSize; @@ -4338,22 +5443,22 @@ DWORD WINAPI DoTlsPrf(__in PCARD_DATA pCardData, dwReturn = SCARD_E_NO_MEMORY; goto cleanup; } - + for (i = 0; idwVersion) - return ERROR_REVISION_MISMATCH; + MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); if (pAgreementInfo->dwVersion > CARD_DERIVE_KEY_CURRENT_VERSION) - return ERROR_REVISION_MISMATCH; + MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); if (pAgreementInfo->pwszKDF == NULL) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (pAgreementInfo->dwFlags & ~(KDF_USE_SECRET_AS_HMAC_KEY_FLAG | CARD_RETURN_KEY_HANDLE | CARD_BUFFER_SIZE_ONLY)) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); - /* according to the documenation, CARD_DERIVE_KEY_CURRENT_VERSION should be equal to 2. - In pratice it is not 2 but 1 + /* according to the documentation, CARD_DERIVE_KEY_CURRENT_VERSION should be equal to 2. + In practice it is not 2 but 1 if ( pAgreementInfo->dwVersion < CARD_DERIVE_KEY_CURRENT_VERSION && pCardData->dwVersion == CARD_DATA_CURRENT_VERSION) return ERROR_REVISION_MISMATCH;*/ vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); /* check if the agreement index is ok */ if (pAgreementInfo->bSecretAgreementIndex >= vs->allocatedAgreements) { - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } agreement = vs->dh_agreements + pAgreementInfo->bSecretAgreementIndex; if (agreement->pbAgreement == NULL) { - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } if (pAgreementInfo->dwFlags & CARD_RETURN_KEY_HANDLE ) { - return SCARD_E_UNSUPPORTED_FEATURE; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /* find the algorithm, checks parameters */ parameters = (NCryptBufferDesc*)pAgreementInfo->pParameterList; - + if (parameters) { for (i = 0; i < parameters->cBuffers; i++) { NCryptBuffer* buffer = parameters->pBuffers + i; @@ -4584,7 +5694,7 @@ DWORD WINAPI CardDeriveKey(__in PCARD_DATA pCardData, logprintf(pCardData, 0, "CardDeriveKey: unsupported algorithm %S\n", (PWSTR)buffer->pvBuffer); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } break; case KDF_HMAC_KEY: @@ -4602,7 +5712,7 @@ DWORD WINAPI CardDeriveKey(__in PCARD_DATA pCardData, case KDF_TLS_PRF_LABEL: if (pbLabel != NULL) { logprintf(pCardData, 0, "CardDeriveKey: got more than one Label\n"); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } pbLabel = (PBYTE)buffer->pvBuffer; dwLabelSize = buffer->cbBuffer; @@ -4610,14 +5720,14 @@ DWORD WINAPI CardDeriveKey(__in PCARD_DATA pCardData, case KDF_TLS_PRF_SEED: if (pbSeed != NULL) { logprintf(pCardData, 0, "CardDeriveKey: got more than one Seed\n"); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } if (buffer->cbBuffer != 64) { logprintf(pCardData, 0, "CardDeriveKey: invalid seed size %lu\n", buffer->cbBuffer); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } pbSeed = (PBYTE)buffer->pvBuffer; break; @@ -4634,7 +5744,7 @@ DWORD WINAPI CardDeriveKey(__in PCARD_DATA pCardData, logprintf(pCardData, 0, "CardDeriveKey: unknown buffer type %lu\n", (parameters->pBuffers + i)->BufferType); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } } } @@ -4643,27 +5753,27 @@ DWORD WINAPI CardDeriveKey(__in PCARD_DATA pCardData, szAlgorithm = BCRYPT_SHA1_ALGORITHM; } - /* check the values with the KDF choosen */ + /* check the values with the KDF chosen */ if (wcscmp(pAgreementInfo->pwszKDF, BCRYPT_KDF_HASH) == 0) { } else if (wcscmp(pAgreementInfo->pwszKDF, BCRYPT_KDF_HMAC) == 0) { if (pbHmacKey == NULL) { logprintf(pCardData, 0, "CardDeriveKey: no hhmac key for hmac KDF\n"); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } } else if (wcscmp(pAgreementInfo->pwszKDF, BCRYPT_KDF_TLS_PRF) == 0) { if (!pbSeed) { logprintf(pCardData, 0, "CardDeriveKey: No seed was provided\n"); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } if (!pbLabel) { logprintf(pCardData, 0, "CardDeriveKey: No label was provided\n"); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } } else { logprintf(pCardData, 0, "CardDeriveKey: unsupported KDF %S\n", pAgreementInfo->pwszKDF); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } /* do the job for the KDF Hash & Hmac */ @@ -4675,7 +5785,7 @@ DWORD WINAPI CardDeriveKey(__in PCARD_DATA pCardData, logprintf(pCardData, 0, "CardDeriveKey: got an error while deriving the Key (hash or HMAC) 0x%08X\n", (unsigned int)dwReturn); - return dwReturn; + MD_FUNC_RETURN(pCardData, 1, dwReturn); } } else if (wcscmp(pAgreementInfo->pwszKDF, BCRYPT_KDF_TLS_PRF) == 0) { @@ -4684,14 +5794,14 @@ DWORD WINAPI CardDeriveKey(__in PCARD_DATA pCardData, logprintf(pCardData, 0, "CardDeriveKey: got an error while deriving the Key (TlsPrf) 0x%08X\n", (unsigned int)dwReturn); - return dwReturn; + MD_FUNC_RETURN(pCardData, 1, dwReturn); } } /*else if (wcscmp(pAgreementInfo->pwszKDF, BCRYPT_KDF_SP80056A_CONCAT ) == 0) { }*/ - return SCARD_S_SUCCESS; + MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } @@ -4703,29 +5813,34 @@ DWORD WINAPI CardDestroyDHAgreement( VENDOR_SPECIFIC *vs; struct md_dh_agreement* agreement = NULL; + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardDestroyDHAgreement\n"); + if (!pCardData) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (dwFlags) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (bSecretAgreementIndex >= vs->allocatedAgreements) { - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } agreement = vs->dh_agreements + bSecretAgreementIndex; if (agreement->pbAgreement == NULL) { - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } SecureZeroMemory(agreement->pbAgreement, agreement->dwSize); pCardData->pfnCspFree(agreement->pbAgreement); agreement->pbAgreement = 0; agreement->dwSize = 0; - return SCARD_S_SUCCESS; + MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } DWORD WINAPI CardGetChallengeEx(__in PCARD_DATA pCardData, @@ -4734,11 +5849,14 @@ DWORD WINAPI CardGetChallengeEx(__in PCARD_DATA pCardData, __out PDWORD pcbChallengeData, __in DWORD dwFlags) { + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardGetChallengeEx - unsupported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } DWORD WINAPI CardAuthenticateEx(__in PCARD_DATA pCardData, @@ -4750,6 +5868,7 @@ DWORD WINAPI CardAuthenticateEx(__in PCARD_DATA pCardData, __out_opt PDWORD pcbSessionPin, __out_opt PDWORD pcAttemptsRemaining) { + DWORD dwret; VENDOR_SPECIFIC *vs; struct sc_pkcs15_object *pin_obj = NULL; struct sc_pkcs15_auth_info *auth_info = NULL; @@ -4757,13 +5876,19 @@ DWORD WINAPI CardAuthenticateEx(__in PCARD_DATA pCardData, int r; BOOL DisplayPinpadUI = FALSE; + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardAuthenticateEx\n"); if (!pCardData) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + dwret = check_card_reader_status(pCardData, "CardAuthenticateEx"); + if (dwret != SCARD_S_SUCCESS) + MD_FUNC_RETURN(pCardData, 1, dwret); logprintf(pCardData, 2, "CardAuthenticateEx: PinId=%u, dwFlags=0x%08X, cbPinData=%lu, Attempts %s\n", @@ -4771,11 +5896,15 @@ DWORD WINAPI CardAuthenticateEx(__in PCARD_DATA pCardData, (unsigned long)cbPinData, pcAttemptsRemaining ? "YES" : "NO"); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); - r = check_reader_status(pCardData); + if (PinId >= MD_MAX_PINS) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); - if ((vs->p15card) == NULL) - return SCARD_F_INTERNAL_ERROR; + pin_obj = vs->pin_objs[PinId]; + if (!pin_obj) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); #if 0 /* TODO do we need to return SCARD_E_UNSUPPORTED_FEATURE if the card @@ -4784,38 +5913,31 @@ DWORD WINAPI CardAuthenticateEx(__in PCARD_DATA pCardData, if (dwFlags == CARD_AUTHENTICATE_GENERATE_SESSION_PIN || dwFlags == CARD_AUTHENTICATE_SESSION_PIN) { if (! (vs->reader->capabilities & SC_READER_CAP_PIN_PAD || vs->p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH)) - return SCARD_E_UNSUPPORTED_FEATURE; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } #endif if (dwFlags & ~(CARD_AUTHENTICATE_GENERATE_SESSION_PIN | CARD_AUTHENTICATE_SESSION_PIN | CARD_PIN_SILENT_CONTEXT)) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + if (dwFlags & CARD_AUTHENTICATE_GENERATE_SESSION_PIN && + (ppbSessionPin == NULL || pcbSessionPin == NULL)) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); /* using a pin pad */ if (NULL == pbPinData) { if (!(vs->reader->capabilities & SC_READER_CAP_PIN_PAD || vs->p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH)) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (!(dwFlags & CARD_PIN_SILENT_CONTEXT) && !(vs->ctx->flags & SC_CTX_FLAG_DISABLE_POPUPS)) { DisplayPinpadUI = TRUE; } } - if (PinId != ROLE_USER) - return SCARD_E_INVALID_PARAMETER; - if(pcAttemptsRemaining) (*pcAttemptsRemaining) = (DWORD) -1; - r = md_get_pin_by_role(pCardData, PinId, &pin_obj); - if (r != SCARD_S_SUCCESS) { - logprintf(pCardData, 2, "Cannot get User PIN object"); - return r; - } - - if (!pin_obj) - return SCARD_F_INTERNAL_ERROR; auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; /* save the pin type */ auth_method = auth_info->auth_method; @@ -4834,41 +5956,66 @@ DWORD WINAPI CardAuthenticateEx(__in PCARD_DATA pCardData, cbPinData = 0; } else { /* seems we have a real session pin, set the pin type accordingly */ - logprintf(pCardData, 2, "use real session pin with %d bytes", cbPinData); + logprintf(pCardData, 2, + "use real session pin with %lu bytes", + (unsigned long)cbPinData); auth_info->auth_method = SC_AC_SESSION; } } /* set the session pin according to the minidriver specification */ if (dwFlags & CARD_AUTHENTICATE_GENERATE_SESSION_PIN) { + size_t session_pin_len = SC_MAX_PIN_SIZE; + logprintf(pCardData, 2, "generating session pin"); - if (ppbSessionPin) *ppbSessionPin = pCardData->pfnCspAlloc(SC_MAX_PIN_SIZE); - if (ppbSessionPin) *pcbSessionPin = SC_MAX_PIN_SIZE; - r = md_dialog_perform_pin_operation(pCardData, SC_PIN_CMD_GET_SESSION_PIN, vs->p15card, pin_obj, (const u8 *) pbPinData, cbPinData, - ppbSessionPin && *ppbSessionPin ? *ppbSessionPin : NULL, pcbSessionPin, DisplayPinpadUI); + *ppbSessionPin = pCardData->pfnCspAlloc(SC_MAX_PIN_SIZE); + r = md_dialog_perform_pin_operation(pCardData, + SC_PIN_CMD_GET_SESSION_PIN, + vs->p15card, pin_obj, + (const u8 *) pbPinData, + cbPinData, + *ppbSessionPin, + *ppbSessionPin != NULL ? + &session_pin_len : NULL, + DisplayPinpadUI, PinId); if (r) { - if (ppbSessionPin) { + if (*ppbSessionPin != NULL) { pCardData->pfnCspFree(*ppbSessionPin); *ppbSessionPin = NULL; } - if (pcbSessionPin) *pcbSessionPin = 0; + *pcbSessionPin = 0; logprintf(pCardData, 2, "generating session pin failed"); } else { - if (pcbSessionPin && *pcbSessionPin) { - logprintf(pCardData, 2, "generated session pin with %d bytes", *pcbSessionPin); + if (*ppbSessionPin != NULL && session_pin_len > 0) { + logprintf(pCardData, 2, + "generated session pin with %"SC_FORMAT_LEN_SIZE_T"u bytes", + session_pin_len); + + *pcbSessionPin = session_pin_len; } else { logprintf(pCardData, 2, "session pin not supported"); - if (ppbSessionPin) { + if (*ppbSessionPin != NULL) { pCardData->pfnCspFree(*ppbSessionPin); *ppbSessionPin = NULL; } + *pcbSessionPin = 0; } } } else { if (pcbSessionPin) *pcbSessionPin = 0; if (ppbSessionPin) *ppbSessionPin = NULL; logprintf(pCardData, 2, "standard pin verification"); - r = md_dialog_perform_pin_operation(pCardData, SC_PIN_CMD_VERIFY, vs->p15card, pin_obj, (const u8 *) pbPinData, cbPinData, NULL, NULL, DisplayPinpadUI); + /* + * TODO the use of auth_method being overridden to do session pin + * conflicts with framework-pkcs15.c use of auth_method SC_AC_CONTEXT_SPECIFIC + * for a different purpose. But needs to be reviewed + */ + if (PinId == MD_ROLE_USER_SIGN && vs->need_pin_always) { + logprintf(pCardData, 7, "Setting SC_AC_CONTEXT_SPECIFIC cbPinData: %lu old auth_method: %0x auth_id:%x \n", + (unsigned long) cbPinData, (unsigned int) auth_info->auth_method, (unsigned char) auth_info->auth_id.value[0]); + auth_info->auth_method = SC_AC_CONTEXT_SPECIFIC; + } + r = md_dialog_perform_pin_operation(pCardData, SC_PIN_CMD_VERIFY, vs->p15card, pin_obj, (const u8 *) pbPinData, cbPinData, NULL, NULL, DisplayPinpadUI, PinId); } /* restore the pin type */ @@ -4880,31 +6027,31 @@ DWORD WINAPI CardAuthenticateEx(__in PCARD_DATA pCardData, if (r == SC_ERROR_AUTH_METHOD_BLOCKED) { if(pcAttemptsRemaining) (*pcAttemptsRemaining) = 0; - return SCARD_W_CHV_BLOCKED; + MD_FUNC_RETURN(pCardData, 1, SCARD_W_CHV_BLOCKED); } if(pcAttemptsRemaining) (*pcAttemptsRemaining) = auth_info->tries_left; - return md_translate_OpenSC_to_Windows_error(r, SCARD_W_WRONG_CHV); + MD_FUNC_RETURN(pCardData, 1, md_translate_OpenSC_to_Windows_error(r, SCARD_W_WRONG_CHV)); } logprintf(pCardData, 2, "Pin code correct.\n"); /* set the session pin according to the minidriver specification */ - if (dwFlags == CARD_AUTHENTICATE_GENERATE_SESSION_PIN - && pcbSessionPin && *pcbSessionPin == 0 + if (dwFlags & CARD_AUTHENTICATE_GENERATE_SESSION_PIN + && *pcbSessionPin == 0 && (vs->reader->capabilities & SC_READER_CAP_PIN_PAD || vs->p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH)) { /* If we could not generate a real session PIN, set it to a special * value for pinpad authentication to force a new pinpad authentication */ - *pcbSessionPin = sizeof(MAGIC_SESSION_PIN); - if (ppbSessionPin) { - *ppbSessionPin = pCardData->pfnCspAlloc(sizeof(MAGIC_SESSION_PIN)); - if (ppbSessionPin) memcpy(*ppbSessionPin, MAGIC_SESSION_PIN, sizeof(MAGIC_SESSION_PIN)); + *ppbSessionPin = pCardData->pfnCspAlloc(sizeof(MAGIC_SESSION_PIN)); + if (*ppbSessionPin != NULL) { + memcpy(*ppbSessionPin, MAGIC_SESSION_PIN, sizeof(MAGIC_SESSION_PIN)); + *pcbSessionPin = sizeof(MAGIC_SESSION_PIN); } } - return SCARD_S_SUCCESS; + MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } @@ -4919,12 +6066,15 @@ DWORD WINAPI CardChangeAuthenticatorEx(__in PCARD_DATA pCardData, __in DWORD cRetryCount, __out_opt PDWORD pcAttemptsRemaining) { + DWORD dwret; VENDOR_SPECIFIC *vs = NULL; - DWORD dw_rv; struct sc_pkcs15_object *pin_obj = NULL; int rv; struct sc_pkcs15_auth_info *auth_info; BOOL DisplayPinpadUI = FALSE; + size_t target_len = cbTargetData; + + MD_FUNC_CALLED(pCardData, 1); logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), @@ -4932,24 +6082,32 @@ DWORD WINAPI CardChangeAuthenticatorEx(__in PCARD_DATA pCardData, logprintf(pCardData, 1, "CardChangeAuthenticatorEx\n"); if (!pCardData) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + dwret = check_card_reader_status(pCardData, "CardChangeAuthenticatorEx"); + if (dwret != SCARD_S_SUCCESS) + MD_FUNC_RETURN(pCardData, 1, dwret); + + vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + if (!(dwFlags & PIN_CHANGE_FLAG_UNBLOCK) && !(dwFlags & PIN_CHANGE_FLAG_CHANGEPIN)){ logprintf(pCardData, 1, "Unknown flag\n"); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } if ((dwFlags & PIN_CHANGE_FLAG_UNBLOCK) && (dwFlags & PIN_CHANGE_FLAG_CHANGEPIN)) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (dwFlags & PIN_CHANGE_FLAG_UNBLOCK && dwAuthenticatingPinId == dwTargetPinId) - return SCARD_E_INVALID_PARAMETER; - if (dwAuthenticatingPinId != ROLE_USER && dwAuthenticatingPinId != ROLE_ADMIN) - return SCARD_E_INVALID_PARAMETER; - if (dwTargetPinId != ROLE_USER && dwTargetPinId != ROLE_ADMIN) { - logprintf(pCardData, 1, "Only ROLE_USER or ROLE_ADMIN is supported\n"); - return SCARD_E_INVALID_PARAMETER; - } + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + if (dwAuthenticatingPinId >= MD_MAX_PINS || dwTargetPinId >= MD_MAX_PINS) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + if (!vs->pin_objs[dwAuthenticatingPinId] || !vs->pin_objs[dwTargetPinId]) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + /* according to the spec: cRetryCount MUST be zero */ if (cRetryCount) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); logprintf(pCardData, 2, "CardChangeAuthenticatorEx: AuthenticatingPinId=%u, dwFlags=0x%08X, cbAuthenticatingPinData=%lu, TargetPinId=%u, cbTargetData=%lu, Attempts %s\n", @@ -4958,21 +6116,16 @@ DWORD WINAPI CardChangeAuthenticatorEx(__in PCARD_DATA pCardData, (unsigned int)dwTargetPinId, (unsigned long)cbTargetData, pcAttemptsRemaining ? "YES" : "NO"); - - check_reader_status(pCardData); - - vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); - if (!(vs->reader->capabilities & SC_READER_CAP_PIN_PAD || vs->p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH)) { if (pbAuthenticatingPinData == NULL || cbAuthenticatingPinData == 0) { logprintf(pCardData, 1, "Invalid current PIN data\n"); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } if (pbTargetData == NULL || cbTargetData == 0) { logprintf(pCardData, 1, "Invalid new PIN data\n"); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } } /* using a pin pad */ @@ -4983,20 +6136,22 @@ DWORD WINAPI CardChangeAuthenticatorEx(__in PCARD_DATA pCardData, } } - dw_rv = md_get_pin_by_role(pCardData, dwTargetPinId, &pin_obj); - if (dw_rv != SCARD_S_SUCCESS) { - logprintf(pCardData, 2, "Cannot get User PIN object %s", (dwTargetPinId==ROLE_ADMIN?"admin":"user")); - return dw_rv; - } - if (!pin_obj) - return SCARD_F_INTERNAL_ERROR; + pin_obj = vs->pin_objs[dwTargetPinId]; if(pcAttemptsRemaining) (*pcAttemptsRemaining) = (DWORD) -1; - rv = md_dialog_perform_pin_operation(pCardData, (dwFlags & PIN_CHANGE_FLAG_UNBLOCK ? SC_PIN_CMD_UNBLOCK:SC_PIN_CMD_CHANGE), - vs->p15card, pin_obj, (const u8 *) pbAuthenticatingPinData, cbAuthenticatingPinData, pbTargetData, &cbTargetData, DisplayPinpadUI); - + /* FIXME: this does not enforce dwAuthenticatingPinId */ + rv = md_dialog_perform_pin_operation(pCardData, + (dwFlags & PIN_CHANGE_FLAG_UNBLOCK ? + SC_PIN_CMD_UNBLOCK : + SC_PIN_CMD_CHANGE), + vs->p15card, pin_obj, + (const u8 *) pbAuthenticatingPinData, + cbAuthenticatingPinData, + pbTargetData, &target_len, + DisplayPinpadUI, dwTargetPinId); + if (rv) { logprintf(pCardData, 2, "Failed to %s %s PIN: '%s' (%i)\n", (dwFlags & PIN_CHANGE_FLAG_CHANGEPIN?"change":"unblock"), @@ -5005,22 +6160,23 @@ DWORD WINAPI CardChangeAuthenticatorEx(__in PCARD_DATA pCardData, if (rv == SC_ERROR_AUTH_METHOD_BLOCKED) { if(pcAttemptsRemaining) (*pcAttemptsRemaining) = 0; - return SCARD_W_CHV_BLOCKED; + MD_FUNC_RETURN(pCardData, 1, SCARD_W_CHV_BLOCKED); } if(pcAttemptsRemaining) (*pcAttemptsRemaining) = auth_info->tries_left; - return md_translate_OpenSC_to_Windows_error(rv, SCARD_W_WRONG_CHV); + MD_FUNC_RETURN(pCardData, 1, md_translate_OpenSC_to_Windows_error(rv, SCARD_W_WRONG_CHV)); } logprintf(pCardData, 7, "returns success\n"); - return SCARD_S_SUCCESS; + MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } DWORD WINAPI CardDeauthenticateEx(__in PCARD_DATA pCardData, __in PIN_SET PinId, __in DWORD dwFlags) { + MD_FUNC_CALLED(pCardData, 1); logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), @@ -5029,7 +6185,7 @@ DWORD WINAPI CardDeauthenticateEx(__in PCARD_DATA pCardData, "CardDeauthenticateEx PinId=%u dwFlags=0x%08X\n", (unsigned int)PinId, (unsigned int)dwFlags); - return CardDeauthenticate(pCardData, wszCARD_USER_USER, 0); + MD_FUNC_RETURN(pCardData, 1, CardDeauthenticate(pCardData, wszCARD_USER_USER, 0)); } DWORD WINAPI CardGetContainerProperty(__in PCARD_DATA pCardData, @@ -5040,38 +6196,48 @@ DWORD WINAPI CardGetContainerProperty(__in PCARD_DATA pCardData, __out PDWORD pdwDataLen, __in DWORD dwFlags) { + DWORD dwret; VENDOR_SPECIFIC *vs = NULL; struct md_pkcs15_container *cont = NULL; + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardGetContainerProperty\n"); - check_reader_status(pCardData); + if (!pCardData) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + dwret = check_card_status(pCardData, "CardGetContainerProperty"); + if (dwret != SCARD_S_SUCCESS) + MD_FUNC_RETURN(pCardData, 1, dwret); - if (!pCardData) return SCARD_E_INVALID_PARAMETER; logprintf(pCardData, 2, "CardGetContainerProperty bContainerIndex=%u, wszProperty=%S, cbData=%lu, dwFlags=0x%08X\n", (unsigned int)bContainerIndex, NULLWSTR(wszProperty), (unsigned long)cbData, (unsigned int)dwFlags); if (!wszProperty) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (dwFlags) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (!pbData || !pdwDataLen) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (bContainerIndex >= MD_MAX_KEY_CONTAINERS) - return SCARD_E_NO_KEY_CONTAINER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_NO_KEY_CONTAINER); - /* the test for the existence of containers is redondant with the one made in CardGetContainerInfo but CCP_PIN_IDENTIFIER does not do it */ + /* the test for the existence of containers is redundant with the one made in CardGetContainerInfo but CCP_PIN_IDENTIFIER does not do it */ vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + cont = &vs->p15_containers[bContainerIndex]; if (!cont->prkey_obj) { logprintf(pCardData, 7, "Container %u is empty\n", (unsigned int)bContainerIndex); - return SCARD_E_NO_KEY_CONTAINER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_NO_KEY_CONTAINER); } if (wcscmp(CCP_CONTAINER_INFO,wszProperty) == 0) { @@ -5079,10 +6245,10 @@ DWORD WINAPI CardGetContainerProperty(__in PCARD_DATA pCardData, if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData >= sizeof(DWORD)) if (p->dwVersion != CONTAINER_INFO_CURRENT_VERSION && p->dwVersion != 0 ) - return ERROR_REVISION_MISMATCH; + MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); if (cbData < sizeof(*p)) - return ERROR_INSUFFICIENT_BUFFER; - return CardGetContainerInfo(pCardData,bContainerIndex,0,p); + MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); + MD_FUNC_RETURN(pCardData, 1, CardGetContainerInfo(pCardData,bContainerIndex,0,p)); } if (wcscmp(CCP_PIN_IDENTIFIER,wszProperty) == 0) { @@ -5090,14 +6256,41 @@ DWORD WINAPI CardGetContainerProperty(__in PCARD_DATA pCardData, if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) - return ERROR_INSUFFICIENT_BUFFER; - *p = ROLE_USER; + MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); + + if (cont->prkey_obj->auth_id.len == 0) + *p = ROLE_EVERYONE; + else { + size_t pinidx; + for (pinidx = 0; pinidx < MD_MAX_PINS; pinidx++) { + struct sc_pkcs15_auth_info *pin_info; + + if (!vs->pin_objs[pinidx]) + continue; + + pin_info = + (struct sc_pkcs15_auth_info *)vs->pin_objs[pinidx]->data; + + if (sc_pkcs15_compare_id(&cont->prkey_obj->auth_id, + &pin_info->auth_id)) + break; + } + + if (pinidx >= MD_MAX_PINS) { + logprintf(pCardData, 2, + "Could not find container %i PIN, returning no PIN needed, might not work properly\n", + bContainerIndex); + *p = ROLE_EVERYONE; + } else + *p = (PIN_ID)pinidx; + } + logprintf(pCardData, 2, "Return Pin id %u\n", (unsigned int)*p); - return SCARD_S_SUCCESS; + MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } DWORD WINAPI CardSetContainerProperty(__in PCARD_DATA pCardData, @@ -5107,11 +6300,14 @@ DWORD WINAPI CardSetContainerProperty(__in PCARD_DATA pCardData, __in DWORD cbDataLen, __in DWORD dwFlags) { + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardSetContainerProperty - unsupported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } @@ -5125,6 +6321,8 @@ DWORD WINAPI CardGetProperty(__in PCARD_DATA pCardData, VENDOR_SPECIFIC *vs; DWORD dwret; + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); @@ -5134,25 +6332,29 @@ DWORD WINAPI CardGetProperty(__in PCARD_DATA pCardData, (unsigned long)dwFlags); if (!pCardData || !wszProperty) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (!pbData || !pdwDataLen) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); - vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + dwret = check_card_reader_status(pCardData, "CardGetProperty"); + if (dwret != SCARD_S_SUCCESS) + MD_FUNC_RETURN(pCardData, 1, dwret); - check_reader_status(pCardData); + vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (wcscmp(CP_CARD_FREE_SPACE,wszProperty) == 0) { PCARD_FREE_SPACE_INFO pCardFreeSpaceInfo = (PCARD_FREE_SPACE_INFO )pbData; if (pdwDataLen) *pdwDataLen = sizeof(*pCardFreeSpaceInfo); if (cbData < sizeof(*pCardFreeSpaceInfo)) - return SCARD_E_NO_MEMORY; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_NO_MEMORY); dwret = md_free_space(pCardData, pCardFreeSpaceInfo); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "Get free space error"); - return dwret; + MD_FUNC_RETURN(pCardData, 1, dwret); } } else if (wcscmp(CP_CARD_CAPABILITIES, wszProperty) == 0) { @@ -5161,28 +6363,29 @@ DWORD WINAPI CardGetProperty(__in PCARD_DATA pCardData, if (pdwDataLen) *pdwDataLen = sizeof(*pCardCapabilities); if (cbData < sizeof(*pCardCapabilities)) - return ERROR_INSUFFICIENT_BUFFER; + MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); dwret = md_card_capabilities(pCardData, pCardCapabilities); if (dwret != SCARD_S_SUCCESS) - return dwret; + MD_FUNC_RETURN(pCardData, 1, dwret); } else if (wcscmp(CP_CARD_KEYSIZES,wszProperty) == 0) { PCARD_KEY_SIZES pKeySizes = (PCARD_KEY_SIZES )pbData; if (pdwDataLen) *pdwDataLen = sizeof(*pKeySizes); if (cbData < sizeof(*pKeySizes)) - return ERROR_INSUFFICIENT_BUFFER; + MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); - dwret = md_query_key_sizes(pCardData, 0, pKeySizes); + /* dwFlags has key_type */ + dwret = md_query_key_sizes(pCardData, dwFlags, pKeySizes); if (dwret != SCARD_S_SUCCESS) - return dwret; + MD_FUNC_RETURN(pCardData, 1, dwret); } else if (wcscmp(CP_CARD_READ_ONLY, wszProperty) == 0) { BOOL *p = (BOOL *)pbData; if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) - return ERROR_INSUFFICIENT_BUFFER; + MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); *p = md_is_read_only(pCardData); } @@ -5191,7 +6394,7 @@ DWORD WINAPI CardGetProperty(__in PCARD_DATA pCardData, if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) - return ERROR_INSUFFICIENT_BUFFER; + MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); *p = CP_CACHE_MODE_NO_CACHE; } else if (wcscmp(CP_SUPPORTS_WIN_X509_ENROLLMENT, wszProperty) == 0) { @@ -5199,7 +6402,7 @@ DWORD WINAPI CardGetProperty(__in PCARD_DATA pCardData, if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) - return ERROR_INSUFFICIENT_BUFFER; + MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); *p = md_is_supports_X509_enrollment(pCardData); } else if (wcscmp(CP_CARD_GUID, wszProperty) == 0) { @@ -5208,13 +6411,13 @@ DWORD WINAPI CardGetProperty(__in PCARD_DATA pCardData, md_fs_find_file(pCardData, NULL, "cardid", &cardid); if (!cardid) { logprintf(pCardData, 2, "file 'cardid' not found\n"); - return SCARD_E_FILE_NOT_FOUND; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_FILE_NOT_FOUND); } if (pdwDataLen) *pdwDataLen = (DWORD) cardid->size; if (cbData < cardid->size) - return ERROR_INSUFFICIENT_BUFFER; + MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); CopyMemory(pbData, cardid->blob, cardid->size); } @@ -5233,7 +6436,7 @@ DWORD WINAPI CardGetProperty(__in PCARD_DATA pCardData, if (pdwDataLen) *pdwDataLen = (DWORD) buf_len; if (cbData < buf_len) - return ERROR_INSUFFICIENT_BUFFER; + MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); CopyMemory(pbData, buf, buf_len); } @@ -5244,27 +6447,34 @@ DWORD WINAPI CardGetProperty(__in PCARD_DATA pCardData, *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) - return ERROR_INSUFFICIENT_BUFFER; + MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); if (p->dwVersion != PIN_INFO_CURRENT_VERSION) - return ERROR_REVISION_MISMATCH; + MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); + + if (dwFlags >= MD_MAX_PINS) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + if (dwFlags != ROLE_EVERYONE && vs->pin_objs[dwFlags] == NULL) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); p->PinType = vs->reader->capabilities & SC_READER_CAP_PIN_PAD || vs->p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH ? ExternalPinType : AlphaNumericPinType; p->dwFlags = 0; switch (dwFlags) { - case ROLE_USER: + case ROLE_EVERYONE: logprintf(pCardData, 2, - "returning info on PIN ROLE_USER ( Auth ) [%lu]\n", - (unsigned long)dwFlags); - p->PinPurpose = DigitalSignaturePin; + "returning info on PIN ROLE_EVERYONE [%lu]\n", + (unsigned long)dwFlags); + p->PinType = 0; /* There is no pin, so don't need reader capabilities */ + p->PinPurpose = 0; /* It can not be PrimaryCardPin */ p->PinCachePolicy.dwVersion = PIN_CACHE_POLICY_CURRENT_VERSION; + p->PinCachePolicy.PinCachePolicyType = PinCacheNone; p->PinCachePolicy.dwPinCachePolicyInfo = 0; - p->PinCachePolicy.PinCachePolicyType = PinCacheNormal; - p->dwChangePermission = CREATE_PIN_SET(ROLE_USER); - p->dwUnblockPermission = CREATE_PIN_SET(ROLE_ADMIN); + p->dwChangePermission = 0; break; + case ROLE_ADMIN: logprintf(pCardData, 2, "returning info on PIN ROLE_ADMIN ( Unblock ) [%lu]\n", @@ -5277,39 +6487,76 @@ DWORD WINAPI CardGetProperty(__in PCARD_DATA pCardData, p->dwUnblockPermission = 0; break; default: - logprintf(pCardData, 0, - "Invalid Pin number %lu requested\n", + logprintf(pCardData, 2, + "returning info on normal PIN [%lu]\n", (unsigned long)dwFlags); - return SCARD_E_INVALID_PARAMETER; + + if (dwFlags == ROLE_USER) { + p->PinCachePolicy.PinCachePolicyType = PinCacheNormal; + p->PinPurpose = PrimaryCardPin; + } + else if (dwFlags == MD_ROLE_USER_SIGN) { + logprintf(pCardData, 7, "vs->need_pin_always %d\n", (int) vs->need_pin_always); + if (vs->need_pin_always) { + p->PinCachePolicy.PinCachePolicyType = PinCacheAlwaysPrompt; + logprintf(pCardData, 7, "Setting PinCacheAlwaysPrompt\n"); + } + else + p->PinCachePolicy.PinCachePolicyType = PinCacheNormal; + + p->PinPurpose = DigitalSignaturePin; + } + else { + p->PinPurpose = AuthenticationPin; + p->PinCachePolicy.PinCachePolicyType = PinCacheNormal; + } + + p->PinCachePolicy.dwVersion = PIN_CACHE_POLICY_CURRENT_VERSION; + p->PinCachePolicy.dwPinCachePolicyInfo = 0; + p->dwChangePermission = CREATE_PIN_SET(dwFlags); + p->dwUnblockPermission = CREATE_PIN_SET(ROLE_ADMIN); + break; } } else if (wcscmp(CP_CARD_LIST_PINS,wszProperty) == 0) { PPIN_SET p = (PPIN_SET) pbData; + size_t pinidx; if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) - return ERROR_INSUFFICIENT_BUFFER; - SET_PIN(*p, ROLE_USER); + MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); + + memset(p, 0, sizeof(*p)); + for (pinidx = 0; pinidx < MD_MAX_PINS; pinidx++) { + if (!vs->pin_objs[pinidx]) + continue; + + SET_PIN(*p, (PIN_ID)pinidx); + } } else if (wcscmp(CP_CARD_AUTHENTICATED_STATE,wszProperty) == 0) { PPIN_SET p = (PPIN_SET) pbData; if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) - return ERROR_INSUFFICIENT_BUFFER; + MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); logprintf(pCardData, 7, "CARD_AUTHENTICATED_STATE invalid\n"); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } else if (wcscmp(CP_CARD_PIN_STRENGTH_VERIFY,wszProperty) == 0) { DWORD *p = (DWORD *)pbData; - if (dwFlags != ROLE_USER) - return SCARD_E_INVALID_PARAMETER; + if (dwFlags >= MD_MAX_PINS) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + if (!vs->pin_objs[dwFlags]) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) - return ERROR_INSUFFICIENT_BUFFER; + MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); *p = CARD_PIN_STRENGTH_PLAINTEXT; if (vs->p15card->card->caps & SC_CARD_CAP_SESSION_PIN) { *p |= CARD_PIN_STRENGTH_SESSION_PIN; @@ -5320,33 +6567,33 @@ DWORD WINAPI CardGetProperty(__in PCARD_DATA pCardData, if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) - return ERROR_INSUFFICIENT_BUFFER; + MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); *p = 0; } else if (wcscmp(CP_ENUM_ALGORITHMS, wszProperty) == 0) { logprintf(pCardData, 3, "Unsupported property '%S'\n", wszProperty); //TODO - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } else if (wcscmp(CP_PADDING_SCHEMES, wszProperty) == 0) { logprintf(pCardData, 3, "Unsupported property '%S'\n", wszProperty); //TODO - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } else if (wcscmp(CP_CHAINING_MODES, wszProperty) == 0) { logprintf(pCardData, 3, "Unsupported property '%S'\n", wszProperty); //TODO - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } else { logprintf(pCardData, 3, "Unsupported property '%S'\n", wszProperty); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } logprintf(pCardData, 7, "returns '%S' ", wszProperty); loghex(pCardData, 7, pbData, *pdwDataLen); - return SCARD_S_SUCCESS; + MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } DWORD WINAPI CardSetProperty(__in PCARD_DATA pCardData, @@ -5357,13 +6604,15 @@ DWORD WINAPI CardSetProperty(__in PCARD_DATA pCardData, { VENDOR_SPECIFIC *vs; + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardSetProperty\n"); if (!pCardData) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); logprintf(pCardData, 2, "CardSetProperty wszProperty=%S, pbData=%p, cbDataLen=%lu, dwFlags=%lu", @@ -5371,15 +6620,17 @@ DWORD WINAPI CardSetProperty(__in PCARD_DATA pCardData, (unsigned long)dwFlags); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (!wszProperty) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (dwFlags) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (!cbDataLen) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); /* the following properties cannot be set according to the minidriver specifications */ if (wcscmp(wszProperty,CP_CARD_FREE_SPACE) == 0 || @@ -5395,14 +6646,14 @@ DWORD WINAPI CardSetProperty(__in PCARD_DATA pCardData, wcscmp(wszProperty,CP_CARD_CACHE_MODE) == 0 || wcscmp(wszProperty,CP_CARD_SERIAL_NO) == 0 ) { - return SCARD_E_UNSUPPORTED_FEATURE; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /* the following properties can be set, but are not implemented by the minidriver */ if (wcscmp(CP_CARD_PIN_STRENGTH_VERIFY, wszProperty) == 0 || wcscmp(CP_CARD_PIN_INFO, wszProperty) == 0 || wcscmp(CP_CARD_GUID, wszProperty) == 0 ) { - return SCARD_E_UNSUPPORTED_FEATURE; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /* This property and CP_PIN_CONTEXT_STRING are set just prior to a call to @@ -5410,25 +6661,25 @@ DWORD WINAPI CardSetProperty(__in PCARD_DATA pCardData, */ if (wcscmp(CP_PARENT_WINDOW, wszProperty) == 0) { if (cbDataLen != sizeof(HWND) || !pbData) { - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } else { HWND cp = *((HWND *) pbData); if (cp!=0 && !IsWindow(cp)) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); vs->hwndParent = cp; } logprintf(pCardData, 3, "Saved parent window (%p)\n", vs->hwndParent); - return SCARD_S_SUCCESS; + MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } - + if (wcscmp(CP_PIN_CONTEXT_STRING, wszProperty) == 0) { vs->wszPinContext = (PWSTR) pbData; logprintf(pCardData, 3, "Saved PIN context string: %S\n", (PWSTR) pbData); - return SCARD_S_SUCCESS; + MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } logprintf(pCardData, 3, "INVALID PARAMETER\n"); - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } @@ -5461,11 +6712,14 @@ DWORD WINAPI CardImportSessionKey( UNREFERENCED_PARAMETER(pbInput); UNREFERENCED_PARAMETER(cbInput); UNREFERENCED_PARAMETER(dwFlags); + + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardImportSessionKey - unsupported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /** The MDImportSessionKey function imports a temporary session key to the card minidriver @@ -5486,11 +6740,14 @@ DWORD WINAPI MDImportSessionKey( UNREFERENCED_PARAMETER(phKey); UNREFERENCED_PARAMETER(pbInput); UNREFERENCED_PARAMETER(cbInput); + + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "MDImportSessionKey - unsupported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /** The MDEncryptData function uses a key handle to encrypt data with a symmetric key. @@ -5516,11 +6773,14 @@ DWORD WINAPI MDEncryptData( UNREFERENCED_PARAMETER(dwFlags); UNREFERENCED_PARAMETER(ppEncryptedData); UNREFERENCED_PARAMETER(pcEncryptedData); + + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "MDEncryptData - unsupported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } @@ -5546,11 +6806,15 @@ DWORD WINAPI CardGetSharedKeyHandle( UNREFERENCED_PARAMETER(ppbOutput); UNREFERENCED_PARAMETER(pcbOutput); UNREFERENCED_PARAMETER(phKey); + + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardGetSharedKeyHandle - unsupported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); + } /** The CardDestroyKey function releases a temporary key on the card. The card @@ -5563,11 +6827,14 @@ DWORD WINAPI CardDestroyKey( { UNREFERENCED_PARAMETER(pCardData); UNREFERENCED_PARAMETER(hKey); + + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardDestroyKey - unsupported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /** This function can be used to get properties for a cryptographic algorithm.*/ @@ -5589,11 +6856,14 @@ DWORD WINAPI CardGetAlgorithmProperty ( UNREFERENCED_PARAMETER(cbData); UNREFERENCED_PARAMETER(pdwDataLen); UNREFERENCED_PARAMETER(dwFlags); + + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardGetAlgorithmProperty - unsupported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /** This function is used to get the properties of a key.*/ @@ -5614,11 +6884,14 @@ DWORD WINAPI CardGetKeyProperty( UNREFERENCED_PARAMETER(cbData); UNREFERENCED_PARAMETER(pdwDataLen); UNREFERENCED_PARAMETER(dwFlags); + + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardGetKeyProperty - unsupported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /** This function is used to set the properties of a key.*/ @@ -5638,11 +6911,15 @@ DWORD WINAPI CardSetKeyProperty( UNREFERENCED_PARAMETER(pbInput); UNREFERENCED_PARAMETER(cbInput); UNREFERENCED_PARAMETER(dwFlags); + + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardSetKeyProperty - unsupported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); + } /** CardProcessEncryptedData processes a set of encrypted data BLOBs by @@ -5671,105 +6948,88 @@ DWORD WINAPI CardProcessEncryptedData( UNREFERENCED_PARAMETER(cbOutput); UNREFERENCED_PARAMETER(pdwOutputLen); UNREFERENCED_PARAMETER(dwFlags); + + MD_FUNC_CALLED(pCardData, 1); + logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardProcessEncryptedData - unsupported\n"); - return SCARD_E_UNSUPPORTED_FEATURE; -} - -/** The CardCreateContainerEx function creates a new key container that the -container index identifies and the bContainerIndex parameter specifies. The function -associates the key container with the PIN that the PinId parameter specified. -This function is useful if the card-edge does not allow for changing the key attributes -after the key container is created. This function replaces the need to call -CardSetContainerProperty to set the CCP_PIN_IDENTIFIER property CardCreateContainer -is called. -The caller of this function can provide the key material that the card imports. -This is useful in those situations in which the card either does not support internal -key generation or the caller requests that the key be archived in the card.*/ -DWORD WINAPI CardCreateContainerEx( - __in PCARD_DATA pCardData, - __in BYTE bContainerIndex, - __in DWORD dwFlags, - __in DWORD dwKeySpec, - __in DWORD dwKeySize, - __in PBYTE pbKeyData, - __in PIN_ID PinId -) -{ - if (PinId == ROLE_ADMIN) - return SCARD_W_SECURITY_VIOLATION; - if (PinId != ROLE_USER) - return SCARD_E_INVALID_PARAMETER; - /* basically CardCreateContainerEx is CardCreateContainer + the PinId */ - return CardCreateContainer(pCardData, bContainerIndex, dwFlags, dwKeySpec, dwKeySize, pbKeyData); + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } DWORD WINAPI CardAcquireContext(__inout PCARD_DATA pCardData, __in DWORD dwFlags) { VENDOR_SPECIFIC *vs; DWORD dwret, suppliedVersion = 0; + CRITICAL_SECTION hScard_lock; if (!pCardData) - return SCARD_E_INVALID_PARAMETER; - if (dwFlags) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + + MD_FUNC_CALLED(pCardData, 1); + + if (dwFlags & ~CARD_SECURE_KEY_INJECTION_NO_CARD_MODE) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + if (!(dwFlags & CARD_SECURE_KEY_INJECTION_NO_CARD_MODE)) { if( pCardData->hSCardCtx == 0) { - logprintf(pCardData, 0, "Invalide handle.\n"); - return SCARD_E_INVALID_HANDLE; + logprintf(pCardData, 0, "Invalid handle.\n"); + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_HANDLE); } if( pCardData->hScard == 0) { - logprintf(pCardData, 0, "Invalide handle.\n"); - return SCARD_E_INVALID_HANDLE; + logprintf(pCardData, 0, "Invalid handle.\n"); + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_HANDLE); } } else { /* secure key injection not supported */ - return SCARD_E_UNSUPPORTED_FEATURE; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } if (pCardData->pbAtr == NULL) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if ( pCardData->pwszCardName == NULL ) - return SCARD_E_INVALID_PARAMETER; - /* <2 lenght or >=0x22 are not ISO compliant */ - if (pCardData->cbAtr >= 0x22 || pCardData->cbAtr <= 0x2) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + /* <2 length or >0x22 are not ISO compliant */ + if (pCardData->cbAtr > 0x22 || pCardData->cbAtr < 0x2) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); /* ATR beginning by 0x00 or 0xFF are not ISO compliant */ if (pCardData->pbAtr[0] == 0xFF || pCardData->pbAtr[0] == 0x00) - return SCARD_E_UNKNOWN_CARD; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNKNOWN_CARD); /* Memory management functions */ if ( ( pCardData->pfnCspAlloc == NULL ) || ( pCardData->pfnCspReAlloc == NULL ) || ( pCardData->pfnCspFree == NULL ) ) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); /* The lowest supported version is 4 - maximum is 7. */ if (pCardData->dwVersion < MD_MINIMUM_VERSION_SUPPORTED) - return (DWORD) ERROR_REVISION_MISMATCH; + MD_FUNC_RETURN(pCardData, 1, (DWORD) ERROR_REVISION_MISMATCH); suppliedVersion = pCardData->dwVersion; /* VENDOR SPECIFIC */ vs = pCardData->pvVendorSpecific = pCardData->pfnCspAlloc(sizeof(VENDOR_SPECIFIC)); if (!vs) - return SCARD_E_NO_MEMORY; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_NO_MEMORY); memset(vs, 0, sizeof(VENDOR_SPECIFIC)); + InitializeCriticalSection(&vs->hScard_lock); + lock(pCardData); + logprintf(pCardData, 1, "==================================================================\n"); logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, - "CardAcquireContext, dwVersion=%lu, name=%S,hScard=0x%08X, hSCardCtx=0x%08X\n", + "CardAcquireContext, dwVersion=%lu, name=%S,hScard=0x%08"SC_FORMAT_LEN_SIZE_T"X, hSCardCtx=0x%08"SC_FORMAT_LEN_SIZE_T"X\n", (unsigned long)pCardData->dwVersion, NULLWSTR(pCardData->pwszCardName), - (unsigned int)pCardData->hScard, - (unsigned int)pCardData->hSCardCtx); + (size_t)pCardData->hScard, + (size_t)pCardData->hSCardCtx); vs->hScard = pCardData->hScard; vs->hSCardCtx = pCardData->hSCardCtx; @@ -5781,12 +7041,8 @@ DWORD WINAPI CardAcquireContext(__inout PCARD_DATA pCardData, __in DWORD dwFlags (unsigned long)pCardData->dwVersion); dwret = md_create_context(pCardData, vs); - if (dwret != SCARD_S_SUCCESS) { - pCardData->pfnCspFree(pCardData->pvVendorSpecific); - pCardData->pvVendorSpecific = NULL; - return dwret; - } - md_static_data.flags &= ~MD_STATIC_FLAG_CONTEXT_DELETED; + if (dwret != SCARD_S_SUCCESS) + goto ret_free; pCardData->pfnCardDeleteContext = CardDeleteContext; pCardData->pfnCardQueryCapabilities = CardQueryCapabilities; @@ -5816,18 +7072,12 @@ DWORD WINAPI CardAcquireContext(__inout PCARD_DATA pCardData, __in DWORD dwFlags pCardData->pfnCardConstructDHAgreement = CardConstructDHAgreement; dwret = associate_card(pCardData); - if (dwret != SCARD_S_SUCCESS) { - pCardData->pfnCspFree(pCardData->pvVendorSpecific); - pCardData->pvVendorSpecific = NULL; - return dwret; - } + if (dwret != SCARD_S_SUCCESS) + goto ret_release; dwret = md_fs_init(pCardData); - if (dwret != SCARD_S_SUCCESS) { - pCardData->pfnCspFree(pCardData->pvVendorSpecific); - pCardData->pvVendorSpecific = NULL; - return dwret; - } + if (dwret != SCARD_S_SUCCESS) + goto ret_disassoc; logprintf(pCardData, 1, "OpenSC init done.\n"); logprintf(pCardData, 1, "Supplied version %lu - version used %lu.\n", @@ -5864,20 +7114,40 @@ DWORD WINAPI CardAcquireContext(__inout PCARD_DATA pCardData, __in DWORD dwFlags } } - return SCARD_S_SUCCESS; + unlock(pCardData); + + MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); + +ret_disassoc: + disassociate_card(pCardData); + +ret_release: + sc_release_context(vs->ctx); + +ret_free: + hScard_lock = vs->hScard_lock; + pCardData->pfnCspFree(pCardData->pvVendorSpecific); + pCardData->pvVendorSpecific = NULL; + LeaveCriticalSection(&hScard_lock); + DeleteCriticalSection(&hScard_lock); + MD_FUNC_RETURN(pCardData, 1, dwret); } -static int associate_card(PCARD_DATA pCardData) +static DWORD associate_card(PCARD_DATA pCardData) { VENDOR_SPECIFIC *vs; - DWORD dw; int r; + struct sc_app_info *app_generic; + struct sc_aid *aid; - logprintf(pCardData, 1, "associate_card\n"); + MD_FUNC_CALLED(pCardData, 1); if (!pCardData) - return SCARD_E_INVALID_PARAMETER; + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); + /* * set the addresses of the reader and card handles * Our pcsc code will use these when we call sc_ctx_use_reader @@ -5886,75 +7156,66 @@ static int associate_card(PCARD_DATA pCardData) vs->hSCardCtx = pCardData->hSCardCtx; vs->hScard = pCardData->hScard; - /** - * Check if a linked context has been deleted - if so, repair shared data. - * Multithreaded issue - TODO: proper multithreaded handling - */ - if (md_static_data.flags & MD_STATIC_FLAG_CONTEXT_DELETED) - { - r = sc_context_repair(&(vs->ctx)); - logprintf(pCardData, 2, "sc_context_repair called - result = %d, %s\n", r, sc_strerror(r)); - md_static_data.flags &= ~MD_STATIC_FLAG_CONTEXT_DELETED; - } - /* set the provided reader and card handles into ctx */ - logprintf(pCardData, 5, "sc_ctx_use_reader %d\n", sc_ctx_use_reader(vs->ctx, &vs->hSCardCtx, &vs->hScard)); + r = sc_ctx_use_reader(vs->ctx, &vs->hSCardCtx, &vs->hScard); + if (r != SC_SUCCESS) { + logprintf(pCardData, 1, "sc_ctx_use_reader() failed with %d\n", r); + MD_FUNC_RETURN(pCardData, 1, SCARD_E_COMM_DATA_LOST); + } /* should be only one reader */ logprintf(pCardData, 5, "sc_ctx_get_reader_count(ctx): %d\n", sc_ctx_get_reader_count(vs->ctx)); vs->reader = sc_ctx_get_reader(vs->ctx, 0); - if(vs->reader) { - struct sc_app_info *app_generic = NULL; - struct sc_aid *aid = NULL; - - r = sc_connect_card(vs->reader, &(vs->card)); - if(r) { - logprintf(pCardData, 0, "Cannot connect card in reader '%s'\n", NULLSTR(vs->reader->name)); - return SCARD_E_UNKNOWN_CARD; - } - logprintf(pCardData, 3, "Connected card in '%s'\n", NULLSTR(vs->reader->name)); + if (!vs->reader) + MD_FUNC_RETURN(pCardData, 1, SCARD_E_COMM_DATA_LOST); - app_generic = sc_pkcs15_get_application_by_type(vs->card, "generic"); - if (app_generic) - logprintf(pCardData, 3, "Use generic application '%s'\n", app_generic->label); - aid = app_generic ? &app_generic->aid : NULL; - - r = sc_pkcs15_bind(vs->card, aid, &(vs->p15card)); - logprintf(pCardData, 2, "PKCS#15 initialization result: %d, %s\n", r, sc_strerror(r)); + r = sc_connect_card(vs->reader, &(vs->card)); + if (r != SC_SUCCESS) { + logprintf(pCardData, 0, "Cannot connect card in reader '%s'\n", NULLSTR(vs->reader->name)); + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNKNOWN_CARD); } + logprintf(pCardData, 3, "Connected card in '%s'\n", NULLSTR(vs->reader->name)); - if(vs->card == NULL || vs->p15card == NULL) { - logprintf(pCardData, 0, "Card unknown.\n"); - return SCARD_E_UNKNOWN_CARD; - } + app_generic = sc_pkcs15_get_application_by_type(vs->card, "generic"); + if (app_generic) + logprintf(pCardData, 3, "Use generic application '%s'\n", app_generic->label); + aid = app_generic ? &app_generic->aid : NULL; - dw = md_get_pin_by_role(pCardData, ROLE_USER, &vs->obj_user_pin); - if (dw != SCARD_S_SUCCESS) { - logprintf(pCardData, 2, "Cannot get User PIN object"); - return dw; + r = sc_pkcs15_bind(vs->card, aid, &(vs->p15card)); + logprintf(pCardData, 2, "PKCS#15 initialization result: %d, %s\n", r, sc_strerror(r)); + if (r != SC_SUCCESS) { + logprintf(pCardData, 0, "PKCS#15 init failed.\n"); + sc_disconnect_card(vs->card); + MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNKNOWN_CARD); } - dw = md_get_pin_by_role(pCardData, ROLE_USER, &vs->obj_sopin); - if (dw != SCARD_S_SUCCESS) - logprintf(pCardData, 2, "Cannot get ADMIN PIN object -- ignored"); - - return SCARD_S_SUCCESS; + vs->initialized = TRUE; + MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } -static int disassociate_card(PCARD_DATA pCardData) +static void disassociate_card(PCARD_DATA pCardData) { VENDOR_SPECIFIC *vs; + if (!pCardData) { + logprintf(pCardData, 1, + "disassociate_card called without card data\n"); + return; + } + logprintf(pCardData, 1, "disassociate_card\n"); - if (!pCardData) - return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); + if (!vs) { + logprintf(pCardData, 1, + "disassociate_card called without vendor specific data\n"); + return; + } - vs->obj_user_pin = NULL; - vs->obj_sopin = NULL; + memset(vs->pin_objs, 0, sizeof(vs->pin_objs)); + memset(vs->p15_containers, 0, sizeof(vs->p15_containers)); if(vs->p15card) { logprintf(pCardData, 6, "sc_pkcs15_unbind\n"); @@ -5972,8 +7233,7 @@ static int disassociate_card(PCARD_DATA pCardData) vs->hSCardCtx = -1; vs->hScard = -1; - - return SCARD_S_SUCCESS; + vs->initialized = FALSE; } @@ -6011,10 +7271,23 @@ BOOL APIENTRY DllMain( HINSTANCE hinstDLL, { case DLL_PROCESS_ATTACH: g_inst = hinstDLL; - md_static_data.attach_check = MD_STATIC_PROCESS_ATTACHED; +#ifdef ENABLE_NOTIFY + sc_notify_instance = hinstDLL; + sc_notify_init(); +#endif break; case DLL_PROCESS_DETACH: - md_static_data.attach_check = 0; +#ifdef ENABLE_NOTIFY + sc_notify_close(); +#endif + if (lpReserved == NULL) { +#if defined(ENABLE_OPENSSL) && defined(OPENSSL_SECURE_MALLOC_SIZE) && !defined(LIBRESSL_VERSION_NUMBER) + CRYPTO_secure_malloc_done(); +#endif +#ifdef ENABLE_OPENPACE + EAC_cleanup(); +#endif + } break; } return TRUE; @@ -6024,4 +7297,3 @@ BOOL APIENTRY DllMain( HINSTANCE hinstDLL, #pragma managed(pop) #endif #endif - diff --git a/src/minidriver/minidriver.exports b/src/minidriver/opensc-minidriver.exports similarity index 100% rename from src/minidriver/minidriver.exports rename to src/minidriver/opensc-minidriver.exports diff --git a/src/minidriver/versioninfo-minidriver.rc b/src/minidriver/versioninfo-minidriver.rc new file mode 100644 index 0000000000..d39fd0dcd5 --- /dev/null +++ b/src/minidriver/versioninfo-minidriver.rc @@ -0,0 +1,50 @@ +#include +#include "config.h" +#define TO_STR_HELPER(x) #x +#define TO_STR(x) TO_STR_HELPER(x) + +#define IDC_STATIC -1 +/* defined twice: in resource file and in source code */ +#define IDI_SMARTCARD 102 + +#ifndef __MINGW32__ +IDI_SMARTCARD ICON "..\\..\\win32\\DDORes.dll_14_2302.ico" +#else +IDI_SMARTCARD ICON "../../win32/DDORes.dll_14_2302.ico" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION OPENSC_VERSION_MAJOR,OPENSC_VERSION_MINOR,OPENSC_VERSION_FIX,OPENSC_VERSION_REVISION + PRODUCTVERSION OPENSC_VERSION_MAJOR,OPENSC_VERSION_MINOR,OPENSC_VERSION_FIX,OPENSC_VERSION_REVISION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x21L +#else + FILEFLAGS 0x20L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", OPENSC_VS_FF_COMMENTS + VALUE "CompanyName", OPENSC_VS_FF_COMPANY_NAME + VALUE "FileVersion", TO_STR(OPENSC_VERSION_MAJOR.OPENSC_VERSION_MINOR.OPENSC_VERSION_FIX.OPENSC_VERSION_REVISION) + VALUE "InternalName", PACKAGE_NAME + VALUE "LegalCopyright", OPENSC_VS_FF_LEGAL_COPYRIGHT + VALUE "LegalTrademarks", "" + VALUE "PrivateBuild", "" + VALUE "ProductName", OPENSC_VS_FF_PRODUCT_NAME + VALUE "ProductVersion", TO_STR(OPENSC_VERSION_MAJOR.OPENSC_VERSION_MINOR.OPENSC_VERSION_FIX.OPENSC_VERSION_REVISION) + VALUE "SpecialBuild", "" + VALUE "FileDescription", "OpenSC minidriver" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/minidriver/versioninfo-minidriver.rc.in b/src/minidriver/versioninfo-minidriver.rc.in deleted file mode 100644 index 4ff841cc4d..0000000000 --- a/src/minidriver/versioninfo-minidriver.rc.in +++ /dev/null @@ -1,60 +0,0 @@ -#include - -#define IDC_STATIC -1 -/* defined twice: in versioninfo-minidriver.rc.in and in minidriver.c */ -#define IDD_PINPAD 101 -#define IDI_LOGO 102 -#define IDC_PINPAD_TEXT 1001 -#define IDC_PINPAD_ICON 1000 - -VS_VERSION_INFO VERSIONINFO - FILEVERSION @OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@ - PRODUCTVERSION @OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@ - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x21L -#else - FILEFLAGS 0x20L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "@OPENSC_VS_FF_COMMENTS@" - VALUE "CompanyName", "@OPENSC_VS_FF_COMPANY_NAME@" - VALUE "FileVersion", "@OPENSC_VERSION_MAJOR@.@OPENSC_VERSION_MINOR@.@OPENSC_VERSION_FIX@.@OPENSC_VERSION_REVISION@" - VALUE "InternalName", "@PACKAGE_NAME@" - VALUE "LegalCopyright", "@OPENSC_VS_FF_LEGAL_COPYRIGHT@" - VALUE "LegalTrademarks", "" - VALUE "PrivateBuild", "" - VALUE "ProductName", "@OPENSC_VS_FF_PRODUCT_NAME@" - VALUE "ProductVersion", "@OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@" - VALUE "SpecialBuild", "" - VALUE "FileDescription", "OpenSC minidriver" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - -IDD_PINPAD DIALOGEX 0, 0, 309, 71 -STYLE DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION -CAPTION "PIN Entry Required" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Please enter PIN on PINPAD",IDC_STATIC,46,9,256,31 - ICON "",IDC_PINPAD_ICON,6,11,20,20 - LTEXT "This window will be closed automatically after the PIN has been submitted on the PINPAD or if the PINPAD timeout occurs (in general 30 seconds).",IDC_STATIC,7,46,298,19 -END - -#ifndef __MINGW32__ -IDI_LOGO ICON "..\\..\\win32\\OpenSC.ico" -#else -IDI_LOGO ICON "../../win32/OpenSC.ico" -#endif diff --git a/src/pkcs11/Makefile.am b/src/pkcs11/Makefile.am index 23cfa97d6b..97b039e610 100644 --- a/src/pkcs11/Makefile.am +++ b/src/pkcs11/Makefile.am @@ -1,28 +1,40 @@ include $(top_srcdir)/win32/ltrc.inc -MAINTAINERCLEANFILES = $(srcdir)/Makefile.in $(srcdir)/versioninfo-pkcs11.rc $(srcdir)/versioninfo-pkcs11-spy.rc -EXTRA_DIST = Makefile.mak versioninfo-pkcs11.rc.in versioninfo-pkcs11-spy.rc.in opensc-pkcs11.pc.in +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +EXTRA_DIST = Makefile.mak versioninfo-pkcs11.rc versioninfo-pkcs11-spy.rc opensc-pkcs11.pc.in opensc.module.in -lib_LTLIBRARIES = opensc-pkcs11.la pkcs11-spy.la onepin-opensc-pkcs11.la +if ENABLE_SHARED +lib_LTLIBRARIES = opensc-pkcs11.la pkcs11-spy.la +else +noinst_LTLIBRARIES = libopensc-pkcs11.la +endif AM_CPPFLAGS = -I$(top_srcdir)/src -OPENSC_PKCS11_INC = sc-pkcs11.h pkcs11.h pkcs11-opensc.h +OPENSC_PKCS11_INC = sc-pkcs11.h pd-pkcs11.h pkcs11.h pkcs11-opensc.h OPENSC_PKCS11_SRC = pkcs11-global.c pkcs11-session.c pkcs11-object.c misc.c slot.c \ mechanism.c openssl.c framework-pkcs15.c \ framework-pkcs15init.c debug.c pkcs11.exports \ pkcs11-display.c pkcs11-display.h OPENSC_PKCS11_CFLAGS = \ - $(OPTIONAL_OPENSSL_CFLAGS) $(OPENSC_PKCS11_PTHREAD_CFLAGS) + $(OPENPACE_CFLAGS) $(OPTIONAL_OPENSSL_CFLAGS) $(OPENSC_PKCS11_PTHREAD_CFLAGS) OPENSC_PKCS11_LIBS = \ $(top_builddir)/src/libopensc/libopensc.la \ $(top_builddir)/src/common/libscdl.la \ $(top_builddir)/src/common/libcompat.la \ - $(OPTIONAL_OPENSSL_LIBS) $(PTHREAD_LIBS) + $(OPENPACE_LIBS) $(OPTIONAL_OPENSSL_LIBS) $(PTHREAD_LIBS) +if WIN32 +OPENSC_PKCS11_LIBS += -lshlwapi +endif pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = opensc-pkcs11.pc DISTCLEANFILES = $(pkgconfig_DATA) +if ENABLE_P11KIT +p11kitdir = $(P11_SYSTEM_CONFIG_MODULES) +p11kit_DATA = opensc.module +DISTCLEANFILES += $(p11kit_DATA) +endif opensc_pkcs11_la_SOURCES = $(OPENSC_PKCS11_SRC) $(OPENSC_PKCS11_INC) opensc_pkcs11_la_CFLAGS = $(OPENSC_PKCS11_CFLAGS) @@ -31,20 +43,18 @@ opensc_pkcs11_la_LDFLAGS = $(AM_LDFLAGS) \ -export-symbols "$(srcdir)/pkcs11.exports" \ -module -shared -avoid-version -no-undefined -onepin_opensc_pkcs11_la_SOURCES = $(OPENSC_PKCS11_SRC) $(OPENSC_PKCS11_INC) -onepin_opensc_pkcs11_la_CFLAGS = -DMODULE_APP_NAME=\"onepin-opensc-pkcs11\" $(OPENSC_PKCS11_CFLAGS) -onepin_opensc_pkcs11_la_LIBADD = $(OPENSC_PKCS11_LIBS) -onepin_opensc_pkcs11_la_LDFLAGS = $(AM_LDFLAGS) \ - -export-symbols "$(srcdir)/pkcs11.exports" \ - -module -shared -avoid-version -no-undefined +libopensc_pkcs11_la_SOURCES = $(OPENSC_PKCS11_SRC) $(OPENSC_PKCS11_INC) +libopensc_pkcs11_la_CFLAGS = $(OPENSC_PKCS11_CFLAGS) +libopensc_pkcs11_la_LIBADD = $(OPENSC_PKCS11_LIBS) +libopensc_pkcs11_la_LDFLAGS = $(AM_LDFLAGS) pkcs11_spy_la_SOURCES = pkcs11-spy.c pkcs11-display.c pkcs11-display.h pkcs11.exports -pkcs11_spy_la_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) +pkcs11_spy_la_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPENSC_PKCS11_PTHREAD_CFLAGS) pkcs11_spy_la_LIBADD = \ $(top_builddir)/src/common/libpkcs11.la \ $(top_builddir)/src/common/libscdl.la \ $(top_builddir)/src/common/libcompat.la \ - $(OPTIONAL_OPENSSL_LIBS) + $(OPTIONAL_OPENSSL_LIBS) $(PTHREAD_LIBS) pkcs11_spy_la_LDFLAGS = $(AM_LDFLAGS) \ -export-symbols "$(srcdir)/pkcs11.exports" \ -module -shared -avoid-version -no-undefined @@ -52,6 +62,7 @@ pkcs11_spy_la_LDFLAGS = $(AM_LDFLAGS) \ if WIN32 opensc_pkcs11_la_SOURCES += versioninfo-pkcs11.rc pkcs11_spy_la_SOURCES += versioninfo-pkcs11-spy.rc +pkcs11_spy_la_LIBADD += -lshlwapi endif if WIN32 @@ -60,13 +71,36 @@ install-exec-hook: for l in opensc-pkcs11.dll pkcs11-spy.dll; do \ mv "$(DESTDIR)$(libdir)/$$l" "$(DESTDIR)$(bindir)/$$l"; \ done + +uninstall-hook: + for l in opensc-pkcs11.dll pkcs11-spy.dll; do \ + rm -f "$(DESTDIR)$(bindir)/$$l"; \ + done else # see http://wiki.cacert.org/wiki/Pkcs11TaskForce install-exec-hook: + cd $(DESTDIR)$(libdir) && \ + rm -f "onepin-opensc-pkcs11$(DYN_LIB_EXT)" && \ + $(LN_S) "opensc-pkcs11$(DYN_LIB_EXT)" "onepin-opensc-pkcs11$(DYN_LIB_EXT)" $(MKDIR_P) "$(DESTDIR)$(pkcs11dir)" for l in opensc-pkcs11$(DYN_LIB_EXT) onepin-opensc-pkcs11$(DYN_LIB_EXT) pkcs11-spy$(DYN_LIB_EXT); do \ rm -f "$(DESTDIR)$(pkcs11dir)/$$l"; \ $(LN_S) ../$$l "$(DESTDIR)$(pkcs11dir)/$$l"; \ done +uninstall-hook: + for l in opensc-pkcs11$(DYN_LIB_EXT) onepin-opensc-pkcs11$(DYN_LIB_EXT) pkcs11-spy$(DYN_LIB_EXT); do \ + rm -f "$(DESTDIR)$(pkcs11dir)/$$l"; \ + done + rm -df "$(DESTDIR)$(pkcs11dir)" || true + rm -f "$(DESTDIR)$(libdir)/onepin-opensc-pkcs11$(DYN_LIB_EXT)" endif + +TIDY_FLAGS = $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) $(OPENSC_PKCS11_CFLAGS) +TIDY_FILES = \ + pkcs11-global.c pkcs11-session.c pkcs11-object.c slot.c \ + mechanism.c openssl.c framework-pkcs15.c \ + framework-pkcs15init.c debug.c + +check-local: + if [ -x "$(CLANGTIDY)" ]; then clang-tidy -config='' --checks='$(TIDY_CHECKS)' --warnings-as-errors='$(TIDY_CHECKS)' -header-filter=.* $(addprefix $(srcdir)/,$(TIDY_FILES)) -- $(TIDY_FLAGS); fi diff --git a/src/pkcs11/Makefile.mak b/src/pkcs11/Makefile.mak index 1aeb9e66bc..c4df9b3bb7 100644 --- a/src/pkcs11/Makefile.mak +++ b/src/pkcs11/Makefile.mak @@ -1,7 +1,6 @@ TOPDIR = ..\.. TARGET1 = opensc-pkcs11.dll -TARGET2 = onepin-opensc-pkcs11.dll TARGET3 = pkcs11-spy.dll OBJECTS = pkcs11-global.obj pkcs11-session.obj pkcs11-object.obj misc.obj slot.obj \ @@ -9,23 +8,24 @@ OBJECTS = pkcs11-global.obj pkcs11-session.obj pkcs11-object.obj misc.obj slot debug.obj pkcs11-display.obj versioninfo-pkcs11.res OBJECTS3 = pkcs11-spy.obj pkcs11-display.obj versioninfo-pkcs11-spy.res -LIBS = $(TOPDIR)\src\libopensc\opensc_a.lib $(TOPDIR)\src\pkcs15init\pkcs15init.lib +LIBS = $(TOPDIR)\src\libopensc\opensc_a.lib \ + $(TOPDIR)\src\pkcs15init\pkcs15init.lib \ + $(TOPDIR)\src\scconf\scconf.lib \ + $(TOPDIR)\src\common\common.lib \ + $(TOPDIR)\src\common\libscdl.lib \ + $(TOPDIR)\src\ui\strings.lib \ + $(TOPDIR)\src\ui\notify.lib \ + $(TOPDIR)\src\sm\libsmiso.lib \ + $(TOPDIR)\src\sm\libsmeac.lib \ + $(TOPDIR)\src\pkcs15init\pkcs15init.lib LIBS3 = $(TOPDIR)\src\common\libpkcs11.lib $(TOPDIR)\src\common\libscdl.lib $(TOPDIR)\src\common\common.lib -all: $(TARGET1) $(TARGET2) $(TARGET3) +all: $(TARGET1) $(TARGET3) !INCLUDE $(TOPDIR)\win32\Make.rules.mak -$(TARGET1): $(OBJECTS) $(LIBS) - link $(LINKFLAGS) /dll /implib:$*.lib /out:$(TARGET1) $(OBJECTS) $(LIBS) $(OPENPACE_LIB) $(OPENSSL_LIB) gdi32.lib - if EXIST $(TARGET1).manifest mt -manifest $(TARGET1).manifest -outputresource:$(TARGET1);2 +$(TARGET1): $(OBJECTS) $(LIBS) pkcs11.def + link /dll $(LINKFLAGS) /out:$@ /def:pkcs11.def /implib:$*.lib $(OBJECTS) $(LIBS) $(OPENPACE_LIB) $(OPENSSL_LIB) $(ZLIB_LIB) gdi32.lib Comctl32.lib Shell32.lib user32.lib advapi32.lib ws2_32.lib Shell32.lib Comctl32.lib shlwapi.lib -$(TARGET2): $(OBJECTS) $(LIBS) - del pkcs11-global.obj - cl $(CODE_OPTIMIZATION) $(COPTS) /DMODULE_APP_NAME=\"onepin-opensc-pkcs11\" /c pkcs11-global.c - link $(LINKFLAGS) /dll /implib:$*.lib /out:$(TARGET2) $(OBJECTS) $(LIBS) $(OPENPACE_LIB) $(OPENSSL_LIB) gdi32.lib - if EXIST $(TARGET2).manifest mt -manifest $(TARGET2).manifest -outputresource:$(TARGET2);2 - -$(TARGET3): $(OBJECTS3) $(LIBS3) - link $(LINKFLAGS) /dll /implib:$*.lib /out:$(TARGET3) $(OBJECTS3) $(LIBS3) $(OPENPACE_LIB) $(OPENSSL_LIB) gdi32.lib advapi32.lib - if EXIST $(TARGET3).manifest mt -manifest $(TARGET3).manifest -outputresource:$(TARGET3);2 +$(TARGET3): $(OBJECTS3) $(LIBS3) pkcs11.def + link /dll $(LINKFLAGS) /out:$@ /def:pkcs11.def /implib:$*.lib $(OBJECTS3) $(LIBS3) $(OPENSSL_LIB) gdi32.lib advapi32.lib shlwapi.lib diff --git a/src/pkcs11/debug.c b/src/pkcs11/debug.c index bb3b180639..a618bd28c6 100644 --- a/src/pkcs11/debug.c +++ b/src/pkcs11/debug.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -34,9 +34,6 @@ struct fmap { struct fmap * map; }; -#define NELE(x) (sizeof(x)/sizeof((x)[0])) -#define STR(x) #x -#define __(x) (x), #x #define _(x) { (x), #x, NULL, NULL } #define ul(x) { (x), #x, sc_pkcs11_print_ulong, NULL } #define ulm(x) { (x), #x, sc_pkcs11_print_ulong, map_##x } @@ -57,110 +54,123 @@ static const char * sc_pkcs11_print_bool(int level, struct fmap *, static const char * sc_pkcs11_print_string(int level, struct fmap *, void *, size_t); -static struct fmap map_CKA_CLASS[] = { - _(CKO_DATA), - _(CKO_CERTIFICATE), - _(CKO_PUBLIC_KEY), - _(CKO_PRIVATE_KEY), - _(CKO_SECRET_KEY), - _(CKO_HW_FEATURE), - _(CKO_DOMAIN_PARAMETERS), - - { 0, NULL, NULL, NULL } +static struct fmap map_CKA_CLASS[] = { + _(CKO_DATA), + _(CKO_CERTIFICATE), + _(CKO_PUBLIC_KEY), + _(CKO_PRIVATE_KEY), + _(CKO_SECRET_KEY), + _(CKO_HW_FEATURE), + _(CKO_DOMAIN_PARAMETERS), + _(CKO_PROFILE), + _(CKO_VALIDATION), + _(CKO_TRUST), + + {0, NULL, NULL, NULL} }; -static struct fmap map_CKA_CERTIFICATE_TYPE[] = { - _(CKC_X_509), - _(CKC_X_509_ATTR_CERT), +static struct fmap map_CKA_CERTIFICATE_TYPE[] = { + _(CKC_X_509), + _(CKC_X_509_ATTR_CERT), - { 0, NULL, NULL, NULL } + {0, NULL, NULL, NULL} }; -static struct fmap map_CKA_KEY_TYPE[] = { - _(CKK_RSA), - _(CKK_DSA), - _(CKK_DH), - _(CKK_ECDSA), - _(CKK_EC), - _(CKK_RC2), - _(CKK_RC4), - _(CKK_RC5), - _(CKK_DES), - _(CKK_DES3), - _(CKK_CAST), - _(CKK_CAST3), - _(CKK_CAST128), - _(CKK_IDEA), - _(CKK_AES), - - { 0, NULL, NULL, NULL } +static struct fmap map_CKA_KEY_TYPE[] = { + _(CKK_RSA), + _(CKK_DSA), + _(CKK_DH), + _(CKK_ECDSA), + _(CKK_EC), + _(CKK_EC_EDWARDS), + _(CKK_EC_MONTGOMERY), + _(CKK_GENERIC_SECRET), + _(CKK_RC2), + _(CKK_RC4), + _(CKK_RC5), + _(CKK_DES), + _(CKK_DES3), + _(CKK_CAST), + _(CKK_CAST3), + _(CKK_CAST128), + _(CKK_IDEA), + _(CKK_AES), + _(CKK_ML_DSA), + _(CKK_ML_KEM), + _(CKK_SLH_DSA), + + {0, NULL, NULL, NULL} }; -static struct fmap p11_attr_names[] = { - ulm(CKA_CLASS), - b(CKA_TOKEN), - b(CKA_PRIVATE), - s(CKA_LABEL), - _(CKA_APPLICATION), - _(CKA_VALUE), - _(CKA_OBJECT_ID), - ulm(CKA_CERTIFICATE_TYPE), - _(CKA_ISSUER), - _(CKA_SERIAL_NUMBER), - _(CKA_AC_ISSUER), - _(CKA_OWNER), - _(CKA_ATTR_TYPES), - b(CKA_TRUSTED), - ulm(CKA_KEY_TYPE), - _(CKA_SUBJECT), - _(CKA_ID), - b(CKA_SENSITIVE), - b(CKA_ENCRYPT), - b(CKA_DECRYPT), - b(CKA_WRAP), - b(CKA_UNWRAP), - b(CKA_SIGN), - b(CKA_SIGN_RECOVER), - b(CKA_VERIFY), - b(CKA_VERIFY_RECOVER), - b(CKA_DERIVE), - _(CKA_START_DATE), - _(CKA_END_DATE), - _(CKA_MODULUS), - ul(CKA_MODULUS_BITS), - _(CKA_PUBLIC_EXPONENT), - _(CKA_PRIVATE_EXPONENT), - _(CKA_PRIME_1), - _(CKA_PRIME_2), - _(CKA_EXPONENT_1), - _(CKA_EXPONENT_2), - _(CKA_COEFFICIENT), - _(CKA_PRIME), - _(CKA_SUBPRIME), - _(CKA_BASE), - _(CKA_PRIME_BITS), - _(CKA_SUB_PRIME_BITS), - _(CKA_VALUE_BITS), - _(CKA_VALUE_LEN), - b(CKA_EXTRACTABLE), - b(CKA_LOCAL), - b(CKA_NEVER_EXTRACTABLE), - b(CKA_ALWAYS_SENSITIVE), - _(CKA_KEY_GEN_MECHANISM), - b(CKA_MODIFIABLE), - _(CKA_ECDSA_PARAMS), - _(CKA_EC_PARAMS), - _(CKA_EC_POINT), - _(CKA_SECONDARY_AUTH), - ul(CKA_AUTH_PIN_FLAGS), - _(CKA_HW_FEATURE_TYPE), - _(CKA_RESET_ON_INIT), - _(CKA_HAS_RESET), - _(CKA_VENDOR_DEFINED), - b(CKA_ALWAYS_AUTHENTICATE), - _(CKA_GOSTR3410_PARAMS), - - { 0, NULL, NULL, NULL } +static struct fmap p11_attr_names[] = { + ulm(CKA_CLASS), + b(CKA_TOKEN), + b(CKA_PRIVATE), + s(CKA_LABEL), + _(CKA_APPLICATION), + _(CKA_VALUE), + _(CKA_OBJECT_ID), + ulm(CKA_CERTIFICATE_TYPE), + _(CKA_ISSUER), + _(CKA_SERIAL_NUMBER), + _(CKA_AC_ISSUER), + _(CKA_OWNER), + _(CKA_ATTR_TYPES), + b(CKA_TRUSTED), + ulm(CKA_KEY_TYPE), + _(CKA_SUBJECT), + _(CKA_ID), + b(CKA_SENSITIVE), + b(CKA_ENCRYPT), + b(CKA_DECRYPT), + b(CKA_WRAP), + b(CKA_UNWRAP), + b(CKA_SIGN), + b(CKA_SIGN_RECOVER), + b(CKA_VERIFY), + b(CKA_VERIFY_RECOVER), + b(CKA_DERIVE), + _(CKA_START_DATE), + _(CKA_END_DATE), + _(CKA_MODULUS), + ul(CKA_MODULUS_BITS), + _(CKA_PUBLIC_EXPONENT), + _(CKA_PRIVATE_EXPONENT), + _(CKA_PRIME_1), + _(CKA_PRIME_2), + _(CKA_EXPONENT_1), + _(CKA_EXPONENT_2), + _(CKA_COEFFICIENT), + _(CKA_PRIME), + _(CKA_SUBPRIME), + _(CKA_BASE), + _(CKA_PRIME_BITS), + _(CKA_SUB_PRIME_BITS), + _(CKA_VALUE_BITS), + _(CKA_VALUE_LEN), + b(CKA_EXTRACTABLE), + b(CKA_LOCAL), + b(CKA_NEVER_EXTRACTABLE), + b(CKA_ALWAYS_SENSITIVE), + _(CKA_KEY_GEN_MECHANISM), + b(CKA_MODIFIABLE), + _(CKA_EC_PARAMS), + _(CKA_ECDSA_PARAMS), + _(CKA_EC_POINT), + _(CKA_SECONDARY_AUTH), + ul(CKA_AUTH_PIN_FLAGS), + _(CKA_HW_FEATURE_TYPE), + _(CKA_RESET_ON_INIT), + _(CKA_HAS_RESET), + _(CKA_VENDOR_DEFINED), + b(CKA_ALWAYS_AUTHENTICATE), + _(CKA_GOSTR3410_PARAMS), + ul(CKA_PARAMETER_SET), + b(CKA_ENCAPSULATE), + b(CKA_DECAPSULATE), + _(CKA_SEED), + + {0, NULL, NULL, NULL} }; void sc_pkcs11_print_attrs(int level, const char *file, unsigned int line, diff --git a/src/pkcs11/framework-pkcs15.c b/src/pkcs11/framework-pkcs15.c index 776f466879..7feab16195 100644 --- a/src/pkcs11/framework-pkcs15.c +++ b/src/pkcs11/framework-pkcs15.c @@ -15,20 +15,28 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "common/constant-time.h" #include "config.h" #include #include +#include #include "libopensc/log.h" #include "libopensc/internal.h" #include "libopensc/asn1.h" #include "libopensc/cardctl.h" +#include "ui/notify.h" #include "common/compat_strnlen.h" - #ifdef ENABLE_OPENSSL -#include +#include +#include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#endif +#else +#define SHA_DIGEST_LENGTH 20 #endif #include "sc-pkcs11.h" @@ -55,13 +63,13 @@ struct pkcs15_slot_data { } \ attr->ulValueLen = size; -#define MAX_OBJECTS 64 +#define MAX_OBJECTS 128 struct pkcs15_fw_data { struct sc_pkcs15_card * p15_card; struct pkcs15_any_object * objects[MAX_OBJECTS]; unsigned int num_objects; unsigned int locked; - unsigned char user_puk[64]; + unsigned char *user_puk; unsigned int user_puk_len; }; @@ -123,6 +131,12 @@ struct pkcs15_data_object { #define data_p15obj base.p15_object #define is_data(obj) (__p15_type(obj) == SC_PKCS15_TYPE_DATA_OBJECT) +struct pkcs15_profile_object { + struct pkcs15_any_object base; + + unsigned long profile_id; +}; + struct pkcs15_skey_object { struct pkcs15_any_object base; @@ -130,14 +144,16 @@ struct pkcs15_skey_object { struct sc_pkcs15_skey *valueXXXX; }; +#define is_skey(obj) ((__p15_type(obj) & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_SKEY) + #define skey_flags base.base.flags #define skey_p15obj base.p15_object -#define is_skey(obj) ((__p15_type(obj) & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_SKEY_OBJECT) extern struct sc_pkcs11_object_ops pkcs15_cert_ops; extern struct sc_pkcs11_object_ops pkcs15_prkey_ops; extern struct sc_pkcs11_object_ops pkcs15_pubkey_ops; extern struct sc_pkcs11_object_ops pkcs15_dobj_ops; +extern struct sc_pkcs11_object_ops pkcs15_profile_ops; extern struct sc_pkcs11_object_ops pkcs15_skey_ops; const CK_BYTE gostr3410_paramset_A_encoded_oid[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01 }; @@ -147,6 +163,15 @@ const unsigned int gostr3410_paramset_B_oid[] = {1, 2, 643, 2, 2, 35, 2, (unsign const CK_BYTE gostr3410_paramset_C_encoded_oid[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x03 }; const unsigned int gostr3410_paramset_C_oid[] = {1, 2, 643, 2, 2, 35, 3, (unsigned int)-1}; +#ifdef ENABLE_OPENSSL +// clang-format off +static const struct sc_object_id oid_ED25519 = { { 1, 3, 101, 112, -1 } }; +static const struct sc_object_id oid_ED448 = { { 1, 3, 101, 113, -1 } }; +static const struct sc_object_id oid_X25519 = { { 1, 3, 101, 110, -1 } }; +static const struct sc_object_id oid_X448 = { { 1, 3, 101, 111, -1 } }; +// clang-format on +#endif /* ENABLE_OPENSSL */ + static const struct { const CK_BYTE *encoded_oid; const unsigned int encoded_oid_size; @@ -174,6 +199,7 @@ static const struct { const CK_BYTE gostr3411_94_cryptopro_paramset_encoded_oid[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01 }; const unsigned int gostr3411_94_cryptopro_paramset_oid[] = {1, 2, 643, 2, 2, 30, 1, (unsigned int)-1}; +#ifdef USE_PKCS15_INIT static const struct { const CK_BYTE *encoded_oid; const unsigned int encoded_oid_size; @@ -185,6 +211,7 @@ static const struct { &gostr3411_94_cryptopro_paramset_oid[0], sizeof(gostr3411_94_cryptopro_paramset_oid)} }; +#endif static int __pkcs15_release_object(struct pkcs15_any_object *); static CK_RV register_mechanisms(struct sc_pkcs11_card *p11card); @@ -200,7 +227,6 @@ static CK_RV get_ec_pubkey_point(struct sc_pkcs15_pubkey *, CK_ATTRIBUTE_PTR); static CK_RV get_ec_pubkey_params(struct sc_pkcs15_pubkey *, CK_ATTRIBUTE_PTR); static int lock_card(struct pkcs15_fw_data *); static int unlock_card(struct pkcs15_fw_data *); -static int reselect_app_df(sc_pkcs15_card_t *p15card); #ifdef USE_PKCS15_INIT static CK_RV set_gost3410_params(struct sc_pkcs15init_prkeyargs *, @@ -255,15 +281,17 @@ static int pkcs11_get_pin_callback(struct sc_profile *profile, int id, } #endif -/* Returns WF data corresponding to the given application or, - * if application info is not supplied, returns first available WF data. */ +/* Returns FW data corresponding to the given application or, + * if application info is not supplied, returns first available FW data. */ static struct pkcs15_fw_data * get_fw_data(struct sc_pkcs11_card *p11card, struct sc_app_info *app_info, int *out_idx) { struct pkcs15_fw_data *out = NULL; int idx; - for (idx=0; idx < SC_PKCS11_FRAMEWORK_DATA_MAX_NUM; idx++) { + if (!p11card) + return NULL; + for (idx=0; p11card && idx < SC_PKCS11_FRAMEWORK_DATA_MAX_NUM; idx++) { struct pkcs15_fw_data *fw_data = (struct pkcs15_fw_data *) p11card->fws_data[idx]; struct sc_file *file_app = NULL; @@ -301,6 +329,8 @@ pkcs15_bind(struct sc_pkcs11_card *p11card, struct sc_app_info *app_info) CK_RV ck_rv; sc_log(context, "Bind PKCS#15 '%s' application", app_info ? app_info->label : ""); + if (!p11card) + return CKR_TOKEN_NOT_RECOGNIZED; for (idx=0; idxfws_data[idx]) break; @@ -339,7 +369,9 @@ pkcs15_unbind(struct sc_pkcs11_card *p11card) unsigned int i, idx; int rv = SC_SUCCESS; - for (idx=0; idxfws_data[idx]; if (!fw_data) @@ -356,8 +388,9 @@ pkcs15_unbind(struct sc_pkcs11_card *p11card) unlock_card(fw_data); - if (fw_data->p15_card) + if (fw_data->p15_card) { rv = sc_pkcs15_unbind(fw_data->p15_card); + } fw_data->p15_card = NULL; free(fw_data); @@ -374,8 +407,6 @@ pkcs15_init_token_info(struct sc_pkcs15_card *p15card, CK_TOKEN_INFO_PTR pToken) scconf_block *conf_block = NULL; char *model = NULL; - strcpy_bp(pToken->manufacturerID, p15card->tokeninfo->manufacturer_id, 32); - conf_block = sc_get_conf_block(p15card->card->ctx, "framework", "pkcs15", 1); if (conf_block && p15card->file_app) { scconf_block **blocks = NULL; @@ -393,21 +424,30 @@ pkcs15_init_token_info(struct sc_pkcs15_card *p15card, CK_TOKEN_INFO_PTR pToken) if (model) strcpy_bp(pToken->model, model, sizeof(pToken->model)); + else if (sc_card_ctl(p15card->card, SC_CARDCTL_GET_MODEL, &model) == SC_SUCCESS) + strcpy_bp(pToken->model, model, sizeof(pToken->model)); else if (p15card->flags & SC_PKCS15_CARD_FLAG_EMULATED) strcpy_bp(pToken->model, "PKCS#15 emulated", sizeof(pToken->model)); else strcpy_bp(pToken->model, "PKCS#15", sizeof(pToken->model)); - /* Take the last 16 chars of the serial number (if the are more than 16). - * _Assuming_ that the serial number is a Big Endian counter, this - * will assure that the serial within each type of card will be - * unique in pkcs11 (at least for the first 8^16 cards :-) */ - if (p15card->tokeninfo->serial_number != NULL) { - int sn_start = strlen(p15card->tokeninfo->serial_number) - 16; + if (p15card->tokeninfo) { + strcpy_bp(pToken->manufacturerID, p15card->tokeninfo->manufacturer_id, 32); - if (sn_start < 0) - sn_start = 0; - strcpy_bp(pToken->serialNumber, p15card->tokeninfo->serial_number + sn_start, 16); + /* Take the last 16 chars of the serial number (if the are more than 16). + * _Assuming_ that the serial number is a Big Endian counter, this + * will assure that the serial within each type of card will be + * unique in pkcs11 (at least for the first 8^16 cards :-) */ + if (p15card->tokeninfo->serial_number != NULL) { + size_t sn_start = strlen(p15card->tokeninfo->serial_number); + + if (sn_start <= 16) + sn_start = 0; + else + sn_start -= 16; + + strcpy_bp(pToken->serialNumber, p15card->tokeninfo->serial_number + sn_start, 16); + } } pToken->ulMaxSessionCount = CK_EFFECTIVELY_INFINITE; @@ -429,7 +469,7 @@ static char * set_cka_label(CK_ATTRIBUTE_PTR attr, char *label) { char *l = (char *)attr->pValue; - int len = attr->ulValueLen; + unsigned long len = attr->ulValueLen; if (len >= SC_PKCS15_MAX_LABEL_SIZE) len = SC_PKCS15_MAX_LABEL_SIZE-1; @@ -462,7 +502,7 @@ __pkcs15_create_object(struct pkcs15_fw_data *fw_data, obj->size = size; *result = obj; - return 0; + return SC_SUCCESS; } static int @@ -502,18 +542,17 @@ CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) { struct sc_pkcs11_slot *slot; struct pkcs15_fw_data *fw_data = NULL; - struct sc_pkcs15_card *p15card = NULL; struct sc_pkcs15_object *auth; - struct sc_pkcs15_auth_info *pin_info; + const char *name; CK_RV rv; - sc_log(context, "C_GetTokenInfo(%lx)", slotID); if (pInfo == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; + sc_log(context, "C_GetTokenInfo(%lx)", slotID); rv = slot_get_token(slotID, &slot); if (rv != CKR_OK) { @@ -522,7 +561,11 @@ CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) } if (slot->p11card == NULL) { - rv = CKR_TOKEN_NOT_PRESENT; + if (slot->slot_info.flags & CKF_TOKEN_PRESENT) { + rv = CKR_TOKEN_NOT_RECOGNIZED; + } else { + rv = CKR_TOKEN_NOT_PRESENT; + } goto out; } @@ -531,17 +574,24 @@ CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) rv = sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_GetTokenInfo"); goto out; } - p15card = fw_data->p15_card; - /* User PIN flags are cleared before re-calculation */ - slot->token_info.flags &= ~(CKF_USER_PIN_COUNT_LOW|CKF_USER_PIN_FINAL_TRY|CKF_USER_PIN_LOCKED); + slot->token_info.flags &= ~(CKF_USER_PIN_COUNT_LOW | CKF_USER_PIN_FINAL_TRY | CKF_USER_PIN_LOCKED | CKF_USER_PIN_TO_BE_CHANGED); auth = slot_data_auth(slot->fw_data); sc_log(context, - "C_GetTokenInfo() auth. object %p, token-info flags 0x%lX", auth, - slot->token_info.flags); + "C_GetTokenInfo() auth. object %p, token-info flags 0x%lX", auth, + slot->token_info.flags); if (auth) { + struct sc_pkcs15_card *p15card = NULL; + struct sc_pkcs15_auth_info *pin_info = NULL; + pin_info = (struct sc_pkcs15_auth_info*) auth->data; + p15card = fw_data->p15_card; + if (!p15card) { + rv = sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GetTokenInfo"); + goto out; + } + sc_pkcs15_get_pin_info(p15card, auth); if (pin_info->tries_left >= 0) { @@ -552,11 +602,20 @@ CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) else if (pin_info->max_tries > 1 && pin_info->tries_left < pin_info->max_tries) slot->token_info.flags |= CKF_USER_PIN_COUNT_LOW; } + if (pin_info->logged_in & SC_PIN_STATE_NEEDS_CHANGE) { + slot->token_info.flags |= CKF_USER_PIN_TO_BE_CHANGED; + } } memcpy(pInfo, &slot->token_info, sizeof(CK_TOKEN_INFO)); + out: + name = lookup_enum(RV_T, rv); + if (name) + sc_log(context, "C_GetTokenInfo(%lx) returns %s", slotID, name); + else + sc_log(context, "C_GetTokenInfo(%lx) returns 0x%08lX", slotID, rv); sc_pkcs11_unlock(); - sc_log(context, "C_GetTokenInfo(%lx) returns 0x%lX", slotID, rv); + return rv; } @@ -604,7 +663,8 @@ pkcs15_cert_extract_label(struct pkcs15_cert_object *cert) cert->cert_data->subject, cert->cert_data->subject_len, &cn_oid, &cn_name, &cn_len); if (rv == SC_SUCCESS) { - sc_log(context, "pkcs15_cert_extract_label(): Name from DN is %s", cn_name); + sc_log(context, "pkcs15_cert_extract_label(): Name from DN is %.*s", + (unsigned int) cn_len, cn_name); cn_len = MIN(cn_len, SC_PKCS15_MAX_LABEL_SIZE-1); memcpy(cert->cert_p15obj->label, cn_name, cn_len); cert->cert_p15obj->label[cn_len] = '\0'; @@ -619,8 +679,10 @@ __pkcs15_create_cert_object(struct pkcs15_fw_data *fw_data, struct sc_pkcs15_obj { struct sc_pkcs15_cert_info *p15_info = NULL; struct sc_pkcs15_cert *p15_cert = NULL; + struct pkcs15_any_object *any_object = NULL; struct pkcs15_cert_object *object = NULL; struct pkcs15_pubkey_object *obj2 = NULL; + struct pkcs15_any_object *any_object2 = NULL; int rv; p15_info = (struct sc_pkcs15_cert_info *) cert->data; @@ -629,14 +691,15 @@ __pkcs15_create_cert_object(struct pkcs15_fw_data *fw_data, struct sc_pkcs15_obj p15_cert = NULL; /* will read cert when needed */ } else { - rv = sc_pkcs15_read_certificate(fw_data->p15_card, p15_info, &p15_cert); + rv = sc_pkcs15_read_certificate(fw_data->p15_card, p15_info, 0, &p15_cert); if (rv < 0) return rv; } /* Certificate object */ - rv = __pkcs15_create_object(fw_data, (struct pkcs15_any_object **) &object, + rv = __pkcs15_create_object(fw_data, &any_object, cert, &pkcs15_cert_ops, sizeof(struct pkcs15_cert_object)); + object = (struct pkcs15_cert_object *) any_object; if (rv < 0) { if (p15_cert != NULL) sc_pkcs15_free_certificate(p15_cert); @@ -647,10 +710,11 @@ __pkcs15_create_cert_object(struct pkcs15_fw_data *fw_data, struct sc_pkcs15_obj object->cert_data = p15_cert; /* Corresponding public key */ - rv = public_key_created(fw_data, &p15_info->id, (struct pkcs15_any_object **) &obj2); + rv = public_key_created(fw_data, &p15_info->id, &any_object2); if (rv != SC_SUCCESS) - rv = __pkcs15_create_object(fw_data, (struct pkcs15_any_object **) &obj2, + rv = __pkcs15_create_object(fw_data, &any_object2, NULL, &pkcs15_pubkey_ops, sizeof(struct pkcs15_pubkey_object)); + obj2 = (struct pkcs15_pubkey_object *) any_object2; if (rv < 0) return rv; @@ -669,7 +733,7 @@ __pkcs15_create_cert_object(struct pkcs15_fw_data *fw_data, struct sc_pkcs15_obj pkcs15_cert_extract_label(object); if (cert_object != NULL) - *cert_object = (struct pkcs15_any_object *) object; + *cert_object = any_object; return 0; } @@ -679,11 +743,11 @@ static int __pkcs15_create_pubkey_object(struct pkcs15_fw_data *fw_data, struct sc_pkcs15_object *pubkey, struct pkcs15_any_object **pubkey_object) { + struct pkcs15_any_object *any_object = NULL; struct pkcs15_pubkey_object *object = NULL; struct sc_pkcs15_pubkey *p15_key = NULL; int rv; - sc_log(context, "__pkcs15_create_pubkey_object() called, pubkey %p, data %p", pubkey, pubkey->data); /* Read public key from card */ /* Attempt to read pubkey from card or file. * During initialization process, the key may have been created @@ -697,7 +761,7 @@ __pkcs15_create_pubkey_object(struct pkcs15_fw_data *fw_data, /* if emulation already created pubkey use it */ if (pubkey->emulated && (fw_data->p15_card->flags & SC_PKCS15_CARD_FLAG_EMULATED)) { sc_log(context, "Use emulated pubkey"); - p15_key = (struct sc_pkcs15_pubkey *) pubkey->emulated; + sc_pkcs15_dup_pubkey(context, (struct sc_pkcs15_pubkey *) pubkey->emulated, &p15_key); } else { sc_log(context, "Get pubkey from PKCS#15 object"); @@ -708,24 +772,24 @@ __pkcs15_create_pubkey_object(struct pkcs15_fw_data *fw_data, } /* Public key object */ - rv = __pkcs15_create_object(fw_data, (struct pkcs15_any_object **) &object, + rv = __pkcs15_create_object(fw_data, &any_object, pubkey, &pkcs15_pubkey_ops, sizeof(struct pkcs15_pubkey_object)); + object = (struct pkcs15_pubkey_object *) any_object; if (rv >= 0) { object->pub_info = (struct sc_pkcs15_pubkey_info *) pubkey->data; object->pub_data = p15_key; if (p15_key && object->pub_info->modulus_length == 0 && p15_key->algorithm == SC_ALGORITHM_RSA) object->pub_info->modulus_length = 8 * p15_key->u.rsa.modulus.len; - } else if (!(pubkey->emulated && (fw_data->p15_card->flags & SC_PKCS15_CARD_FLAG_EMULATED))) { + } else { sc_pkcs15_free_pubkey(p15_key); } - if (object->pub_data) { + if (object && object->pub_data) { if ((object->pub_data->alg_id)&&(object->pub_data->algorithm == SC_ALGORITHM_GOSTR3410)) object->pub_data->alg_id->params = &((object->pub_data->u).gostr3410.params); } if (pubkey_object != NULL) - *pubkey_object = (struct pkcs15_any_object *) object; + *pubkey_object = any_object; - sc_log(context, "__pkcs15_create_pubkey_object() returns pubkey object %p", object); return rv; } @@ -734,18 +798,20 @@ static int __pkcs15_create_prkey_object(struct pkcs15_fw_data *fw_data, struct sc_pkcs15_object *prkey, struct pkcs15_any_object **prkey_object) { - struct pkcs15_prkey_object *object; + struct pkcs15_any_object *any_object = NULL; + struct pkcs15_prkey_object *object = NULL; int rv; - rv = __pkcs15_create_object(fw_data, (struct pkcs15_any_object **) &object, + rv = __pkcs15_create_object(fw_data, &any_object, prkey, &pkcs15_prkey_ops, sizeof(struct pkcs15_prkey_object)); + object = (struct pkcs15_prkey_object *) any_object; if (rv >= 0) object->prv_info = (struct sc_pkcs15_prkey_info *) prkey->data; if (prkey_object != NULL) - *prkey_object = (struct pkcs15_any_object *) object; + *prkey_object = any_object; - return 0; + return rv; } @@ -753,42 +819,75 @@ static int __pkcs15_create_data_object(struct pkcs15_fw_data *fw_data, struct sc_pkcs15_object *object, struct pkcs15_any_object **data_object) { + struct pkcs15_any_object *any_object = NULL; struct pkcs15_data_object *dobj = NULL; int rv; - rv = __pkcs15_create_object(fw_data, (struct pkcs15_any_object **) &dobj, + rv = __pkcs15_create_object(fw_data, &any_object, object, &pkcs15_dobj_ops, sizeof(struct pkcs15_data_object)); + dobj = (struct pkcs15_data_object *) any_object; if (rv >= 0) { - dobj->info = (struct sc_pkcs15_data_info *) object->data; - dobj->value = NULL; + dobj->info = (struct sc_pkcs15_data_info *) object->data; + dobj->value = NULL; } if (data_object != NULL) - *data_object = (struct pkcs15_any_object *) dobj; + *data_object = any_object; - return 0; + return rv; } +/* Note, that this is not an actual PKCS #15 object, but just a bogus + * structure to create PKCS #11 object. There is no corresponding + * PKCS #15 object. */ +static int +__pkcs15_create_profile_object(struct pkcs15_fw_data *fw_data, + int public_certificates, struct pkcs15_any_object **profile_object) +{ + struct pkcs15_profile_object *pobj = NULL; + struct pkcs15_any_object *any_pobj = NULL; + struct sc_pkcs15_object *obj = NULL; + int rv; + + obj = calloc(1, sizeof(struct sc_pkcs15_object)); + + rv = __pkcs15_create_object(fw_data, &any_pobj, + obj, &pkcs15_profile_ops, sizeof(struct pkcs15_profile_object)); + + if (rv != SC_SUCCESS) { + free(obj); + return rv; + } + pobj = (struct pkcs15_profile_object *) any_pobj; + pobj->profile_id = public_certificates ? CKP_PUBLIC_CERTIFICATES_TOKEN : CKP_AUTHENTICATION_TOKEN; + + if (profile_object != NULL) + *profile_object = (struct pkcs15_any_object *) pobj; + + return rv; +} + + -#ifdef USE_PKCS15_INIT static int __pkcs15_create_secret_key_object(struct pkcs15_fw_data *fw_data, struct sc_pkcs15_object *object, struct pkcs15_any_object **skey_object) { + struct pkcs15_any_object *any_object = NULL; struct pkcs15_skey_object *skey = NULL; int rv; - rv = __pkcs15_create_object(fw_data, (struct pkcs15_any_object **) &skey, + rv = __pkcs15_create_object(fw_data, &any_object, object, &pkcs15_skey_ops, sizeof(struct pkcs15_skey_object)); + skey = (struct pkcs15_skey_object *)any_object; if (rv >= 0) - skey->info = (struct sc_pkcs15_skey_info *) object->data; + skey->info = (struct sc_pkcs15_skey_info *)object->data; if (skey_object != NULL) - *skey_object = (struct pkcs15_any_object *) skey; + *skey_object = any_object; - return 0; + return rv; } -#endif static int @@ -880,7 +979,7 @@ __pkcs15_cert_bind_related(struct pkcs15_fw_data *fw_data, struct pkcs15_cert_ob if (c1->issuer_len == c2->subject_len && !memcmp(c1->issuer, c2->subject, c1->issuer_len)) { sc_log(context, "Associating object %d (id %s) as issuer", - i, sc_pkcs15_print_id(&cert2->cert_info->id)); + i, sc_pkcs15_print_id(&cert2->cert_info->id)); cert->cert_issuer = (struct pkcs15_cert_object *) obj; return; } @@ -927,13 +1026,15 @@ check_cert_data_read(struct pkcs15_fw_data *fw_data, struct pkcs15_cert_object * { struct pkcs15_pubkey_object *obj2; int rv; + int private_obj; if (!cert) return SC_ERROR_OBJECT_NOT_FOUND; if (cert->cert_data) return 0; - rv = sc_pkcs15_read_certificate(fw_data->p15_card, cert->cert_info, &cert->cert_data); + private_obj = cert->cert_flags & SC_PKCS15_CO_FLAG_PRIVATE; + rv = sc_pkcs15_read_certificate(fw_data->p15_card, cert->cert_info, private_obj, &cert->cert_data); if (rv < 0) return rv; @@ -959,7 +1060,7 @@ pkcs15_add_object(struct sc_pkcs11_slot *slot, struct pkcs15_any_object *obj, unsigned int i; struct pkcs15_fw_data *card_fw_data; CK_OBJECT_HANDLE handle = - (CK_OBJECT_HANDLE)obj; /* cast pointer to long, will truncate on Win64 */ + (CK_OBJECT_HANDLE)(uintptr_t)obj; /* cast pointer to long, will truncate on Win64 */ if (obj == NULL || slot == NULL) return; @@ -974,7 +1075,7 @@ pkcs15_add_object(struct sc_pkcs11_slot *slot, struct pkcs15_any_object *obj, list_append(&slot->objects, obj); sc_log(context, "Slot:%lX Setting object handle of 0x%lx to 0x%lx", - slot->id, obj->base.handle, handle); + slot->id, obj->base.handle, handle); obj->base.handle = handle; obj->base.flags |= SC_PKCS11_OBJECT_SEEN; obj->refcount++; @@ -989,21 +1090,27 @@ pkcs15_add_object(struct sc_pkcs11_slot *slot, struct pkcs15_any_object *obj, case SC_PKCS15_TYPE_PRKEY_RSA: case SC_PKCS15_TYPE_PRKEY_GOSTR3410: case SC_PKCS15_TYPE_PRKEY_EC: - pkcs15_add_object(slot, (struct pkcs15_any_object *) obj->related_pubkey, NULL); - card_fw_data = (struct pkcs15_fw_data *) slot->p11card->fws_data[slot->fw_data_idx]; - for (i = 0; i < card_fw_data->num_objects; i++) { - struct pkcs15_any_object *obj2 = card_fw_data->objects[i]; - struct pkcs15_cert_object *cert; + case SC_PKCS15_TYPE_PRKEY_EDDSA: + case SC_PKCS15_TYPE_PRKEY_XEDDSA: + if (slot->p11card != NULL) { + pkcs15_add_object(slot, (struct pkcs15_any_object *) obj->related_pubkey, NULL); + if (!slot->p11card) + return; + card_fw_data = (struct pkcs15_fw_data *) slot->p11card->fws_data[slot->fw_data_idx]; + for (i = 0; i < card_fw_data->num_objects; i++) { + struct pkcs15_any_object *obj2 = card_fw_data->objects[i]; + struct pkcs15_cert_object *cert; - if (!is_cert(obj2)) - continue; + if (!is_cert(obj2)) + continue; - cert = (struct pkcs15_cert_object*) obj2; + cert = (struct pkcs15_cert_object*) obj2; - if ((struct pkcs15_any_object*)(cert->cert_prvkey) != obj) - continue; + if ((struct pkcs15_any_object*)(cert->cert_prvkey) != obj) + continue; - pkcs15_add_object(slot, obj2, NULL); + pkcs15_add_object(slot, obj2, NULL); + } } break; case SC_PKCS15_TYPE_CERT_X509: @@ -1015,6 +1122,17 @@ pkcs15_add_object(struct sc_pkcs11_slot *slot, struct pkcs15_any_object *obj, obj->base.flags &= ~SC_PKCS11_OBJECT_RECURS; } +static unsigned int +get_num_slots(struct sc_card *card) +{ + unsigned int i; + for (i = 0; i < list_size(&virtual_slots); i++) { + sc_pkcs11_slot_t *slot = (sc_pkcs11_slot_t *)list_get_at(&virtual_slots, i); + if (slot && slot->p11card && slot->p11card->card == card) + return slot->p11card->num_slots; + } + return 0; +} static void pkcs15_init_slot(struct sc_pkcs15_card *p15card, struct sc_pkcs11_slot *slot, @@ -1022,8 +1140,10 @@ pkcs15_init_slot(struct sc_pkcs15_card *p15card, struct sc_pkcs11_slot *slot, { struct pkcs15_slot_data *fw_data; struct sc_pkcs15_auth_info *pin_info = NULL; - char label[64]; + int write_protected; + scconf_block *atrblock; + sc_log(context, "Called"); pkcs15_init_token_info(p15card, &slot->token_info); slot->token_info.flags |= CKF_TOKEN_INITIALIZED; if (auth != NULL) @@ -1035,6 +1155,19 @@ pkcs15_init_slot(struct sc_pkcs15_card *p15card, struct sc_pkcs11_slot *slot, if (p15card->card->caps & SC_CARD_CAP_RNG && p15card->card->ops->get_challenge != NULL) slot->token_info.flags |= CKF_RNG; + if (p15card->tokeninfo && p15card->tokeninfo->flags & SC_PKCS15_TOKEN_READONLY) { + write_protected = 1; + } else { + write_protected = 0; + } + atrblock = _sc_match_atr_block(p15card->card->ctx, NULL, &p15card->card->atr); + if (atrblock) { + write_protected = scconf_get_bool(atrblock, "read_only", write_protected); + } + if (write_protected) { + slot->token_info.flags |= CKF_WRITE_PROTECTED; + } + slot->fw_data = fw_data = calloc(1, sizeof(*fw_data)); if (!fw_data) { return; @@ -1048,17 +1181,50 @@ pkcs15_init_slot(struct sc_pkcs15_card *p15card, struct sc_pkcs11_slot *slot, pin_info = NULL; } else { - if (auth->label[0]) - snprintf(label, sizeof(label), "%.*s (%s)", (int) sizeof auth->label, auth->label, p15card->tokeninfo->label); - else - snprintf(label, sizeof(label), "%s", p15card->tokeninfo->label); + size_t pin_len = 0; + if (auth->label[0] && strncmp(auth->label, "PIN", 4) != 0) + pin_len = strlen(auth->label); + if (pin_len && get_num_slots(p15card->card) > 1) { + size_t tokeninfo_len = 0; + if (p15card->tokeninfo && p15card->tokeninfo->label) + tokeninfo_len = strlen(p15card->tokeninfo->label); + /* Print the possibly truncated token label with at least 4 + * characters followed by the PIN label in parenthesis */ + if (tokeninfo_len == 0 + || pin_len + strlen("L... ()") > 32) { + /* There is no token label or it doesn't fit, + * print only PIN label */ + strcpy_bp(slot->token_info.label, auth->label, 32); + } else { + size_t max_tokeninfo_len = MIN(tokeninfo_len, + 32 - pin_len - strlen(" ()")); + strcpy_bp(slot->token_info.label, + p15card->tokeninfo->label, + max_tokeninfo_len); + slot->token_info.label[max_tokeninfo_len] = ' '; + slot->token_info.label[max_tokeninfo_len+1] = '('; + strcpy_bp(slot->token_info.label+max_tokeninfo_len+2, + auth->label, pin_len); + strcpy_bp(slot->token_info.label+max_tokeninfo_len+2+pin_len, + ")", 32 - max_tokeninfo_len-2-pin_len); + } + } else { + /* PIN label is empty or just says useless "PIN", + * print only token label */ + strcpy_bp(slot->token_info.label, + p15card->tokeninfo ? p15card->tokeninfo->label : "", + 32); + } + /* Some applications (NSS) do not like the colons in the + * TOKEN_INFO label so replace them here */ + for (int i = 0; i < 32; i++) { + if (slot->token_info.label[i] == ':') { + slot->token_info.label[i] = '.'; + } + } slot->token_info.flags |= CKF_LOGIN_REQUIRED; } } - else { - snprintf(label, sizeof(label), "%s", p15card->tokeninfo->label); - } - strcpy_bp(slot->token_info.label, label, 32); if (pin_info) { slot->token_info.ulMaxPinLen = pin_info->attrs.pin.max_length; @@ -1068,10 +1234,14 @@ pkcs15_init_slot(struct sc_pkcs15_card *p15card, struct sc_pkcs11_slot *slot, /* choose reasonable defaults */ slot->token_info.ulMaxPinLen = 8; slot->token_info.ulMinPinLen = 4; + strcpy_bp(slot->token_info.label, + p15card->tokeninfo ? p15card->tokeninfo->label : "", + 32); } slot->app_info = app_info; - sc_log(context, "Initialized token '%s' in slot 0x%lx", label, slot->id); + sc_log(context, "Initialized slot 0x%lx with token %*s", slot->id, + (int)sizeof(slot->token_info.label), slot->token_info.label); } @@ -1081,9 +1251,8 @@ pkcs15_create_slot(struct sc_pkcs11_card *p11card, struct pkcs15_fw_data *fw_dat struct sc_pkcs11_slot **out) { struct sc_pkcs11_slot *slot = NULL; - int rv; + CK_RV rv; - sc_log(context, "Create slot (p11card %p, fw_data %p, auth %p, app_info %p)", p11card, fw_data, auth, app_info); rv = slot_allocate(&slot, p11card); if (rv != CKR_OK) return rv; @@ -1094,6 +1263,11 @@ pkcs15_create_slot(struct sc_pkcs11_card *p11card, struct pkcs15_fw_data *fw_dat /* Fill in the slot/token info from pkcs15 data */ if (fw_data) pkcs15_init_slot(fw_data->p15_card, slot, auth, app_info); + else { + /* Token is not initialized, announce pinpad capability nevertheless */ + if (slot->reader->capabilities & SC_READER_CAP_PIN_PAD) + slot->token_info.flags |= CKF_PROTECTED_AUTHENTICATION_PATH; + } *out = slot; return CKR_OK; @@ -1125,6 +1299,26 @@ _pkcs15_create_typed_objects(struct pkcs15_fw_data *fw_data) if (rv < 0) return rv; + rv = pkcs15_create_pkcs11_objects(fw_data, SC_PKCS15_TYPE_PRKEY_EDDSA, "EdDSA private key", + __pkcs15_create_prkey_object); + if (rv < 0) + return rv; + + rv = pkcs15_create_pkcs11_objects(fw_data, SC_PKCS15_TYPE_PUBKEY_EDDSA, "EdDSA public key", + __pkcs15_create_pubkey_object); + if (rv < 0) + return rv; + + rv = pkcs15_create_pkcs11_objects(fw_data, SC_PKCS15_TYPE_PRKEY_XEDDSA, "XEdDSA private key", + __pkcs15_create_prkey_object); + if (rv < 0) + return rv; + + rv = pkcs15_create_pkcs11_objects(fw_data, SC_PKCS15_TYPE_PUBKEY_XEDDSA, "XEdDSA public key", + __pkcs15_create_pubkey_object); + if (rv < 0) + return rv; + rv = pkcs15_create_pkcs11_objects(fw_data, SC_PKCS15_TYPE_PRKEY_GOSTR3410, "GOSTR3410 private key", __pkcs15_create_prkey_object); if (rv < 0) @@ -1145,6 +1339,11 @@ _pkcs15_create_typed_objects(struct pkcs15_fw_data *fw_data) if (rv < 0) return rv; + rv = pkcs15_create_pkcs11_objects(fw_data, SC_PKCS15_TYPE_SKEY_GENERIC, "Generic secret key", + __pkcs15_create_secret_key_object); + if (rv < 0) + return rv; + /* Match up related keys and certificates */ pkcs15_bind_related_objects(fw_data); sc_log(context, "found %i FW objects", fw_data->num_objects); @@ -1181,10 +1380,16 @@ int slot_get_logged_in_state(struct sc_pkcs11_slot *slot) struct sc_pkcs15_object *pin_obj = NULL; struct sc_pkcs15_auth_info *pin_info; + if (slot->p11card == NULL) { + goto out; + } + fw_data = (struct pkcs15_fw_data *) slot->p11card->fws_data[slot->fw_data_idx]; if (!fw_data) goto out; p15card = fw_data->p15_card; + if (!p15card) + goto out; if (slot->login_user == CKU_SO) { sc_pkcs15_find_so_pin(p15card, &pin_obj); @@ -1199,19 +1404,52 @@ int slot_get_logged_in_state(struct sc_pkcs11_slot *slot) if (!pin_info) goto out; sc_pkcs15_get_pin_info(p15card, pin_obj); - logged_in = pin_info->logged_in; + logged_in = pin_info->logged_in & ~SC_PIN_STATE_NEEDS_CHANGE; out: return logged_in; } +int slot_get_card_state(struct sc_pkcs11_slot *slot) +{ + struct pkcs15_fw_data *fw_data = NULL; + struct sc_pkcs15_card *p15card = NULL; + int rv = 0; + + if (slot->p11card == NULL) { + return 0; + } + + fw_data = (struct pkcs15_fw_data *) slot->p11card->fws_data[slot->fw_data_idx]; + if (!fw_data) + return 0; + p15card = fw_data->p15_card; + if (!p15card) + return 0; + + if ((rv = sc_detect_card_presence(p15card->card->reader)) <= 0) + return 0; + return rv; +} + struct sc_pkcs15_object * -_get_auth_object_by_name(struct sc_pkcs15_card *p15card, char *name) +_get_auth_object_by_name(struct sc_pkcs15_card *p15card, char *name, char *label) { struct sc_pkcs15_object *out = NULL; int rv = SC_ERROR_OBJECT_NOT_FOUND; - if (!strcmp(name, "UserPIN")) { + /* please keep me in sync with md_get_pin_by_role() in minidriver */ + + /* If 'label' is set, then search for PIN with that label */ + if (label) { + struct sc_pkcs15_id id; + strncpy((char*)id.value, label, sizeof(id.value) - 1); + id.len = strlen(label); + if (id.len > sizeof(id.value)) + id.len = sizeof(id.value); + rv = sc_pkcs15_find_pin_by_auth_id(p15card, &id, &out); + } + else if (!strcmp(name, "UserPIN")) { /* Try to get 'global' PIN; if no, get the 'local' one */ rv = sc_pkcs15_find_pin_by_flags(p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_GLOBAL, SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, &out); @@ -1261,12 +1499,38 @@ _get_auth_object_by_name(struct sc_pkcs15_card *p15card, char *name) return rv ? NULL : out; } +/* If all certificates and public keys are visible, we can claim conformance + * to Public Certificate Token profile, making life easier for many applications + * saying, they do not need to login to see all keys available */ +static void _add_profile_object(struct sc_pkcs11_slot *slot, struct pkcs15_fw_data *fw_data, int public_certificates) +{ + /* Public Certificates Token in PKCS #11 3.0 */ + struct pkcs15_any_object *any_pobj = NULL; + int rv; + + if (slot->profile) { + struct pkcs15_profile_object *pobj = (struct pkcs15_profile_object *)slot->profile; + /* already exists -- downgrade if we found some non-public certificates */ + if (pobj->profile_id == CKP_PUBLIC_CERTIFICATES_TOKEN && !public_certificates) { + pobj->profile_id = CKP_AUTHENTICATION_TOKEN; + } + return; + } + + rv = __pkcs15_create_profile_object(fw_data, public_certificates, &any_pobj); + if (rv != CKR_OK || any_pobj == NULL) { + return; + } + pkcs15_add_object(slot, any_pobj, NULL); + slot->profile = any_pobj; +} static void _add_pin_related_objects(struct sc_pkcs11_slot *slot, struct sc_pkcs15_object *pin_obj, struct pkcs15_fw_data *fw_data, struct pkcs15_fw_data *move_to_fw) { struct sc_pkcs15_auth_info *pin_info = (struct sc_pkcs15_auth_info *)pin_obj->data; + int public_certificates = 1; unsigned i; sc_log(context, "Add objects related to PIN('%.*s',ID:%s)", (int) sizeof pin_obj->label, pin_obj->label, sc_pkcs15_print_id(&pin_info->auth_id)); @@ -1298,9 +1562,14 @@ _add_pin_related_objects(struct sc_pkcs11_slot *slot, struct sc_pkcs15_object *p else if (is_cert(obj)) { sc_log(context, "Slot:%p Adding cert object %d to PIN '%.*s'", slot, i, (int) sizeof pin_obj->label, pin_obj->label); pkcs15_add_object(slot, obj, NULL); + public_certificates = 0; + } + else if (is_skey(obj)) { + sc_log(context, "Slot:%p Adding secret key object %d to PIN '%.*s'", slot, i, (int) sizeof pin_obj->label, pin_obj->label); + pkcs15_add_object(slot, obj, NULL); } else { - sc_log(context, "Slot:%p Object %d skeeped", slot, i); + sc_log(context, "Slot:%p Object %d skipped", slot, i); continue; } @@ -1314,12 +1583,14 @@ _add_pin_related_objects(struct sc_pkcs11_slot *slot, struct sc_pkcs15_object *p fw_data->num_objects--; } } + _add_profile_object(slot, fw_data, public_certificates); } static void _add_public_objects(struct sc_pkcs11_slot *slot, struct pkcs15_fw_data *fw_data) { + int public_certificates = 1; unsigned i; if (slot == NULL || fw_data == NULL) @@ -1336,22 +1607,31 @@ _add_public_objects(struct sc_pkcs11_slot *slot, struct pkcs15_fw_data *fw_data) if (obj->base.flags & SC_PKCS11_OBJECT_SEEN) continue; /* Ignore 'private' object */ - if (obj->p15_object->flags & SC_PKCS15_CO_FLAG_PRIVATE) + if (obj->p15_object->flags & SC_PKCS15_CO_FLAG_PRIVATE) { + /* If we found some non-accessible public object, + * we can no longer claim Public Certificate Token conformance */ + if ((obj->p15_object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PUBKEY || + (obj->p15_object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_CERT) { + public_certificates = 0; + } continue; + } /* PKCS#15 4.1.3 is a little vague, but implies if not PRIVATE it is readable - * even if there is an auth_id to allow writting for example. + * even if there is an auth_id to allow writing for example. * See bug issue #291 - * treat pubkey and cert as readable.a + * treat pubkey and cert as readable. */ if (obj->p15_object->auth_id.len && !(is_pubkey(obj) || is_cert(obj))) continue; - sc_log(context, "Add public object(%p,%.*s,%x)", obj, (int) sizeof obj->p15_object->label, obj->p15_object->label, obj->p15_object->type); + sc_log(context, "Add public object(%p,%.*s,%x)", obj, + (int) sizeof obj->p15_object->label, obj->p15_object->label, + obj->p15_object->type); pkcs15_add_object(slot, obj, NULL); } + _add_profile_object(slot, fw_data, public_certificates); } - static CK_RV pkcs15_create_tokens(struct sc_pkcs11_card *p11card, struct sc_app_info *app_info) { @@ -1359,30 +1639,58 @@ pkcs15_create_tokens(struct sc_pkcs11_card *p11card, struct sc_app_info *app_inf struct sc_pkcs15_object *auth_user_pin = NULL, *auth_sign_pin = NULL; struct sc_pkcs11_slot *slot = NULL, *sign_slot = NULL; unsigned int cs_flags = sc_pkcs11_conf.create_slots_flags; - int i, rv, idx; + CK_RV rv; + int rc, i, idx; + scconf_block *conf_block = NULL; + char *user_pin = NULL, *sign_pin = NULL; - sc_log(context, "create PKCS#15 tokens; fws:%p,%p,%p", p11card->fws_data[0], p11card->fws_data[1], p11card->fws_data[2]); + if (p11card) { + sc_log(context, "create PKCS#15 tokens; fws:%p,%p,%p", p11card->fws_data[0], p11card->fws_data[1], p11card->fws_data[2]); + } sc_log(context, "create slots flags 0x%X", cs_flags); + if (p11card) { + p11card->num_slots = 0; + } + /* Find out framework data corresponding to the given application */ fw_data = get_fw_data(p11card, app_info, &idx); if (!fw_data) { - sc_log(context, "Create slot for the non-binded card"); - pkcs15_create_slot(p11card, NULL, NULL, app_info, &slot); + if (p11card) { + sc_log(context, "Create slot for the non-binded card"); + pkcs15_create_slot(p11card, NULL, NULL, app_info, &slot); + } return CKR_OK; } sc_log(context, "Use FW data with index %i; fw_data->p15_card %p", idx, fw_data->p15_card); + conf_block = sc_get_conf_block(p11card->card->ctx, "framework", "pkcs15", 1); + if (conf_block && app_info) { + scconf_block **blocks = NULL; + char str_path[SC_MAX_AID_STRING_SIZE]; + + memset(str_path, 0, sizeof(str_path)); + sc_bin_to_hex(app_info->path.value, app_info->path.len, str_path, sizeof(str_path), 0); + blocks = scconf_find_blocks(p11card->card->ctx->conf, conf_block, "application", str_path); + if (blocks) { + if (blocks[0]) { + user_pin = (char *)scconf_get_str(blocks[0], "user_pin", NULL); + sign_pin = (char *)scconf_get_str(blocks[0], "sign_pin", NULL); + } + free(blocks); + } + } + /* Try to identify UserPIN and SignPIN by their symbolic name */ - auth_user_pin = _get_auth_object_by_name(fw_data->p15_card, "UserPIN"); + auth_user_pin = _get_auth_object_by_name(fw_data->p15_card, "UserPIN", user_pin); if (cs_flags & SC_PKCS11_SLOT_FOR_PIN_SIGN) - auth_sign_pin = _get_auth_object_by_name(fw_data->p15_card, "SignPIN"); + auth_sign_pin = _get_auth_object_by_name(fw_data->p15_card, "SignPIN", sign_pin); sc_log(context, "Flags:0x%X; Auth User/Sign PINs %p/%p", cs_flags, auth_user_pin, auth_sign_pin); /* Add PKCS#15 objects of the known types to the framework data */ - rv = _pkcs15_create_typed_objects(fw_data); - if (rv < 0) - return sc_to_cryptoki_error(rv, NULL); + rc = _pkcs15_create_typed_objects(fw_data); + if (rc < 0) + return sc_to_cryptoki_error(rc, NULL); sc_log(context, "Found %d FW objects objects", fw_data->num_objects); /* Create slots for all non-unblock, non-so PINs if: @@ -1395,12 +1703,19 @@ pkcs15_create_tokens(struct sc_pkcs11_card *p11card, struct sc_app_info *app_inf memset(auths, 0, sizeof(auths)); /* Get authentication PKCS#15 objects present in the associated on-card application */ - rv = sc_pkcs15_get_objects(fw_data->p15_card, SC_PKCS15_TYPE_AUTH_PIN, auths, SC_PKCS15_MAX_PINS); - if (rv < 0) - return sc_to_cryptoki_error(rv, NULL); - auth_count = rv; + rc = sc_pkcs15_get_objects(fw_data->p15_card, SC_PKCS15_TYPE_AUTH_PIN, auths, SC_PKCS15_MAX_PINS); + if (rc < 0) + return sc_to_cryptoki_error(rc, NULL); + auth_count = rc; sc_log(context, "Found %d authentication objects", auth_count); + for (i = 0; i < auth_count; i++) { + struct sc_pkcs15_auth_info *pin_info = (struct sc_pkcs15_auth_info *)auths[i]->data; + if (!_is_slot_auth_object(pin_info)) + continue; + p11card->num_slots++; + } + for (i = 0; i < auth_count; i++) { struct sc_pkcs15_auth_info *pin_info = (struct sc_pkcs15_auth_info*)auths[i]->data; struct sc_pkcs11_slot *islot = NULL; @@ -1424,6 +1739,10 @@ pkcs15_create_tokens(struct sc_pkcs11_card *p11card, struct sc_app_info *app_inf } } else { + if (auth_user_pin && (cs_flags & SC_PKCS11_SLOT_FOR_PIN_USER)) + p11card->num_slots++; + if (auth_sign_pin && (cs_flags & SC_PKCS11_SLOT_FOR_PIN_SIGN)) + p11card->num_slots++; sc_log(context, "User/Sign PINs %p/%p", auth_user_pin, auth_sign_pin); if (auth_user_pin && (cs_flags & SC_PKCS11_SLOT_FOR_PIN_USER)) { /* For the UserPIN of the first slot create slot */ @@ -1445,7 +1764,6 @@ pkcs15_create_tokens(struct sc_pkcs11_card *p11card, struct sc_app_info *app_inf _add_pin_related_objects(sign_slot, auth_sign_pin, fw_data, NULL); } - sc_log(context, "slot %p, sign-slot %p\n", slot, sign_slot); if (!slot && sign_slot) slot = sign_slot; } @@ -1479,17 +1797,23 @@ static CK_RV pkcs15_login(struct sc_pkcs11_slot *slot, CK_USER_TYPE userType, CK_CHAR_PTR pPin, CK_ULONG ulPinLen) { - struct sc_pkcs11_card *p11card = slot->p11card; + struct sc_pkcs11_card *p11card; struct pkcs15_fw_data *fw_data = NULL; struct sc_pkcs15_card *p15card = NULL; struct sc_pkcs15_object *auth_object = NULL; struct sc_pkcs15_auth_info *pin_info = NULL; int rc; + if (slot->p11card == NULL) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_Login"); + p11card = slot->p11card; + fw_data = (struct pkcs15_fw_data *) p11card->fws_data[slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_Login"); p15card = fw_data->p15_card; + if (!p15card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_Login"); sc_log(context, "pkcs15-login: userType 0x%lX, PIN length %li", userType, ulPinLen); switch (userType) { @@ -1512,9 +1836,16 @@ pkcs15_login(struct sc_pkcs11_slot *slot, CK_USER_TYPE userType, rc = lock_card(fw_data); if (sc_pkcs11_conf.pin_unblock_style == SC_PKCS11_PIN_UNBLOCK_SO_LOGGED_INITPIN) { - if (ulPinLen && ulPinLen < sizeof(fw_data->user_puk)) { - memcpy(fw_data->user_puk, pPin, ulPinLen); - fw_data->user_puk_len = ulPinLen; + if (ulPinLen) { + unsigned char *user_puk = sc_mem_secure_alloc(ulPinLen); + if (user_puk) { + memcpy(user_puk, pPin, ulPinLen); + + sc_mem_secure_clear_free(fw_data->user_puk, fw_data->user_puk_len); + + fw_data->user_puk = user_puk; + fw_data->user_puk_len = (unsigned int) ulPinLen; + } } } @@ -1557,22 +1888,8 @@ pkcs15_login(struct sc_pkcs11_slot *slot, CK_USER_TYPE userType, if (pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return CKR_FUNCTION_REJECTED; - if (p11card->card->reader->capabilities & SC_READER_CAP_PIN_PAD - || (p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH)) { - /* pPin should be NULL in case of a pin pad reader, but - * some apps (e.g. older Netscapes) don't know about it. - * So we don't require that pPin == NULL, but set it to - * NULL ourselves. This way, you can supply an empty (if - * possible) or fake PIN if an application asks a PIN). - */ - /* But we want to be able to specify a PIN on the command - * line (e.g. for the test scripts). So we don't do anything - * here - this gives the user the choice of entering - * an empty pin (which makes us use the pin pad) or - * a valid pin (which is processed normally). --okir */ - if (ulPinLen == 0) - pPin = NULL; - } + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_Login"); /* By default, we make the reader resource manager keep other * processes from accessing the card while we're logged in. @@ -1591,7 +1908,16 @@ pkcs15_login(struct sc_pkcs11_slot *slot, CK_USER_TYPE userType, } } - rc = sc_pkcs15_verify_pin(p15card, auth_object, pPin, ulPinLen); + if (userType == CKU_CONTEXT_SPECIFIC) { + int auth_meth_saved = pin_info->auth_method; + + sc_log(context, "Setting SC_AC_CONTEXT_SPECIFIC"); + pin_info->auth_method = SC_AC_CONTEXT_SPECIFIC; + rc = sc_pkcs15_verify_pin(p15card, auth_object, pPin, ulPinLen); + pin_info->auth_method = auth_meth_saved; + } else + rc = sc_pkcs15_verify_pin(p15card, auth_object, pPin, ulPinLen); + sc_log(context, "PKCS15 verify PIN returned %d", rc); if (rc != SC_SUCCESS) @@ -1647,23 +1973,44 @@ pkcs15_login(struct sc_pkcs11_slot *slot, CK_USER_TYPE userType, } +/** + * Unlike OpenSC minidriver and OpenSCToken, the OpenSC PKCS#11 module does + * not benefit on an external PIN status tracking. Since the OpenSC module is + * incapable of detecting whether or not it is executed in concurrently + * running processes, it cannot simply logout of a token as it may conflict + * with a login session of a different process. If `SW_PIN_LOGOUT_ONLY` is + * set to `1`, the OpenSC PKCS#11 module will rely on its PIN tracking in + * software only without actually logging out of the token. + */ +#define SW_PIN_LOGOUT_ONLY 1 + static CK_RV pkcs15_logout(struct sc_pkcs11_slot *slot) { struct sc_pkcs11_card *p11card = slot->p11card; struct pkcs15_fw_data *fw_data = NULL; CK_RV ret = CKR_OK; +#if !(defined(SW_PIN_LOGOUT_ONLY) && SW_PIN_LOGOUT_ONLY == 1) int rc; +#endif + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_Logout"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_Logout"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_Logout"); - memset(fw_data->user_puk, 0, sizeof(fw_data->user_puk)); + sc_mem_secure_clear_free(fw_data->user_puk, fw_data->user_puk_len); + fw_data->user_puk = NULL; fw_data->user_puk_len = 0; sc_pkcs15_pincache_clear(fw_data->p15_card); +#if defined(SW_PIN_LOGOUT_ONLY) && SW_PIN_LOGOUT_ONLY == 1 + sc_log(context, "Clearing PIN state without calling sc_logout()"); +#else rc = sc_logout(fw_data->p15_card->card); /* Ignore missing card specific logout functions. #302 */ @@ -1678,6 +2025,7 @@ pkcs15_logout(struct sc_pkcs11_slot *slot) if (rc != SC_SUCCESS) ret = sc_to_cryptoki_error(rc, "C_Logout"); } +#endif /* TODO DEE free any session objects ? */ @@ -1698,11 +2046,14 @@ pkcs15_change_pin(struct sc_pkcs11_slot *slot, int login_user = slot->login_user; int rc; + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_SetPin"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_SetPin"); - p15card = fw_data->p15_card; + if (!p15card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_SetPin"); if (login_user == CKU_SO) { rc = sc_pkcs15_find_so_pin(p15card, &pin_obj); @@ -1719,26 +2070,11 @@ pkcs15_change_pin(struct sc_pkcs11_slot *slot, return CKR_USER_PIN_NOT_INITIALIZED; sc_log(context, "Change '%.*s' (ref:%i,type:%i)", (int) sizeof pin_obj->label, pin_obj->label, auth_info->attrs.pin.reference, login_user); - if ((p11card->card->reader->capabilities & SC_READER_CAP_PIN_PAD) - || (p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH)) { - /* pPin should be NULL in case of a pin pad reader, but - * some apps (e.g. older Netscapes) don't know about it. - * So we don't require that pPin == NULL, but set it to - * NULL ourselves. This way, you can supply an empty (if - * possible) or fake PIN if an application asks a PIN). - */ - pOldPin = pNewPin = NULL; - ulOldLen = ulNewLen = 0; - } - else if (ulNewLen < auth_info->attrs.pin.min_length || ulNewLen > auth_info->attrs.pin.max_length) { + if (pNewPin && (ulNewLen < auth_info->attrs.pin.min_length || ulNewLen > auth_info->attrs.pin.max_length)) { return CKR_PIN_LEN_RANGE; } - if (login_user < 0) { - if (sc_pkcs11_conf.pin_unblock_style != SC_PKCS11_PIN_UNBLOCK_UNLOGGED_SETPIN) { - sc_log(context, "PIN unlock is not allowed in unlogged session"); - return CKR_FUNCTION_NOT_SUPPORTED; - } + if (login_user < 0 && sc_pkcs11_conf.pin_unblock_style == SC_PKCS11_PIN_UNBLOCK_UNLOGGED_SETPIN) { rc = sc_pkcs15_unblock_pin(fw_data->p15_card, pin_obj, pOldPin, ulOldLen, pNewPin, ulNewLen); } else if (login_user == CKU_CONTEXT_SPECIFIC) { @@ -1748,7 +2084,7 @@ pkcs15_change_pin(struct sc_pkcs11_slot *slot, } rc = sc_pkcs15_unblock_pin(fw_data->p15_card, pin_obj, pOldPin, ulOldLen, pNewPin, ulNewLen); } - else if ((login_user == CKU_USER) || (login_user == CKU_SO)) { + else if (login_user < 0 || login_user == CKU_USER || login_user == CKU_SO) { rc = sc_pkcs15_change_pin(fw_data->p15_card, pin_obj, pOldPin, ulOldLen, pNewPin, ulNewLen); } else { @@ -1770,14 +2106,15 @@ pkcs15_initialize(struct sc_pkcs11_slot *slot, void *ptr, { struct sc_pkcs11_card *p11card = slot->p11card; struct sc_cardctl_pkcs11_init_token args; - scconf_block *atrblock = NULL; + scconf_block *conf_block = NULL; int rc, enable_InitToken = 0; CK_RV rv; sc_log(context, "Get 'enable-InitToken' card configuration option"); - atrblock = sc_match_atr_block(p11card->card->ctx, NULL, &p11card->reader->atr); - if (atrblock) - enable_InitToken = scconf_get_bool(atrblock, "pkcs11_enable_InitToken", 0); + if (!p11card) + return CKR_TOKEN_NOT_RECOGNIZED; + conf_block = sc_get_conf_block(p11card->card->ctx, "framework", "pkcs15", 1); + enable_InitToken = scconf_get_bool(conf_block, "pkcs11_enable_InitToken", 0); memset(&args, 0, sizeof(args)); args.so_pin = pPin; @@ -1821,7 +2158,7 @@ pkcs15_initialize(struct sc_pkcs11_slot *slot, void *ptr, if (p15card) { sc_log(context, "pkcs15init erase card"); - rc = sc_pkcs15init_erase_card(p15card, profile, NULL); + sc_pkcs15init_erase_card(p15card, profile, NULL); sc_log(context, "pkcs15init unbind"); sc_pkcs15init_unbind(profile); @@ -1907,6 +2244,8 @@ pkcs15_init_pin(struct sc_pkcs11_slot *slot, CK_CHAR_PTR pPin, CK_ULONG ulPinLen p11args.pin = pPin; p11args.pin_len = ulPinLen; + if (!p11card) + return CKR_TOKEN_NOT_RECOGNIZED; rc = sc_card_ctl(p11card->card, SC_CARDCTL_PKCS11_INIT_PIN, &p11args); if (rc != SC_ERROR_NOT_SUPPORTED) { if (rc == SC_SUCCESS) @@ -1915,7 +2254,7 @@ pkcs15_init_pin(struct sc_pkcs11_slot *slot, CK_CHAR_PTR pPin, CK_ULONG ulPinLen } sc_log(context, "Init PIN: pin %p:%lu; unblock style %i", pPin, - ulPinLen, sc_pkcs11_conf.pin_unblock_style); + ulPinLen, sc_pkcs11_conf.pin_unblock_style); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[slot->fw_data_idx]; if (!fw_data) @@ -1923,6 +2262,8 @@ pkcs15_init_pin(struct sc_pkcs11_slot *slot, CK_CHAR_PTR pPin, CK_ULONG ulPinLen auth_info = slot_data_auth_info(slot->fw_data); if (auth_info && sc_pkcs11_conf.pin_unblock_style == SC_PKCS11_PIN_UNBLOCK_SO_LOGGED_INITPIN) { + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_InitPin"); /* C_InitPIN is used to unblock User PIN or set it in the SO session .*/ auth_obj = slot_data_auth(slot->fw_data); if (fw_data->user_puk_len) @@ -2004,13 +2345,19 @@ pkcs15_create_private_key(struct sc_pkcs11_slot *slot, struct sc_profile *profil CK_KEY_TYPE key_type; struct sc_pkcs15_prkey_rsa *rsa = NULL; struct sc_pkcs15_prkey_gostr3410 *gost = NULL; - int rc, rv; + struct sc_pkcs15_prkey_ec *ec = NULL; + int rc; + CK_RV rv; char label[SC_PKCS15_MAX_LABEL_SIZE]; memset(&args, 0, sizeof(args)); + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_CreateObject"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_CreateObject"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_CreateObject"); /* See if the "slot" is pin protected. If so, get the PIN id */ if ((pin = slot_data_auth_info(slot->fw_data)) != NULL) @@ -2030,10 +2377,18 @@ pkcs15_create_private_key(struct sc_pkcs11_slot *slot, struct sc_profile *profil args.key.algorithm = SC_ALGORITHM_GOSTR3410; gost = &args.key.u.gostr3410; break; + case CKK_EC_EDWARDS: + args.key.algorithm = SC_ALGORITHM_EDDSA; + ec = &args.key.u.ec; + break; + case CKK_EC_MONTGOMERY: + args.key.algorithm = SC_ALGORITHM_XEDDSA; + ec = &args.key.u.ec; + break; case CKK_EC: args.key.algorithm = SC_ALGORITHM_EC; - /* TODO: -DEE Do not have PKCS15 card with EC to test this */ - /* fall through */ + ec = &args.key.u.ec; + break; default: return CKR_ATTRIBUTE_VALUE_INVALID; } @@ -2071,6 +2426,23 @@ pkcs15_create_private_key(struct sc_pkcs11_slot *slot, struct sc_profile *profil case CKA_VALUE: if (key_type == CKK_GOSTR3410) bn = &gost->d; + else if (key_type == CKK_EC || + key_type == CKK_EC_EDWARDS || + key_type == CKK_EC_MONTGOMERY) { + bn = &ec->privateD; + } + break; + case CKA_EC_PARAMS: + ec->params.der.value = calloc(1, attr->ulValueLen); + ec->params.der.len = attr->ulValueLen; + rv = attr_extract(attr, ec->params.der.value, &ec->params.der.len); + if (rv != CKR_OK) { + free(ec->params.der.value); + ec->params.der.value = NULL; + goto out; + } + if (sc_pkcs15_fix_ec_parameters(p11card->card->ctx, &ec->params) != SC_SUCCESS) + return CKR_ATTRIBUTE_VALUE_INVALID; break; case CKA_SIGN: args.usage |= pkcs15_check_bool_cka(attr, SC_PKCS15_PRKEY_USAGE_SIGN); @@ -2087,6 +2459,9 @@ pkcs15_create_private_key(struct sc_pkcs11_slot *slot, struct sc_profile *profil case CKA_OPENSC_NON_REPUDIATION: args.usage |= pkcs15_check_bool_cka(attr, SC_PKCS15_PRKEY_USAGE_NONREPUDIATION); break; + case CKA_ALWAYS_AUTHENTICATE: + args.user_consent = (int) (pkcs15_check_bool_cka(attr, 1)); + break; default: /* ignore unknown attrs, or flag error? */ continue; @@ -2119,9 +2494,23 @@ pkcs15_create_private_key(struct sc_pkcs11_slot *slot, struct sc_profile *profil rv = sc_to_cryptoki_error(rc, "C_CreateObject"); goto out; } + } else if (key_type == CKK_EC || + key_type == CKK_EC_EDWARDS || + key_type == CKK_EC_MONTGOMERY) { + if (!ec->privateD.len || !ec->params.field_length) { + sc_log(context, "Template to store the EC private key is incomplete"); + return CKR_TEMPLATE_INCOMPLETE; + } } rc = sc_pkcs15init_store_private_key(fw_data->p15_card, profile, &args, &key_obj); + /* free args now */ + if (key_type == CKK_EC || + key_type == CKK_EC_EDWARDS || + key_type == CKK_EC_MONTGOMERY) { + sc_clear_ec_params(&ec->params); + /* allocated above */ + } if (rc < 0) { rv = sc_to_cryptoki_error(rc, "C_CreateObject"); goto out; @@ -2133,16 +2522,24 @@ pkcs15_create_private_key(struct sc_pkcs11_slot *slot, struct sc_profile *profil rv = CKR_OK; -out: return rv; +out: + return rv; } - -/* TODO Only Session secret key objects are supported for now - * Sesison objects have CKA_TOKEN=false +/* + * Secret key objects can be stored on card, if the card supports them + * + * Session objects have CKA_TOKEN=false + * + * CKA_TOKEN = FALSE can mean two things: + * 1. If the card supports on card session objects, the object is stored on card for duration of the PKCS#11 session. + * Depending on card implementation, it can be automatically deleted during the card's reset. + * This kind of objects are not written to the PKCS#15 directory file. + * 2. If the card doesn't support on card session objects, a CKA_TOKEN = FALSE object is stored only in OpenSC's memory. + * * This is used by the C_DeriveKey with ECDH to hold the - * key, and the calling application can then retrieve tha attributes as needed. - * TODO If a card can support secret key objects on the card, this - * code will need to be expanded. + * key, and the calling application can then retrieve the attributes as needed. + * . */ static CK_RV pkcs15_create_secret_key(struct sc_pkcs11_slot *slot, struct sc_profile *profile, @@ -2154,16 +2551,23 @@ pkcs15_create_secret_key(struct sc_pkcs11_slot *slot, struct sc_profile *profile struct sc_pkcs15init_skeyargs args; struct pkcs15_any_object *key_any_obj = NULL; struct sc_pkcs15_object *key_obj = NULL; + struct sc_pkcs15_auth_info *pin = NULL; struct sc_pkcs15_skey_info *skey_info; CK_KEY_TYPE key_type; CK_BBOOL _token = FALSE; - int rv; + CK_RV rv; + int rc; char label[SC_PKCS15_MAX_LABEL_SIZE]; + CK_BBOOL temp_object = FALSE; memset(&args, 0, sizeof(args)); + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_CreateObject"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_CreateObject"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_CreateObject"); /* Get the key type */ rv = attr_find(pTemplate, ulCount, CKA_KEY_TYPE, &key_type, NULL); @@ -2172,12 +2576,26 @@ pkcs15_create_secret_key(struct sc_pkcs11_slot *slot, struct sc_profile *profile /* CKA_TOKEN defaults to false */ rv = attr_find(pTemplate, ulCount, CKA_TOKEN, &_token, NULL); - if (rv != CKR_OK) + if (rv != CKR_TEMPLATE_INCOMPLETE && rv != CKR_OK) { return rv; + } + + /* See if the "slot" is pin protected. If so, get the PIN id */ + if ((pin = slot_data_auth_info(slot->fw_data)) != NULL) + args.auth_id = pin->auth_id; switch (key_type) { - /* Only support GENERIC_SECRET for now */ case CKK_GENERIC_SECRET: + args.algorithm = SC_ALGORITHM_UNDEFINED; + break; + case CKK_AES: + args.algorithm = SC_ALGORITHM_AES; + break; + case CKK_DES3: + args.algorithm = SC_ALGORITHM_3DES; + break; + case CKK_DES: + args.algorithm = SC_ALGORITHM_DES; break; default: return CKR_ATTRIBUTE_VALUE_INVALID; @@ -2207,11 +2625,12 @@ pkcs15_create_secret_key(struct sc_pkcs11_slot *slot, struct sc_profile *profile break; case CKA_VALUE: if (attr->pValue) { - args.key.data = calloc(1,attr->ulValueLen); - if (!args.key.data) - return CKR_HOST_MEMORY; - memcpy(args.key.data, attr->pValue, attr->ulValueLen); - args.key.data_len = attr->ulValueLen; + free(args.key.data); + args.key.data = calloc(1,attr->ulValueLen); + if (!args.key.data) + return CKR_HOST_MEMORY; + memcpy(args.key.data, attr->pValue, attr->ulValueLen); + args.key.data_len = attr->ulValueLen; } break; case CKA_DECRYPT: @@ -2226,6 +2645,13 @@ pkcs15_create_secret_key(struct sc_pkcs11_slot *slot, struct sc_profile *profile case CKA_UNWRAP: args.usage |= pkcs15_check_bool_cka(attr, SC_PKCS15_PRKEY_USAGE_UNWRAP); break; + case CKA_EXTRACTABLE: + if (pkcs15_check_bool_cka(attr, 1)) + args.access_flags |= SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE; + break; + case CKA_OPENSC_ALWAYS_AUTH_ANY_OBJECT: + args.user_consent = (int) (pkcs15_check_bool_cka(attr, 1)); + break; default: /* ignore unknown attrs, or flag error? */ continue; @@ -2233,54 +2659,53 @@ pkcs15_create_secret_key(struct sc_pkcs11_slot *slot, struct sc_profile *profile } /* If creating a PKCS#11 session object, i.e. one that is only in memory */ - if (_token == FALSE) { + if (_token == FALSE && (fw_data->p15_card->card->caps & SC_CARD_CAP_ONCARD_SESSION_OBJECTS) == 0) { - /* TODO Have 3 choices as to how to create the object. - * (1)create a sc_pkcs15init_store_secret_key routine like the others - * (2)use the sc_pkcs15emu_ routines - * (3)do it inline here (Will do this for now) - */ + /* TODO Have 3 choices as to how to create the object. + * (1)create a sc_pkcs15init_store_secret_key routine like the others + * (2)use the sc_pkcs15emu_ routines + * (3)do it inline here (Will do this for now) + */ - key_obj = calloc(1, sizeof(sc_pkcs15_object_t)); - if (key_obj == NULL) { + key_obj = calloc(1, sizeof(sc_pkcs15_object_t)); + if (key_obj == NULL) { rv = CKR_HOST_MEMORY; goto out; - } - key_obj->type = SC_PKCS15_TYPE_SKEY; + } + temp_object = TRUE; + key_obj->type = SC_PKCS15_TYPE_SKEY; - if (args.id.len) - memcpy(key_obj->label, args.id.value, args.id.len); + if (args.id.len) + memcpy(key_obj->label, args.id.value, args.id.len); - key_obj->flags = 2; /* TODO not sure what these mean */ + key_obj->flags = 2; /* TODO not sure what these mean */ - skey_info = calloc(1, sizeof(sc_pkcs15_skey_info_t)); + skey_info = calloc(1, sizeof(sc_pkcs15_skey_info_t)); if (skey_info == NULL) { rv = CKR_HOST_MEMORY; goto out; - } - key_obj->data = skey_info; - skey_info->usage = args.usage; - skey_info->native = 0; /* card can not use this */ - skey_info->access_flags = 0; /* looks like not needed */ - skey_info->key_type = key_type; /* PKCS#11 CKK_* */ - skey_info->data.value = args.key.data; - skey_info->data.len = args.key.data_len; - skey_info->value_len = args.value_len; /* callers prefered length */ - + } + key_obj->data = skey_info; + skey_info->usage = (unsigned int) args.usage; + skey_info->native = 0; /* card can not use this */ + skey_info->access_flags = 0; /* looks like not needed */ + skey_info->key_type = key_type; /* PKCS#11 CKK_* */ + skey_info->data.value = args.key.data; + skey_info->data.len = args.key.data_len; + skey_info->value_len = args.value_len * 8; /* key length comes in number of bytes, use length in bits in PKCS#15. */ + args.key.data = NULL; + key_obj->session_object = 1; } else { -#if 1 - rv = CKR_FUNCTION_NOT_SUPPORTED; - goto out; -#else - /* TODO add support for secret key on the card with something like this: */ + if(_token == FALSE) + args.session_object = 1; /* store the object on card for duration of the session. */ - rc = sc_pkcs15init_store_secret_key(fw_data->p15_card, profile, &args, &key_obj); - if (rc < 0) { - rv = sc_to_cryptoki_error(rc, "C_CreateObject"); - goto out; - } -#endif + args.value_len = args.value_len * 8; /* CKA_VALUE_LEN is number of bytes, PKCS#15 needs key length in bits */ + rc = sc_pkcs15init_store_secret_key(fw_data->p15_card, profile, &args, &key_obj); + if (rc < 0) { + rv = sc_to_cryptoki_error(rc, "C_CreateObject"); + goto out; + } } /* Create a new pkcs11 object for it */ @@ -2290,11 +2715,14 @@ pkcs15_create_secret_key(struct sc_pkcs11_slot *slot, struct sc_profile *profile rv = CKR_OK; out: - free(key_obj); + free(args.key.data); /* if allocated */ + + /* on error, free key_obj, unless it's created by pkcs15init. if OK, let it live as part of key_any_obj. */ + if (rv != CKR_OK && temp_object) + free(key_obj); /* do not free if the object was created by pkcs15init. It will be freed in C_Finalize */ return rv; } - static CK_RV pkcs15_create_public_key(struct sc_pkcs11_slot *slot, struct sc_profile *profile, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) @@ -2307,13 +2735,19 @@ pkcs15_create_public_key(struct sc_pkcs11_slot *slot, struct sc_profile *profile struct sc_pkcs15_auth_info *pin = NULL; CK_KEY_TYPE key_type; struct sc_pkcs15_pubkey_rsa *rsa = NULL; - int rc, rv; + struct sc_pkcs15_pubkey_ec *ec = NULL; + int rc; + CK_RV rv; char label[SC_PKCS15_MAX_LABEL_SIZE]; memset(&args, 0, sizeof(args)); + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_CreateObject"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_CreateObject"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_CreateObject"); /* See if the "slot" is pin protected. If so, get the PIN id */ if ((pin = slot_data_auth_info(slot->fw_data)) != NULL) @@ -2329,13 +2763,21 @@ pkcs15_create_public_key(struct sc_pkcs11_slot *slot, struct sc_profile *profile rsa = &args.key.u.rsa; break; case CKK_EC: - /* TODO: -DEE Do not have real pkcs15 card with EC */ - /* fall through */ + args.key.algorithm = SC_ALGORITHM_EC; + ec = &args.key.u.ec; + break; + case CKK_EC_EDWARDS: + args.key.algorithm = SC_ALGORITHM_EDDSA; + ec = &args.key.u.ec; + break; + case CKK_EC_MONTGOMERY: + args.key.algorithm = SC_ALGORITHM_XEDDSA; + ec = &args.key.u.ec; + break; default: return CKR_ATTRIBUTE_VALUE_INVALID; } - rv = CKR_OK; while (ulCount--) { CK_ATTRIBUTE_PTR attr = pTemplate++; sc_pkcs15_bignum_t *bn = NULL; @@ -2372,8 +2814,49 @@ pkcs15_create_public_key(struct sc_pkcs11_slot *slot, struct sc_profile *profile case CKA_WRAP: args.usage |= pkcs15_check_bool_cka(attr, SC_PKCS15_PRKEY_USAGE_WRAP); break; - default: - /* ignore unknown attrs, or flag error? */ + case CKA_EC_POINT: + switch (key_type) { + case CKK_EC: + if (sc_pkcs15_decode_pubkey_ec(p11card->card->ctx, ec, attr->pValue, attr->ulValueLen) < 0) { + free(ec->ecpointQ.value); + ec->ecpointQ.value = NULL; + ec->ecpointQ.len = 0; + sc_clear_ec_params(&ec->params); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + + break; + case CKK_EC_EDWARDS: + case CKK_EC_MONTGOMERY: + /* TODO my need to decode here too to support OS vs BS */ + /* Difference between 25519 and 448 versions set by ec->ecpointQ.len below */ + ec->ecpointQ.value = calloc(1, attr->ulValueLen); + ec->ecpointQ.len = attr->ulValueLen; + rv = attr_extract(attr, ec->ecpointQ.value, &ec->ecpointQ.len); + if (rv != CKR_OK) { + free(ec->ecpointQ.value); + ec->ecpointQ.value = NULL; + ec->ecpointQ.len = 0; + sc_clear_ec_params(&ec->params); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + default: + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + case CKA_EC_PARAMS: + ec->params.der.value = calloc(1, attr->ulValueLen); + ec->params.der.len = attr->ulValueLen; + rv = attr_extract(attr, ec->params.der.value, &ec->params.der.len); + if (rv != CKR_OK) { + sc_clear_ec_params(&ec->params); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + default: + /* ignore unknown attrs, or flag error? */ continue; } @@ -2385,10 +2868,45 @@ pkcs15_create_public_key(struct sc_pkcs11_slot *slot, struct sc_profile *profile } } - if (!rsa->modulus.len || !rsa->exponent.len) - return CKR_TEMPLATE_INCOMPLETE; + if (key_type == CKK_EC_EDWARDS) { + if (ec->ecpointQ.len == BYTES4BITS(256)) /* note extra bit */ + ec->params.id = oid_ED25519; + else if (ec->ecpointQ.len == ED448_KEY_SIZE_BYTES) + ec->params.id = oid_ED448; + else + return CKR_ATTRIBUTE_VALUE_INVALID; + + } else if (key_type == CKK_EC_MONTGOMERY) { + if (ec->ecpointQ.len == BYTES4BITS(256)) /* note extra bit */ + ec->params.id = oid_X25519; + else if (ec->ecpointQ.len == BYTES4BITS(448)) + ec->params.id = oid_X448; + else + return CKR_ATTRIBUTE_VALUE_INVALID; + } + + if (key_type == CKK_RSA) { + if (!rsa->modulus.len || !rsa->exponent.len) + return CKR_TEMPLATE_INCOMPLETE; + } else if (key_type == CKK_EC || + key_type == CKK_EC_EDWARDS || + key_type == CKK_EC_MONTGOMERY) { + rc = sc_pkcs15_fix_ec_parameters(p11card->card->ctx, &ec->params); + + if (rc || !ec->ecpointQ.len || !ec->params.der.value) { + sc_log(context, "Template to store the EC public key is incomplete"); + return CKR_TEMPLATE_INCOMPLETE; + } + } rc = sc_pkcs15init_store_public_key(fw_data->p15_card, profile, &args, &key_obj); + /* free args now */ + if (key_type == CKK_EC || + key_type == CKK_EC_EDWARDS || + key_type == CKK_EC_MONTGOMERY) { + sc_clear_ec_params(&ec->params); + free(ec->ecpointQ.value); + } if (rc < 0) return sc_to_cryptoki_error(rc, "C_CreateObject"); @@ -2413,13 +2931,18 @@ pkcs15_create_certificate(struct sc_pkcs11_slot *slot, struct sc_pkcs15_object *cert_obj = NULL; CK_CERTIFICATE_TYPE cert_type; CK_BBOOL bValue; - int rc, rv; + int rc; + CK_RV rv; char label[SC_PKCS15_MAX_LABEL_SIZE]; memset(&args, 0, sizeof(args)); + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_CreateObject"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_CreateObject"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_CreateObject"); /* Get the key type */ rv = attr_find(pTemplate, ulCount, CKA_CERTIFICATE_TYPE, @@ -2429,7 +2952,6 @@ pkcs15_create_certificate(struct sc_pkcs11_slot *slot, if (cert_type != CKC_X_509) return CKR_ATTRIBUTE_VALUE_INVALID; - rv = CKR_OK; while (ulCount--) { CK_ATTRIBUTE_PTR attr = pTemplate++; @@ -2438,7 +2960,7 @@ pkcs15_create_certificate(struct sc_pkcs11_slot *slot, case CKA_CLASS: break; case CKA_PRIVATE: - rv = attr_extract(attr, &bValue, NULL); + attr_extract(attr, &bValue, NULL); if (bValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto out; @@ -2495,17 +3017,21 @@ pkcs15_create_data(struct sc_pkcs11_slot *slot, struct sc_profile *profile, struct sc_pkcs15_object *data_obj = NULL; struct sc_pkcs15_auth_info *pin = NULL; CK_BBOOL bValue; - int rc, rv; + int rc; + CK_RV rv; char label[SC_PKCS15_MAX_LABEL_SIZE]; memset(&args, 0, sizeof(args)); sc_init_oid(&args.app_oid); + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_CreateObject"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_CreateObject"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_CreateObject"); - rv = CKR_OK; while (ulCount--) { CK_ATTRIBUTE_PTR attr = pTemplate++; @@ -2514,7 +3040,7 @@ pkcs15_create_data(struct sc_pkcs11_slot *slot, struct sc_profile *profile, case CKA_CLASS: break; case CKA_PRIVATE: - rv = attr_extract(attr, &bValue, NULL); + attr_extract(attr, &bValue, NULL); if (bValue) { pin = slot_data_auth_info(slot->fw_data); if (pin == NULL) { @@ -2537,7 +3063,7 @@ pkcs15_create_data(struct sc_pkcs11_slot *slot, struct sc_profile *profile, args.app_label = (char *) attr->pValue; break; case CKA_OBJECT_ID: - if (sc_asn1_decode_object_id(attr->pValue, attr->ulValueLen, &args.app_oid)) { + if (sc_asn1_decode_object_id(attr->pValue, attr->ulValueLen, &args.app_oid)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto out; } @@ -2552,11 +3078,6 @@ pkcs15_create_data(struct sc_pkcs11_slot *slot, struct sc_profile *profile, } } - if (args.der_encoded.len == 0) { - rv = CKR_TEMPLATE_INCOMPLETE; - goto out; - } - rc = sc_pkcs15init_store_data_object(fw_data->p15_card, profile, &args, &data_obj); if (rc < 0) { rv = sc_to_cryptoki_error(rc, "C_CreateObject"); @@ -2581,35 +3102,32 @@ pkcs15_create_object(struct sc_pkcs11_slot *slot, CK_ATTRIBUTE_PTR pTemplate, CK struct sc_profile *profile = NULL; CK_OBJECT_CLASS _class; CK_BBOOL _token = FALSE; - int rv, rc; + CK_RV rv; + int rc; + CK_BBOOL p15init_create_object; + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_CreateObject"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_CreateObject"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_CreateObject"); rv = attr_find(pTemplate, ulCount, CKA_CLASS, &_class, NULL); if (rv != CKR_OK) return rv; rv = attr_find(pTemplate, ulCount, CKA_TOKEN, &_token, NULL); - if (rv == CKR_TEMPLATE_INCOMPLETE) { - /* TODO OpenSC has not checked CKA_TOKEN == TRUE, so only - * so only enforce for secret_key - */ - if (_class != CKO_SECRET_KEY) - _token = TRUE; /* default if not in template */ - } - else if (rv != CKR_OK) { + if (rv != CKR_TEMPLATE_INCOMPLETE && rv != CKR_OK) { return rv; } - /* TODO The previous code does not check for CKA_TOKEN=TRUE - * PKCS#11 CreatObject examples always have it, but - * PKCS#11 says the default is false. - * for backward compatability, will default to TRUE - */ - /* Dont need profile id creating session only objects */ - if (_token == TRUE) { + /* Dont need profile if creating session only objects, + * except when the card supports temporary on card session objects */ + p15init_create_object = _token == TRUE || (p11card->card->caps & SC_CARD_CAP_ONCARD_SESSION_OBJECTS) == SC_CARD_CAP_ONCARD_SESSION_OBJECTS; + + if (p15init_create_object) { struct sc_aid *aid = NULL; rc = sc_lock(p11card->card); @@ -2655,7 +3173,9 @@ pkcs15_create_object(struct sc_pkcs11_slot *slot, CK_ATTRIBUTE_PTR pTemplate, CK rv = CKR_FUNCTION_NOT_SUPPORTED; } - if (_token == TRUE) { + if (p15init_create_object) { + // TODO: after sc_pkcs15init_unbind, user may have to enter PIN on a pin pad reader even though authentication state + // is supposed to remain open. Check why this happens. sc_pkcs15init_unbind(profile); sc_unlock(p11card->card); } @@ -2685,8 +3205,8 @@ get_X509_usage_privk(CK_ATTRIBUTE_PTR pTempl, CK_ULONG ulCount, unsigned long *x *x509_usage |= SC_PKCS15INIT_X509_NON_REPUDIATION; if (typ == CKA_VERIFY || typ == CKA_WRAP || typ == CKA_ENCRYPT) { sc_log(context, - "get_X509_usage_privk(): invalid typ = 0x%0lx", - typ); + "get_X509_usage_privk(): invalid typ = 0x%0lx", + typ); return CKR_ATTRIBUTE_TYPE_INVALID; } } @@ -2713,8 +3233,8 @@ get_X509_usage_pubk(CK_ATTRIBUTE_PTR pTempl, CK_ULONG ulCount, unsigned long *x5 *x509_usage |= SC_PKCS15INIT_X509_KEY_AGREEMENT; if (typ == CKA_SIGN || typ == CKA_UNWRAP || typ == CKA_DECRYPT) { sc_log(context, - "get_X509_usage_pubk(): invalid typ = 0x%0lx", - typ); + "get_X509_usage_pubk(): invalid typ = 0x%0lx", + typ); return CKR_ATTRIBUTE_TYPE_INVALID; } } @@ -2731,14 +3251,17 @@ set_gost3410_params(struct sc_pkcs15init_prkeyargs *prkey_args, const CK_BYTE * gost_params_encoded_oid_from_template; const CK_BYTE * gost_hash_params_encoded_oid_from_template; size_t len, param_index, hash_index; + void *ptr = NULL; CK_RV rv; /* If template has CKA_GOSTR3410_PARAMS attribute, set param_index to * corresponding item's index in gostr3410_param_oid[] */ - if (pPrivTpl && ulPrivCnt) - rv = attr_find_ptr2(pPubTpl, ulPubCnt, pPrivTpl, ulPrivCnt, CKA_GOSTR3410_PARAMS, (void **)&gost_params_encoded_oid_from_template, &len); - else - rv = attr_find_ptr(pPubTpl, ulPubCnt, CKA_GOSTR3410_PARAMS, (void **)&gost_params_encoded_oid_from_template, &len); + if (pPrivTpl && ulPrivCnt) { + rv = attr_find_ptr2(pPubTpl, ulPubCnt, pPrivTpl, ulPrivCnt, CKA_GOSTR3410_PARAMS, &ptr, &len); + } else { + rv = attr_find_ptr(pPubTpl, ulPubCnt, CKA_GOSTR3410_PARAMS, &ptr, &len); + } + gost_params_encoded_oid_from_template = (const CK_BYTE *) ptr; if (rv == CKR_OK) { size_t nn = sizeof(gostr3410_param_oid)/sizeof(gostr3410_param_oid[0]); @@ -2761,10 +3284,12 @@ set_gost3410_params(struct sc_pkcs15init_prkeyargs *prkey_args, /* If template has CKA_GOSTR3411_PARAMS attribute, set hash_index to * corresponding item's index in gostr3410_hash_param_oid[] */ - if (pPrivTpl && ulPrivCnt) - rv = attr_find_ptr2(pPubTpl, ulPubCnt, pPrivTpl, ulPrivCnt, CKA_GOSTR3411_PARAMS, (void **)&gost_hash_params_encoded_oid_from_template, &len); - else - rv = attr_find_ptr(pPubTpl, ulPubCnt, CKA_GOSTR3411_PARAMS, (void **)&gost_hash_params_encoded_oid_from_template, &len); + if (pPrivTpl && ulPrivCnt) { + rv = attr_find_ptr2(pPubTpl, ulPubCnt, pPrivTpl, ulPrivCnt, CKA_GOSTR3411_PARAMS, &ptr, &len); + } else { + rv = attr_find_ptr(pPubTpl, ulPubCnt, CKA_GOSTR3411_PARAMS, &ptr, &len); + } + gost_hash_params_encoded_oid_from_template = ptr; if (rv == CKR_OK) { size_t nn = sizeof(gostr3410_hash_param_oid)/sizeof(gostr3410_hash_param_oid[0]); @@ -2832,19 +3357,29 @@ pkcs15_gen_keypair(struct sc_pkcs11_slot *slot, CK_MECHANISM_PTR pMechanism, CK_ULONG keybits = 0; char pub_label[SC_PKCS15_MAX_LABEL_SIZE]; char priv_label[SC_PKCS15_MAX_LABEL_SIZE]; - int rc, rv = CKR_OK; + int rc; + CK_RV rv = CKR_OK; + CK_BBOOL always_auth = CK_FALSE; + CK_BBOOL sensitive = CK_FALSE; + CK_BBOOL extractable = CK_FALSE; - sc_log(context, "Keypair generation, mech = 0x%0lx", - pMechanism->mechanism); + sc_log(context, "Key pair generation, mech = 0x%0lx", + pMechanism->mechanism); if (pMechanism->mechanism != CKM_RSA_PKCS_KEY_PAIR_GEN && pMechanism->mechanism != CKM_GOSTR3410_KEY_PAIR_GEN - && pMechanism->mechanism != CKM_EC_KEY_PAIR_GEN) + && pMechanism->mechanism != CKM_EC_KEY_PAIR_GEN + && pMechanism->mechanism != CKM_EC_EDWARDS_KEY_PAIR_GEN + && pMechanism->mechanism != CKM_EC_MONTGOMERY_KEY_PAIR_GEN) return CKR_MECHANISM_INVALID; + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GenerateKeyPair"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_GenerateKeyPair"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GenerateKeyPair"); rc = sc_lock(p11card->card); if (rc < 0) @@ -2879,6 +3414,10 @@ pkcs15_gen_keypair(struct sc_pkcs11_slot *slot, CK_MECHANISM_PTR pMechanism, keytype = CKK_RSA; else if (rv != CKR_OK && pMechanism->mechanism == CKM_EC_KEY_PAIR_GEN) keytype = CKK_EC; + else if (rv != CKR_OK && pMechanism->mechanism == CKM_EC_EDWARDS_KEY_PAIR_GEN) + keytype = CKK_EC_EDWARDS; + else if (rv != CKR_OK && pMechanism->mechanism == CKM_EC_MONTGOMERY_KEY_PAIR_GEN) + keytype = CKK_EC_MONTGOMERY; else if (rv != CKR_OK && pMechanism->mechanism == CKM_GOSTR3410_KEY_PAIR_GEN) keytype = CKK_GOSTR3410; else if (rv != CKR_OK) @@ -2902,26 +3441,40 @@ pkcs15_gen_keypair(struct sc_pkcs11_slot *slot, CK_MECHANISM_PTR pMechanism, if (rv != CKR_OK) keybits = 1024; /* Default key size */ /* TODO: check allowed values of keybits */ - } - else if (keytype == CKK_EC) { + } else if (keytype == CKK_EC || keytype == CKK_EC_EDWARDS || keytype == CKK_EC_MONTGOMERY) { struct sc_lv_data *der = &keygen_args.prkey_args.key.u.ec.params.der; + void *ptr = NULL; - der->len = sizeof(struct sc_object_id); - rv = attr_find_and_allocate_ptr(pPubTpl, ulPubCnt, CKA_EC_PARAMS, (void **)&der->value, &der->len); + rv = attr_find_and_allocate_ptr(pPubTpl, ulPubCnt, CKA_EC_PARAMS, &ptr, &der->len); + der->value = (unsigned char *) ptr; if (rv != CKR_OK) { sc_unlock(p11card->card); - return sc_to_cryptoki_error(rc, "C_GenerateKeyPair"); + return rv; } - - keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_EC; - pub_args.key.algorithm = SC_ALGORITHM_EC; - } - else { + if (sc_pkcs15_fix_ec_parameters(context, &keygen_args.prkey_args.key.u.ec.params) < 0) { + rv = CKR_CURVE_NOT_SUPPORTED; + goto kpgen_done; + } + } else { /* CKA_KEY_TYPE is set, but keytype isn't correct */ rv = CKR_ATTRIBUTE_VALUE_INVALID; goto kpgen_done; } + if (keytype == CKK_EC) { + keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_EC; + pub_args.key.algorithm = SC_ALGORITHM_EC; + } else if (keytype == CKK_EC_EDWARDS) { + keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_EDDSA; + keygen_args.prkey_args.usage |= SC_PKCS15_PRKEY_USAGE_SIGN; + pub_args.key.algorithm = SC_ALGORITHM_EDDSA; + } else if (keytype == CKK_EC_MONTGOMERY) { + keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_XEDDSA; + /* Can not sign. To created a cert, see: openssl x509 -force_pubkey */ + keygen_args.prkey_args.usage |= SC_PKCS15_PRKEY_USAGE_DERIVE; + pub_args.key.algorithm = SC_ALGORITHM_XEDDSA; + } + id.len = SC_PKCS15_MAX_ID_SIZE; rv = attr_find2(pPubTpl, ulPubCnt, pPrivTpl, ulPrivCnt, CKA_ID, &id.value, &id.len); if (rv == CKR_OK) @@ -2948,12 +3501,36 @@ pkcs15_gen_keypair(struct sc_pkcs11_slot *slot, CK_MECHANISM_PTR pMechanism, goto kpgen_done; pub_args.x509_usage = keygen_args.prkey_args.x509_usage; + len = sizeof(always_auth); + rv = attr_find(pPrivTpl, ulPrivCnt, CKA_ALWAYS_AUTHENTICATE, &always_auth, &len); + if (rv == CKR_OK && always_auth == CK_TRUE) { + keygen_args.prkey_args.user_consent = 1; + } + + /* set EXTRACTABLE and SENSITIVESE flags */ + len = sizeof(extractable); + rv = attr_find(pPrivTpl, ulPrivCnt, CKA_EXTRACTABLE, &extractable, &len); + if (rv == CKR_OK && extractable == CK_TRUE) + keygen_args.prkey_args.access_flags |= SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE; + else + keygen_args.prkey_args.access_flags |= SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; + + len = sizeof(sensitive); + rv = attr_find(pPrivTpl, ulPrivCnt, CKA_SENSITIVE, &sensitive, &len); + if (rv == CKR_OK && sensitive == CK_TRUE) { + keygen_args.prkey_args.access_flags |= SC_PKCS15_PRKEY_ACCESS_SENSITIVE; + keygen_args.prkey_args.access_flags |= SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE; + } + + /* This is called form C_GenerateKey, key is alwways LOCAL (PKCS#11 v3.0 section 4.7.2) */ + keygen_args.prkey_args.access_flags |= SC_PKCS15_PRKEY_ACCESS_LOCAL; + /* 3.a Try on-card key pair generation */ sc_pkcs15init_set_p15card(profile, fw_data->p15_card); sc_log(context, "Try on-card key pair generation"); - rc = sc_pkcs15init_generate_key(fw_data->p15_card, profile, &keygen_args, keybits, &priv_key_obj); + rc = sc_pkcs15init_generate_key(fw_data->p15_card, profile, &keygen_args, (unsigned int) keybits, &priv_key_obj); if (rc >= 0) { id = ((struct sc_pkcs15_prkey_info *) priv_key_obj->data)->id; rc = sc_pkcs15_find_pubkey_by_id(fw_data->p15_card, &id, &pub_key_obj); @@ -2990,6 +3567,7 @@ pkcs15_gen_keypair(struct sc_pkcs11_slot *slot, CK_MECHANISM_PTR pMechanism, rv = sc_pkcs15_dup_pubkey(context, ((struct pkcs15_pubkey_object *)pub_any_obj)->pub_data, &priv_prk_obj->pub_data); kpgen_done: + sc_pkcs15_erase_prkey(&keygen_args.prkey_args.key); sc_pkcs15init_unbind(profile); sc_unlock(p11card->card); @@ -3007,18 +3585,29 @@ pkcs15_skey_destroy(struct sc_pkcs11_session *session, void *object) struct pkcs15_any_object *any_obj = (struct pkcs15_any_object*) object; struct sc_pkcs11_card *p11card = session->slot->p11card; struct pkcs15_fw_data *fw_data = NULL; + struct sc_pkcs15_object *p15obj = any_obj->p15_object; int rv; + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GenerateKeyPair"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_GenerateKeyPair"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GenerateKeyPair"); + + if (p15obj->session_object) { + struct sc_pkcs15_skey_info *skey_info = ((struct pkcs15_skey_object *)any_obj)->info; + sc_pkcs15_free_skey_info(skey_info); + free(p15obj); + } /* TODO assuming this is a session only object. */ rv = sc_lock(p11card->card); if (rv < 0) return sc_to_cryptoki_error(rv, "C_DestroyObject"); /* Oppose to pkcs15_add_object */ - --any_obj->refcount; /* correct refcont */ + --any_obj->refcount; /* correct refcount */ list_delete(&session->slot->objects, any_obj); /* Delete object in pkcs15 */ rv = __pkcs15_delete_object(fw_data, any_obj); @@ -3042,14 +3631,18 @@ pkcs15_any_destroy(struct sc_pkcs11_session *session, void *object) struct pkcs15_any_object *any_obj = (struct pkcs15_any_object*) object; struct sc_pkcs11_slot *slot = session->slot; struct sc_pkcs11_card *p11card = slot->p11card; - struct pkcs15_fw_data *fw_data = NULL; + struct pkcs15_fw_data *fw_data = NULL; struct sc_aid *aid = NULL; struct sc_profile *profile = NULL; int rv; + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_DestroyObject"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_DestroyObject"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_DestroyObject"); rv = sc_lock(p11card->card); if (rv < 0) @@ -3102,7 +3695,7 @@ pkcs15_any_destroy(struct sc_pkcs11_session *session, void *object) rv = sc_pkcs15init_delete_object(fw_data->p15_card, profile, obj->base.p15_object); if (rv >= 0) { /* Oppose to pkcs15_add_object */ - --any_obj->refcount; /* correct refcont */ + --any_obj->refcount; /* correct refcount */ list_delete(&session->slot->objects, any_obj); /* Delete object in pkcs15 */ rv = __pkcs15_delete_object(fw_data, any_obj); @@ -3126,9 +3719,13 @@ pkcs15_get_random(struct sc_pkcs11_slot *slot, CK_BYTE_PTR p, CK_ULONG len) struct pkcs15_fw_data *fw_data = NULL; int rc; + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GenerateRandom"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_GenerateRandom"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GenerateRandom"); rc = sc_get_challenge(fw_data->p15_card->card, p, (size_t)len); return sc_to_cryptoki_error(rc, "C_GenerateRandom"); @@ -3174,9 +3771,13 @@ pkcs15_set_attrib(struct sc_pkcs11_session *session, struct sc_pkcs15_object *p1 int rv = 0; CK_RV ck_rv = CKR_OK; + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_SetAttributeValue"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_SetAttributeValue"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_SetAttributeValue"); rv = sc_lock(p11card->card); if (rv < 0) @@ -3202,7 +3803,7 @@ pkcs15_set_attrib(struct sc_pkcs11_session *session, struct sc_pkcs15_object *p1 switch(attr->type) { case CKA_LABEL: rv = sc_pkcs15init_change_attrib(fw_data->p15_card, profile, p15_object, - P15_ATTR_TYPE_LABEL, attr->pValue, attr->ulValueLen); + P15_ATTR_TYPE_LABEL, attr->pValue, (unsigned int) attr->ulValueLen); break; case CKA_ID: if (attr->ulValueLen > SC_PKCS15_MAX_ID_SIZE) { @@ -3217,6 +3818,14 @@ pkcs15_set_attrib(struct sc_pkcs11_session *session, struct sc_pkcs15_object *p1 case CKA_SUBJECT: rv = SC_SUCCESS; break; + case CKA_VALUE: + if ((p15_object->type & SC_PKCS15_TYPE_CLASS_MASK) != SC_PKCS15_TYPE_DATA_OBJECT) { + ck_rv = CKR_ATTRIBUTE_READ_ONLY; + goto set_attr_done; + } + rv = sc_pkcs15init_change_attrib(fw_data->p15_card, profile, p15_object, + P15_ATTR_TYPE_VALUE, attr->pValue, (unsigned int) attr->ulValueLen); + break; default: ck_rv = CKR_ATTRIBUTE_READ_ONLY; goto set_attr_done; @@ -3265,9 +3874,13 @@ pkcs15_cert_get_attribute(struct sc_pkcs11_session *session, void *object, CK_AT sc_log(context, "pkcs15_cert_get_attribute() called"); p11card = session->slot->p11card; + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GetAttributeValue"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_GetAttributeValue"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GetAttributeValue"); switch (attr->type) { case CKA_CLASS: @@ -3300,14 +3913,15 @@ pkcs15_cert_get_attribute(struct sc_pkcs11_session *session, void *object, CK_AT *(CK_CERTIFICATE_TYPE*)attr->pValue = CKC_X_509; break; case CKA_ID: - if (cert->cert_info->authority && sc_pkcs11_conf.zero_ckaid_for_ca_certs) { +#ifdef ZERO_CKAID_FOR_CA_CERTS + if (cert->cert_info->authority) { check_attribute_buffer(attr, 1); *(unsigned char*)attr->pValue = 0; + break; } - else { - check_attribute_buffer(attr, cert->cert_info->id.len); - memcpy(attr->pValue, cert->cert_info->id.value, cert->cert_info->id.len); - } +#endif + check_attribute_buffer(attr, cert->cert_info->id.len); + memcpy(attr->pValue, cert->cert_info->id.value, cert->cert_info->id.len); break; case CKA_TRUSTED: check_attribute_buffer(attr, sizeof(CK_BBOOL)); @@ -3355,7 +3969,7 @@ pkcs15_cert_get_attribute(struct sc_pkcs11_session *session, void *object, CK_AT #define ASN1_SET_TAG (SC_ASN1_SET | SC_ASN1_TAG_CONSTRUCTED) #define ASN1_SEQ_TAG (SC_ASN1_SEQUENCE | SC_ASN1_TAG_CONSTRUCTED) -static int +static CK_RV pkcs15_cert_cmp_attribute(struct sc_pkcs11_session *session, void *object, CK_ATTRIBUTE_PTR attr) { @@ -3366,11 +3980,15 @@ pkcs15_cert_cmp_attribute(struct sc_pkcs11_session *session, size_t len, _len; sc_log(context, "pkcs15_cert_cmp_attribute() called"); + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GetAttributeValue"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; if (!fw_data) { sc_log(context, "pkcs15_cert_cmp_attribute() returns SC_ERROR_INTERNAL"); return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_GetAttributeValue"); } + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GetAttributeValue"); switch (attr->type) { /* Check the issuer/subject. Some pkcs11 callers (i.e. netscape) will pass @@ -3425,8 +4043,11 @@ struct sc_pkcs11_object_ops pkcs15_cert_ops = { NULL, /* sign */ NULL, /* unwrap_key */ NULL, /* decrypt */ + NULL, /* encrypt */ NULL, /* derive */ - NULL /* can_do */ + NULL, /* can_do */ + NULL, /* init_params */ + NULL /* wrap_key */ }; /* @@ -3464,9 +4085,13 @@ pkcs15_prkey_get_attribute(struct sc_pkcs11_session *session, sc_log(context, "pkcs15_prkey_get_attribute() called"); p11card = session->slot->p11card; + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GetAttributeValue"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_GetAttributeValue"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GetAttributeValue"); /* PKCS#11 requires us to supply CKA_MODULUS for private keys, * although that is not generally available from a smart card @@ -3482,7 +4107,7 @@ pkcs15_prkey_get_attribute(struct sc_pkcs11_session *session, */ if ((attr->type == CKA_MODULUS) || (attr->type == CKA_PUBLIC_EXPONENT) || ((attr->type == CKA_MODULUS_BITS) && (prkey->prv_p15obj->type == SC_PKCS15_TYPE_PRKEY_EC)) || - (attr->type == CKA_ECDSA_PARAMS)) { + (attr->type == CKA_EC_PARAMS)) { /* First see if we have an associated public key */ if (prkey->pub_data) { key = prkey->pub_data; @@ -3546,18 +4171,25 @@ pkcs15_prkey_get_attribute(struct sc_pkcs11_session *session, check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = (prkey->prv_info->access_flags & SC_PKCS15_PRKEY_ACCESS_LOCAL) != 0; break; - case CKA_ALWAYS_AUTHENTICATE: + case CKA_ALWAYS_AUTHENTICATE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); - *(CK_BBOOL*)attr->pValue = prkey->prv_p15obj->user_consent; + if (fw_data->p15_card->opts.pin_cache_ignore_user_consent) { + *(CK_BBOOL*)attr->pValue = CK_FALSE; + } else { + *(CK_BBOOL*)attr->pValue = prkey->prv_p15obj->user_consent >= 1 ? CK_TRUE : CK_FALSE; + } break; case CKA_PRIVATE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = (prkey->prv_p15obj->flags & SC_PKCS15_CO_FLAG_PRIVATE) != 0; break; case CKA_MODIFIABLE: + check_attribute_buffer(attr, sizeof(CK_BBOOL)); + *(CK_BBOOL*)attr->pValue = (prkey->prv_p15obj->flags & SC_PKCS15_CO_FLAG_MODIFIABLE) != 0; + break; case CKA_EXTRACTABLE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); - *(CK_BBOOL*)attr->pValue = FALSE; + *(CK_BBOOL *)attr->pValue = (prkey->prv_info->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE) != 0; break; case CKA_LABEL: len = strnlen(prkey->prv_p15obj->label, sizeof prkey->prv_p15obj->label); @@ -3573,6 +4205,12 @@ pkcs15_prkey_get_attribute(struct sc_pkcs11_session *session, case SC_PKCS15_TYPE_PRKEY_GOSTR3410: *(CK_KEY_TYPE*)attr->pValue = CKK_GOSTR3410; break; + case SC_PKCS15_TYPE_PRKEY_EDDSA: + *(CK_KEY_TYPE*)attr->pValue = CKK_EC_EDWARDS; + break; + case SC_PKCS15_TYPE_PRKEY_XEDDSA: + *(CK_KEY_TYPE*)attr->pValue = CKK_EC_MONTGOMERY; + break; case SC_PKCS15_TYPE_PRKEY_EC: *(CK_KEY_TYPE*)attr->pValue = CKK_EC; break; @@ -3604,6 +4242,11 @@ pkcs15_prkey_get_attribute(struct sc_pkcs11_session *session, case CKA_MODULUS_BITS: check_attribute_buffer(attr, sizeof(CK_ULONG)); switch (prkey->prv_p15obj->type) { + case SC_PKCS15_TYPE_PRKEY_EDDSA: + case SC_PKCS15_TYPE_PRKEY_XEDDSA: + /* TODO where to get field length ? */ + *(CK_ULONG *) attr->pValue = 255; + return CKR_OK; case SC_PKCS15_TYPE_PRKEY_EC: if (key) { if (key->u.ec.params.field_length > 0) @@ -3646,6 +4289,49 @@ pkcs15_prkey_get_attribute(struct sc_pkcs11_session *session, } + +static CK_RV +pkcs15_prkey_check_pss_param(CK_MECHANISM_PTR pMechanism, CK_ULONG hlen) +{ + CK_RSA_PKCS_PSS_PARAMS *pss_param; + int i; + const unsigned int hash_lens[5] = { 160, 256, 385, 512, 224 }; + const unsigned int hashes[5] = { CKM_SHA_1, CKM_SHA256, + CKM_SHA384, CKM_SHA512, CKM_SHA224 }; + + pss_param = (CK_RSA_PKCS_PSS_PARAMS *)pMechanism->pParameter; + + // Hash parameter must match length of data supplied for CKM_RSA_PKCS_PSS + for (i = 0; i < 5; i++) { + if (pss_param->hashAlg == hashes[i] + && hlen != hash_lens[i]/8) + return CKR_MECHANISM_PARAM_INVALID; + } + /* other aspects of pss params were already verified during SignInit */ + + return CKR_OK; +} + +static int mgf2flags(CK_RSA_PKCS_MGF_TYPE mgf) +{ + switch (mgf) { + case CKG_MGF1_SHA224: + return SC_ALGORITHM_MGF1_SHA224; + break; + case CKG_MGF1_SHA256: + return SC_ALGORITHM_MGF1_SHA256; + case CKG_MGF1_SHA384: + return SC_ALGORITHM_MGF1_SHA384; + case CKG_MGF1_SHA512: + return SC_ALGORITHM_MGF1_SHA512; + case CKG_MGF1_SHA1: + return SC_ALGORITHM_MGF1_SHA1; + default: + return -1; + } +} + + static CK_RV pkcs15_prkey_sign(struct sc_pkcs11_session *session, void *obj, CK_MECHANISM_PTR pMechanism, CK_BYTE_PTR pData, @@ -3655,15 +4341,20 @@ pkcs15_prkey_sign(struct sc_pkcs11_session *session, void *obj, struct pkcs15_prkey_object *prkey = (struct pkcs15_prkey_object *) obj; struct sc_pkcs11_card *p11card = session->slot->p11card; struct pkcs15_fw_data *fw_data = NULL; - int rv, flags = 0, prkey_has_path = 0; + CK_RV rv; + int flags = 0, rc; unsigned sign_flags = SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; sc_log(context, "Initiating signing operation, mechanism 0x%lx.", - pMechanism->mechanism); + pMechanism->mechanism); + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_Sign"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_Sign"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_Sign"); /* See which of the alternative keys supports signing */ while (prkey && !(prkey->prv_info->usage & sign_flags)) @@ -3672,33 +4363,95 @@ pkcs15_prkey_sign(struct sc_pkcs11_session *session, void *obj, if (prkey == NULL) return CKR_KEY_FUNCTION_NOT_PERMITTED; - if (prkey->prv_info->path.len || prkey->prv_info->path.aid.len) - prkey_has_path = 1; - switch (pMechanism->mechanism) { case CKM_RSA_PKCS: - flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE; + flags = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_HASH_NONE; break; case CKM_MD5_RSA_PKCS: - flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_MD5; + flags = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_HASH_MD5; break; case CKM_SHA1_RSA_PKCS: - flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA1; + flags = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_HASH_SHA1; + break; + case CKM_SHA224_RSA_PKCS: + flags = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_HASH_SHA224; break; case CKM_SHA256_RSA_PKCS: - flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA256; + flags = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_HASH_SHA256; break; case CKM_SHA384_RSA_PKCS: - flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA384; + flags = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_HASH_SHA384; break; case CKM_SHA512_RSA_PKCS: - flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA512; + flags = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_HASH_SHA512; break; case CKM_RIPEMD160_RSA_PKCS: - flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_RIPEMD160; + flags = SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01 | SC_ALGORITHM_RSA_HASH_RIPEMD160; break; case CKM_RSA_X_509: flags = SC_ALGORITHM_RSA_RAW; + break; + case CKM_RSA_PKCS_PSS: + flags = SC_ALGORITHM_RSA_PAD_PSS; + /* The hash was done outside of the module */ + flags |= SC_ALGORITHM_RSA_HASH_NONE; + /* Omitted parameter can use MGF1-SHA1 ? */ + if (pMechanism->pParameter == NULL) { + flags |= SC_ALGORITHM_MGF1_SHA1; + if (ulDataLen != SHA_DIGEST_LENGTH) + return CKR_MECHANISM_PARAM_INVALID; + break; + } + + /* Check the data length matches the selected hash */ + rv = pkcs15_prkey_check_pss_param(pMechanism, (int)ulDataLen); + if (rv != CKR_OK) { + sc_log(context, "Invalid data length for the selected " + "PSS parameters"); + return rv; + } + + /* The MGF parameter was already verified in SignInit() */ + flags |= mgf2flags(((CK_RSA_PKCS_PSS_PARAMS*)pMechanism->pParameter)->mgf); + + /* Assuming salt is the size of hash */ + break; + case CKM_SHA1_RSA_PKCS_PSS: + case CKM_SHA224_RSA_PKCS_PSS: + case CKM_SHA256_RSA_PKCS_PSS: + case CKM_SHA384_RSA_PKCS_PSS: + case CKM_SHA512_RSA_PKCS_PSS: + flags = SC_ALGORITHM_RSA_PAD_PSS; + /* Omitted parameter can use MGF1-SHA1 and SHA1 hash ? */ + if (pMechanism->pParameter == NULL) { + flags |= SC_ALGORITHM_RSA_HASH_SHA1; + flags |= SC_ALGORITHM_MGF1_SHA1; + break; + } + + switch (((CK_RSA_PKCS_PSS_PARAMS*)pMechanism->pParameter)->hashAlg) { + case CKM_SHA_1: + flags |= SC_ALGORITHM_RSA_HASH_SHA1; + break; + case CKM_SHA224: + flags |= SC_ALGORITHM_RSA_HASH_SHA224; + break; + case CKM_SHA256: + flags |= SC_ALGORITHM_RSA_HASH_SHA256; + break; + case CKM_SHA384: + flags |= SC_ALGORITHM_RSA_HASH_SHA384; + break; + case CKM_SHA512: + flags |= SC_ALGORITHM_RSA_HASH_SHA512; + break; + default: + return CKR_MECHANISM_PARAM_INVALID; + } + + /* The MGF parameter was already verified in SignInit() */ + flags |= mgf2flags(((CK_RSA_PKCS_PSS_PARAMS*)pMechanism->pParameter)->mgf); + break; case CKM_GOSTR3410: flags = SC_ALGORITHM_GOSTR3410_HASH_NONE; @@ -3706,51 +4459,123 @@ pkcs15_prkey_sign(struct sc_pkcs11_session *session, void *obj, case CKM_GOSTR3410_WITH_GOSTR3411: flags = SC_ALGORITHM_GOSTR3410_HASH_GOSTR3411; break; + case CKM_EDDSA: + flags = SC_ALGORITHM_EDDSA_RAW; + break; + case CKM_XEDDSA: + flags = SC_ALGORITHM_XEDDSA_RAW; + break; case CKM_ECDSA: flags = SC_ALGORITHM_ECDSA_HASH_NONE; break; case CKM_ECDSA_SHA1: flags = SC_ALGORITHM_ECDSA_HASH_SHA1; break; + case CKM_ECDSA_SHA224: + flags = SC_ALGORITHM_ECDSA_HASH_SHA224; + break; + case CKM_ECDSA_SHA256: + flags = SC_ALGORITHM_ECDSA_HASH_SHA256; + break; + case CKM_ECDSA_SHA384: + flags = SC_ALGORITHM_ECDSA_HASH_SHA384; + break; + case CKM_ECDSA_SHA512: + flags = SC_ALGORITHM_ECDSA_HASH_SHA512; + break; default: sc_log(context, "DEE - need EC for %lu", pMechanism->mechanism); return CKR_MECHANISM_INVALID; } - rv = sc_lock(p11card->card); - if (rv < 0) - return sc_to_cryptoki_error(rv, "C_Sign"); + rc = sc_lock(p11card->card); + if (rc < 0) + return sc_to_cryptoki_error(rc, "C_Sign"); sc_log(context, "Selected flags %X. Now computing signature for %lu bytes. %lu bytes reserved.", flags, ulDataLen, *pulDataLen); - rv = sc_pkcs15_compute_signature(fw_data->p15_card, prkey->prv_p15obj, flags, - pData, ulDataLen, pSignature, *pulDataLen); - if (rv < 0 && !sc_pkcs11_conf.lock_login && !prkey_has_path) { - /* If private key PKCS#15 object do not have 'path' attribute, - * and if PKCS#11 login session is not locked, - * the compute signature could fail because of concurent access to the card - * by other application that could change the current DF. - * In this particular case try to 'reselect' application DF. - */ - if (reselect_app_df(fw_data->p15_card) == SC_SUCCESS) - rv = sc_pkcs15_compute_signature(fw_data->p15_card, prkey->prv_p15obj, flags, - pData, ulDataLen, pSignature, *pulDataLen); - } + rc = sc_pkcs15_compute_signature(fw_data->p15_card, prkey->prv_p15obj, flags, + pData, ulDataLen, pSignature, *pulDataLen, pMechanism); sc_unlock(p11card->card); - sc_log(context, "Sign complete. Result %d.", rv); + sc_log(context, "Sign complete. Result %d.", rc); - if (rv > 0) { - *pulDataLen = rv; + if (rc > 0) { + *pulDataLen = rc; return CKR_OK; } - return sc_to_cryptoki_error(rv, "C_Sign"); + return sc_to_cryptoki_error(rc, "C_Sign"); } +static CK_RV +pkcs15_prkey_unwrap(struct sc_pkcs11_session *session, void *obj, + CK_MECHANISM_PTR pMechanism, CK_BYTE_PTR pWrappedKey, + CK_ULONG ulWrappedKeyLen, + void *targetKey) +{ + struct sc_pkcs11_card *p11card = session->slot->p11card; + struct pkcs15_fw_data *fw_data = NULL; + struct pkcs15_prkey_object *prkey = (struct pkcs15_prkey_object *) obj; + struct pkcs15_any_object *targetKeyObj = (struct pkcs15_any_object *) targetKey; + int rv, flags = 0; + + sc_log(context, "Initiating unwrapping with private key."); + + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_UnwrapKey"); + fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; + if (!fw_data) + return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_UnwrapKey"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_UnwrapKey"); + + if (pMechanism == NULL || pWrappedKey == NULL || ulWrappedKeyLen == 0 || targetKeyObj == NULL) { + sc_log(context, "One or more of mandatory arguments were NULL."); + return CKR_ARGUMENTS_BAD; + } + + /* See which of the alternative keys supports unwrap */ + while (prkey && !(prkey->prv_info->usage & SC_PKCS15_PRKEY_USAGE_UNWRAP)) + prkey = prkey->prv_next; + + if (prkey == NULL) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + sc_log(context, "Using mechanism %lx.", pMechanism->mechanism); + + /* Select the proper padding mechanism */ + switch (pMechanism->mechanism) { + case CKM_RSA_PKCS: + flags |= SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02; + break; + case CKM_RSA_X_509: + flags |= SC_ALGORITHM_RSA_RAW; + break; + default: + return CKR_MECHANISM_INVALID; + } + + rv = sc_lock(p11card->card); + + if (rv < 0) + return sc_to_cryptoki_error(rv, "C_UnwrapKey"); + + /* Call the card to do the unwrap operation */ + rv = sc_pkcs15_unwrap(fw_data->p15_card, prkey->prv_p15obj, targetKeyObj->p15_object, flags, + pWrappedKey, ulWrappedKeyLen, NULL, 0); + + sc_unlock(p11card->card); + + if (rv < 0) + return sc_to_cryptoki_error(rv, "C_UnwrapKey"); + + return CKR_OK; +} + static CK_RV pkcs15_prkey_decrypt(struct sc_pkcs11_session *session, void *obj, CK_MECHANISM_PTR pMechanism, @@ -3761,13 +4586,33 @@ pkcs15_prkey_decrypt(struct sc_pkcs11_session *session, void *obj, struct pkcs15_fw_data *fw_data = NULL; struct pkcs15_prkey_object *prkey; unsigned char decrypted[512]; /* FIXME: Will not work for keys above 4096 bits */ - int buff_too_small, rv, flags = 0, prkey_has_path = 0; + int rv, flags = 0; + CK_ULONG mask, good, rv_pkcs11; + + if (pulDataLen == NULL) { + /* This is call from the C_DecyptInit function */ + sc_log(context, "C_DecryptInit..."); + return CKR_OK; + } + if (pEncryptedData == NULL && ulEncryptedDataLen == 0) { + /* This is call from the C_DecryptFinalize function */ + sc_log(context, "C_DecryptFinalize..."); + *pulDataLen = 0; + return CKR_OK; + } + /* DecryptUpdate: we assume this code is called only once per session, either + * from the C_Decrypt function or from an application using the C_DecryptUpdate call + */ sc_log(context, "Initiating decryption."); + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_Decrypt"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_Decrypt"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_Decrypt"); /* See which of the alternative keys supports decrypt */ prkey = (struct pkcs15_prkey_object *) obj; @@ -3776,17 +4621,47 @@ pkcs15_prkey_decrypt(struct sc_pkcs11_session *session, void *obj, if (prkey == NULL) return CKR_KEY_FUNCTION_NOT_PERMITTED; - if (prkey->prv_info->path.len || prkey->prv_info->path.aid.len) - prkey_has_path = 1; - /* Select the proper padding mechanism */ switch (pMechanism->mechanism) { case CKM_RSA_PKCS: - flags |= SC_ALGORITHM_RSA_PAD_PKCS1; + flags |= SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02; break; case CKM_RSA_X_509: flags |= SC_ALGORITHM_RSA_RAW; break; + case CKM_RSA_PKCS_OAEP: + flags |= SC_ALGORITHM_RSA_PAD_OAEP; + + /* Omitted parameter can use MGF1-SHA1 and SHA1 hash ? */ + if (pMechanism->pParameter == NULL) { + flags |= SC_ALGORITHM_RSA_HASH_SHA1; + flags |= SC_ALGORITHM_MGF1_SHA1; + break; + } + + switch (((CK_RSA_PKCS_OAEP_PARAMS*)pMechanism->pParameter)->hashAlg) { + case CKM_SHA_1: + flags |= SC_ALGORITHM_RSA_HASH_SHA1; + break; + case CKM_SHA224: + flags |= SC_ALGORITHM_RSA_HASH_SHA224; + break; + case CKM_SHA256: + flags |= SC_ALGORITHM_RSA_HASH_SHA256; + break; + case CKM_SHA384: + flags |= SC_ALGORITHM_RSA_HASH_SHA384; + break; + case CKM_SHA512: + flags |= SC_ALGORITHM_RSA_HASH_SHA512; + break; + default: + return CKR_MECHANISM_PARAM_INVALID; + } + + /* The MGF parameter was already verified in SignInit() */ + flags |= mgf2flags(((CK_RSA_PKCS_OAEP_PARAMS*)pMechanism->pParameter)->mgf); + break; default: return CKR_MECHANISM_INVALID; } @@ -3796,29 +4671,48 @@ pkcs15_prkey_decrypt(struct sc_pkcs11_session *session, void *obj, return sc_to_cryptoki_error(rv, "C_Decrypt"); rv = sc_pkcs15_decipher(fw_data->p15_card, prkey->prv_p15obj, flags, - pEncryptedData, ulEncryptedDataLen, decrypted, sizeof(decrypted)); - - if (rv < 0 && !sc_pkcs11_conf.lock_login && !prkey_has_path) - if (reselect_app_df(fw_data->p15_card) == SC_SUCCESS) - rv = sc_pkcs15_decipher(fw_data->p15_card, prkey->prv_p15obj, flags, - pEncryptedData, ulEncryptedDataLen, decrypted, sizeof(decrypted)); + pEncryptedData, ulEncryptedDataLen, decrypted, sizeof(decrypted), pMechanism); sc_unlock(p11card->card); - sc_log(context, "Decryption complete. Result %d.", rv); + sc_log(context, "Decryption complete."); - if (rv < 0) - return sc_to_cryptoki_error(rv, "C_Decrypt"); + /* Handle following code in constant-time + * to prevent Marvin attack for PKCS#1 v1.5 padding. */ - buff_too_small = (*pulDataLen < (CK_ULONG)rv); - *pulDataLen = rv; - if (pData == NULL_PTR) - return CKR_OK; - if (buff_too_small) - return CKR_BUFFER_TOO_SMALL; - memcpy(pData, decrypted, *pulDataLen); + /* only padding error must be handled in constant-time way, + * other error can be returned straight away */ + if ((~constant_time_eq_s((size_t)rv, (size_t)SC_ERROR_WRONG_PADDING) & constant_time_lt_s(sizeof(decrypted), (size_t)rv))) + return sc_to_cryptoki_error(rv, "C_Decrypt"); - return CKR_OK; + /* check rv for padding error */ + good = ~constant_time_eq_i(rv, SC_ERROR_WRONG_PADDING); + rv_pkcs11 = sc_to_cryptoki_error(SC_ERROR_WRONG_PADDING, "C_Decrypt"); + rv_pkcs11 = constant_time_select_s(good, CKR_OK, rv_pkcs11); + + if (pData == NULL_PTR) { + /* set length only if no error */ + *pulDataLen = constant_time_select_s(good, rv, *pulDataLen); + /* return error only if original rv < 0 */ + return rv_pkcs11; + } + + /* check whether *pulDataLen < rv and set return value for small output buffer */ + mask = good & constant_time_lt_s(*pulDataLen, rv); + rv_pkcs11 = constant_time_select_s(mask, CKR_BUFFER_TOO_SMALL, rv_pkcs11); + good &= ~mask; + + /* move everything from decrypted into out buffer constant-time, if rv is ok */ + for (CK_ULONG i = 0; i < *pulDataLen; i++) { /* iterate over whole pData to not disclose real depadded length */ + CK_ULONG msg_index; + mask = good & constant_time_lt_s(i, sizeof(decrypted)); /* i should be in the bounds of decrypted */ + mask &= constant_time_lt_s(i, constant_time_select_s(good, rv, 0)); /* check that is in bounds of depadded message */ + msg_index = constant_time_select_s(mask, i, 0); + pData[i] = constant_time_select_8(mask, decrypted[msg_index], pData[i]); + } + *pulDataLen = constant_time_select_s(good, rv, *pulDataLen); + /* do not log error code to prevent side channel attack */ + return rv_pkcs11; } @@ -3831,19 +4725,20 @@ pkcs15_prkey_derive(struct sc_pkcs11_session *session, void *obj, struct sc_pkcs11_card *p11card = session->slot->p11card; struct pkcs15_fw_data *fw_data = NULL; struct pkcs15_prkey_object *prkey = (struct pkcs15_prkey_object *) obj; - int need_unlock = 0, prkey_has_path = 0; + int need_unlock = 0; int rv, flags = 0; CK_BYTE_PTR pSeedData = NULL; CK_ULONG ulSeedDataLen = 0; sc_log(context, "Initiating derivation"); + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_DeriveKey"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_DeriveKey"); - - sc_log(context, "derivation %p %p %p %p %lu %p %lu", session, obj, - pMechanism, pParameters, ulParametersLen, pData, *pulDataLen); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_DeriveKey"); /* See which of the alternative keys supports derivation */ while (prkey && !(prkey->prv_info->usage & SC_PKCS15_PRKEY_USAGE_DERIVE)) @@ -3852,14 +4747,11 @@ pkcs15_prkey_derive(struct sc_pkcs11_session *session, void *obj, if (prkey == NULL) return CKR_KEY_FUNCTION_NOT_PERMITTED; - if (prkey->prv_info->path.len || prkey->prv_info->path.aid.len) - prkey_has_path = 1; - if (pData != NULL && *pulDataLen > 0) { /* TODO DEE only test for NULL? */ - need_unlock = 1; - rv = sc_lock(p11card->card); - if (rv < 0) - return sc_to_cryptoki_error(rv, "C_DeriveKey"); + need_unlock = 1; + rv = sc_lock(p11card->card); + if (rv < 0) + return sc_to_cryptoki_error(rv, "C_DeriveKey"); } /* TODO DEE This may not be the place to get the parameters, @@ -3867,21 +4759,21 @@ pkcs15_prkey_derive(struct sc_pkcs11_session *session, void *obj, * RSA parameters would be null. */ switch (prkey->base.p15_object->type) { - case SC_PKCS15_TYPE_PRKEY_EC: + case SC_PKCS15_TYPE_PRKEY_EC: + case SC_PKCS15_TYPE_PRKEY_XEDDSA: { - CK_ECDH1_DERIVE_PARAMS * ecdh_params = (CK_ECDH1_DERIVE_PARAMS *) pParameters; - ulSeedDataLen = ecdh_params->ulPublicDataLen; - pSeedData = ecdh_params->pPublicData; + CK_ECDH1_DERIVE_PARAMS * ecdh_params = (CK_ECDH1_DERIVE_PARAMS *) pParameters; + ulSeedDataLen = ecdh_params->ulPublicDataLen; + pSeedData = ecdh_params->pPublicData; + flags = SC_ALGORITHM_ECDH_CDH_RAW; } break; } + size_t len = *pulDataLen; rv = sc_pkcs15_derive(fw_data->p15_card, prkey->prv_p15obj, flags, - pSeedData, ulSeedDataLen, pData, pulDataLen); - if (rv < 0 && !sc_pkcs11_conf.lock_login && !prkey_has_path && need_unlock) - if (reselect_app_df(fw_data->p15_card) == SC_SUCCESS) - rv = sc_pkcs15_derive(fw_data->p15_card, prkey->prv_p15obj, flags, - pSeedData, ulSeedDataLen, pData, pulDataLen); + pSeedData, ulSeedDataLen, pData, &len); + *pulDataLen = len; /* this may have been a request for size */ @@ -3908,15 +4800,22 @@ pkcs15_prkey_can_do(struct sc_pkcs11_session *session, void *obj, struct sc_supported_algo_info *token_algos = NULL; int ii, jj; + LOG_FUNC_CALLED(context); + sc_log(context, "check hardware capabilities: CK_MECHANISM_TYPE=0x%lx (CKM) and CKF_xxx=0x%x", mech_type, flags); + if (!prkey || !prkey->prv_info) - return CKR_KEY_FUNCTION_NOT_PERMITTED; + LOG_FUNC_RETURN(context, CKR_KEY_FUNCTION_NOT_PERMITTED); pkinfo = prkey->prv_info; - /* Return in there are no usage algorithms specified for this key. */ + /* Return if there are no usage algorithms specified for this key. */ if (!pkinfo->algo_refs[0]) - return CKR_FUNCTION_NOT_SUPPORTED; + LOG_FUNC_RETURN(context, CKR_FUNCTION_NOT_SUPPORTED); + if (!p11card) + LOG_FUNC_RETURN(context, CKR_FUNCTION_NOT_SUPPORTED); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; + if (!fw_data->p15_card) + LOG_FUNC_RETURN(context, CKR_FUNCTION_NOT_SUPPORTED); token_algos = &fw_data->p15_card->tokeninfo->supported_algos[0]; for (ii=0;iialgo_refs[ii];ii++) { @@ -3925,7 +4824,7 @@ pkcs15_prkey_can_do(struct sc_pkcs11_session *session, void *obj, if (pkinfo->algo_refs[ii] == (token_algos + jj)->reference) break; if ((jj == SC_MAX_SUPPORTED_ALGORITHMS) || !(token_algos + jj)->reference) - return CKR_GENERAL_ERROR; + LOG_FUNC_RETURN(context, CKR_GENERAL_ERROR); if ((token_algos + jj)->mechanism != mech_type) continue; @@ -3940,8 +4839,83 @@ pkcs15_prkey_can_do(struct sc_pkcs11_session *session, void *obj, } if (ii == SC_MAX_SUPPORTED_ALGORITHMS || !pkinfo->algo_refs[ii]) - return CKR_MECHANISM_INVALID; + LOG_FUNC_RETURN(context, CKR_MECHANISM_INVALID); + + LOG_FUNC_RETURN(context, CKR_OK); +} + + +static CK_RV +pkcs15_prkey_init_params(struct sc_pkcs11_session *session, + CK_MECHANISM_PTR pMechanism) +{ + const CK_RSA_PKCS_PSS_PARAMS *pss_params; + unsigned int expected_hash = 0, i; + const unsigned int hashes[5] = { CKM_SHA_1, CKM_SHA256, + CKM_SHA384, CKM_SHA512, CKM_SHA224 }; + const CK_RSA_PKCS_OAEP_PARAMS *oaep_params; + + switch (pMechanism->mechanism) { + case CKM_RSA_PKCS_PSS: + case CKM_SHA1_RSA_PKCS_PSS: + case CKM_SHA224_RSA_PKCS_PSS: + case CKM_SHA256_RSA_PKCS_PSS: + case CKM_SHA384_RSA_PKCS_PSS: + case CKM_SHA512_RSA_PKCS_PSS: + if (!pMechanism->pParameter || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS)) + return CKR_MECHANISM_PARAM_INVALID; + + pss_params = (CK_RSA_PKCS_PSS_PARAMS*)pMechanism->pParameter; + if (pss_params->mgf < CKG_MGF1_SHA1 || pss_params->mgf > CKG_MGF1_SHA224) + return CKR_MECHANISM_PARAM_INVALID; + + /* The hashAlg field can have any value for CKM_RSA_PKCS_PSS and must be + * used again in the PSS padding; for the other mechanisms it strictly + * must match the padding declared in the mechanism. + */ + if (pMechanism->mechanism == CKM_SHA1_RSA_PKCS_PSS) { + expected_hash = CKM_SHA_1; + } else if (pMechanism->mechanism == CKM_SHA224_RSA_PKCS_PSS) { + expected_hash = CKM_SHA224; + } else if (pMechanism->mechanism == CKM_SHA256_RSA_PKCS_PSS) { + expected_hash = CKM_SHA256; + } else if (pMechanism->mechanism == CKM_SHA384_RSA_PKCS_PSS) { + expected_hash = CKM_SHA384; + } else if (pMechanism->mechanism == CKM_SHA512_RSA_PKCS_PSS) { + expected_hash = CKM_SHA512; + } else if (pMechanism->mechanism == CKM_RSA_PKCS_PSS) { + for (i = 0; i < 5; ++i) { + if (hashes[i] == pss_params->hashAlg) { + expected_hash = hashes[i]; + } + } + } + + if (expected_hash != pss_params->hashAlg) + return CKR_MECHANISM_PARAM_INVALID; + break; + case CKM_RSA_PKCS_OAEP: + if (!pMechanism->pParameter || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_OAEP_PARAMS)) + return CKR_MECHANISM_PARAM_INVALID; + + oaep_params = (CK_RSA_PKCS_OAEP_PARAMS*)pMechanism->pParameter; + switch (oaep_params->mgf) { + case CKG_MGF1_SHA1: + case CKG_MGF1_SHA224: + case CKG_MGF1_SHA256: + case CKG_MGF1_SHA384: + case CKG_MGF1_SHA512: + /* OK */ + break; + default: + return CKR_MECHANISM_PARAM_INVALID; + } + /* TODO is there something more to check */ + break; + } return CKR_OK; } @@ -3954,10 +4928,13 @@ struct sc_pkcs11_object_ops pkcs15_prkey_ops = { pkcs15_any_destroy, NULL, /* get_size */ pkcs15_prkey_sign, - NULL, /* unwrap */ + pkcs15_prkey_unwrap, pkcs15_prkey_decrypt, - pkcs15_prkey_derive, - pkcs15_prkey_can_do + NULL, /* encrypt */ + pkcs15_prkey_derive, + pkcs15_prkey_can_do, + pkcs15_prkey_init_params, + NULL /* wrap_key */ }; /* @@ -3998,15 +4975,21 @@ pkcs15_pubkey_get_attribute(struct sc_pkcs11_session *session, void *object, CK_ p11card = session->slot->p11card; cert = pubkey->pub_genfrom; + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GetAttributeValue"); fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_GetAttributeValue"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GetAttributeValue"); + /* We may need to get these from cert */ switch (attr->type) { case CKA_MODULUS: case CKA_MODULUS_BITS: case CKA_VALUE: case CKA_SPKI: + case CKA_PUBLIC_KEY_INFO: case CKA_PUBLIC_EXPONENT: case CKA_EC_PARAMS: case CKA_EC_POINT: @@ -4033,9 +5016,9 @@ pkcs15_pubkey_get_attribute(struct sc_pkcs11_session *session, void *object, CK_ case CKA_LOCAL: check_attribute_buffer(attr, sizeof(CK_BBOOL)); if (pubkey->pub_info) - *(CK_BBOOL*)attr->pValue = (pubkey->pub_info->access_flags & SC_PKCS15_PRKEY_ACCESS_LOCAL) != 0; + *(CK_BBOOL*)attr->pValue = (pubkey->pub_info->access_flags & SC_PKCS15_PRKEY_ACCESS_LOCAL) != 0; else /* no pub_info structure, falling back to TRUE */ - *(CK_BBOOL*)attr->pValue = TRUE; + *(CK_BBOOL*)attr->pValue = TRUE; break; case CKA_PRIVATE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); @@ -4047,9 +5030,20 @@ pkcs15_pubkey_get_attribute(struct sc_pkcs11_session *session, void *object, CK_ return CKR_ATTRIBUTE_TYPE_INVALID; break; case CKA_MODIFIABLE: + check_attribute_buffer(attr, sizeof(CK_BBOOL)); + if (pubkey->pub_p15obj) + *(CK_BBOOL*)attr->pValue = (pubkey->pub_p15obj->flags & SC_PKCS15_CO_FLAG_MODIFIABLE) != 0; + else if (cert && cert->cert_p15obj) + *(CK_BBOOL*)attr->pValue = (cert->pub_p15obj->flags & SC_PKCS15_CO_FLAG_MODIFIABLE) != 0; + else + return CKR_ATTRIBUTE_TYPE_INVALID; + break; case CKA_EXTRACTABLE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); - *(CK_BBOOL*)attr->pValue = FALSE; + if (pubkey->pub_info) + *(CK_BBOOL*)attr->pValue = (pubkey->pub_info->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE) != 0; + else /* no pub_info structure, falling back to TRUE */ + *(CK_BBOOL*)attr->pValue = TRUE; break; case CKA_LABEL: if (pubkey->pub_p15obj) { @@ -4072,6 +5066,10 @@ pkcs15_pubkey_get_attribute(struct sc_pkcs11_session *session, void *object, CK_ /* even if we do not, we should not assume RSA */ if (pubkey->pub_data && pubkey->pub_data->algorithm == SC_ALGORITHM_GOSTR3410) *(CK_KEY_TYPE*)attr->pValue = CKK_GOSTR3410; + else if (pubkey->pub_data && pubkey->pub_data->algorithm == SC_ALGORITHM_EDDSA) + *(CK_KEY_TYPE*)attr->pValue = CKK_EC_EDWARDS; + else if (pubkey->pub_data && pubkey->pub_data->algorithm == SC_ALGORITHM_XEDDSA) + *(CK_KEY_TYPE*)attr->pValue = CKK_EC_MONTGOMERY; else if (pubkey->pub_data && pubkey->pub_data->algorithm == SC_ALGORITHM_EC) *(CK_KEY_TYPE*)attr->pValue = CKK_EC; else @@ -4109,7 +5107,7 @@ pkcs15_pubkey_get_attribute(struct sc_pkcs11_session *session, void *object, CK_ return get_modulus_bits(pubkey->pub_data, attr); case CKA_PUBLIC_EXPONENT: return get_public_exponent(pubkey->pub_data, attr); - /* + /* * PKCS#11 does not define a CKA_VALUE for a CKO_PUBLIC_KEY. * OpenSC does, but it is not consistent it what it returns * Internally to do verify, with OpenSSL, we need a SPKI that @@ -4118,26 +5116,25 @@ pkcs15_pubkey_get_attribute(struct sc_pkcs11_session *session, void *object, CK_ */ case CKA_VALUE: case CKA_SPKI: + case CKA_PUBLIC_KEY_INFO: - if (attr->type != CKA_SPKI && pubkey->pub_info && pubkey->pub_info->direct.raw.value && pubkey->pub_info->direct.raw.len) { + if (attr->type != CKA_SPKI && attr->type != CKA_PUBLIC_KEY_INFO && pubkey->pub_info && pubkey->pub_info->direct.raw.value && pubkey->pub_info->direct.raw.len) { check_attribute_buffer(attr, pubkey->pub_info->direct.raw.len); memcpy(attr->pValue, pubkey->pub_info->direct.raw.value, pubkey->pub_info->direct.raw.len); - } - else if (pubkey->pub_info && pubkey->pub_info->direct.spki.value && pubkey->pub_info->direct.spki.len) { + } else if (pubkey->pub_info && pubkey->pub_info->direct.spki.value && pubkey->pub_info->direct.spki.len) { check_attribute_buffer(attr, pubkey->pub_info->direct.spki.len); memcpy(attr->pValue, pubkey->pub_info->direct.spki.value, pubkey->pub_info->direct.spki.len); - } - else if (pubkey->pub_data) { + } else if (pubkey->pub_data) { unsigned char *value = NULL; size_t len; - if (attr->type != CKA_SPKI) { + if (attr->type != CKA_SPKI && attr->type != CKA_PUBLIC_KEY_INFO) { if (sc_pkcs15_encode_pubkey(context, pubkey->pub_data, &value, &len)) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_GetAttributeValue"); - } else { - if (sc_pkcs15_encode_pubkey_as_spki(context, pubkey->pub_data, &value, &len)) + } else { + if (sc_pkcs15_encode_pubkey_as_spki(context, pubkey->pub_data, &value, &len)) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_GetAttributeValue"); - } + } if (attr->pValue == NULL_PTR) { attr->ulValueLen = len; @@ -4153,16 +5150,13 @@ pkcs15_pubkey_get_attribute(struct sc_pkcs11_session *session, void *object, CK_ memcpy(attr->pValue, value, len); free(value); - } - else if (attr->type != CKA_SPKI && pubkey->base.p15_object && pubkey->base.p15_object->content.value && pubkey->base.p15_object->content.len) { + } else if (attr->type != CKA_SPKI && attr->type != CKA_PUBLIC_KEY_INFO && pubkey->base.p15_object && pubkey->base.p15_object->content.value && pubkey->base.p15_object->content.len) { check_attribute_buffer(attr, pubkey->base.p15_object->content.len); memcpy(attr->pValue, pubkey->base.p15_object->content.value, pubkey->base.p15_object->content.len); - } - else if (cert && cert->cert_data) { + } else if (cert && cert->cert_data) { check_attribute_buffer(attr, cert->cert_data->data.len); memcpy(attr->pValue, cert->cert_data->data.value, cert->cert_data->data.len); - } - else { + } else { return CKR_ATTRIBUTE_TYPE_INVALID; } break; @@ -4193,8 +5187,11 @@ struct sc_pkcs11_object_ops pkcs15_pubkey_ops = { NULL, /* sign */ NULL, /* unwrap_key */ NULL, /* decrypt */ + NULL, /* ecrypt */ NULL, /* derive */ - NULL /* can_do */ + NULL, /* can_do */ + NULL, /* init_params */ + NULL /* wrap_key */ }; @@ -4216,27 +5213,40 @@ pkcs15_dobj_set_attribute(struct sc_pkcs11_session *session, } -static int +static CK_RV pkcs15_dobj_get_value(struct sc_pkcs11_session *session, struct pkcs15_data_object *dobj, struct sc_pkcs15_data **out_data) { struct sc_pkcs11_card *p11card = session->slot->p11card; struct pkcs15_fw_data *fw_data = NULL; - struct sc_card *card = session->slot->p11card->card; + struct sc_card *card; int rv; + int private_obj; + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GetAttributeValue"); + card = session->slot->p11card->card; if (!out_data) return SC_ERROR_INVALID_ARGUMENTS; + if (dobj->info->data.len == 0) + /* CKA_VALUE is empty we may need to read it */ + { + *out_data = NULL; + } + fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; if (!fw_data) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_GetAttributeValue"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_GetAttributeValue"); rv = sc_lock(card); if (rv < 0) return sc_to_cryptoki_error(rv, "C_GetAttributeValue"); - rv = sc_pkcs15_read_data_object(fw_data->p15_card, dobj->info, out_data); + private_obj = dobj->data_flags; + rv = sc_pkcs15_read_data_object(fw_data->p15_card, dobj->info, private_obj, out_data); sc_unlock(card); if (rv < 0) @@ -4252,10 +5262,6 @@ data_value_to_attr(CK_ATTRIBUTE_PTR attr, struct sc_pkcs15_data *data) if (!attr || !data) return CKR_ATTRIBUTE_VALUE_INVALID; - sc_log(context, - "data_value_to_attr(): data(%p,len:%"SC_FORMAT_LEN_SIZE_T"u)", - data, data->data_len); - check_attribute_buffer(attr, data->data_len); memcpy(attr->pValue, data->data, data->data_len); return CKR_OK; @@ -4329,9 +5335,17 @@ pkcs15_dobj_get_attribute(struct sc_pkcs11_session *session, void *object, CK_AT free(buf); break; case CKA_VALUE: + /* if CKA_VALUE is empty, sets data to NULL */ rv = pkcs15_dobj_get_value(session, dobj, &data); - if (rv == CKR_OK) - rv = data_value_to_attr(attr, data); + if (rv == CKR_OK) { + if (data) { + rv = data_value_to_attr(attr, data); + } + else { + attr->ulValueLen = 0; + attr->pValue = NULL_PTR; + } + } if (data) { free(data->data); free(data); @@ -4356,17 +5370,100 @@ struct sc_pkcs11_object_ops pkcs15_dobj_ops = { NULL, /* sign */ NULL, /* unwrap_key */ NULL, /* decrypt */ + NULL, /* encrypt */ + NULL, /* derive */ + NULL, /* can_do */ + NULL, /* init_params */ + NULL /* wrap_key */ +}; + +/* PKCS#15 Data Object*/ +static void +pkcs15_profile_release(void *obj) +{ + struct pkcs15_any_object *object = (struct pkcs15_any_object *) obj; + struct sc_pkcs15_object *p15_obj = object->p15_object; + + if (__pkcs15_release_object((struct pkcs15_any_object *) obj) < 1) { + /* This is not a real pkcs15 object. We need to free it here */ + free(p15_obj); + } +} + + +static CK_RV +pkcs15_profile_set_attribute(struct sc_pkcs11_session *session, + void *object, CK_ATTRIBUTE_PTR attr) +{ + /* Profile object is not writable */ + return CKR_ACTION_PROHIBITED; +} + +static CK_RV +pkcs15_profile_get_attribute(struct sc_pkcs11_session *session, void *object, CK_ATTRIBUTE_PTR attr) +{ + struct pkcs15_profile_object *pobj = (struct pkcs15_profile_object*) object; + + sc_log(context, "pkcs15_profile_get_attribute() called"); + switch (attr->type) { + case CKA_CLASS: + check_attribute_buffer(attr, sizeof(CK_OBJECT_CLASS)); + *(CK_OBJECT_CLASS*)attr->pValue = CKO_PROFILE; + break; + case CKA_PRIVATE: + /* This is needed internally for the object to be visible */ + check_attribute_buffer(attr, sizeof(CK_BBOOL)); + *(CK_BBOOL*)attr->pValue = CK_FALSE; + break; + case CKA_PROFILE_ID: + /* TODO */ + check_attribute_buffer(attr, sizeof(CK_ULONG)); + *(CK_ULONG*)attr->pValue = pobj->profile_id; + break; + case CKA_TOKEN: + check_attribute_buffer(attr, sizeof(CK_BBOOL)); + *(CK_BBOOL*)attr->pValue = CK_TRUE; + break; + default: + return CKR_ATTRIBUTE_TYPE_INVALID; + } + + return CKR_OK; +} +struct sc_pkcs11_object_ops pkcs15_profile_ops = { + pkcs15_profile_release, + pkcs15_profile_set_attribute, + pkcs15_profile_get_attribute, + sc_pkcs11_any_cmp_attribute, + pkcs15_any_destroy, + NULL, /* get_size */ + NULL, /* sign */ + NULL, /* unwrap_key */ + NULL, /* decrypt */ + NULL, /* encrypt */ NULL, /* derive */ - NULL /* can_do */ + NULL, /* can_do */ + NULL, /* init_params */ + NULL /* wrap_key */ }; + /* PKCS#15 Secret Key Objects */ /* TODO Currently only session objects */ static void pkcs15_skey_release(void *object) { - __pkcs15_release_object((struct pkcs15_any_object *) object); + struct pkcs15_skey_object *skey = (struct pkcs15_skey_object *)object; + struct sc_pkcs15_skey_info *skey_info = skey->info; + struct sc_pkcs15_object *p15obj = skey->skey_p15obj; + + if (__pkcs15_release_object((struct pkcs15_any_object *)skey) == 0) { + if (p15obj->session_object) { + sc_pkcs15_free_skey_info(skey_info); + free(p15obj); + } + } } @@ -4384,6 +5481,7 @@ pkcs15_skey_set_attribute(struct sc_pkcs11_session *session, switch (attr->type) { case CKA_VALUE: if (attr->pValue) { + free(skey->info->data.value); skey->info->data.value = calloc(1,attr->ulValueLen); if (!skey->info->data.value) return CKR_HOST_MEMORY; @@ -4412,17 +5510,16 @@ pkcs15_skey_get_attribute(struct sc_pkcs11_session *session, *(CK_OBJECT_CLASS*)attr->pValue = CKO_SECRET_KEY; break; case CKA_TOKEN: - /*TODO DEE change if on card skeys are supported */ check_attribute_buffer(attr, sizeof(CK_BBOOL)); - *(CK_BBOOL*)attr->pValue = FALSE; + *(CK_BBOOL*)attr->pValue = skey->base.p15_object->session_object == 0 ? CK_TRUE : CK_FALSE; break; case CKA_PRIVATE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); - *(CK_BBOOL*)attr->pValue = (skey->base.p15_object->flags & SC_PKCS15_CO_FLAG_PRIVATE) != 0; + *(CK_BBOOL*)attr->pValue = (skey->base.p15_object->flags & SC_PKCS15_CO_FLAG_PRIVATE) != 0 ? CK_TRUE : CK_FALSE; break; case CKA_MODIFIABLE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); - *(CK_BBOOL*)attr->pValue = (skey->base.p15_object->flags & 0x02) != 0; + *(CK_BBOOL*)attr->pValue = (skey->base.p15_object->flags & 0x02) != 0 ? CK_TRUE : CK_FALSE; /*TODO Why no definition of the flag */ break; case CKA_LABEL: @@ -4456,9 +5553,43 @@ pkcs15_skey_get_attribute(struct sc_pkcs11_session *session, check_attribute_buffer(attr, skey->info->id.len); memcpy(attr->pValue, skey->info->id.value, skey->info->id.len); break; + case CKA_EXTRACTABLE: + check_attribute_buffer(attr, sizeof(CK_BBOOL)); + *(CK_BBOOL*)attr->pValue = (((skey->info->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE) == SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE) + && (skey->info->access_flags & SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE) == 0 + && (skey->info->access_flags & SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE) == 0) ? CK_TRUE : CK_FALSE; + break; + case CKA_ALWAYS_SENSITIVE: + check_attribute_buffer(attr, sizeof(CK_BBOOL)); + *(CK_BBOOL*)attr->pValue = (skey->info->access_flags & SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE) != 0; + break; + case CKA_NEVER_EXTRACTABLE: + check_attribute_buffer(attr, sizeof(CK_BBOOL)); + *(CK_BBOOL*)attr->pValue = (skey->info->access_flags & SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE) != 0; + break; + case CKA_SENSITIVE: + check_attribute_buffer(attr, sizeof(CK_BBOOL)); + *(CK_BBOOL*)attr->pValue = (skey->info->access_flags & SC_PKCS15_PRKEY_ACCESS_SENSITIVE) != 0; + break; + case CKA_LOCAL: + check_attribute_buffer(attr, sizeof(CK_BBOOL)); + *(CK_BBOOL*)attr->pValue = (skey->info->access_flags & SC_PKCS15_PRKEY_ACCESS_LOCAL) != 0; + break; + case CKA_ALWAYS_AUTHENTICATE: + check_attribute_buffer(attr, sizeof(CK_BBOOL)); + *(CK_BBOOL *)attr->pValue = skey->base.p15_object->user_consent >= 1 ? CK_TRUE : CK_FALSE; + break; + case CKA_OPENSC_ALWAYS_AUTH_ANY_OBJECT: + check_attribute_buffer(attr, sizeof(CK_BBOOL)); + *(CK_BBOOL*)attr->pValue = skey->base.p15_object->user_consent >= 1 ? CK_TRUE : CK_FALSE; + break; case CKA_VALUE_LEN: check_attribute_buffer(attr, sizeof(CK_ULONG)); - *(CK_ULONG*)attr->pValue = skey->info->data.len; + if (skey->info->data.len) { /* Available only if the CKA_VALUE is present -- probably never */ + *(CK_ULONG*)attr->pValue = skey->info->data.len; + } else { /* From standard secret key SKDF keyLen attribute (in bits) */ + *(CK_ULONG*)attr->pValue = skey->info->value_len/8; + } break; case CKA_VALUE: check_attribute_buffer(attr, skey->info->data.len); @@ -4471,6 +5602,312 @@ pkcs15_skey_get_attribute(struct sc_pkcs11_session *session, return CKR_OK; } +static CK_RV +pkcs15_skey_unwrap(struct sc_pkcs11_session *session, void *obj, + CK_MECHANISM_PTR pMechanism, CK_BYTE_PTR pWrappedKey, + CK_ULONG ulWrappedKeyLen, + void *targetKey) +{ + struct sc_pkcs11_card *p11card = session->slot->p11card; + struct pkcs15_fw_data *fw_data = NULL; + struct pkcs15_skey_object *skey = (struct pkcs15_skey_object *) obj; + struct pkcs15_skey_object *targetKeyObj = (struct pkcs15_skey_object *) targetKey; + int rv, flags = 0; + + sc_log(context, "Initiating unwrapping with a secret key."); + + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_UnwrapKey"); + fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; + if (!fw_data) + return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_UnwrapKey"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_UnwrapKey"); + + if (pMechanism == NULL || pWrappedKey == NULL || ulWrappedKeyLen == 0 || targetKeyObj == NULL) { + sc_log(context, "One or more of mandatory arguments were NULL."); + return CKR_ARGUMENTS_BAD; + } + + /* Check whether this key supports unwrap */ + if (skey && !(skey->info->usage & SC_PKCS15_PRKEY_USAGE_UNWRAP)) + skey = NULL; + + /* TODO: should we look for a compatible key automatically? prv_next not implemented yet. */ + /* skey = skey->prv_next; */ + + if (skey == NULL) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + sc_log(context, "Using mechanism %lx.", pMechanism->mechanism); + /* Select the proper padding mechanism */ + switch (pMechanism->mechanism) { + case CKM_AES_ECB: + flags |= SC_ALGORITHM_AES_ECB; + break; + case CKM_AES_CBC_PAD: + flags |= SC_ALGORITHM_AES_CBC_PAD; + break; + case CKM_AES_CBC: + flags |= SC_ALGORITHM_AES_CBC; /* in this case, pMechanism->pParameter contains IV */ + break; + default: + return CKR_MECHANISM_INVALID; + } + + rv = sc_lock(p11card->card); + + if (rv < 0) + return sc_to_cryptoki_error(rv, "C_UnwrapKey"); + + /* Call the card to do the unwrap operation */ + rv = sc_pkcs15_unwrap(fw_data->p15_card, skey->prv_p15obj, targetKeyObj->prv_p15obj, flags, + pWrappedKey, ulWrappedKeyLen, pMechanism->pParameter, pMechanism->ulParameterLen); + + sc_unlock(p11card->card); + + if (rv < 0) + return sc_to_cryptoki_error(rv, "C_UnwrapKey"); + + return CKR_OK; +} + +/* + * Wrap a key using a secret key. obj = wrapping key, targetKey = key to be wrapped. + * Wrapped key data is returned in pData + */ + +static CK_RV +pkcs15_skey_wrap(struct sc_pkcs11_session *session, void *obj, + CK_MECHANISM_PTR pMechanism, + void *targetKey, + CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) + +{ + struct sc_pkcs11_card *p11card; + struct pkcs15_fw_data *fw_data = NULL; + struct pkcs15_skey_object *skey = (struct pkcs15_skey_object *) obj; + struct pkcs15_skey_object *targetKeyObj = (struct pkcs15_skey_object *) targetKey; + size_t len = pulDataLen ? *pulDataLen : 0; + int rv, flags = 0; + + sc_log(context, "Initializing wrapping with a secret key."); + + if (session == NULL || pMechanism == NULL || obj == NULL || targetKey == NULL) { + sc_log(context, "One or more of mandatory arguments were NULL."); + return CKR_ARGUMENTS_BAD; + } + + p11card = session->slot->p11card; + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_WrapKey"); + fw_data = (struct pkcs15_fw_data *) p11card->fws_data[session->slot->fw_data_idx]; + if (!fw_data) + return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_WrapKey"); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_WrapKey"); + + /* Verify that the key supports wrapping */ + if (skey && !(skey->info->usage & SC_PKCS15_PRKEY_USAGE_WRAP)) + skey = NULL; + /* TODO: browse for a key that supports, like other similar funcs */ + + if (skey == NULL) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + sc_log(context, "Using mechanism %lx.", pMechanism->mechanism); + + /* Select the proper padding mechanism */ + switch (pMechanism->mechanism) { + case CKM_AES_ECB: + flags |= SC_ALGORITHM_AES_ECB; + break; + case CKM_AES_CBC_PAD: /* with CBC, pMechanism->pParameter contains IV */ + flags |= SC_ALGORITHM_AES_CBC_PAD; + break; + case CKM_AES_CBC: + flags |= SC_ALGORITHM_AES_CBC; + break; + default: + return CKR_MECHANISM_INVALID; + } + + rv = sc_lock(p11card->card); + + if (rv < 0) + return sc_to_cryptoki_error(rv, "C_UnwrapKey"); + + /* Call the card to do the wrapping operation */ + rv = sc_pkcs15_wrap(fw_data->p15_card, skey->prv_p15obj, targetKeyObj->prv_p15obj, flags, + pData, &len, pMechanism->pParameter, pMechanism->ulParameterLen); + + if (pulDataLen) { + *pulDataLen = len; + } + + sc_unlock(p11card->card); + + if (rv < 0) + return sc_to_cryptoki_error(rv, "C_UnwrapKey"); + + return CKR_OK; +} + +static CK_RV +pkcs15_skey_encrypt(struct sc_pkcs11_session *session, void *obj, + CK_MECHANISM_PTR pMechanism, + CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) +{ + struct sc_pkcs11_card *p11card = session->slot->p11card; + struct pkcs15_fw_data *fw_data = NULL; + struct pkcs15_skey_object *skey = (struct pkcs15_skey_object *)obj; + int rv, flags = 0; + size_t lEncryptedDataLen, *lpEncryptedDataLen; + + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_Encrypt..."); + fw_data = (struct pkcs15_fw_data *)p11card->fws_data[session->slot->fw_data_idx]; + if (!fw_data) + return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_Encrypt..."); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_Encrypt..."); + + if (pMechanism == NULL) { + sc_log(context, "No mechanism specified\n"); + return CKR_ARGUMENTS_BAD; + } + + /* do not check NULL/0 in Data/EncryptedData here, this + can be an init operation or final operation..*/ + + if (skey && !(skey->info->usage & SC_PKCS15_PRKEY_USAGE_ENCRYPT)) + skey = NULL; + + /* TODO: should we look for a compatible key automatically? prv_next not implemented yet. */ + /* skey = skey->prv_next; */ + + if (skey == NULL) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + sc_log(context, "Using mechanism %lx.", pMechanism->mechanism); + + switch (pMechanism->mechanism) { + case CKM_AES_ECB: + /* handle this in card driver + if (ulDataLen % 16) + return CKR_DATA_LEN_RANGE; */ + flags |= SC_ALGORITHM_AES_ECB; + break; + case CKM_AES_CBC: + /* handle this in card driver + if (ulDataLen % 16) + return CKR_DATA_LEN_RANGE; */ + flags |= SC_ALGORITHM_AES_CBC; + break; + case CKM_AES_CBC_PAD: + flags |= SC_ALGORITHM_AES_CBC_PAD; + break; + default: + return CKR_MECHANISM_INVALID; + } + + rv = sc_lock(p11card->card); + + if (rv < 0) + return sc_to_cryptoki_error(rv, "C_Encrypt..."); + + /* pointer CK_ULONG_PTR to size_t conversion */ + lpEncryptedDataLen = pulEncryptedDataLen ? &lEncryptedDataLen : NULL; + + rv = sc_pkcs15_encrypt_sym(fw_data->p15_card, skey->prv_p15obj, flags, + pData, ulDataLen, pEncryptedData, lpEncryptedDataLen, + pMechanism->pParameter, pMechanism->ulParameterLen); + + if (pulEncryptedDataLen) + *pulEncryptedDataLen = *lpEncryptedDataLen; + + sc_unlock(p11card->card); + return sc_to_cryptoki_error(rv, "C_Encrypt..."); +} + +static CK_RV +pkcs15_skey_decrypt(struct sc_pkcs11_session *session, void *obj, + CK_MECHANISM_PTR pMechanism, + CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, + CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) +{ + struct sc_pkcs11_card *p11card = session->slot->p11card; + struct pkcs15_fw_data *fw_data = NULL; + struct pkcs15_skey_object *skey = (struct pkcs15_skey_object *)obj; + int rv, flags = 0; + size_t lDataLen, *lpDataLen; + + if (!p11card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_Decrypt..."); + fw_data = (struct pkcs15_fw_data *)p11card->fws_data[session->slot->fw_data_idx]; + if (!fw_data) + return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_Decrypt..."); + if (!fw_data->p15_card) + return sc_to_cryptoki_error(SC_ERROR_INVALID_CARD, "C_Decrypt..."); + + if (pMechanism == NULL) { + sc_log(context, "No mechanism specified\n"); + return CKR_ARGUMENTS_BAD; + } + + /* do not check NULL/0 in Data/DecryptedData here, this + can be an init operation or final operation..*/ + + if (skey && !(skey->info->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT)) + skey = NULL; + + /* Please read comments in pkcs15_skey_unwrap() and pkcs15_skey_wrap() */ + + if (skey == NULL) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + sc_log(context, "Using mechanism %lx.", pMechanism->mechanism); + + switch (pMechanism->mechanism) { + case CKM_AES_ECB: + /* handle this in card driver + if (ulDataLen % 16) + return CKR_DATA_LEN_RANGE; */ + flags |= SC_ALGORITHM_AES_ECB; + break; + case CKM_AES_CBC: + /* handle this in card driver + if (ulDataLen % 16) + return CKR_DATA_LEN_RANGE; */ + flags |= SC_ALGORITHM_AES_CBC; + break; + case CKM_AES_CBC_PAD: + flags |= SC_ALGORITHM_AES_CBC_PAD; + break; + default: + return CKR_MECHANISM_INVALID; + } + + rv = sc_lock(p11card->card); + + if (rv < 0) + return sc_to_cryptoki_error(rv, "C_Decrypt..."); + + /* pointer CK_ULONG_PTR to size_t conversion */ + lDataLen = pulDataLen ? *pulDataLen : 0; + lpDataLen = pulDataLen ? &lDataLen : NULL; + + rv = sc_pkcs15_decrypt_sym(fw_data->p15_card, skey->prv_p15obj, flags, + pEncryptedData, ulEncryptedDataLen, pData, lpDataLen, + pMechanism->pParameter, pMechanism->ulParameterLen); + + if (pulDataLen) + *pulDataLen = *lpDataLen; + + sc_unlock(p11card->card); + return sc_to_cryptoki_error(rv, "C_Decrypt..."); +} /* * Secret key objects, currently used only to retrieve derived session key @@ -4483,10 +5920,13 @@ struct sc_pkcs11_object_ops pkcs15_skey_ops = { pkcs15_skey_destroy, NULL, /* get_size */ NULL, /* sign */ - NULL, /* unwrap_key */ - NULL, /* decrypt */ + pkcs15_skey_unwrap, + pkcs15_skey_decrypt, /* decrypt */ + pkcs15_skey_encrypt, /* encrypt */ NULL, /* derive */ - NULL /* can_do */ + NULL, /* can_do */ + NULL, /* init_params */ + pkcs15_skey_wrap /* wrap_key */ }; /* @@ -4559,6 +5999,10 @@ static CK_RV get_ec_pubkey_params(struct sc_pkcs15_pubkey *key, CK_ATTRIBUTE_PTR attr) { struct sc_ec_parameters *ecp; + size_t value_size = 0; + unsigned char *value = NULL; + + int r; if (key == NULL) return CKR_ATTRIBUTE_TYPE_INVALID; @@ -4566,6 +6010,29 @@ get_ec_pubkey_params(struct sc_pkcs15_pubkey *key, CK_ATTRIBUTE_PTR attr) return CKR_ATTRIBUTE_TYPE_INVALID; switch (key->algorithm) { + case SC_ALGORITHM_EDDSA: + case SC_ALGORITHM_XEDDSA: + r = sc_encode_oid(context, &key->alg_id->oid, &value, &value_size); + if (r != SC_SUCCESS) { + return sc_to_cryptoki_error(r, NULL); + } + + if (attr->pValue == NULL_PTR) { + attr->ulValueLen = (unsigned long)value_size; + free(value); + return CKR_OK; + } + if (attr->ulValueLen < (unsigned long)value_size) { + attr->ulValueLen = (unsigned long)value_size; + free(value); + return CKR_BUFFER_TOO_SMALL; + } + + attr->ulValueLen = (unsigned long)value_size; + memcpy(attr->pValue, value, value_size); + free(value); + return CKR_OK; + case SC_ALGORITHM_EC: /* TODO parms should not be in two places */ /* ec_params may be in key->alg_id or in key->u.ec */ @@ -4597,12 +6064,42 @@ get_ec_pubkey_point(struct sc_pkcs15_pubkey *key, CK_ATTRIBUTE_PTR attr) return CKR_ATTRIBUTE_TYPE_INVALID; switch (key->algorithm) { + case SC_ALGORITHM_EDDSA: + case SC_ALGORITHM_XEDDSA: + /* For PKCS11 3.0 errata and 3.1, Edwards and Montgomery EC_POINT is raw byte string */ + /* key->key->u.ec.ecpointQ is raw byte string */ + if (key->u.ec.ecpointQ.value == NULL || key->u.ec.ecpointQ.len == 0) + return CKR_ATTRIBUTE_TYPE_INVALID; + + if (attr->pValue == NULL_PTR) { + attr->ulValueLen = key->u.ec.ecpointQ.len; + return CKR_OK; + } + if (attr->ulValueLen < key->u.ec.ecpointQ.len) { + attr->ulValueLen = key->u.ec.ecpointQ.len; + return CKR_BUFFER_TOO_SMALL; + } + attr->ulValueLen = key->u.ec.ecpointQ.len; + memcpy(attr->pValue, key->u.ec.ecpointQ.value, key->u.ec.ecpointQ.len); + return CKR_OK; + case SC_ALGORITHM_EC: rc = sc_pkcs15_encode_pubkey_ec(context, &key->u.ec, &value, &value_len); if (rc != SC_SUCCESS) return sc_to_cryptoki_error(rc, NULL); - check_attribute_buffer(attr, value_len); + if (attr->pValue == NULL_PTR) { + attr->ulValueLen = value_len; + free(value); + return CKR_OK; + } + if (attr->ulValueLen < value_len) { + attr->ulValueLen = value_len; + free(value); + return CKR_BUFFER_TOO_SMALL; + } + attr->ulValueLen = value_len; + memcpy(attr->pValue, value, value_len); free(value); return CKR_OK; @@ -4671,12 +6168,12 @@ get_usage_bit(unsigned int usage, CK_ATTRIBUTE_PTR attr) } -static int +static CK_RV register_gost_mechanisms(struct sc_pkcs11_card *p11card, int flags) { CK_MECHANISM_INFO mech_info; sc_pkcs11_mechanism_type_t *mt; - int rc; + CK_RV rc; mech_info.flags = CKF_HW | CKF_SIGN | CKF_DECRYPT; #ifdef ENABLE_OPENSSL @@ -4688,29 +6185,32 @@ register_gost_mechanisms(struct sc_pkcs11_card *p11card, int flags) if (flags & SC_ALGORITHM_GOSTR3410_HASH_NONE) { mt = sc_pkcs11_new_fw_mechanism(CKM_GOSTR3410, - &mech_info, CKK_GOSTR3410, NULL, NULL); + &mech_info, CKK_GOSTR3410, NULL, NULL, NULL); if (!mt) return CKR_HOST_MEMORY; - rc = sc_pkcs11_register_mechanism(p11card, mt); + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); if (rc != CKR_OK) return rc; } if (flags & SC_ALGORITHM_GOSTR3410_HASH_GOSTR3411) { mt = sc_pkcs11_new_fw_mechanism(CKM_GOSTR3410_WITH_GOSTR3411, - &mech_info, CKK_GOSTR3410, NULL, NULL); + &mech_info, CKK_GOSTR3410, NULL, NULL, NULL); if (!mt) return CKR_HOST_MEMORY; - rc = sc_pkcs11_register_mechanism(p11card, mt); + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); if (rc != CKR_OK) return rc; } if (flags & SC_ALGORITHM_ONBOARD_KEY_GEN) { mech_info.flags = CKF_HW | CKF_GENERATE_KEY_PAIR; mt = sc_pkcs11_new_fw_mechanism(CKM_GOSTR3410_KEY_PAIR_GEN, - &mech_info, CKK_GOSTR3410, NULL, NULL); + &mech_info, CKK_GOSTR3410, NULL, NULL, NULL); if (!mt) return CKR_HOST_MEMORY; - rc = sc_pkcs11_register_mechanism(p11card, mt); + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); if (rc != CKR_OK) return rc; } @@ -4719,13 +6219,13 @@ register_gost_mechanisms(struct sc_pkcs11_card *p11card, int flags) } -static int register_ec_mechanisms(struct sc_pkcs11_card *p11card, int flags, +static CK_RV register_ec_mechanisms(struct sc_pkcs11_card *p11card, int flags, unsigned long ext_flags, CK_ULONG min_key_size, CK_ULONG max_key_size) { CK_MECHANISM_INFO mech_info; - sc_pkcs11_mechanism_type_t *mt; + sc_pkcs11_mechanism_type_t *mt = NULL, *registered_mt = NULL; CK_FLAGS ec_flags = 0; - int rc; + CK_RV rc; if (ext_flags & SC_ALGORITHM_EXT_EC_F_P) ec_flags |= CKF_EC_F_P; @@ -4740,48 +6240,145 @@ static int register_ec_mechanisms(struct sc_pkcs11_card *p11card, int flags, if (ext_flags & SC_ALGORITHM_EXT_EC_COMPRESS) ec_flags |= CKF_EC_COMPRESS; - mech_info.flags = CKF_HW | CKF_SIGN; /* check for more */ + mech_info.flags = CKF_HW | CKF_SIGN | CKF_VERIFY; mech_info.flags |= ec_flags; mech_info.ulMinKeySize = min_key_size; mech_info.ulMaxKeySize = max_key_size; - if(flags & SC_ALGORITHM_ECDSA_HASH_NONE) { - mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA, &mech_info, CKK_EC, NULL, NULL); + /* add mechs card or driver support */ + if (flags & SC_ALGORITHM_ECDSA_RAW) { + mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA, &mech_info, CKK_EC, NULL, NULL, NULL); if (!mt) return CKR_HOST_MEMORY; - rc = sc_pkcs11_register_mechanism(p11card, mt); + if (flags & SC_ALGORITHM_ECDSA_HASH_NONE) { + rc = sc_pkcs11_register_mechanism(p11card, mt, ®istered_mt); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) + return rc; + } + +#ifdef ENABLE_OPENSSL + /* if card supports RAW add sign_and_hash using RAW for mechs card does not support */ + sc_pkcs11_mechanism_type_t *sign_type = mt ? mt : registered_mt; + if (flags & SC_ALGORITHM_ECDSA_RAW) { + if (!(flags & SC_ALGORITHM_ECDSA_HASH_SHA1)) { + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_ECDSA_SHA1, CKM_SHA_1, sign_type); + if (rc != CKR_OK) { + sc_pkcs11_free_mechanism(&mt); + return rc; + } + } + + if (!(flags & SC_ALGORITHM_ECDSA_HASH_SHA224)) { + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_ECDSA_SHA224, CKM_SHA224, sign_type); + if (rc != CKR_OK) { + sc_pkcs11_free_mechanism(&mt); + return rc; + } + } + + if (!(flags & SC_ALGORITHM_ECDSA_HASH_SHA256)) { + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_ECDSA_SHA256, CKM_SHA256, sign_type); + if (rc != CKR_OK) { + sc_pkcs11_free_mechanism(&mt); + return rc; + } + } + + if (!(flags & SC_ALGORITHM_ECDSA_HASH_SHA384)) { + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_ECDSA_SHA384, CKM_SHA384, sign_type); + if (rc != CKR_OK) { + sc_pkcs11_free_mechanism(&mt); + return rc; + } + } + + if (!(flags & SC_ALGORITHM_ECDSA_HASH_SHA512)) { + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_ECDSA_SHA512, CKM_SHA512, sign_type); + if (rc != CKR_OK) { + sc_pkcs11_free_mechanism(&mt); + return rc; + } + } + } + sc_pkcs11_free_mechanism(&mt); +#endif + } + + if (flags & SC_ALGORITHM_ECDSA_HASH_SHA1) { + mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA_SHA1, &mech_info, CKK_EC, NULL, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); if (rc != CKR_OK) return rc; } -#ifdef ENABLE_OPENSSL - if(flags & SC_ALGORITHM_ECDSA_HASH_SHA1) { - mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA_SHA1, &mech_info, CKK_EC, NULL, NULL); + if (flags & SC_ALGORITHM_ECDSA_HASH_SHA224) { + mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA_SHA224, &mech_info, CKK_EC, NULL, NULL, NULL); if (!mt) return CKR_HOST_MEMORY; - rc = sc_pkcs11_register_mechanism(p11card, mt); + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) + return rc; + } + + if (flags & SC_ALGORITHM_ECDSA_HASH_SHA256) { + mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA_SHA256, &mech_info, CKK_EC, NULL, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) + return rc; + } + + if (flags & SC_ALGORITHM_ECDSA_HASH_SHA384) { + mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA_SHA384, &mech_info, CKK_EC, NULL, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) + return rc; + } + + if (flags & SC_ALGORITHM_ECDSA_HASH_SHA512) { + mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA_SHA512, &mech_info, CKK_EC, NULL, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); if (rc != CKR_OK) return rc; } -#endif /* ADD ECDH mechanisms */ /* The PIV uses curves where CKM_ECDH1_DERIVE and CKM_ECDH1_COFACTOR_DERIVE produce the same results */ - if(flags & SC_ALGORITHM_ECDH_CDH_RAW) { - mech_info.flags &= ~CKF_SIGN; + if (flags & SC_ALGORITHM_ECDH_CDH_RAW) { + mech_info.flags &= ~(CKF_SIGN | CKF_VERIFY); mech_info.flags |= CKF_DERIVE; - mt = sc_pkcs11_new_fw_mechanism(CKM_ECDH1_COFACTOR_DERIVE, &mech_info, CKK_EC, NULL, NULL); + mt = sc_pkcs11_new_fw_mechanism(CKM_ECDH1_COFACTOR_DERIVE, &mech_info, CKK_EC, NULL, NULL, NULL); if (!mt) return CKR_HOST_MEMORY; - rc = sc_pkcs11_register_mechanism(p11card, mt); + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); if (rc != CKR_OK) return rc; - mt = sc_pkcs11_new_fw_mechanism(CKM_ECDH1_DERIVE, &mech_info, CKK_EC, NULL, NULL); + mt = sc_pkcs11_new_fw_mechanism(CKM_ECDH1_DERIVE, &mech_info, CKK_EC, NULL, NULL, NULL); if (!mt) return CKR_HOST_MEMORY; - rc = sc_pkcs11_register_mechanism(p11card, mt); + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); if (rc != CKR_OK) return rc; } @@ -4789,14 +6386,150 @@ static int register_ec_mechanisms(struct sc_pkcs11_card *p11card, int flags, if (flags & SC_ALGORITHM_ONBOARD_KEY_GEN) { mech_info.flags = CKF_HW | CKF_GENERATE_KEY_PAIR; mech_info.flags |= ec_flags; - mt = sc_pkcs11_new_fw_mechanism(CKM_EC_KEY_PAIR_GEN, &mech_info, CKK_EC, NULL, NULL); + mt = sc_pkcs11_new_fw_mechanism(CKM_EC_KEY_PAIR_GEN, &mech_info, CKK_EC, NULL, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) + return rc; + } + + return CKR_OK; +} + +static CK_RV register_eddsa_mechanisms(struct sc_pkcs11_card *p11card, int flags, + CK_ULONG min_key_size, CK_ULONG max_key_size) +{ + CK_MECHANISM_INFO mech_info; + sc_pkcs11_mechanism_type_t *mt; + CK_RV rc; + + mech_info.flags = CKF_HW | CKF_SIGN; + mech_info.ulMinKeySize = min_key_size; + mech_info.ulMaxKeySize = max_key_size; + +#ifdef ENABLE_OPENSSL + /* TODO verification using EDDSA + mech_info.flags |= CKF_VERIFY; + */ +#endif + if (flags & SC_ALGORITHM_EDDSA_RAW) { + mt = sc_pkcs11_new_fw_mechanism(CKM_EDDSA, &mech_info, CKK_EC_EDWARDS, NULL, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) + return rc; + } + + if (flags & SC_ALGORITHM_ONBOARD_KEY_GEN) { + mech_info.flags = CKF_HW | CKF_GENERATE_KEY_PAIR; + mt = sc_pkcs11_new_fw_mechanism(CKM_EC_EDWARDS_KEY_PAIR_GEN, + &mech_info, CKK_EC_EDWARDS, NULL, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) + return rc; + } + + return CKR_OK; +} + +static CK_RV register_xeddsa_mechanisms(struct sc_pkcs11_card *p11card, int flags, + CK_ULONG min_key_size, CK_ULONG max_key_size) +{ + CK_MECHANISM_INFO mech_info; + sc_pkcs11_mechanism_type_t *mt; + CK_RV rc; + + mech_info.flags = CKF_HW | CKF_SIGN | CKF_DERIVE; + mech_info.ulMinKeySize = min_key_size; + mech_info.ulMaxKeySize = max_key_size; + + if (flags & SC_ALGORITHM_XEDDSA_RAW) { + mt = sc_pkcs11_new_fw_mechanism(CKM_XEDDSA, &mech_info, CKK_EC_MONTGOMERY, NULL, NULL, NULL); if (!mt) return CKR_HOST_MEMORY; - rc = sc_pkcs11_register_mechanism(p11card, mt); + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); if (rc != CKR_OK) return rc; } + /* ADD ECDH mechanisms */ + if (flags & SC_ALGORITHM_ECDH_CDH_RAW) { + mech_info.flags &= ~CKF_SIGN; + mech_info.flags |= CKF_DERIVE; + + /* Montgomery curves derive function is defined only for CKM_ECDH1_DERIVE mechanism */ + mt = sc_pkcs11_new_fw_mechanism(CKM_ECDH1_DERIVE, &mech_info, CKK_EC_MONTGOMERY, NULL, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) + return rc; + } + + if (flags & SC_ALGORITHM_ONBOARD_KEY_GEN) { + mech_info.flags = CKF_HW | CKF_GENERATE_KEY_PAIR; + mt = sc_pkcs11_new_fw_mechanism(CKM_EC_MONTGOMERY_KEY_PAIR_GEN, &mech_info, CKK_EC_MONTGOMERY, NULL, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) + return rc; + } + + return CKR_OK; +} + +static CK_RV sc_pkcs11_register_aes_mechanisms(struct sc_pkcs11_card *p11card, int flags, + CK_ULONG min_key_size, CK_ULONG max_key_size) +{ + CK_RV rc; + CK_MECHANISM_INFO mech_info; + sc_pkcs11_mechanism_type_t *mt; + sc_card_t* card = p11card->card; + memset(&mech_info, 0, sizeof(mech_info)); + mech_info.flags = CKF_ENCRYPT | CKF_DECRYPT; + mech_info.ulMinKeySize = min_key_size; + mech_info.ulMaxKeySize = max_key_size; + + if ((card->caps & SC_CARD_CAP_UNWRAP_KEY) == SC_CARD_CAP_UNWRAP_KEY) + mech_info.flags |= CKF_UNWRAP; + if ((card->caps & SC_CARD_CAP_WRAP_KEY) == SC_CARD_CAP_WRAP_KEY) + mech_info.flags |= CKF_WRAP; + + mt = sc_pkcs11_new_fw_mechanism(CKM_AES_ECB, &mech_info, CKK_AES, NULL, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) + return rc; + + mt = sc_pkcs11_new_fw_mechanism(CKM_AES_CBC, &mech_info, CKK_AES, NULL, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) + return rc; + + mt = sc_pkcs11_new_fw_mechanism(CKM_AES_CBC_PAD, &mech_info, CKK_AES, NULL, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) + return rc; + return CKR_OK; } @@ -4811,11 +6544,14 @@ register_mechanisms(struct sc_pkcs11_card *p11card) sc_card_t *card = p11card->card; sc_algorithm_info_t *alg_info; CK_MECHANISM_INFO mech_info; - CK_ULONG ec_min_key_size, ec_max_key_size; + CK_ULONG ec_min_key_size, ec_max_key_size, + aes_min_key_size, aes_max_key_size; unsigned long ec_ext_flags; - sc_pkcs11_mechanism_type_t *mt; + sc_pkcs11_mechanism_type_t *mt = NULL, *registered_mt = NULL; unsigned int num; - int rc, rsa_flags = 0, ec_flags = 0, gostr_flags = 0; + int rsa_flags = 0, ec_flags = 0, eddsa_flags = 0, xeddsa_flags = 0; + int ec_found = 0, gostr_flags = 0, aes_flags = 0; + CK_RV rc; /* Register generic mechanisms */ sc_pkcs11_register_generic_mechanisms(p11card); @@ -4825,10 +6561,18 @@ register_mechanisms(struct sc_pkcs11_card *p11card) /* That practise definitely conflicts with CKF_HW -- andre 2010-11-28 */ mech_info.flags |= CKF_VERIFY; #endif + if ((card->caps & SC_CARD_CAP_UNWRAP_KEY) == SC_CARD_CAP_UNWRAP_KEY) + mech_info.flags |= CKF_UNWRAP; + if ((card->caps & SC_CARD_CAP_WRAP_KEY) == SC_CARD_CAP_WRAP_KEY) + mech_info.flags |= CKF_WRAP; + mech_info.ulMinKeySize = ~0; mech_info.ulMaxKeySize = 0; ec_min_key_size = ~0; ec_max_key_size = 0; + aes_min_key_size = ~0; + aes_max_key_size = 0; + ec_ext_flags = 0; /* For now, we just OR all the algorithm specific @@ -4852,20 +6596,51 @@ register_mechanisms(struct sc_pkcs11_card *p11card) ec_max_key_size = alg_info->key_length; ec_flags |= alg_info->flags; ec_ext_flags |= alg_info->u._ec.ext_flags; + ec_found = 1; + break; + case SC_ALGORITHM_EDDSA: + eddsa_flags |= alg_info->flags; + break; + case SC_ALGORITHM_XEDDSA: + xeddsa_flags |= alg_info->flags; break; case SC_ALGORITHM_GOSTR3410: gostr_flags |= alg_info->flags; break; + case SC_ALGORITHM_AES: + aes_flags |= alg_info->flags; + if (alg_info->key_length < aes_min_key_size) + aes_min_key_size = alg_info->key_length; + if (alg_info->key_length > aes_max_key_size) + aes_max_key_size = alg_info->key_length; + break; } alg_info++; } - if (ec_flags & SC_ALGORITHM_ECDSA_RAW) { + /* + * TODO this looked like a bug: + * if (ec_flags & SC_ALGORITHM_ECDSA_RAW) + * Card driver driver should not have to specify SC_ALGORITHM_ECDSA_RAW + */ + if (ec_found) { rc = register_ec_mechanisms(p11card, ec_flags, ec_ext_flags, ec_min_key_size, ec_max_key_size); if (rc != CKR_OK) return rc; } + if (eddsa_flags & SC_ALGORITHM_EDDSA_RAW) { + rc = register_eddsa_mechanisms(p11card, eddsa_flags, 255, 255); + if (rc != CKR_OK) + return rc; + } + + if (xeddsa_flags & (SC_ALGORITHM_XEDDSA_RAW | SC_ALGORITHM_ECDH_CDH_RAW)) { + rc = register_xeddsa_mechanisms(p11card, xeddsa_flags, 255, 255); + if (rc != CKR_OK) + return rc; + } + if (gostr_flags & (SC_ALGORITHM_GOSTR3410_RAW | SC_ALGORITHM_GOSTR3410_HASH_NONE | SC_ALGORITHM_GOSTR3410_HASH_GOSTR3411)) { @@ -4878,35 +6653,46 @@ register_mechanisms(struct sc_pkcs11_card *p11card) /* Check if we support raw RSA */ if (rsa_flags & SC_ALGORITHM_RSA_RAW) { - mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_X_509, &mech_info, CKK_RSA, NULL, NULL); - rc = sc_pkcs11_register_mechanism(p11card, mt); + mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_X_509, &mech_info, CKK_RSA, NULL, NULL, NULL); + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); if (rc != CKR_OK) return rc; /* We support PKCS1 padding in software */ /* either the card supports it or OpenSC does */ rsa_flags |= SC_ALGORITHM_RSA_PAD_PKCS1; +#ifdef ENABLE_OPENSSL + rsa_flags |= SC_ALGORITHM_RSA_PAD_PSS; + rsa_flags |= SC_ALGORITHM_RSA_PAD_OAEP; +#endif + } + + if (rsa_flags & SC_ALGORITHM_RSA_PAD_ISO9796) { + /* Supported in hardware only, if the card driver declares it. */ + mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_9796, &mech_info, CKK_RSA, NULL, NULL, NULL); + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) + return rc; } #ifdef ENABLE_OPENSSL - /* all our software hashes are in OpenSSL */ - /* Only if card did not lists the hashs, will we - * help it a little, by adding all the OpenSSL hashes - * that have PKCS#11 mechanisms. - */ - if (!(rsa_flags & SC_ALGORITHM_RSA_HASHES)) { - rsa_flags |= SC_ALGORITHM_RSA_HASHES; -#if OPENSSL_VERSION_NUMBER < 0x00908000L - /* turn off hashes not in openssl 0.9.8 */ - rsa_flags &= ~(SC_ALGORITHM_RSA_HASH_SHA256 | SC_ALGORITHM_RSA_HASH_SHA384 | SC_ALGORITHM_RSA_HASH_SHA512 | SC_ALGORITHM_RSA_HASH_SHA224); -#endif - } + /* all our software hashes are in OpenSSL */ + /* Only if card did not list the hashes, will we + * help it a little, by adding all the OpenSSL hashes + * that have PKCS#11 mechanisms. + */ + if (!(rsa_flags & (SC_ALGORITHM_RSA_HASHES & ~SC_ALGORITHM_RSA_HASH_NONE))) { + rsa_flags |= SC_ALGORITHM_RSA_HASHES; + } #endif /* No need to Check for PKCS1 We support it in software and turned it on above so always added it */ if (rsa_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { - mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_PKCS, &mech_info, CKK_RSA, NULL, NULL); - rc = sc_pkcs11_register_mechanism(p11card, mt); + mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_PKCS, &mech_info, CKK_RSA, NULL, NULL, NULL); + rc = sc_pkcs11_register_mechanism(p11card, mt, ®istered_mt); + sc_pkcs11_free_mechanism(&mt); if (rc != CKR_OK) return rc; @@ -4916,50 +6702,128 @@ register_mechanisms(struct sc_pkcs11_card *p11card) * Either the card set the hashes or we helped it above */ if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA1) { - rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_SHA1_RSA_PKCS, CKM_SHA_1, mt); + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_SHA1_RSA_PKCS, CKM_SHA_1, registered_mt); + if (rc != CKR_OK) + return rc; + } + if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA224) { + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_SHA224_RSA_PKCS, CKM_SHA224, registered_mt); if (rc != CKR_OK) return rc; } if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA256) { - rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_SHA256_RSA_PKCS, CKM_SHA256, mt); + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_SHA256_RSA_PKCS, CKM_SHA256, registered_mt); if (rc != CKR_OK) return rc; } if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA384) { - rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_SHA384_RSA_PKCS, CKM_SHA384, mt); + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_SHA384_RSA_PKCS, CKM_SHA384, registered_mt); if (rc != CKR_OK) return rc; } if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA512) { - rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_SHA512_RSA_PKCS, CKM_SHA512, mt); + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_SHA512_RSA_PKCS, CKM_SHA512, registered_mt); + if (rc != CKR_OK) + return rc; + } + if (!FIPS_mode() && rsa_flags & SC_ALGORITHM_RSA_HASH_MD5) { + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_MD5_RSA_PKCS, CKM_MD5, registered_mt); + if (rc != CKR_OK) + return rc; + } + if (!FIPS_mode() && rsa_flags & SC_ALGORITHM_RSA_HASH_RIPEMD160) { + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_RIPEMD160_RSA_PKCS, CKM_RIPEMD160, registered_mt); + if (rc != CKR_OK) + return rc; + } +#endif /* ENABLE_OPENSSL */ + } + + if (rsa_flags & SC_ALGORITHM_RSA_PAD_PSS) { + CK_FLAGS old_flags = mech_info.flags; + mech_info.flags &= ~(CKF_DECRYPT|CKF_ENCRYPT|CKF_WRAP|CKF_UNWRAP); + mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_PKCS_PSS, &mech_info, CKK_RSA, NULL, NULL, NULL); + rc = sc_pkcs11_register_mechanism(p11card, mt, ®istered_mt); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) + return rc; + +#ifdef ENABLE_OPENSSL + /* sc_pkcs11_register_sign_and_hash_mechanism expects software hash */ + /* All hashes are in OpenSSL + * Either the card set the hashes or we helped it above */ + + if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA1) { + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_SHA1_RSA_PKCS_PSS, CKM_SHA_1, registered_mt); + if (rc != CKR_OK) + return rc; + } + if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA224) { + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_SHA224_RSA_PKCS_PSS, CKM_SHA224, registered_mt); if (rc != CKR_OK) return rc; } - if (rsa_flags & SC_ALGORITHM_RSA_HASH_MD5) { - rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_MD5_RSA_PKCS, CKM_MD5, mt); + if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA256) { + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_SHA256_RSA_PKCS_PSS, CKM_SHA256, registered_mt); if (rc != CKR_OK) return rc; } - if (rsa_flags & SC_ALGORITHM_RSA_HASH_RIPEMD160) { - rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_RIPEMD160_RSA_PKCS, CKM_RIPEMD160, mt); + if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA384) { + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_SHA384_RSA_PKCS_PSS, CKM_SHA384, registered_mt); + if (rc != CKR_OK) + return rc; + } + if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA512) { + rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, + CKM_SHA512_RSA_PKCS_PSS, CKM_SHA512, registered_mt); if (rc != CKR_OK) return rc; } #endif /* ENABLE_OPENSSL */ + mech_info.flags = old_flags; } - /* TODO support other padding mechanisms */ + if (rsa_flags & SC_ALGORITHM_RSA_PAD_OAEP) { + CK_FLAGS old_flags = mech_info.flags; + mech_info.flags &= ~(CKF_SIGN|CKF_VERIFY|CKF_SIGN_RECOVER|CKF_VERIFY_RECOVER); + mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_PKCS_OAEP, &mech_info, CKK_RSA, NULL, NULL, NULL); + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + if (rc != CKR_OK) { + return rc; + } + mech_info.flags = old_flags; + } if (rsa_flags & SC_ALGORITHM_ONBOARD_KEY_GEN) { - mech_info.flags = CKF_GENERATE_KEY_PAIR; - mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_PKCS_KEY_PAIR_GEN, &mech_info, CKK_RSA, NULL, NULL); + mech_info.flags = CKF_HW | CKF_GENERATE_KEY_PAIR; + mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_PKCS_KEY_PAIR_GEN, &mech_info, CKK_RSA, NULL, NULL, NULL); if (!mt) return CKR_HOST_MEMORY; - rc = sc_pkcs11_register_mechanism(p11card, mt); + rc = sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); if (rc != CKR_OK) return rc; } + if (aes_max_key_size > 0) { + rc = sc_pkcs11_register_aes_mechanisms(p11card, aes_flags, aes_min_key_size, aes_max_key_size); + if (rc != CKR_OK) + return rc; + } + + return CKR_OK; } @@ -4987,18 +6851,3 @@ unlock_card(struct pkcs15_fw_data *fw_data) } return 0; } - - -static int -reselect_app_df(sc_pkcs15_card_t *p15card) -{ - int r = SC_SUCCESS; - - if (p15card->file_app != NULL) { - /* if the application df (of the pkcs15 application) is specified select it */ - sc_path_t *tpath = &p15card->file_app->path; - sc_log(p15card->card->ctx, "reselect application df"); - r = sc_select_file(p15card->card, tpath, NULL); - } - return r; -} diff --git a/src/pkcs11/framework-pkcs15init.c b/src/pkcs11/framework-pkcs15init.c index e3038cfffe..a21b904082 100644 --- a/src/pkcs11/framework-pkcs15init.c +++ b/src/pkcs11/framework-pkcs15init.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -32,10 +32,13 @@ */ static CK_RV pkcs15init_bind(struct sc_pkcs11_card *p11card, struct sc_app_info *app_info) { - struct sc_card *card = p11card->card; + struct sc_card *card; struct sc_profile *profile; int rc; + if (!p11card) + return CKR_TOKEN_NOT_RECOGNIZED; + card = p11card->card; rc = sc_pkcs15init_bind(card, "pkcs15", NULL, NULL, &profile); if (rc == 0) p11card->fws_data[0] = profile; @@ -46,6 +49,8 @@ static CK_RV pkcs15init_unbind(struct sc_pkcs11_card *p11card) { struct sc_profile *profile; + if (!p11card) + return CKR_TOKEN_NOT_RECOGNIZED; profile = (struct sc_profile *) p11card->fws_data[0]; sc_pkcs15init_unbind(profile); return CKR_OK; @@ -57,8 +62,10 @@ pkcs15init_create_tokens(struct sc_pkcs11_card *p11card, struct sc_app_info *app { struct sc_profile *profile; struct sc_pkcs11_slot *slot; - int rc; + CK_RV rc; + if (!p11card) + return CKR_TOKEN_NOT_RECOGNIZED; profile = (struct sc_profile *) p11card->fws_data[0]; rc = slot_allocate(&slot, p11card); @@ -127,11 +134,15 @@ pkcs15init_initialize(struct sc_pkcs11_slot *pslot, void *ptr, CK_UTF8CHAR_PTR pLabel) { struct sc_pkcs11_card *p11card = pslot->p11card; - struct sc_profile *profile = (struct sc_profile *) p11card->fws_data[0]; + struct sc_profile *profile; struct sc_pkcs15init_initargs args; struct sc_pkcs11_slot *slot; - int rc, rv, id; + CK_RV rv; + int rc, id; + if (!p11card) + return CKR_TOKEN_NOT_RECOGNIZED; + profile = (struct sc_profile *) p11card->fws_data[0]; memset(&args, 0, sizeof(args)); args.so_pin = pPin; args.so_pin_len = ulPinLen; @@ -193,7 +204,7 @@ struct sc_pkcs11_framework_ops framework_pkcs15init = { NULL, /* login */ NULL, /* logout */ NULL, /* change_pin */ - NULL, /* inti_token */ + NULL, /* init_token */ NULL, /* init_pin */ NULL, /* create_object */ NULL, /* gen_keypair */ diff --git a/src/pkcs11/mechanism.c b/src/pkcs11/mechanism.c index b64d052036..2f11c066d2 100644 --- a/src/pkcs11/mechanism.c +++ b/src/pkcs11/mechanism.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -23,7 +23,10 @@ #include #include +#include "common/compat_overflow.h" +#include "common/constant-time.h" #include "sc-pkcs11.h" +#include "libopensc/internal.h" /* Also used for verification data */ struct hash_signature_info { @@ -31,49 +34,177 @@ struct hash_signature_info { CK_MECHANISM_TYPE hash_mech; CK_MECHANISM_TYPE sign_mech; sc_pkcs11_mechanism_type_t *hash_type; - sc_pkcs11_mechanism_type_t *sign_type; }; /* Also used for verification and decryption data */ -struct signature_data { +struct operation_data { struct sc_pkcs11_object *key; struct hash_signature_info *info; - sc_pkcs11_operation_t * md; - CK_BYTE buffer[4096/8]; - unsigned int buffer_len; + sc_pkcs11_operation_t *md; + CK_BYTE *buffer; + CK_ULONG buffer_len; }; +static struct operation_data * +new_operation_data() +{ + return calloc(1, sizeof(struct operation_data)); +} + +static void +operation_data_release(struct operation_data *data) +{ + if (!data) + return; + sc_pkcs11_release_operation(&data->md); + sc_mem_secure_clear_free(data->buffer, data->buffer_len); + free(data); +} + +static CK_RV +signature_data_buffer_append(struct operation_data *data, + const CK_BYTE *in, CK_ULONG in_len) +{ + if (!data) + return CKR_ARGUMENTS_BAD; + if (in_len == 0) + return CKR_OK; + + CK_ULONG new_len; + if (__builtin_uaddl_overflow(data->buffer_len, in_len, &new_len)) + return CKR_ARGUMENTS_BAD; + CK_BYTE *new_buffer = sc_mem_secure_alloc(new_len); + if (!new_buffer) + return CKR_HOST_MEMORY; + + if (data->buffer_len != 0) + memcpy(new_buffer, data->buffer, data->buffer_len); + memcpy(new_buffer+data->buffer_len, in, in_len); + + sc_mem_secure_clear_free(data->buffer, data->buffer_len); + data->buffer = new_buffer; + data->buffer_len = new_len; + return CKR_OK; +} + +void _update_mech_info(CK_MECHANISM_INFO_PTR mech_info, CK_MECHANISM_INFO_PTR new_mech_info) { + if (new_mech_info->ulMaxKeySize > mech_info->ulMaxKeySize) + mech_info->ulMaxKeySize = new_mech_info->ulMaxKeySize; + if (new_mech_info->ulMinKeySize < mech_info->ulMinKeySize) + mech_info->ulMinKeySize = new_mech_info->ulMinKeySize; + mech_info->flags |= new_mech_info->flags; +} + +/* + * Copy a mechanism + */ +static CK_RV +sc_pkcs11_copy_mechanism(sc_pkcs11_mechanism_type_t *mt, + sc_pkcs11_mechanism_type_t **new_mt) +{ + CK_RV rv; + + *new_mt = calloc(1, sizeof(sc_pkcs11_mechanism_type_t)); + if (!(*new_mt)) + return CKR_HOST_MEMORY; + + memcpy(*new_mt, mt, sizeof(sc_pkcs11_mechanism_type_t)); + /* mech_data needs specific function for making copy*/ + if (mt->copy_mech_data + && (rv = mt->copy_mech_data(mt->mech_data, (void **) &(*new_mt)->mech_data)) != CKR_OK) { + free(*new_mt); + return rv; + } + + return CKR_OK; +} + /* * Register a mechanism + * Check whether the mechanism is already in p11card, + * if not, make a copy of the given mechanism and store it + * in p11card. */ CK_RV sc_pkcs11_register_mechanism(struct sc_pkcs11_card *p11card, - sc_pkcs11_mechanism_type_t *mt) + sc_pkcs11_mechanism_type_t *mt, sc_pkcs11_mechanism_type_t **result_mt) { + sc_pkcs11_mechanism_type_t *existing_mt; + sc_pkcs11_mechanism_type_t *copy_mt = NULL; sc_pkcs11_mechanism_type_t **p; + int i; + CK_RV rv; if (mt == NULL) return CKR_HOST_MEMORY; + if ((existing_mt = sc_pkcs11_find_mechanism(p11card, mt->mech, mt->mech_info.flags))) { + for (i = 0; i < MAX_KEY_TYPES; i++) { + if (existing_mt->key_types[i] == mt->key_types[0]) { + /* Mechanism already registered with the same key_type, + * just update it's info */ + _update_mech_info(&existing_mt->mech_info, &mt->mech_info); + return CKR_OK; + } + if (existing_mt->key_types[i] < 0) { + /* There is a free slot for new key type, let's add it and + * update mechanism info */ + _update_mech_info(&existing_mt->mech_info, &mt->mech_info); + /* XXX Should be changed to loop over mt->key_types, if + * we allow to register mechanism with multiple key types + * in one operation */ + existing_mt->key_types[i] = mt->key_types[0]; + if (i + 1 < MAX_KEY_TYPES) { + existing_mt->key_types[i + 1] = -1; + } + return CKR_OK; + } + } + sc_log(p11card->card->ctx, "Too many key types in mechanism 0x%lx, more than %d", mt->mech, MAX_KEY_TYPES); + return CKR_BUFFER_TOO_SMALL; + } + p = (sc_pkcs11_mechanism_type_t **) realloc(p11card->mechanisms, (p11card->nmechanisms + 2) * sizeof(*p)); if (p == NULL) return CKR_HOST_MEMORY; + if ((rv = sc_pkcs11_copy_mechanism(mt, ©_mt)) != CKR_OK) { + free(p); + return rv; + } p11card->mechanisms = p; - p[p11card->nmechanisms++] = mt; + p[p11card->nmechanisms++] = copy_mt; p[p11card->nmechanisms] = NULL; + /* Return registered mechanism for further use */ + if (result_mt) + *result_mt = copy_mt; return CKR_OK; } + +CK_RV +_validate_key_type(sc_pkcs11_mechanism_type_t *mech, CK_KEY_TYPE key_type) { + int i; + for (i = 0; i < MAX_KEY_TYPES; i++) { + if (mech->key_types[i] < 0) + break; + if ((CK_KEY_TYPE)mech->key_types[i] == key_type) + return CKR_OK; + } + return CKR_KEY_TYPE_INCONSISTENT; +} + /* * Look up a mechanism */ sc_pkcs11_mechanism_type_t * -sc_pkcs11_find_mechanism(struct sc_pkcs11_card *p11card, CK_MECHANISM_TYPE mech, unsigned int flags) +sc_pkcs11_find_mechanism(struct sc_pkcs11_card *p11card, CK_MECHANISM_TYPE mech, CK_FLAGS flags) { sc_pkcs11_mechanism_type_t *mt; unsigned int n; + if (p11card == NULL) + return NULL; for (n = 0; n < p11card->nmechanisms; n++) { mt = p11card->mechanisms[n]; if (mt && mt->mech == mech && ((mt->mech_info.flags & flags) == flags)) @@ -95,7 +226,7 @@ sc_pkcs11_get_mechanism_list(struct sc_pkcs11_card *p11card, { sc_pkcs11_mechanism_type_t *mt; unsigned int n, count = 0; - int rv; + CK_RV rv; if (!p11card) return CKR_TOKEN_NOT_PRESENT; @@ -166,7 +297,7 @@ sc_pkcs11_md_init(struct sc_pkcs11_session *session, struct sc_pkcs11_card *p11card; sc_pkcs11_operation_t *operation; sc_pkcs11_mechanism_type_t *mt; - int rv; + CK_RV rv; LOG_FUNC_CALLED(context); if (!session || !session->slot || !(p11card = session->slot->p11card)) @@ -179,7 +310,7 @@ sc_pkcs11_md_init(struct sc_pkcs11_session *session, rv = session_start_operation(session, SC_PKCS11_OPERATION_DIGEST, mt, &operation); if (rv != CKR_OK) - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM)); @@ -188,7 +319,7 @@ sc_pkcs11_md_init(struct sc_pkcs11_session *session, if (rv != CKR_OK) session_stop_operation(session, SC_PKCS11_OPERATION_DIGEST); - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); } CK_RV @@ -196,7 +327,7 @@ sc_pkcs11_md_update(struct sc_pkcs11_session *session, CK_BYTE_PTR pData, CK_ULONG ulDataLen) { sc_pkcs11_operation_t *op; - int rv; + CK_RV rv; rv = session_get_operation(session, SC_PKCS11_OPERATION_DIGEST, &op); if (rv != CKR_OK) @@ -208,7 +339,7 @@ sc_pkcs11_md_update(struct sc_pkcs11_session *session, if (rv != CKR_OK) session_stop_operation(session, SC_PKCS11_OPERATION_DIGEST); - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); } CK_RV @@ -220,7 +351,7 @@ sc_pkcs11_md_final(struct sc_pkcs11_session *session, rv = session_get_operation(session, SC_PKCS11_OPERATION_DIGEST, &op); if (rv != CKR_OK) - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); /* This is a request for the digest length */ if (pData == NULL) @@ -231,7 +362,7 @@ sc_pkcs11_md_final(struct sc_pkcs11_session *session, LOG_FUNC_RETURN(context, pData == NULL ? CKR_OK : CKR_BUFFER_TOO_SMALL); session_stop_operation(session, SC_PKCS11_OPERATION_DIGEST); - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); } /* @@ -240,12 +371,12 @@ sc_pkcs11_md_final(struct sc_pkcs11_session *session, */ CK_RV sc_pkcs11_sign_init(struct sc_pkcs11_session *session, CK_MECHANISM_PTR pMechanism, - struct sc_pkcs11_object *key, CK_MECHANISM_TYPE key_type) + struct sc_pkcs11_object *key, CK_KEY_TYPE key_type) { struct sc_pkcs11_card *p11card; sc_pkcs11_operation_t *operation; sc_pkcs11_mechanism_type_t *mt; - int rv; + CK_RV rv; LOG_FUNC_CALLED(context); if (!session || !session->slot || !(p11card = session->slot->p11card)) @@ -259,19 +390,29 @@ sc_pkcs11_sign_init(struct sc_pkcs11_session *session, CK_MECHANISM_PTR pMechani LOG_FUNC_RETURN(context, CKR_MECHANISM_INVALID); /* See if compatible with key type */ - if (mt->key_type != key_type) - LOG_FUNC_RETURN(context, CKR_KEY_TYPE_INCONSISTENT); + rv = _validate_key_type(mt, key_type); + if (rv != CKR_OK) + LOG_FUNC_RETURN(context, (int) rv); + + if (pMechanism->pParameter && + pMechanism->ulParameterLen > sizeof(operation->mechanism_params)) + LOG_FUNC_RETURN(context, CKR_ARGUMENTS_BAD); rv = session_start_operation(session, SC_PKCS11_OPERATION_SIGN, mt, &operation); if (rv != CKR_OK) - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM)); + if (pMechanism->pParameter) { + memcpy(&operation->mechanism_params, pMechanism->pParameter, + pMechanism->ulParameterLen); + operation->mechanism.pParameter = &operation->mechanism_params; + } rv = mt->sign_init(operation, key); if (rv != CKR_OK) session_stop_operation(session, SC_PKCS11_OPERATION_SIGN); - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); } CK_RV @@ -279,12 +420,12 @@ sc_pkcs11_sign_update(struct sc_pkcs11_session *session, CK_BYTE_PTR pData, CK_ULONG ulDataLen) { sc_pkcs11_operation_t *op; - int rv; + CK_RV rv; LOG_FUNC_CALLED(context); rv = session_get_operation(session, SC_PKCS11_OPERATION_SIGN, &op); if (rv != CKR_OK) - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); if (op->type->sign_update == NULL) { rv = CKR_KEY_TYPE_INCONSISTENT; @@ -297,7 +438,7 @@ sc_pkcs11_sign_update(struct sc_pkcs11_session *session, if (rv != CKR_OK) session_stop_operation(session, SC_PKCS11_OPERATION_SIGN); - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); } CK_RV @@ -305,12 +446,12 @@ sc_pkcs11_sign_final(struct sc_pkcs11_session *session, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { sc_pkcs11_operation_t *op; - int rv; + CK_RV rv; LOG_FUNC_CALLED(context); rv = session_get_operation(session, SC_PKCS11_OPERATION_SIGN, &op); if (rv != CKR_OK) - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); /* Bail out for signature mechanisms that don't do hashing */ if (op->type->sign_final == NULL) { @@ -324,18 +465,18 @@ sc_pkcs11_sign_final(struct sc_pkcs11_session *session, if (rv != CKR_BUFFER_TOO_SMALL && pSignature != NULL) session_stop_operation(session, SC_PKCS11_OPERATION_SIGN); - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); } CK_RV sc_pkcs11_sign_size(struct sc_pkcs11_session *session, CK_ULONG_PTR pLength) { sc_pkcs11_operation_t *op; - int rv; + CK_RV rv; rv = session_get_operation(session, SC_PKCS11_OPERATION_SIGN, &op); if (rv != CKR_OK) - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); /* Bail out for signature mechanisms that don't do hashing */ if (op->type->sign_size == NULL) { @@ -349,7 +490,7 @@ sc_pkcs11_sign_size(struct sc_pkcs11_session *session, CK_ULONG_PTR pLength) if (rv != CKR_OK) session_stop_operation(session, SC_PKCS11_OPERATION_SIGN); - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); } /* @@ -360,12 +501,12 @@ sc_pkcs11_signature_init(sc_pkcs11_operation_t *operation, struct sc_pkcs11_object *key) { struct hash_signature_info *info; - struct signature_data *data; + struct operation_data *data; CK_RV rv; int can_do_it = 0; LOG_FUNC_CALLED(context); - if (!(data = calloc(1, sizeof(*data)))) + if (!(data = new_operation_data())) LOG_FUNC_RETURN(context, CKR_HOST_MEMORY); data->info = NULL; data->key = key; @@ -382,15 +523,25 @@ sc_pkcs11_signature_init(sc_pkcs11_operation_t *operation, } else { /* Mechanism recognised but cannot be performed by pkcs#15 card, or some general error. */ - free(data); - LOG_FUNC_RETURN(context, rv); + operation_data_release(data); + LOG_FUNC_RETURN(context, (int) rv); + } + } + + /* Validate the mechanism parameters */ + if (key->ops->init_params) { + rv = key->ops->init_params(operation->session, &operation->mechanism); + if (rv != CKR_OK) { + /* Probably bad arguments */ + operation_data_release(data); + LOG_FUNC_RETURN(context, (int) rv); } } /* If this is a signature with hash operation, * and card cannot perform itself signature with hash operation, * set up the hash operation */ - info = (struct hash_signature_info *) operation->type->mech_data; + info = (struct hash_signature_info *)operation->type->mech_data; if (info != NULL && !can_do_it) { /* Initialize hash operation */ @@ -401,8 +552,8 @@ sc_pkcs11_signature_init(sc_pkcs11_operation_t *operation, rv = info->hash_type->md_init(data->md); if (rv != CKR_OK) { sc_pkcs11_release_operation(&data->md); - free(data); - LOG_FUNC_RETURN(context, rv); + operation_data_release(data); + LOG_FUNC_RETURN(context, (int) rv); } data->info = info; } @@ -415,51 +566,49 @@ static CK_RV sc_pkcs11_signature_update(sc_pkcs11_operation_t *operation, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { - struct signature_data *data; + struct operation_data *data; + CK_RV rv; LOG_FUNC_CALLED(context); sc_log(context, "data part length %li", ulPartLen); - data = (struct signature_data *) operation->priv_data; + data = (struct operation_data *)operation->priv_data; if (data->md) { - CK_RV rv = data->md->type->md_update(data->md, pPart, ulPartLen); - LOG_FUNC_RETURN(context, rv); + rv = data->md->type->md_update(data->md, pPart, ulPartLen); + LOG_FUNC_RETURN(context, (int) rv); } /* This signature mechanism operates on the raw data */ - if (data->buffer_len + ulPartLen > sizeof(data->buffer)) - LOG_FUNC_RETURN(context, CKR_DATA_LEN_RANGE); - memcpy(data->buffer + data->buffer_len, pPart, ulPartLen); - data->buffer_len += ulPartLen; - sc_log(context, "data length %u", data->buffer_len); - LOG_FUNC_RETURN(context, CKR_OK); + rv = signature_data_buffer_append(data, pPart, ulPartLen); + LOG_FUNC_RETURN(context, (int) rv); } static CK_RV sc_pkcs11_signature_final(sc_pkcs11_operation_t *operation, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { - struct signature_data *data; + struct operation_data *data; CK_RV rv; LOG_FUNC_CALLED(context); - data = (struct signature_data *) operation->priv_data; - sc_log(context, "data length %u", data->buffer_len); + data = (struct operation_data *)operation->priv_data; if (data->md) { sc_pkcs11_operation_t *md = data->md; - CK_ULONG len = sizeof(data->buffer); + CK_BYTE hash[64]; + CK_ULONG len = sizeof(hash); - rv = md->type->md_final(md, data->buffer, &len); + rv = md->type->md_final(md, hash, &len); if (rv == CKR_BUFFER_TOO_SMALL) rv = CKR_FUNCTION_FAILED; if (rv != CKR_OK) - LOG_FUNC_RETURN(context, rv); - data->buffer_len = len; + LOG_FUNC_RETURN(context, (int) rv); + rv = signature_data_buffer_append(data, hash, len); + if (rv != CKR_OK) + LOG_FUNC_RETURN(context, (int) rv); } - sc_log(context, "%u bytes to sign", data->buffer_len); rv = data->key->ops->sign(operation->session, data->key, &operation->mechanism, data->buffer, data->buffer_len, pSignature, pulSignatureLen); - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); } static CK_RV @@ -471,7 +620,7 @@ sc_pkcs11_signature_size(sc_pkcs11_operation_t *operation, CK_ULONG_PTR pLength) CK_ATTRIBUTE attr_key_type = { CKA_KEY_TYPE, &key_type, sizeof(key_type) }; CK_RV rv; - key = ((struct signature_data *) operation->priv_data)->key; + key = ((struct operation_data *)operation->priv_data)->key; /* * EC and GOSTR do not have CKA_MODULUS_BITS attribute. * But other code in framework treats them as if they do. @@ -485,37 +634,35 @@ sc_pkcs11_signature_size(sc_pkcs11_operation_t *operation, CK_ULONG_PTR pLength) rv = key->ops->get_attribute(operation->session, key, &attr); /* convert bits to bytes */ if (rv == CKR_OK) - *pLength = (*pLength + 7) / 8; + *pLength = BYTES4BITS(*pLength); break; case CKK_EC: + case CKK_EC_EDWARDS: + case CKK_EC_MONTGOMERY: /* TODO: -DEE we should use something other then CKA_MODULUS_BITS... */ rv = key->ops->get_attribute(operation->session, key, &attr); - *pLength = ((*pLength + 7)/8) * 2 ; /* 2*nLen in bytes */ + if (rv == CKR_OK) + *pLength = BYTES4BITS(*pLength) * 2 ; /* 2*nLen in bytes */ break; case CKK_GOSTR3410: rv = key->ops->get_attribute(operation->session, key, &attr); if (rv == CKR_OK) - *pLength = (*pLength + 7) / 8 * 2; + *pLength = BYTES4BITS(*pLength) * 2; break; default: rv = CKR_MECHANISM_INVALID; } } - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); } static void -sc_pkcs11_signature_release(sc_pkcs11_operation_t *operation) +sc_pkcs11_operation_release(sc_pkcs11_operation_t *operation) { - struct signature_data *data; - - data = (struct signature_data *) operation->priv_data; - if (!data) + if (!operation) return; - sc_pkcs11_release_operation(&data->md); - memset(data, 0, sizeof(*data)); - free(data); + operation_data_release(operation->priv_data); } #ifdef ENABLE_OPENSSL @@ -525,12 +672,12 @@ sc_pkcs11_signature_release(sc_pkcs11_operation_t *operation) */ CK_RV sc_pkcs11_verif_init(struct sc_pkcs11_session *session, CK_MECHANISM_PTR pMechanism, - struct sc_pkcs11_object *key, CK_MECHANISM_TYPE key_type) + struct sc_pkcs11_object *key, CK_KEY_TYPE key_type) { struct sc_pkcs11_card *p11card; sc_pkcs11_operation_t *operation; sc_pkcs11_mechanism_type_t *mt; - int rv; + CK_RV rv; if (!session || !session->slot || !(p11card = session->slot->p11card)) @@ -542,14 +689,25 @@ sc_pkcs11_verif_init(struct sc_pkcs11_session *session, CK_MECHANISM_PTR pMechan return CKR_MECHANISM_INVALID; /* See if compatible with key type */ - if (mt->key_type != key_type) - return CKR_KEY_TYPE_INCONSISTENT; + rv = _validate_key_type(mt, key_type); + if (rv != CKR_OK) + LOG_FUNC_RETURN(context, (int) rv); + + if (pMechanism->pParameter && + pMechanism->ulParameterLen > sizeof(operation->mechanism_params)) + LOG_FUNC_RETURN(context, CKR_ARGUMENTS_BAD); rv = session_start_operation(session, SC_PKCS11_OPERATION_VERIFY, mt, &operation); if (rv != CKR_OK) return rv; memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM)); + if (pMechanism->pParameter) { + memcpy(&operation->mechanism_params, pMechanism->pParameter, + pMechanism->ulParameterLen); + operation->mechanism.pParameter = &operation->mechanism_params; + } + rv = mt->verif_init(operation, key); if (rv != CKR_OK) @@ -564,7 +722,7 @@ sc_pkcs11_verif_update(struct sc_pkcs11_session *session, CK_BYTE_PTR pData, CK_ULONG ulDataLen) { sc_pkcs11_operation_t *op; - int rv; + CK_RV rv; rv = session_get_operation(session, SC_PKCS11_OPERATION_VERIFY, &op); if (rv != CKR_OK) @@ -589,7 +747,7 @@ sc_pkcs11_verif_final(struct sc_pkcs11_session *session, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { sc_pkcs11_operation_t *op; - int rv; + CK_RV rv; rv = session_get_operation(session, SC_PKCS11_OPERATION_VERIFY, &op); if (rv != CKR_OK) @@ -615,10 +773,10 @@ sc_pkcs11_verify_init(sc_pkcs11_operation_t *operation, struct sc_pkcs11_object *key) { struct hash_signature_info *info; - struct signature_data *data; - int rv; + struct operation_data *data; + CK_RV rv; - if (!(data = calloc(1, sizeof(*data)))) + if (!(data = new_operation_data())) return CKR_HOST_MEMORY; data->info = NULL; @@ -632,7 +790,17 @@ sc_pkcs11_verify_init(sc_pkcs11_operation_t *operation, else { /* Mechanism cannot be performed by pkcs#15 card, or some general error. */ free(data); - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); + } + } + + /* Validate the mechanism parameters */ + if (key->ops->init_params) { + rv = key->ops->init_params(operation->session, &operation->mechanism); + if (rv != CKR_OK) { + /* Probably bad arguments */ + free(data); + LOG_FUNC_RETURN(context, (int) rv); } } @@ -663,9 +831,9 @@ static CK_RV sc_pkcs11_verify_update(sc_pkcs11_operation_t *operation, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { - struct signature_data *data; + struct operation_data *data; - data = (struct signature_data *) operation->priv_data; + data = (struct operation_data *)operation->priv_data; if (data->md) { sc_pkcs11_operation_t *md = data->md; @@ -673,18 +841,15 @@ sc_pkcs11_verify_update(sc_pkcs11_operation_t *operation, } /* This verification mechanism operates on the raw data */ - if (data->buffer_len + ulPartLen > sizeof(data->buffer)) - return CKR_DATA_LEN_RANGE; - memcpy(data->buffer + data->buffer_len, pPart, ulPartLen); - data->buffer_len += ulPartLen; - return CKR_OK; + CK_RV rv = signature_data_buffer_append(data, pPart, ulPartLen); + LOG_FUNC_RETURN(context, (int) rv); } static CK_RV sc_pkcs11_verify_final(sc_pkcs11_operation_t *operation, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { - struct signature_data *data; + struct operation_data *data; struct sc_pkcs11_object *key; unsigned char *pubkey_value = NULL; CK_KEY_TYPE key_type; @@ -692,9 +857,9 @@ sc_pkcs11_verify_final(sc_pkcs11_operation_t *operation, CK_ATTRIBUTE attr = {CKA_VALUE, NULL, 0}; CK_ATTRIBUTE attr_key_type = {CKA_KEY_TYPE, &key_type, sizeof(key_type)}; CK_ATTRIBUTE attr_key_params = {CKA_GOSTR3410_PARAMS, ¶ms, sizeof(params)}; - int rv; + CK_RV rv; - data = (struct signature_data *) operation->priv_data; + data = (struct operation_data *)operation->priv_data; if (pSignature == NULL) return CKR_ARGUMENTS_BAD; @@ -706,7 +871,6 @@ sc_pkcs11_verify_final(sc_pkcs11_operation_t *operation, if (key_type != CKK_GOSTR3410) attr.type = CKA_SPKI; - rv = key->ops->get_attribute(operation->session, key, &attr); if (rv != CKR_OK) @@ -729,7 +893,7 @@ sc_pkcs11_verify_final(sc_pkcs11_operation_t *operation, rv = sc_pkcs11_verify_data(pubkey_value, attr.ulValueLen, params, sizeof(params), - operation->mechanism.mechanism, data->md, + &operation->mechanism, data->md, data->buffer, data->buffer_len, pSignature, ulSignatureLen); done: @@ -738,6 +902,136 @@ sc_pkcs11_verify_final(sc_pkcs11_operation_t *operation, return rv; } #endif +/* + * Initialize a encrypting context. When we get here, we know + * the key object is capable of encrypt _something_ + */ +CK_RV +sc_pkcs11_encr_init(struct sc_pkcs11_session *session, + CK_MECHANISM_PTR pMechanism, + struct sc_pkcs11_object *key, + CK_KEY_TYPE key_type) +{ + struct sc_pkcs11_card *p11card; + sc_pkcs11_operation_t *operation; + sc_pkcs11_mechanism_type_t *mt; + CK_RV rv; + + if (!session || !session->slot || !(p11card = session->slot->p11card)) + return CKR_ARGUMENTS_BAD; + + /* See if we support this mechanism type */ + mt = sc_pkcs11_find_mechanism(p11card, pMechanism->mechanism, CKF_ENCRYPT); + if (mt == NULL) + return CKR_MECHANISM_INVALID; + + /* See if compatible with key type */ + rv = _validate_key_type(mt, key_type); + if (rv != CKR_OK) + LOG_FUNC_RETURN(context, (int)rv); + + if (pMechanism->pParameter && + pMechanism->ulParameterLen > sizeof(operation->mechanism_params)) + LOG_FUNC_RETURN(context, CKR_ARGUMENTS_BAD); + + rv = session_start_operation(session, SC_PKCS11_OPERATION_ENCRYPT, mt, &operation); + if (rv != CKR_OK) + return rv; + + memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM)); + if (pMechanism->pParameter) { + memcpy(&operation->mechanism_params, pMechanism->pParameter, + pMechanism->ulParameterLen); + operation->mechanism.pParameter = &operation->mechanism_params; + } + rv = mt->encrypt_init(operation, key); + if (rv != CKR_OK) + goto out; + + /* Validate the mechanism parameters */ + if (key->ops->init_params) { + rv = key->ops->init_params(operation->session, &operation->mechanism); + if (rv != CKR_OK) + goto out; + } + LOG_FUNC_RETURN(context, (int)rv); +out: + session_stop_operation(session, SC_PKCS11_OPERATION_ENCRYPT); + LOG_FUNC_RETURN(context, (int)rv); +} + +CK_RV +sc_pkcs11_encr(struct sc_pkcs11_session *session, + CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) +{ + sc_pkcs11_operation_t *op; + CK_RV rv; + + rv = session_get_operation(session, SC_PKCS11_OPERATION_ENCRYPT, &op); + if (rv != CKR_OK) + return rv; + + rv = op->type->encrypt(op, pData, ulDataLen, + pEncryptedData, pulEncryptedDataLen); + + /* application is requesting buffer size ? */ + if (pEncryptedData == NULL) { + /* do not terminate session for CKR_OK */ + if (rv == CKR_OK) + LOG_FUNC_RETURN(context, CKR_OK); + } else if (rv == CKR_BUFFER_TOO_SMALL) + LOG_FUNC_RETURN(context, CKR_BUFFER_TOO_SMALL); + + session_stop_operation(session, SC_PKCS11_OPERATION_ENCRYPT); + LOG_FUNC_RETURN(context, (int)rv); +} + +CK_RV +sc_pkcs11_encr_update(struct sc_pkcs11_session *session, + CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) +{ + sc_pkcs11_operation_t *op; + CK_RV rv; + + rv = session_get_operation(session, SC_PKCS11_OPERATION_ENCRYPT, &op); + if (rv != CKR_OK) + return rv; + + rv = op->type->encrypt_update(op, pData, ulDataLen, + pEncryptedData, pulEncryptedDataLen); + + /* terminate session for any error except CKR_BUFFER_TOO_SMALL */ + if (rv != CKR_OK && rv != CKR_BUFFER_TOO_SMALL) + session_stop_operation(session, SC_PKCS11_OPERATION_ENCRYPT); + LOG_FUNC_RETURN(context, (int)rv); +} + +CK_RV +sc_pkcs11_encr_final(struct sc_pkcs11_session *session, + CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) +{ + sc_pkcs11_operation_t *op; + CK_RV rv; + + rv = session_get_operation(session, SC_PKCS11_OPERATION_ENCRYPT, &op); + if (rv != CKR_OK) + return rv; + + rv = op->type->encrypt_final(op, pEncryptedData, pulEncryptedDataLen); + + /* application is requesting buffer size ? */ + if (pEncryptedData == NULL) { + /* do not terminate session for CKR_OK */ + if (rv == CKR_OK) + LOG_FUNC_RETURN(context, CKR_OK); + } else if (rv == CKR_BUFFER_TOO_SMALL) + LOG_FUNC_RETURN(context, CKR_BUFFER_TOO_SMALL); + + session_stop_operation(session, SC_PKCS11_OPERATION_ENCRYPT); + LOG_FUNC_RETURN(context, (int)rv); +} /* * Initialize a decryption context. When we get here, we know @@ -747,7 +1041,7 @@ CK_RV sc_pkcs11_decr_init(struct sc_pkcs11_session *session, CK_MECHANISM_PTR pMechanism, struct sc_pkcs11_object *key, - CK_MECHANISM_TYPE key_type) + CK_KEY_TYPE key_type) { struct sc_pkcs11_card *p11card; sc_pkcs11_operation_t *operation; @@ -764,16 +1058,31 @@ sc_pkcs11_decr_init(struct sc_pkcs11_session *session, return CKR_MECHANISM_INVALID; /* See if compatible with key type */ - if (mt->key_type != key_type) - return CKR_KEY_TYPE_INCONSISTENT; + rv = _validate_key_type(mt, key_type); + if (rv != CKR_OK) + LOG_FUNC_RETURN(context, (int) rv); + + if (pMechanism->pParameter && + pMechanism->ulParameterLen > sizeof(operation->mechanism_params)) + LOG_FUNC_RETURN(context, CKR_ARGUMENTS_BAD); rv = session_start_operation(session, SC_PKCS11_OPERATION_DECRYPT, mt, &operation); if (rv != CKR_OK) return rv; memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM)); + if (pMechanism->pParameter) { + memcpy(&operation->mechanism_params, pMechanism->pParameter, + pMechanism->ulParameterLen); + operation->mechanism.pParameter = &operation->mechanism_params; + } rv = mt->decrypt_init(operation, key); + /* Validate the mechanism parameters */ + if (key->ops->init_params) { + rv = key->ops->init_params(operation->session, &operation->mechanism); + } + if (rv != CKR_OK) session_stop_operation(session, SC_PKCS11_OPERATION_DECRYPT); @@ -786,7 +1095,7 @@ sc_pkcs11_decr(struct sc_pkcs11_session *session, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { sc_pkcs11_operation_t *op; - int rv; + CK_RV rv; rv = session_get_operation(session, SC_PKCS11_OPERATION_DECRYPT, &op); if (rv != CKR_OK) @@ -795,8 +1104,154 @@ sc_pkcs11_decr(struct sc_pkcs11_session *session, rv = op->type->decrypt(op, pEncryptedData, ulEncryptedDataLen, pData, pulDataLen); - if (rv != CKR_BUFFER_TOO_SMALL && pData != NULL) + /* terminate session for any return value except CKR_BUFFER_TOO_SMALL, + * perform check in time side-channel free way to prevent Marvin attack */ + if (!constant_time_eq_s(rv, CKR_BUFFER_TOO_SMALL) && pData != NULL) + session_stop_operation(session, SC_PKCS11_OPERATION_DECRYPT); + + return rv; +} + +CK_RV +sc_pkcs11_decr_update(struct sc_pkcs11_session *session, + CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, + CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) +{ + sc_pkcs11_operation_t *op; + CK_RV rv; + + rv = session_get_operation(session, SC_PKCS11_OPERATION_DECRYPT, &op); + if (rv != CKR_OK) + return rv; + + rv = op->type->decrypt_update(op, pEncryptedData, ulEncryptedDataLen, + pData, pulDataLen); + + /* terminate session for any return value except CKR_BUFFER_TOO_SMALL, + * perform check in time side-channel free way to prevent Marvin attack */ + if (~constant_time_eq_s(rv, CKR_OK) & ~constant_time_eq_s(rv, CKR_BUFFER_TOO_SMALL)) session_stop_operation(session, SC_PKCS11_OPERATION_DECRYPT); + /* do not log error code to prevent side channel attack */ + return rv; +} + +CK_RV +sc_pkcs11_decr_final(struct sc_pkcs11_session *session, + CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) +{ + sc_pkcs11_operation_t *op; + CK_RV rv; + + rv = session_get_operation(session, SC_PKCS11_OPERATION_DECRYPT, &op); + if (rv != CKR_OK) + return rv; + + rv = op->type->decrypt_final(op, pData, pulDataLen); + + /* application is requesting buffer size ? */ + if (pData == NULL) { + /* do not terminate session for CKR_OK */ + if (rv == CKR_OK) + LOG_FUNC_RETURN(context, CKR_OK); + } else if (rv == CKR_BUFFER_TOO_SMALL) + LOG_FUNC_RETURN(context, CKR_BUFFER_TOO_SMALL); + + session_stop_operation(session, SC_PKCS11_OPERATION_DECRYPT); + LOG_FUNC_RETURN(context, (int)rv); +} + +CK_RV +sc_pkcs11_wrap(struct sc_pkcs11_session *session, + CK_MECHANISM_PTR pMechanism, + struct sc_pkcs11_object *wrappingKey, /* wrapping key */ + CK_KEY_TYPE key_type, /* type of the wrapping key */ + struct sc_pkcs11_object *targetKey, /* the key to be wrapped */ + CK_BYTE_PTR wrappedData, + CK_ULONG_PTR wrappedDataLen) +{ + struct sc_pkcs11_card *p11card; + sc_pkcs11_operation_t *operation; + sc_pkcs11_mechanism_type_t *mt; + CK_RV rv; + + if (!session || !session->slot || !(p11card = session->slot->p11card)) + return CKR_ARGUMENTS_BAD; + + /* See if we support this mechanism type */ + mt = sc_pkcs11_find_mechanism(p11card, pMechanism->mechanism, CKF_UNWRAP); + if (mt == NULL) + return CKR_MECHANISM_INVALID; + + /* See if compatible with key type */ + rv = _validate_key_type(mt, key_type); + if (rv != CKR_OK) + LOG_FUNC_RETURN(context, (int) rv); + + rv = session_start_operation(session, SC_PKCS11_OPERATION_WRAP, mt, &operation); + if (rv != CKR_OK) + return rv; + + memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM)); + + rv = operation->type->wrap(operation, wrappingKey, + targetKey, wrappedData, + wrappedDataLen); + + session_stop_operation(session, SC_PKCS11_OPERATION_WRAP); + + return rv; +} + +/* + * Unwrap a wrapped key into card. A new key object is created on card. + */ +CK_RV +sc_pkcs11_unwrap(struct sc_pkcs11_session *session, + CK_MECHANISM_PTR pMechanism, + struct sc_pkcs11_object *unwrappingKey, + CK_KEY_TYPE key_type, /* type of the unwrapping key */ + CK_BYTE_PTR pWrappedKey, /* the wrapped key */ + CK_ULONG ulWrappedKeyLen, /* bytes length of wrapped key */ + struct sc_pkcs11_object *targetKey) +{ + struct sc_pkcs11_card *p11card; + sc_pkcs11_operation_t *operation; + sc_pkcs11_mechanism_type_t *mt; + + CK_RV rv; + + if (!session || !session->slot || !(p11card = session->slot->p11card)) + return CKR_ARGUMENTS_BAD; + + /* See if we support this mechanism type */ + mt = sc_pkcs11_find_mechanism(p11card, pMechanism->mechanism, CKF_UNWRAP); + if (mt == NULL) + return CKR_MECHANISM_INVALID; + + /* See if compatible with key type */ + rv = _validate_key_type(mt, key_type); + if (rv != CKR_OK) + LOG_FUNC_RETURN(context, (int) rv); + + rv = session_start_operation(session, SC_PKCS11_OPERATION_UNWRAP, mt, &operation); + if (rv != CKR_OK) + return rv; + + memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM)); + + + /* + * TODO: does it make sense to support unwrapping to an in memory key object? + * This implementation assumes that the key should be unwrapped into a + * key object on card, regardless whether CKA_TOKEN = FALSE + * CKA_TOKEN = FALSE is considered an on card session object. + */ + + rv = operation->type->unwrap(operation, unwrappingKey, + pWrappedKey, ulWrappedKeyLen, + targetKey); + + session_stop_operation(session, SC_PKCS11_OPERATION_UNWRAP); return rv; } @@ -834,9 +1289,9 @@ sc_pkcs11_deri(struct sc_pkcs11_session *session, return CKR_MECHANISM_INVALID; /* See if compatible with key type */ - if (mt->key_type != key_type) - return CKR_KEY_TYPE_INCONSISTENT; - + rv = _validate_key_type(mt, key_type); + if (rv != CKR_OK) + LOG_FUNC_RETURN(context, (int) rv); rv = session_start_operation(session, SC_PKCS11_OPERATION_DERIVE, mt, &operation); if (rv != CKR_OK) @@ -847,7 +1302,7 @@ sc_pkcs11_deri(struct sc_pkcs11_session *session, /* Get the size of the data to be returned * If the card could derive a key an leave it on the card * then no data is returned. - * If the card returns the data, we will store it in the sercet key CKA_VALUE + * If the card returns the data, we will store it in the secret key CKA_VALUE */ ulDataLen = 0; @@ -867,7 +1322,7 @@ sc_pkcs11_deri(struct sc_pkcs11_session *session, goto out; } - /* Now do the actuall derivation */ + /* Now do the actual derivation */ rv = operation->type->derive(operation, basekey, pMechanism->pParameter, pMechanism->ulParameterLen, @@ -898,6 +1353,142 @@ sc_pkcs11_deri(struct sc_pkcs11_session *session, return rv; } +/* + * Initialize a encrypt operation + */ +static CK_RV +sc_pkcs11_encrypt_init(sc_pkcs11_operation_t *operation, + struct sc_pkcs11_object *key) +{ + struct operation_data *data; + CK_RV rv; + + if (!(data = new_operation_data())) + return CKR_HOST_MEMORY; + + data->key = key; + + if (key->ops->can_do) { + rv = key->ops->can_do(operation->session, key, operation->type->mech, CKF_ENCRYPT); + if ((rv == CKR_OK) || (rv == CKR_FUNCTION_NOT_SUPPORTED)) { + /* Mechanism recognized and can be performed by pkcs#15 card or algorithm references not supported */ + } else { + /* Mechanism cannot be performed by pkcs#15 card, or some general error. */ + free(data); + LOG_FUNC_RETURN(context, (int)rv); + } + } + + operation->priv_data = data; + + /* The last parameter is NULL - this is call to INIT code in underlying functions */ + return key->ops->encrypt(operation->session, + key, &operation->mechanism, NULL, 0, NULL, NULL); +} + +static CK_RV +sc_pkcs11_encrypt(sc_pkcs11_operation_t *operation, + CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) +{ + struct operation_data *data; + struct sc_pkcs11_object *key; + CK_RV rv; + CK_ULONG ulEncryptedDataLen, ulLastEncryptedPartLen; + + /* PKCS#11: If pBuf is not NULL_PTR, then *pulBufLen must contain the size in bytes.. */ + if (pEncryptedData && !pulEncryptedDataLen) + return CKR_ARGUMENTS_BAD; + + ulEncryptedDataLen = pulEncryptedDataLen ? *pulEncryptedDataLen : 0; + ulLastEncryptedPartLen = ulEncryptedDataLen; + + data = (struct operation_data *)operation->priv_data; + + key = data->key; + + /* Encrypt (Update) */ + rv = key->ops->encrypt(operation->session, key, &operation->mechanism, + pData, ulDataLen, pEncryptedData, &ulEncryptedDataLen); + + if (pulEncryptedDataLen) + *pulEncryptedDataLen = ulEncryptedDataLen; + + if (rv != CKR_OK) + return rv; + + /* recalculate buffer space */ + if (ulEncryptedDataLen <= ulLastEncryptedPartLen) + ulLastEncryptedPartLen -= ulEncryptedDataLen; + else + ulLastEncryptedPartLen = 0; + + /* EncryptFinalize */ + rv = key->ops->encrypt(operation->session, key, &operation->mechanism, + NULL, 0, + pEncryptedData ? pEncryptedData + ulEncryptedDataLen : NULL, + &ulLastEncryptedPartLen); + + if (pulEncryptedDataLen) + *pulEncryptedDataLen = ulEncryptedDataLen + ulLastEncryptedPartLen; + return rv; +} + +static CK_RV +sc_pkcs11_encrypt_update(sc_pkcs11_operation_t *operation, + CK_BYTE_PTR pPart, CK_ULONG ulPartLen, + CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) +{ + struct operation_data *data; + struct sc_pkcs11_object *key; + CK_RV rv; + CK_ULONG ulEncryptedPartLen; + + /* PKCS#11: If pBuf is not NULL_PTR, then *pulBufLen must contain the size in bytes.. */ + if (pEncryptedPart && !pulEncryptedPartLen) + return CKR_ARGUMENTS_BAD; + + ulEncryptedPartLen = pulEncryptedPartLen ? *pulEncryptedPartLen : 0; + + data = (struct operation_data *)operation->priv_data; + + key = data->key; + + rv = key->ops->encrypt(operation->session, key, &operation->mechanism, + pPart, ulPartLen, pEncryptedPart, &ulEncryptedPartLen); + + if (pulEncryptedPartLen) + *pulEncryptedPartLen = ulEncryptedPartLen; + return rv; +} + +static CK_RV +sc_pkcs11_encrypt_final(sc_pkcs11_operation_t *operation, + CK_BYTE_PTR pLastEncryptedPart, + CK_ULONG_PTR pulLastEncryptedPartLen) +{ + struct operation_data *data; + struct sc_pkcs11_object *key; + CK_RV rv; + CK_ULONG ulLastEncryptedPartLen; + + /* PKCS#11: If pBuf is not NULL_PTR, then *pulBufLen must contain the size in bytes.. */ + if (pLastEncryptedPart && !pulLastEncryptedPartLen) + return CKR_ARGUMENTS_BAD; + + ulLastEncryptedPartLen = pulLastEncryptedPartLen ? *pulLastEncryptedPartLen : 0; + + data = (struct operation_data *)operation->priv_data; + + key = data->key; + + rv = key->ops->encrypt(operation->session, key, &operation->mechanism, + NULL, 0, pLastEncryptedPart, &ulLastEncryptedPartLen); + + if (pulLastEncryptedPartLen) + *pulLastEncryptedPartLen = ulLastEncryptedPartLen; + return rv; +} /* * Initialize a decrypt operation @@ -906,10 +1497,10 @@ static CK_RV sc_pkcs11_decrypt_init(sc_pkcs11_operation_t *operation, struct sc_pkcs11_object *key) { - struct signature_data *data; + struct operation_data *data; CK_RV rv; - if (!(data = calloc(1, sizeof(*data)))) + if (!(data = new_operation_data())) return CKR_HOST_MEMORY; data->key = key; @@ -922,12 +1513,14 @@ sc_pkcs11_decrypt_init(sc_pkcs11_operation_t *operation, else { /* Mechanism cannot be performed by pkcs#15 card, or some general error. */ free(data); - LOG_FUNC_RETURN(context, rv); + LOG_FUNC_RETURN(context, (int) rv); } } - operation->priv_data = data; - return CKR_OK; + + /* The last parameter is NULL - this is call to INIT code in underlying functions */ + return key->ops->decrypt(operation->session, + key, &operation->mechanism, NULL, 0, NULL, NULL); } static CK_RV @@ -935,16 +1528,109 @@ sc_pkcs11_decrypt(sc_pkcs11_operation_t *operation, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { - struct signature_data *data; + struct operation_data *data; struct sc_pkcs11_object *key; + CK_RV rv; + CK_ULONG ulDataLen, ulLastDataLen; + + /* PKCS#11: If pBuf is not NULL_PTR, then *pulBufLen must contain the size in bytes.. */ + if (pData && !pulDataLen) + return CKR_ARGUMENTS_BAD; - data = (struct signature_data*) operation->priv_data; + ulDataLen = pulDataLen ? *pulDataLen : 0; + ulLastDataLen = ulDataLen; + + data = (struct operation_data *)operation->priv_data; key = data->key; - return key->ops->decrypt(operation->session, - key, &operation->mechanism, - pEncryptedData, ulEncryptedDataLen, - pData, pulDataLen); + + /* Decrypt */ + rv = key->ops->decrypt(operation->session, key, &operation->mechanism, + pEncryptedData, ulEncryptedDataLen, pData, &ulDataLen); + + if (pulDataLen) + *pulDataLen = ulDataLen; + + /* Skip DecryptFinalize for PKCS#1 v1.5 padding to prevent time side-channel leakage */ + if (((CK_MECHANISM_PTR)&operation->mechanism)->mechanism == CKM_RSA_PKCS) + return rv; + + if (rv != CKR_OK) + return rv; + + /* recalculate buffer space */ + if (ulDataLen <= ulLastDataLen) + ulLastDataLen -= ulDataLen; + else + ulLastDataLen = 0; + + /* DecryptFinalize */ + rv = key->ops->decrypt(operation->session, key, &operation->mechanism, + NULL, 0, pData ? pData + ulDataLen : NULL, + &ulLastDataLen); + if (pulDataLen) + *pulDataLen = ulDataLen + ulLastDataLen; + return rv; +} + +static CK_RV +sc_pkcs11_decrypt_update(sc_pkcs11_operation_t *operation, + CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, + CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) +{ + struct operation_data *data; + struct sc_pkcs11_object *key; + CK_RV rv; + CK_ULONG ulPartLen; + + /* PKCS#11: If pBuf is not NULL_PTR, then *pulBufLen must contain the size in bytes.. */ + if (pPart && !pulPartLen) + return CKR_ARGUMENTS_BAD; + + ulPartLen = pulPartLen ? *pulPartLen : 0; + + data = (struct operation_data *)operation->priv_data; + + key = data->key; + + rv = key->ops->decrypt(operation->session, + key, &operation->mechanism, + pEncryptedPart, ulEncryptedPartLen, + pPart, &ulPartLen); + + if (pulPartLen) + *pulPartLen = ulPartLen; + return rv; +} + +static CK_RV +sc_pkcs11_decrypt_final(sc_pkcs11_operation_t *operation, + CK_BYTE_PTR pLastPart, + CK_ULONG_PTR pulLastPartLen) +{ + struct operation_data *data; + struct sc_pkcs11_object *key; + CK_RV rv; + CK_ULONG ulLastPartLen; + + /* PKCS#11: If pBuf is not NULL_PTR, then *pulBufLen must contain the size in bytes.. */ + if (pLastPart && !pulLastPartLen) + return CKR_ARGUMENTS_BAD; + + ulLastPartLen = pulLastPartLen ? *pulLastPartLen : 0; + + data = (struct operation_data *)operation->priv_data; + + key = data->key; + + rv = key->ops->decrypt(operation->session, + key, &operation->mechanism, + NULL, 0, + pLastPart, &ulLastPartLen); + + if (pulLastPartLen) + *pulLastPartLen = ulLastPartLen; + return rv; } static CK_RV @@ -961,6 +1647,39 @@ sc_pkcs11_derive(sc_pkcs11_operation_t *operation, pData, pulDataLen); } + +static CK_RV +sc_pkcs11_wrap_operation(sc_pkcs11_operation_t *operation, + struct sc_pkcs11_object *wrappingKey, + struct sc_pkcs11_object *targetKey, + CK_BYTE_PTR pWrappedData, CK_ULONG_PTR ulWrappedDataLen) +{ + if (!operation || !wrappingKey || !wrappingKey->ops || !wrappingKey->ops->wrap_key) + return CKR_ARGUMENTS_BAD; + + return wrappingKey->ops->wrap_key(operation->session, + wrappingKey, + &operation->mechanism, + targetKey, pWrappedData, + ulWrappedDataLen); +} + +static CK_RV +sc_pkcs11_unwrap_operation(sc_pkcs11_operation_t *operation, + struct sc_pkcs11_object *unwrappingKey, + CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen, + struct sc_pkcs11_object *targetKey) +{ + if (!operation || !unwrappingKey || !unwrappingKey->ops || !unwrappingKey->ops->unwrap_key) + return CKR_ARGUMENTS_BAD; + + return unwrappingKey->ops->unwrap_key(operation->session, + unwrappingKey, + &operation->mechanism, + pWrappedKey, ulWrappedKeyLen, + targetKey); +} + /* * Create new mechanism type for a mechanism supported by * the card @@ -970,7 +1689,8 @@ sc_pkcs11_new_fw_mechanism(CK_MECHANISM_TYPE mech, CK_MECHANISM_INFO_PTR pInfo, CK_KEY_TYPE key_type, const void *priv_data, - void (*free_priv_data)(const void *priv_data)) + void (*free_priv_data)(const void *priv_data), + CK_RV (*copy_priv_data)(const void *mech_data, void **new_data)) { sc_pkcs11_mechanism_type_t *mt; @@ -979,12 +1699,14 @@ sc_pkcs11_new_fw_mechanism(CK_MECHANISM_TYPE mech, return mt; mt->mech = mech; mt->mech_info = *pInfo; - mt->key_type = key_type; + mt->key_types[0] = (int)key_type; + mt->key_types[1] = -1; mt->mech_data = priv_data; mt->free_mech_data = free_priv_data; + mt->copy_mech_data = copy_priv_data; mt->obj_size = sizeof(sc_pkcs11_operation_t); - mt->release = sc_pkcs11_signature_release; + mt->release = sc_pkcs11_operation_release; if (pInfo->flags & CKF_SIGN) { mt->sign_init = sc_pkcs11_signature_init; @@ -997,8 +1719,11 @@ sc_pkcs11_new_fw_mechanism(CK_MECHANISM_TYPE mech, mt->verif_final = sc_pkcs11_verify_final; #endif } + if (pInfo->flags & CKF_WRAP) { + mt->wrap = sc_pkcs11_wrap_operation; + } if (pInfo->flags & CKF_UNWRAP) { - /* TODO */ + mt->unwrap = sc_pkcs11_unwrap_operation; } if (pInfo->flags & CKF_DERIVE) { mt->derive = sc_pkcs11_derive; @@ -1006,11 +1731,29 @@ sc_pkcs11_new_fw_mechanism(CK_MECHANISM_TYPE mech, if (pInfo->flags & CKF_DECRYPT) { mt->decrypt_init = sc_pkcs11_decrypt_init; mt->decrypt = sc_pkcs11_decrypt; + mt->decrypt_update = sc_pkcs11_decrypt_update; + mt->decrypt_final = sc_pkcs11_decrypt_final; + } + if (pInfo->flags & CKF_ENCRYPT) { + mt->encrypt_init = sc_pkcs11_encrypt_init; + mt->encrypt = sc_pkcs11_encrypt; + mt->encrypt_update = sc_pkcs11_encrypt_update; + mt->encrypt_final = sc_pkcs11_encrypt_final; } return mt; } +void sc_pkcs11_free_mechanism(sc_pkcs11_mechanism_type_t **mt) +{ + if (!mt || !(*mt)) + return; + if ((*mt)->free_mech_data) + (*mt)->free_mech_data((*mt)->mech_data); + free(*mt); + *mt = NULL; +} + /* * Register generic mechanisms */ @@ -1028,6 +1771,19 @@ void free_info(const void *info) free((void *) info); } +CK_RV copy_hash_signature_info(const void *mech_data, void **new_data) +{ + if (mech_data == NULL || new_data == NULL) + return CKR_ARGUMENTS_BAD; + + *new_data = calloc(1, sizeof(struct hash_signature_info)); + if (!(*new_data)) + return CKR_HOST_MEMORY; + + memcpy(*new_data, mech_data, sizeof(struct hash_signature_info)); + return CKR_OK; +} + /* * Register a sign+hash algorithm derived from an algorithm supported * by the token + a software hash mechanism @@ -1040,27 +1796,40 @@ sc_pkcs11_register_sign_and_hash_mechanism(struct sc_pkcs11_card *p11card, { sc_pkcs11_mechanism_type_t *hash_type, *new_type; struct hash_signature_info *info; - CK_MECHANISM_INFO mech_info = sign_type->mech_info; + CK_MECHANISM_INFO mech_info; + CK_RV rv; + + LOG_FUNC_CALLED(p11card->card->ctx); + sc_log(p11card->card->ctx, "mech = 0x%lx, hash_mech = 0x%lx", mech, hash_mech); + + if (!sign_type) + LOG_FUNC_RETURN(p11card->card->ctx, CKR_MECHANISM_INVALID); + mech_info = sign_type->mech_info; if (!(hash_type = sc_pkcs11_find_mechanism(p11card, hash_mech, CKF_DIGEST))) - return CKR_MECHANISM_INVALID; + LOG_FUNC_RETURN(p11card->card->ctx, CKR_MECHANISM_INVALID); /* These hash-based mechs can only be used for sign/verify */ mech_info.flags &= (CKF_SIGN | CKF_SIGN_RECOVER | CKF_VERIFY | CKF_VERIFY_RECOVER); info = calloc(1, sizeof(*info)); if (!info) - LOG_FUNC_RETURN(p11card->card->ctx, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(p11card->card->ctx, CKR_HOST_MEMORY); info->mech = mech; - info->sign_type = sign_type; info->hash_type = hash_type; info->sign_mech = sign_type->mech; info->hash_mech = hash_mech; - new_type = sc_pkcs11_new_fw_mechanism(mech, &mech_info, sign_type->key_type, info, free_info); + new_type = sc_pkcs11_new_fw_mechanism(mech, &mech_info, sign_type->key_types[0], + info, free_info, copy_hash_signature_info); + if (!new_type) { + free_info(info); + LOG_FUNC_RETURN(p11card->card->ctx, CKR_HOST_MEMORY); + } - if (!new_type) - return CKR_HOST_MEMORY; - return sc_pkcs11_register_mechanism(p11card, new_type); + rv = sc_pkcs11_register_mechanism(p11card, new_type, NULL); + sc_pkcs11_free_mechanism(&new_type); + + LOG_FUNC_RETURN(p11card->card->ctx, (int)rv); } diff --git a/src/pkcs11/misc.c b/src/pkcs11/misc.c index 3847885e84..1d893d6181 100644 --- a/src/pkcs11/misc.c +++ b/src/pkcs11/misc.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -23,6 +23,7 @@ #include #include +#include "common/constant-time.h" #include "sc-pkcs11.h" #define DUMP_TEMPLATE_MAX 32 @@ -43,16 +44,26 @@ static struct sc_to_cryptoki_error_conversion sc_to_cryptoki_error_map[] = { void strcpy_bp(u8 * dst, const char *src, size_t dstsize) { - size_t c; - - if (!dst || !src || !dstsize) + if (!dst || !dstsize) return; memset((char *)dst, ' ', dstsize); - c = strlen(src) > dstsize ? dstsize : strlen(src); + if (src) { + size_t src_len = strlen(src); - memcpy((char *)dst, src, c); + if (src_len > dstsize) { + /* string will be truncated */ + memcpy((char *)dst, src, dstsize); + if (dstsize > 3) { + /* show truncation with '...' */ + /* FIXME avoid breaking an UTF-8 character on multiple bytes */ + memset((char *)dst + dstsize - 3, '.', 3); + } + } else { + memcpy((char *)dst, src, src_len); + } + } } @@ -105,6 +116,8 @@ static CK_RV sc_to_cryptoki_error_common(int rc) return CKR_DEVICE_MEMORY; case SC_ERROR_MEMORY_FAILURE: /* EEPROM has failed */ return CKR_DEVICE_ERROR; + case SC_ERROR_WRONG_PADDING: + return CKR_ENCRYPTED_DATA_INVALID; } return CKR_GENERAL_ERROR; } @@ -140,7 +153,7 @@ CK_RV restore_login_state(struct sc_pkcs11_slot *slot) if (sc_pkcs11_conf.atomic && slot) { if (list_iterator_start(&slot->logins)) { struct sc_pkcs11_login *login = list_iterator_next(&slot->logins); - while (login) { + while (login && slot->p11card && slot->p11card->framework) { r = slot->p11card->framework->login(slot, login->userType, login->pPin, login->ulPinLen); if (r != CKR_OK) @@ -162,7 +175,7 @@ CK_RV reset_login_state(struct sc_pkcs11_slot *slot, CK_RV rv) slot->p11card->framework->logout(slot); } - if (rv == CKR_USER_NOT_LOGGED_IN) { + if (constant_time_eq_s(rv, CKR_USER_NOT_LOGGED_IN)) { slot->login_user = -1; pop_all_login_states(slot); } @@ -187,7 +200,7 @@ CK_RV push_login_state(struct sc_pkcs11_slot *slot, } if (pPin && ulPinLen) { - login->pPin = sc_mem_alloc_secure(context, (sizeof *pPin)*ulPinLen); + login->pPin = sc_mem_secure_alloc((sizeof *pPin)*ulPinLen); if (login->pPin == NULL) { goto err; } @@ -206,8 +219,7 @@ CK_RV push_login_state(struct sc_pkcs11_slot *slot, err: if (login) { if (login->pPin) { - sc_mem_clear(login->pPin, login->ulPinLen); - free(login->pPin); + sc_mem_secure_clear_free(login->pPin, login->ulPinLen); } free(login); } @@ -222,8 +234,7 @@ void pop_login_state(struct sc_pkcs11_slot *slot) if (size > 0) { struct sc_pkcs11_login *login = list_get_at(&slot->logins, size-1); if (login) { - sc_mem_clear(login->pPin, login->ulPinLen); - free(login->pPin); + sc_mem_secure_clear_free(login->pPin, login->ulPinLen); free(login); } if (0 > list_delete_at(&slot->logins, size-1)) @@ -237,8 +248,7 @@ void pop_all_login_states(struct sc_pkcs11_slot *slot) if (sc_pkcs11_conf.atomic && slot) { struct sc_pkcs11_login *login = list_fetch(&slot->logins); while (login) { - sc_mem_clear(login->pPin, login->ulPinLen); - free(login->pPin); + sc_mem_secure_clear_free(login->pPin, login->ulPinLen); free(login); login = list_fetch(&slot->logins); } @@ -305,7 +315,7 @@ CK_RV session_stop_operation(struct sc_pkcs11_session * session, int type) CK_RV attr_extract(CK_ATTRIBUTE_PTR pAttr, void *ptr, size_t * sizep) { - unsigned int size; + size_t size; if (sizep) { size = *sizep; @@ -439,6 +449,31 @@ CK_RV attr_find_var(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_ULONG type, return attr_extract(pTemplate, ptr, sizep); } +static int is_nss_browser(sc_context_t * ctx) +{ + const char *basename; +#ifdef _WIN32 + const char sep = '\\'; +#else + const char sep = '/'; +#endif + if (!ctx || !ctx->exe_path) + return 0; + + basename = strrchr(ctx->exe_path, sep); + if (!basename) + basename = ctx->exe_path; + else + /* discard the separator */ + basename += sizeof(char); + + if (strstr(basename, "chromium") || strstr(basename, "chrome") + || strstr(basename, "firefox") || strstr(basename, "msedge")) + return 1; + + return 0; +} + void load_pkcs11_parameters(struct sc_pkcs11_config *conf, sc_context_t * ctx) { scconf_block *conf_block = NULL; @@ -447,28 +482,29 @@ void load_pkcs11_parameters(struct sc_pkcs11_config *conf, sc_context_t * ctx) /* Set defaults */ conf->max_virtual_slots = 16; - if (strcmp(ctx->app_name, "onepin-opensc-pkcs11") == 0) { + if (is_nss_browser(ctx)) { + /* NSS verifies *every* PIN even though only a single one would be + * needed to use one specific key. In known NSS browsers, we set + * slots_per_card to `1` to only look at the authentication PIN, i.e. + * ignoring a potential signature PIN. */ conf->slots_per_card = 1; } else { conf->slots_per_card = 4; } - conf->hide_empty_tokens = 1; conf->atomic = 0; conf->lock_login = 0; conf->init_sloppy = 1; conf->pin_unblock_style = SC_PKCS11_PIN_UNBLOCK_NOT_ALLOWED; conf->create_puk_slot = 0; - conf->zero_ckaid_for_ca_certs = 0; conf->create_slots_flags = SC_PKCS11_SLOT_CREATE_ALL; conf_block = sc_get_conf_block(ctx, "pkcs11", NULL, 1); if (!conf_block) - return; + goto out; /* contains the defaults, if there is a "pkcs11" config block */ conf->max_virtual_slots = scconf_get_int(conf_block, "max_virtual_slots", conf->max_virtual_slots); conf->slots_per_card = scconf_get_int(conf_block, "slots_per_card", conf->slots_per_card); - conf->hide_empty_tokens = scconf_get_bool(conf_block, "hide_empty_tokens", conf->hide_empty_tokens); conf->atomic = scconf_get_bool(conf_block, "atomic", conf->atomic); if (conf->atomic) conf->lock_login = 1; @@ -484,7 +520,6 @@ void load_pkcs11_parameters(struct sc_pkcs11_config *conf, sc_context_t * ctx) conf->pin_unblock_style = SC_PKCS11_PIN_UNBLOCK_SO_LOGGED_INITPIN; conf->create_puk_slot = scconf_get_bool(conf_block, "create_puk_slot", conf->create_puk_slot); - conf->zero_ckaid_for_ca_certs = scconf_get_bool(conf_block, "zero_ckaid_for_ca_certs", conf->zero_ckaid_for_ca_certs); create_slots_for_pins = (char *)scconf_get_str(conf_block, "create_slots_for_pins", "all"); conf->create_slots_flags = 0; @@ -499,12 +534,13 @@ void load_pkcs11_parameters(struct sc_pkcs11_config *conf, sc_context_t * ctx) conf->create_slots_flags |= SC_PKCS11_SLOT_CREATE_ALL; op = strtok(NULL, " ,"); } - free(tmp); + free(tmp); +out: sc_log(ctx, "PKCS#11 options: max_virtual_slots=%d slots_per_card=%d " - "hide_empty_tokens=%d lock_login=%d atomic=%d pin_unblock_style=%d " - "zero_ckaid_for_ca_certs=%d create_slots_flags=0x%X", + "lock_login=%d atomic=%d pin_unblock_style=%d " + "create_slots_flags=0x%X", conf->max_virtual_slots, conf->slots_per_card, - conf->hide_empty_tokens, conf->lock_login, conf->atomic, conf->pin_unblock_style, - conf->zero_ckaid_for_ca_certs, conf->create_slots_flags); + conf->lock_login, conf->atomic, conf->pin_unblock_style, + conf->create_slots_flags); } diff --git a/src/pkcs11/opensc.module.in b/src/pkcs11/opensc.module.in new file mode 100644 index 0000000000..a3050801cd --- /dev/null +++ b/src/pkcs11/opensc.module.in @@ -0,0 +1,9 @@ +# This file describes how to load the opensc module +# See: https://p11-glue.github.io/p11-glue/p11-kit/manual/pkcs11-conf.html +# or man pkcs11.conf + +# This is a relative path, which means it will be loaded from +# the p11-kit default path which is usually $(libdir)/pkcs11. +# Doing it this way allows for packagers to package opensc for +# 32-bit and 64-bit and make them parallel installable +module: opensc-pkcs11@DYN_LIB_EXT@ diff --git a/src/pkcs11/openssl.c b/src/pkcs11/openssl.c index 50b9b0a614..be70f70dad 100644 --- a/src/pkcs11/openssl.c +++ b/src/pkcs11/openssl.c @@ -16,21 +16,28 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #ifdef ENABLE_OPENSSL /* empty file without openssl */ #include +#include #include #include #include #include #include -#if OPENSSL_VERSION_NUMBER >= 0x10000000L +#include #include #include /* for OPENSSL_NO_* */ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#define OPENSSL_NO_ENGINE +#include +#include +#endif +#include "libopensc/sc-ossl-compat.h" #ifndef OPENSSL_NO_EC #include #endif /* OPENSSL_NO_EC */ @@ -39,7 +46,6 @@ #endif /* OPENSSL_NO_ENGINE */ #include #include -#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ #include "sc-pkcs11.h" @@ -50,10 +56,11 @@ static CK_RV sc_pkcs11_openssl_md_final(sc_pkcs11_operation_t *, CK_BYTE_PTR, CK_ULONG_PTR); static void sc_pkcs11_openssl_md_release(sc_pkcs11_operation_t *); +// clang-format off static sc_pkcs11_mechanism_type_t openssl_sha1_mech = { CKM_SHA_1, { 0, 0, CKF_DIGEST }, - 0, + { -1 }, sizeof(struct sc_pkcs11_operation), sc_pkcs11_openssl_md_release, sc_pkcs11_openssl_md_init, @@ -61,17 +68,41 @@ static sc_pkcs11_mechanism_type_t openssl_sha1_mech = { sc_pkcs11_openssl_md_final, NULL, NULL, NULL, NULL, /* sign_* */ NULL, NULL, NULL, /* verif_* */ - NULL, NULL, /* decrypt_* */ + NULL, NULL, NULL,NULL, /* decrypt_* */ + NULL, NULL, NULL, NULL, /* encrypt */ NULL, /* derive */ + NULL, /* wrap */ + NULL, /* unwrap */ NULL, /* mech_data */ NULL, /* free_mech_data */ + NULL, /* copy_mech_data */ +}; + +static sc_pkcs11_mechanism_type_t openssl_sha224_mech = { + CKM_SHA224, + { 0, 0, CKF_DIGEST }, + { -1 }, + sizeof(struct sc_pkcs11_operation), + sc_pkcs11_openssl_md_release, + sc_pkcs11_openssl_md_init, + sc_pkcs11_openssl_md_update, + sc_pkcs11_openssl_md_final, + NULL, NULL, NULL, NULL, /* sign_* */ + NULL, NULL, NULL, /* verif_* */ + NULL, NULL, NULL, NULL, /* decrypt_* */ + NULL, NULL, NULL, NULL, /* encrypt */ + NULL, /* derive */ + NULL, /* wrap */ + NULL, /* unwrap */ + NULL, /* mech_data */ + NULL, /* free_mech_data */ + NULL, /* copy_mech_data */ }; -#if OPENSSL_VERSION_NUMBER >= 0x00908000L static sc_pkcs11_mechanism_type_t openssl_sha256_mech = { CKM_SHA256, { 0, 0, CKF_DIGEST }, - 0, + { -1 }, sizeof(struct sc_pkcs11_operation), sc_pkcs11_openssl_md_release, sc_pkcs11_openssl_md_init, @@ -79,16 +110,20 @@ static sc_pkcs11_mechanism_type_t openssl_sha256_mech = { sc_pkcs11_openssl_md_final, NULL, NULL, NULL, NULL, /* sign_* */ NULL, NULL, NULL, /* verif_* */ - NULL, NULL, /* decrypt_* */ + NULL, NULL, NULL, NULL, /* decrypt_* */ + NULL, NULL, NULL, NULL, /* encrypt */ NULL, /* derive */ + NULL, /* wrap */ + NULL, /* unwrap */ NULL, /* mech_data */ NULL, /* free_mech_data */ + NULL, /* copy_mech_data */ }; static sc_pkcs11_mechanism_type_t openssl_sha384_mech = { CKM_SHA384, { 0, 0, CKF_DIGEST }, - 0, + { -1 }, sizeof(struct sc_pkcs11_operation), sc_pkcs11_openssl_md_release, sc_pkcs11_openssl_md_init, @@ -96,16 +131,20 @@ static sc_pkcs11_mechanism_type_t openssl_sha384_mech = { sc_pkcs11_openssl_md_final, NULL, NULL, NULL, NULL, /* sign_* */ NULL, NULL, NULL, /* verif_* */ - NULL, NULL, /* decrypt_* */ + NULL, NULL, NULL, NULL, /* decrypt_* */ + NULL, NULL, NULL, NULL, /* encrypt */ NULL, /* derive */ + NULL, /* wrap */ + NULL, /* unwrap */ NULL, /* mech_data */ NULL, /* free_mech_data */ + NULL, /* copy_mech_data */ }; static sc_pkcs11_mechanism_type_t openssl_sha512_mech = { CKM_SHA512, { 0, 0, CKF_DIGEST }, - 0, + { -1 }, sizeof(struct sc_pkcs11_operation), sc_pkcs11_openssl_md_release, sc_pkcs11_openssl_md_init, @@ -113,18 +152,20 @@ static sc_pkcs11_mechanism_type_t openssl_sha512_mech = { sc_pkcs11_openssl_md_final, NULL, NULL, NULL, NULL, /* sign_* */ NULL, NULL, NULL, /* verif_* */ - NULL, NULL, /* decrypt_* */ + NULL, NULL, NULL, NULL, /* decrypt_* */ + NULL, NULL, NULL, NULL, /* encrypt */ NULL, /* derive */ + NULL, /* wrap */ + NULL, /* unwrap */ NULL, /* mech_data */ NULL, /* free_mech_data */ + NULL, /* copy_mech_data */ }; -#endif -#if OPENSSL_VERSION_NUMBER >= 0x10000000L static sc_pkcs11_mechanism_type_t openssl_gostr3411_mech = { CKM_GOSTR3411, { 0, 0, CKF_DIGEST }, - 0, + { -1 }, sizeof(struct sc_pkcs11_operation), sc_pkcs11_openssl_md_release, sc_pkcs11_openssl_md_init, @@ -132,17 +173,20 @@ static sc_pkcs11_mechanism_type_t openssl_gostr3411_mech = { sc_pkcs11_openssl_md_final, NULL, NULL, NULL, NULL, /* sign_* */ NULL, NULL, NULL, /* verif_* */ - NULL, NULL, /* decrypt_* */ + NULL, NULL, NULL,NULL, /* decrypt_* */ + NULL, NULL, NULL, NULL, /* encrypt */ NULL, /* derive */ + NULL, /* wrap */ + NULL, /* unwrap */ NULL, /* mech_data */ NULL, /* free_mech_data */ + NULL, /* copy_mech_data */ }; -#endif static sc_pkcs11_mechanism_type_t openssl_md5_mech = { CKM_MD5, { 0, 0, CKF_DIGEST }, - 0, + { -1 }, sizeof(struct sc_pkcs11_operation), sc_pkcs11_openssl_md_release, sc_pkcs11_openssl_md_init, @@ -150,16 +194,20 @@ static sc_pkcs11_mechanism_type_t openssl_md5_mech = { sc_pkcs11_openssl_md_final, NULL, NULL, NULL, NULL, /* sign_* */ NULL, NULL, NULL, /* verif_* */ - NULL, NULL, /* decrypt_* */ + NULL, NULL, NULL, NULL, /* decrypt_* */ + NULL, NULL, NULL, NULL, /* encrypt */ NULL, /* derive */ + NULL, /* wrap */ + NULL, /* unwrap */ NULL, /* mech_data */ NULL, /* free_mech_data */ + NULL, /* copy_mech_data */ }; static sc_pkcs11_mechanism_type_t openssl_ripemd160_mech = { CKM_RIPEMD160, { 0, 0, CKF_DIGEST }, - 0, + { -1 }, sizeof(struct sc_pkcs11_operation), sc_pkcs11_openssl_md_release, sc_pkcs11_openssl_md_init, @@ -167,11 +215,16 @@ static sc_pkcs11_mechanism_type_t openssl_ripemd160_mech = { sc_pkcs11_openssl_md_final, NULL, NULL, NULL, NULL, /* sign_* */ NULL, NULL, NULL, /* verif_* */ - NULL, NULL, /* decrypt_* */ + NULL, NULL, NULL, NULL, /* decrypt_* */ + NULL, NULL, NULL, NULL, /* encrypt */ NULL, /* derive */ + NULL, /* wrap */ + NULL, /* unwrap */ NULL, /* mech_data */ NULL, /* free_mech_data */ + NULL, /* copy_mech_data */ }; +// clang-format on static void * dup_mem(void *in, size_t in_len) { @@ -181,10 +234,32 @@ static void * dup_mem(void *in, size_t in_len) return out; } +static CK_RV ossl_md_copy(const void *src, void **dst) +{ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + int ret = EVP_MD_up_ref((EVP_MD *)src); + if (ret != 1) { + return CKR_GENERAL_ERROR; + } +#endif + *dst = (EVP_MD *)src; + return CKR_OK; +} + +static void ossl_md_free(const void *md) +{ + sc_evp_md_free((EVP_MD *)md); +} + void sc_pkcs11_register_openssl_mechanisms(struct sc_pkcs11_card *p11card) { -#if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_ENGINE) + sc_pkcs11_mechanism_type_t *mt = NULL; +/* + * Engine support is being deprecated in 3.0. OpenSC loads GOST as engine. + * When GOST developers convert to provider, we can load the provider + */ +#if !defined(OPENSSL_NO_ENGINE) ENGINE *e; /* crypto locking removed in 1.1 */ #if OPENSSL_VERSION_NUMBER < 0x10100000L @@ -217,7 +292,7 @@ sc_pkcs11_register_openssl_mechanisms(struct sc_pkcs11_card *p11card) ENGINE_free(e); e = NULL; } -#endif /* !OPENSSL_NO_STATIC_ENGINE && !OPENSSL_NO_GOST */ +#endif /* !OPENSSL_NO_STATIC_ENGINE && !OPENSSL_NO_GOST && !LIBRESSL_VERSION_NUMBER */ } if (e) { ENGINE_set_default(e, ENGINE_METHOD_ALL); @@ -228,26 +303,64 @@ sc_pkcs11_register_openssl_mechanisms(struct sc_pkcs11_card *p11card) if (locking_cb) CRYPTO_set_locking_callback(locking_cb); #endif -#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_ENGINE) */ - - openssl_sha1_mech.mech_data = EVP_sha1(); - sc_pkcs11_register_mechanism(p11card, dup_mem(&openssl_sha1_mech, sizeof openssl_sha1_mech)); -#if OPENSSL_VERSION_NUMBER >= 0x00908000L - openssl_sha256_mech.mech_data = EVP_sha256(); - sc_pkcs11_register_mechanism(p11card, dup_mem(&openssl_sha256_mech, sizeof openssl_sha256_mech)); - openssl_sha384_mech.mech_data = EVP_sha384(); - sc_pkcs11_register_mechanism(p11card, dup_mem(&openssl_sha384_mech, sizeof openssl_sha384_mech)); - openssl_sha512_mech.mech_data = EVP_sha512(); - sc_pkcs11_register_mechanism(p11card, dup_mem(&openssl_sha512_mech, sizeof openssl_sha512_mech)); -#endif - openssl_md5_mech.mech_data = EVP_md5(); - sc_pkcs11_register_mechanism(p11card, dup_mem(&openssl_md5_mech, sizeof openssl_md5_mech)); - openssl_ripemd160_mech.mech_data = EVP_ripemd160(); - sc_pkcs11_register_mechanism(p11card, dup_mem(&openssl_ripemd160_mech, sizeof openssl_ripemd160_mech)); -#if OPENSSL_VERSION_NUMBER >= 0x10000000L +#endif /* !defined(OPENSSL_NO_ENGINE) */ + + openssl_sha1_mech.mech_data = sc_evp_md(context, "sha1"); + openssl_sha1_mech.free_mech_data = ossl_md_free; + openssl_sha1_mech.copy_mech_data = ossl_md_copy; + mt = dup_mem(&openssl_sha1_mech, sizeof openssl_sha1_mech); + sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + + openssl_sha224_mech.mech_data = sc_evp_md(context, "sha224"); + openssl_sha224_mech.free_mech_data = ossl_md_free; + openssl_sha224_mech.copy_mech_data = ossl_md_copy; + mt = dup_mem(&openssl_sha224_mech, sizeof openssl_sha224_mech); + sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + + openssl_sha256_mech.mech_data = sc_evp_md(context, "sha256"); + openssl_sha256_mech.free_mech_data = ossl_md_free; + openssl_sha256_mech.copy_mech_data = ossl_md_copy; + mt = dup_mem(&openssl_sha256_mech, sizeof openssl_sha256_mech); + sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + + openssl_sha384_mech.mech_data = sc_evp_md(context, "sha384"); + openssl_sha384_mech.free_mech_data = ossl_md_free; + openssl_sha384_mech.copy_mech_data = ossl_md_copy; + mt = dup_mem(&openssl_sha384_mech, sizeof openssl_sha384_mech); + sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + + openssl_sha512_mech.mech_data = sc_evp_md(context, "sha512"); + openssl_sha512_mech.free_mech_data = ossl_md_free; + openssl_sha512_mech.copy_mech_data = ossl_md_copy; + mt = dup_mem(&openssl_sha512_mech, sizeof openssl_sha512_mech); + sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + + if (!FIPS_mode()) { + openssl_md5_mech.mech_data = sc_evp_md(context, "md5"); + openssl_md5_mech.free_mech_data = ossl_md_free; + openssl_md5_mech.copy_mech_data = ossl_md_copy; + mt = dup_mem(&openssl_md5_mech, sizeof openssl_md5_mech); + sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + + openssl_ripemd160_mech.mech_data = sc_evp_md(context, "ripemd160"); + openssl_ripemd160_mech.free_mech_data = ossl_md_free; + openssl_ripemd160_mech.copy_mech_data = ossl_md_copy; + mt = dup_mem(&openssl_ripemd160_mech, sizeof openssl_ripemd160_mech); + sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + + } openssl_gostr3411_mech.mech_data = EVP_get_digestbynid(NID_id_GostR3411_94); - sc_pkcs11_register_mechanism(p11card, dup_mem(&openssl_gostr3411_mech, sizeof openssl_gostr3411_mech)); -#endif + mt = dup_mem(&openssl_gostr3411_mech, sizeof openssl_gostr3411_mech); + sc_pkcs11_register_mechanism(p11card, mt, NULL); + sc_pkcs11_free_mechanism(&mt); + } @@ -255,7 +368,7 @@ sc_pkcs11_register_openssl_mechanisms(struct sc_pkcs11_card *p11card) * Handle OpenSSL digest functions */ #define DIGEST_CTX(op) \ - ((EVP_MD_CTX *) (op)->priv_data) + (op ? (EVP_MD_CTX *) (op)->priv_data : NULL) static CK_RV sc_pkcs11_openssl_md_init(sc_pkcs11_operation_t *op) { @@ -266,9 +379,15 @@ static CK_RV sc_pkcs11_openssl_md_init(sc_pkcs11_operation_t *op) if (!op || !(mt = op->type) || !(md = (EVP_MD *) mt->mech_data)) return CKR_ARGUMENTS_BAD; - if (!(md_ctx = EVP_MD_CTX_create())) + if (!(md_ctx = EVP_MD_CTX_create())) { + sc_log_openssl(context); return CKR_HOST_MEMORY; - EVP_DigestInit(md_ctx, md); + } + if (!EVP_DigestInit(md_ctx, md)) { + sc_log_openssl(context); + EVP_MD_CTX_destroy(md_ctx); + return CKR_GENERAL_ERROR; + } op->priv_data = md_ctx; return CKR_OK; } @@ -276,7 +395,15 @@ static CK_RV sc_pkcs11_openssl_md_init(sc_pkcs11_operation_t *op) static CK_RV sc_pkcs11_openssl_md_update(sc_pkcs11_operation_t *op, CK_BYTE_PTR pData, CK_ULONG pDataLen) { - EVP_DigestUpdate(DIGEST_CTX(op), pData, pDataLen); + EVP_MD_CTX *md_ctx = DIGEST_CTX(op); + if (!md_ctx) { + sc_log_openssl(context); + return CKR_ARGUMENTS_BAD; + } + if (!EVP_DigestUpdate(md_ctx, pData, pDataLen)) { + sc_log_openssl(context); + return CKR_GENERAL_ERROR; + } return CKR_OK; } @@ -285,62 +412,87 @@ static CK_RV sc_pkcs11_openssl_md_final(sc_pkcs11_operation_t *op, { EVP_MD_CTX *md_ctx = DIGEST_CTX(op); + if (!md_ctx) + return CKR_ARGUMENTS_BAD; if (*pulDigestLen < (unsigned) EVP_MD_CTX_size(md_ctx)) { sc_log(context, "Provided buffer too small: %lu < %d", *pulDigestLen, EVP_MD_CTX_size(md_ctx)); *pulDigestLen = EVP_MD_CTX_size(md_ctx); return CKR_BUFFER_TOO_SMALL; } - - EVP_DigestFinal(md_ctx, pDigest, (unsigned *) pulDigestLen); - + if (!EVP_DigestFinal(md_ctx, pDigest, (unsigned *)pulDigestLen)) { + sc_log_openssl(context); + return CKR_GENERAL_ERROR; + } return CKR_OK; } static void sc_pkcs11_openssl_md_release(sc_pkcs11_operation_t *op) { - EVP_MD_CTX *md_ctx = DIGEST_CTX(op); - - if (md_ctx) - EVP_MD_CTX_destroy(md_ctx); - op->priv_data = NULL; + if (op) { + EVP_MD_CTX *md_ctx = DIGEST_CTX(op); + if (md_ctx) + EVP_MD_CTX_destroy(md_ctx); + op->priv_data = NULL; + } } -#if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) +#if !defined(OPENSSL_NO_EC) -static void reverse(unsigned char *buf, size_t len) +static unsigned char * +reverse(const unsigned char *buf, size_t len) { + unsigned char *out = malloc(len); unsigned char tmp; size_t i; + if (out == NULL) + return NULL; + + memcpy(out, buf, len); for (i = 0; i < len / 2; ++i) { - tmp = buf[i]; - buf[i] = buf[len - 1 - i]; - buf[len - 1 - i] = tmp; + tmp = out[i]; + out[i] = out[len - 1 - i]; + out[len - 1 - i] = tmp; } + return out; } -static CK_RV gostr3410_verify_data(const unsigned char *pubkey, int pubkey_len, - const unsigned char *params, int params_len, - unsigned char *data, int data_len, - unsigned char *signat, int signat_len) +static CK_RV gostr3410_verify_data(const CK_BYTE_PTR pubkey, CK_ULONG pubkey_len, + const CK_BYTE_PTR params, CK_ULONG params_len, + CK_BYTE_PTR data, CK_ULONG data_len, + CK_BYTE_PTR signat, CK_ULONG signat_len) { EVP_PKEY *pkey; EVP_PKEY_CTX *pkey_ctx = NULL; EC_POINT *P; BIGNUM *X, *Y; - ASN1_OCTET_STRING *octet; - const EC_GROUP *group = NULL; + ASN1_OCTET_STRING *octet = NULL; char paramset[2] = "A"; int r = -1, ret_vrf = 0; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const EC_GROUP *group = NULL; +#else + EC_GROUP *group = NULL; + char group_name[256]; + OSSL_PARAM *old_params = NULL, *new_params = NULL, *p = NULL; + OSSL_PARAM_BLD *bld = NULL; + unsigned char *buf = NULL; + size_t buf_len = 0; + EVP_PKEY *new_pkey = NULL; +#endif pkey = EVP_PKEY_new(); - if (!pkey) + if (!pkey) { + sc_log_openssl(context); return CKR_HOST_MEMORY; + } + r = EVP_PKEY_set_type(pkey, NID_id_GostR3410_2001); if (r == 1) { pkey_ctx = EVP_PKEY_CTX_new(pkey, NULL); if (!pkey_ctx) { + sc_log_openssl(context); EVP_PKEY_free(pkey); return CKR_HOST_MEMORY; } @@ -356,61 +508,119 @@ static CK_RV gostr3410_verify_data(const unsigned char *pubkey, int pubkey_len, r = EVP_PKEY_paramgen_init(pkey_ctx); if (r == 1) r = EVP_PKEY_paramgen(pkey_ctx, &pkey); +#if OPENSSL_VERSION_NUMBER < 0x30000000L if (r == 1 && EVP_PKEY_get0(pkey) != NULL) group = EC_KEY_get0_group(EVP_PKEY_get0(pkey)); +#else + if (r == 1) { + EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, group_name, sizeof(group_name), NULL); + group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(group_name)); + } +#endif r = -1; - if (group) - octet = d2i_ASN1_OCTET_STRING(NULL, &pubkey, (long)pubkey_len); + if (group && pubkey_len <= LONG_MAX) { + const unsigned char *p = pubkey; + octet = d2i_ASN1_OCTET_STRING(NULL, &p, (long)pubkey_len); + } if (group && octet) { - reverse(octet->data, octet->length); - Y = BN_bin2bn(octet->data, octet->length / 2, NULL); - X = BN_bin2bn((const unsigned char*)octet->data + - octet->length / 2, octet->length / 2, NULL); + X = BN_lebin2bn(ASN1_STRING_get0_data(octet), ASN1_STRING_length(octet) / 2, NULL); + Y = BN_lebin2bn(ASN1_STRING_get0_data(octet) + ASN1_STRING_length(octet) / 2, ASN1_STRING_length(octet) / 2, NULL); ASN1_OCTET_STRING_free(octet); P = EC_POINT_new(group); if (P && X && Y) - r = EC_POINT_set_affine_coordinates_GFp(group, - P, X, Y, NULL); + r = EC_POINT_set_affine_coordinates(group, P, X, Y, NULL); BN_free(X); BN_free(Y); + +#if OPENSSL_VERSION_NUMBER < 0x30000000L if (r == 1 && EVP_PKEY_get0(pkey) && P) r = EC_KEY_set_public_key(EVP_PKEY_get0(pkey), P); +#else + EC_GROUP_free(group); + + buf_len = EC_POINT_point2oct(group, P, POINT_CONVERSION_COMPRESSED, NULL, 0, NULL); + if (!(buf = malloc(buf_len))) + r = -1; + if (r == 1 && P) { + size_t len = EC_POINT_point2oct(group, P, POINT_CONVERSION_COMPRESSED, buf, + buf_len, NULL); + if (len == 0) { + sc_log_openssl(context); + r = -1; + } + } + + if (r != 1 || + EVP_PKEY_todata(pkey, EVP_PKEY_KEYPAIR, &old_params) != 1 || + !(bld = OSSL_PARAM_BLD_new()) || + OSSL_PARAM_BLD_push_octet_string(bld, "pub", buf, buf_len) != 1 || + !(new_params = OSSL_PARAM_BLD_to_param(bld)) || + !(p = OSSL_PARAM_merge(old_params, new_params))) { + sc_log_openssl(context); + r = -1; + } + free(buf); + OSSL_PARAM_BLD_free(bld); + + if (r == 1) { + if (EVP_PKEY_fromdata_init(pkey_ctx) != 1 || + EVP_PKEY_fromdata(pkey_ctx, &new_pkey, EVP_PKEY_KEYPAIR, p) != 1) { + sc_log_openssl(context); + r = -1; + } + } + OSSL_PARAM_free(old_params); + OSSL_PARAM_free(new_params); + OSSL_PARAM_free(p); + + if (r == 1) { + EVP_PKEY_free(pkey); + pkey = new_pkey; + } +#endif EC_POINT_free(P); } if (r == 1) { - r = EVP_PKEY_verify_init(pkey_ctx); - reverse(data, data_len); - if (r == 1) - ret_vrf = EVP_PKEY_verify(pkey_ctx, signat, signat_len, - data, data_len); + unsigned char *rev_data = reverse(data, data_len); + if (rev_data == NULL) { + r = -1; + } else { + r = EVP_PKEY_verify_init(pkey_ctx); + if (r == 1) + ret_vrf = EVP_PKEY_verify(pkey_ctx, signat, signat_len, + rev_data, data_len); + free(rev_data); + } } } EVP_PKEY_CTX_free(pkey_ctx); EVP_PKEY_free(pkey); - if (r != 1) + if (r != 1) { return CKR_GENERAL_ERROR; + } return ret_vrf == 1 ? CKR_OK : CKR_SIGNATURE_INVALID; } -#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) */ +#endif /* !defined(OPENSSL_NO_EC) */ /* If no hash function was used, finish with RSA_public_decrypt(). * If a hash function was used, we can make a big shortcut by * finishing with EVP_VerifyFinal(). */ -CK_RV sc_pkcs11_verify_data(const unsigned char *pubkey, int pubkey_len, - const unsigned char *pubkey_params, int pubkey_params_len, - CK_MECHANISM_TYPE mech, sc_pkcs11_operation_t *md, - unsigned char *data, int data_len, - unsigned char *signat, int signat_len) +CK_RV sc_pkcs11_verify_data(const CK_BYTE_PTR pubkey, CK_ULONG pubkey_len, + const CK_BYTE_PTR pubkey_params, CK_ULONG pubkey_params_len, + CK_MECHANISM_PTR mech, sc_pkcs11_operation_t *md, + CK_BYTE_PTR data, CK_ULONG data_len, + CK_BYTE_PTR signat, CK_ULONG signat_len) { int res; CK_RV rv = CKR_GENERAL_ERROR; EVP_PKEY *pkey = NULL; const unsigned char *pubkey_tmp = NULL; + int sLen; - if (mech == CKM_GOSTR3410) + if (mech->mechanism == CKM_GOSTR3410) { -#if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) +#if !defined(OPENSSL_NO_EC) return gostr3410_verify_data(pubkey, pubkey_len, pubkey_params, pubkey_params_len, data, data_len, signat, signat_len); @@ -424,70 +634,359 @@ CK_RV sc_pkcs11_verify_data(const unsigned char *pubkey, int pubkey_len, * PKCS#11 does not define CKA_VALUE for public keys, and different cards * return either the raw or spki versions as defined in PKCS#15 * And we need to support more then just RSA. - * We can use d2i_PUBKEY which works for SPKI and any key type. + * We can use d2i_PUBKEY which works for SPKI and any key type. */ pubkey_tmp = pubkey; /* pass in so pubkey pointer is not modified */ pkey = d2i_PUBKEY(NULL, &pubkey_tmp, pubkey_len); - if (pkey == NULL) + if (pkey == NULL) { + sc_log_openssl(context); return CKR_GENERAL_ERROR; + } - if (md != NULL) { + if (md != NULL && (mech->mechanism == CKM_SHA1_RSA_PKCS + || mech->mechanism == CKM_MD5_RSA_PKCS + || mech->mechanism == CKM_RIPEMD160_RSA_PKCS + || mech->mechanism == CKM_SHA224_RSA_PKCS + || mech->mechanism == CKM_SHA256_RSA_PKCS + || mech->mechanism == CKM_SHA384_RSA_PKCS + || mech->mechanism == CKM_SHA512_RSA_PKCS + || mech->mechanism == CKM_ECDSA_SHA1 + || mech->mechanism == CKM_ECDSA_SHA224 + || mech->mechanism == CKM_ECDSA_SHA256 + || mech->mechanism == CKM_ECDSA_SHA384 + || mech->mechanism == CKM_ECDSA_SHA512 + )) { EVP_MD_CTX *md_ctx = DIGEST_CTX(md); - res = EVP_VerifyFinal(md_ctx, signat, signat_len, pkey); + /* This does not really use the data argument, but the data + * are already collected in the md_ctx + */ + sc_log(context, "Trying to verify using EVP"); + if (md_ctx) { + + if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) { + unsigned char *signat_tmp = NULL; + size_t signat_len_tmp; + int r; + r = sc_asn1_sig_value_rs_to_sequence(NULL, signat, + signat_len, &signat_tmp, &signat_len_tmp); + if (r == 0 && signat_len_tmp < UINT_MAX) { + res = EVP_VerifyFinal(md_ctx, signat_tmp, (unsigned int) signat_len_tmp, pkey); + } else { + sc_log(context, "sc_asn1_sig_value_rs_to_sequence failed r:%d " + "or output too long signat_len_tmp:%"SC_FORMAT_LEN_SIZE_T"u", + r, signat_len_tmp); + res = -1; + } + free(signat_tmp); + } else + res = EVP_VerifyFinal(md_ctx, signat, (unsigned int) signat_len, pkey); + } else { + res = -1; + } EVP_PKEY_free(pkey); if (res == 1) return CKR_OK; - else if (res == 0) + else if (res == 0) { + sc_log_openssl(context); + sc_log(context, "EVP_VerifyFinal(): Signature invalid"); return CKR_SIGNATURE_INVALID; - else { + } else { + sc_log_openssl(context); sc_log(context, "EVP_VerifyFinal() returned %d\n", res); return CKR_GENERAL_ERROR; } - } - else { - RSA *rsa; + } else /* If plain CKM_ECDSA (without any hashing) is used or card supports + * on-card CKM_ECDSA_SHAx only we land here. Since for CKM_ECDSA_SHAx no + * hashing happened in C_VerifyUpdate() we do it here instead. + */ + if (md == NULL && (mech->mechanism == CKM_ECDSA + || mech->mechanism == CKM_ECDSA_SHA1 + || mech->mechanism == CKM_ECDSA_SHA224 + || mech->mechanism == CKM_ECDSA_SHA256 + || mech->mechanism == CKM_ECDSA_SHA384 + || mech->mechanism == CKM_ECDSA_SHA512)) { + size_t signat_len_tmp; + unsigned char *signat_tmp = NULL; + unsigned int mdbuf_len; + unsigned char *mdbuf = NULL; + EVP_PKEY_CTX *ctx; + int r; + + sc_log(context, "Trying to verify using EVP"); + + /* If needed, hash input first + */ + if (mech->mechanism == CKM_ECDSA_SHA1 + || mech->mechanism == CKM_ECDSA_SHA224 + || mech->mechanism == CKM_ECDSA_SHA256 + || mech->mechanism == CKM_ECDSA_SHA384 + || mech->mechanism == CKM_ECDSA_SHA512) { + EVP_MD_CTX *mdctx; + EVP_MD *md = NULL; + switch (mech->mechanism) { + case CKM_ECDSA_SHA1: + md = sc_evp_md(context, "sha1"); + break; + case CKM_ECDSA_SHA224: + md = sc_evp_md(context, "sha224"); + break; + case CKM_ECDSA_SHA256: + md = sc_evp_md(context, "sha256"); + break; + case CKM_ECDSA_SHA384: + md = sc_evp_md(context, "sha384"); + break; + case CKM_ECDSA_SHA512: + md = sc_evp_md(context, "sha512"); + break; + default: + EVP_PKEY_free(pkey); + return CKR_GENERAL_ERROR; + } + mdbuf_len = EVP_MD_size(md); + mdbuf = calloc(1, mdbuf_len); + if (mdbuf == NULL) { + EVP_PKEY_free(pkey); + sc_evp_md_free(md); + return CKR_DEVICE_MEMORY; + } + if ((mdctx = EVP_MD_CTX_new()) == NULL) { + sc_log_openssl(context); + free(mdbuf); + EVP_PKEY_free(pkey); + sc_evp_md_free(md); + return CKR_GENERAL_ERROR; + } + if (!EVP_DigestInit(mdctx, md) + || !EVP_DigestUpdate(mdctx, data, data_len) + || !EVP_DigestFinal(mdctx, mdbuf, &mdbuf_len)) { + sc_log_openssl(context); + EVP_PKEY_free(pkey); + EVP_MD_CTX_free(mdctx); + sc_evp_md_free(md); + free(mdbuf); + return CKR_GENERAL_ERROR; + } + EVP_MD_CTX_free(mdctx); + sc_evp_md_free(md); + data = mdbuf; + data_len = mdbuf_len; + } + + res = 0; + r = sc_asn1_sig_value_rs_to_sequence(NULL, signat, signat_len, + &signat_tmp, &signat_len_tmp); + ctx = sc_evp_pkey_ctx_new(context, pkey); + if (r == 0 && EVP_PKEY_base_id(pkey) == EVP_PKEY_EC && ctx && EVP_PKEY_verify_init(ctx) == 1) + res = EVP_PKEY_verify(ctx, signat_tmp, signat_len_tmp, data, data_len); + + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pkey); + free(signat_tmp); + free(mdbuf); + + if (res == 1) { + return CKR_OK; + } else if (res == 0) { + sc_log_openssl(context); + return CKR_SIGNATURE_INVALID; + } else { + sc_log_openssl(context); + return CKR_GENERAL_ERROR; + } + } else { unsigned char *rsa_out = NULL, pad; - int rsa_outlen = 0; + size_t rsa_outlen = 0; + EVP_PKEY_CTX *ctx = sc_evp_pkey_ctx_new(context, pkey); + if (!ctx) { + sc_log_openssl(context); + EVP_PKEY_free(pkey); + return CKR_DEVICE_MEMORY; + } - switch(mech) { + sc_log(context, "Trying to verify using low-level API"); + switch (mech->mechanism) { case CKM_RSA_PKCS: + case CKM_MD5_RSA_PKCS: + case CKM_RIPEMD160_RSA_PKCS: pad = RSA_PKCS1_PADDING; break; - case CKM_RSA_X_509: - pad = RSA_NO_PADDING; - break; - /* TODO support more then RSA */ - default: + case CKM_RSA_X_509: + pad = RSA_NO_PADDING; + break; + case CKM_RSA_PKCS_PSS: + case CKM_SHA1_RSA_PKCS_PSS: + case CKM_SHA224_RSA_PKCS_PSS: + case CKM_SHA256_RSA_PKCS_PSS: + case CKM_SHA384_RSA_PKCS_PSS: + case CKM_SHA512_RSA_PKCS_PSS: + pad = RSA_NO_PADDING; + break; + default: EVP_PKEY_free(pkey); - return CKR_ARGUMENTS_BAD; - } + EVP_PKEY_CTX_free(ctx); + return CKR_ARGUMENTS_BAD; + } - rsa = EVP_PKEY_get1_RSA(pkey); - EVP_PKEY_free(pkey); - if (rsa == NULL) - return CKR_DEVICE_MEMORY; + if (EVP_PKEY_verify_recover_init(ctx) != 1 || + EVP_PKEY_CTX_set_rsa_padding(ctx, pad) != 1) { + sc_log_openssl(context); + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pkey); + return CKR_GENERAL_ERROR; + } - rsa_out = calloc(1, RSA_size(rsa)); + rsa_outlen = EVP_PKEY_size(pkey); + rsa_out = calloc(1, rsa_outlen); if (rsa_out == NULL) { - RSA_free(rsa); + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(ctx); return CKR_DEVICE_MEMORY; } - - rsa_outlen = RSA_public_decrypt(signat_len, signat, rsa_out, rsa, pad); - RSA_free(rsa); - if(rsa_outlen <= 0) { + if (EVP_PKEY_verify_recover(ctx, rsa_out, &rsa_outlen, signat, signat_len) != 1) { + sc_log_openssl(context); free(rsa_out); - sc_log(context, "RSA_public_decrypt() returned %d\n", rsa_outlen); + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(ctx); + sc_log(context, "RSA_public_decrypt() returned %d\n", (int) rsa_outlen); return CKR_GENERAL_ERROR; } + EVP_PKEY_CTX_free(ctx); + /* For PSS mechanisms we can not simply compare the "decrypted" + * data -- we need to verify the PSS padding is valid + */ + if (mech->mechanism == CKM_RSA_PKCS_PSS || + mech->mechanism == CKM_SHA1_RSA_PKCS_PSS || + mech->mechanism == CKM_SHA224_RSA_PKCS_PSS || + mech->mechanism == CKM_SHA256_RSA_PKCS_PSS || + mech->mechanism == CKM_SHA384_RSA_PKCS_PSS || + mech->mechanism == CKM_SHA512_RSA_PKCS_PSS) { + CK_RSA_PKCS_PSS_PARAMS* param = NULL; + EVP_MD *mgf_md = NULL, *pss_md = NULL; + unsigned char digest[EVP_MAX_MD_SIZE]; + + if (mech->pParameter == NULL) { + free(rsa_out); + EVP_PKEY_free(pkey); + sc_log(context, "PSS mechanism requires parameter"); + return CKR_MECHANISM_PARAM_INVALID; + } + + param = (CK_RSA_PKCS_PSS_PARAMS*)mech->pParameter; + switch (param->mgf) { + case CKG_MGF1_SHA1: + mgf_md = sc_evp_md(context, "sha1"); + break; + case CKG_MGF1_SHA224: + mgf_md = sc_evp_md(context, "sha224"); + break; + case CKG_MGF1_SHA256: + mgf_md = sc_evp_md(context, "sha256"); + break; + case CKG_MGF1_SHA384: + mgf_md = sc_evp_md(context, "sha384"); + break; + case CKG_MGF1_SHA512: + mgf_md = sc_evp_md(context, "sha512"); + break; + default: + free(rsa_out); + EVP_PKEY_free(pkey); + return CKR_MECHANISM_PARAM_INVALID; + } + + switch (param->hashAlg) { + case CKM_SHA_1: + pss_md = sc_evp_md(context, "sha1"); + break; + case CKM_SHA224: + pss_md = sc_evp_md(context, "sha224"); + break; + case CKM_SHA256: + pss_md = sc_evp_md(context, "sha256"); + break; + case CKM_SHA384: + pss_md = sc_evp_md(context, "sha384"); + break; + case CKM_SHA512: + pss_md = sc_evp_md(context, "sha512"); + break; + default: + sc_evp_md_free(mgf_md); + free(rsa_out); + EVP_PKEY_free(pkey); + return CKR_MECHANISM_PARAM_INVALID; + } + + /* for the mechanisms with hash algorithm, the data + * is already added to the hash buffer, so we need + * to finish the hash operation here + */ + if (mech->mechanism != CKM_RSA_PKCS_PSS) { + EVP_MD_CTX *md_ctx = DIGEST_CTX(md); + unsigned char *tmp = digest; + unsigned int tmp_len; + + if (!md_ctx || !EVP_DigestFinal(md_ctx, tmp, &tmp_len)) { + sc_log_openssl(context); + sc_evp_md_free(mgf_md); + sc_evp_md_free(pss_md); + free(rsa_out); + EVP_PKEY_free(pkey); + return CKR_GENERAL_ERROR; + } + data = tmp; + data_len = tmp_len; + } + rv = CKR_SIGNATURE_INVALID; + + /* special mode - autodetect sLen from signature */ + /* https://github.com/openssl/openssl/blob/master/crypto/rsa/rsa_pss.c */ + /* there is no way to pass negative value here, we using maximal value for this */ + if (((CK_ULONG) 1 ) << (sizeof(CK_ULONG) * CHAR_BIT -1) == param->sLen || param->sLen > INT_MAX) + sLen = RSA_PSS_SALTLEN_AUTO; + else + sLen = (int) param->sLen; + + if ((ctx = sc_evp_pkey_ctx_new(context, pkey)) == NULL || + EVP_PKEY_verify_init(ctx) != 1 || + EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) != 1 || + EVP_PKEY_CTX_set_signature_md(ctx, pss_md) != 1 || + EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, sLen) != 1 || + EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, mgf_md) != 1) { + sc_log_openssl(context); + sc_evp_md_free(mgf_md); + sc_evp_md_free(pss_md); + free(rsa_out); + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(ctx); + sc_log(context, "Failed to initialize EVP_PKEY_CTX"); + return rv; + } + + if (data_len == (unsigned int)EVP_MD_size(pss_md) && + EVP_PKEY_verify(ctx, signat, signat_len, data, data_len) == 1) { + rv = CKR_OK; + } else { + sc_log_openssl(context); + } + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(ctx); + sc_evp_md_free(mgf_md); + sc_evp_md_free(pss_md); + free(rsa_out); + sc_log(context, "Returning %lu", rv); + return rv; + } else { + EVP_PKEY_free(pkey); + } - if (rsa_outlen == data_len && memcmp(rsa_out, data, data_len) == 0) + if ((unsigned int) rsa_outlen == data_len && memcmp(rsa_out, data, data_len) == 0) rv = CKR_OK; else rv = CKR_SIGNATURE_INVALID; - free(rsa_out); } diff --git a/src/pkcs11/pd-pkcs11.h b/src/pkcs11/pd-pkcs11.h new file mode 100644 index 0000000000..003bbd307f --- /dev/null +++ b/src/pkcs11/pd-pkcs11.h @@ -0,0 +1,2775 @@ +/* https://github.com/latchset/pkcs11-headers/blob/main/public-domain/3.2/pkcs11.h */ +// clang-format off + +/* This file is in the Public Domain */ + +#ifndef _PD_PKCS11_ +#define _PD_PKCS11_ + +#define CRYPTOKI_VERSION_MAJOR 3 +#define CRYPTOKI_VERSION_MINOR 2 +#define CRYPTOKI_VERSION_AMENDMENT 0 + +/* Basic types */ +typedef unsigned char CK_BBOOL; +typedef unsigned char CK_BYTE; +typedef unsigned char CK_CHAR; +typedef unsigned char CK_UTF8CHAR; +typedef unsigned long int CK_ULONG; + +typedef CK_BBOOL * CK_BBOOL_PTR; +typedef CK_BYTE * CK_BYTE_PTR; +typedef CK_CHAR * CK_CHAR_PTR; +typedef CK_UTF8CHAR * CK_UTF8CHAR_PTR; +typedef CK_ULONG * CK_ULONG_PTR; + +/* Basic defines */ +#define NULL_PTR ((void *)0) +typedef void * CK_VOID_PTR; +typedef void ** CK_VOID_PTR_PTR; + +#define CK_EFFECTIVELY_INFINITE 0UL +#define CK_UNAVAILABLE_INFORMATION ~0UL +#define CK_INVALID_HANDLE 0UL +#define CK_TRUE 1 +#define CK_FALSE 0 + +/* CK_ types in alphabetical order */ +#define ULONGDEF(__name__) \ +typedef CK_ULONG __name__; \ +typedef __name__ * __name__ ## _PTR; + +ULONGDEF(CK_ATTRIBUTE_TYPE); +ULONGDEF(CK_CERTIFICATE_CATEGORY); +ULONGDEF(CK_CERTIFICATE_TYPE); +ULONGDEF(CK_EC_KDF_TYPE); +ULONGDEF(CK_EXTRACT_PARAMS); +ULONGDEF(CK_FLAGS); +ULONGDEF(CK_GENERATOR_FUNCTION); +ULONGDEF(CK_HEDGE_TYPE); +ULONGDEF(CK_HSS_LEVELS); +ULONGDEF(CK_HW_FEATURE_TYPE); +ULONGDEF(CK_JAVA_MIDP_SECURITY_DOMAIN); +ULONGDEF(CK_KEY_TYPE); +ULONGDEF(CK_LMS_TYPE); +ULONGDEF(CK_LMOTS_TYPE); +ULONGDEF(CK_MAC_GENERAL_PARAMS); +ULONGDEF(CK_MECHANISM_TYPE); +ULONGDEF(CK_ML_DSA_PARAMETER_SET_TYPE); +ULONGDEF(CK_ML_KEM_PARAMETER_SET_TYPE); +ULONGDEF(CK_NOTIFICATION); +ULONGDEF(CK_OBJECT_CLASS); +ULONGDEF(CK_OBJECT_HANDLE); +ULONGDEF(CK_OTP_PARAM_TYPE); +ULONGDEF(CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE); +ULONGDEF(CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE); +ULONGDEF(CK_PRF_DATA_TYPE); +ULONGDEF(CK_PROFILE_ID); +ULONGDEF(CK_RC2_PARAMS); +ULONGDEF(CK_RSA_PKCS_MGF_TYPE); +ULONGDEF(CK_RSA_PKCS_OAEP_SOURCE_TYPE); +ULONGDEF(CK_RV); +ULONGDEF(CK_SESSION_HANDLE); +ULONGDEF(CK_SESSION_VALIDATION_FLAGS_TYPE); +ULONGDEF(CK_SLH_DSA_PARAMETER_SET_TYPE); +ULONGDEF(CK_SLOT_ID); +ULONGDEF(CK_SP800_108_DKM_LENGTH_METHOD); +ULONGDEF(CK_STATE); +ULONGDEF(CK_TRUST); +ULONGDEF(CK_USER_TYPE); +ULONGDEF(CK_VALIDATION_AUTHORITY_TYPE); +ULONGDEF(CK_VALIDATION_TYPE); +ULONGDEF(CK_X2RATCHET_KDF_TYPE); +ULONGDEF(CK_X3DH_KDF_TYPE); +ULONGDEF(CK_X9_42_DH_KDF_TYPE); +ULONGDEF(CK_XEDDSA_HASH_TYPE); +ULONGDEF(CK_XMSSMT_PARAMETER_SET_TYPE); +ULONGDEF(CK_XMSS_PARAMETER_SET_TYPE); + +/* domain specific values and constants */ + +/* CK (certificate) */ +#define CK_CERTIFICATE_CATEGORY_UNSPECIFIED 0UL +#define CK_CERTIFICATE_CATEGORY_TOKEN_USER 1UL +#define CK_CERTIFICATE_CATEGORY_AUTHORITY 2UL +#define CK_CERTIFICATE_CATEGORY_OTHER_ENTITY 3UL + +/* CK (OTP) */ +#define CK_OTP_VALUE 0UL +#define CK_OTP_PIN 1UL +#define CK_OTP_CHALLENGE 2UL +#define CK_OTP_TIME 3UL +#define CK_OTP_COUNTER 4UL +#define CK_OTP_FLAGS 5UL +#define CK_OTP_OUTPUT_LENGTH 6UL +#define CK_OTP_OUTPUT_FORMAT 7UL + +/* CK (OTP format) */ +#define CK_OTP_FORMAT_DECIMAL 0UL +#define CK_OTP_FORMAT_HEXADECIMAL 1UL +#define CK_OTP_FORMAT_ALPHANUMERIC 2UL +#define CK_OTP_FORMAT_BINARY 3UL + +/* CK (OTP requirement) */ +#define CK_OTP_PARAM_IGNORED 0UL +#define CK_OTP_PARAM_OPTIONAL 1UL +#define CK_OTP_PARAM_MANDATORY 2UL + +/* CK (security) */ +#define CK_SECURITY_DOMAIN_UNSPECIFIED 0UL +#define CK_SECURITY_DOMAIN_MANUFACTURER 1UL +#define CK_SECURITY_DOMAIN_OPERATOR 2UL +#define CK_SECURITY_DOMAIN_THIRD_PARTY 3UL + +/* CK (SP800 KDF) */ +#define CK_SP800_108_ITERATION_VARIABLE 0x00000001UL +#define CK_SP800_108_OPTIONAL_COUNTER 0x00000002UL +#define CK_SP800_108_COUNTER 0x00000002UL +#define CK_SP800_108_DKM_LENGTH 0x00000003UL +#define CK_SP800_108_BYTE_ARRAY 0x00000004UL +#define CK_SP800_108_KEY_HANDLE 0x00000005UL + +/* CK (SP800 DKM) */ +#define CK_SP800_108_DKM_LENGTH_SUM_OF_KEYS 0x00000001UL +#define CK_SP800_108_DKM_LENGTH_SUM_OF_SEGMENTS 0x00000002UL + +/* CKA */ +#define CKA_CLASS 0x00000000UL +#define CKA_TOKEN 0x00000001UL +#define CKA_PRIVATE 0x00000002UL +#define CKA_LABEL 0x00000003UL +#define CKA_UNIQUE_ID 0x00000004UL +#define CKA_APPLICATION 0x00000010UL +#define CKA_VALUE 0x00000011UL +#define CKA_OBJECT_ID 0x00000012UL +#define CKA_CERTIFICATE_TYPE 0x00000080UL +#define CKA_ISSUER 0x00000081UL +#define CKA_SERIAL_NUMBER 0x00000082UL +#define CKA_AC_ISSUER 0x00000083UL +#define CKA_OWNER 0x00000084UL +#define CKA_ATTR_TYPES 0x00000085UL +#define CKA_TRUSTED 0x00000086UL +#define CKA_CERTIFICATE_CATEGORY 0x00000087UL +#define CKA_JAVA_MIDP_SECURITY_DOMAIN 0x00000088UL +#define CKA_URL 0x00000089UL +#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY 0x0000008AUL +#define CKA_HASH_OF_ISSUER_PUBLIC_KEY 0x0000008BUL +#define CKA_NAME_HASH_ALGORITHM 0x0000008CUL +#define CKA_CHECK_VALUE 0x00000090UL +#define CKA_KEY_TYPE 0x00000100UL +#define CKA_SUBJECT 0x00000101UL +#define CKA_ID 0x00000102UL +#define CKA_SENSITIVE 0x00000103UL +#define CKA_ENCRYPT 0x00000104UL +#define CKA_DECRYPT 0x00000105UL +#define CKA_WRAP 0x00000106UL +#define CKA_UNWRAP 0x00000107UL +#define CKA_SIGN 0x00000108UL +#define CKA_SIGN_RECOVER 0x00000109UL +#define CKA_VERIFY 0x0000010AUL +#define CKA_VERIFY_RECOVER 0x0000010BUL +#define CKA_DERIVE 0x0000010CUL +#define CKA_START_DATE 0x00000110UL +#define CKA_END_DATE 0x00000111UL +#define CKA_MODULUS 0x00000120UL +#define CKA_MODULUS_BITS 0x00000121UL +#define CKA_PUBLIC_EXPONENT 0x00000122UL +#define CKA_PRIVATE_EXPONENT 0x00000123UL +#define CKA_PRIME_1 0x00000124UL +#define CKA_PRIME_2 0x00000125UL +#define CKA_EXPONENT_1 0x00000126UL +#define CKA_EXPONENT_2 0x00000127UL +#define CKA_COEFFICIENT 0x00000128UL +#define CKA_PUBLIC_KEY_INFO 0x00000129UL +#define CKA_PRIME 0x00000130UL +#define CKA_SUBPRIME 0x00000131UL +#define CKA_BASE 0x00000132UL +#define CKA_PRIME_BITS 0x00000133UL +#define CKA_SUBPRIME_BITS 0x00000134UL +#define CKA_SUB_PRIME_BITS 0x00000134UL +#define CKA_VALUE_BITS 0x00000160UL +#define CKA_VALUE_LEN 0x00000161UL +#define CKA_EXTRACTABLE 0x00000162UL +#define CKA_LOCAL 0x00000163UL +#define CKA_NEVER_EXTRACTABLE 0x00000164UL +#define CKA_ALWAYS_SENSITIVE 0x00000165UL +#define CKA_KEY_GEN_MECHANISM 0x00000166UL +#define CKA_MODIFIABLE 0x00000170UL +#define CKA_COPYABLE 0x00000171UL +#define CKA_DESTROYABLE 0x00000172UL +#define CKA_EC_PARAMS 0x00000180UL +#define CKA_EC_POINT 0x00000181UL +#define CKA_ALWAYS_AUTHENTICATE 0x00000202UL +#define CKA_WRAP_WITH_TRUSTED 0x00000210UL +#define CKA_OTP_FORMAT 0x00000220UL +#define CKA_OTP_LENGTH 0x00000221UL +#define CKA_OTP_TIME_INTERVAL 0x00000222UL +#define CKA_OTP_USER_FRIENDLY_MODE 0x00000223UL +#define CKA_OTP_CHALLENGE_REQUIREMENT 0x00000224UL +#define CKA_OTP_TIME_REQUIREMENT 0x00000225UL +#define CKA_OTP_COUNTER_REQUIREMENT 0x00000226UL +#define CKA_OTP_PIN_REQUIREMENT 0x00000227UL +#define CKA_OTP_COUNTER 0x0000022EUL +#define CKA_OTP_TIME 0x0000022FUL +#define CKA_OTP_USER_IDENTIFIER 0x0000022AUL +#define CKA_OTP_SERVICE_IDENTIFIER 0x0000022BUL +#define CKA_OTP_SERVICE_LOGO 0x0000022CUL +#define CKA_OTP_SERVICE_LOGO_TYPE 0x0000022DUL +#define CKA_GOSTR3410_PARAMS 0x00000250UL +#define CKA_GOSTR3411_PARAMS 0x00000251UL +#define CKA_GOST28147_PARAMS 0x00000252UL +#define CKA_HW_FEATURE_TYPE 0x00000300UL +#define CKA_RESET_ON_INIT 0x00000301UL +#define CKA_HAS_RESET 0x00000302UL +#define CKA_PIXEL_X 0x00000400UL +#define CKA_PIXEL_Y 0x00000401UL +#define CKA_RESOLUTION 0x00000402UL +#define CKA_CHAR_ROWS 0x00000403UL +#define CKA_CHAR_COLUMNS 0x00000404UL +#define CKA_COLOR 0x00000405UL +#define CKA_BITS_PER_PIXEL 0x00000406UL +#define CKA_CHAR_SETS 0x00000480UL +#define CKA_ENCODING_METHODS 0x00000481UL +#define CKA_MIME_TYPES 0x00000482UL +#define CKA_MECHANISM_TYPE 0x00000500UL +#define CKA_REQUIRED_CMS_ATTRIBUTES 0x00000501UL +#define CKA_DEFAULT_CMS_ATTRIBUTES 0x00000502UL +#define CKA_SUPPORTED_CMS_ATTRIBUTES 0x00000503UL +#define CKA_PROFILE_ID 0x00000601UL +#define CKA_X2RATCHET_BAG 0x00000602UL +#define CKA_X2RATCHET_BAGSIZE 0x00000603UL +#define CKA_X2RATCHET_BOBS1STMSG 0x00000604UL +#define CKA_X2RATCHET_CKR 0x00000605UL +#define CKA_X2RATCHET_CKS 0x00000606UL +#define CKA_X2RATCHET_DHP 0x00000607UL +#define CKA_X2RATCHET_DHR 0x00000608UL +#define CKA_X2RATCHET_DHS 0x00000609UL +#define CKA_X2RATCHET_HKR 0x0000060AUL +#define CKA_X2RATCHET_HKS 0x0000060BUL +#define CKA_X2RATCHET_ISALICE 0x0000060CUL +#define CKA_X2RATCHET_NHKR 0x0000060DUL +#define CKA_X2RATCHET_NHKS 0x0000060EUL +#define CKA_X2RATCHET_NR 0x0000060FUL +#define CKA_X2RATCHET_NS 0x00000610UL +#define CKA_X2RATCHET_PNS 0x00000611UL +#define CKA_X2RATCHET_RK 0x00000612UL +#define CKA_HSS_LEVELS 0x00000617UL +#define CKA_HSS_LMS_TYPE 0x00000618UL +#define CKA_HSS_LMOTS_TYPE 0x00000619UL +#define CKA_HSS_LMS_TYPES 0x0000061AUL +#define CKA_HSS_LMOTS_TYPES 0x0000061BUL +#define CKA_HSS_KEYS_REMAINING 0x0000061CUL +#define CKA_PARAMETER_SET 0x0000061DUL +#define CKA_OBJECT_VALIDATION_FLAGS 0x0000061EUL +#define CKA_VALIDATION_TYPE 0x0000061FUL +#define CKA_VALIDATION_VERSION 0x00000620UL +#define CKA_VALIDATION_LEVEL 0x00000621UL +#define CKA_VALIDATION_MODULE_ID 0x00000622UL +#define CKA_VALIDATION_FLAG 0x00000623UL +#define CKA_VALIDATION_AUTHORITY_TYPE 0x00000624UL +#define CKA_VALIDATION_COUNTRY 0x00000625UL +#define CKA_VALIDATION_CERTIFICATE_IDENTIFIER 0x00000626UL +#define CKA_VALIDATION_CERTIFICATE_URI 0x00000627UL +#define CKA_VALIDATION_VENDOR_URI 0x00000628UL +#define CKA_VALIDATION_PROFILE 0x00000629UL +#define CKA_ENCAPSULATE_TEMPLATE 0x0000062AUL +#define CKA_DECAPSULATE_TEMPLATE 0x0000062BUL +#define CKA_TRUST_SERVER_AUTH 0x0000062CUL +#define CKA_TRUST_CLIENT_AUTH 0x0000062DUL +#define CKA_TRUST_CODE_SIGNING 0x0000062EUL +#define CKA_TRUST_EMAIL_PROTECTION 0x0000062FUL +#define CKA_TRUST_IPSEC_IKE 0x00000630UL +#define CKA_TRUST_TIME_STAMPING 0x00000631UL +#define CKA_TRUST_OCSP_SIGNING 0x00000632UL +#define CKA_ENCAPSULATE 0x00000633UL +#define CKA_DECAPSULATE 0x00000634UL +#define CKA_HASH_OF_CERTIFICATE 0x00000635UL +#define CKA_PUBLIC_CRC64_VALUE 0x00000636UL +#define CKA_SEED 0x00000637UL +#define CKA_VENDOR_DEFINED 0x80000000UL +/* Array attributes */ +#define CKA_WRAP_TEMPLATE 0x40000211UL +#define CKA_UNWRAP_TEMPLATE 0x40000212UL +#define CKA_DERIVE_TEMPLATE 0x40000213UL +#define CKA_ALLOWED_MECHANISMS 0x40000600UL +/* Deprecated */ +#ifdef PKCS11_DEPRECATED +#define CKA_ECDSA_PARAMS 0x00000180UL +#define CKA_SECONDARY_AUTH 0x00000200UL +#define CKA_AUTH_PIN_FLAGS 0x00000201UL +#endif + +/* CKC */ +#define CKC_X_509 0x00000000UL +#define CKC_X_509_ATTR_CERT 0x00000001UL +#define CKC_WTLS 0x00000002UL +#define CKC_VENDOR_DEFINED 0x80000000UL + +/* CKD */ +#define CKD_NULL 0x00000001UL +#define CKD_SHA1_KDF 0x00000002UL +#define CKD_SHA1_KDF_ASN1 0x00000003UL +#define CKD_SHA1_KDF_CONCATENATE 0x00000004UL +#define CKD_SHA224_KDF 0x00000005UL +#define CKD_SHA256_KDF 0x00000006UL +#define CKD_SHA384_KDF 0x00000007UL +#define CKD_SHA512_KDF 0x00000008UL +#define CKD_CPDIVERSIFY_KDF 0x00000009UL +#define CKD_SHA3_224_KDF 0x0000000AUL +#define CKD_SHA3_256_KDF 0x0000000BUL +#define CKD_SHA3_384_KDF 0x0000000CUL +#define CKD_SHA3_512_KDF 0x0000000DUL +#define CKD_SHA1_KDF_SP800 0x0000000EUL +#define CKD_SHA224_KDF_SP800 0x0000000FUL +#define CKD_SHA256_KDF_SP800 0x00000010UL +#define CKD_SHA384_KDF_SP800 0x00000011UL +#define CKD_SHA512_KDF_SP800 0x00000012UL +#define CKD_SHA3_224_KDF_SP800 0x00000013UL +#define CKD_SHA3_256_KDF_SP800 0x00000014UL +#define CKD_SHA3_384_KDF_SP800 0x00000015UL +#define CKD_SHA3_512_KDF_SP800 0x00000016UL +#define CKD_BLAKE2B_160_KDF 0x00000017UL +#define CKD_BLAKE2B_256_KDF 0x00000018UL +#define CKD_BLAKE2B_384_KDF 0x00000019UL +#define CKD_BLAKE2B_512_KDF 0x0000001AUL + +/* CFK (array attributes) */ +#define CKF_ARRAY_ATTRIBUTE 0x40000000UL + +/* CKF (capabilities) */ +#define CKF_LIBRARY_CANT_CREATE_OS_THREADS 0x00000001UL +#define CKF_OS_LOCKING_OK 0x00000002UL + +/* CKF (HKDF) */ +#define CKF_HKDF_SALT_NULL 0x00000001UL +#define CKF_HKDF_SALT_DATA 0x00000002UL +#define CKF_HKDF_SALT_KEY 0x00000004UL + +/* CKF (interface) */ +#define CKF_INTERFACE_FORK_SAFE 0x00000001UL + +/* CKF (mechanism) */ +#define CKF_HW 0x00000001UL +#define CKF_MESSAGE_ENCRYPT 0x00000002UL +#define CKF_MESSAGE_DECRYPT 0x00000004UL +#define CKF_MESSAGE_SIGN 0x00000008UL +#define CKF_MESSAGE_VERIFY 0x00000010UL +#define CKF_MULTI_MESSAGE 0x00000020UL +#define CKF_MULTI_MESSGE 0x00000020UL +#define CKF_FIND_OBJECTS 0x00000040UL +#define CKF_ENCRYPT 0x00000100UL +#define CKF_DECRYPT 0x00000200UL +#define CKF_DIGEST 0x00000400UL +#define CKF_SIGN 0x00000800UL +#define CKF_SIGN_RECOVER 0x00001000UL +#define CKF_VERIFY 0x00002000UL +#define CKF_VERIFY_RECOVER 0x00004000UL +#define CKF_GENERATE 0x00008000UL +#define CKF_GENERATE_KEY_PAIR 0x00010000UL +#define CKF_WRAP 0x00020000UL +#define CKF_UNWRAP 0x00040000UL +#define CKF_DERIVE 0x00080000UL +#define CKF_EC_F_P 0x00100000UL +#define CKF_EC_F_2M 0x00200000UL +#define CKF_EC_ECPARAMETERS 0x00400000UL +#define CKF_EC_OID 0x00800000UL +#define CKF_EC_UNCOMPRESS 0x01000000UL +#define CKF_EC_COMPRESS 0x02000000UL +#define CKF_EC_CURVENAME 0x04000000UL +#define CKF_ENCAPSULATE 0x10000000UL +#define CKF_DECAPSULATE 0x20000000UL +#define CKF_EXTENSION 0x80000000UL +/* Deprecated */ +#ifdef PKCS11_DEPRECATED +#define CKF_EC_NAMEDCURVE 0x00800000U +#endif + +/* CKF (message) */ +#define CKF_END_OF_MESSAGE 0x00000001UL + +/* CKF (OTP) */ +#define CKF_NEXT_OTP 0x00000001UL +#define CKF_EXCLUDE_TIME 0x00000002UL +#define CKF_EXCLUDE_COUNTER 0x00000004UL +#define CKF_EXCLUDE_CHALLENGE 0x00000008UL +#define CKF_EXCLUDE_PIN 0x00000010UL +#define CKF_USER_FRIENDLY_OTP 0x00000020UL + +/* CKF (parameters to functions) */ +#define CKF_DONT_BLOCK 1 + +/* CKF (session) */ +#define CKF_RW_SESSION 0x00000002UL +#define CKF_SERIAL_SESSION 0x00000004UL +#define CKF_ASYNC_SESSION 0x00000008UL + +/* CFK (slot) */ +#define CKF_TOKEN_PRESENT 0x00000001UL +#define CKF_REMOVABLE_DEVICE 0x00000002UL +#define CKF_HW_SLOT 0x00000004UL + +/* CKF (token) */ +#define CKF_RNG 0x00000001UL +#define CKF_WRITE_PROTECTED 0x00000002UL +#define CKF_LOGIN_REQUIRED 0x00000004UL +#define CKF_USER_PIN_INITIALIZED 0x00000008UL +#define CKF_RESTORE_KEY_NOT_NEEDED 0x00000020UL +#define CKF_CLOCK_ON_TOKEN 0x00000040UL +#define CKF_PROTECTED_AUTHENTICATION_PATH 0x00000100UL +#define CKF_DUAL_CRYPTO_OPERATIONS 0x00000200UL +#define CKF_TOKEN_INITIALIZED 0x00000400UL +#define CKF_SECONDARY_AUTHENTICATION 0x00000800UL +#define CKF_USER_PIN_COUNT_LOW 0x00010000UL +#define CKF_USER_PIN_FINAL_TRY 0x00020000UL +#define CKF_USER_PIN_LOCKED 0x00040000UL +#define CKF_USER_PIN_TO_BE_CHANGED 0x00080000UL +#define CKF_SO_PIN_COUNT_LOW 0x00100000UL +#define CKF_SO_PIN_FINAL_TRY 0x00200000UL +#define CKF_SO_PIN_LOCKED 0x00400000UL +#define CKF_SO_PIN_TO_BE_CHANGED 0x00800000UL +#define CKF_ERROR_STATE 0x01000000UL +#define CKF_SEED_RANDOM_REQUIRED 0x02000000UL +#define CKF_ASYNC_SESSION_SUPPORTED 0x04000000UL + +/* CKG (GCM) */ +#define CKG_NO_GENERATE 0x00000000UL +#define CKG_GENERATE 0x00000001UL +#define CKG_GENERATE_COUNTER 0x00000002UL +#define CKG_GENERATE_RANDOM 0x00000003UL +#define CKG_GENERATE_COUNTER_XOR 0x00000004UL + +/* CKG (MFG) */ +#define CKG_MGF1_SHA1 0x00000001UL +#define CKG_MGF1_SHA256 0x00000002UL +#define CKG_MGF1_SHA384 0x00000003UL +#define CKG_MGF1_SHA512 0x00000004UL +#define CKG_MGF1_SHA224 0x00000005UL +#define CKG_MGF1_SHA3_224 0x00000006UL +#define CKG_MGF1_SHA3_256 0x00000007UL +#define CKG_MGF1_SHA3_384 0x00000008UL +#define CKG_MGF1_SHA3_512 0x00000009UL + +/* CKH (clock) */ +#define CKH_MONOTONIC_COUNTER 0x00000001UL +#define CKH_CLOCK 0x00000002UL +#define CKH_USER_INTERFACE 0x00000003UL +#define CKH_VENDOR_DEFINED 0x80000000UL + +/* CKH (hedge type) */ +#define CKH_HEDGE_PREFERRED 0x00000000UL +#define CKH_HEDGE_REQUIRED 0x00000001UL +#define CKH_DETERMINISTIC_REQUIRED 0x00000002UL + +/* CKK */ +#define CKK_RSA 0x00000000UL +#define CKK_DSA 0x00000001UL +#define CKK_DH 0x00000002UL +#define CKK_EC 0x00000003UL +#define CKK_X9_42_DH 0x00000004UL +#define CKK_KEA 0x00000005UL +#define CKK_GENERIC_SECRET 0x00000010UL +#define CKK_RC2 0x00000011UL +#define CKK_RC4 0x00000012UL +#define CKK_DES 0x00000013UL +#define CKK_DES2 0x00000014UL +#define CKK_DES3 0x00000015UL +#define CKK_CAST 0x00000016UL +#define CKK_CAST3 0x00000017UL +#define CKK_CAST128 0x00000018UL +#define CKK_RC5 0x00000019UL +#define CKK_IDEA 0x0000001AUL +#define CKK_SKIPJACK 0x0000001BUL +#define CKK_BATON 0x0000001CUL +#define CKK_JUNIPER 0x0000001DUL +#define CKK_CDMF 0x0000001EUL +#define CKK_AES 0x0000001FUL +#define CKK_BLOWFISH 0x00000020UL +#define CKK_TWOFISH 0x00000021UL +#define CKK_SECURID 0x00000022UL +#define CKK_HOTP 0x00000023UL +#define CKK_ACTI 0x00000024UL +#define CKK_CAMELLIA 0x00000025UL +#define CKK_ARIA 0x00000026UL +#define CKK_MD5_HMAC 0x00000027UL +#define CKK_SHA_1_HMAC 0x00000028UL +#define CKK_RIPEMD128_HMAC 0x00000029UL +#define CKK_RIPEMD160_HMAC 0x0000002AUL +#define CKK_SHA256_HMAC 0x0000002BUL +#define CKK_SHA384_HMAC 0x0000002CUL +#define CKK_SHA512_HMAC 0x0000002DUL +#define CKK_SHA224_HMAC 0x0000002EUL +#define CKK_SEED 0x0000002FUL +#define CKK_GOSTR3410 0x00000030UL +#define CKK_GOSTR3411 0x00000031UL +#define CKK_GOST28147 0x00000032UL +#define CKK_CHACHA20 0x00000033UL +#define CKK_POLY1305 0x00000034UL +#define CKK_AES_XTS 0x00000035UL +#define CKK_SHA3_224_HMAC 0x00000036UL +#define CKK_SHA3_256_HMAC 0x00000037UL +#define CKK_SHA3_384_HMAC 0x00000038UL +#define CKK_SHA3_512_HMAC 0x00000039UL +#define CKK_BLAKE2B_160_HMAC 0x0000003AUL +#define CKK_BLAKE2B_256_HMAC 0x0000003BUL +#define CKK_BLAKE2B_384_HMAC 0x0000003CUL +#define CKK_BLAKE2B_512_HMAC 0x0000003DUL +#define CKK_SALSA20 0x0000003EUL +#define CKK_X2RATCHET 0x0000003FUL +#define CKK_EC_EDWARDS 0x00000040UL +#define CKK_EC_MONTGOMERY 0x00000041UL +#define CKK_HKDF 0x00000042UL +#define CKK_SHA512_224_HMAC 0x00000043UL +#define CKK_SHA512_256_HMAC 0x00000044UL +#define CKK_SHA512_T_HMAC 0x00000045UL +#define CKK_HSS 0x00000046UL +#define CKK_XMSS 0x00000047UL +#define CKK_XMSSMT 0x00000048UL +#define CKK_ML_KEM 0x00000049UL +#define CKK_ML_DSA 0x0000004AUL +#define CKK_SLH_DSA 0x0000004BUL +#define CKK_VENDOR_DEFINED 0x80000000UL +/* Deprecated */ +#ifdef PKCS11_DEPRECATED +#define CKK_ECDSA 0x00000003UL +#define CKK_CAST5 0x00000018UL +#endif + +/* CKM */ +#define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000UL +#define CKM_RSA_PKCS 0x00000001UL +#define CKM_RSA_9796 0x00000002UL +#define CKM_RSA_X_509 0x00000003UL +#define CKM_MD2_RSA_PKCS 0x00000004UL +#define CKM_MD5_RSA_PKCS 0x00000005UL +#define CKM_SHA1_RSA_PKCS 0x00000006UL +#define CKM_RIPEMD128_RSA_PKCS 0x00000007UL +#define CKM_RIPEMD160_RSA_PKCS 0x00000008UL +#define CKM_RSA_PKCS_OAEP 0x00000009UL +#define CKM_RSA_X9_31_KEY_PAIR_GEN 0x0000000AUL +#define CKM_RSA_X9_31 0x0000000BUL +#define CKM_SHA1_RSA_X9_31 0x0000000CUL +#define CKM_RSA_PKCS_PSS 0x0000000DUL +#define CKM_SHA1_RSA_PKCS_PSS 0x0000000EUL +#define CKM_ML_KEM_KEY_PAIR_GEN 0x0000000FUL +#define CKM_DSA_KEY_PAIR_GEN 0x00000010UL +#define CKM_DSA 0x00000011UL +#define CKM_DSA_SHA1 0x00000012UL +#define CKM_DSA_SHA224 0x00000013UL +#define CKM_DSA_SHA256 0x00000014UL +#define CKM_DSA_SHA384 0x00000015UL +#define CKM_DSA_SHA512 0x00000016UL +#define CKM_ML_KEM 0x00000017UL +#define CKM_DSA_SHA3_224 0x00000018UL +#define CKM_DSA_SHA3_256 0x00000019UL +#define CKM_DSA_SHA3_384 0x0000001AUL +#define CKM_DSA_SHA3_512 0x0000001BUL +#define CKM_ML_DSA_KEY_PAIR_GEN 0x0000001CUL +#define CKM_ML_DSA 0x0000001DUL +#define CKM_HASH_ML_DSA 0x0000001FUL +#define CKM_DH_PKCS_KEY_PAIR_GEN 0x00000020UL +#define CKM_DH_PKCS_DERIVE 0x00000021UL +#define CKM_HASH_ML_DSA_SHA224 0x00000023UL +#define CKM_HASH_ML_DSA_SHA256 0x00000024UL +#define CKM_HASH_ML_DSA_SHA384 0x00000025UL +#define CKM_HASH_ML_DSA_SHA512 0x00000026UL +#define CKM_HASH_ML_DSA_SHA3_224 0x00000027UL +#define CKM_HASH_ML_DSA_SHA3_256 0x00000028UL +#define CKM_HASH_ML_DSA_SHA3_384 0x00000029UL +#define CKM_HASH_ML_DSA_SHA3_512 0x0000002AUL +#define CKM_HASH_ML_DSA_SHAKE128 0x0000002BUL +#define CKM_HASH_ML_DSA_SHAKE256 0x0000002CUL +#define CKM_SLH_DSA_KEY_PAIR_GEN 0x0000002DUL +#define CKM_SLH_DSA 0x0000002EUL +#define CKM_X9_42_DH_KEY_PAIR_GEN 0x00000030UL +#define CKM_X9_42_DH_DERIVE 0x00000031UL +#define CKM_X9_42_DH_HYBRID_DERIVE 0x00000032UL +#define CKM_X9_42_MQV_DERIVE 0x00000033UL +#define CKM_HASH_SLH_DSA 0x00000034UL +#define CKM_HASH_SLH_DSA_SHA224 0x00000036UL +#define CKM_HASH_SLH_DSA_SHA256 0x00000037UL +#define CKM_HASH_SLH_DSA_SHA384 0x00000038UL +#define CKM_HASH_SLH_DSA_SHA512 0x00000039UL +#define CKM_HASH_SLH_DSA_SHA3_224 0x0000003AUL +#define CKM_HASH_SLH_DSA_SHA3_256 0x0000003BUL +#define CKM_HASH_SLH_DSA_SHA3_384 0x0000003CUL +#define CKM_HASH_SLH_DSA_SHA3_512 0x0000003DUL +#define CKM_HASH_SLH_DSA_SHAKE128 0x0000003EUL +#define CKM_HASH_SLH_DSA_SHAKE256 0x0000003FUL +#define CKM_SHA256_RSA_PKCS 0x00000040UL +#define CKM_SHA384_RSA_PKCS 0x00000041UL +#define CKM_SHA512_RSA_PKCS 0x00000042UL +#define CKM_SHA256_RSA_PKCS_PSS 0x00000043UL +#define CKM_SHA384_RSA_PKCS_PSS 0x00000044UL +#define CKM_SHA512_RSA_PKCS_PSS 0x00000045UL +#define CKM_SHA224_RSA_PKCS 0x00000046UL +#define CKM_SHA224_RSA_PKCS_PSS 0x00000047UL +#define CKM_SHA512_224 0x00000048UL +#define CKM_SHA512_224_HMAC 0x00000049UL +#define CKM_SHA512_224_HMAC_GENERAL 0x0000004AUL +#define CKM_SHA512_224_KEY_DERIVATION 0x0000004BUL +#define CKM_SHA512_256 0x0000004CUL +#define CKM_SHA512_256_HMAC 0x0000004DUL +#define CKM_SHA512_256_HMAC_GENERAL 0x0000004EUL +#define CKM_SHA512_256_KEY_DERIVATION 0x0000004FUL +#define CKM_SHA512_T 0x00000050UL +#define CKM_SHA512_T_HMAC 0x00000051UL +#define CKM_SHA512_T_HMAC_GENERAL 0x00000052UL +#define CKM_SHA512_T_KEY_DERIVATION 0x00000053UL +#define CKM_TLS12_EXTENDED_MASTER_KEY_DERIVE 0x00000056UL +#define CKM_TLS12_EXTENDED_MASTER_KEY_DERIVE_DH 0x00000057UL +#define CKM_SHA3_256_RSA_PKCS 0x00000060UL +#define CKM_SHA3_384_RSA_PKCS 0x00000061UL +#define CKM_SHA3_512_RSA_PKCS 0x00000062UL +#define CKM_SHA3_256_RSA_PKCS_PSS 0x00000063UL +#define CKM_SHA3_384_RSA_PKCS_PSS 0x00000064UL +#define CKM_SHA3_512_RSA_PKCS_PSS 0x00000065UL +#define CKM_SHA3_224_RSA_PKCS 0x00000066UL +#define CKM_SHA3_224_RSA_PKCS_PSS 0x00000067UL +#define CKM_RC2_KEY_GEN 0x00000100UL +#define CKM_RC2_ECB 0x00000101UL +#define CKM_RC2_CBC 0x00000102UL +#define CKM_RC2_MAC 0x00000103UL +#define CKM_RC2_MAC_GENERAL 0x00000104UL +#define CKM_RC2_CBC_PAD 0x00000105UL +#define CKM_RC4_KEY_GEN 0x00000110UL +#define CKM_RC4 0x00000111UL +#define CKM_DES_KEY_GEN 0x00000120UL +#define CKM_DES_ECB 0x00000121UL +#define CKM_DES_CBC 0x00000122UL +#define CKM_DES_MAC 0x00000123UL +#define CKM_DES_MAC_GENERAL 0x00000124UL +#define CKM_DES_CBC_PAD 0x00000125UL +#define CKM_DES2_KEY_GEN 0x00000130UL +#define CKM_DES3_KEY_GEN 0x00000131UL +#define CKM_DES3_ECB 0x00000132UL +#define CKM_DES3_CBC 0x00000133UL +#define CKM_DES3_MAC 0x00000134UL +#define CKM_DES3_MAC_GENERAL 0x00000135UL +#define CKM_DES3_CBC_PAD 0x00000136UL +#define CKM_DES3_CMAC_GENERAL 0x00000137UL +#define CKM_DES3_CMAC 0x00000138UL +#define CKM_CDMF_KEY_GEN 0x00000140UL +#define CKM_CDMF_ECB 0x00000141UL +#define CKM_CDMF_CBC 0x00000142UL +#define CKM_CDMF_MAC 0x00000143UL +#define CKM_CDMF_MAC_GENERAL 0x00000144UL +#define CKM_CDMF_CBC_PAD 0x00000145UL +#define CKM_DES_OFB64 0x00000150UL +#define CKM_DES_OFB8 0x00000151UL +#define CKM_DES_CFB64 0x00000152UL +#define CKM_DES_CFB8 0x00000153UL +#define CKM_MD2 0x00000200UL +#define CKM_MD2_HMAC 0x00000201UL +#define CKM_MD2_HMAC_GENERAL 0x00000202UL +#define CKM_MD5 0x00000210UL +#define CKM_MD5_HMAC 0x00000211UL +#define CKM_MD5_HMAC_GENERAL 0x00000212UL +#define CKM_SHA_1 0x00000220UL +#define CKM_SHA_1_HMAC 0x00000221UL +#define CKM_SHA_1_HMAC_GENERAL 0x00000222UL +#define CKM_RIPEMD128 0x00000230UL +#define CKM_RIPEMD128_HMAC 0x00000231UL +#define CKM_RIPEMD128_HMAC_GENERAL 0x00000232UL +#define CKM_RIPEMD160 0x00000240UL +#define CKM_RIPEMD160_HMAC 0x00000241UL +#define CKM_RIPEMD160_HMAC_GENERAL 0x00000242UL +#define CKM_SHA256 0x00000250UL +#define CKM_SHA256_HMAC 0x00000251UL +#define CKM_SHA256_HMAC_GENERAL 0x00000252UL +#define CKM_SHA224 0x00000255UL +#define CKM_SHA224_HMAC 0x00000256UL +#define CKM_SHA224_HMAC_GENERAL 0x00000257UL +#define CKM_SHA384 0x00000260UL +#define CKM_SHA384_HMAC 0x00000261UL +#define CKM_SHA384_HMAC_GENERAL 0x00000262UL +#define CKM_SHA512 0x00000270UL +#define CKM_SHA512_HMAC 0x00000271UL +#define CKM_SHA512_HMAC_GENERAL 0x00000272UL +#define CKM_SECURID_KEY_GEN 0x00000280UL +#define CKM_SECURID 0x00000282UL +#define CKM_HOTP_KEY_GEN 0x00000290UL +#define CKM_HOTP 0x00000291UL +#define CKM_ACTI 0x000002A0UL +#define CKM_ACTI_KEY_GEN 0x000002A1UL +#define CKM_SHA3_256 0x000002B0UL +#define CKM_SHA3_256_HMAC 0x000002B1UL +#define CKM_SHA3_256_HMAC_GENERAL 0x000002B2UL +#define CKM_SHA3_256_KEY_GEN 0x000002B3UL +#define CKM_SHA3_224 0x000002B5UL +#define CKM_SHA3_224_HMAC 0x000002B6UL +#define CKM_SHA3_224_HMAC_GENERAL 0x000002B7UL +#define CKM_SHA3_224_KEY_GEN 0x000002B8UL +#define CKM_SHA3_384 0x000002C0UL +#define CKM_SHA3_384_HMAC 0x000002C1UL +#define CKM_SHA3_384_HMAC_GENERAL 0x000002C2UL +#define CKM_SHA3_384_KEY_GEN 0x000002C3UL +#define CKM_SHA3_512 0x000002D0UL +#define CKM_SHA3_512_HMAC 0x000002D1UL +#define CKM_SHA3_512_HMAC_GENERAL 0x000002D2UL +#define CKM_SHA3_512_KEY_GEN 0x000002D3UL +#define CKM_CAST_KEY_GEN 0x00000300UL +#define CKM_CAST_ECB 0x00000301UL +#define CKM_CAST_CBC 0x00000302UL +#define CKM_CAST_MAC 0x00000303UL +#define CKM_CAST_MAC_GENERAL 0x00000304UL +#define CKM_CAST_CBC_PAD 0x00000305UL +#define CKM_CAST3_KEY_GEN 0x00000310UL +#define CKM_CAST3_ECB 0x00000311UL +#define CKM_CAST3_CBC 0x00000312UL +#define CKM_CAST3_MAC 0x00000313UL +#define CKM_CAST3_MAC_GENERAL 0x00000314UL +#define CKM_CAST3_CBC_PAD 0x00000315UL +#define CKM_CAST128_KEY_GEN 0x00000320UL +#define CKM_CAST128_ECB 0x00000321UL +#define CKM_CAST128_MAC 0x00000323UL +#define CKM_CAST128_CBC 0x00000322UL +#define CKM_CAST128_MAC_GENERAL 0x00000324UL +#define CKM_CAST128_CBC_PAD 0x00000325UL +#define CKM_RC5_KEY_GEN 0x00000330UL +#define CKM_RC5_ECB 0x00000331UL +#define CKM_RC5_CBC 0x00000332UL +#define CKM_RC5_MAC 0x00000333UL +#define CKM_RC5_MAC_GENERAL 0x00000334UL +#define CKM_RC5_CBC_PAD 0x00000335UL +#define CKM_IDEA_KEY_GEN 0x00000340UL +#define CKM_IDEA_ECB 0x00000341UL +#define CKM_IDEA_CBC 0x00000342UL +#define CKM_IDEA_MAC 0x00000343UL +#define CKM_IDEA_MAC_GENERAL 0x00000344UL +#define CKM_IDEA_CBC_PAD 0x00000345UL +#define CKM_GENERIC_SECRET_KEY_GEN 0x00000350UL +#define CKM_CONCATENATE_BASE_AND_KEY 0x00000360UL +#define CKM_CONCATENATE_BASE_AND_DATA 0x00000362UL +#define CKM_CONCATENATE_DATA_AND_BASE 0x00000363UL +#define CKM_XOR_BASE_AND_DATA 0x00000364UL +#define CKM_EXTRACT_KEY_FROM_KEY 0x00000365UL +#define CKM_SSL3_PRE_MASTER_KEY_GEN 0x00000370UL +#define CKM_SSL3_MASTER_KEY_DERIVE 0x00000371UL +#define CKM_SSL3_KEY_AND_MAC_DERIVE 0x00000372UL +#define CKM_SSL3_MASTER_KEY_DERIVE_DH 0x00000373UL +#define CKM_TLS_PRE_MASTER_KEY_GEN 0x00000374UL +#define CKM_TLS_MASTER_KEY_DERIVE 0x00000375UL +#define CKM_TLS_KEY_AND_MAC_DERIVE 0x00000376UL +#define CKM_TLS_MASTER_KEY_DERIVE_DH 0x00000377UL +#define CKM_TLS_PRF 0x00000378UL +#define CKM_SSL3_MD5_MAC 0x00000380UL +#define CKM_SSL3_SHA1_MAC 0x00000381UL +#define CKM_MD5_KEY_DERIVATION 0x00000390UL +#define CKM_MD2_KEY_DERIVATION 0x00000391UL +#define CKM_SHA1_KEY_DERIVATION 0x00000392UL +#define CKM_SHA256_KEY_DERIVATION 0x00000393UL +#define CKM_SHA384_KEY_DERIVATION 0x00000394UL +#define CKM_SHA512_KEY_DERIVATION 0x00000395UL +#define CKM_SHA224_KEY_DERIVATION 0x00000396UL +#define CKM_SHA3_256_KEY_DERIVATION 0x00000397UL +#define CKM_SHA3_256_KEY_DERIVE 0x00000397UL +#define CKM_SHA3_224_KEY_DERIVATION 0x00000398UL +#define CKM_SHA3_224_KEY_DERIVE 0x00000398UL +#define CKM_SHA3_384_KEY_DERIVATION 0x00000399UL +#define CKM_SHA3_384_KEY_DERIVE 0x00000399UL +#define CKM_SHA3_512_KEY_DERIVATION 0x0000039AUL +#define CKM_SHA3_512_KEY_DERIVE 0x0000039AUL +#define CKM_SHAKE_128_KEY_DERIVATION 0x0000039BUL +#define CKM_SHAKE_128_KEY_DERIVE 0x0000039BUL +#define CKM_SHAKE_256_KEY_DERIVATION 0x0000039CUL +#define CKM_SHAKE_256_KEY_DERIVE 0x0000039CUL +#define CKM_PBE_MD2_DES_CBC 0x000003A0UL +#define CKM_PBE_MD5_DES_CBC 0x000003A1UL +#define CKM_PBE_MD5_CAST_CBC 0x000003A2UL +#define CKM_PBE_MD5_CAST3_CBC 0x000003A3UL +#define CKM_PBE_MD5_CAST128_CBC 0x000003A4UL +#define CKM_PBE_SHA1_CAST128_CBC 0x000003A5UL +#define CKM_PBE_SHA1_RC4_128 0x000003A6UL +#define CKM_PBE_SHA1_RC4_40 0x000003A7UL +#define CKM_PBE_SHA1_DES3_EDE_CBC 0x000003A8UL +#define CKM_PBE_SHA1_DES2_EDE_CBC 0x000003A9UL +#define CKM_PBE_SHA1_RC2_128_CBC 0x000003AAUL +#define CKM_PBE_SHA1_RC2_40_CBC 0x000003ABUL +#define CKM_PKCS5_PBKD2 0x000003B0UL +#define CKM_PBA_SHA1_WITH_SHA1_HMAC 0x000003C0UL +#define CKM_WTLS_PRE_MASTER_KEY_GEN 0x000003D0UL +#define CKM_WTLS_MASTER_KEY_DERIVE 0x000003D1UL +#define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC 0x000003D2UL +#define CKM_WTLS_PRF 0x000003D3UL +#define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE 0x000003D4UL +#define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE 0x000003D5UL +#define CKM_TLS10_MAC_SERVER 0x000003D6UL +#define CKM_TLS10_MAC_CLIENT 0x000003D7UL +#define CKM_TLS12_MAC 0x000003D8UL +#define CKM_TLS12_KDF 0x000003D9UL +#define CKM_TLS12_MASTER_KEY_DERIVE 0x000003E0UL +#define CKM_TLS12_KEY_AND_MAC_DERIVE 0x000003E1UL +#define CKM_TLS12_MASTER_KEY_DERIVE_DH 0x000003E2UL +#define CKM_TLS12_KEY_SAFE_DERIVE 0x000003E3UL +#define CKM_TLS_MAC 0x000003E4UL +#define CKM_TLS_KDF 0x000003E5UL +#define CKM_KEY_WRAP_LYNKS 0x00000400UL +#define CKM_KEY_WRAP_SET_OAEP 0x00000401UL +#define CKM_CMS_SIG 0x00000500UL +#define CKM_KIP_DERIVE 0x00000510UL +#define CKM_KIP_WRAP 0x00000511UL +#define CKM_KIP_MAC 0x00000512UL +#define CKM_CAMELLIA_KEY_GEN 0x00000550UL +#define CKM_CAMELLIA_ECB 0x00000551UL +#define CKM_CAMELLIA_CBC 0x00000552UL +#define CKM_CAMELLIA_MAC 0x00000553UL +#define CKM_CAMELLIA_MAC_GENERAL 0x00000554UL +#define CKM_CAMELLIA_CBC_PAD 0x00000555UL +#define CKM_CAMELLIA_ECB_ENCRYPT_DATA 0x00000556UL +#define CKM_CAMELLIA_CBC_ENCRYPT_DATA 0x00000557UL +#define CKM_CAMELLIA_CTR 0x00000558UL +#define CKM_ARIA_KEY_GEN 0x00000560UL +#define CKM_ARIA_ECB 0x00000561UL +#define CKM_ARIA_CBC 0x00000562UL +#define CKM_ARIA_MAC 0x00000563UL +#define CKM_ARIA_MAC_GENERAL 0x00000564UL +#define CKM_ARIA_CBC_PAD 0x00000565UL +#define CKM_ARIA_ECB_ENCRYPT_DATA 0x00000566UL +#define CKM_ARIA_CBC_ENCRYPT_DATA 0x00000567UL +#define CKM_SEED_KEY_GEN 0x00000650UL +#define CKM_SEED_ECB 0x00000651UL +#define CKM_SEED_CBC 0x00000652UL +#define CKM_SEED_MAC 0x00000653UL +#define CKM_SEED_MAC_GENERAL 0x00000654UL +#define CKM_SEED_CBC_PAD 0x00000655UL +#define CKM_SEED_ECB_ENCRYPT_DATA 0x00000656UL +#define CKM_SEED_CBC_ENCRYPT_DATA 0x00000657UL +#define CKM_SKIPJACK_KEY_GEN 0x00001000UL +#define CKM_SKIPJACK_ECB64 0x00001001UL +#define CKM_SKIPJACK_CBC64 0x00001002UL +#define CKM_SKIPJACK_OFB64 0x00001003UL +#define CKM_SKIPJACK_CFB64 0x00001004UL +#define CKM_SKIPJACK_CFB32 0x00001005UL +#define CKM_SKIPJACK_CFB16 0x00001006UL +#define CKM_SKIPJACK_CFB8 0x00001007UL +#define CKM_SKIPJACK_WRAP 0x00001008UL +#define CKM_SKIPJACK_PRIVATE_WRAP 0x00001009UL +#define CKM_SKIPJACK_RELAYX 0x0000100AUL +#define CKM_KEA_KEY_PAIR_GEN 0x00001010UL +#define CKM_KEA_KEY_DERIVE 0x00001011UL +#define CKM_KEA_DERIVE 0x00001012UL +#define CKM_FORTEZZA_TIMESTAMP 0x00001020UL +#define CKM_BATON_KEY_GEN 0x00001030UL +#define CKM_BATON_ECB128 0x00001031UL +#define CKM_BATON_ECB96 0x00001032UL +#define CKM_BATON_CBC128 0x00001033UL +#define CKM_BATON_COUNTER 0x00001034UL +#define CKM_BATON_SHUFFLE 0x00001035UL +#define CKM_BATON_WRAP 0x00001036UL +#define CKM_EC_KEY_PAIR_GEN 0x00001040UL +#define CKM_ECDSA 0x00001041UL +#define CKM_ECDSA_SHA1 0x00001042UL +#define CKM_ECDSA_SHA224 0x00001043UL +#define CKM_ECDSA_SHA256 0x00001044UL +#define CKM_ECDSA_SHA384 0x00001045UL +#define CKM_ECDSA_SHA512 0x00001046UL +#define CKM_EC_KEY_PAIR_GEN_W_EXTRA_BITS 0x0000140BUL +#define CKM_ECDH1_DERIVE 0x00001050UL +#define CKM_ECDH1_COFACTOR_DERIVE 0x00001051UL +#define CKM_ECMQV_DERIVE 0x00001052UL +#define CKM_ECDH_AES_KEY_WRAP 0x00001053UL +#define CKM_RSA_AES_KEY_WRAP 0x00001054UL +#define CKM_JUNIPER_KEY_GEN 0x00001060UL +#define CKM_JUNIPER_ECB128 0x00001061UL +#define CKM_JUNIPER_CBC128 0x00001062UL +#define CKM_JUNIPER_COUNTER 0x00001063UL +#define CKM_JUNIPER_SHUFFLE 0x00001064UL +#define CKM_JUNIPER_WRAP 0x00001065UL +#define CKM_FASTHASH 0x00001070UL +#define CKM_AES_XTS 0x00001071UL +#define CKM_AES_XTS_KEY_GEN 0x00001072UL +#define CKM_AES_KEY_GEN 0x00001080UL +#define CKM_AES_ECB 0x00001081UL +#define CKM_AES_CBC 0x00001082UL +#define CKM_AES_MAC 0x00001083UL +#define CKM_AES_MAC_GENERAL 0x00001084UL +#define CKM_AES_CBC_PAD 0x00001085UL +#define CKM_AES_CTR 0x00001086UL +#define CKM_AES_GCM 0x00001087UL +#define CKM_AES_CCM 0x00001088UL +#define CKM_AES_CTS 0x00001089UL +#define CKM_AES_CMAC 0x0000108AUL +#define CKM_AES_CMAC_GENERAL 0x0000108BUL +#define CKM_AES_XCBC_MAC 0x0000108CUL +#define CKM_AES_XCBC_MAC_96 0x0000108DUL +#define CKM_AES_GMAC 0x0000108EUL +#define CKM_BLOWFISH_KEY_GEN 0x00001090UL +#define CKM_BLOWFISH_CBC 0x00001091UL +#define CKM_TWOFISH_KEY_GEN 0x00001092UL +#define CKM_TWOFISH_CBC 0x00001093UL +#define CKM_BLOWFISH_CBC_PAD 0x00001094UL +#define CKM_TWOFISH_CBC_PAD 0x00001095UL +#define CKM_DES_ECB_ENCRYPT_DATA 0x00001100UL +#define CKM_DES_CBC_ENCRYPT_DATA 0x00001101UL +#define CKM_DES3_ECB_ENCRYPT_DATA 0x00001102UL +#define CKM_DES3_CBC_ENCRYPT_DATA 0x00001103UL +#define CKM_AES_ECB_ENCRYPT_DATA 0x00001104UL +#define CKM_AES_CBC_ENCRYPT_DATA 0x00001105UL +#define CKM_GOSTR3410_KEY_PAIR_GEN 0x00001200UL +#define CKM_GOSTR3410 0x00001201UL +#define CKM_GOSTR3410_WITH_GOSTR3411 0x00001202UL +#define CKM_GOSTR3410_KEY_WRAP 0x00001203UL +#define CKM_GOSTR3410_DERIVE 0x00001204UL +#define CKM_GOSTR3411 0x00001210UL +#define CKM_GOSTR3411_HMAC 0x00001211UL +#define CKM_GOST28147_KEY_GEN 0x00001220UL +#define CKM_GOST28147_ECB 0x00001221UL +#define CKM_GOST28147 0x00001222UL +#define CKM_GOST28147_MAC 0x00001223UL +#define CKM_GOST28147_KEY_WRAP 0x00001224UL +#define CKM_CHACHA20_KEY_GEN 0x00001225UL +#define CKM_CHACHA20 0x00001226UL +#define CKM_POLY1305_KEY_GEN 0x00001227UL +#define CKM_POLY1305 0x00001228UL +#define CKM_DSA_PARAMETER_GEN 0x00002000UL +#define CKM_DH_PKCS_PARAMETER_GEN 0x00002001UL +#define CKM_X9_42_DH_PARAMETER_GEN 0x00002002UL +#define CKM_DSA_PROBABILISTIC_PARAMETER_GEN 0x00002003UL +#define CKM_DSA_SHAWE_TAYLOR_PARAMETER_GEN 0x00002004UL +#define CKM_DSA_FIPS_G_GEN 0x00002005UL +#define CKM_AES_OFB 0x00002104UL +#define CKM_AES_CFB64 0x00002105UL +#define CKM_AES_CFB8 0x00002106UL +#define CKM_AES_CFB128 0x00002107UL +#define CKM_AES_CFB1 0x00002108UL +#define CKM_AES_KEY_WRAP 0x00002109UL +#define CKM_AES_KEY_WRAP_PAD 0x0000210AUL +#define CKM_AES_KEY_WRAP_KWP 0x0000210BUL +#define CKM_AES_KEY_WRAP_PKCS7 0x0000210CUL +#define CKM_RSA_PKCS_TPM_1_1 0x00004001UL +#define CKM_RSA_PKCS_OAEP_TPM_1_1 0x00004002UL +#define CKM_SHA_1_KEY_GEN 0x00004003UL +#define CKM_SHA224_KEY_GEN 0x00004004UL +#define CKM_SHA256_KEY_GEN 0x00004005UL +#define CKM_SHA384_KEY_GEN 0x00004006UL +#define CKM_SHA512_KEY_GEN 0x00004007UL +#define CKM_SHA512_224_KEY_GEN 0x00004008UL +#define CKM_SHA512_256_KEY_GEN 0x00004009UL +#define CKM_SHA512_T_KEY_GEN 0x0000400AUL +#define CKM_NULL 0x0000400BUL +#define CKM_BLAKE2B_160 0x0000400CUL +#define CKM_BLAKE2B_160_HMAC 0x0000400DUL +#define CKM_BLAKE2B_160_HMAC_GENERAL 0x0000400EUL +#define CKM_BLAKE2B_160_KEY_DERIVE 0x0000400FUL +#define CKM_BLAKE2B_160_KEY_GEN 0x00004010UL +#define CKM_BLAKE2B_256 0x00004011UL +#define CKM_BLAKE2B_256_HMAC 0x00004012UL +#define CKM_BLAKE2B_256_HMAC_GENERAL 0x00004013UL +#define CKM_BLAKE2B_256_KEY_DERIVE 0x00004014UL +#define CKM_BLAKE2B_256_KEY_GEN 0x00004015UL +#define CKM_BLAKE2B_384 0x00004016UL +#define CKM_BLAKE2B_384_HMAC 0x00004017UL +#define CKM_BLAKE2B_384_HMAC_GENERAL 0x00004018UL +#define CKM_BLAKE2B_384_KEY_DERIVE 0x00004019UL +#define CKM_BLAKE2B_384_KEY_GEN 0x0000401AUL +#define CKM_BLAKE2B_512 0x0000401BUL +#define CKM_BLAKE2B_512_HMAC 0x0000401CUL +#define CKM_BLAKE2B_512_HMAC_GENERAL 0x0000401DUL +#define CKM_BLAKE2B_512_KEY_DERIVE 0x0000401EUL +#define CKM_BLAKE2B_512_KEY_GEN 0x0000401FUL +#define CKM_SALSA20 0x00004020UL +#define CKM_CHACHA20_POLY1305 0x00004021UL +#define CKM_SALSA20_POLY1305 0x00004022UL +#define CKM_X3DH_INITIALIZE 0x00004023UL +#define CKM_X3DH_RESPOND 0x00004024UL +#define CKM_X2RATCHET_INITIALIZE 0x00004025UL +#define CKM_X2RATCHET_RESPOND 0x00004026UL +#define CKM_X2RATCHET_ENCRYPT 0x00004027UL +#define CKM_X2RATCHET_DECRYPT 0x00004028UL +#define CKM_XEDDSA 0x00004029UL +#define CKM_HKDF_DERIVE 0x0000402AUL +#define CKM_HKDF_DATA 0x0000402BUL +#define CKM_HKDF_KEY_GEN 0x0000402CUL +#define CKM_SALSA20_KEY_GEN 0x0000402DUL +#define CKM_ECDSA_SHA3_224 0x00001047UL +#define CKM_ECDSA_SHA3_256 0x00001048UL +#define CKM_ECDSA_SHA3_384 0x00001049UL +#define CKM_ECDSA_SHA3_512 0x0000104AUL +#define CKM_EC_EDWARDS_KEY_PAIR_GEN 0x00001055UL +#define CKM_EC_MONTGOMERY_KEY_PAIR_GEN 0x00001056UL +#define CKM_EDDSA 0x00001057UL +#define CKM_SP800_108_COUNTER_KDF 0x000003ACUL +#define CKM_SP800_108_FEEDBACK_KDF 0x000003ADUL +#define CKM_SP800_108_DOUBLE_PIPELINE_KDF 0x000003AEUL +#define CKM_IKE2_PRF_PLUS_DERIVE 0x0000402EUL +#define CKM_IKE_PRF_DERIVE 0x0000402FUL +#define CKM_IKE1_PRF_DERIVE 0x00004030UL +#define CKM_IKE1_EXTENDED_DERIVE 0x00004031UL +#define CKM_HSS_KEY_PAIR_GEN 0x00004032UL +#define CKM_HSS 0x00004033UL +#define CKM_XMSS_KEY_PAIR_GEN 0x00004034UL +#define CKM_XMSSMT_KEY_PAIR_GEN 0x00004035UL +#define CKM_XMSS 0x00004036UL +#define CKM_XMSSMT 0x00004037UL +#define CKM_ECDH_X_AES_KEY_WRAP 0x00004038UL +#define CKM_ECDH_COF_AES_KEY_WRAP 0x00004039UL +#define CKM_PUB_KEY_FROM_PRIV_KEY 0x0000403AUL +#define CKM_VENDOR_DEFINED 0x80000000UL +/* Deprecated */ +#ifdef PKCS11_DEPRECATED +#define CKM_CAST5_KEY_GEN 0x00000320UL +#define CKM_CAST5_ECB 0x00000321UL +#define CKM_CAST5_CBC 0x00000322UL +#define CKM_CAST5_MAC 0x00000323UL +#define CKM_CAST5_MAC_GENERAL 0x00000324UL +#define CKM_CAST5_CBC_PAD 0x00000325UL +#define CKM_PBE_MD5_CAST5_CBC 0x000003A4UL +#define CKM_PBE_SHA1_CAST5_CBC 0x000003A5UL +#define CKM_ECDSA_KEY_PAIR_GEN 0x00001040UL +#define CKM_DSA_PROBABLISTIC_PARAMETER_GEN 0x00002003UL +#endif + +/* CKN */ +#define CKN_SURRENDER 0UL +#define CKN_OTP_CHANGED 1UL + +/* CKO */ +#define CKO_DATA 0x00000000UL +#define CKO_CERTIFICATE 0x00000001UL +#define CKO_PUBLIC_KEY 0x00000002UL +#define CKO_PRIVATE_KEY 0x00000003UL +#define CKO_SECRET_KEY 0x00000004UL +#define CKO_HW_FEATURE 0x00000005UL +#define CKO_DOMAIN_PARAMETERS 0x00000006UL +#define CKO_MECHANISM 0x00000007UL +#define CKO_OTP_KEY 0x00000008UL +#define CKO_PROFILE 0x00000009UL +#define CKO_VALIDATION 0x0000000AUL +#define CKO_TRUST 0x0000000BUL +#define CKO_VENDOR_DEFINED 0x80000000UL + +/* CKP (profile) */ +#define CKP_INVALID_ID 0x00000000UL +#define CKP_BASELINE_PROVIDER 0x00000001UL +#define CKP_EXTENDED_PROVIDER 0x00000002UL +#define CKP_AUTHENTICATION_TOKEN 0x00000003UL +#define CKP_PUBLIC_CERTIFICATES_TOKEN 0x00000004UL +#define CKP_COMPLETE_PROVIDER 0x00000005UL +#define CKP_HKDF_TLS_TOKEN 0x00000006UL +#define CKP_VENDOR_DEFINED 0x80000000UL + +/* CKP (PBKD2) */ +#define CKP_PKCS5_PBKD2_HMAC_SHA1 0x00000001UL +#define CKP_PKCS5_PBKD2_HMAC_GOSTR3411 0x00000002UL +#define CKP_PKCS5_PBKD2_HMAC_SHA224 0x00000003UL +#define CKP_PKCS5_PBKD2_HMAC_SHA256 0x00000004UL +#define CKP_PKCS5_PBKD2_HMAC_SHA384 0x00000005UL +#define CKP_PKCS5_PBKD2_HMAC_SHA512 0x00000006UL +#define CKP_PKCS5_PBKD2_HMAC_SHA512_224 0x00000007UL +#define CKP_PKCS5_PBKD2_HMAC_SHA512_256 0x00000008UL + +/* CKP (ML-DSA) */ +#define CKP_ML_DSA_44 0x00000001UL +#define CKP_ML_DSA_65 0x00000002UL +#define CKP_ML_DSA_87 0x00000003UL + +/* CKP (ML_KEM) */ +#define CKP_ML_KEM_512 0x00000001UL +#define CKP_ML_KEM_768 0x00000002UL +#define CKP_ML_KEM_1024 0x00000003UL + +/* CKP (SLH-DSA) */ +#define CKP_SLH_DSA_SHA2_128S 0x00000001UL +#define CKP_SLH_DSA_SHAKE_128S 0x00000002UL +#define CKP_SLH_DSA_SHA2_128F 0x00000003UL +#define CKP_SLH_DSA_SHAKE_128F 0x00000004UL +#define CKP_SLH_DSA_SHA2_192S 0x00000005UL +#define CKP_SLH_DSA_SHAKE_192S 0x00000006UL +#define CKP_SLH_DSA_SHA2_192F 0x00000007UL +#define CKP_SLH_DSA_SHAKE_192F 0x00000008UL +#define CKP_SLH_DSA_SHA2_256S 0x00000009UL +#define CKP_SLH_DSA_SHAKE_256S 0x0000000AUL +#define CKP_SLH_DSA_SHA2_256F 0x0000000BUL +#define CKP_SLH_DSA_SHAKE_256F 0x0000000CUL + +/* CKR */ +#define CKR_OK 0x00000000UL +#define CKR_CANCEL 0x00000001UL +#define CKR_HOST_MEMORY 0x00000002UL +#define CKR_SLOT_ID_INVALID 0x00000003UL +#define CKR_GENERAL_ERROR 0x00000005UL +#define CKR_FUNCTION_FAILED 0x00000006UL +#define CKR_ARGUMENTS_BAD 0x00000007UL +#define CKR_NO_EVENT 0x00000008UL +#define CKR_NEED_TO_CREATE_THREADS 0x00000009UL +#define CKR_CANT_LOCK 0x0000000AUL +#define CKR_ATTRIBUTE_READ_ONLY 0x00000010UL +#define CKR_ATTRIBUTE_SENSITIVE 0x00000011UL +#define CKR_ATTRIBUTE_TYPE_INVALID 0x00000012UL +#define CKR_ATTRIBUTE_VALUE_INVALID 0x00000013UL +#define CKR_ACTION_PROHIBITED 0x0000001BUL +#define CKR_DATA_INVALID 0x00000020UL +#define CKR_DATA_LEN_RANGE 0x00000021UL +#define CKR_DEVICE_ERROR 0x00000030UL +#define CKR_DEVICE_MEMORY 0x00000031UL +#define CKR_DEVICE_REMOVED 0x00000032UL +#define CKR_ENCRYPTED_DATA_INVALID 0x00000040UL +#define CKR_ENCRYPTED_DATA_LEN_RANGE 0x00000041UL +#define CKR_AEAD_DECRYPT_FAILED 0x00000042UL +#define CKR_FUNCTION_CANCELED 0x00000050UL +#define CKR_FUNCTION_NOT_PARALLEL 0x00000051UL +#define CKR_FUNCTION_NOT_SUPPORTED 0x00000054UL +#define CKR_KEY_HANDLE_INVALID 0x00000060UL +#define CKR_KEY_SIZE_RANGE 0x00000062UL +#define CKR_KEY_TYPE_INCONSISTENT 0x00000063UL +#define CKR_KEY_NOT_NEEDED 0x00000064UL +#define CKR_KEY_CHANGED 0x00000065UL +#define CKR_KEY_NEEDED 0x00000066UL +#define CKR_KEY_INDIGESTIBLE 0x00000067UL +#define CKR_KEY_FUNCTION_NOT_PERMITTED 0x00000068UL +#define CKR_KEY_NOT_WRAPPABLE 0x00000069UL +#define CKR_KEY_UNEXTRACTABLE 0x0000006AUL +#define CKR_MECHANISM_INVALID 0x00000070UL +#define CKR_MECHANISM_PARAM_INVALID 0x00000071UL +#define CKR_OBJECT_HANDLE_INVALID 0x00000082UL +#define CKR_OPERATION_ACTIVE 0x00000090UL +#define CKR_OPERATION_NOT_INITIALIZED 0x00000091UL +#define CKR_PIN_INCORRECT 0x000000A0UL +#define CKR_PIN_INVALID 0x000000A1UL +#define CKR_PIN_LEN_RANGE 0x000000A2UL +#define CKR_PIN_EXPIRED 0x000000A3UL +#define CKR_PIN_LOCKED 0x000000A4UL +#define CKR_SESSION_CLOSED 0x000000B0UL +#define CKR_SESSION_COUNT 0x000000B1UL +#define CKR_SESSION_HANDLE_INVALID 0x000000B3UL +#define CKR_SESSION_PARALLEL_NOT_SUPPORTED 0x000000B4UL +#define CKR_SESSION_READ_ONLY 0x000000B5UL +#define CKR_SESSION_EXISTS 0x000000B6UL +#define CKR_SESSION_READ_ONLY_EXISTS 0x000000B7UL +#define CKR_SESSION_READ_WRITE_SO_EXISTS 0x000000B8UL +#define CKR_SIGNATURE_INVALID 0x000000C0UL +#define CKR_SIGNATURE_LEN_RANGE 0x000000C1UL +#define CKR_TEMPLATE_INCOMPLETE 0x000000D0UL +#define CKR_TEMPLATE_INCONSISTENT 0x000000D1UL +#define CKR_TOKEN_NOT_PRESENT 0x000000E0UL +#define CKR_TOKEN_NOT_RECOGNIZED 0x000000E1UL +#define CKR_TOKEN_WRITE_PROTECTED 0x000000E2UL +#define CKR_UNWRAPPING_KEY_HANDLE_INVALID 0x000000F0UL +#define CKR_UNWRAPPING_KEY_SIZE_RANGE 0x000000F1UL +#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT 0x000000F2UL +#define CKR_USER_ALREADY_LOGGED_IN 0x00000100UL +#define CKR_USER_NOT_LOGGED_IN 0x00000101UL +#define CKR_USER_PIN_NOT_INITIALIZED 0x00000102UL +#define CKR_USER_TYPE_INVALID 0x00000103UL +#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN 0x00000104UL +#define CKR_USER_TOO_MANY_TYPES 0x00000105UL +#define CKR_WRAPPED_KEY_INVALID 0x00000110UL +#define CKR_WRAPPED_KEY_LEN_RANGE 0x00000112UL +#define CKR_WRAPPING_KEY_HANDLE_INVALID 0x00000113UL +#define CKR_WRAPPING_KEY_SIZE_RANGE 0x00000114UL +#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT 0x00000115UL +#define CKR_RANDOM_SEED_NOT_SUPPORTED 0x00000120UL +#define CKR_RANDOM_NO_RNG 0x00000121UL +#define CKR_DOMAIN_PARAMS_INVALID 0x00000130UL +#define CKR_CURVE_NOT_SUPPORTED 0x00000140UL +#define CKR_BUFFER_TOO_SMALL 0x00000150UL +#define CKR_SAVED_STATE_INVALID 0x00000160UL +#define CKR_INFORMATION_SENSITIVE 0x00000170UL +#define CKR_STATE_UNSAVEABLE 0x00000180UL +#define CKR_CRYPTOKI_NOT_INITIALIZED 0x00000190UL +#define CKR_CRYPTOKI_ALREADY_INITIALIZED 0x00000191UL +#define CKR_MUTEX_BAD 0x000001A0UL +#define CKR_MUTEX_NOT_LOCKED 0x000001A1UL +#define CKR_NEW_PIN_MODE 0x000001B0UL +#define CKR_NEXT_OTP 0x000001B1UL +#define CKR_EXCEEDED_MAX_ITERATIONS 0x000001B5UL +#define CKR_FIPS_SELF_TEST_FAILED 0x000001B6UL +#define CKR_LIBRARY_LOAD_FAILED 0x000001B7UL +#define CKR_PIN_TOO_WEAK 0x000001B8UL +#define CKR_PUBLIC_KEY_INVALID 0x000001B9UL +#define CKR_FUNCTION_REJECTED 0x00000200UL +#define CKR_TOKEN_RESOURCE_EXCEEDED 0x00000201UL +#define CKR_OPERATION_CANCEL_FAILED 0x00000202UL +#define CKR_KEY_EXHAUSTED 0x00000203UL +#define CKR_PENDING 0x00000204UL +#define CKR_SESSION_ASYNC_NOT_SUPPORTED 0x00000205UL +#define CKR_SEED_RANDOM_REQUIRED 0x00000206UL +#define CKR_OPERATION_NOT_VALIDATED 0x00000207UL +#define CKR_TOKEN_NOT_INITIALIZED 0x00000208UL +#define CKR_PARAMETER_SET_NOT_SUPPORTED 0x00000209UL +#define CKR_VENDOR_DEFINED 0x80000000UL + +/* CKS */ +#define CKS_RO_PUBLIC_SESSION 0UL +#define CKS_RO_USER_FUNCTIONS 1UL +#define CKS_RW_PUBLIC_SESSION 2UL +#define CKS_RW_USER_FUNCTIONS 3UL +#define CKS_RW_SO_FUNCTIONS 4UL + +/* CKS (validation) */ +#define CKS_LAST_VALIDATION_OK 0x00000001UL + +/* CKT (trust) */ +#define CKT_TRUST_UNKNOWN 0x00000000UL +#define CKT_TRUSTED 0x00000001UL +#define CKT_TRUST_ANCHOR 0x00000002UL +#define CKT_NOT_TRUSTED 0x00000003UL +#define CKT_TRUST_MUST_VERIFY_TRUST 0x00000004UL + +/* CKU */ +#define CKU_SO 0UL +#define CKU_USER 1UL +#define CKU_CONTEXT_SPECIFIC 2UL + +/* CKV (validation authority) */ +#define CKV_AUTHORITY_TYPE_UNSPECIFIED 0x00000000UL +#define CKV_AUTHORITY_TYPE_NIST_CMVP 0x00000001UL +#define CKV_AUTHORITY_TYPE_COMMON_CRITERIA 0x00000002UL + +/* CKV (validation type) */ +#define CKV_TYPE_UNSPECIFIED 0x00000000UL +#define CKV_TYPE_SOFTWARE 0x00000001UL +#define CKV_TYPE_HARDWARE 0x00000002UL +#define CKV_TYPE_FIRMWARE 0x00000003UL +#define CKV_TYPE_HYBRID 0x00000004UL + +/* CKZ (data) */ +#define CKZ_DATA_SPECIFIED 0x00000001UL + +/* CKZ (salt) */ +#define CKZ_SALT_SPECIFIED 0x00000001UL + +/* Sundry structures type definition in alphabetical order */ +#define STRUCTDEF(__name__) \ +struct __name__; \ +typedef struct __name__ __name__; \ +typedef struct __name__ * __name__ ## _PTR; \ +typedef struct __name__ ** __name__ ## _PTR_PTR; + +STRUCTDEF(CK_ATTRIBUTE); +STRUCTDEF(CK_C_INITIALIZE_ARGS); +STRUCTDEF(CK_DATE); +STRUCTDEF(CK_DERIVED_KEY); +STRUCTDEF(CK_FUNCTION_LIST); +STRUCTDEF(CK_FUNCTION_LIST_3_0); +STRUCTDEF(CK_FUNCTION_LIST_3_2); +STRUCTDEF(CK_INFO); +STRUCTDEF(CK_INTERFACE); +STRUCTDEF(CK_MECHANISM); +STRUCTDEF(CK_MECHANISM_INFO); +STRUCTDEF(CK_SESSION_INFO); +STRUCTDEF(CK_SLOT_INFO); +STRUCTDEF(CK_TOKEN_INFO); +STRUCTDEF(CK_VERSION); + +/* Function type definitions */ +typedef CK_RV (* CK_NOTIFY)(CK_SESSION_HANDLE, CK_NOTIFICATION, void *); +typedef CK_RV (* CK_CREATEMUTEX)(void **); +typedef CK_RV (* CK_DESTROYMUTEX)(void *); +typedef CK_RV (* CK_LOCKMUTEX)(void *); +typedef CK_RV (* CK_UNLOCKMUTEX)(void *); + +/* General Structure definitions */ +struct CK_ATTRIBUTE { + CK_ATTRIBUTE_TYPE type; + void * pValue; + CK_ULONG ulValueLen; +}; + +struct CK_C_INITIALIZE_ARGS { + CK_CREATEMUTEX CreateMutex; + CK_DESTROYMUTEX DestroyMutex; + CK_LOCKMUTEX LockMutex; + CK_UNLOCKMUTEX UnlockMutex; + CK_FLAGS flags; + void * pReserved; +}; + +struct CK_DATE{ + CK_CHAR year[4]; + CK_CHAR month[2]; + CK_CHAR day[2]; +}; + +struct CK_DERIVED_KEY +{ + CK_ATTRIBUTE * pTemplate; + CK_ULONG ulAttributeCount; + CK_OBJECT_HANDLE * phKey; +}; + +struct CK_VERSION { + CK_BYTE major; + CK_BYTE minor; +}; + +struct CK_INFO { + struct CK_VERSION cryptokiVersion; + CK_UTF8CHAR manufacturerID[32]; + CK_FLAGS flags; + CK_UTF8CHAR libraryDescription[32]; + struct CK_VERSION libraryVersion; +}; + +struct CK_INTERFACE { + CK_UTF8CHAR * pInterfaceName; + void * pFunctionList; + CK_FLAGS flags; +}; + +struct CK_MECHANISM { + CK_MECHANISM_TYPE mechanism; + void * pParameter; + CK_ULONG ulParameterLen; +}; + +struct CK_MECHANISM_INFO { + CK_ULONG ulMinKeySize; + CK_ULONG ulMaxKeySize; + CK_FLAGS flags; +}; + +struct CK_SESSION_INFO { + CK_SLOT_ID slotID; + CK_STATE state; + CK_FLAGS flags; + CK_ULONG ulDeviceError; +}; + +struct CK_SLOT_INFO { + CK_UTF8CHAR slotDescription[64]; + CK_UTF8CHAR manufacturerID[32]; + CK_FLAGS flags; + CK_VERSION hardwareVersion; + CK_VERSION firmwareVersion; +}; + +struct CK_TOKEN_INFO { + CK_UTF8CHAR label[32]; + CK_UTF8CHAR manufacturerID[32]; + CK_UTF8CHAR model[16]; + CK_CHAR serialNumber[16]; + CK_FLAGS flags; + CK_ULONG ulMaxSessionCount; + CK_ULONG ulSessionCount; + CK_ULONG ulMaxRwSessionCount; + CK_ULONG ulRwSessionCount; + CK_ULONG ulMaxPinLen; + CK_ULONG ulMinPinLen; + CK_ULONG ulTotalPublicMemory; + CK_ULONG ulFreePublicMemory; + CK_ULONG ulTotalPrivateMemory; + CK_ULONG ulFreePrivateMemory; + CK_VERSION hardwareVersion; + CK_VERSION firmwareVersion; + CK_CHAR utcTime[16]; +}; + +/* Param Structure definitions in alphabetical order */ +STRUCTDEF(CK_AES_CBC_ENCRYPT_DATA_PARAMS); +STRUCTDEF(CK_AES_CCM_PARAMS); +STRUCTDEF(CK_AES_CTR_PARAMS); +STRUCTDEF(CK_AES_GCM_PARAMS); +STRUCTDEF(CK_ARIA_CBC_ENCRYPT_DATA_PARAMS); +STRUCTDEF(CK_ASYNC_DATA); +STRUCTDEF(CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS); +STRUCTDEF(CK_CAMELLIA_CTR_PARAMS); +STRUCTDEF(CK_CCM_MESSAGE_PARAMS); +STRUCTDEF(CK_CCM_PARAMS); +STRUCTDEF(CK_CCM_WRAP_PARAMS); +STRUCTDEF(CK_CHACHA20_PARAMS); +STRUCTDEF(CK_CMS_SIG_PARAMS); +STRUCTDEF(CK_DES_CBC_ENCRYPT_DATA_PARAMS); +STRUCTDEF(CK_DSA_PARAMETER_GEN_PARAM); +STRUCTDEF(CK_ECDH_AES_KEY_WRAP_PARAMS); +STRUCTDEF(CK_ECDH1_DERIVE_PARAMS); +STRUCTDEF(CK_ECDH2_DERIVE_PARAMS); +STRUCTDEF(CK_ECMQV_DERIVE_PARAMS); +STRUCTDEF(CK_EDDSA_PARAMS); +STRUCTDEF(CK_GCM_MESSAGE_PARAMS); +STRUCTDEF(CK_GCM_PARAMS); +STRUCTDEF(CK_GCM_WRAP_PARAMS); +STRUCTDEF(CK_GOSTR3410_DERIVE_PARAMS); +STRUCTDEF(CK_GOSTR3410_KEY_WRAP_PARAMS); +STRUCTDEF(CK_HASH_SIGN_ADDITIONAL_CONTEXT); +STRUCTDEF(CK_HKDF_PARAMS); +STRUCTDEF(CK_IKE_PRF_DERIVE_PARAMS); +STRUCTDEF(CK_IKE1_EXTENDED_DERIVE_PARAMS); +STRUCTDEF(CK_IKE1_PRF_DERIVE_PARAMS); +STRUCTDEF(CK_IKE2_PRF_PLUS_DERIVE_PARAMS); +STRUCTDEF(CK_KEA_DERIVE_PARAMS); +STRUCTDEF(CK_KEY_DERIVATION_STRING_DATA); +STRUCTDEF(CK_KEY_WRAP_SET_OAEP_PARAMS); +STRUCTDEF(CK_KIP_PARAMS); +STRUCTDEF(CK_OTP_PARAM); +STRUCTDEF(CK_OTP_PARAMS); +STRUCTDEF(CK_OTP_SIGNATURE_INFO); +STRUCTDEF(CK_PBE_PARAMS); +STRUCTDEF(CK_PKCS5_PBKD2_PARAMS); +STRUCTDEF(CK_PKCS5_PBKD2_PARAMS2); +STRUCTDEF(CK_PRF_DATA_PARAM); +STRUCTDEF(CK_RC2_CBC_PARAMS); +STRUCTDEF(CK_RC2_MAC_GENERAL_PARAMS); +STRUCTDEF(CK_RC5_CBC_PARAMS); +STRUCTDEF(CK_RC5_MAC_GENERAL_PARAMS); +STRUCTDEF(CK_RC5_PARAMS); +STRUCTDEF(CK_RSA_AES_KEY_WRAP_PARAMS); +STRUCTDEF(CK_RSA_PKCS_OAEP_PARAMS); +STRUCTDEF(CK_RSA_PKCS_PSS_PARAMS); +STRUCTDEF(CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS); +STRUCTDEF(CK_SALSA20_CHACHA20_POLY1305_PARAMS); +STRUCTDEF(CK_SALSA20_PARAMS); +STRUCTDEF(CK_SEED_CBC_ENCRYPT_DATA_PARAMS); +STRUCTDEF(CK_SIGN_ADDITIONAL_CONTEXT); +STRUCTDEF(CK_SKIPJACK_PRIVATE_WRAP_PARAMS); +STRUCTDEF(CK_SKIPJACK_RELAYX_PARAMS); +STRUCTDEF(CK_SP800_108_COUNTER_FORMAT); +STRUCTDEF(CK_SP800_108_DKM_LENGTH_FORMAT); +STRUCTDEF(CK_SP800_108_FEEDBACK_KDF_PARAMS); +STRUCTDEF(CK_SP800_108_KDF_PARAMS); +STRUCTDEF(CK_TLS12_EXTENDED_MASTER_KEY_DERIVE_PARAMS); +STRUCTDEF(CK_X2RATCHET_INITIALIZE_PARAMS); +STRUCTDEF(CK_X2RATCHET_RESPOND_PARAMS); +STRUCTDEF(CK_X3DH_INITIATE_PARAMS); +STRUCTDEF(CK_X3DH_RESPOND_PARAMS); +STRUCTDEF(CK_X9_42_DH1_DERIVE_PARAMS); +STRUCTDEF(CK_X9_42_DH2_DERIVE_PARAMS); +STRUCTDEF(CK_X9_42_MQV_DERIVE_PARAMS); +STRUCTDEF(CK_XEDDSA_PARAMS); +STRUCTDEF(specifiedParams); + +struct CK_AES_CBC_ENCRYPT_DATA_PARAMS { + CK_BYTE iv[16]; + CK_BYTE * pData; + CK_ULONG length; +}; + +struct CK_AES_CCM_PARAMS { + CK_ULONG ulDataLen; + CK_BYTE * pNonce; + CK_ULONG ulNonceLen; + CK_BYTE * pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulMACLen; +}; + +struct CK_AES_CTR_PARAMS { + CK_ULONG ulCounterBits; + CK_BYTE cb[16]; +}; + +struct CK_AES_GCM_PARAMS { + CK_BYTE * pIv; + CK_ULONG ulIvLen; + CK_ULONG ulIvBits; + CK_BYTE * pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulTagBits; +}; + +struct CK_ARIA_CBC_ENCRYPT_DATA_PARAMS { + CK_BYTE iv[16]; + CK_BYTE * pData; + CK_ULONG length; +}; + +struct CK_ASYNC_DATA { + CK_ULONG ulVersion; + CK_BYTE * pValue; + CK_ULONG ulValue; + CK_OBJECT_HANDLE hObject; + CK_OBJECT_HANDLE hAdditionalObject; +}; + +struct CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS { + CK_BYTE iv[16]; + CK_BYTE * pData; + CK_ULONG length; +}; + +struct CK_CAMELLIA_CTR_PARAMS { + CK_ULONG ulCounterBits; + CK_BYTE cb[16]; +}; + +struct CK_CCM_MESSAGE_PARAMS { + CK_ULONG ulDataLen; + CK_BYTE * pNonce; + CK_ULONG ulNonceLen; + CK_ULONG ulNonceFixedBits; + CK_GENERATOR_FUNCTION nonceGenerator; + CK_BYTE * pMAC; + CK_ULONG ulMACLen; +}; + +struct CK_CCM_PARAMS { + CK_ULONG ulDataLen; + CK_BYTE * pNonce; + CK_ULONG ulNonceLen; + CK_BYTE * pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulMACLen; +}; +struct CK_CCM_WRAP_PARAMS { + CK_ULONG ulDataLen; + CK_BYTE * pNonce; + CK_ULONG ulNonceLen; + CK_ULONG ulNonceFixedBits; + CK_GENERATOR_FUNCTION nonceGenerator; + CK_BYTE * pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulMACLen; +}; + +struct CK_CHACHA20_PARAMS { + CK_BYTE * pBlockCounter; + CK_ULONG blockCounterBits; + CK_BYTE * pNonce; + CK_ULONG ulNonceBits; +}; + +struct CK_CMS_SIG_PARAMS { + CK_OBJECT_HANDLE certificateHandle; + CK_MECHANISM * pSigningMechanism; + CK_MECHANISM * pDigestMechanism; + CK_UTF8CHAR * pContentType; + CK_BYTE * pRequestedAttributes; + CK_ULONG ulRequestedAttributesLen; + CK_BYTE * pRequiredAttributes; + CK_ULONG ulRequiredAttributesLen; +}; + +struct CK_DES_CBC_ENCRYPT_DATA_PARAMS { + CK_BYTE iv[8]; + CK_BYTE * pData; + CK_ULONG length; +}; + +struct CK_DSA_PARAMETER_GEN_PARAM { + CK_MECHANISM_TYPE hash; + CK_BYTE * pSeed; + CK_ULONG ulSeedLen; + CK_ULONG ulIndex; +}; + +struct CK_ECDH_AES_KEY_WRAP_PARAMS { + CK_ULONG ulAESKeyBits; + CK_EC_KDF_TYPE kdf; + CK_ULONG ulSharedDataLen; + CK_BYTE * pSharedData; +}; + +struct CK_ECDH1_DERIVE_PARAMS { + CK_EC_KDF_TYPE kdf; + CK_ULONG ulSharedDataLen; + CK_BYTE * pSharedData; + CK_ULONG ulPublicDataLen; + CK_BYTE * pPublicData; +}; + +struct CK_ECDH2_DERIVE_PARAMS { + CK_EC_KDF_TYPE kdf; + CK_ULONG ulSharedDataLen; + CK_BYTE * pSharedData; + CK_ULONG ulPublicDataLen; + CK_BYTE * pPublicData; + CK_ULONG ulPrivateDataLen; + CK_OBJECT_HANDLE hPrivateData; + CK_ULONG ulPublicDataLen2; + CK_BYTE * pPublicData2; +}; + +struct CK_ECMQV_DERIVE_PARAMS { + CK_EC_KDF_TYPE kdf; + CK_ULONG ulSharedDataLen; + CK_BYTE * pSharedData; + CK_ULONG ulPublicDataLen; + CK_BYTE * pPublicData; + CK_ULONG ulPrivateDataLen; + CK_OBJECT_HANDLE hPrivateData; + CK_ULONG ulPublicDataLen2; + CK_BYTE * pPublicData2; + CK_OBJECT_HANDLE publicKey; +}; + +struct CK_EDDSA_PARAMS { + CK_BBOOL phFlag; + CK_ULONG ulContextDataLen; + CK_BYTE * pContextData; +}; + +struct CK_GCM_MESSAGE_PARAMS { + CK_BYTE * pIv; + CK_ULONG ulIvLen; + CK_ULONG ulIvFixedBits; + CK_GENERATOR_FUNCTION ivGenerator; + CK_BYTE * pTag; + CK_ULONG ulTagBits; +}; + +struct CK_GCM_PARAMS { + CK_BYTE * pIv; + CK_ULONG ulIvLen; + CK_ULONG ulIvBits; + CK_BYTE * pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulTagBits; +}; + +struct CK_GCM_WRAP_PARAMS { + CK_BYTE * pIv; + CK_ULONG ulIvLen; + CK_ULONG ulIvFixedBits; + CK_GENERATOR_FUNCTION ivGenerator; + CK_BYTE * pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulTagBits; +}; + +struct CK_GOSTR3410_DERIVE_PARAMS { + CK_EC_KDF_TYPE kdf; + CK_BYTE * pPublicData; + CK_ULONG ulPublicDataLen; + CK_BYTE * pUKM; + CK_ULONG ulUKMLen; +}; + +struct CK_GOSTR3410_KEY_WRAP_PARAMS { + CK_BYTE * pWrapOID; + CK_ULONG ulWrapOIDLen; + CK_BYTE * pUKM; + CK_ULONG ulUKMLen; + CK_OBJECT_HANDLE hKey; +}; + +struct CK_HASH_SIGN_ADDITIONAL_CONTEXT { + CK_HEDGE_TYPE hedgeVariant; + CK_BYTE * pContext; + CK_ULONG ulContextLen; + CK_MECHANISM_TYPE hash; +}; + +struct CK_HKDF_PARAMS { + CK_BBOOL bExtract; + CK_BBOOL bExpand; + CK_MECHANISM_TYPE prfHashMechanism; + CK_ULONG ulSaltType; + CK_BYTE * pSalt; + CK_ULONG ulSaltLen; + CK_OBJECT_HANDLE hSaltKey; + CK_BYTE * pInfo; + CK_ULONG ulInfoLen; +}; + +struct CK_IKE_PRF_DERIVE_PARAMS { + CK_MECHANISM_TYPE prfMechanism; + CK_BBOOL bDataAsKey; + CK_BBOOL bRekey; + CK_BYTE * pNi; + CK_ULONG ulNiLen; + CK_BYTE * pNr; + CK_ULONG ulNrLen; + CK_OBJECT_HANDLE hNewKey; +}; + +struct CK_IKE1_EXTENDED_DERIVE_PARAMS { + CK_MECHANISM_TYPE prfMechanism; + CK_BBOOL bHasKeygxy; + CK_OBJECT_HANDLE hKeygxy; + CK_BYTE * pExtraData; + CK_ULONG ulExtraDataLen; +}; + +struct CK_IKE1_PRF_DERIVE_PARAMS { + CK_MECHANISM_TYPE prfMechanism; + CK_BBOOL bHasPrevKey; + CK_OBJECT_HANDLE hKeygxy; + CK_OBJECT_HANDLE hPrevKey; + CK_BYTE * pCKYi; + CK_ULONG ulCKYiLen; + CK_BYTE * pCKYr; + CK_ULONG ulCKYrLen; + CK_BYTE keyNumber; +}; + +struct CK_IKE2_PRF_PLUS_DERIVE_PARAMS { + CK_MECHANISM_TYPE prfMechanism; + CK_BBOOL bHasSeedKey; + CK_OBJECT_HANDLE hSeedKey; + CK_BYTE * pSeedData; + CK_ULONG ulSeedDataLen; +}; + +struct CK_KEA_DERIVE_PARAMS { + CK_BBOOL isSender; + CK_ULONG ulRandomLen; + CK_BYTE * RandomA; + CK_BYTE * RandomB; + CK_ULONG ulPublicDataLen; + CK_BYTE * PublicData; +}; + +struct CK_KEY_DERIVATION_STRING_DATA { + CK_BYTE * pData; + CK_ULONG ulLen; +}; + +struct CK_KEY_WRAP_SET_OAEP_PARAMS { + CK_BYTE bBC; + CK_BYTE * pX; + CK_ULONG ulXLen; +}; + +struct CK_KIP_PARAMS { + CK_MECHANISM * pMechanism; + CK_OBJECT_HANDLE hKey; + CK_BYTE * pSeed; + CK_ULONG ulSeedLen; +}; + +struct CK_OTP_PARAM { + CK_OTP_PARAM_TYPE type; + void * pValue; + CK_ULONG ulValueLen; +}; + +struct CK_OTP_PARAMS { + CK_OTP_PARAM * pParams; + CK_ULONG ulCount; +}; + +struct CK_OTP_SIGNATURE_INFO { + CK_OTP_PARAM * pParams; + CK_ULONG ulCount; +}; + +struct CK_PBE_PARAMS { + CK_BYTE * pInitVector; + CK_UTF8CHAR * pPassword; + CK_ULONG ulPasswordLen; + CK_BYTE * pSalt; + CK_ULONG ulSaltLen; + CK_ULONG ulIteration; +}; + +struct CK_PKCS5_PBKD2_PARAMS { + CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource; + void * pSaltSourceData; + CK_ULONG ulSaltSourceDataLen; + CK_ULONG iterations; + CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf; + void * pPrfData; + CK_ULONG ulPrfDataLen; + CK_UTF8CHAR * pPassword; + CK_ULONG * ulPasswordLen; +}; + +struct CK_PKCS5_PBKD2_PARAMS2 { + CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource; + void * pSaltSourceData; + CK_ULONG ulSaltSourceDataLen; + CK_ULONG iterations; + CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf; + void * pPrfData; + CK_ULONG ulPrfDataLen; + CK_UTF8CHAR * pPassword; + CK_ULONG ulPasswordLen; +}; + +struct CK_PRF_DATA_PARAM { + CK_PRF_DATA_TYPE type; + void * pValue; + CK_ULONG ulValueLen; +}; + +struct CK_RC2_CBC_PARAMS { + CK_ULONG ulEffectiveBits; + CK_BYTE iv[8]; +}; + +struct CK_RC2_MAC_GENERAL_PARAMS { + CK_ULONG ulEffectiveBits; + CK_ULONG ulMacLength; +}; + +struct CK_RC5_CBC_PARAMS { + CK_ULONG ulWordsize; + CK_ULONG ulRounds; + CK_BYTE * pIv; + CK_ULONG ulIvLen; +}; + +struct CK_RC5_MAC_GENERAL_PARAMS { + CK_ULONG ulWordsize; + CK_ULONG ulRounds; + CK_ULONG ulMacLength; +}; + +struct CK_RC5_PARAMS { + CK_ULONG ulWordsize; + CK_ULONG ulRounds; +}; + +struct CK_RSA_AES_KEY_WRAP_PARAMS { + CK_ULONG ulAESKeyBits; + CK_RSA_PKCS_OAEP_PARAMS * pOAEPParams; +}; + +struct CK_RSA_PKCS_OAEP_PARAMS { + CK_MECHANISM_TYPE hashAlg; + CK_RSA_PKCS_MGF_TYPE mgf; + CK_RSA_PKCS_OAEP_SOURCE_TYPE source; + void * pSourceData; + CK_ULONG ulSourceDataLen; +}; + +struct CK_RSA_PKCS_PSS_PARAMS { + CK_MECHANISM_TYPE hashAlg; + CK_RSA_PKCS_MGF_TYPE mgf; + CK_ULONG sLen; +}; + +struct CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS { + CK_BYTE * pNonce; + CK_ULONG ulNonceLen; + CK_BYTE * pTag; +}; + +struct CK_SALSA20_CHACHA20_POLY1305_PARAMS { + CK_BYTE * pNonce; + CK_ULONG ulNonceLen; + CK_BYTE * pAAD; + CK_ULONG ulAADLen; +}; + +struct CK_SALSA20_PARAMS { + CK_BYTE * pBlockCounter; + CK_BYTE * pNonce; + CK_ULONG ulNonceBits; +}; + +struct CK_SEED_CBC_ENCRYPT_DATA_PARAMS { + CK_BYTE iv[16]; + CK_BYTE * pData; + CK_ULONG length; +}; + +struct CK_SIGN_ADDITIONAL_CONTEXT { + CK_HEDGE_TYPE hedgeVariant; + CK_BYTE * pContext; + CK_ULONG ulContextLen; +}; + +struct CK_SKIPJACK_PRIVATE_WRAP_PARAMS { + CK_ULONG ulPasswordLen; + CK_BYTE * pPassword; + CK_ULONG ulPublicDataLen; + CK_BYTE * pPublicData; + CK_ULONG ulPAndGLen; + CK_ULONG ulQLen; + CK_ULONG ulRandomLen; + CK_BYTE * pRandomA; + CK_BYTE * pPrimeP; + CK_BYTE * pBaseG; + CK_BYTE * pSubprimeQ; +}; + +struct CK_SKIPJACK_RELAYX_PARAMS { + CK_ULONG ulOldWrappedXLen; + CK_BYTE * pOldWrappedX; + CK_ULONG ulOldPasswordLen; + CK_BYTE * pOldPassword; + CK_ULONG ulOldPublicDataLen; + CK_BYTE * pOldPublicData; + CK_ULONG ulOldRandomLen; + CK_BYTE * pOldRandomA; + CK_ULONG ulNewPasswordLen; + CK_BYTE * pNewPassword; + CK_ULONG ulNewPublicDataLen; + CK_BYTE * pNewPublicData; + CK_ULONG ulNewRandomLen; + CK_BYTE * pNewRandomA; +}; + +struct CK_SP800_108_COUNTER_FORMAT { + CK_BBOOL bLittleEndian; + CK_ULONG ulWidthInBits; +}; + +struct CK_SP800_108_DKM_LENGTH_FORMAT { + CK_SP800_108_DKM_LENGTH_METHOD dkmLengthMethod; + CK_BBOOL bLittleEndian; + CK_ULONG ulWidthInBits; +}; + +typedef CK_MECHANISM_TYPE CK_SP800_108_PRF_TYPE; + +struct CK_SP800_108_FEEDBACK_KDF_PARAMS +{ + CK_SP800_108_PRF_TYPE prfType; + CK_ULONG ulNumberOfDataParams; + CK_PRF_DATA_PARAM * pDataParams; + CK_ULONG ulIVLen; + CK_BYTE * pIV; + CK_ULONG ulAdditionalDerivedKeys; + CK_DERIVED_KEY * pAdditionalDerivedKeys; +}; + +struct CK_TLS12_EXTENDED_MASTER_KEY_DERIVE_PARAMS { + CK_MECHANISM_TYPE prfHashMechanism; + CK_BYTE * pSessionHash; + CK_ULONG ulSessionHashLen; + CK_VERSION * pVersion; +}; + +struct CK_SP800_108_KDF_PARAMS +{ + CK_SP800_108_PRF_TYPE prfType; + CK_ULONG ulNumberOfDataParams; + CK_PRF_DATA_PARAM * pDataParams; + CK_ULONG ulAdditionalDerivedKeys; + CK_DERIVED_KEY * pAdditionalDerivedKeys; +}; + +struct CK_X2RATCHET_INITIALIZE_PARAMS { + CK_BYTE * sk; + CK_OBJECT_HANDLE peer_public_prekey; + CK_OBJECT_HANDLE peer_public_identity; + CK_OBJECT_HANDLE own_public_identity; + CK_BBOOL bEncryptedHeader; + CK_ULONG eCurve; + CK_MECHANISM_TYPE aeadMechanism; + CK_X2RATCHET_KDF_TYPE kdfMechanism; +}; + +struct CK_X2RATCHET_RESPOND_PARAMS { + CK_BYTE * sk; + CK_OBJECT_HANDLE own_prekey; + CK_OBJECT_HANDLE initiator_identity; + CK_OBJECT_HANDLE own_public_identity; + CK_BBOOL bEncryptedHeader; + CK_ULONG eCurve; + CK_MECHANISM_TYPE aeadMechanism; + CK_X2RATCHET_KDF_TYPE kdfMechanism; +}; + +struct CK_X3DH_INITIATE_PARAMS { + CK_X3DH_KDF_TYPE kdf; + CK_OBJECT_HANDLE pPeer_identity; + CK_OBJECT_HANDLE pPeer_prekey; + CK_BYTE * pPrekey_signature; + CK_BYTE * pOnetime_key; + CK_OBJECT_HANDLE pOwn_identity; + CK_OBJECT_HANDLE pOwn_ephemeral; +}; + +struct CK_X3DH_RESPOND_PARAMS { + CK_X3DH_KDF_TYPE kdf; + CK_BYTE * pIdentity_id; + CK_BYTE * pPrekey_id; + CK_BYTE * pOnetime_id; + CK_OBJECT_HANDLE pInitiator_identity; + CK_BYTE * pInitiator_ephemeral; +}; + +struct CK_X9_42_DH1_DERIVE_PARAMS { + CK_X9_42_DH_KDF_TYPE kdf; + CK_ULONG ulOtherInfoLen; + CK_BYTE * pOtherInfo; + CK_ULONG ulPublicDataLen; + CK_BYTE * pPublicData; +}; + +struct CK_X9_42_DH2_DERIVE_PARAMS { + CK_X9_42_DH_KDF_TYPE kdf; + CK_ULONG ulOtherInfoLen; + CK_BYTE * pOtherInfo; + CK_ULONG ulPublicDataLen; + CK_BYTE * pPublicData; + CK_ULONG ulPrivateDataLen; + CK_OBJECT_HANDLE hPrivateData; + CK_ULONG ulPublicDataLen2; + CK_BYTE * pPublicData2; +}; + +struct CK_X9_42_MQV_DERIVE_PARAMS { + CK_X9_42_DH_KDF_TYPE kdf; + CK_ULONG ulOtherInfoLen; + CK_BYTE * OtherInfo; + CK_ULONG ulPublicDataLen; + CK_BYTE * PublicData; + CK_ULONG ulPrivateDataLen; + CK_OBJECT_HANDLE hPrivateData; + CK_ULONG ulPublicDataLen2; + CK_BYTE * PublicData2; + CK_OBJECT_HANDLE publicKey; +}; + +struct CK_XEDDSA_PARAMS { + CK_XEDDSA_HASH_TYPE hash; +}; + +/* TLS related structure definitions */ +STRUCTDEF(CK_SSL3_KEY_MAT_OUT); +STRUCTDEF(CK_SSL3_KEY_MAT_PARAMS); +STRUCTDEF(CK_SSL3_MASTER_KEY_DERIVE_PARAMS); +STRUCTDEF(CK_SSL3_RANDOM_DATA); +STRUCTDEF(CK_TLS_KDF_PARAMS); +STRUCTDEF(CK_TLS_MAC_PARAMS); +STRUCTDEF(CK_TLS_PRF_PARAMS); +STRUCTDEF(CK_TLS12_KEY_MAT_PARAMS); +STRUCTDEF(CK_TLS12_MASTER_KEY_DERIVE_PARAMS); +STRUCTDEF(CK_WTLS_KEY_MAT_OUT); +STRUCTDEF(CK_WTLS_KEY_MAT_PARAMS); +STRUCTDEF(CK_WTLS_MASTER_KEY_DERIVE_PARAMS); +STRUCTDEF(CK_WTLS_PRF_PARAMS); +STRUCTDEF(CK_WTLS_RANDOM_DATA); + +struct CK_SSL3_KEY_MAT_OUT { + CK_OBJECT_HANDLE hClientMacSecret; + CK_OBJECT_HANDLE hServerMacSecret; + CK_OBJECT_HANDLE hClientKey; + CK_OBJECT_HANDLE hServerKey; + CK_BYTE * pIVClient; + CK_BYTE * pIVServer; +}; + +struct CK_SSL3_RANDOM_DATA { + CK_BYTE * pClientRandom; + CK_ULONG ulClientRandomLen; + CK_BYTE * pServerRandom; + CK_ULONG ulServerRandomLen; +}; + +struct CK_SSL3_KEY_MAT_PARAMS { + CK_ULONG ulMacSizeInBits; + CK_ULONG ulKeySizeInBits; + CK_ULONG ulIVSizeInBits; + CK_BBOOL bIsExport; + CK_SSL3_RANDOM_DATA RandomInfo; + CK_SSL3_KEY_MAT_OUT * pReturnedKeyMaterial; +}; + +struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS { + CK_SSL3_RANDOM_DATA RandomInfo; + CK_VERSION * pVersion; +}; + +struct CK_TLS_KDF_PARAMS { + CK_MECHANISM_TYPE prfMechanism; + CK_BYTE * pLabel; + CK_ULONG ulLabelLength; + CK_SSL3_RANDOM_DATA RandomInfo; + CK_BYTE * pContextData; + CK_ULONG ulContextDataLength; +}; + +struct CK_TLS_MAC_PARAMS { + CK_MECHANISM_TYPE prfHashMechanism; + CK_ULONG ulMacLength; + CK_ULONG ulServerOrClient; +}; + +struct CK_TLS_PRF_PARAMS { + CK_BYTE * pSeed; + CK_ULONG ulSeedLen; + CK_BYTE * pLabel; + CK_ULONG ulLabelLen; + CK_BYTE * pOutput; + CK_ULONG * pulOutputLen; +}; + +struct CK_TLS12_KEY_MAT_PARAMS { + CK_ULONG ulMacSizeInBits; + CK_ULONG ulKeySizeInBits; + CK_ULONG ulIVSizeInBits; + CK_BBOOL bIsExport; + CK_SSL3_RANDOM_DATA RandomInfo; + CK_SSL3_KEY_MAT_OUT * pReturnedKeyMaterial; + CK_MECHANISM_TYPE prfHashMechanism; +}; + +struct CK_TLS12_MASTER_KEY_DERIVE_PARAMS { + CK_SSL3_RANDOM_DATA RandomInfo; + CK_VERSION * pVersion; + CK_MECHANISM_TYPE prfHashMechanism; +}; + +struct CK_WTLS_KEY_MAT_OUT { + CK_OBJECT_HANDLE hMacSecret; + CK_OBJECT_HANDLE hKey; + CK_BYTE * pIV; +}; + +struct CK_WTLS_RANDOM_DATA { + CK_BYTE * pClientRandom; + CK_ULONG ulClientRandomLen; + CK_BYTE * pServerRandom; + CK_ULONG ulServerRandomLen; +}; + +struct CK_WTLS_KEY_MAT_PARAMS { + CK_MECHANISM_TYPE DigestMechanism; + CK_ULONG ulMacSizeInBits; + CK_ULONG ulKeySizeInBits; + CK_ULONG ulIVSizeInBits; + CK_ULONG ulSequenceNumber; + CK_BBOOL bIsExport; + CK_WTLS_RANDOM_DATA RandomInfo; + CK_WTLS_KEY_MAT_OUT * pReturnedKeyMaterial; +}; + +struct CK_WTLS_MASTER_KEY_DERIVE_PARAMS { + CK_MECHANISM_TYPE DigestMechanism; + CK_WTLS_RANDOM_DATA RandomInfo; + CK_BYTE * pVersion; +}; + +struct CK_WTLS_PRF_PARAMS { + CK_MECHANISM_TYPE DigestMechanism; + CK_BYTE * pSeed; + CK_ULONG ulSeedLen; + CK_BYTE * pLabel; + CK_ULONG ulLabelLen; + CK_BYTE * pOutput; + CK_ULONG * pulOutputLen; +}; + +/* PKCS11 Functions */ +extern CK_RV C_Initialize(void *); +extern CK_RV C_Finalize(void *); +extern CK_RV C_GetInfo(CK_INFO *); +extern CK_RV C_GetFunctionList(CK_FUNCTION_LIST **); +extern CK_RV C_GetSlotList(CK_BBOOL, CK_SLOT_ID *, CK_ULONG *); +extern CK_RV C_GetSlotInfo(CK_SLOT_ID, CK_SLOT_INFO *); +extern CK_RV C_GetTokenInfo(CK_SLOT_ID, CK_TOKEN_INFO *); +extern CK_RV C_GetMechanismList(CK_SLOT_ID, CK_MECHANISM_TYPE *, CK_ULONG *); +extern CK_RV C_GetMechanismInfo(CK_SLOT_ID, CK_MECHANISM_TYPE, + CK_MECHANISM_INFO *); +extern CK_RV C_InitToken(CK_SLOT_ID, CK_UTF8CHAR *, CK_ULONG, CK_UTF8CHAR *); +extern CK_RV C_InitPIN(CK_SESSION_HANDLE, CK_UTF8CHAR *, CK_ULONG); +extern CK_RV C_SetPIN(CK_SESSION_HANDLE, CK_UTF8CHAR *, CK_ULONG, CK_UTF8CHAR *, + CK_ULONG); +extern CK_RV C_OpenSession(CK_SLOT_ID, CK_FLAGS, void *, CK_NOTIFY, + CK_SESSION_HANDLE *); +extern CK_RV C_CloseSession(CK_SESSION_HANDLE); +extern CK_RV C_CloseAllSessions(CK_SLOT_ID); +extern CK_RV C_GetSessionInfo(CK_SESSION_HANDLE, CK_SESSION_INFO *); +extern CK_RV C_GetOperationState(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG *); +extern CK_RV C_SetOperationState(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_OBJECT_HANDLE, CK_OBJECT_HANDLE); +extern CK_RV C_Login(CK_SESSION_HANDLE, CK_USER_TYPE, CK_UTF8CHAR *, CK_ULONG); +extern CK_RV C_Logout(CK_SESSION_HANDLE); +extern CK_RV C_CreateObject(CK_SESSION_HANDLE, CK_ATTRIBUTE *, CK_ULONG, + CK_OBJECT_HANDLE *); +extern CK_RV C_CopyObject(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ATTRIBUTE *, + CK_ULONG, CK_OBJECT_HANDLE *); +extern CK_RV C_DestroyObject(CK_SESSION_HANDLE, CK_OBJECT_HANDLE); +extern CK_RV C_GetObjectSize(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ULONG *); +extern CK_RV C_GetAttributeValue(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, + CK_ATTRIBUTE *, CK_ULONG); +extern CK_RV C_SetAttributeValue(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, + CK_ATTRIBUTE *, CK_ULONG); +extern CK_RV C_FindObjectsInit(CK_SESSION_HANDLE, CK_ATTRIBUTE *, CK_ULONG); +extern CK_RV C_FindObjects(CK_SESSION_HANDLE, CK_OBJECT_HANDLE *, CK_ULONG, + CK_ULONG *); +extern CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE); +extern CK_RV C_EncryptInit(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +extern CK_RV C_Encrypt(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG *); +extern CK_RV C_EncryptUpdate(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +extern CK_RV C_EncryptFinal(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG *); +extern CK_RV C_DecryptInit(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +extern CK_RV C_Decrypt(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG *); +extern CK_RV C_DecryptUpdate(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +extern CK_RV C_DecryptFinal(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG *); +extern CK_RV C_DigestInit(CK_SESSION_HANDLE, CK_MECHANISM *); +extern CK_RV C_Digest(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG *); +extern CK_RV C_DigestUpdate(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +extern CK_RV C_DigestKey(CK_SESSION_HANDLE, CK_OBJECT_HANDLE); +extern CK_RV C_DigestFinal(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG *); +extern CK_RV C_SignInit(CK_SESSION_HANDLE, CK_MECHANISM *, CK_OBJECT_HANDLE); +extern CK_RV C_Sign(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG *); +extern CK_RV C_SignUpdate(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +extern CK_RV C_SignFinal(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG *); +extern CK_RV C_SignRecoverInit(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +extern CK_RV C_SignRecover(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG *); +extern CK_RV C_VerifyInit(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +extern CK_RV C_Verify(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG); +extern CK_RV C_VerifyUpdate(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +extern CK_RV C_VerifyFinal(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +extern CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +extern CK_RV C_VerifyRecover(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +extern CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +extern CK_RV C_DecryptDigestUpdate(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +extern CK_RV C_SignEncryptUpdate(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +extern CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +extern CK_RV C_GenerateKey(CK_SESSION_HANDLE, CK_MECHANISM *, CK_ATTRIBUTE *, + CK_ULONG, CK_OBJECT_HANDLE *); +extern CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_ATTRIBUTE *, CK_ULONG, CK_ATTRIBUTE *, + CK_ULONG, CK_OBJECT_HANDLE *, + CK_OBJECT_HANDLE *); +extern CK_RV C_WrapKey(CK_SESSION_HANDLE, CK_MECHANISM *, CK_OBJECT_HANDLE, + CK_OBJECT_HANDLE, CK_BYTE *, CK_ULONG *); +extern CK_RV C_UnwrapKey(CK_SESSION_HANDLE, CK_MECHANISM *, CK_OBJECT_HANDLE, + CK_BYTE *, CK_ULONG, CK_ATTRIBUTE *, CK_ULONG, + CK_OBJECT_HANDLE *); +extern CK_RV C_DeriveKey(CK_SESSION_HANDLE, CK_MECHANISM *, CK_OBJECT_HANDLE, + CK_ATTRIBUTE *, CK_ULONG, CK_OBJECT_HANDLE *); +extern CK_RV C_SeedRandom(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +extern CK_RV C_GenerateRandom(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +extern CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE); +extern CK_RV C_CancelFunction(CK_SESSION_HANDLE); +extern CK_RV C_WaitForSlotEvent(CK_FLAGS, CK_SLOT_ID *, void *); +extern CK_RV C_GetInterfaceList(CK_INTERFACE *, CK_ULONG *); +extern CK_RV C_GetInterface(CK_UTF8CHAR *, CK_VERSION *, CK_INTERFACE **, + CK_FLAGS); +extern CK_RV C_LoginUser(CK_SESSION_HANDLE, CK_USER_TYPE, CK_UTF8CHAR *, + CK_ULONG, CK_UTF8CHAR *, CK_ULONG); +extern CK_RV C_SessionCancel(CK_SESSION_HANDLE, CK_FLAGS); +extern CK_RV C_MessageEncryptInit(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +extern CK_RV C_EncryptMessage(CK_SESSION_HANDLE, void *, CK_ULONG, CK_BYTE *, + CK_ULONG, CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG *); +extern CK_RV C_EncryptMessageBegin(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG); +extern CK_RV C_EncryptMessageNext(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG, CK_BYTE *, CK_ULONG *, + CK_FLAGS); +extern CK_RV C_MessageEncryptFinal(CK_SESSION_HANDLE); +extern CK_RV C_MessageDecryptInit(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +extern CK_RV C_DecryptMessage(CK_SESSION_HANDLE, void *, CK_ULONG, CK_BYTE *, + CK_ULONG, CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG *); +extern CK_RV C_DecryptMessageBegin(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG); +extern CK_RV C_DecryptMessageNext(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG, CK_BYTE *, CK_ULONG *, + CK_FLAGS); +extern CK_RV C_MessageDecryptFinal(CK_SESSION_HANDLE); +extern CK_RV C_MessageSignInit(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +extern CK_RV C_SignMessage(CK_SESSION_HANDLE, void *, CK_ULONG, CK_BYTE *, + CK_ULONG, CK_BYTE *, CK_ULONG *); +extern CK_RV C_SignMessageBegin(CK_SESSION_HANDLE, void *, CK_ULONG); +extern CK_RV C_SignMessageNext(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG, CK_BYTE *, CK_ULONG *); +extern CK_RV C_MessageSignFinal(CK_SESSION_HANDLE); +extern CK_RV C_MessageVerifyInit(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +extern CK_RV C_VerifyMessage(CK_SESSION_HANDLE, void *, CK_ULONG, CK_BYTE *, + CK_ULONG, CK_BYTE *, CK_ULONG); +extern CK_RV C_VerifyMessageBegin(CK_SESSION_HANDLE, void *, CK_ULONG); +extern CK_RV C_VerifyMessageNext(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG, CK_BYTE *, CK_ULONG); +extern CK_RV C_MessageVerifyFinal(CK_SESSION_HANDLE); +extern CK_RV C_EncapsulateKey(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE, CK_ATTRIBUTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *, CK_OBJECT_HANDLE *); +extern CK_RV C_DecapsulateKey(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE, CK_ATTRIBUTE *, CK_ULONG, + CK_BYTE *, CK_ULONG, CK_OBJECT_HANDLE *); +extern CK_RV C_VerifySignatureInit(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE, CK_BYTE *, CK_ULONG); +extern CK_RV C_VerifySignature(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +extern CK_RV C_VerifySignatureUpdate(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +extern CK_RV C_VerifySignatureFinal(CK_SESSION_HANDLE); +extern CK_RV C_GetSessionValidationFlags(CK_SESSION_HANDLE, + CK_SESSION_VALIDATION_FLAGS_TYPE, + CK_FLAGS *); +extern CK_RV C_AsyncComplete(CK_SESSION_HANDLE, CK_UTF8CHAR *, + CK_ASYNC_DATA *); +extern CK_RV C_AsyncGetID(CK_SESSION_HANDLE, CK_UTF8CHAR *, CK_ULONG *); +extern CK_RV C_AsyncJoin(CK_SESSION_HANDLE, CK_UTF8CHAR *, CK_ULONG, CK_BYTE *, + CK_ULONG); +extern CK_RV C_WrapKeyAuthenticated(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE, CK_OBJECT_HANDLE, + CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG *); +extern CK_RV C_UnwrapKeyAuthenticated(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE, CK_BYTE *, + CK_ULONG, CK_ATTRIBUTE *, + CK_ULONG, CK_BYTE *, CK_ULONG, + CK_OBJECT_HANDLE *); + +typedef CK_RV (* CK_C_Initialize)(void *); +typedef CK_RV (* CK_C_Finalize)(void *); +typedef CK_RV (* CK_C_GetInfo)(CK_INFO *); +typedef CK_RV (* CK_C_GetFunctionList)(CK_FUNCTION_LIST **); +typedef CK_RV (* CK_C_GetSlotList)(CK_BBOOL, CK_SLOT_ID *, CK_ULONG *); +typedef CK_RV (* CK_C_GetSlotInfo)(CK_SLOT_ID, CK_SLOT_INFO *); +typedef CK_RV (* CK_C_GetTokenInfo)(CK_SLOT_ID, CK_TOKEN_INFO *); +typedef CK_RV (* CK_C_GetMechanismList)(CK_SLOT_ID, CK_MECHANISM_TYPE *, + CK_ULONG *); +typedef CK_RV (* CK_C_GetMechanismInfo)(CK_SLOT_ID, CK_MECHANISM_TYPE, + CK_MECHANISM_INFO *); +typedef CK_RV (* CK_C_InitToken)(CK_SLOT_ID, CK_UTF8CHAR *, CK_ULONG, + CK_UTF8CHAR *); +typedef CK_RV (* CK_C_InitPIN)(CK_SESSION_HANDLE, CK_UTF8CHAR *, CK_ULONG); +typedef CK_RV (* CK_C_SetPIN)(CK_SESSION_HANDLE, CK_UTF8CHAR *, CK_ULONG, + CK_UTF8CHAR *, CK_ULONG); +typedef CK_RV (* CK_C_OpenSession)(CK_SLOT_ID, CK_FLAGS, void *, CK_NOTIFY, + CK_SESSION_HANDLE *); +typedef CK_RV (* CK_C_CloseSession)(CK_SESSION_HANDLE); +typedef CK_RV (* CK_C_CloseAllSessions)(CK_SLOT_ID); +typedef CK_RV (* CK_C_GetSessionInfo)(CK_SESSION_HANDLE, CK_SESSION_INFO *); +typedef CK_RV (* CK_C_GetOperationState)(CK_SESSION_HANDLE, CK_BYTE *, + CK_ULONG *); +typedef CK_RV (* CK_C_SetOperationState)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_OBJECT_HANDLE, CK_OBJECT_HANDLE); +typedef CK_RV (* CK_C_Login)(CK_SESSION_HANDLE, CK_USER_TYPE, CK_UTF8CHAR *, + CK_ULONG); +typedef CK_RV (* CK_C_Logout)(CK_SESSION_HANDLE); +typedef CK_RV (* CK_C_CreateObject)(CK_SESSION_HANDLE, CK_ATTRIBUTE *, CK_ULONG, + CK_OBJECT_HANDLE *); +typedef CK_RV (* CK_C_CopyObject)(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, + CK_ATTRIBUTE *, CK_ULONG, CK_OBJECT_HANDLE *); +typedef CK_RV (* CK_C_DestroyObject)(CK_SESSION_HANDLE, CK_OBJECT_HANDLE); +typedef CK_RV (* CK_C_GetObjectSize)(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, + CK_ULONG *); +typedef CK_RV (* CK_C_GetAttributeValue)(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, + CK_ATTRIBUTE *, CK_ULONG); +typedef CK_RV (* CK_C_SetAttributeValue)(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, + CK_ATTRIBUTE *, CK_ULONG); +typedef CK_RV (* CK_C_FindObjectsInit)(CK_SESSION_HANDLE, CK_ATTRIBUTE *, + CK_ULONG); +typedef CK_RV (* CK_C_FindObjects)(CK_SESSION_HANDLE, CK_OBJECT_HANDLE *, + CK_ULONG, CK_ULONG *); +typedef CK_RV (* CK_C_FindObjectsFinal)(CK_SESSION_HANDLE); +typedef CK_RV (* CK_C_EncryptInit)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +typedef CK_RV (* CK_C_Encrypt)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_EncryptUpdate)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_EncryptFinal)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_DecryptInit)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +typedef CK_RV (* CK_C_Decrypt)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_DecryptUpdate)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_DecryptFinal)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_DigestInit)(CK_SESSION_HANDLE, CK_MECHANISM *); +typedef CK_RV (* CK_C_Digest)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG *); +typedef CK_RV (* CK_C_DigestUpdate)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +typedef CK_RV (* CK_C_DigestKey)(CK_SESSION_HANDLE, CK_OBJECT_HANDLE); +typedef CK_RV (* CK_C_DigestFinal)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_SignInit)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +typedef CK_RV (* CK_C_Sign)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG *); +typedef CK_RV (* CK_C_SignUpdate)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +typedef CK_RV (* CK_C_SignFinal)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_SignRecoverInit)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +typedef CK_RV (* CK_C_SignRecover)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_VerifyInit)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +typedef CK_RV (* CK_C_Verify)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG); +typedef CK_RV (* CK_C_VerifyUpdate)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +typedef CK_RV (* CK_C_VerifyFinal)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +typedef CK_RV (* CK_C_VerifyRecoverInit)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +typedef CK_RV (* CK_C_VerifyRecover)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_DigestEncryptUpdate)(CK_SESSION_HANDLE, CK_BYTE *, + CK_ULONG, CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_DecryptDigestUpdate)(CK_SESSION_HANDLE, CK_BYTE *, + CK_ULONG, CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_SignEncryptUpdate)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_DecryptVerifyUpdate)(CK_SESSION_HANDLE, CK_BYTE *, + CK_ULONG, CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_GenerateKey)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_ATTRIBUTE *, CK_ULONG, + CK_OBJECT_HANDLE *); +typedef CK_RV (* CK_C_GenerateKeyPair)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_ATTRIBUTE *, CK_ULONG, CK_ATTRIBUTE *, + CK_ULONG, CK_OBJECT_HANDLE *, + CK_OBJECT_HANDLE *); +typedef CK_RV (* CK_C_WrapKey)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE, CK_OBJECT_HANDLE, CK_BYTE *, + CK_ULONG *); +typedef CK_RV (* CK_C_UnwrapKey)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE, CK_BYTE *, CK_ULONG, + CK_ATTRIBUTE *, CK_ULONG, CK_OBJECT_HANDLE *); +typedef CK_RV (* CK_C_DeriveKey)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE, CK_ATTRIBUTE *, CK_ULONG, + CK_OBJECT_HANDLE *); +typedef CK_RV (* CK_C_SeedRandom)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +typedef CK_RV (* CK_C_GenerateRandom)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +typedef CK_RV (* CK_C_GetFunctionStatus)(CK_SESSION_HANDLE); +typedef CK_RV (* CK_C_CancelFunction)(CK_SESSION_HANDLE); +typedef CK_RV (* CK_C_WaitForSlotEvent)(CK_FLAGS, CK_SLOT_ID *, void *); +typedef CK_RV (* CK_C_GetInterfaceList)(CK_INTERFACE *, CK_ULONG *); +typedef CK_RV (* CK_C_GetInterface)(CK_UTF8CHAR *, CK_VERSION *, + CK_INTERFACE **, CK_FLAGS); +typedef CK_RV (* CK_C_LoginUser)(CK_SESSION_HANDLE, CK_USER_TYPE, CK_UTF8CHAR *, + CK_ULONG, CK_UTF8CHAR *, CK_ULONG); +typedef CK_RV (* CK_C_SessionCancel)(CK_SESSION_HANDLE, CK_FLAGS); +typedef CK_RV (* CK_C_MessageEncryptInit)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +typedef CK_RV (* CK_C_EncryptMessage)(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_EncryptMessageBegin)(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG); +typedef CK_RV (* CK_C_EncryptMessageNext)(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG *, CK_FLAGS); +typedef CK_RV (* CK_C_MessageEncryptFinal)(CK_SESSION_HANDLE); +typedef CK_RV (* CK_C_MessageDecryptInit)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +typedef CK_RV (* CK_C_DecryptMessage)(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG, CK_BYTE *, CK_ULONG, + CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_DecryptMessageBegin)(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG); +typedef CK_RV (* CK_C_DecryptMessageNext)(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG *, CK_FLAGS); +typedef CK_RV (* CK_C_MessageDecryptFinal)(CK_SESSION_HANDLE); +typedef CK_RV (* CK_C_MessageSignInit)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +typedef CK_RV (* CK_C_SignMessage)(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG, CK_BYTE *, CK_ULONG *); +typedef CK_RV (* CK_C_SignMessageBegin)(CK_SESSION_HANDLE, void *, CK_ULONG); +typedef CK_RV (* CK_C_SignMessageNext)(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG *); +typedef CK_RV (* CK_C_MessageSignFinal)(CK_SESSION_HANDLE); +typedef CK_RV (* CK_C_MessageVerifyInit)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE); +typedef CK_RV (* CK_C_VerifyMessage)(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG, CK_BYTE *, CK_ULONG); +typedef CK_RV (* CK_C_VerifyMessageBegin)(CK_SESSION_HANDLE, void *, CK_ULONG); +typedef CK_RV (* CK_C_VerifyMessageNext)(CK_SESSION_HANDLE, void *, CK_ULONG, + CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG); +typedef CK_RV (* CK_C_MessageVerifyFinal)(CK_SESSION_HANDLE); +typedef CK_RV (* CK_C_EncapsulateKey)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE, CK_ATTRIBUTE *, + CK_ULONG, CK_BYTE *, CK_ULONG *, + CK_OBJECT_HANDLE *); +typedef CK_RV (* CK_C_DecapsulateKey)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE, CK_ATTRIBUTE *, + CK_ULONG, CK_BYTE *, CK_ULONG, + CK_OBJECT_HANDLE *); +typedef CK_RV (* CK_C_VerifySignatureInit)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE, CK_BYTE *, + CK_ULONG); +typedef CK_RV (* CK_C_VerifySignature)(CK_SESSION_HANDLE, CK_BYTE *, CK_ULONG); +typedef CK_RV (* CK_C_VerifySignatureUpdate)(CK_SESSION_HANDLE, CK_BYTE *, + CK_ULONG); +typedef CK_RV (* CK_C_VerifySignatureFinal)(CK_SESSION_HANDLE); +typedef CK_RV (* CK_C_GetSessionValidationFlags)(CK_SESSION_HANDLE, + CK_SESSION_VALIDATION_FLAGS_TYPE, + CK_FLAGS *); +typedef CK_RV (* CK_C_AsyncComplete)(CK_SESSION_HANDLE, CK_UTF8CHAR *, + CK_ASYNC_DATA *); +typedef CK_RV (* CK_C_AsyncGetID)(CK_SESSION_HANDLE, CK_UTF8CHAR *, CK_ULONG *); +typedef CK_RV (* CK_C_AsyncJoin)(CK_SESSION_HANDLE, CK_UTF8CHAR *, CK_ULONG, + CK_BYTE *, CK_ULONG); +typedef CK_RV (* CK_C_WrapKeyAuthenticated)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE, CK_OBJECT_HANDLE, + CK_BYTE *, CK_ULONG, CK_BYTE *, + CK_ULONG *); +typedef CK_RV (* CK_C_UnwrapKeyAuthenticated)(CK_SESSION_HANDLE, CK_MECHANISM *, + CK_OBJECT_HANDLE, CK_BYTE *, + CK_ULONG, CK_ATTRIBUTE *, + CK_ULONG, CK_BYTE *, CK_ULONG, + CK_OBJECT_HANDLE *); + +struct CK_FUNCTION_LIST_3_2 { + CK_VERSION version; + CK_C_Initialize C_Initialize; + CK_C_Finalize C_Finalize; + CK_C_GetInfo C_GetInfo; + CK_C_GetFunctionList C_GetFunctionList; + CK_C_GetSlotList C_GetSlotList; + CK_C_GetSlotInfo C_GetSlotInfo; + CK_C_GetTokenInfo C_GetTokenInfo; + CK_C_GetMechanismList C_GetMechanismList; + CK_C_GetMechanismInfo C_GetMechanismInfo; + CK_C_InitToken C_InitToken; + CK_C_InitPIN C_InitPIN; + CK_C_SetPIN C_SetPIN; + CK_C_OpenSession C_OpenSession; + CK_C_CloseSession C_CloseSession; + CK_C_CloseAllSessions C_CloseAllSessions; + CK_C_GetSessionInfo C_GetSessionInfo; + CK_C_GetOperationState C_GetOperationState; + CK_C_SetOperationState C_SetOperationState; + CK_C_Login C_Login; + CK_C_Logout C_Logout; + CK_C_CreateObject C_CreateObject; + CK_C_CopyObject C_CopyObject; + CK_C_DestroyObject C_DestroyObject; + CK_C_GetObjectSize C_GetObjectSize; + CK_C_GetAttributeValue C_GetAttributeValue; + CK_C_SetAttributeValue C_SetAttributeValue; + CK_C_FindObjectsInit C_FindObjectsInit; + CK_C_FindObjects C_FindObjects; + CK_C_FindObjectsFinal C_FindObjectsFinal; + CK_C_EncryptInit C_EncryptInit; + CK_C_Encrypt C_Encrypt; + CK_C_EncryptUpdate C_EncryptUpdate; + CK_C_EncryptFinal C_EncryptFinal; + CK_C_DecryptInit C_DecryptInit; + CK_C_Decrypt C_Decrypt; + CK_C_DecryptUpdate C_DecryptUpdate; + CK_C_DecryptFinal C_DecryptFinal; + CK_C_DigestInit C_DigestInit; + CK_C_Digest C_Digest; + CK_C_DigestUpdate C_DigestUpdate; + CK_C_DigestKey C_DigestKey; + CK_C_DigestFinal C_DigestFinal; + CK_C_SignInit C_SignInit; + CK_C_Sign C_Sign; + CK_C_SignUpdate C_SignUpdate; + CK_C_SignFinal C_SignFinal; + CK_C_SignRecoverInit C_SignRecoverInit; + CK_C_SignRecover C_SignRecover; + CK_C_VerifyInit C_VerifyInit; + CK_C_Verify C_Verify; + CK_C_VerifyUpdate C_VerifyUpdate; + CK_C_VerifyFinal C_VerifyFinal; + CK_C_VerifyRecoverInit C_VerifyRecoverInit; + CK_C_VerifyRecover C_VerifyRecover; + CK_C_DigestEncryptUpdate C_DigestEncryptUpdate; + CK_C_DecryptDigestUpdate C_DecryptDigestUpdate; + CK_C_SignEncryptUpdate C_SignEncryptUpdate; + CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate; + CK_C_GenerateKey C_GenerateKey; + CK_C_GenerateKeyPair C_GenerateKeyPair; + CK_C_WrapKey C_WrapKey; + CK_C_UnwrapKey C_UnwrapKey; + CK_C_DeriveKey C_DeriveKey; + CK_C_SeedRandom C_SeedRandom; + CK_C_GenerateRandom C_GenerateRandom; + CK_C_GetFunctionStatus C_GetFunctionStatus; + CK_C_CancelFunction C_CancelFunction; + CK_C_WaitForSlotEvent C_WaitForSlotEvent; + CK_C_GetInterfaceList C_GetInterfaceList; + CK_C_GetInterface C_GetInterface; + CK_C_LoginUser C_LoginUser; + CK_C_SessionCancel C_SessionCancel; + CK_C_MessageEncryptInit C_MessageEncryptInit; + CK_C_EncryptMessage C_EncryptMessage; + CK_C_EncryptMessageBegin C_EncryptMessageBegin; + CK_C_EncryptMessageNext C_EncryptMessageNext; + CK_C_MessageEncryptFinal C_MessageEncryptFinal; + CK_C_MessageDecryptInit C_MessageDecryptInit; + CK_C_DecryptMessage C_DecryptMessage; + CK_C_DecryptMessageBegin C_DecryptMessageBegin; + CK_C_DecryptMessageNext C_DecryptMessageNext; + CK_C_MessageDecryptFinal C_MessageDecryptFinal; + CK_C_MessageSignInit C_MessageSignInit; + CK_C_SignMessage C_SignMessage; + CK_C_SignMessageBegin C_SignMessageBegin; + CK_C_SignMessageNext C_SignMessageNext; + CK_C_MessageSignFinal C_MessageSignFinal; + CK_C_MessageVerifyInit C_MessageVerifyInit; + CK_C_VerifyMessage C_VerifyMessage; + CK_C_VerifyMessageBegin C_VerifyMessageBegin; + CK_C_VerifyMessageNext C_VerifyMessageNext; + CK_C_MessageVerifyFinal C_MessageVerifyFinal; + CK_C_EncapsulateKey C_EncapsulateKey; + CK_C_DecapsulateKey C_DecapsulateKey; + CK_C_VerifySignatureInit C_VerifySignatureInit; + CK_C_VerifySignature C_VerifySignature; + CK_C_VerifySignatureUpdate C_VerifySignatureUpdate; + CK_C_VerifySignatureFinal C_VerifySignatureFinal; + CK_C_GetSessionValidationFlags C_GetSessionValidationFlags; + CK_C_AsyncComplete C_AsyncComplete; + CK_C_AsyncGetID C_AsyncGetID; + CK_C_AsyncJoin C_AsyncJoin; + CK_C_WrapKeyAuthenticated C_WrapKeyAuthenticated ; + CK_C_UnwrapKeyAuthenticated C_UnwrapKeyAuthenticated ; +}; + +struct CK_FUNCTION_LIST_3_0 { + CK_VERSION version; + CK_C_Initialize C_Initialize; + CK_C_Finalize C_Finalize; + CK_C_GetInfo C_GetInfo; + CK_C_GetFunctionList C_GetFunctionList; + CK_C_GetSlotList C_GetSlotList; + CK_C_GetSlotInfo C_GetSlotInfo; + CK_C_GetTokenInfo C_GetTokenInfo; + CK_C_GetMechanismList C_GetMechanismList; + CK_C_GetMechanismInfo C_GetMechanismInfo; + CK_C_InitToken C_InitToken; + CK_C_InitPIN C_InitPIN; + CK_C_SetPIN C_SetPIN; + CK_C_OpenSession C_OpenSession; + CK_C_CloseSession C_CloseSession; + CK_C_CloseAllSessions C_CloseAllSessions; + CK_C_GetSessionInfo C_GetSessionInfo; + CK_C_GetOperationState C_GetOperationState; + CK_C_SetOperationState C_SetOperationState; + CK_C_Login C_Login; + CK_C_Logout C_Logout; + CK_C_CreateObject C_CreateObject; + CK_C_CopyObject C_CopyObject; + CK_C_DestroyObject C_DestroyObject; + CK_C_GetObjectSize C_GetObjectSize; + CK_C_GetAttributeValue C_GetAttributeValue; + CK_C_SetAttributeValue C_SetAttributeValue; + CK_C_FindObjectsInit C_FindObjectsInit; + CK_C_FindObjects C_FindObjects; + CK_C_FindObjectsFinal C_FindObjectsFinal; + CK_C_EncryptInit C_EncryptInit; + CK_C_Encrypt C_Encrypt; + CK_C_EncryptUpdate C_EncryptUpdate; + CK_C_EncryptFinal C_EncryptFinal; + CK_C_DecryptInit C_DecryptInit; + CK_C_Decrypt C_Decrypt; + CK_C_DecryptUpdate C_DecryptUpdate; + CK_C_DecryptFinal C_DecryptFinal; + CK_C_DigestInit C_DigestInit; + CK_C_Digest C_Digest; + CK_C_DigestUpdate C_DigestUpdate; + CK_C_DigestKey C_DigestKey; + CK_C_DigestFinal C_DigestFinal; + CK_C_SignInit C_SignInit; + CK_C_Sign C_Sign; + CK_C_SignUpdate C_SignUpdate; + CK_C_SignFinal C_SignFinal; + CK_C_SignRecoverInit C_SignRecoverInit; + CK_C_SignRecover C_SignRecover; + CK_C_VerifyInit C_VerifyInit; + CK_C_Verify C_Verify; + CK_C_VerifyUpdate C_VerifyUpdate; + CK_C_VerifyFinal C_VerifyFinal; + CK_C_VerifyRecoverInit C_VerifyRecoverInit; + CK_C_VerifyRecover C_VerifyRecover; + CK_C_DigestEncryptUpdate C_DigestEncryptUpdate; + CK_C_DecryptDigestUpdate C_DecryptDigestUpdate; + CK_C_SignEncryptUpdate C_SignEncryptUpdate; + CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate; + CK_C_GenerateKey C_GenerateKey; + CK_C_GenerateKeyPair C_GenerateKeyPair; + CK_C_WrapKey C_WrapKey; + CK_C_UnwrapKey C_UnwrapKey; + CK_C_DeriveKey C_DeriveKey; + CK_C_SeedRandom C_SeedRandom; + CK_C_GenerateRandom C_GenerateRandom; + CK_C_GetFunctionStatus C_GetFunctionStatus; + CK_C_CancelFunction C_CancelFunction; + CK_C_WaitForSlotEvent C_WaitForSlotEvent; + CK_C_GetInterfaceList C_GetInterfaceList; + CK_C_GetInterface C_GetInterface; + CK_C_LoginUser C_LoginUser; + CK_C_SessionCancel C_SessionCancel; + CK_C_MessageEncryptInit C_MessageEncryptInit; + CK_C_EncryptMessage C_EncryptMessage; + CK_C_EncryptMessageBegin C_EncryptMessageBegin; + CK_C_EncryptMessageNext C_EncryptMessageNext; + CK_C_MessageEncryptFinal C_MessageEncryptFinal; + CK_C_MessageDecryptInit C_MessageDecryptInit; + CK_C_DecryptMessage C_DecryptMessage; + CK_C_DecryptMessageBegin C_DecryptMessageBegin; + CK_C_DecryptMessageNext C_DecryptMessageNext; + CK_C_MessageDecryptFinal C_MessageDecryptFinal; + CK_C_MessageSignInit C_MessageSignInit; + CK_C_SignMessage C_SignMessage; + CK_C_SignMessageBegin C_SignMessageBegin; + CK_C_SignMessageNext C_SignMessageNext; + CK_C_MessageSignFinal C_MessageSignFinal; + CK_C_MessageVerifyInit C_MessageVerifyInit; + CK_C_VerifyMessage C_VerifyMessage; + CK_C_VerifyMessageBegin C_VerifyMessageBegin; + CK_C_VerifyMessageNext C_VerifyMessageNext; + CK_C_MessageVerifyFinal C_MessageVerifyFinal; +}; + +struct CK_FUNCTION_LIST { + CK_VERSION version; + CK_C_Initialize C_Initialize; + CK_C_Finalize C_Finalize; + CK_C_GetInfo C_GetInfo; + CK_C_GetFunctionList C_GetFunctionList; + CK_C_GetSlotList C_GetSlotList; + CK_C_GetSlotInfo C_GetSlotInfo; + CK_C_GetTokenInfo C_GetTokenInfo; + CK_C_GetMechanismList C_GetMechanismList; + CK_C_GetMechanismInfo C_GetMechanismInfo; + CK_C_InitToken C_InitToken; + CK_C_InitPIN C_InitPIN; + CK_C_SetPIN C_SetPIN; + CK_C_OpenSession C_OpenSession; + CK_C_CloseSession C_CloseSession; + CK_C_CloseAllSessions C_CloseAllSessions; + CK_C_GetSessionInfo C_GetSessionInfo; + CK_C_GetOperationState C_GetOperationState; + CK_C_SetOperationState C_SetOperationState; + CK_C_Login C_Login; + CK_C_Logout C_Logout; + CK_C_CreateObject C_CreateObject; + CK_C_CopyObject C_CopyObject; + CK_C_DestroyObject C_DestroyObject; + CK_C_GetObjectSize C_GetObjectSize; + CK_C_GetAttributeValue C_GetAttributeValue; + CK_C_SetAttributeValue C_SetAttributeValue; + CK_C_FindObjectsInit C_FindObjectsInit; + CK_C_FindObjects C_FindObjects; + CK_C_FindObjectsFinal C_FindObjectsFinal; + CK_C_EncryptInit C_EncryptInit; + CK_C_Encrypt C_Encrypt; + CK_C_EncryptUpdate C_EncryptUpdate; + CK_C_EncryptFinal C_EncryptFinal; + CK_C_DecryptInit C_DecryptInit; + CK_C_Decrypt C_Decrypt; + CK_C_DecryptUpdate C_DecryptUpdate; + CK_C_DecryptFinal C_DecryptFinal; + CK_C_DigestInit C_DigestInit; + CK_C_Digest C_Digest; + CK_C_DigestUpdate C_DigestUpdate; + CK_C_DigestKey C_DigestKey; + CK_C_DigestFinal C_DigestFinal; + CK_C_SignInit C_SignInit; + CK_C_Sign C_Sign; + CK_C_SignUpdate C_SignUpdate; + CK_C_SignFinal C_SignFinal; + CK_C_SignRecoverInit C_SignRecoverInit; + CK_C_SignRecover C_SignRecover; + CK_C_VerifyInit C_VerifyInit; + CK_C_Verify C_Verify; + CK_C_VerifyUpdate C_VerifyUpdate; + CK_C_VerifyFinal C_VerifyFinal; + CK_C_VerifyRecoverInit C_VerifyRecoverInit; + CK_C_VerifyRecover C_VerifyRecover; + CK_C_DigestEncryptUpdate C_DigestEncryptUpdate; + CK_C_DecryptDigestUpdate C_DecryptDigestUpdate; + CK_C_SignEncryptUpdate C_SignEncryptUpdate; + CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate; + CK_C_GenerateKey C_GenerateKey; + CK_C_GenerateKeyPair C_GenerateKeyPair; + CK_C_WrapKey C_WrapKey; + CK_C_UnwrapKey C_UnwrapKey; + CK_C_DeriveKey C_DeriveKey; + CK_C_SeedRandom C_SeedRandom; + CK_C_GenerateRandom C_GenerateRandom; + CK_C_GetFunctionStatus C_GetFunctionStatus; + CK_C_CancelFunction C_CancelFunction; + CK_C_WaitForSlotEvent C_WaitForSlotEvent; +}; + + +// clang-format on +#endif diff --git a/src/pkcs11/pkcs11-display.c b/src/pkcs11/pkcs11-display.c index a05a7f9376..62f45d8bce 100644 --- a/src/pkcs11/pkcs11-display.c +++ b/src/pkcs11/pkcs11-display.c @@ -13,7 +13,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA */ @@ -29,63 +29,64 @@ #include "pkcs11-display.h" -/* Some Netscape/Mozilla-specific stuff: - * http://www.opensource.apple.com/darwinsource/10.3/SecurityNssAsn1-11/nssDER/Source/pkcs11n.h */ +#include "common/compat_strlcat.h" + +/* NSS-specific stuff: + * https://github.com/nss-dev/nss/blob/d86f709bb4974d10b343c746b6f89dd1ef80259b/lib/util/pkcs11n.h + */ /* - * Netscape-defined object classes - * + * NSS-defined object classes */ -#define CKO_NETSCAPE 0xCE534350 +#define CKO_NSS 0xCE534350 -#define CKO_NETSCAPE_CRL (CKO_NETSCAPE + 1) -#define CKO_NETSCAPE_SMIME (CKO_NETSCAPE + 2) -#define CKO_NETSCAPE_TRUST (CKO_NETSCAPE + 3) -#define CKO_NETSCAPE_BUILTIN_ROOT_LIST (CKO_NETSCAPE + 4) +#define CKO_NSS_CRL (CKO_NSS + 1) +#define CKO_NSS_SMIME (CKO_NSS + 2) +#define CKO_NSS_TRUST (CKO_NSS + 3) +#define CKO_NSS_BUILTIN_ROOT_LIST (CKO_NSS + 4) /* - * Netscape-defined object attributes - * + * NSS-defined object attributes */ -#define CKA_NETSCAPE 0xCE534350 +#define CKA_NSS 0xCE534350 -#define CKA_NETSCAPE_URL (CKA_NETSCAPE + 1) -#define CKA_NETSCAPE_EMAIL (CKA_NETSCAPE + 2) -#define CKA_NETSCAPE_SMIME_INFO (CKA_NETSCAPE + 3) -#define CKA_NETSCAPE_SMIME_TIMESTAMP (CKA_NETSCAPE + 4) -#define CKA_NETSCAPE_PKCS8_SALT (CKA_NETSCAPE + 5) -#define CKA_NETSCAPE_PASSWORD_CHECK (CKA_NETSCAPE + 6) -#define CKA_NETSCAPE_EXPIRES (CKA_NETSCAPE + 7) -#define CKA_NETSCAPE_KRL (CKA_NETSCAPE + 8) +#define CKA_NSS_URL (CKA_NSS + 1) +#define CKA_NSS_EMAIL (CKA_NSS + 2) +#define CKA_NSS_SMIME_INFO (CKA_NSS + 3) +#define CKA_NSS_SMIME_TIMESTAMP (CKA_NSS + 4) +#define CKA_NSS_PKCS8_SALT (CKA_NSS + 5) +#define CKA_NSS_PASSWORD_CHECK (CKA_NSS + 6) +#define CKA_NSS_EXPIRES (CKA_NSS + 7) +#define CKA_NSS_KRL (CKA_NSS + 8) -#define CKA_NETSCAPE_PQG_COUNTER (CKA_NETSCAPE + 20) -#define CKA_NETSCAPE_PQG_SEED (CKA_NETSCAPE + 21) -#define CKA_NETSCAPE_PQG_H (CKA_NETSCAPE + 22) -#define CKA_NETSCAPE_PQG_SEED_BITS (CKA_NETSCAPE + 23) +#define CKA_NSS_PQG_COUNTER (CKA_NSS + 20) +#define CKA_NSS_PQG_SEED (CKA_NSS + 21) +#define CKA_NSS_PQG_H (CKA_NSS + 22) +#define CKA_NSS_PQG_SEED_BITS (CKA_NSS + 23) -#define CKA_TRUST (CKA_NETSCAPE + 0x2000) +#define CKA_NSS_TRUST_BASE (CKA_NSS + 0x2000) /* "Usage" key information */ -#define CKA_TRUST_DIGITAL_SIGNATURE (CKA_TRUST + 1) -#define CKA_TRUST_NON_REPUDIATION (CKA_TRUST + 2) -#define CKA_TRUST_KEY_ENCIPHERMENT (CKA_TRUST + 3) -#define CKA_TRUST_DATA_ENCIPHERMENT (CKA_TRUST + 4) -#define CKA_TRUST_KEY_AGREEMENT (CKA_TRUST + 5) -#define CKA_TRUST_KEY_CERT_SIGN (CKA_TRUST + 6) -#define CKA_TRUST_CRL_SIGN (CKA_TRUST + 7) +#define CKA_NSS_TRUST_DIGITAL_SIGNATURE (CKA_NSS_TRUST_BASE + 1) +#define CKA_NSS_TRUST_NON_REPUDIATION (CKA_NSS_TRUST_BASE + 2) +#define CKA_NSS_TRUST_KEY_ENCIPHERMENT (CKA_NSS_TRUST_BASE + 3) +#define CKA_NSS_TRUST_DATA_ENCIPHERMENT (CKA_NSS_TRUST_BASE + 4) +#define CKA_NSS_TRUST_KEY_AGREEMENT (CKA_NSS_TRUST_BASE + 5) +#define CKA_NSS_TRUST_KEY_CERT_SIGN (CKA_NSS_TRUST_BASE + 6) +#define CKA_NSS_TRUST_CRL_SIGN (CKA_NSS_TRUST_BASE + 7) /* "Purpose" trust information */ -#define CKA_TRUST_SERVER_AUTH (CKA_TRUST + 8) -#define CKA_TRUST_CLIENT_AUTH (CKA_TRUST + 9) -#define CKA_TRUST_CODE_SIGNING (CKA_TRUST + 10) -#define CKA_TRUST_EMAIL_PROTECTION (CKA_TRUST + 11) -#define CKA_TRUST_IPSEC_END_SYSTEM (CKA_TRUST + 12) -#define CKA_TRUST_IPSEC_TUNNEL (CKA_TRUST + 13) -#define CKA_TRUST_IPSEC_USER (CKA_TRUST + 14) -#define CKA_TRUST_TIME_STAMPING (CKA_TRUST + 15) -#define CKA_CERT_SHA1_HASH (CKA_TRUST + 100) -#define CKA_CERT_MD5_HASH (CKA_TRUST + 101) +#define CKA_NSS_TRUST_SERVER_AUTH (CKA_NSS_TRUST_BASE + 8) +#define CKA_NSS_TRUST_CLIENT_AUTH (CKA_NSS_TRUST_BASE + 9) +#define CKA_NSS_TRUST_CODE_SIGNING (CKA_NSS_TRUST_BASE + 10) +#define CKA_NSS_TRUST_EMAIL_PROTECTION (CKA_NSS_TRUST_BASE + 11) +#define CKA_NSS_TRUST_IPSEC_END_SYSTEM (CKA_NSS_TRUST_BASE + 12) +#define CKA_NSS_TRUST_IPSEC_TUNNEL (CKA_NSS_TRUST_BASE + 13) +#define CKA_NSS_TRUST_IPSEC_USER (CKA_NSS_TRUST_BASE + 14) +#define CKA_NSS_TRUST_TIME_STAMPING (CKA_NSS_TRUST_BASE + 15) +#define CKA_NSS_CERT_SHA1_HASH (CKA_NSS_TRUST_BASE + 100) +#define CKA_NSS_CERT_MD5_HASH (CKA_NSS_TRUST_BASE + 101) static char * @@ -96,7 +97,7 @@ buf_spec(CK_VOID_PTR buf_addr, CK_ULONG buf_len) #if !defined(_MSC_VER) || _MSC_VER >= 1800 const size_t prwidth = sizeof(CK_VOID_PTR) * 2; - sprintf(ret, "%0*"PRIxPTR" / %lu", (int) prwidth, (uintptr_t) buf_addr, + sprintf(ret, "%0*"PRIxPTR" / %ld", (int) prwidth, (uintptr_t) buf_addr, buf_len); #else if (sizeof(CK_VOID_PTR) == 4) @@ -109,9 +110,8 @@ buf_spec(CK_VOID_PTR buf_addr, CK_ULONG buf_len) return ret; } - void -print_enum(FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg) +print_enum(FILE *f, long type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg, CK_KEY_TYPE key_type) { enum_spec *spec = (enum_spec*)arg; CK_ULONG i; @@ -126,22 +126,21 @@ print_enum(FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR fprintf(f, "Value %lX not found for type %s\n", ctype, spec->name); } - void -print_boolean(FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg) +print_boolean(FILE *f, long type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg, CK_KEY_TYPE key_type) { CK_BYTE i = *((CK_BYTE *)value); fprintf(f, i ? "True\n" : "False\n"); } - void -print_generic(FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg) +print_generic(FILE *f, long type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg, CK_KEY_TYPE key_type) { CK_ULONG i; - if((CK_LONG)size != -1 && value != NULL) { - char hex[16*3+1], ascii[16+1]; + if((long)size != -1 && value != NULL) { + char hex[16*3+1] = {0}; + char ascii[16+1]; char *hex_ptr = hex, *ascii_ptr = ascii; int offset = 0; @@ -171,9 +170,9 @@ print_generic(FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_P ascii_ptr++; } - /* padd */ + /* padding */ while (strlen(hex) < 3*16) - strcat(hex, " "); + strlcat(hex, " ", sizeof(hex)); fprintf(f, "\n %08X %s %s", offset, hex, ascii); } else { @@ -185,12 +184,11 @@ print_generic(FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_P fprintf(f, "\n"); } - #ifdef ENABLE_OPENSSL static void -print_dn(FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg) +print_dn(FILE *f, long type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg, CK_KEY_TYPE key_type) { - print_generic(f, type, value, size, arg); + print_generic(f, type, value, size, arg, key_type); if(size && value) { X509_NAME *name; const unsigned char *tmp = value; @@ -200,7 +198,7 @@ print_dn(FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR ar BIO *bio = BIO_new(BIO_s_file()); BIO_set_fp(bio, f, 0); fprintf(f, " DN: "); - X509_NAME_print(bio, name, XN_FLAG_RFC2253); + X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253); fprintf(f, "\n"); BIO_free(bio); } @@ -208,13 +206,72 @@ print_dn(FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR ar } #endif +static enum_specs ck_mldsa_s[] = { + {CKP_ML_DSA_44, "CKP_ML_DSA_44"}, + {CKP_ML_DSA_65, "CKP_ML_DSA_65"}, + {CKP_ML_DSA_87, "CKP_ML_DSA_87"}, +}; + +static enum_specs ck_mlkem_s[] = { + {CKP_ML_KEM_512, "CKP_ML_KEM_512"}, + {CKP_ML_KEM_768, "CKP_ML_KEM_768"}, + {CKP_ML_KEM_1024, "CKP_ML_KEM_1024"}, +}; + +static enum_specs ck_slh_dsa_s[] = { + {CKP_SLH_DSA_SHA2_128S, "CKP_SLH_DSA_SHA2_128S"}, + {CKP_SLH_DSA_SHAKE_128S, "CKP_SLH_DSA_SHAKE_128S"}, + {CKP_SLH_DSA_SHA2_128F, "CKP_SLH_DSA_SHA2_128F"}, + {CKP_SLH_DSA_SHAKE_128F, "CKP_SLH_DSA_SHAKE_128F"}, + {CKP_SLH_DSA_SHA2_192S, "CKP_SLH_DSA_SHA2_192S"}, + {CKP_SLH_DSA_SHAKE_192S, "CKP_SLH_DSA_SHAKE_192S"}, + {CKP_SLH_DSA_SHA2_192F, "CKP_SLH_DSA_SHA2_192F"}, + {CKP_SLH_DSA_SHAKE_192F, "CKP_SLH_DSA_SHAKE_192F"}, + {CKP_SLH_DSA_SHA2_256S, "CKP_SLH_DSA_SHA2_256S"}, + {CKP_SLH_DSA_SHAKE_256S, "CKP_SLH_DSA_SHAKE_256S"}, + {CKP_SLH_DSA_SHA2_256F, "CKP_SLH_DSA_SHA2_256F"}, + {CKP_SLH_DSA_SHAKE_256F, "CKP_SLH_DSA_SHAKE_256F"}, +}; + +#define SZ_SPECS sizeof(enum_specs) + +static enum_spec ck_ml_dsa_t[] = { + {ML_DSA_T, ck_mldsa_s, sizeof(ck_mldsa_s) / SZ_SPECS, "CK_ML_DSA_PARAMETER_SET_TYPE"}, +}; +static enum_spec ck_ml_kem_t[] = { + {ML_KEM_T, ck_mlkem_s, sizeof(ck_mlkem_s) / SZ_SPECS, "CK_ML_KEM_PARAMETER_SET_TYPE"}, +}; +static enum_spec ck_slh_dsa_t[] = { + {SLH_DSA_T, ck_slh_dsa_s, sizeof(ck_slh_dsa_s) / SZ_SPECS, "CK_SLH_DSA_PARAMETER_SET_TYPE"}, +}; + void -print_print(FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg) +print_parameter_set(FILE *f, long type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg, + CK_KEY_TYPE key_type) +{ + switch (key_type) { + case CKK_ML_DSA: + print_enum(f, type, value, size, ck_ml_dsa_t, key_type); + break; + case CKK_ML_KEM: + print_enum(f, type, value, size, ck_ml_kem_t, key_type); + break; + case CKK_SLH_DSA: + print_enum(f, type, value, size, ck_slh_dsa_t, key_type); + break; + default: + print_generic(f, type, value, size, arg, key_type); + break; + } +} + +void +print_print(FILE *f, long type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg, CK_KEY_TYPE key_type) { CK_ULONG i, j=0; CK_BYTE c; - if((CK_LONG)size != -1) { + if((long)size != -1) { fprintf(f, "%s\n ", buf_spec(value, size)); for(i = 0; i < size; i += j) { for(j = 0; ((i + j < size) && (j < 32)); j++) { @@ -244,400 +301,829 @@ print_print(FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR fprintf(f, "\n"); } +// clang-format off static enum_specs ck_cls_s[] = { - { CKO_DATA , "CKO_DATA " }, - { CKO_CERTIFICATE , "CKO_CERTIFICATE " }, - { CKO_PUBLIC_KEY , "CKO_PUBLIC_KEY " }, - { CKO_PRIVATE_KEY , "CKO_PRIVATE_KEY " }, - { CKO_SECRET_KEY , "CKO_SECRET_KEY " }, - { CKO_HW_FEATURE , "CKO_HW_FEATURE " }, - { CKO_DOMAIN_PARAMETERS, "CKO_DOMAIN_PARAMETERS" }, - { CKO_NETSCAPE_CRL, "CKO_NETSCAPE_CRL " }, - { CKO_NETSCAPE_SMIME , "CKO_NETSCAPE_SMIME " }, - { CKO_NETSCAPE_TRUST, "CKO_NETSCAPE_TRUST " }, - { CKO_NETSCAPE_BUILTIN_ROOT_LIST, "CKO_NETSCAPE_BUILTIN_ROOT_LIST" }, - { CKO_VENDOR_DEFINED , "CKO_VENDOR_DEFINED " } + {CKO_DATA , "CKO_DATA "}, + {CKO_CERTIFICATE , "CKO_CERTIFICATE "}, + {CKO_PUBLIC_KEY , "CKO_PUBLIC_KEY "}, + {CKO_PRIVATE_KEY , "CKO_PRIVATE_KEY "}, + {CKO_SECRET_KEY , "CKO_SECRET_KEY "}, + {CKO_HW_FEATURE , "CKO_HW_FEATURE "}, + {CKO_DOMAIN_PARAMETERS , "CKO_DOMAIN_PARAMETERS "}, + {CKO_MECHANISM , "CKO_MECHANISM "}, + {CKO_OTP_KEY , "CKO_OTP_KEY "}, + {CKO_PROFILE , "CKO_PROFILE "}, + {CKO_VALIDATION , "CKO_VALIDATION "}, + {CKO_TRUST , "CKO_TRUST "}, + {CKO_NSS_CRL , "CKO_NSS_CRL "}, + {CKO_NSS_SMIME , "CKO_NSS_SMIME "}, + {CKO_NSS_TRUST , "CKO_NSS_TRUST "}, + {CKO_NSS_BUILTIN_ROOT_LIST, "CKO_NSS_BUILTIN_ROOT_LIST"}, + {CKO_VENDOR_DEFINED , "CKO_VENDOR_DEFINED "} +}; + +enum_specs ck_profile_s[] = { + {CKP_INVALID_ID , "CKP_INVALID_ID "}, + {CKP_BASELINE_PROVIDER , "CKP_BASELINE_PROVIDER "}, + {CKP_EXTENDED_PROVIDER , "CKP_EXTENDED_PROVIDER "}, + {CKP_AUTHENTICATION_TOKEN , "CKP_AUTHENTICATION_TOKEN "}, + {CKP_PUBLIC_CERTIFICATES_TOKEN, "CKP_PUBLIC_CERTIFICATES_TOKEN"}, + {CKP_COMPLETE_PROVIDER , "CKP_COMPLETE_PROVIDER "}, + {CKP_HKDF_TLS_TOKEN , "CKP_HKDF_TLS_TOKEN "}, + {CKP_VENDOR_DEFINED , "CKP_VENDOR_DEFINED "} }; static enum_specs ck_crt_s[] = { - { CKC_X_509, "CKC_X_509" }, - { CKC_X_509_ATTR_CERT, "CKC_X_509_ATTR_CERT" }, + {CKC_X_509 , "CKC_X_509 "}, + {CKC_X_509_ATTR_CERT, "CKC_X_509_ATTR_CERT"}, + {CKC_WTLS , "CKC_WTLS "}, }; static enum_specs ck_key_s[] = { - { CKK_RSA , "CKK_RSA " }, - { CKK_DSA , "CKK_DSA " }, - { CKK_DH , "CKK_DH " }, - { CKK_EC , "CKK_EC " }, - { CKK_X9_42_DH , "CKK_X9_42_DH " }, - { CKK_KEA , "CKK_KEA " }, - { CKK_GENERIC_SECRET, "CKK_GENERIC_SECRET " }, - { CKK_RC2 , "CKK_RC2 " }, - { CKK_RC4 , "CKK_RC4 " }, - { CKK_DES , "CKK_DES " }, - { CKK_DES2 , "CKK_DES2 " }, - { CKK_DES3 , "CKK_DES3 " }, - { CKK_CAST , "CKK_CAST " }, - { CKK_CAST3 , "CKK_CAST3 " }, - { CKK_CAST128 , "CKK_CAST128 " }, - { CKK_RC5 , "CKK_RC5 " }, - { CKK_IDEA , "CKK_IDEA " }, - { CKK_SKIPJACK , "CKK_SKIPJACK " }, - { CKK_BATON , "CKK_BATON " }, - { CKK_JUNIPER , "CKK_JUNIPER " }, - { CKK_CDMF , "CKK_CDMF " }, - { CKK_AES , "CKK_AES " }, - { CKK_BLOWFISH , "CKK_BLOWFISH " }, - { CKK_TWOFISH , "CKK_TWOFISH " }, - { CKK_GOSTR3410 , "CKK_GOSTR3410 " }, - { CKK_GOSTR3411 , "CKK_GOSTR3411 " }, - { CKK_GOST28147 , "CKK_GOST28147 " } + {CKK_RSA , "CKK_RSA "}, + {CKK_DSA , "CKK_DSA "}, + {CKK_DH , "CKK_DH "}, + {CKK_EC , "CKK_EC "}, + {CKK_X9_42_DH , "CKK_X9_42_DH "}, + {CKK_KEA , "CKK_KEA "}, + {CKK_GENERIC_SECRET , "CKK_GENERIC_SECRET "}, + {CKK_RC2 , "CKK_RC2 "}, + {CKK_RC4 , "CKK_RC4 "}, + {CKK_DES , "CKK_DES "}, + {CKK_DES2 , "CKK_DES2 "}, + {CKK_DES3 , "CKK_DES3 "}, + {CKK_CAST , "CKK_CAST "}, + {CKK_CAST3 , "CKK_CAST3 "}, + {CKK_CAST128 , "CKK_CAST128 "}, + {CKK_RC5 , "CKK_RC5 "}, + {CKK_IDEA , "CKK_IDEA "}, + {CKK_SKIPJACK , "CKK_SKIPJACK "}, + {CKK_BATON , "CKK_BATON "}, + {CKK_JUNIPER , "CKK_JUNIPER "}, + {CKK_CDMF , "CKK_CDMF "}, + {CKK_AES , "CKK_AES "}, + {CKK_BLOWFISH , "CKK_BLOWFISH "}, + {CKK_TWOFISH , "CKK_TWOFISH "}, + {CKK_SECURID , "CKK_SECURID "}, + {CKK_HOTP , "CKK_HOTP "}, + {CKK_ACTI , "CKK_ACTI "}, + {CKK_CAMELLIA , "CKK_CAMELLIA "}, + {CKK_ARIA , "CKK_ARIA "}, + {CKK_MD5_HMAC , "CKK_MD5_HMAC "}, + {CKK_SHA_1_HMAC , "CKK_SHA_1_HMAC "}, + {CKK_RIPEMD128_HMAC , "CKK_RIPEMD128_HMAC "}, + {CKK_RIPEMD160_HMAC , "CKK_RIPEMD160_HMAC "}, + {CKK_SHA256_HMAC , "CKK_SHA256_HMAC "}, + {CKK_SHA384_HMAC , "CKK_SHA384_HMAC "}, + {CKK_SHA512_HMAC , "CKK_SHA512_HMAC "}, + {CKK_SHA224_HMAC , "CKK_SHA224_HMAC "}, + {CKK_SEED , "CKK_SEED "}, + {CKK_GOSTR3410 , "CKK_GOSTR3410 "}, + {CKK_GOSTR3411 , "CKK_GOSTR3411 "}, + {CKK_GOST28147 , "CKK_GOST28147 "}, + {CKK_CHACHA20 , "CKK_CHACHA20 "}, + {CKK_POLY1305 , "CKK_POLY1305 "}, + {CKK_AES_XTS , "CKK_AES_XTS "}, + {CKK_SHA3_224_HMAC , "CKK_SHA3_224_HMAC "}, + {CKK_SHA3_256_HMAC , "CKK_SHA3_256_HMAC "}, + {CKK_SHA3_384_HMAC , "CKK_SHA3_384_HMAC "}, + {CKK_SHA3_512_HMAC , "CKK_SHA3_512_HMAC "}, + {CKK_BLAKE2B_160_HMAC, "CKK_BLAKE2B_160_HMAC"}, + {CKK_BLAKE2B_256_HMAC, "CKK_BLAKE2B_256_HMAC"}, + {CKK_BLAKE2B_384_HMAC, "CKK_BLAKE2B_384_HMAC"}, + {CKK_BLAKE2B_512_HMAC, "CKK_BLAKE2B_512_HMAC"}, + {CKK_SALSA20 , "CKK_SALSA20 "}, + {CKK_X2RATCHET , "CKK_X2RATCHET "}, + {CKK_EC_EDWARDS , "CKK_EC_EDWARDS "}, + {CKK_EC_MONTGOMERY , "CKK_EC_MONTOGMERY "}, + {CKK_HKDF , "CKK_HKDF "}, + {CKK_SHA512_224_HMAC , "CKK_SHA512_224_HMAC "}, + {CKK_SHA512_256_HMAC , "CKK_SHA512_256_HMAC "}, + {CKK_SHA512_T_HMAC , "CKK_SHA512_T_HMAC "}, + {CKK_HSS , "CKK_HSS "}, + {CKK_XMSS , "CKK_XMSS "}, + {CKK_XMSSMT , "CKK_XMSSMT "}, + {CKK_ML_KEM , "CKK_ML_KEM "}, + {CKK_ML_DSA , "CKK_ML_DSA "}, + {CKK_SLH_DSA , "CKK_SLH_DSA "} }; static enum_specs ck_mec_s[] = { - { CKM_RSA_PKCS_KEY_PAIR_GEN , "CKM_RSA_PKCS_KEY_PAIR_GEN " }, - { CKM_RSA_PKCS , "CKM_RSA_PKCS " }, - { CKM_RSA_9796 , "CKM_RSA_9796 " }, - { CKM_RSA_X_509 , "CKM_RSA_X_509 " }, - { CKM_MD2_RSA_PKCS , "CKM_MD2_RSA_PKCS " }, - { CKM_MD5_RSA_PKCS , "CKM_MD5_RSA_PKCS " }, - { CKM_SHA1_RSA_PKCS , "CKM_SHA1_RSA_PKCS " }, - { CKM_SHA256_RSA_PKCS , "CKM_SHA256_RSA_PKCS " }, - { CKM_SHA384_RSA_PKCS , "CKM_SHA384_RSA_PKCS " }, - { CKM_SHA512_RSA_PKCS , "CKM_SHA512_RSA_PKCS " }, - { CKM_RIPEMD128_RSA_PKCS , "CKM_RIPEMD128_RSA_PKCS " }, - { CKM_RIPEMD160_RSA_PKCS , "CKM_RIPEMD160_RSA_PKCS " }, - { CKM_RSA_PKCS_OAEP , "CKM_RSA_PKCS_OAEP " }, - { CKM_RSA_X9_31_KEY_PAIR_GEN , "CKM_RSA_X9_31_KEY_PAIR_GEN " }, - { CKM_RSA_X9_31 , "CKM_RSA_X9_31 " }, - { CKM_SHA1_RSA_X9_31 , "CKM_SHA1_RSA_X9_31 " }, - { CKM_RSA_PKCS_PSS , "CKM_RSA_PKCS_PSS " }, - { CKM_SHA1_RSA_PKCS_PSS , "CKM_SHA1_RSA_PKCS_PSS " }, - { CKM_SHA256_RSA_PKCS_PSS , "CKM_SHA256_RSA_PKCS_PSS " }, - { CKM_SHA384_RSA_PKCS_PSS , "CKM_SHA384_RSA_PKCS_PSS " }, - { CKM_SHA512_RSA_PKCS_PSS , "CKM_SHA512_RSA_PKCS_PSS " }, - { CKM_DSA_KEY_PAIR_GEN , "CKM_DSA_KEY_PAIR_GEN " }, - { CKM_DSA , "CKM_DSA " }, - { CKM_DSA_SHA1 , "CKM_DSA_SHA1 " }, - { CKM_DH_PKCS_KEY_PAIR_GEN , "CKM_DH_PKCS_KEY_PAIR_GEN " }, - { CKM_DH_PKCS_DERIVE , "CKM_DH_PKCS_DERIVE " }, - { CKM_X9_42_DH_KEY_PAIR_GEN , "CKM_X9_42_DH_KEY_PAIR_GEN " }, - { CKM_X9_42_DH_DERIVE , "CKM_X9_42_DH_DERIVE " }, - { CKM_X9_42_DH_HYBRID_DERIVE , "CKM_X9_42_DH_HYBRID_DERIVE " }, - { CKM_X9_42_MQV_DERIVE , "CKM_X9_42_MQV_DERIVE " }, - { CKM_RC2_KEY_GEN , "CKM_RC2_KEY_GEN " }, - { CKM_RC2_ECB , "CKM_RC2_ECB " }, - { CKM_RC2_CBC , "CKM_RC2_CBC " }, - { CKM_RC2_MAC , "CKM_RC2_MAC " }, - { CKM_RC2_MAC_GENERAL , "CKM_RC2_MAC_GENERAL " }, - { CKM_RC2_CBC_PAD , "CKM_RC2_CBC_PAD " }, - { CKM_RC4_KEY_GEN , "CKM_RC4_KEY_GEN " }, - { CKM_RC4 , "CKM_RC4 " }, - { CKM_DES_KEY_GEN , "CKM_DES_KEY_GEN " }, - { CKM_DES_ECB , "CKM_DES_ECB " }, - { CKM_DES_CBC , "CKM_DES_CBC " }, - { CKM_DES_MAC , "CKM_DES_MAC " }, - { CKM_DES_MAC_GENERAL , "CKM_DES_MAC_GENERAL " }, - { CKM_DES_CBC_PAD , "CKM_DES_CBC_PAD " }, - { CKM_DES2_KEY_GEN , "CKM_DES2_KEY_GEN " }, - { CKM_DES3_KEY_GEN , "CKM_DES3_KEY_GEN " }, - { CKM_DES3_ECB , "CKM_DES3_ECB " }, - { CKM_DES3_CBC , "CKM_DES3_CBC " }, - { CKM_DES3_MAC , "CKM_DES3_MAC " }, - { CKM_DES3_MAC_GENERAL , "CKM_DES3_MAC_GENERAL " }, - { CKM_DES3_CBC_PAD , "CKM_DES3_CBC_PAD " }, - { CKM_CDMF_KEY_GEN , "CKM_CDMF_KEY_GEN " }, - { CKM_CDMF_ECB , "CKM_CDMF_ECB " }, - { CKM_CDMF_CBC , "CKM_CDMF_CBC " }, - { CKM_CDMF_MAC , "CKM_CDMF_MAC " }, - { CKM_CDMF_MAC_GENERAL , "CKM_CDMF_MAC_GENERAL " }, - { CKM_CDMF_CBC_PAD , "CKM_CDMF_CBC_PAD " }, - { CKM_MD2 , "CKM_MD2 " }, - { CKM_MD2_HMAC , "CKM_MD2_HMAC " }, - { CKM_MD2_HMAC_GENERAL , "CKM_MD2_HMAC_GENERAL " }, - { CKM_MD5 , "CKM_MD5 " }, - { CKM_MD5_HMAC , "CKM_MD5_HMAC " }, - { CKM_MD5_HMAC_GENERAL , "CKM_MD5_HMAC_GENERAL " }, - { CKM_SHA_1 , "CKM_SHA_1 " }, - { CKM_SHA_1_HMAC , "CKM_SHA_1_HMAC " }, - { CKM_SHA_1_HMAC_GENERAL , "CKM_SHA_1_HMAC_GENERAL " }, - { CKM_SHA256 , "CKM_SHA256 " }, - { CKM_SHA256_HMAC , "CKM_SHA256_HMAC " }, - { CKM_SHA256_HMAC_GENERAL , "CKM_SHA256_HMAC_GENERAL " }, - { CKM_SHA384 , "CKM_SHA384 " }, - { CKM_SHA384_HMAC , "CKM_SHA384_HMAC " }, - { CKM_SHA384_HMAC_GENERAL , "CKM_SHA384_HMAC_GENERAL " }, - { CKM_SHA512 , "CKM_SHA512 " }, - { CKM_SHA512_HMAC , "CKM_SHA512_HMAC " }, - { CKM_SHA512_HMAC_GENERAL , "CKM_SHA512_HMAC_GENERAL " }, - { CKM_RIPEMD128 , "CKM_RIPEMD128 " }, - { CKM_RIPEMD128_HMAC , "CKM_RIPEMD128_HMAC " }, - { CKM_RIPEMD128_HMAC_GENERAL , "CKM_RIPEMD128_HMAC_GENERAL " }, - { CKM_RIPEMD160 , "CKM_RIPEMD160 " }, - { CKM_RIPEMD160_HMAC , "CKM_RIPEMD160_HMAC " }, - { CKM_RIPEMD160_HMAC_GENERAL , "CKM_RIPEMD160_HMAC_GENERAL " }, - { CKM_SHA256 , "CKM_SHA256 " }, - { CKM_SHA256_HMAC , "CKM_SHA256_HMAC " }, - { CKM_SHA256_HMAC_GENERAL , "CKM_SHA256_HMAC_GENERAL " }, - { CKM_SHA384 , "CKM_SHA384 " }, - { CKM_SHA384_HMAC , "CKM_SHA384_HMAC " }, - { CKM_SHA384_HMAC_GENERAL , "CKM_SHA384_HMAC_GENERAL " }, - { CKM_CAST_KEY_GEN , "CKM_CAST_KEY_GEN " }, - { CKM_CAST_ECB , "CKM_CAST_ECB " }, - { CKM_CAST_CBC , "CKM_CAST_CBC " }, - { CKM_CAST_MAC , "CKM_CAST_MAC " }, - { CKM_CAST_MAC_GENERAL , "CKM_CAST_MAC_GENERAL " }, - { CKM_CAST_CBC_PAD , "CKM_CAST_CBC_PAD " }, - { CKM_CAST3_KEY_GEN , "CKM_CAST3_KEY_GEN " }, - { CKM_CAST3_ECB , "CKM_CAST3_ECB " }, - { CKM_CAST3_CBC , "CKM_CAST3_CBC " }, - { CKM_CAST3_MAC , "CKM_CAST3_MAC " }, - { CKM_CAST3_MAC_GENERAL , "CKM_CAST3_MAC_GENERAL " }, - { CKM_CAST3_CBC_PAD , "CKM_CAST3_CBC_PAD " }, - { CKM_CAST5_KEY_GEN , "CKM_CAST5_KEY_GEN " }, - { CKM_CAST128_KEY_GEN , "CKM_CAST128_KEY_GEN " }, - { CKM_CAST5_ECB , "CKM_CAST5_ECB " }, - { CKM_CAST128_ECB , "CKM_CAST128_ECB " }, - { CKM_CAST5_CBC , "CKM_CAST5_CBC " }, - { CKM_CAST128_CBC , "CKM_CAST128_CBC " }, - { CKM_CAST5_MAC , "CKM_CAST5_MAC " }, - { CKM_CAST128_MAC , "CKM_CAST128_MAC " }, - { CKM_CAST5_MAC_GENERAL , "CKM_CAST5_MAC_GENERAL " }, - { CKM_CAST128_MAC_GENERAL , "CKM_CAST128_MAC_GENERAL " }, - { CKM_CAST5_CBC_PAD , "CKM_CAST5_CBC_PAD " }, - { CKM_CAST128_CBC_PAD , "CKM_CAST128_CBC_PAD " }, - { CKM_RC5_KEY_GEN , "CKM_RC5_KEY_GEN " }, - { CKM_RC5_ECB , "CKM_RC5_ECB " }, - { CKM_RC5_CBC , "CKM_RC5_CBC " }, - { CKM_RC5_MAC , "CKM_RC5_MAC " }, - { CKM_RC5_MAC_GENERAL , "CKM_RC5_MAC_GENERAL " }, - { CKM_RC5_CBC_PAD , "CKM_RC5_CBC_PAD " }, - { CKM_IDEA_KEY_GEN , "CKM_IDEA_KEY_GEN " }, - { CKM_IDEA_ECB , "CKM_IDEA_ECB " }, - { CKM_IDEA_CBC , "CKM_IDEA_CBC " }, - { CKM_IDEA_MAC , "CKM_IDEA_MAC " }, - { CKM_IDEA_MAC_GENERAL , "CKM_IDEA_MAC_GENERAL " }, - { CKM_IDEA_CBC_PAD , "CKM_IDEA_CBC_PAD " }, - { CKM_GENERIC_SECRET_KEY_GEN , "CKM_GENERIC_SECRET_KEY_GEN " }, - { CKM_CONCATENATE_BASE_AND_KEY , "CKM_CONCATENATE_BASE_AND_KEY " }, - { CKM_CONCATENATE_BASE_AND_DATA, "CKM_CONCATENATE_BASE_AND_DATA" }, - { CKM_CONCATENATE_DATA_AND_BASE, "CKM_CONCATENATE_DATA_AND_BASE" }, - { CKM_XOR_BASE_AND_DATA , "CKM_XOR_BASE_AND_DATA " }, - { CKM_EXTRACT_KEY_FROM_KEY , "CKM_EXTRACT_KEY_FROM_KEY " }, - { CKM_SSL3_PRE_MASTER_KEY_GEN , "CKM_SSL3_PRE_MASTER_KEY_GEN " }, - { CKM_SSL3_MASTER_KEY_DERIVE , "CKM_SSL3_MASTER_KEY_DERIVE " }, - { CKM_SSL3_KEY_AND_MAC_DERIVE , "CKM_SSL3_KEY_AND_MAC_DERIVE " }, - { CKM_SSL3_MASTER_KEY_DERIVE_DH, "CKM_SSL3_MASTER_KEY_DERIVE_DH" }, - { CKM_TLS_PRE_MASTER_KEY_GEN , "CKM_TLS_PRE_MASTER_KEY_GEN " }, - { CKM_TLS_MASTER_KEY_DERIVE , "CKM_TLS_MASTER_KEY_DERIVE " }, - { CKM_TLS_KEY_AND_MAC_DERIVE , "CKM_TLS_KEY_AND_MAC_DERIVE " }, - { CKM_TLS_MASTER_KEY_DERIVE_DH , "CKM_TLS_MASTER_KEY_DERIVE_DH " }, - { CKM_SSL3_MD5_MAC , "CKM_SSL3_MD5_MAC " }, - { CKM_SSL3_SHA1_MAC , "CKM_SSL3_SHA1_MAC " }, - { CKM_MD5_KEY_DERIVATION , "CKM_MD5_KEY_DERIVATION " }, - { CKM_MD2_KEY_DERIVATION , "CKM_MD2_KEY_DERIVATION " }, - { CKM_SHA1_KEY_DERIVATION , "CKM_SHA1_KEY_DERIVATION " }, - { CKM_PBE_MD2_DES_CBC , "CKM_PBE_MD2_DES_CBC " }, - { CKM_PBE_MD5_DES_CBC , "CKM_PBE_MD5_DES_CBC " }, - { CKM_PBE_MD5_CAST_CBC , "CKM_PBE_MD5_CAST_CBC " }, - { CKM_PBE_MD5_CAST3_CBC , "CKM_PBE_MD5_CAST3_CBC " }, - { CKM_PBE_MD5_CAST5_CBC , "CKM_PBE_MD5_CAST5_CBC " }, - { CKM_PBE_MD5_CAST128_CBC , "CKM_PBE_MD5_CAST128_CBC " }, - { CKM_PBE_SHA1_CAST5_CBC , "CKM_PBE_SHA1_CAST5_CBC " }, - { CKM_PBE_SHA1_CAST128_CBC , "CKM_PBE_SHA1_CAST128_CBC " }, - { CKM_PBE_SHA1_RC4_128 , "CKM_PBE_SHA1_RC4_128 " }, - { CKM_PBE_SHA1_RC4_40 , "CKM_PBE_SHA1_RC4_40 " }, - { CKM_PBE_SHA1_DES3_EDE_CBC , "CKM_PBE_SHA1_DES3_EDE_CBC " }, - { CKM_PBE_SHA1_DES2_EDE_CBC , "CKM_PBE_SHA1_DES2_EDE_CBC " }, - { CKM_PBE_SHA1_RC2_128_CBC , "CKM_PBE_SHA1_RC2_128_CBC " }, - { CKM_PBE_SHA1_RC2_40_CBC , "CKM_PBE_SHA1_RC2_40_CBC " }, - { CKM_PKCS5_PBKD2 , "CKM_PKCS5_PBKD2 " }, - { CKM_PBA_SHA1_WITH_SHA1_HMAC , "CKM_PBA_SHA1_WITH_SHA1_HMAC " }, - { CKM_KEY_WRAP_LYNKS , "CKM_KEY_WRAP_LYNKS " }, - { CKM_KEY_WRAP_SET_OAEP , "CKM_KEY_WRAP_SET_OAEP " }, - { CKM_SKIPJACK_KEY_GEN , "CKM_SKIPJACK_KEY_GEN " }, - { CKM_SKIPJACK_ECB64 , "CKM_SKIPJACK_ECB64 " }, - { CKM_SKIPJACK_CBC64 , "CKM_SKIPJACK_CBC64 " }, - { CKM_SKIPJACK_OFB64 , "CKM_SKIPJACK_OFB64 " }, - { CKM_SKIPJACK_CFB64 , "CKM_SKIPJACK_CFB64 " }, - { CKM_SKIPJACK_CFB32 , "CKM_SKIPJACK_CFB32 " }, - { CKM_SKIPJACK_CFB16 , "CKM_SKIPJACK_CFB16 " }, - { CKM_SKIPJACK_CFB8 , "CKM_SKIPJACK_CFB8 " }, - { CKM_SKIPJACK_WRAP , "CKM_SKIPJACK_WRAP " }, - { CKM_SKIPJACK_PRIVATE_WRAP , "CKM_SKIPJACK_PRIVATE_WRAP " }, - { CKM_SKIPJACK_RELAYX , "CKM_SKIPJACK_RELAYX " }, - { CKM_KEA_KEY_PAIR_GEN , "CKM_KEA_KEY_PAIR_GEN " }, - { CKM_KEA_KEY_DERIVE , "CKM_KEA_KEY_DERIVE " }, - { CKM_FORTEZZA_TIMESTAMP , "CKM_FORTEZZA_TIMESTAMP " }, - { CKM_BATON_KEY_GEN , "CKM_BATON_KEY_GEN " }, - { CKM_BATON_ECB128 , "CKM_BATON_ECB128 " }, - { CKM_BATON_ECB96 , "CKM_BATON_ECB96 " }, - { CKM_BATON_CBC128 , "CKM_BATON_CBC128 " }, - { CKM_BATON_COUNTER , "CKM_BATON_COUNTER " }, - { CKM_BATON_SHUFFLE , "CKM_BATON_SHUFFLE " }, - { CKM_BATON_WRAP , "CKM_BATON_WRAP " }, - { CKM_EC_KEY_PAIR_GEN , "CKM_EC_KEY_PAIR_GEN " }, - { CKM_ECDSA , "CKM_ECDSA " }, - { CKM_ECDSA_SHA1 , "CKM_ECDSA_SHA1 " }, - { CKM_ECDH1_DERIVE , "CKM_ECDH1_DERIVE " }, - { CKM_ECDH1_COFACTOR_DERIVE , "CKM_ECDH1_COFACTOR_DERIVE " }, - { CKM_ECMQV_DERIVE , "CKM_ECMQV_DERIVE " }, - { CKM_JUNIPER_KEY_GEN , "CKM_JUNIPER_KEY_GEN " }, - { CKM_JUNIPER_ECB128 , "CKM_JUNIPER_ECB128 " }, - { CKM_JUNIPER_CBC128 , "CKM_JUNIPER_CBC128 " }, - { CKM_JUNIPER_COUNTER , "CKM_JUNIPER_COUNTER " }, - { CKM_JUNIPER_SHUFFLE , "CKM_JUNIPER_SHUFFLE " }, - { CKM_JUNIPER_WRAP , "CKM_JUNIPER_WRAP " }, - { CKM_FASTHASH , "CKM_FASTHASH " }, - { CKM_AES_KEY_GEN , "CKM_AES_KEY_GEN " }, - { CKM_AES_ECB , "CKM_AES_ECB " }, - { CKM_AES_CBC , "CKM_AES_CBC " }, - { CKM_AES_MAC , "CKM_AES_MAC " }, - { CKM_AES_MAC_GENERAL , "CKM_AES_MAC_GENERAL " }, - { CKM_AES_CBC_PAD , "CKM_AES_CBC_PAD " }, - { CKM_AES_CTR , "CKM_AES_CTR " }, - { CKM_AES_GCM , "CKM_AES_GCM " }, - { CKM_AES_CCM , "CKM_AES_CCM " }, - { CKM_AES_CTS , "CKM_AES_CTS " }, - { CKM_BLOWFISH_KEY_GEN , "CKM_BLOWFISH_KEY_GEN " }, - { CKM_BLOWFISH_CBC , "CKM_BLOWFISH_CBC " }, - { CKM_TWOFISH_KEY_GEN , "CKM_TWOFISH_KEY_GEN " }, - { CKM_TWOFISH_CBC , "CKM_TWOFISH_CBC " }, - { CKM_GOSTR3410_KEY_PAIR_GEN , "CKM_GOSTR3410_KEY_PAIR_GEN " }, - { CKM_GOSTR3410 , "CKM_GOSTR3410 " }, - { CKM_GOSTR3410_WITH_GOSTR3411 , "CKM_GOSTR3410_WITH_GOSTR3411 " }, - { CKM_GOSTR3410_KEY_WRAP , "CKM_GOSTR3410_KEY_WRAP " }, - { CKM_GOSTR3410_DERIVE , "CKM_GOSTR3410_DERIVE " }, - { CKM_GOSTR3411 , "CKM_GOSTR3411 " }, - { CKM_GOSTR3411_HMAC , "CKM_GOSTR3411_HMAC " }, - { CKM_GOST28147_KEY_GEN , "CKM_GOST28147_KEY_GEN " }, - { CKM_GOST28147_ECB , "CKM_GOST28147_ECB " }, - { CKM_GOST28147 , "CKM_GOST28147 " }, - { CKM_GOST28147_MAC , "CKM_GOST28147_MAC " }, - { CKM_GOST28147_KEY_WRAP , "CKM_GOST28147_KEY_WRAP " }, - { CKM_DSA_PARAMETER_GEN , "CKM_DSA_PARAMETER_GEN " }, - { CKM_DH_PKCS_PARAMETER_GEN , "CKM_DH_PKCS_PARAMETER_GEN " }, - { CKM_X9_42_DH_PARAMETER_GEN , "CKM_X9_42_DH_PARAMETER_GEN " }, - { CKM_VENDOR_DEFINED , "CKM_VENDOR_DEFINED " } + {CKM_RSA_PKCS_KEY_PAIR_GEN , "CKM_RSA_PKCS_KEY_PAIR_GEN "}, + {CKM_RSA_PKCS , "CKM_RSA_PKCS "}, + {CKM_RSA_9796 , "CKM_RSA_9796 "}, + {CKM_RSA_X_509 , "CKM_RSA_X_509 "}, + {CKM_MD2_RSA_PKCS , "CKM_MD2_RSA_PKCS "}, + {CKM_MD5_RSA_PKCS , "CKM_MD5_RSA_PKCS "}, + {CKM_SHA1_RSA_PKCS , "CKM_SHA1_RSA_PKCS "}, + {CKM_RIPEMD128_RSA_PKCS , "CKM_RIPEMD128_RSA_PKCS "}, + {CKM_RIPEMD160_RSA_PKCS , "CKM_RIPEMD160_RSA_PKCS "}, + {CKM_RSA_PKCS_OAEP , "CKM_RSA_PKCS_OAEP "}, + {CKM_RSA_X9_31_KEY_PAIR_GEN , "CKM_RSA_X9_31_KEY_PAIR_GEN "}, + {CKM_RSA_X9_31 , "CKM_RSA_X9_31 "}, + {CKM_SHA1_RSA_X9_31 , "CKM_SHA1_RSA_X9_31 "}, + {CKM_RSA_PKCS_PSS , "CKM_RSA_PKCS_PSS "}, + {CKM_SHA1_RSA_PKCS_PSS , "CKM_SHA1_RSA_PKCS_PSS "}, + {CKM_ML_KEM_KEY_PAIR_GEN , "CKM_ML_KEM_KEY_PAIR_GEN "}, + {CKM_DSA_KEY_PAIR_GEN , "CKM_DSA_KEY_PAIR_GEN "}, + {CKM_DSA , "CKM_DSA "}, + {CKM_DSA_SHA1 , "CKM_DSA_SHA1 "}, + {CKM_DSA_SHA224 , "CKM_DSA_SHA224 "}, + {CKM_DSA_SHA256 , "CKM_DSA_SHA256 "}, + {CKM_DSA_SHA384 , "CKM_DSA_SHA384 "}, + {CKM_DSA_SHA512 , "CKM_DSA_SHA512 "}, + {CKM_ML_KEM , "CKM_ML_KEM "}, + {CKM_DSA_SHA3_224 , "CKM_DSA_SHA3_224 "}, + {CKM_DSA_SHA3_256 , "CKM_DSA_SHA3_256 "}, + {CKM_DSA_SHA3_384 , "CKM_DSA_SHA3_384 "}, + {CKM_DSA_SHA3_512 , "CKM_DSA_SHA3_512 "}, + {CKM_ML_DSA_KEY_PAIR_GEN , "CKM_ML_DSA_KEY_PAIR_GEN "}, + {CKM_ML_DSA , "CKM_ML_DSA "}, + {CKM_HASH_ML_DSA , "CKM_HASH_ML_DSA "}, + {CKM_DH_PKCS_KEY_PAIR_GEN , "CKM_DH_PKCS_KEY_PAIR_GEN "}, + {CKM_DH_PKCS_DERIVE , "CKM_DH_PKCS_DERIVE "}, + {CKM_HASH_ML_DSA_SHA224 , "CKM_HASH_ML_DSA_SHA224 "}, + {CKM_HASH_ML_DSA_SHA256 , "CKM_HASH_ML_DSA_SHA256 "}, + {CKM_HASH_ML_DSA_SHA384 , "CKM_HASH_ML_DSA_SHA384 "}, + {CKM_HASH_ML_DSA_SHA512 , "CKM_HASH_ML_DSA_SHA512 "}, + {CKM_HASH_ML_DSA_SHA3_224 , "CKM_HASH_ML_DSA_SHA3_224 "}, + {CKM_HASH_ML_DSA_SHA3_256 , "CKM_HASH_ML_DSA_SHA3_256 "}, + {CKM_HASH_ML_DSA_SHA3_384 , "CKM_HASH_ML_DSA_SHA3_384 "}, + {CKM_HASH_ML_DSA_SHA3_512 , "CKM_HASH_ML_DSA_SHA3_512 "}, + {CKM_HASH_ML_DSA_SHAKE128 , "CKM_HASH_ML_DSA_SHAKE128 "}, + {CKM_HASH_ML_DSA_SHAKE256 , "CKM_HASH_ML_DSA_SHAKE256 "}, + {CKM_SLH_DSA_KEY_PAIR_GEN , "CKM_SLH_DSA_KEY_PAIR_GEN "}, + {CKM_SLH_DSA , "CKM_SLH_DSA "}, + {CKM_X9_42_DH_KEY_PAIR_GEN , "CKM_X9_42_DH_KEY_PAIR_GEN "}, + {CKM_X9_42_DH_DERIVE , "CKM_X9_42_DH_DERIVE "}, + {CKM_X9_42_DH_HYBRID_DERIVE , "CKM_X9_42_DH_HYBRID_DERIVE "}, + {CKM_X9_42_MQV_DERIVE , "CKM_X9_42_MQV_DERIVE "}, + {CKM_HASH_SLH_DSA , "CKM_HASH_SLH_DSA "}, + {CKM_HASH_SLH_DSA_SHA224 , "CKM_HASH_SLH_DSA_SHA224 "}, + {CKM_HASH_SLH_DSA_SHA256 , "CKM_HASH_SLH_DSA_SHA256 "}, + {CKM_HASH_SLH_DSA_SHA384 , "CKM_HASH_SLH_DSA_SHA384 "}, + {CKM_HASH_SLH_DSA_SHA512 , "CKM_HASH_SLH_DSA_SHA512 "}, + {CKM_HASH_SLH_DSA_SHA3_224 , "CKM_HASH_SLH_DSA_SHA3_224 "}, + {CKM_HASH_SLH_DSA_SHA3_256 , "CKM_HASH_SLH_DSA_SHA3_256 "}, + {CKM_HASH_SLH_DSA_SHA3_384 , "CKM_HASH_SLH_DSA_SHA3_384 "}, + {CKM_HASH_SLH_DSA_SHA3_512 , "CKM_HASH_SLH_DSA_SHA3_512 "}, + {CKM_HASH_SLH_DSA_SHAKE128 , "CKM_HASH_SLH_DSA_SHAKE128 "}, + {CKM_HASH_SLH_DSA_SHAKE256 , "CKM_HASH_SLH_DSA_SHAKE256 "}, + {CKM_SHA256_RSA_PKCS , "CKM_SHA256_RSA_PKCS "}, + {CKM_SHA384_RSA_PKCS , "CKM_SHA384_RSA_PKCS "}, + {CKM_SHA512_RSA_PKCS , "CKM_SHA512_RSA_PKCS "}, + {CKM_SHA256_RSA_PKCS_PSS , "CKM_SHA256_RSA_PKCS_PSS "}, + {CKM_SHA384_RSA_PKCS_PSS , "CKM_SHA384_RSA_PKCS_PSS "}, + {CKM_SHA512_RSA_PKCS_PSS , "CKM_SHA512_RSA_PKCS_PSS "}, + {CKM_SHA224_RSA_PKCS , "CKM_SHA224_RSA_PKCS "}, + {CKM_SHA224_RSA_PKCS_PSS , "CKM_SHA224_RSA_PKCS_PSS "}, + {CKM_SHA512_224 , "CKM_SHA512_224 "}, + {CKM_SHA512_224_HMAC , "CKM_SHA512_224_HMAC "}, + {CKM_SHA512_224_HMAC_GENERAL , "CKM_SHA512_224_HMAC_GENERAL "}, + {CKM_SHA512_224_KEY_DERIVATION , "CKM_SHA512_224_KEY_DERIVATION "}, + {CKM_SHA512_256 , "CKM_SHA512_256 "}, + {CKM_SHA512_256 , "CKM_SHA512_256 "}, + {CKM_SHA512_256_HMAC , "CKM_SHA512_256_HMAC "}, + {CKM_SHA512_256_HMAC_GENERAL , "CKM_SHA512_256_HMAC_GENERAL "}, + {CKM_SHA512_256_KEY_DERIVATION , "CKM_SHA512_256_KEY_DERIVATION "}, + {CKM_SHA512_T , "CKM_SHA512_T "}, + {CKM_SHA512_T_HMAC , "CKM_SHA512_T_HMAC "}, + {CKM_SHA512_T_HMAC_GENERAL , "CKM_SHA512_T_HMAC_GENERAL "}, + {CKM_SHA512_T_KEY_DERIVATION , "CKM_SHA512_T_KEY_DERIVATION "}, + {CKM_TLS12_EXTENDED_MASTER_KEY_DERIVE , "CKM_TLS12_EXTENDED_MASTER_KEY_DERIVE "}, + {CKM_TLS12_EXTENDED_MASTER_KEY_DERIVE_DH, "CKM_TLS12_EXTENDED_MASTER_KEY_DERIVE_DH"}, + {CKM_SHA3_256_RSA_PKCS , "CKM_SHA3_256_RSA_PKCS "}, + {CKM_SHA3_384_RSA_PKCS , "CKM_SHA3_383_RSA_PKCS "}, + {CKM_SHA3_512_RSA_PKCS , "CKM_SHA3_512_RSA_PKCS "}, + {CKM_SHA3_256_RSA_PKCS_PSS , "CKM_SHA3_256_RSA_PKCS_PSS "}, + {CKM_SHA3_384_RSA_PKCS_PSS , "CKM_SHA3_384_RSA_PKCS_PSS "}, + {CKM_SHA3_512_RSA_PKCS_PSS , "CKM_SHA3_512_RSA_PKCS_PSS "}, + {CKM_SHA3_224_RSA_PKCS , "CKM_SHA3_224_RSA_PKCS "}, + {CKM_SHA3_224_RSA_PKCS_PSS , "CKM_SHA3_224_RSA_PKCS_PSS "}, + {CKM_RC2_KEY_GEN , "CKM_RC2_KEY_GEN "}, + {CKM_RC2_ECB , "CKM_RC2_ECB "}, + {CKM_RC2_CBC , "CKM_RC2_CBC "}, + {CKM_RC2_MAC , "CKM_RC2_MAC "}, + {CKM_RC2_MAC_GENERAL , "CKM_RC2_MAC_GENERAL "}, + {CKM_RC2_CBC_PAD , "CKM_RC2_CBC_PAD "}, + {CKM_RC4_KEY_GEN , "CKM_RC4_KEY_GEN "}, + {CKM_RC4 , "CKM_RC4 "}, + {CKM_DES_KEY_GEN , "CKM_DES_KEY_GEN "}, + {CKM_DES_ECB , "CKM_DES_ECB "}, + {CKM_DES_CBC , "CKM_DES_CBC "}, + {CKM_DES_MAC , "CKM_DES_MAC "}, + {CKM_DES_MAC_GENERAL , "CKM_DES_MAC_GENERAL "}, + {CKM_DES_CBC_PAD , "CKM_DES_CBC_PAD "}, + {CKM_DES2_KEY_GEN , "CKM_DES2_KEY_GEN "}, + {CKM_DES3_KEY_GEN , "CKM_DES3_KEY_GEN "}, + {CKM_DES3_ECB , "CKM_DES3_ECB "}, + {CKM_DES3_CBC , "CKM_DES3_CBC "}, + {CKM_DES3_MAC , "CKM_DES3_MAC "}, + {CKM_DES3_MAC_GENERAL , "CKM_DES3_MAC_GENERAL "}, + {CKM_DES3_CBC_PAD , "CKM_DES3_CBC_PAD "}, + {CKM_DES3_CMAC , "CKM_DES3_CMAC "}, + {CKM_CDMF_KEY_GEN , "CKM_CDMF_KEY_GEN "}, + {CKM_CDMF_ECB , "CKM_CDMF_ECB "}, + {CKM_CDMF_CBC , "CKM_CDMF_CBC "}, + {CKM_CDMF_MAC , "CKM_CDMF_MAC "}, + {CKM_CDMF_MAC_GENERAL , "CKM_CDMF_MAC_GENERAL "}, + {CKM_CDMF_CBC_PAD , "CKM_CDMF_CBC_PAD "}, + {CKM_DES_OFB64 , "CKM_DES_OFB64 "}, + {CKM_DES_OFB8 , "CKM_DES_OFB8 "}, + {CKM_DES_CFB64 , "CKM_DES_CFB64 "}, + {CKM_DES_CFB8 , "CKM_DES_CFB8 "}, + {CKM_MD2 , "CKM_MD2 "}, + {CKM_MD2_HMAC , "CKM_MD2_HMAC "}, + {CKM_MD2_HMAC_GENERAL , "CKM_MD2_HMAC_GENERAL "}, + {CKM_MD5 , "CKM_MD5 "}, + {CKM_MD5_HMAC , "CKM_MD5_HMAC "}, + {CKM_MD5_HMAC_GENERAL , "CKM_MD5_HMAC_GENERAL "}, + {CKM_SHA_1 , "CKM_SHA_1 "}, + {CKM_SHA_1_HMAC , "CKM_SHA_1_HMAC "}, + {CKM_SHA_1_HMAC_GENERAL , "CKM_SHA_1_HMAC_GENERAL "}, + {CKM_RIPEMD128 , "CKM_RIPEMD128 "}, + {CKM_RIPEMD128_HMAC , "CKM_RIPEMD128_HMAC "}, + {CKM_RIPEMD128_HMAC_GENERAL , "CKM_RIPEMD128_HMAC_GENERAL "}, + {CKM_RIPEMD160 , "CKM_RIPEMD160 "}, + {CKM_RIPEMD160_HMAC , "CKM_RIPEMD160_HMAC "}, + {CKM_RIPEMD160_HMAC_GENERAL , "CKM_RIPEMD160_HMAC_GENERAL "}, + {CKM_SHA256 , "CKM_SHA256 "}, + {CKM_SHA256_HMAC , "CKM_SHA256_HMAC "}, + {CKM_SHA256_HMAC_GENERAL , "CKM_SHA256_HMAC_GENERAL "}, + {CKM_SHA224 , "CKM_SHA224 "}, + {CKM_SHA224_HMAC , "CKM_SHA224_HMAC "}, + {CKM_SHA224_HMAC_GENERAL , "CKM_SHA224_HMAC_GENERAL "}, + {CKM_SHA384 , "CKM_SHA384 "}, + {CKM_SHA384_HMAC , "CKM_SHA384_HMAC "}, + {CKM_SHA384_HMAC_GENERAL , "CKM_SHA384_HMAC_GENERAL "}, + {CKM_SHA512 , "CKM_SHA512 "}, + {CKM_SHA512_HMAC , "CKM_SHA512_HMAC "}, + {CKM_SHA512_HMAC_GENERAL , "CKM_SHA512_HMAC_GENERAL "}, + {CKM_SECURID_KEY_GEN , "CKM_SECURID_KEY_GEN "}, + {CKM_SECURID , "CKM_SECURID "}, + {CKM_HOTP_KEY_GEN , "CKM_HOTP_KEY_GEN "}, + {CKM_HOTP , "CKM_HOTP "}, + {CKM_ACTI , "CKM_ACTI "}, + {CKM_ACTI_KEY_GEN , "CKM_ACTI_KEY_GEN "}, + {CKM_SHA3_256 , "CKM_SHA3_256 "}, + {CKM_SHA3_256_HMAC , "CKM_SHA3_256_HMAC "}, + {CKM_SHA3_256_HMAC_GENERAL , "CKM_SHA3_256_HMAC_GENERAL "}, + {CKM_SHA3_256_KEY_GEN , "CKM_SHA3_256_KEY_GEN "}, + {CKM_SHA3_224 , "CKM_SHA3_224 "}, + {CKM_SHA3_224_HMAC , "CKM_SHA3_224_HMAC "}, + {CKM_SHA3_224_HMAC_GENERAL , "CKM_SHA3_224_HMAC_GENERAL "}, + {CKM_SHA3_224_KEY_GEN , "CKM_SHA3_224_KEY_GEN "}, + {CKM_SHA3_384 , "CKM_SHA3_384 "}, + {CKM_SHA3_384_HMAC , "CKM_SHA3_384_HMAC "}, + {CKM_SHA3_384_HMAC_GENERAL , "CKM_SHA3_384_HMAC_GENERAL "}, + {CKM_SHA3_384_KEY_GEN , "CKM_SHA3_384_KEY_GEN "}, + {CKM_SHA3_512 , "CKM_SHA3_512 "}, + {CKM_SHA3_512_HMAC , "CKM_SHA3_512_HMAC "}, + {CKM_SHA3_512_HMAC_GENERAL , "CKM_SHA3_512_HMAC_GENERAL "}, + {CKM_SHA3_512_KEY_GEN , "CKM_SHA3_512_KEY_GEN "}, + {CKM_CAST_KEY_GEN , "CKM_CAST_KEY_GEN "}, + {CKM_CAST_ECB , "CKM_CAST_ECB "}, + {CKM_CAST_CBC , "CKM_CAST_CBC "}, + {CKM_CAST_MAC , "CKM_CAST_MAC "}, + {CKM_CAST_MAC_GENERAL , "CKM_CAST_MAC_GENERAL "}, + {CKM_CAST_CBC_PAD , "CKM_CAST_CBC_PAD "}, + {CKM_CAST3_KEY_GEN , "CKM_CAST3_KEY_GEN "}, + {CKM_CAST3_ECB , "CKM_CAST3_ECB "}, + {CKM_CAST3_CBC , "CKM_CAST3_CBC "}, + {CKM_CAST3_MAC , "CKM_CAST3_MAC "}, + {CKM_CAST3_MAC_GENERAL , "CKM_CAST3_MAC_GENERAL "}, + {CKM_CAST3_CBC_PAD , "CKM_CAST3_CBC_PAD "}, + {CKM_CAST5_KEY_GEN , "CKM_CAST5_KEY_GEN "}, + {CKM_CAST128_KEY_GEN , "CKM_CAST128_KEY_GEN "}, + {CKM_CAST5_ECB , "CKM_CAST5_ECB "}, + {CKM_CAST128_ECB , "CKM_CAST128_ECB "}, + {CKM_CAST5_CBC , "CKM_CAST5_CBC "}, + {CKM_CAST128_CBC , "CKM_CAST128_CBC "}, + {CKM_CAST5_MAC , "CKM_CAST5_MAC "}, + {CKM_CAST128_MAC , "CKM_CAST128_MAC "}, + {CKM_CAST5_MAC_GENERAL , "CKM_CAST5_MAC_GENERAL "}, + {CKM_CAST128_MAC_GENERAL , "CKM_CAST128_MAC_GENERAL "}, + {CKM_CAST5_CBC_PAD , "CKM_CAST5_CBC_PAD "}, + {CKM_CAST128_CBC_PAD , "CKM_CAST128_CBC_PAD "}, + {CKM_RC5_KEY_GEN , "CKM_RC5_KEY_GEN "}, + {CKM_RC5_ECB , "CKM_RC5_ECB "}, + {CKM_RC5_CBC , "CKM_RC5_CBC "}, + {CKM_RC5_MAC , "CKM_RC5_MAC "}, + {CKM_RC5_MAC_GENERAL , "CKM_RC5_MAC_GENERAL "}, + {CKM_RC5_CBC_PAD , "CKM_RC5_CBC_PAD "}, + {CKM_IDEA_KEY_GEN , "CKM_IDEA_KEY_GEN "}, + {CKM_IDEA_ECB , "CKM_IDEA_ECB "}, + {CKM_IDEA_CBC , "CKM_IDEA_CBC "}, + {CKM_IDEA_MAC , "CKM_IDEA_MAC "}, + {CKM_IDEA_MAC_GENERAL , "CKM_IDEA_MAC_GENERAL "}, + {CKM_IDEA_CBC_PAD , "CKM_IDEA_CBC_PAD "}, + {CKM_GENERIC_SECRET_KEY_GEN , "CKM_GENERIC_SECRET_KEY_GEN "}, + {CKM_CONCATENATE_BASE_AND_KEY , "CKM_CONCATENATE_BASE_AND_KEY "}, + {CKM_CONCATENATE_BASE_AND_DATA , "CKM_CONCATENATE_BASE_AND_DATA "}, + {CKM_CONCATENATE_DATA_AND_BASE , "CKM_CONCATENATE_DATA_AND_BASE "}, + {CKM_XOR_BASE_AND_DATA , "CKM_XOR_BASE_AND_DATA "}, + {CKM_EXTRACT_KEY_FROM_KEY , "CKM_EXTRACT_KEY_FROM_KEY "}, + {CKM_SSL3_PRE_MASTER_KEY_GEN , "CKM_SSL3_PRE_MASTER_KEY_GEN "}, + {CKM_SSL3_MASTER_KEY_DERIVE , "CKM_SSL3_MASTER_KEY_DERIVE "}, + {CKM_SSL3_KEY_AND_MAC_DERIVE , "CKM_SSL3_KEY_AND_MAC_DERIVE "}, + {CKM_SSL3_MASTER_KEY_DERIVE_DH , "CKM_SSL3_MASTER_KEY_DERIVE_DH "}, + {CKM_TLS_PRE_MASTER_KEY_GEN , "CKM_TLS_PRE_MASTER_KEY_GEN "}, + {CKM_TLS_MASTER_KEY_DERIVE , "CKM_TLS_MASTER_KEY_DERIVE "}, + {CKM_TLS_KEY_AND_MAC_DERIVE , "CKM_TLS_KEY_AND_MAC_DERIVE "}, + {CKM_TLS_MASTER_KEY_DERIVE_DH , "CKM_TLS_MASTER_KEY_DERIVE_DH "}, + {CKM_SSL3_MD5_MAC , "CKM_SSL3_MD5_MAC "}, + {CKM_SSL3_SHA1_MAC , "CKM_SSL3_SHA1_MAC "}, + {CKM_MD5_KEY_DERIVATION , "CKM_MD5_KEY_DERIVATION "}, + {CKM_MD2_KEY_DERIVATION , "CKM_MD2_KEY_DERIVATION "}, + {CKM_SHA1_KEY_DERIVATION , "CKM_SHA1_KEY_DERIVATION "}, + {CKM_SHA256_KEY_DERIVATION , "CKM_SHA256_KEY_DERIVATION "}, + {CKM_SHA384_KEY_DERIVATION , "CKM_SHA384_KEY_DERIVATION "}, + {CKM_SHA512_KEY_DERIVATION , "CKM_SHA512_KEY_DERIVATION "}, + {CKM_SHA224_KEY_DERIVATION , "CKM_SHA224_KEY_DERIVATION "}, + {CKM_SHA3_256_KEY_DERIVATION , "CKM_SHA3_256_KEY_DERIVATION "}, + {CKM_SHA3_256_KEY_DERIVE , "CKM_SHA3_256_KEY_DERIVE "}, + {CKM_SHA3_224_KEY_DERIVATION , "CKM_SHA3_224_KEY_DERIVATION "}, + {CKM_SHA3_224_KEY_DERIVE , "CKM_SHA3_224_KEY_DERIVE "}, + {CKM_SHA3_384_KEY_DERIVATION , "CKM_SHA3_384_KEY_DERIVATION "}, + {CKM_SHA3_384_KEY_DERIVE , "CKM_SHA3_384_KEY_DERIVE "}, + {CKM_SHA3_512_KEY_DERIVATION , "CKM_SHA3_512_KEY_DERIVATION "}, + {CKM_SHA3_512_KEY_DERIVE , "CKM_SHA3_512_KEY_DERIVE "}, + {CKM_SHAKE_128_KEY_DERIVATION , "CKM_SHAKE_128_KEY_DERIVATION "}, + {CKM_SHAKE_128_KEY_DERIVE , "CKM_SHAKE_128_KEY_DERIVE "}, + {CKM_SHAKE_256_KEY_DERIVATION , "CKM_SHAKE_256_KEY_DERIVATION "}, + {CKM_SHAKE_256_KEY_DERIVE , "CKM_SHAKE_256_KEY_DERIVE "}, + {CKM_PBE_MD2_DES_CBC , "CKM_PBE_MD2_DES_CBC "}, + {CKM_PBE_MD5_DES_CBC , "CKM_PBE_MD5_DES_CBC "}, + {CKM_PBE_MD5_CAST_CBC , "CKM_PBE_MD5_CAST_CBC "}, + {CKM_PBE_MD5_CAST3_CBC , "CKM_PBE_MD5_CAST3_CBC "}, + {CKM_PBE_MD5_CAST5_CBC , "CKM_PBE_MD5_CAST5_CBC "}, + {CKM_PBE_MD5_CAST128_CBC , "CKM_PBE_MD5_CAST128_CBC "}, + {CKM_PBE_SHA1_CAST5_CBC , "CKM_PBE_SHA1_CAST5_CBC "}, + {CKM_PBE_SHA1_CAST128_CBC , "CKM_PBE_SHA1_CAST128_CBC "}, + {CKM_PBE_SHA1_RC4_128 , "CKM_PBE_SHA1_RC4_128 "}, + {CKM_PBE_SHA1_RC4_40 , "CKM_PBE_SHA1_RC4_40 "}, + {CKM_PBE_SHA1_DES3_EDE_CBC , "CKM_PBE_SHA1_DES3_EDE_CBC "}, + {CKM_PBE_SHA1_DES2_EDE_CBC , "CKM_PBE_SHA1_DES2_EDE_CBC "}, + {CKM_PBE_SHA1_RC2_128_CBC , "CKM_PBE_SHA1_RC2_128_CBC "}, + {CKM_PBE_SHA1_RC2_40_CBC , "CKM_PBE_SHA1_RC2_40_CBC "}, + {CKM_PKCS5_PBKD2 , "CKM_PKCS5_PBKD2 "}, + {CKM_WTLS_PRE_MASTER_KEY_GEN , "CKM_WTLS_PRE_MASTER_KEY_GEN "}, + {CKM_WTLS_MASTER_KEY_DERIVE , "CKM_WTLS_MASTER_KEY_DERIVE "}, + {CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC , "CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC "}, + {CKM_WTLS_PRF , "CKM_WTLS_PRF "}, + {CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE , "CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE "}, + {CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE , "CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE "}, + {CKM_TLS10_MAC_SERVER , "CKM_TLS10_MAC_SERVER "}, + {CKM_TLS10_MAC_CLIENT , "CKM_TLS10_MAC_CLIENT "}, + {CKM_TLS12_MAC , "CKM_TLS12_MAC "}, + {CKM_TLS12_MAC , "CKM_TLS12_MAC "}, + {CKM_TLS12_KDF , "CKM_TLS12_KDF "}, + {CKM_TLS12_MASTER_KEY_DERIVE , "CKM_TLS12_MASTER_KEY_DERIVE "}, + {CKM_TLS12_KEY_AND_MAC_DERIVE , "CKM_TLS12_KEY_AND_MAC_DERIVE "}, + {CKM_TLS12_MASTER_KEY_DERIVE_DH , "CKM_TLS12_MASTER_KEY_DERIVE_DH "}, + {CKM_TLS12_KEY_SAFE_DERIVE , "CKM_TLS12_KEY_SAFE_DERIVE "}, + {CKM_TLS_MAC , "CKM_TLS_MAC "}, + {CKM_TLS_KDF , "CKM_TLS_KDF "}, + {CKM_KEY_WRAP_LYNKS , "CKM_KEY_WRAP_LYNKS "}, + {CKM_KEY_WRAP_SET_OAEP , "CKM_KEY_WRAP_SET_OAEP "}, + {CKM_CMS_SIG , "CKM_CMS_SIG "}, + {CKM_KIP_DERIVE , "CKM_KIP_DERIVE "}, + {CKM_KIP_WRAP , "CKM_KIP_WRAP "}, + {CKM_KIP_MAC , "CKM_KIP_MAC "}, + {CKM_CAMELLIA_KEY_GEN , "CKM_CAMELLIA_KEY_GEN "}, + {CKM_CAMELLIA_ECB , "CKM_CAMELLIA_ECB "}, + {CKM_CAMELLIA_CBC , "CKM_CAMELLIA_CBC "}, + {CKM_CAMELLIA_MAC , "CKM_CAMELLIA_MAC "}, + {CKM_CAMELLIA_MAC_GENERAL , "CKM_CAMELLIA_MAC_GENERAL "}, + {CKM_CAMELLIA_CBC_PAD , "CKM_CAMELLIA_CBC_PAD "}, + {CKM_CAMELLIA_ECB_ENCRYPT_DATA , "CKM_CAMELLIA_ECB_ENCRYPT_DATA "}, + {CKM_CAMELLIA_CBC_ENCRYPT_DATA , "CKM_CAMELLIA_CBC_ENCRYPT_DATA "}, + {CKM_CAMELLIA_CTR , "CKM_CAMELLIA_CTR "}, + {CKM_ARIA_KEY_GEN , "CKM_ARIA_KEY_GEN "}, + {CKM_ARIA_ECB , "CKM_ARIA_ECB "}, + {CKM_ARIA_CBC , "CKM_ARIA_CBC "}, + {CKM_ARIA_MAC , "CKM_ARIA_MAC "}, + {CKM_ARIA_MAC_GENERAL , "CKM_ARIA_MAC_GENERAL "}, + {CKM_ARIA_CBC_PAD , "CKM_ARIA_CBC_PAD "}, + {CKM_ARIA_ECB_ENCRYPT_DATA , "CKM_ARIA_ECB_ENCRYPT_DATA "}, + {CKM_ARIA_CBC_ENCRYPT_DATA , "CKM_ARIA_CBC_ENCRYPT_DATA "}, + {CKM_SKIPJACK_KEY_GEN , "CKM_SKIPJACK_KEY_GEN "}, + {CKM_SKIPJACK_ECB64 , "CKM_SKIPJACK_ECB64 "}, + {CKM_SKIPJACK_CBC64 , "CKM_SKIPJACK_CBC64 "}, + {CKM_SKIPJACK_OFB64 , "CKM_SKIPJACK_OFB64 "}, + {CKM_SKIPJACK_CFB64 , "CKM_SKIPJACK_CFB64 "}, + {CKM_SKIPJACK_CFB32 , "CKM_SKIPJACK_CFB32 "}, + {CKM_SKIPJACK_CFB16 , "CKM_SKIPJACK_CFB16 "}, + {CKM_SKIPJACK_CFB8 , "CKM_SKIPJACK_CFB8 "}, + {CKM_SKIPJACK_WRAP , "CKM_SKIPJACK_WRAP "}, + {CKM_SKIPJACK_PRIVATE_WRAP , "CKM_SKIPJACK_PRIVATE_WRAP "}, + {CKM_SKIPJACK_RELAYX , "CKM_SKIPJACK_RELAYX "}, + {CKM_KEA_KEY_PAIR_GEN , "CKM_KEA_KEY_PAIR_GEN "}, + {CKM_KEA_KEY_DERIVE , "CKM_KEA_KEY_DERIVE "}, + {CKM_FORTEZZA_TIMESTAMP , "CKM_FORTEZZA_TIMESTAMP "}, + {CKM_BATON_KEY_GEN , "CKM_BATON_KEY_GEN "}, + {CKM_BATON_ECB128 , "CKM_BATON_ECB128 "}, + {CKM_BATON_ECB96 , "CKM_BATON_ECB96 "}, + {CKM_BATON_CBC128 , "CKM_BATON_CBC128 "}, + {CKM_BATON_COUNTER , "CKM_BATON_COUNTER "}, + {CKM_BATON_SHUFFLE , "CKM_BATON_SHUFFLE "}, + {CKM_BATON_WRAP , "CKM_BATON_WRAP "}, + {CKM_EC_KEY_PAIR_GEN , "CKM_EC_KEY_PAIR_GEN "}, + {CKM_ECDSA , "CKM_ECDSA "}, + {CKM_ECDSA_SHA1 , "CKM_ECDSA_SHA1 "}, + {CKM_ECDSA_SHA224 , "CKM_ECDSA_SHA224 "}, + {CKM_ECDSA_SHA256 , "CKM_ECDSA_SHA256 "}, + {CKM_ECDSA_SHA384 , "CKM_ECDSA_SHA384 "}, + {CKM_ECDSA_SHA512 , "CKM_ECDSA_SHA512 "}, + {CKM_EC_KEY_PAIR_GEN_W_EXTRA_BITS , "CKM_EC_KEY_PAIR_GEN_W_EXTRA_BITS "}, + {CKM_ECDH1_DERIVE , "CKM_ECDH1_DERIVE "}, + {CKM_ECDH1_COFACTOR_DERIVE , "CKM_ECDH1_COFACTOR_DERIVE "}, + {CKM_ECMQV_DERIVE , "CKM_ECMQV_DERIVE "}, + {CKM_ECDH_AES_KEY_WRAP , "CKM_ECDH_AES_KEY_WRAP "}, + {CKM_RSA_AES_KEY_WRAP , "CKM_RSA_AES_KEY_WRAP "}, + {CKM_JUNIPER_KEY_GEN , "CKM_JUNIPER_KEY_GEN "}, + {CKM_JUNIPER_ECB128 , "CKM_JUNIPER_ECB128 "}, + {CKM_JUNIPER_CBC128 , "CKM_JUNIPER_CBC128 "}, + {CKM_JUNIPER_COUNTER , "CKM_JUNIPER_COUNTER "}, + {CKM_JUNIPER_SHUFFLE , "CKM_JUNIPER_SHUFFLE "}, + {CKM_JUNIPER_WRAP , "CKM_JUNIPER_WRAP "}, + {CKM_FASTHASH , "CKM_FASTHASH "}, + {CKM_AES_XTS , "CKM_AES_XTS "}, + {CKM_AES_XTS_KEY_GEN , "CKM_AES_XTS_KEY_GEN "}, + {CKM_AES_KEY_GEN , "CKM_AES_KEY_GEN "}, + {CKM_AES_ECB , "CKM_AES_ECB "}, + {CKM_AES_CBC , "CKM_AES_CBC "}, + {CKM_AES_MAC , "CKM_AES_MAC "}, + {CKM_AES_MAC_GENERAL , "CKM_AES_MAC_GENERAL "}, + {CKM_AES_CBC_PAD , "CKM_AES_CBC_PAD "}, + {CKM_AES_CTR , "CKM_AES_CTR "}, + {CKM_AES_GCM , "CKM_AES_GCM "}, + {CKM_AES_CCM , "CKM_AES_CCM "}, + {CKM_AES_CMAC , "CKM_AES_CMAC "}, + {CKM_AES_CTS , "CKM_AES_CTS "}, + {CKM_AES_CMAC_GENERAL , "CKM_AES_CMAC_GENERAL "}, + {CKM_AES_XCBC_MAC , "CKM_AES_XCBC_MAC "}, + {CKM_AES_XCBC_MAC_96 , "CKM_AES_XCBC_MAC_96 "}, + {CKM_AES_GMAC , "CKM_AES_GMAC "}, + {CKM_AES_XCBC_MAC_96 , "CKM_AES_XCBC_MAC_96 "}, + {CKM_BLOWFISH_KEY_GEN , "CKM_BLOWFISH_KEY_GEN "}, + {CKM_BLOWFISH_CBC , "CKM_BLOWFISH_CBC "}, + {CKM_TWOFISH_KEY_GEN , "CKM_TWOFISH_KEY_GEN "}, + {CKM_TWOFISH_CBC , "CKM_TWOFISH_CBC "}, + {CKM_BLOWFISH_CBC_PAD , "CKM_BLOWFISH_CBC_PAD "}, + {CKM_TWOFISH_CBC_PAD , "CKM_TWOFISH_CBC_PAD "}, + {CKM_AES_XCBC_MAC_96 , "CKM_AES_XCBC_MAC_96 "}, + {CKM_DES_ECB_ENCRYPT_DATA , "CKM_DES_ECB_ENCRYPT_DATA "}, + {CKM_DES_CBC_ENCRYPT_DATA , "CKM_DES_CBC_ENCRYPT_DATA "}, + {CKM_DES3_ECB_ENCRYPT_DATA , "CKM_DES3_ECB_ENCRYPT_DATA "}, + {CKM_DES3_CBC_ENCRYPT_DATA , "CKM_DES3_CBC_ENCRYPT_DATA "}, + {CKM_AES_ECB_ENCRYPT_DATA , "CKM_AES_ECB_ENCRYPT_DATA "}, + {CKM_AES_CBC_ENCRYPT_DATA , "CKM_AES_CBC_ENCRYPT_DATA "}, + {CKM_GOSTR3410_KEY_PAIR_GEN , "CKM_GOSTR3410_KEY_PAIR_GEN "}, + {CKM_GOSTR3410 , "CKM_GOSTR3410 "}, + {CKM_GOSTR3410_WITH_GOSTR3411 , "CKM_GOSTR3410_WITH_GOSTR3411 "}, + {CKM_GOSTR3410_KEY_WRAP , "CKM_GOSTR3410_KEY_WRAP "}, + {CKM_GOSTR3410_DERIVE , "CKM_GOSTR3410_DERIVE "}, + {CKM_GOSTR3411 , "CKM_GOSTR3411 "}, + {CKM_GOSTR3411_HMAC , "CKM_GOSTR3411_HMAC "}, + {CKM_GOST28147_KEY_GEN , "CKM_GOST28147_KEY_GEN "}, + {CKM_GOST28147_ECB , "CKM_GOST28147_ECB "}, + {CKM_GOST28147 , "CKM_GOST28147 "}, + {CKM_GOST28147_MAC , "CKM_GOST28147_MAC "}, + {CKM_GOST28147_KEY_WRAP , "CKM_GOST28147_KEY_WRAP "}, + {CKM_CHACHA20_KEY_GEN , "CKM_CHACHA20_KEY_GEN "}, + {CKM_CHACHA20 , "CKM_CHACHA20 "}, + {CKM_POLY1305_KEY_GEN , "CKM_POLY1305_KEY_GEN "}, + {CKM_POLY1305 , "CKM_POLY1305 "}, + {CKM_DSA_PARAMETER_GEN , "CKM_DSA_PARAMETER_GEN "}, + {CKM_DH_PKCS_PARAMETER_GEN , "CKM_DH_PKCS_PARAMETER_GEN "}, + {CKM_X9_42_DH_PARAMETER_GEN , "CKM_X9_42_DH_PARAMETER_GEN "}, + {CKM_DSA_PROBABILISTIC_PARAMETER_GEN , "CKM_DSA_PROBABILISTIC_PARAMETER_GEN "}, + {CKM_DSA_SHAWE_TAYLOR_PARAMETER_GEN , "CKM_DSA_SHAWE_TAYLOR_PARAMETER_GEN "}, + {CKM_DSA_FIPS_G_GEN , "CKM_DSA_FIPS_G_GEN "}, + {CKM_AES_OFB , "CKM_AES_OFB "}, + {CKM_AES_CFB64 , "CKM_AES_CFB64 "}, + {CKM_AES_CFB8 , "CKM_AES_CFB8 "}, + {CKM_AES_CFB128 , "CKM_AES_CFB128 "}, + {CKM_AES_CFB1 , "CKM_AES_CFB1 "}, + {CKM_AES_KEY_WRAP , "CKM_AES_KEY_WRAP "}, + {CKM_AES_KEY_WRAP_PAD , "CKM_AES_KEY_WRAP_PAD "}, + {CKM_AES_KEY_WRAP_KWP , "CKM_AES_KEY_WRAP_KWP "}, + {CKM_AES_KEY_WRAP_PKCS7 , "CKM_AES_KEY_WRAP_PKCS7 "}, + {CKM_RSA_PKCS_TPM_1_1 , "CKM_RSA_PKCS_TPM_1_1 "}, + {CKM_RSA_PKCS_OAEP_TPM_1_1 , "CKM_RSA_PKCS_OAEP_TPM_1_1 "}, + {CKM_SHA_1_KEY_GEN , "CKM_SHA_1_KEY_GEN "}, + {CKM_SHA224_KEY_GEN , "CKM_SHA224_KEY_GEN "}, + {CKM_SHA256_KEY_GEN , "CKM_SHA256_KEY_GEN "}, + {CKM_SHA384_KEY_GEN , "CKM_SHA384_KEY_GEN "}, + {CKM_SHA512_KEY_GEN , "CKM_SHA512_KEY_GEN "}, + {CKM_SHA512_224_KEY_GEN , "CKM_SHA512_224_KEY_GEN "}, + {CKM_SHA512_256_KEY_GEN , "CKM_SHA512_256_KEY_GEN "}, + {CKM_SHA512_T_KEY_GEN , "CKM_SHA512_T_KEY_GEN "}, + {CKM_NULL , "CKM_NULL "}, + {CKM_BLAKE2B_160 , "CKM_BLAKE2B_160 "}, + {CKM_BLAKE2B_160_HMAC , "CKM_BLAKE2B_160_HMAC "}, + {CKM_BLAKE2B_160_HMAC_GENERAL , "CKM_BLAKE2B_160_HMAC_GENERAL "}, + {CKM_BLAKE2B_160_KEY_DERIVE , "CKM_BLAKE2B_160_KEY_DERIVE "}, + {CKM_BLAKE2B_160_KEY_GEN , "CKM_BLAKE2B_160_KEY_GEN "}, + {CKM_BLAKE2B_256 , "CKM_BLAKE2B_256 "}, + {CKM_BLAKE2B_256_HMAC , "CKM_BLAKE2B_256_HMAC "}, + {CKM_BLAKE2B_256_HMAC_GENERAL , "CKM_BLAKE2B_256_HMAC_GENERAL "}, + {CKM_BLAKE2B_256_KEY_DERIVE , "CKM_BLAKE2B_256_KEY_DERIVE "}, + {CKM_BLAKE2B_256_KEY_GEN , "CKM_BLAKE2B_256_KEY_GEN "}, + {CKM_BLAKE2B_384 , "CKM_BLAKE2B_384 "}, + {CKM_BLAKE2B_384_HMAC , "CKM_BLAKE2B_384_HMAC "}, + {CKM_BLAKE2B_384_HMAC_GENERAL , "CKM_BLAKE2B_384_HMAC_GENERAL "}, + {CKM_BLAKE2B_384_KEY_DERIVE , "CKM_BLAKE2B_384_KEY_DERIVE "}, + {CKM_BLAKE2B_384_KEY_GEN , "CKM_BLAKE2B_384_KEY_GEN "}, + {CKM_BLAKE2B_512 , "CKM_BLAKE2B_512 "}, + {CKM_BLAKE2B_512_HMAC , "CKM_BLAKE2B_512_HMAC "}, + {CKM_BLAKE2B_512_HMAC_GENERAL , "CKM_BLAKE2B_512_HMAC_GENERAL "}, + {CKM_BLAKE2B_512_KEY_DERIVE , "CKM_BLAKE2B_512_KEY_DERIVE "}, + {CKM_BLAKE2B_512_KEY_GEN , "CKM_BLAKE2B_512_KEY_GEN "}, + {CKM_SALSA20 , "CKM_SALSA20 "}, + {CKM_CHACHA20_POLY1305 , "CKM_CHACHA20_POLY1305 "}, + {CKM_SALSA20_POLY1305 , "CKM_SALSA20_POLY1305 "}, + {CKM_X3DH_INITIALIZE , "CKM_X3DH_INITIALIZE "}, + {CKM_X3DH_RESPOND , "CKM_X3DH_RESPOND "}, + {CKM_X2RATCHET_INITIALIZE , "CKM_X2RATCHET_INITIALIZE "}, + {CKM_X2RATCHET_RESPOND , "CKM_X2RATCHET_RESPOND "}, + {CKM_X2RATCHET_ENCRYPT , "CKM_X2RATCHET_ENCRYPT "}, + {CKM_X2RATCHET_DECRYPT , "CKM_X2RATCHET_DECRYPT "}, + {CKM_XEDDSA , "CKM_XEDDSA "}, + {CKM_HKDF_DERIVE , "CKM_HKDF_DERIVE "}, + {CKM_HKDF_DATA , "CKM_HKDF_DATA "}, + {CKM_HKDF_KEY_GEN , "CKM_HKDF_KEY_GEN "}, + {CKM_SALSA20_KEY_GEN , "CKM_SALSA20_KEY_GEN "}, + {CKM_ECDSA_SHA3_224 , "CKM_ECDSA_SHA3_224 "}, + {CKM_ECDSA_SHA3_256 , "CKM_ECDSA_SHA3_256 "}, + {CKM_ECDSA_SHA3_384 , "CKM_ECDSA_SHA3_384 "}, + {CKM_ECDSA_SHA3_512 , "CKM_ECDSA_SHA3_512 "}, + {CKM_EC_EDWARDS_KEY_PAIR_GEN , "CKM_EC_EDWARDS_KEY_PAIR_GEN "}, + {CKM_EC_MONTGOMERY_KEY_PAIR_GEN , "CKM_EC_MONTGOMERY_KEY_PAIR_GEN "}, + {CKM_EDDSA , "CKM_EDDSA "}, + {CKM_SP800_108_COUNTER_KDF , "CKM_SP800_108_COUNTER_KDF "}, + {CKM_SP800_108_FEEDBACK_KDF , "CKM_SP800_108_FEEDBACK_KDF "}, + {CKM_SP800_108_DOUBLE_PIPELINE_KDF , "CKM_SP800_108_DOUBLE_PIPELINE_KDF "}, + {CKM_IKE2_PRF_PLUS_DERIVE , "CKM_IKE2_PRF_PLUS_DERIVE "}, + {CKM_IKE_PRF_DERIVE , "CKM_IKE_PRF_DERIVE "}, + {CKM_IKE1_PRF_DERIVE , "CKM_IKE1_PRF_DERIVE "}, + {CKM_IKE1_EXTENDED_DERIVE , "CKM_IKE1_EXTENDED_DERIVE "}, + {CKM_HSS_KEY_PAIR_GEN , "CKM_HSS_KEY_PAIR_GEN "}, + {CKM_HSS , "CKM_HSS "}, + {CKM_XMSS_KEY_PAIR_GEN , "CKM_XMSS_KEY_PAIR_GEN "}, + {CKM_XMSSMT_KEY_PAIR_GEN , "CKM_XMSSMT_KEY_PAIR_GEN "}, + {CKM_XMSS , "CKM_XMSS "}, + {CKM_XMSSMT , "CKM_XMSSMT "}, + {CKM_ECDH_X_AES_KEY_WRAP , "CKM_ECDH_X_AES_KEY_WRAP "}, + {CKM_ECDH_COF_AES_KEY_WRAP , "CKM_ECDH_COF_AES_KEY_WRAP "}, + {CKM_PUB_KEY_FROM_PRIV_KEY , "CKM_PUB_KEY_FROM_PRIV_KEY "}, + {CKM_XMSS , "CKM_XMSS "}, + {CKM_XMSS , "CKM_XMSS "}, + {CKM_XMSS , "CKM_XMSS "}, + {CKM_VENDOR_DEFINED , "CKM_VENDOR_DEFINED "} +}; + +static enum_specs ck_mgf_s[] = { + { CKG_MGF1_SHA1 , "CKG_MGF1_SHA1 " }, + { CKG_MGF1_SHA224 , "CKG_MGF1_SHA224 " }, + { CKG_MGF1_SHA256 , "CKG_MGF1_SHA256 " }, + { CKG_MGF1_SHA384 , "CKG_MGF1_SHA384 " }, + { CKG_MGF1_SHA512 , "CKG_MGF1_SHA512 " }, + { CKG_MGF1_SHA3_224, "CKG_MGF1_SHA3_224" }, + { CKG_MGF1_SHA3_256, "CKG_MGF1_SHA3_256" }, + { CKG_MGF1_SHA3_384, "CKG_MGF1_SHA3_384" }, + { CKG_MGF1_SHA3_512, "CKG_MGF1_SHA3_512" }, +}; + +static enum_specs ck_generate_s[] = { + {CKG_NO_GENERATE , "CKG_NO_GENERATE "}, + {CKG_GENERATE , "CKG_GENERATE "}, + {CKG_GENERATE_COUNTER , "CKG_GENERATE_COUNTER "}, + {CKG_GENERATE_RANDOM , "CKG_GENERATE_RANDOM "}, + {CKG_GENERATE_COUNTER_XOR, "CKG_GENERATE_COUNTER_XOR"}, +}; + +static enum_specs ck_hw_s[] = { + {CKH_MONOTONIC_COUNTER, "CKH_MONOTONIC_COUNTER"}, + {CKH_CLOCK , "CKH_CLOCK "}, + {CKH_USER_INTERFACE , "CKH_USER_INTERFACE "} +}; + +static enum_specs ck_hg_s[] = { + {CKH_HEDGE_PREFERRED , "CKH_HEDGE_PREFERRED "}, + {CKH_HEDGE_REQUIRED , "CKH_HEDGE_REQUIRED "}, + {CKH_DETERMINISTIC_REQUIRED, "CKH_DETERMINISTIC_REQUIRED"} +}; + +static enum_specs ck_not_s[] = { + {CKN_SURRENDER , "CKN_SURRENDER "}, + {CKN_OTP_CHANGED, "CKN_OTP_CHANGED "} }; static enum_specs ck_err_s[] = { - { CKR_OK, "CKR_OK" }, - { CKR_CANCEL, "CKR_CANCEL" }, - { CKR_HOST_MEMORY, "CKR_HOST_MEMORY" }, - { CKR_SLOT_ID_INVALID, "CKR_SLOT_ID_INVALID" }, - { CKR_GENERAL_ERROR, "CKR_GENERAL_ERROR" }, - { CKR_FUNCTION_FAILED, "CKR_FUNCTION_FAILED" }, - { CKR_ARGUMENTS_BAD, "CKR_ARGUMENTS_BAD" }, - { CKR_NO_EVENT, "CKR_NO_EVENT" }, - { CKR_NEED_TO_CREATE_THREADS, "CKR_NEED_TO_CREATE_THREADS" }, - { CKR_CANT_LOCK, "CKR_CANT_LOCK" }, - { CKR_ATTRIBUTE_READ_ONLY, "CKR_ATTRIBUTE_READ_ONLY" }, - { CKR_ATTRIBUTE_SENSITIVE, "CKR_ATTRIBUTE_SENSITIVE" }, - { CKR_ATTRIBUTE_TYPE_INVALID, "CKR_ATTRIBUTE_TYPE_INVALID" }, - { CKR_ATTRIBUTE_VALUE_INVALID, "CKR_ATTRIBUTE_VALUE_INVALID" }, - { CKR_DATA_INVALID, "CKR_DATA_INVALID" }, - { CKR_DATA_LEN_RANGE, "CKR_DATA_LEN_RANGE" }, - { CKR_DEVICE_ERROR, "CKR_DEVICE_ERROR" }, - { CKR_DEVICE_MEMORY, "CKR_DEVICE_MEMORY" }, - { CKR_DEVICE_REMOVED, "CKR_DEVICE_REMOVED" }, - { CKR_ENCRYPTED_DATA_INVALID, "CKR_ENCRYPTED_DATA_INVALID" }, - { CKR_ENCRYPTED_DATA_LEN_RANGE, "CKR_ENCRYPTED_DATA_LEN_RANGE" }, - { CKR_FUNCTION_CANCELED, "CKR_FUNCTION_CANCELED" }, - { CKR_FUNCTION_NOT_PARALLEL, "CKR_FUNCTION_NOT_PARALLEL" }, - { CKR_FUNCTION_NOT_SUPPORTED, "CKR_FUNCTION_NOT_SUPPORTED" }, - { CKR_KEY_HANDLE_INVALID, "CKR_KEY_HANDLE_INVALID" }, - { CKR_KEY_SIZE_RANGE, "CKR_KEY_SIZE_RANGE" }, - { CKR_KEY_TYPE_INCONSISTENT, "CKR_KEY_TYPE_INCONSISTENT" }, - { CKR_KEY_NOT_NEEDED, "CKR_KEY_NOT_NEEDED" }, - { CKR_KEY_CHANGED, "CKR_KEY_CHANGED" }, - { CKR_KEY_NEEDED, "CKR_KEY_NEEDED" }, - { CKR_KEY_INDIGESTIBLE, "CKR_KEY_INDIGESTIBLE" }, - { CKR_KEY_FUNCTION_NOT_PERMITTED, "CKR_KEY_FUNCTION_NOT_PERMITTED" }, - { CKR_KEY_NOT_WRAPPABLE, "CKR_KEY_NOT_WRAPPABLE" }, - { CKR_KEY_UNEXTRACTABLE, "CKR_KEY_UNEXTRACTABLE" }, - { CKR_MECHANISM_INVALID, "CKR_MECHANISM_INVALID" }, - { CKR_MECHANISM_PARAM_INVALID, "CKR_MECHANISM_PARAM_INVALID" }, - { CKR_OBJECT_HANDLE_INVALID, "CKR_OBJECT_HANDLE_INVALID" }, - { CKR_OPERATION_ACTIVE, "CKR_OPERATION_ACTIVE" }, - { CKR_OPERATION_NOT_INITIALIZED, "CKR_OPERATION_NOT_INITIALIZED" }, - { CKR_PIN_INCORRECT, "CKR_PIN_INCORRECT" }, - { CKR_PIN_INVALID, "CKR_PIN_INVALID" }, - { CKR_PIN_LEN_RANGE, "CKR_PIN_LEN_RANGE" }, - { CKR_PIN_EXPIRED, "CKR_PIN_EXPIRED" }, - { CKR_PIN_LOCKED, "CKR_PIN_LOCKED" }, - { CKR_SESSION_CLOSED, "CKR_SESSION_CLOSED" }, - { CKR_SESSION_COUNT, "CKR_SESSION_COUNT" }, - { CKR_SESSION_HANDLE_INVALID, "CKR_SESSION_HANDLE_INVALID" }, - { CKR_SESSION_PARALLEL_NOT_SUPPORTED, "CKR_SESSION_PARALLEL_NOT_SUPPORTED" }, - { CKR_SESSION_READ_ONLY, "CKR_SESSION_READ_ONLY" }, - { CKR_SESSION_EXISTS, "CKR_SESSION_EXISTS" }, - { CKR_SESSION_READ_ONLY_EXISTS, "CKR_SESSION_READ_ONLY_EXISTS" }, - { CKR_SESSION_READ_WRITE_SO_EXISTS, "CKR_SESSION_READ_WRITE_SO_EXISTS" }, - { CKR_SIGNATURE_INVALID, "CKR_SIGNATURE_INVALID" }, - { CKR_SIGNATURE_LEN_RANGE, "CKR_SIGNATURE_LEN_RANGE" }, - { CKR_TEMPLATE_INCOMPLETE, "CKR_TEMPLATE_INCOMPLETE" }, - { CKR_TEMPLATE_INCONSISTENT, "CKR_TEMPLATE_INCONSISTENT" }, - { CKR_TOKEN_NOT_PRESENT, "CKR_TOKEN_NOT_PRESENT" }, - { CKR_TOKEN_NOT_RECOGNIZED, "CKR_TOKEN_NOT_RECOGNIZED" }, - { CKR_TOKEN_WRITE_PROTECTED, "CKR_TOKEN_WRITE_PROTECTED" }, - { CKR_UNWRAPPING_KEY_HANDLE_INVALID, "CKR_UNWRAPPING_KEY_HANDLE_INVALID" }, - { CKR_UNWRAPPING_KEY_SIZE_RANGE, "CKR_UNWRAPPING_KEY_SIZE_RANGE" }, - { CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT, "CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT" }, - { CKR_USER_ALREADY_LOGGED_IN, "CKR_USER_ALREADY_LOGGED_IN" }, - { CKR_USER_NOT_LOGGED_IN, "CKR_USER_NOT_LOGGED_IN" }, - { CKR_USER_PIN_NOT_INITIALIZED, "CKR_USER_PIN_NOT_INITIALIZED" }, - { CKR_USER_TYPE_INVALID, "CKR_USER_TYPE_INVALID" }, - { CKR_USER_ANOTHER_ALREADY_LOGGED_IN, "CKR_USER_ANOTHER_ALREADY_LOGGED_IN" }, - { CKR_USER_TOO_MANY_TYPES, "CKR_USER_TOO_MANY_TYPES" }, - { CKR_WRAPPED_KEY_INVALID, "CKR_WRAPPED_KEY_INVALID" }, - { CKR_WRAPPED_KEY_LEN_RANGE, "CKR_WRAPPED_KEY_LEN_RANGE" }, - { CKR_WRAPPING_KEY_HANDLE_INVALID, "CKR_WRAPPING_KEY_HANDLE_INVALID" }, - { CKR_WRAPPING_KEY_SIZE_RANGE, "CKR_WRAPPING_KEY_SIZE_RANGE" }, - { CKR_WRAPPING_KEY_TYPE_INCONSISTENT, "CKR_WRAPPING_KEY_TYPE_INCONSISTENT" }, - { CKR_RANDOM_SEED_NOT_SUPPORTED, "CKR_RANDOM_SEED_NOT_SUPPORTED" }, - { CKR_RANDOM_NO_RNG, "CKR_RANDOM_NO_RNG" }, - { CKR_DOMAIN_PARAMS_INVALID, "CKR_DOMAIN_PARAMS_INVALID" }, - { CKR_BUFFER_TOO_SMALL, "CKR_BUFFER_TOO_SMALL" }, - { CKR_SAVED_STATE_INVALID, "CKR_SAVED_STATE_INVALID" }, - { CKR_INFORMATION_SENSITIVE, "CKR_INFORMATION_SENSITIVE" }, - { CKR_STATE_UNSAVEABLE, "CKR_STATE_UNSAVEABLE" }, - { CKR_CRYPTOKI_NOT_INITIALIZED, "CKR_CRYPTOKI_NOT_INITIALIZED" }, - { CKR_CRYPTOKI_ALREADY_INITIALIZED, "CKR_CRYPTOKI_ALREADY_INITIALIZED" }, - { CKR_MUTEX_BAD, "CKR_MUTEX_BAD" }, - { CKR_MUTEX_NOT_LOCKED, "CKR_MUTEX_NOT_LOCKED" }, - { CKR_VENDOR_DEFINED, "CKR_VENDOR_DEFINED" } + {CKR_OK, "CKR_OK"}, + {CKR_CANCEL, "CKR_CANCEL"}, + {CKR_HOST_MEMORY, "CKR_HOST_MEMORY"}, + {CKR_SLOT_ID_INVALID, "CKR_SLOT_ID_INVALID"}, + {CKR_GENERAL_ERROR, "CKR_GENERAL_ERROR"}, + {CKR_FUNCTION_FAILED, "CKR_FUNCTION_FAILED"}, + {CKR_ARGUMENTS_BAD, "CKR_ARGUMENTS_BAD"}, + {CKR_NO_EVENT, "CKR_NO_EVENT"}, + {CKR_NEED_TO_CREATE_THREADS, "CKR_NEED_TO_CREATE_THREADS"}, + {CKR_CANT_LOCK, "CKR_CANT_LOCK"}, + {CKR_ATTRIBUTE_READ_ONLY, "CKR_ATTRIBUTE_READ_ONLY"}, + {CKR_ATTRIBUTE_SENSITIVE, "CKR_ATTRIBUTE_SENSITIVE"}, + {CKR_ATTRIBUTE_TYPE_INVALID, "CKR_ATTRIBUTE_TYPE_INVALID"}, + {CKR_ATTRIBUTE_VALUE_INVALID, "CKR_ATTRIBUTE_VALUE_INVALID"}, + {CKR_ACTION_PROHIBITED, "CKR_ACTION_PROHIBITED"}, + {CKR_DATA_INVALID, "CKR_DATA_INVALID"}, + {CKR_DATA_LEN_RANGE, "CKR_DATA_LEN_RANGE"}, + {CKR_DEVICE_ERROR, "CKR_DEVICE_ERROR"}, + {CKR_DEVICE_MEMORY, "CKR_DEVICE_MEMORY"}, + {CKR_DEVICE_REMOVED, "CKR_DEVICE_REMOVED"}, + {CKR_ENCRYPTED_DATA_INVALID, "CKR_ENCRYPTED_DATA_INVALID"}, + {CKR_ENCRYPTED_DATA_LEN_RANGE, "CKR_ENCRYPTED_DATA_LEN_RANGE"}, + {CKR_AEAD_DECRYPT_FAILED, "CKR_AEAD_DECRYPT_FAILED"}, + {CKR_FUNCTION_CANCELED, "CKR_FUNCTION_CANCELED"}, + {CKR_FUNCTION_NOT_PARALLEL, "CKR_FUNCTION_NOT_PARALLEL"}, + {CKR_FUNCTION_NOT_SUPPORTED, "CKR_FUNCTION_NOT_SUPPORTED"}, + {CKR_KEY_HANDLE_INVALID, "CKR_KEY_HANDLE_INVALID"}, + {CKR_KEY_SIZE_RANGE, "CKR_KEY_SIZE_RANGE"}, + {CKR_KEY_TYPE_INCONSISTENT, "CKR_KEY_TYPE_INCONSISTENT"}, + {CKR_KEY_NOT_NEEDED, "CKR_KEY_NOT_NEEDED"}, + {CKR_KEY_CHANGED, "CKR_KEY_CHANGED"}, + {CKR_KEY_NEEDED, "CKR_KEY_NEEDED"}, + {CKR_KEY_INDIGESTIBLE, "CKR_KEY_INDIGESTIBLE"}, + {CKR_KEY_FUNCTION_NOT_PERMITTED, "CKR_KEY_FUNCTION_NOT_PERMITTED"}, + {CKR_KEY_NOT_WRAPPABLE, "CKR_KEY_NOT_WRAPPABLE"}, + {CKR_KEY_UNEXTRACTABLE, "CKR_KEY_UNEXTRACTABLE"}, + {CKR_MECHANISM_INVALID, "CKR_MECHANISM_INVALID"}, + {CKR_MECHANISM_PARAM_INVALID, "CKR_MECHANISM_PARAM_INVALID"}, + {CKR_OBJECT_HANDLE_INVALID, "CKR_OBJECT_HANDLE_INVALID"}, + {CKR_OPERATION_ACTIVE, "CKR_OPERATION_ACTIVE"}, + {CKR_OPERATION_NOT_INITIALIZED, "CKR_OPERATION_NOT_INITIALIZED"}, + {CKR_PIN_INCORRECT, "CKR_PIN_INCORRECT"}, + {CKR_PIN_INVALID, "CKR_PIN_INVALID"}, + {CKR_PIN_LEN_RANGE, "CKR_PIN_LEN_RANGE"}, + {CKR_PIN_EXPIRED, "CKR_PIN_EXPIRED"}, + {CKR_PIN_LOCKED, "CKR_PIN_LOCKED"}, + {CKR_SESSION_CLOSED, "CKR_SESSION_CLOSED"}, + {CKR_SESSION_COUNT, "CKR_SESSION_COUNT"}, + {CKR_SESSION_HANDLE_INVALID, "CKR_SESSION_HANDLE_INVALID"}, + {CKR_SESSION_PARALLEL_NOT_SUPPORTED, "CKR_SESSION_PARALLEL_NOT_SUPPORTED"}, + {CKR_SESSION_READ_ONLY, "CKR_SESSION_READ_ONLY"}, + {CKR_SESSION_EXISTS, "CKR_SESSION_EXISTS"}, + {CKR_SESSION_READ_ONLY_EXISTS, "CKR_SESSION_READ_ONLY_EXISTS"}, + {CKR_SESSION_READ_WRITE_SO_EXISTS, "CKR_SESSION_READ_WRITE_SO_EXISTS"}, + {CKR_SIGNATURE_INVALID, "CKR_SIGNATURE_INVALID"}, + {CKR_SIGNATURE_LEN_RANGE, "CKR_SIGNATURE_LEN_RANGE"}, + {CKR_TEMPLATE_INCOMPLETE, "CKR_TEMPLATE_INCOMPLETE"}, + {CKR_TEMPLATE_INCONSISTENT, "CKR_TEMPLATE_INCONSISTENT"}, + {CKR_TOKEN_NOT_PRESENT, "CKR_TOKEN_NOT_PRESENT"}, + {CKR_TOKEN_NOT_RECOGNIZED, "CKR_TOKEN_NOT_RECOGNIZED"}, + {CKR_TOKEN_WRITE_PROTECTED, "CKR_TOKEN_WRITE_PROTECTED"}, + {CKR_UNWRAPPING_KEY_HANDLE_INVALID, "CKR_UNWRAPPING_KEY_HANDLE_INVALID"}, + {CKR_UNWRAPPING_KEY_SIZE_RANGE, "CKR_UNWRAPPING_KEY_SIZE_RANGE"}, + {CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT, "CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT"}, + {CKR_USER_ALREADY_LOGGED_IN, "CKR_USER_ALREADY_LOGGED_IN"}, + {CKR_USER_NOT_LOGGED_IN, "CKR_USER_NOT_LOGGED_IN"}, + {CKR_USER_PIN_NOT_INITIALIZED, "CKR_USER_PIN_NOT_INITIALIZED"}, + {CKR_USER_TYPE_INVALID, "CKR_USER_TYPE_INVALID"}, + {CKR_USER_ANOTHER_ALREADY_LOGGED_IN, "CKR_USER_ANOTHER_ALREADY_LOGGED_IN"}, + {CKR_USER_TOO_MANY_TYPES, "CKR_USER_TOO_MANY_TYPES"}, + {CKR_WRAPPED_KEY_INVALID, "CKR_WRAPPED_KEY_INVALID"}, + {CKR_WRAPPED_KEY_LEN_RANGE, "CKR_WRAPPED_KEY_LEN_RANGE"}, + {CKR_WRAPPING_KEY_HANDLE_INVALID, "CKR_WRAPPING_KEY_HANDLE_INVALID"}, + {CKR_WRAPPING_KEY_SIZE_RANGE, "CKR_WRAPPING_KEY_SIZE_RANGE"}, + {CKR_WRAPPING_KEY_TYPE_INCONSISTENT, "CKR_WRAPPING_KEY_TYPE_INCONSISTENT"}, + {CKR_RANDOM_SEED_NOT_SUPPORTED, "CKR_RANDOM_SEED_NOT_SUPPORTED"}, + {CKR_RANDOM_NO_RNG, "CKR_RANDOM_NO_RNG"}, + {CKR_DOMAIN_PARAMS_INVALID, "CKR_DOMAIN_PARAMS_INVALID"}, + {CKR_CURVE_NOT_SUPPORTED, "CKR_CURVE_NOT_SUPPORTED"}, + {CKR_BUFFER_TOO_SMALL, "CKR_BUFFER_TOO_SMALL"}, + {CKR_SAVED_STATE_INVALID, "CKR_SAVED_STATE_INVALID"}, + {CKR_INFORMATION_SENSITIVE, "CKR_INFORMATION_SENSITIVE"}, + {CKR_STATE_UNSAVEABLE, "CKR_STATE_UNSAVEABLE"}, + {CKR_CRYPTOKI_NOT_INITIALIZED, "CKR_CRYPTOKI_NOT_INITIALIZED"}, + {CKR_CRYPTOKI_ALREADY_INITIALIZED, "CKR_CRYPTOKI_ALREADY_INITIALIZED"}, + {CKR_MUTEX_BAD, "CKR_MUTEX_BAD"}, + {CKR_MUTEX_NOT_LOCKED, "CKR_MUTEX_NOT_LOCKED"}, + {CKR_NEW_PIN_MODE, "CKR_NEW_PIN_MODE"}, + {CKR_NEXT_OTP, "CKR_NEXT_OTP"}, + {CKR_EXCEEDED_MAX_ITERATIONS, "CKR_EXCEEDED_MAX_ITERATIONS"}, + {CKR_FIPS_SELF_TEST_FAILED, "CKR_FIPS_SELF_TEST_FAILED"}, + {CKR_LIBRARY_LOAD_FAILED, "CKR_LIBRARY_LOAD_FAILED"}, + {CKR_PIN_TOO_WEAK, "CKR_PIN_TOO_WEAK"}, + {CKR_PUBLIC_KEY_INVALID, "CKR_PUBLIC_KEY_INVALID"}, + {CKR_FUNCTION_REJECTED, "CKR_FUNCTION_REJECTED"}, + {CKR_TOKEN_RESOURCE_EXCEEDED, "CKR_TOKEN_RESOURCE_EXCEEDED"}, + {CKR_OPERATION_CANCEL_FAILED, "CKR_OPERATION_CANCEL_FAILED"}, + {CKR_KEY_EXHAUSTED, "CKR_KEY_EXHAUSTED"}, + {CKR_PENDING, "CKR_PENDING"}, + {CKR_SESSION_ASYNC_NOT_SUPPORTED, "CKR_SESSION_ASYNC_NOT_SUPPORTED"}, + {CKR_SEED_RANDOM_REQUIRED, "CKR_SEED_RANDOM_REQUIRED"}, + {CKR_OPERATION_NOT_VALIDATED, "CKR_OPERATION_NOT_VALIDATED"}, + {CKR_TOKEN_NOT_INITIALIZED, "CKR_TOKEN_NOT_INITIALIZED"}, + {CKR_PARAMETER_SET_NOT_SUPPORTED, "CKR_PARAMETER_SET_NOT_SUPPORTED"}, + {CKR_VENDOR_DEFINED, "CKR_VENDOR_DEFINED"} }; static enum_specs ck_usr_s[] = { - { CKU_SO, "CKU_SO" }, - { CKU_USER, "CKU_USER" }, - { CKU_CONTEXT_SPECIFIC, "CKU_CONTEXT_SPECIFIC" } + {CKU_SO, "CKU_SO"}, + {CKU_USER, "CKU_USER"}, + {CKU_CONTEXT_SPECIFIC, "CKU_CONTEXT_SPECIFIC"} }; static enum_specs ck_sta_s[] = { - { CKS_RO_PUBLIC_SESSION, "CKS_RO_PUBLIC_SESSION" }, - { CKS_RO_USER_FUNCTIONS, "CKS_RO_USER_FUNCTIONS" }, - { CKS_RW_PUBLIC_SESSION, "CKS_RW_PUBLIC_SESSION" }, - { CKS_RW_USER_FUNCTIONS, "CKS_RW_USER_FUNCTIONS" }, - { CKS_RW_SO_FUNCTIONS, "CKS_RW_SO_FUNCTIONS" } + {CKS_RO_PUBLIC_SESSION, "CKS_RO_PUBLIC_SESSION"}, + {CKS_RO_USER_FUNCTIONS, "CKS_RO_USER_FUNCTIONS"}, + {CKS_RW_PUBLIC_SESSION, "CKS_RW_PUBLIC_SESSION"}, + {CKS_RW_USER_FUNCTIONS, "CKS_RW_USER_FUNCTIONS"}, + {CKS_RW_SO_FUNCTIONS, "CKS_RW_SO_FUNCTIONS"} }; -#define SZ_SPECS sizeof(enum_specs) +static enum_specs ck_tru_s[] = { + {CKT_TRUST_UNKNOWN, "CKT_TRUST_UNKNOWN"}, + {CKT_TRUSTED, "CKT_TRUSTED"}, + {CKT_TRUST_ANCHOR, "CKT_TRUST_ANCHOR"}, + {CKT_NOT_TRUSTED, "CKT_NOT_TRUSTED"}, + {CKT_TRUST_MUST_VERIFY_TRUST, "CKT_TRUST_MUST_VERIFY_TRUST"} +}; + +static enum_specs ck_auth_s[] = { + {CKV_AUTHORITY_TYPE_UNSPECIFIED, "CKV_AUTHORITY_TYPE_UNSPECIFIED"}, + {CKV_AUTHORITY_TYPE_NIST_CMVP, "CKV_AUTHORITY_TYPE_NIST_CMVP"}, + {CKV_AUTHORITY_TYPE_COMMON_CRITERIA, "CKV_AUTHORITY_TYPE_COMMON_CRITERIA"} +}; + +static enum_specs ck_val_s[] = { + {CKV_TYPE_UNSPECIFIED, "CKV_TYPE_UNSPECIFIED"}, + {CKV_TYPE_SOFTWARE, "CKV_TYPE_SOFTWARE"}, + {CKV_TYPE_HARDWARE, "CKV_TYPE_HARDWARE"}, + {CKV_TYPE_FIRMWARE, "CKV_TYPE_FIRMWARE"}, + {CKV_TYPE_HYBRID, "CKV_TYPE_HYBRID"} +}; + +static enum_specs ck_ckd_s[] = { + {CKD_NULL, "CKD_NULL"}, + {CKD_SHA1_KDF, "CKD_SHA1_KDF"}, + {CKD_SHA1_KDF_ASN1, "CKD_SHA1_KDF_ASN1"}, + {CKD_SHA1_KDF_CONCATENATE, "CKD_SHA1_KDF_CONCATENATE"}, + {CKD_SHA224_KDF, "CKD_SHA224_KDF"}, + {CKD_SHA256_KDF, "CKD_SHA256_KDF"}, + {CKD_SHA384_KDF, "CKD_SHA384_KDF"}, + {CKD_SHA512_KDF, "CKD_SHA512_KDF"}, + {CKD_CPDIVERSIFY_KDF, "CKD_CPDIVERSIFY_KDF"}, + {CKD_SHA3_224_KDF, "CKD_SHA3_224_KDF"}, + {CKD_SHA3_256_KDF, "CKD_SHA3_256_KDF"}, + {CKD_SHA3_384_KDF, "CKD_SHA3_384_KDF"}, + {CKD_SHA3_512_KDF, "CKD_SHA3_512_KDF"}, + {CKD_SHA1_KDF_SP800, "CKD_SHA1_KDF_SP800"}, + {CKD_SHA224_KDF_SP800, "CKD_SHA224_KDF_SP800"}, + {CKD_SHA256_KDF_SP800, "CKD_SHA256_KDF_SP800"}, + {CKD_SHA384_KDF_SP800, "CKD_SHA384_KDF_SP800"}, + {CKD_SHA512_KDF_SP800, "CKD_SHA512_KDF_SP800"}, + {CKD_SHA3_224_KDF_SP800, "CKD_SHA3_224_KDF_SP800"}, + {CKD_SHA3_256_KDF_SP800, "CKD_SHA3_256_KDF_SP800"}, + {CKD_SHA3_384_KDF_SP800, "CKD_SHA3_384_KDF_SP800"}, + {CKD_SHA3_512_KDF_SP800, "CKD_SHA3_512_KDF_SP800"}, + {CKD_BLAKE2B_160_KDF, "CKD_BLAKE2B_160_KDF"}, + {CKD_BLAKE2B_256_KDF, "CKD_BLAKE2B_256_KDF"}, + {CKD_BLAKE2B_384_KDF, "CKD_BLAKE2B_384_KDF"}, + {CKD_BLAKE2B_512_KDF, "CKD_BLAKE2B_512_KDF"} +}; enum_spec ck_types[] = { - { OBJ_T, ck_cls_s, sizeof(ck_cls_s) / SZ_SPECS, "CK_OBJECT_CLASS" }, - { KEY_T, ck_key_s, sizeof(ck_key_s) / SZ_SPECS, "CK_KEY_TYPE" }, - { CRT_T, ck_crt_s, sizeof(ck_crt_s) / SZ_SPECS, "CK_CERTIFICATE_TYPE" }, - { MEC_T, ck_mec_s, sizeof(ck_mec_s) / SZ_SPECS, "CK_MECHANISM_TYPE" }, - { USR_T, ck_usr_s, sizeof(ck_usr_s) / SZ_SPECS, "CK_USER_TYPE" }, - { STA_T, ck_sta_s, sizeof(ck_sta_s) / SZ_SPECS, "CK_STATE" }, - { RV_T, ck_err_s, sizeof(ck_err_s) / SZ_SPECS, "CK_RV" }, + {OBJ_T, ck_cls_s, sizeof(ck_cls_s) / SZ_SPECS, "CK_OBJECT_CLASS" }, + {PROFILE_T, ck_profile_s, sizeof(ck_profile_s)/SZ_SPECS, "CK_PROFILE" }, + {KEY_T, ck_key_s, sizeof(ck_key_s) / SZ_SPECS, "CK_KEY_TYPE" }, + {CRT_T, ck_crt_s, sizeof(ck_crt_s) / SZ_SPECS, "CK_CERTIFICATE_TYPE" }, + {MEC_T, ck_mec_s, sizeof(ck_mec_s) / SZ_SPECS, "CK_MECHANISM_TYPE" }, + {MGF_T, ck_mgf_s, sizeof(ck_mgf_s) / SZ_SPECS, "CK_RSA_PKCS_MGF_TYPE" }, + {GENERATE_T, ck_generate_s, sizeof(ck_generate_s) / SZ_SPECS, "CK_GENERATOR_FUNCTION" }, + {USR_T, ck_usr_s, sizeof(ck_usr_s) / SZ_SPECS, "CK_USER_TYPE" }, + {STA_T, ck_sta_s, sizeof(ck_sta_s) / SZ_SPECS, "CK_STATE" }, + {CKD_T, ck_ckd_s, sizeof(ck_ckd_s) / SZ_SPECS, "CK_EC_KDF_TYPE" }, + {RV_T, ck_err_s, sizeof(ck_err_s) / SZ_SPECS, "CK_RV" }, + {HW_T, ck_hw_s, sizeof(ck_hw_s) / SZ_SPECS, "CK_HW_FEATURE_TYPE" }, + {HG_T, ck_hg_s, sizeof(ck_hg_s) / SZ_SPECS, "CK_HEDGE_TYPE" }, + {NOT_T, ck_not_s, sizeof(ck_not_s) / SZ_SPECS, "CK_NOTIFICATION" }, + /* CKP PBKD2, ML-DSA, ML_KEM, SLH-DSA missing*/ + {TRU_T, ck_tru_s, sizeof(ck_tru_s) / SZ_SPECS, "CK_TRUST" }, + {AUTH_T, ck_auth_s, sizeof(ck_auth_s) / SZ_SPECS, "CK_VALIDATION_AUTHORITY_TYPE" }, + {VAL_T, ck_val_s, sizeof(ck_val_s) / SZ_SPECS, "CK_VALIDATION_TYPE" }, + {ML_DSA_T, ck_mldsa_s, sizeof(ck_mldsa_s) / SZ_SPECS, "CK_ML_DSA_PARAMETER_SET_TYPE" }, + {ML_KEM_T, ck_mlkem_s, sizeof(ck_mlkem_s) / SZ_SPECS, "CK_ML_KEM_PARAMETER_SET_TYPE" }, + {SLH_DSA_T, ck_slh_dsa_s, sizeof(ck_slh_dsa_s) / SZ_SPECS, "CK_SLH_DSA_PARAMETER_SET_TYPE"}, }; + + static enum_spec ck_key_t[] = { { KEY_T, ck_key_s, sizeof(ck_key_s) / SZ_SPECS, "CK_KEY_TYPE" } }; static enum_spec ck_cls_t[] = { { OBJ_T, ck_cls_s, sizeof(ck_cls_s) / SZ_SPECS, "CK_OBJECT_CLASS" } }; static enum_spec ck_crt_t[] = { { CRT_T, ck_crt_s, sizeof(ck_crt_s) / SZ_SPECS, "CK_CERTIFICATE_TYPE" } }; +static enum_spec ck_profile_t[] = { { PROFILE_T, ck_profile_s, sizeof(ck_profile_s) / SZ_SPECS, "CK_PROFILE" } }; type_spec ck_attribute_specs[] = { { CKA_CLASS , "CKA_CLASS ", print_enum, ck_cls_t }, @@ -675,6 +1161,7 @@ type_spec ck_attribute_specs[] = { { CKA_SUBJECT , "CKA_SUBJECT ", print_generic, NULL }, #endif { CKA_ID , "CKA_ID ", print_generic, NULL }, + { CKA_UNIQUE_ID , "CKA_UNIQUE_ID ", print_generic, NULL }, { CKA_SENSITIVE , "CKA_SENSITIVE ", print_boolean, NULL }, { CKA_ENCRYPT , "CKA_ENCRYPT ", print_boolean, NULL }, { CKA_DECRYPT , "CKA_DECRYPT ", print_boolean, NULL }, @@ -696,6 +1183,7 @@ type_spec ck_attribute_specs[] = { { CKA_EXPONENT_1 , "CKA_EXPONENT_1 ", print_generic, NULL }, { CKA_EXPONENT_2 , "CKA_EXPONENT_2 ", print_generic, NULL }, { CKA_COEFFICIENT , "CKA_COEFFICIENT ", print_generic, NULL }, + { CKA_PUBLIC_KEY_INFO , "CKA_PUBLIC_KEY_INFO ", print_generic, NULL }, { CKA_PRIME , "CKA_PRIME ", print_generic, NULL }, { CKA_SUBPRIME , "CKA_SUBPRIME ", print_generic, NULL }, { CKA_BASE , "CKA_BASE ", print_generic, NULL }, @@ -709,15 +1197,28 @@ type_spec ck_attribute_specs[] = { { CKA_ALWAYS_SENSITIVE , "CKA_ALWAYS_SENSITIVE ", print_boolean, NULL }, { CKA_KEY_GEN_MECHANISM , "CKA_KEY_GEN_MECHANISM", print_boolean, NULL }, { CKA_MODIFIABLE , "CKA_MODIFIABLE ", print_boolean, NULL }, - { CKA_ECDSA_PARAMS , "CKA_ECDSA_PARAMS ", print_generic, NULL }, + { CKA_COPYABLE , "CKA_COPYABLE ", print_boolean, NULL }, { CKA_EC_PARAMS , "CKA_EC_PARAMS ", print_generic, NULL }, + { CKA_ECDSA_PARAMS , "CKA_ECDSA_PARAMS ", print_generic, NULL }, { CKA_EC_POINT , "CKA_EC_POINT ", print_generic, NULL }, { CKA_SECONDARY_AUTH , "CKA_SECONDARY_AUTH ", print_generic, NULL }, { CKA_AUTH_PIN_FLAGS , "CKA_AUTH_PIN_FLAGS ", print_generic, NULL }, { CKA_ALWAYS_AUTHENTICATE, "CKA_ALWAYS_AUTHENTICATE ", print_boolean, NULL }, { CKA_WRAP_WITH_TRUSTED , "CKA_WRAP_WITH_TRUSTED ", print_generic, NULL }, - { CKA_WRAP_TEMPLATE , "CKA_WRAP_TEMPLATE ", print_generic, NULL }, - { CKA_UNWRAP_TEMPLATE , "CKA_UNWRAP_TEMPLATE ", print_generic, NULL }, + { CKA_OTP_FORMAT , "CKA_OTP_FORMAT ", print_generic, NULL }, + { CKA_OTP_LENGTH , "CKA_OTP_LENGTH ", print_generic, NULL }, + { CKA_OTP_TIME_INTERVAL , "CKA_OTP_TIME_INTERVAL ", print_generic, NULL }, + { CKA_OTP_USER_FRIENDLY_MODE, "CKA_OTP_USER_FRIENDLY_MODE ", print_boolean, NULL }, + { CKA_OTP_CHALLENGE_REQUIREMENT, "CKA_OTP_CHALLENGE_REQUIREMENT ", print_generic, NULL }, + { CKA_OTP_TIME_REQUIREMENT, "CKA_OTP_TIME_REQUIREMENT ", print_generic, NULL }, + { CKA_OTP_COUNTER_REQUIREMENT, "CKA_OTP_COUNTER_REQUIREMENT ", print_generic, NULL }, + { CKA_OTP_PIN_REQUIREMENT, "CKA_OTP_PIN_REQUIREMENT ", print_generic, NULL }, + { CKA_OTP_COUNTER , "CKA_OTP_COUNTER ", print_generic, NULL }, + { CKA_OTP_TIME , "CKA_OTP_TIME ", print_print, NULL }, + { CKA_OTP_USER_IDENTIFIER, "CKA_OTP_USER_IDENTIFIER ", print_print, NULL }, + { CKA_OTP_SERVICE_IDENTIFIER, "CKA_OTP_SERVICE_IDENTIFIER ", print_print, NULL }, + { CKA_OTP_SERVICE_LOGO , "CKA_OTP_SERVICE_LOGO ", print_generic, NULL }, + { CKA_OTP_SERVICE_LOGO_TYPE, "CKA_OTP_SERVICE_LOGO_TYPE ", print_print, NULL }, { CKA_GOSTR3410_PARAMS , "CKA_GOSTR3410_PARAMS ", print_generic, NULL }, { CKA_GOSTR3411_PARAMS , "CKA_GOSTR3411_PARAMS ", print_generic, NULL }, { CKA_GOST28147_PARAMS , "CKA_GOST28147_PARAMS ", print_generic, NULL }, @@ -735,40 +1236,98 @@ type_spec ck_attribute_specs[] = { { CKA_ENCODING_METHODS , "CKA_ENCODING_METHODS ", print_generic, NULL }, { CKA_MIME_TYPES , "CKA_MIME_TYPES ", print_generic, NULL }, { CKA_MECHANISM_TYPE , "CKA_MECHANISM_TYPE ", print_generic, NULL }, + { CKA_PROFILE_ID , "CKA_PROFILE_ID ", print_enum, ck_profile_t }, { CKA_REQUIRED_CMS_ATTRIBUTES, "CKA_REQUIRED_CMS_ATTRIBUTES ", print_generic, NULL }, { CKA_DEFAULT_CMS_ATTRIBUTES, "CKA_DEFAULT_CMS_ATTRIBUTES ", print_generic, NULL }, { CKA_SUPPORTED_CMS_ATTRIBUTES, "CKA_SUPPORTED_CMS_ATTRIBUTES ", print_generic, NULL }, + + // NEW in PKCS#11 v3.2 + { CKA_X2RATCHET_BAG , "CKA_X2RATCHET_BAG ", print_generic, NULL }, + { CKA_X2RATCHET_BAGSIZE , "CKA_X2RATCHET_BAGSIZE ", print_generic, NULL }, + { CKA_X2RATCHET_BOBS1STMSG, "CKA_X2RATCHET_BOBS1STMSG ", print_generic, NULL }, + { CKA_X2RATCHET_CKR , "CKA_X2RATCHET_CKR ", print_generic, NULL }, + { CKA_X2RATCHET_CKS , "CKA_X2RATCHET_CKS " , print_generic, NULL }, + { CKA_X2RATCHET_DHP , "CKA_X2RATCHET_DHP " , print_generic, NULL }, + { CKA_X2RATCHET_DHR , "CKA_X2RATCHET_DHR " , print_generic, NULL }, + { CKA_X2RATCHET_DHS , "CKA_X2RATCHET_DHS " , print_generic, NULL }, + { CKA_X2RATCHET_HKR , "CKA_X2RATCHET_HKR ", print_generic, NULL }, + { CKA_X2RATCHET_HKS , "CKA_X2RATCHET_HKS ", print_generic, NULL }, + { CKA_X2RATCHET_ISALICE , "CKA_X2RATCHET_ISALICE ", print_generic, NULL }, + { CKA_X2RATCHET_NHKR , "CKA_X2RATCHET_NHKR ", print_generic, NULL }, + { CKA_X2RATCHET_NHKS , "CKA_X2RATCHET_NHKS ", print_generic, NULL }, + { CKA_X2RATCHET_NR , "CKA_X2RATCHET_NR ", print_generic, NULL }, + { CKA_X2RATCHET_NS , "CKA_X2RATCHET_NS ", print_generic, NULL }, + { CKA_X2RATCHET_PNS , "CKA_X2RATCHET_PNS ", print_generic, NULL }, + { CKA_X2RATCHET_RK , "CKA_X2RATCHET_BAGSIZE ", print_generic, NULL }, + { CKA_HSS_LEVELS , "CKA_HSS_LEVELS ", print_generic, NULL }, + { CKA_HSS_LMS_TYPE , "CKA_HSS_LMS_TYPE ", print_generic, NULL }, + { CKA_HSS_LMOTS_TYPE , "CKA_HSS_LMOTS_TYPE ", print_generic, NULL }, + { CKA_HSS_LMS_TYPES , "CKA_HSS_LMS_TYPES ", print_generic, NULL }, + { CKA_HSS_LMOTS_TYPES , "CKA_HSS_LMOTS_TYPES ", print_generic, NULL }, + { CKA_HSS_KEYS_REMAINING, "CKA_HSS_KEYS_REMAINING ", print_generic, NULL }, + { CKA_PARAMETER_SET , "CKA_PARAMETER_SET ", print_parameter_set, NULL }, + { CKA_OBJECT_VALIDATION_FLAGS , "CKA_OBJECT_VALIDATION_FLAGS ", print_generic, NULL }, + { CKA_VALIDATION_TYPE , "CKA_VALIDATION_TYPE ", print_generic, NULL }, + { CKA_VALIDATION_VERSION , "CKA_VALIDATION_VERSION ", print_generic, NULL }, + { CKA_VALIDATION_LEVEL , "CKA_VALIDATION_LEVEL ", print_generic, NULL }, + { CKA_VALIDATION_MODULE_ID , "CKA_VALIDATION_MODULE_ID ", print_generic, NULL }, + { CKA_VALIDATION_FLAG , "CKA_VALIDATION_FLAG ", print_generic, NULL }, + { CKA_VALIDATION_AUTHORITY_TYPE , "CKA_VALIDATION_AUTHORITY_TYPE ", print_generic, NULL }, + { CKA_VALIDATION_COUNTRY , "CKA_VALIDATION_COUNTRY ", print_generic, NULL }, + { CKA_VALIDATION_CERTIFICATE_IDENTIFIER , "CKA_VALIDATION_CERTIFICATE_IDENTIFIER ", print_generic, NULL }, + { CKA_VALIDATION_CERTIFICATE_URI , "CKA_VALIDATION_CERTIFICATE_URI ", print_generic, NULL }, + { CKA_VALIDATION_VENDOR_URI , "CKA_VALIDATION_VENDOR_URI ", print_generic, NULL }, + { CKA_VALIDATION_PROFILE , "CKA_VALIDATION_PROFILE ", print_generic, NULL }, + { CKA_OBJECT_VALIDATION_FLAGS , "CKA_OBJECT_VALIDATION_FLAGS ", print_generic, NULL }, + { CKA_ENCAPSULATE_TEMPLATE , "CKA_ENCAPSULATE_TEMPLATE ", print_generic, NULL }, + { CKA_DECAPSULATE_TEMPLATE , "CKA_DECAPSULATE_TEMPLATE ", print_generic, NULL }, + { CKA_TRUST_SERVER_AUTH , "CKA_TRUST_SERVER_AUTH ", print_generic, NULL }, + { CKA_TRUST_CLIENT_AUTH , "CKA_TRUST_CLIENT_AUTH ", print_generic, NULL }, + { CKA_TRUST_CODE_SIGNING , "CKA_TRUST_CODE_SIGNING ", print_generic, NULL }, + { CKA_TRUST_EMAIL_PROTECTION , "CKA_TRUST_EMAIL_PROTECTION ", print_generic, NULL }, + { CKA_TRUST_IPSEC_IKE , "CKA_TRUST_IPSEC_IKE ", print_generic, NULL }, + { CKA_TRUST_TIME_STAMPING , "CKA_TRUST_TIME_STAMPING ", print_generic, NULL }, + { CKA_TRUST_OCSP_SIGNING , "CKA_TRUST_OCSP_SIGNING ", print_generic, NULL }, + { CKA_ENCAPSULATE , "CKA_ENCAPSULATE ", print_boolean, NULL }, + { CKA_DECAPSULATE , "CKA_DECAPSULATE ", print_boolean, NULL }, + { CKA_HASH_OF_CERTIFICATE , "CKA_HASH_OF_CERTIFICATE ", print_generic, NULL }, + { CKA_PUBLIC_CRC64_VALUE , "CKA_PUBLIC_CRC64_VALUE ", print_generic, NULL }, + { CKA_SEED , "CKA_SEED ", print_generic, NULL }, + { CKA_WRAP_TEMPLATE , "CKA_WRAP_TEMPLATE ", print_generic, NULL }, + { CKA_UNWRAP_TEMPLATE , "CKA_UNWRAP_TEMPLATE ", print_generic, NULL }, + { CKA_DERIVE_TEMPLATE , "CKA_DERIVE_TEMPLATE ", print_generic, NULL }, { CKA_ALLOWED_MECHANISMS, "CKA_ALLOWED_MECHANISMS ", print_generic, NULL }, - { CKA_NETSCAPE_URL, "CKA_NETSCAPE_URL(Netsc) ", print_generic, NULL }, - { CKA_NETSCAPE_EMAIL, "CKA_NETSCAPE_EMAIL(Netsc) ", print_generic, NULL }, - { CKA_NETSCAPE_SMIME_INFO, "CKA_NETSCAPE_SMIME_INFO(Netsc) ", print_boolean, NULL }, - { CKA_NETSCAPE_SMIME_TIMESTAMP, "CKA_NETSCAPE_SMIME_TIMESTAMP(Netsc) ", print_generic, NULL }, - { CKA_NETSCAPE_PKCS8_SALT, "CKA_NETSCAPE_PKCS8_SALT(Netsc) ", print_generic, NULL }, - { CKA_NETSCAPE_PASSWORD_CHECK, "CKA_NETSCAPE_PASSWORD_CHECK(Netsc) ", print_generic, NULL }, - { CKA_NETSCAPE_EXPIRES, "CKA_NETSCAPE_EXPIRES(Netsc) ", print_generic, NULL }, - { CKA_NETSCAPE_KRL, "CKA_NETSCAPE_KRL(Netsc) ", print_generic, NULL }, - { CKA_NETSCAPE_PQG_COUNTER, "CKA_NETSCAPE_PQG_COUNTER(Netsc) ", print_generic, NULL }, - { CKA_NETSCAPE_PQG_SEED, "CKA_NETSCAPE_PQG_SEED(Netsc) ", print_generic, NULL }, - { CKA_NETSCAPE_PQG_H, "CKA_NETSCAPE_PQG_H(Netsc) ", print_generic, NULL }, - { CKA_NETSCAPE_PQG_SEED_BITS, "CKA_NETSCAPE_PQG_SEED_BITS(Netsc) ", print_generic, NULL }, - { CKA_TRUST_DIGITAL_SIGNATURE, "CKA_TRUST_DIGITAL_SIGNATURE(Netsc) ", print_boolean, NULL }, - { CKA_TRUST_NON_REPUDIATION, "CKA_TRUST_NON_REPUDIATION(Netsc) ", print_boolean, NULL }, - { CKA_TRUST_KEY_ENCIPHERMENT, "CKA_TRUST_KEY_ENCIPHERMENT(Netsc) ", print_boolean, NULL }, - { CKA_TRUST_DATA_ENCIPHERMENT, "CKA_TRUST_DATA_ENCIPHERMENT(Netsc) ", print_boolean, NULL }, - { CKA_TRUST_KEY_AGREEMENT, "CKA_TRUST_KEY_AGREEMENT(Netsc) ", print_boolean, NULL }, - { CKA_TRUST_KEY_CERT_SIGN, "CKA_TRUST_KEY_CERT_SIGN(Netsc) ", print_boolean, NULL }, - { CKA_TRUST_CRL_SIGN, "CKA_TRUST_CRL_SIGN(Netsc) ", print_boolean, NULL }, - { CKA_TRUST_SERVER_AUTH, "CKA_TRUST_SERVER_AUTH(Netsc) ", print_boolean, NULL }, - { CKA_TRUST_CLIENT_AUTH, "CKA_TRUST_CLIENT_AUTH(Netsc) ", print_boolean, NULL }, - { CKA_TRUST_CODE_SIGNING, "CKA_TRUST_CODE_SIGNING(Netsc) ", print_boolean, NULL }, - { CKA_TRUST_EMAIL_PROTECTION, "CKA_TRUST_EMAIL_PROTECTION(Netsc) ", print_boolean, NULL }, - { CKA_TRUST_IPSEC_END_SYSTEM, "CKA_TRUST_IPSEC_END_SYSTEM(Netsc) ", print_boolean, NULL }, - { CKA_TRUST_IPSEC_TUNNEL, "CKA_TRUST_IPSEC_TUNNEL(Netsc) ", print_boolean, NULL }, - { CKA_TRUST_IPSEC_USER, "CKA_TRUST_IPSEC_USER(Netsc) ", print_boolean, NULL }, - { CKA_TRUST_TIME_STAMPING, "CKA_TRUST_TIME_STAMPING(Netsc) ", print_boolean, NULL }, - { CKA_CERT_SHA1_HASH, "CKA_CERT_SHA1_HASH(Netsc) ", print_generic, NULL }, - { CKA_CERT_MD5_HASH, "CKA_CERT_MD5_HASH(Netsc) ", print_generic, NULL }, + { CKA_NSS_URL, "CKA_NSS_URL(NSS) ", print_generic, NULL }, + { CKA_NSS_EMAIL, "CKA_NSS_EMAIL(NSS) ", print_generic, NULL }, + { CKA_NSS_SMIME_INFO, "CKA_NSS_SMIME_INFO(NSS) ", print_boolean, NULL }, + { CKA_NSS_SMIME_TIMESTAMP, "CKA_NSS_SMIME_TIMESTAMP(NSS) ", print_generic, NULL }, + { CKA_NSS_PKCS8_SALT, "CKA_NSS_PKCS8_SALT(NSS) ", print_generic, NULL }, + { CKA_NSS_PASSWORD_CHECK, "CKA_NSS_PASSWORD_CHECK(NSS) ", print_generic, NULL }, + { CKA_NSS_EXPIRES, "CKA_NSS_EXPIRES(NSS) ", print_generic, NULL }, + { CKA_NSS_KRL, "CKA_NSS_KRL(NSS) ", print_generic, NULL }, + { CKA_NSS_PQG_COUNTER, "CKA_NSS_PQG_COUNTER(NSS) ", print_generic, NULL }, + { CKA_NSS_PQG_SEED, "CKA_NSS_PQG_SEED(NSS) ", print_generic, NULL }, + { CKA_NSS_PQG_H, "CKA_NSS_PQG_H(NSS) ", print_generic, NULL }, + { CKA_NSS_PQG_SEED_BITS, "CKA_NSS_PQG_SEED_BITS(NSS) ", print_generic, NULL }, + { CKA_NSS_TRUST_DIGITAL_SIGNATURE, "CKA_NSS_TRUST_DIGITAL_SIGNATURE(NSS) ", print_boolean, NULL }, + { CKA_NSS_TRUST_NON_REPUDIATION, "CKA_TRUST_NON_REPUDIATION(NSS) ", print_boolean, NULL }, + { CKA_NSS_TRUST_KEY_ENCIPHERMENT, "CKA_TRUST_KEY_ENCIPHERMENT(NSS) ", print_boolean, NULL }, + { CKA_NSS_TRUST_DATA_ENCIPHERMENT, "CKA_TRUST_DATA_ENCIPHERMENT(NSS) ", print_boolean, NULL }, + { CKA_NSS_TRUST_KEY_AGREEMENT, "CKA_TRUST_KEY_AGREEMENT(NSS) ", print_boolean, NULL }, + { CKA_NSS_TRUST_KEY_CERT_SIGN, "CKA_TRUST_KEY_CERT_SIGN(NSS) ", print_boolean, NULL }, + { CKA_NSS_TRUST_CRL_SIGN, "CKA_TRUST_CRL_SIGN(NSS) ", print_boolean, NULL }, + { CKA_NSS_TRUST_SERVER_AUTH, "CKA_TRUST_SERVER_AUTH(NSS) ", print_boolean, NULL }, + { CKA_NSS_TRUST_CLIENT_AUTH, "CKA_TRUST_CLIENT_AUTH(NSS) ", print_boolean, NULL }, + { CKA_NSS_TRUST_CODE_SIGNING, "CKA_TRUST_CODE_SIGNING(NSS) ", print_boolean, NULL }, + { CKA_NSS_TRUST_EMAIL_PROTECTION, "CKA_TRUST_EMAIL_PROTECTION(NSS) ", print_boolean, NULL }, + { CKA_NSS_TRUST_IPSEC_END_SYSTEM, "CKA_TRUST_IPSEC_END_SYSTEM(NSS) ", print_boolean, NULL }, + { CKA_NSS_TRUST_IPSEC_TUNNEL, "CKA_TRUST_IPSEC_TUNNEL(NSS) ", print_boolean, NULL }, + { CKA_NSS_TRUST_IPSEC_USER, "CKA_TRUST_IPSEC_USER(NSS) ", print_boolean, NULL }, + { CKA_NSS_TRUST_TIME_STAMPING, "CKA_TRUST_TIME_STAMPING(NSS) ", print_boolean, NULL }, + { CKA_NSS_CERT_SHA1_HASH, "CKA_CERT_SHA1_HASH(NSS) ", print_generic, NULL }, + { CKA_NSS_CERT_MD5_HASH, "CKA_CERT_MD5_HASH(NSS) ", print_generic, NULL }, }; +// clang-format on CK_ULONG ck_attribute_num = sizeof(ck_attribute_specs)/sizeof(type_spec); @@ -800,7 +1359,7 @@ lookup_enum(CK_ULONG type, CK_ULONG value) void show_error( FILE *f, char *str, CK_RV rc ) { - fprintf(f, "%s returned: %ld %s", str, (unsigned long) rc, lookup_enum ( RV_T, rc )); + fprintf(f, "%s returned: %ld %s", str, (unsigned long) rc, lookup_enum (RV_T, rc )); fprintf(f, "\n"); } @@ -876,7 +1435,10 @@ print_token_info(FILE *f, CK_TOKEN_INFO *info) { CKF_SO_PIN_COUNT_LOW , "CKF_SO_PIN_COUNT_LOW " }, { CKF_SO_PIN_FINAL_TRY , "CKF_SO_PIN_FINAL_TRY " }, { CKF_SO_PIN_LOCKED , "CKF_SO_PIN_LOCKED " }, - { CKF_SO_PIN_TO_BE_CHANGED , "CKF_SO_PIN_TO_BE_CHANGED " } + { CKF_SO_PIN_TO_BE_CHANGED , "CKF_SO_PIN_TO_BE_CHANGED " }, + { CKF_ERROR_STATE , "CKF_ERROR_STATE " }, + { CKF_SEED_RANDOM_REQUIRED , "CKF_SEED_RANDOM_REQUIRED " }, + { CKF_ASYNC_SESSION_SUPPORTED , "CKF_ASYNC_SESSION_SUPPORTED " }, }; fprintf(f, " label: '%32.32s'\n", info->label ); @@ -928,11 +1490,14 @@ void print_mech_info(FILE *f, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR minfo) { const char *name = lookup_enum(MEC_T, type); - CK_ULONG known_flags = CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_DIGEST | - CKF_SIGN | CKF_SIGN_RECOVER | CKF_VERIFY | CKF_VERIFY_RECOVER | - CKF_GENERATE | CKF_GENERATE_KEY_PAIR | CKF_WRAP | CKF_UNWRAP | - CKF_DERIVE | CKF_EC_F_P | CKF_EC_F_2M |CKF_EC_ECPARAMETERS | - CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS | CKF_EC_COMPRESS; + CK_ULONG known_flags = CKF_HW | CKF_MESSAGE_ENCRYPT | CKF_MESSAGE_DECRYPT | + CKF_MESSAGE_SIGN | CKF_MESSAGE_VERIFY | CKF_MULTI_MESSAGE | + CKF_FIND_OBJECTS | CKF_ENCRYPT | CKF_DECRYPT | CKF_DIGEST | + CKF_SIGN | CKF_SIGN_RECOVER | CKF_VERIFY | CKF_VERIFY_RECOVER | + CKF_GENERATE | CKF_GENERATE_KEY_PAIR | CKF_WRAP | CKF_UNWRAP | + CKF_DERIVE | CKF_EC_F_P | CKF_EC_F_2M | CKF_EC_ECPARAMETERS | + CKF_EC_OID | CKF_EC_UNCOMPRESS | CKF_EC_CURVENAME | + CKF_EC_NAMEDCURVE | CKF_ENCAPSULATE | CKF_DECAPSULATE; if (name) fprintf(f, "%s : ", name); @@ -942,27 +1507,36 @@ print_mech_info(FILE *f, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR minfo) fprintf(f, "min:%lu max:%lu flags:0x%lX ", (unsigned long) minfo->ulMinKeySize, (unsigned long) minfo->ulMaxKeySize, minfo->flags); - fprintf(f, "( %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n", - (minfo->flags & CKF_HW) ? "Hardware " : "", - (minfo->flags & CKF_ENCRYPT) ? "Encrypt " : "", - (minfo->flags & CKF_DECRYPT) ? "Decrypt " : "", - (minfo->flags & CKF_DIGEST) ? "Digest " : "", - (minfo->flags & CKF_SIGN) ? "Sign " : "", - (minfo->flags & CKF_SIGN_RECOVER) ? "SigRecov " : "", - (minfo->flags & CKF_VERIFY) ? "Verify " : "", - (minfo->flags & CKF_VERIFY_RECOVER) ? "VerRecov " : "", - (minfo->flags & CKF_GENERATE) ? "Generate " : "", - (minfo->flags & CKF_GENERATE_KEY_PAIR) ? "KeyPair " : "", - (minfo->flags & CKF_WRAP) ? "Wrap " : "", - (minfo->flags & CKF_UNWRAP) ? "Unwrap " : "", - (minfo->flags & CKF_DERIVE) ? "Derive " : "", - (minfo->flags & CKF_EC_F_P) ? "F(P) " : "", - (minfo->flags & CKF_EC_F_2M) ? "F(2^M) " : "", - (minfo->flags & CKF_EC_ECPARAMETERS) ? "EcParams " : "", - (minfo->flags & CKF_EC_NAMEDCURVE) ? "NamedCurve " : "", - (minfo->flags & CKF_EC_UNCOMPRESS) ? "Uncompress " : "", - (minfo->flags & CKF_EC_COMPRESS) ? "Compress " : "", - (minfo->flags & ~known_flags) ? "Unknown " : ""); + fprintf(f, "( %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n", + (minfo->flags & CKF_HW) ? "Hardware " : "", + (minfo->flags & CKF_MESSAGE_ENCRYPT) ? "MsgEncrypt " : "", + (minfo->flags & CKF_MESSAGE_DECRYPT) ? "MsgDencrypt " : "", + (minfo->flags & CKF_MESSAGE_SIGN) ? "MsgSign " : "", + (minfo->flags & CKF_MESSAGE_VERIFY) ? "MsgVerify " : "", + (minfo->flags & CKF_MULTI_MESSAGE) ? "MultiMsg " : "", + (minfo->flags & CKF_ENCRYPT) ? "Encrypt " : "", + (minfo->flags & CKF_DECRYPT) ? "Decrypt " : "", + (minfo->flags & CKF_DIGEST) ? "Digest " : "", + (minfo->flags & CKF_SIGN) ? "Sign " : "", + (minfo->flags & CKF_SIGN_RECOVER) ? "SigRecov " : "", + (minfo->flags & CKF_VERIFY) ? "Verify " : "", + (minfo->flags & CKF_VERIFY_RECOVER) ? "VerRecov " : "", + (minfo->flags & CKF_GENERATE) ? "Generate " : "", + (minfo->flags & CKF_GENERATE_KEY_PAIR) ? "KeyPair " : "", + (minfo->flags & CKF_WRAP) ? "Wrap " : "", + (minfo->flags & CKF_UNWRAP) ? "Unwrap " : "", + (minfo->flags & CKF_DERIVE) ? "Derive " : "", + (minfo->flags & CKF_EC_F_P) ? "F(P) " : "", + (minfo->flags & CKF_EC_F_2M) ? "F(2^M) " : "", + (minfo->flags & CKF_EC_ECPARAMETERS) ? "EcParams " : "", + (minfo->flags & CKF_EC_OID) ? "OID " : "", + (minfo->flags & CKF_EC_UNCOMPRESS) ? "Uncompress " : "", + (minfo->flags & CKF_EC_COMPRESS) ? "Compress " : "", + (minfo->flags & CKF_EC_CURVENAME) ? "CurveName " : "", + (minfo->flags & CKF_EC_NAMEDCURVE) ? "NamedCurve " : "", + (minfo->flags & CKF_ENCAPSULATE) ? "Encapsulate " : "", + (minfo->flags & CKF_DECAPSULATE) ? "Decapsulate " : "", + (minfo->flags & ~known_flags) ? "Unknown " : ""); } @@ -970,19 +1544,33 @@ void print_attribute_list(FILE *f, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { CK_ULONG j, k; + CK_KEY_TYPE key_type = -1; int found; + if (!pTemplate) + return; + + /* Some attributes are key type specific -- first check if we have a key type + * in the template and if so, store it */ + for (j = 0; j < ulCount; j++) { + if (pTemplate[j].type == CKA_KEY_TYPE && pTemplate[j].pValue) { + key_type = *(CK_KEY_TYPE *)pTemplate[j].pValue; + break; + } + } + for(j = 0; j < ulCount ; j++) { found = 0; for(k = 0; k < ck_attribute_num; k++) { if(ck_attribute_specs[k].type == pTemplate[j].type) { found = 1; fprintf(f, " %s ", ck_attribute_specs[k].name); - if(pTemplate[j].pValue && ((CK_LONG) pTemplate[j].ulValueLen) > 0) { - ck_attribute_specs[k].display - (f, pTemplate[j].type, pTemplate[j].pValue, - pTemplate[j].ulValueLen, - ck_attribute_specs[k].arg); + if(pTemplate[j].pValue && ((long)pTemplate[j].ulValueLen) > 0) { + ck_attribute_specs[k].display( + f, pTemplate[j].type, pTemplate[j].pValue, + pTemplate[j].ulValueLen, + ck_attribute_specs[k].arg, + key_type); } else { fprintf(f, "%s\n", buf_spec(pTemplate[j].pValue, pTemplate[j].ulValueLen)); } @@ -1003,6 +1591,9 @@ print_attribute_list_req(FILE *f, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) CK_ULONG j, k; int found; + if (!pTemplate) + return; + for(j = 0; j < ulCount ; j++) { found = 0; for(k = 0; k < ck_attribute_num; k++) { @@ -1027,12 +1618,13 @@ print_session_info(FILE *f, CK_SESSION_INFO *info) { size_t i; enum_specs ck_flags[] = { - { CKF_RW_SESSION , "CKF_RW_SESSION " }, - { CKF_SERIAL_SESSION , "CKF_SERIAL_SESSION " } + {CKF_RW_SESSION , "CKF_RW_SESSION "}, + {CKF_SERIAL_SESSION , "CKF_SERIAL_SESSION "}, + {CKF_ASYNC_SESSION , "CKF_ASYNC_SESSION "}, }; fprintf(f, " slotID: %ld\n", info->slotID ); - fprintf(f, " state: '%32.32s'\n", lookup_enum(STA_T, info->state)); + fprintf(f, " state: %0lx (%32.32s)\n", info->state, lookup_enum(STA_T, info->state)); fprintf(f, " flags: %0lx\n", info->flags ); for(i = 0; i < sizeof (ck_flags) / sizeof (*ck_flags); i++) { @@ -1041,3 +1633,24 @@ print_session_info(FILE *f, CK_SESSION_INFO *info) } fprintf(f, " ulDeviceError: %0lx\n", info->ulDeviceError ); } + + +void +print_interfaces_list(FILE *f, CK_INTERFACE_PTR pInterfacesList, CK_ULONG ulCount) +{ + CK_ULONG i; + + if (pInterfacesList) { + for (i = 0; i < ulCount; i++) { + CK_INTERFACE_PTR in = &pInterfacesList[i]; + fprintf(f, "Interface '%s' version=%d.%d flags=%lx\n", + in->pInterfaceName, + ((CK_VERSION *)(in->pFunctionList))->major, + ((CK_VERSION *)(in->pFunctionList))->minor, + in->flags); + } + } + else { + fprintf(f, "Count is %ld\n", ulCount); + } +} diff --git a/src/pkcs11/pkcs11-display.h b/src/pkcs11/pkcs11-display.h index 0f7bc5fb38..a0433b99f7 100644 --- a/src/pkcs11/pkcs11-display.h +++ b/src/pkcs11/pkcs11-display.h @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA */ @@ -29,8 +29,7 @@ extern "C" { #endif -typedef void (display_func) \ - (FILE *, CK_LONG, CK_VOID_PTR, CK_ULONG, CK_VOID_PTR); +typedef void(display_func) (FILE *, long, CK_VOID_PTR, CK_ULONG, CK_VOID_PTR, CK_KEY_TYPE); typedef struct { CK_ULONG type; @@ -51,22 +50,35 @@ typedef struct { void * arg; } type_spec; -enum ck_type{ - OBJ_T, - KEY_T, - CRT_T, - MEC_T, - USR_T, - STA_T, - RV_T +enum ck_type { + OBJ_T, + PROFILE_T, + KEY_T, + CRT_T, + MEC_T, + MGF_T, + GENERATE_T, + USR_T, + STA_T, + CKD_T, + RV_T, + HW_T, + HG_T, + NOT_T, + TRU_T, + AUTH_T, + VAL_T, + ML_DSA_T, + ML_KEM_T, + SLH_DSA_T, }; const char *lookup_enum_spec(enum_spec *spec, CK_ULONG value); const char *lookup_enum(CK_ULONG type, CK_ULONG value); -void print_enum (FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg); -void print_boolean (FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg); -void print_generic (FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg); -void print_print (FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg); +void print_enum (FILE *f, long type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg, CK_KEY_TYPE key_type); +void print_boolean (FILE *f, long type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg, CK_KEY_TYPE key_type); +void print_generic (FILE *f, long type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg, CK_KEY_TYPE key_type); +void print_print (FILE *f, long type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg, CK_KEY_TYPE key_type); void show_error (FILE *f, char *str, CK_RV rc); void print_ck_info(FILE *f, CK_INFO *info); @@ -82,6 +94,7 @@ void print_attribute_list(FILE *f, CK_ATTRIBUTE_PTR pTemplate, void print_attribute_list_req(FILE *f, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount); void print_session_info(FILE *f, CK_SESSION_INFO *info); +void print_interfaces_list(FILE *f, CK_INTERFACE_PTR pInterfacesList, CK_ULONG ulCount); extern type_spec ck_attribute_specs[]; extern CK_ULONG ck_attribute_num; diff --git a/src/pkcs11/pkcs11-global.c b/src/pkcs11/pkcs11-global.c index 5f04a7f18f..1cef976ec0 100644 --- a/src/pkcs11/pkcs11-global.c +++ b/src/pkcs11/pkcs11-global.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -35,6 +35,15 @@ #endif /* PKCS11_THREAD_LOCKING */ #include "sc-pkcs11.h" +#include "ui/notify.h" + +#ifdef ENABLE_OPENSSL +#include +#include "libopensc/sc-ossl-compat.h" +#endif +#ifdef ENABLE_OPENPACE +#include +#endif #ifndef MODULE_APP_NAME #define MODULE_APP_NAME "opensc-pkcs11" @@ -49,18 +58,26 @@ pid_t initialized_pid = (pid_t)-1; #endif static int in_finalize = 0; extern CK_FUNCTION_LIST pkcs11_function_list; +extern CK_FUNCTION_LIST_3_0 pkcs11_function_list_3_0; +extern CK_FUNCTION_LIST_3_2 pkcs11_function_list_3_2; +int nesting = 0; #ifdef PKCS11_THREAD_LOCKING #if defined(HAVE_PTHREAD) +/* mutex used to control C_Initilize creation of mutexes */ +static pthread_mutex_t c_initialize_m = PTHREAD_MUTEX_INITIALIZER; +#define C_INITIALIZE_M_LOCK pthread_mutex_lock(&c_initialize_m); +#define C_INITIALIZE_M_UNLOCK pthread_mutex_unlock(&c_initialize_m); + CK_RV mutex_create(void **mutex) { pthread_mutex_t *m; m = calloc(1, sizeof(*m)); if (m == NULL) - return CKR_GENERAL_ERROR;; + return CKR_GENERAL_ERROR; pthread_mutex_init(m, NULL); *mutex = m; return CKR_OK; @@ -94,6 +111,9 @@ static CK_C_INITIALIZE_ARGS _def_locks = { #define HAVE_OS_LOCKING #elif defined(_WIN32) +CRITICAL_SECTION c_initialize_cs = {0}; +#define C_INITIALIZE_M_LOCK EnterCriticalSection(&c_initialize_cs); +#define C_INITIALIZE_M_UNLOCK LeaveCriticalSection(&c_initialize_cs); CK_RV mutex_create(void **mutex) { @@ -134,9 +154,15 @@ static CK_C_INITIALIZE_ARGS _def_locks = { #endif +#else /* PKCS11_THREAD_LOCKING */ +#define C_INITIALIZE_M_LOCK +#define C_INITIALIZE_M_UNLOCK + #endif /* PKCS11_THREAD_LOCKING */ static CK_C_INITIALIZE_ARGS_PTR global_locking; +static CK_C_INITIALIZE_ARGS app_locking = { + NULL, NULL, NULL, NULL, 0, NULL }; static void *global_lock = NULL; #ifdef HAVE_OS_LOCKING static CK_C_INITIALIZE_ARGS_PTR default_mutex_funcs = &_def_locks; @@ -209,31 +235,105 @@ static int slot_list_seeker(const void *el, const void *key) { return 0; } +#ifndef _WIN32 +__attribute__((constructor)) +#endif +int module_init() +{ +#ifdef _WIN32 + InitializeCriticalSection(&c_initialize_cs); +#endif + sc_notify_init(); + return 1; +} +#ifndef _WIN32 +__attribute__((destructor)) +#endif +int module_close() +{ + sc_notify_close(); +#if defined(ENABLE_OPENSSL) && defined(OPENSSL_SECURE_MALLOC_SIZE) && !defined(LIBRESSL_VERSION_NUMBER) + CRYPTO_secure_malloc_done(); +#endif +#ifdef ENABLE_OPENPACE + EAC_cleanup(); +#endif +#ifdef _WIN32 + DeleteCriticalSection(&c_initialize_cs); +#endif + return 1; +} + +#ifdef _WIN32 +BOOL APIENTRY DllMain( HINSTANCE hinstDLL, + DWORD ul_reason_for_call, + LPVOID lpReserved +) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + if (!module_init()) + return FALSE; + break; + case DLL_PROCESS_DETACH: + if (lpReserved == NULL) { + if (!module_close()) + return FALSE; + } + break; + } + return TRUE; +} +#endif CK_RV C_Initialize(CK_VOID_PTR pInitArgs) { CK_RV rv; #if !defined(_WIN32) - pid_t current_pid = getpid(); + pid_t current_pid; #endif int rc; - unsigned int i; sc_context_param_t ctx_opts; #if !defined(_WIN32) /* Handle fork() exception */ + C_INITIALIZE_M_LOCK + current_pid = getpid(); if (current_pid != initialized_pid) { - if (context) + if (context && CKR_OK == sc_pkcs11_lock()) { context->flags |= SC_CTX_FLAG_TERMINATE; + sc_pkcs11_unlock(); + } C_Finalize(NULL_PTR); } initialized_pid = current_pid; in_finalize = 0; + C_INITIALIZE_M_UNLOCK #endif + /* protect from nesting */ + C_INITIALIZE_M_LOCK + nesting++; + if (nesting > 1) { + nesting--; + C_INITIALIZE_M_UNLOCK + return CKR_GENERAL_ERROR; + } + C_INITIALIZE_M_UNLOCK + /* protect from nesting */ + + /* protect from multiple threads tryng to setup locking */ + C_INITIALIZE_M_LOCK + if (context != NULL) { - sc_log(context, "C_Initialize(): Cryptoki already initialized\n"); + if (CKR_OK == sc_pkcs11_lock()) { + sc_log(context, "C_Initialize(): Cryptoki already initialized\n"); + sc_pkcs11_unlock(); + } + nesting--; + C_INITIALIZE_M_UNLOCK return CKR_CRYPTOKI_ALREADY_INITIALIZED; } @@ -264,20 +364,17 @@ CK_RV C_Initialize(CK_VOID_PTR pInitArgs) list_attributes_seeker(&sessions, session_list_seeker); /* List of slots */ - list_init(&virtual_slots); if (0 != list_init(&virtual_slots)) { rv = CKR_HOST_MEMORY; goto out; } list_attributes_seeker(&virtual_slots, slot_list_seeker); - /* Create slots for readers found on initialization, only if in 2.11 mode */ - for (i=0; ireader_events); list_destroy(&slot->objects); list_destroy(&slot->logins); free(slot); @@ -337,7 +443,7 @@ CK_RV C_Finalize(CK_VOID_PTR pReserved) return rv; } -CK_RV C_GetInfo(CK_INFO_PTR pInfo) +CK_RV get_info_version(CK_INFO_PTR pInfo, CK_VERSION version) { CK_RV rv = CKR_OK; @@ -351,8 +457,8 @@ CK_RV C_GetInfo(CK_INFO_PTR pInfo) sc_log(context, "C_GetInfo()"); memset(pInfo, 0, sizeof(CK_INFO)); - pInfo->cryptokiVersion.major = 2; - pInfo->cryptokiVersion.minor = 20; + pInfo->cryptokiVersion.major = version.major; + pInfo->cryptokiVersion.minor = version.minor; strcpy_bp(pInfo->manufacturerID, OPENSC_VS_FF_COMPANY_NAME, sizeof(pInfo->manufacturerID)); @@ -366,6 +472,20 @@ CK_RV C_GetInfo(CK_INFO_PTR pInfo) return rv; } +CK_RV C_GetInfoV2(CK_INFO_PTR pInfo) +{ + CK_VERSION v = {2, 20}; + + return get_info_version(pInfo, v); +} + +CK_RV C_GetInfo(CK_INFO_PTR pInfo) +{ + CK_VERSION v = {3, 0}; + + return get_info_version(pInfo, v); +} + CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) { if (ppFunctionList == NULL_PTR) @@ -395,13 +515,23 @@ CK_RV C_GetSlotList(CK_BBOOL tokenPresent, /* only slots with token prese sc_log(context, "C_GetSlotList(token=%d, %s)", tokenPresent, pSlotList==NULL_PTR? "plug-n-play":"refresh"); + DEBUG_VSS(NULL, "C_GetSlotList before ctx_detect_detect"); /* Slot list can only change in v2.20 */ if (pSlotList == NULL_PTR) sc_ctx_detect_readers(context); + DEBUG_VSS(NULL, "C_GetSlotList after ctx_detect_readers"); + card_detect_all(); + if (list_empty(&virtual_slots)) { + sc_log(context, "returned 0 slots\n"); + *pulCount = 0; + rv = CKR_OK; + goto out; + } + found = calloc(list_size(&virtual_slots), sizeof(CK_SLOT_ID)); if (found == NULL) { @@ -414,20 +544,20 @@ CK_RV C_GetSlotList(CK_BBOOL tokenPresent, /* only slots with token prese for (i=0; ireader) - || (!tokenPresent && slot->reader != prev_reader) - || (slot->slot_info.flags & CKF_TOKEN_PRESENT) - || (slot->flags & SC_PKCS11_SLOT_FLAG_SEEN)) { + if ((!tokenPresent && + (slot->reader != prev_reader || + slot->flags & SC_PKCS11_SLOT_FLAG_SEEN)) + || slot->slot_info.flags & CKF_TOKEN_PRESENT) { found[numMatches++] = slot->id; slot->flags |= SC_PKCS11_SLOT_FLAG_SEEN; } prev_reader = slot->reader; } + DEBUG_VSS(NULL, "C_GetSlotList after card_detect_all"); if (pSlotList == NULL_PTR) { sc_log(context, "was only a size inquiry (%lu)\n", numMatches); @@ -435,6 +565,7 @@ CK_RV C_GetSlotList(CK_BBOOL tokenPresent, /* only slots with token prese rv = CKR_OK; goto out; } + DEBUG_VSS(NULL, "C_GetSlotList after slot->id reassigned"); if (*pulCount < numMatches) { sc_log(context, "buffer was too small (needed %lu)\n", numMatches); @@ -448,12 +579,10 @@ CK_RV C_GetSlotList(CK_BBOOL tokenPresent, /* only slots with token prese rv = CKR_OK; sc_log(context, "returned %lu slots\n", numMatches); + DEBUG_VSS(NULL, "Returning a new slot list"); out: - if (found != NULL) { - free (found); - found = NULL; - } + free (found); sc_pkcs11_unlock(); return rv; } @@ -487,8 +616,9 @@ static sc_timestamp_t get_current_time(void) CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) { - struct sc_pkcs11_slot *slot; + struct sc_pkcs11_slot *slot = NULL; sc_timestamp_t now; + const char *name; CK_RV rv; if (pInfo == NULL_PTR) @@ -501,7 +631,7 @@ CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) sc_log(context, "C_GetSlotInfo(0x%lx)", slotID); if (sc_pkcs11_conf.init_sloppy) { - /* Most likely virtual_slots only contains the hotplug slot and has not + /* Most likely virtual_slots is empty and has not * been initialized because the caller has *not* called C_GetSlotList * before C_GetSlotInfo, as required by PKCS#11. Initialize * virtual_slots to make things work and hope the caller knows what @@ -510,16 +640,16 @@ CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) } rv = slot_get_slot(slotID, &slot); - sc_log(context, "C_GetSlotInfo() get slot rv %lu", rv); - if (rv == CKR_OK) { - if (slot->reader == NULL) { + DEBUG_VSS(slot, "C_GetSlotInfo found"); + SC_LOG_RV("C_GetSlotInfo() get slot rv %s", rv); + if (rv == CKR_OK) { + if (slot->reader == NULL) { rv = CKR_TOKEN_NOT_PRESENT; - } - else { + } else { now = get_current_time(); if (now >= slot->slot_state_expires || now == 0) { /* Update slot status */ - rv = card_detect(slot->reader); + rv = card_detect(slot->reader, slot->reader_events); sc_log(context, "C_GetSlotInfo() card detect rv 0x%lX", rv); if (rv == CKR_TOKEN_NOT_RECOGNIZED || rv == CKR_OK) @@ -538,7 +668,12 @@ CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) memcpy(pInfo, &slot->slot_info, sizeof(CK_SLOT_INFO)); sc_log(context, "C_GetSlotInfo() flags 0x%lX", pInfo->flags); - sc_log(context, "C_GetSlotInfo(0x%lx) = %s", slotID, lookup_enum( RV_T, rv)); + + name = lookup_enum(RV_T, rv); + if (name) + sc_log(context, "C_GetSlotInfo(0x%lx) = %s", slotID, name); + else + sc_log(context, "C_GetSlotInfo(0x%lx) = 0x%08lX", slotID, rv); sc_pkcs11_unlock(); return rv; } @@ -594,13 +729,32 @@ CK_RV C_InitToken(CK_SLOT_ID slotID, { struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; + unsigned char *label, *cpo; CK_RV rv; unsigned int i; - sc_log(context, "C_InitToken(pLabel='%s') called", pLabel); + /* Strip trailing whitespace and null terminate the label. + * Keep the fixed-length buffer though as some other layers or drivers (SC-HSM) + * might expect the length is fixed! */ + label = malloc(33); + if (label == NULL) { + sc_log(context, "Failed to allocate label memory"); + return CKR_HOST_MEMORY; + } + memcpy(label, pLabel, 32); + label[32] = 0; + cpo = label + 31; + while ((cpo >= label) && (*cpo == ' ')) { + *cpo = 0; + cpo--; + } + + sc_log(context, "C_InitToken(pLabel='%s') called", label); rv = sc_pkcs11_lock(); - if (rv != CKR_OK) + if (rv != CKR_OK) { + free(label); return rv; + } rv = slot_get_token(slotID, &slot); if (rv != CKR_OK) { @@ -624,7 +778,7 @@ CK_RV C_InitToken(CK_SLOT_ID slotID, } } - rv = slot->p11card->framework->init_token(slot, slot->fw_data, pPin, ulPinLen, pLabel); + rv = slot->p11card->framework->init_token(slot, slot->fw_data, pPin, ulPinLen, label); if (rv == CKR_OK) { /* Now we should re-bind all tokens so they get the * corresponding function vector and flags */ @@ -632,7 +786,8 @@ CK_RV C_InitToken(CK_SLOT_ID slotID, out: sc_pkcs11_unlock(); - sc_log(context, "C_InitToken(pLabel='%s') returns 0x%lX", pLabel, rv); + sc_log(context, "C_InitToken(pLabel='%s') returns 0x%lX", label, rv); + free(label); return rv; } @@ -651,11 +806,6 @@ CK_RV C_WaitForSlotEvent(CK_FLAGS flags, /* blocking/nonblocking flag */ return CKR_ARGUMENTS_BAD; sc_log(context, "C_WaitForSlotEvent(block=%d)", !(flags & CKF_DONT_BLOCK)); -#ifndef PCSCLITE_GOOD - /* Not all pcsc-lite versions implement consistently used functions as they are */ - if (!(flags & CKF_DONT_BLOCK)) - return CKR_FUNCTION_NOT_SUPPORTED; -#endif /* PCSCLITE_GOOD */ rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; @@ -671,13 +821,6 @@ CK_RV C_WaitForSlotEvent(CK_FLAGS flags, /* blocking/nonblocking flag */ sc_log(context, "C_WaitForSlotEvent() reader_states:%p", reader_states); sc_pkcs11_unlock(); r = sc_wait_for_event(context, mask, &found, &events, -1, &reader_states); - if (events & SC_EVENT_READER_ATTACHED) { - rv = sc_pkcs11_lock(); - if (rv != CKR_OK) - return rv; - - goto out; - } /* Was C_Finalize called ? */ if (in_finalize == 1) return CKR_CRYPTOKI_NOT_INITIALIZED; @@ -707,11 +850,102 @@ CK_RV C_WaitForSlotEvent(CK_FLAGS flags, /* blocking/nonblocking flag */ sc_wait_for_event(context, 0, NULL, NULL, -1, &reader_states); } - sc_log(context, "C_WaitForSlotEvent() = %s", lookup_enum (RV_T, rv)); + SC_LOG_RV("C_WaitForSlotEvent() = %s", rv); sc_pkcs11_unlock(); return rv; } +/* + * Interfaces + */ +#define NUM_INTERFACES 3 +#define DEFAULT_INTERFACE 0 +// clang-format off +CK_INTERFACE interfaces[NUM_INTERFACES] = { + {(CK_UTF8CHAR_PTR)"PKCS 11", (void *)&pkcs11_function_list_3_2, 0}, + {(CK_UTF8CHAR_PTR)"PKCS 11", (void *)&pkcs11_function_list_3_0, 0}, + {(CK_UTF8CHAR_PTR)"PKCS 11", (void *)&pkcs11_function_list, 0} +}; +// clang-format on + +CK_RV C_GetInterfaceList(CK_INTERFACE_PTR pInterfacesList, /* returned interfaces */ + CK_ULONG_PTR pulCount) /* number of interfaces returned */ +{ + sc_log(context, "C_GetInterfaceList()"); + + if (pulCount == NULL_PTR) + return CKR_ARGUMENTS_BAD; + + if (pInterfacesList == NULL_PTR) { + *pulCount = NUM_INTERFACES; + sc_log(context, "was only a size inquiry (%lu)\n", *pulCount); + return CKR_OK; + } + + if (*pulCount < NUM_INTERFACES) { + sc_log(context, "buffer was too small (needed %d)\n", NUM_INTERFACES); + *pulCount = NUM_INTERFACES; + return CKR_BUFFER_TOO_SMALL; + } + + memcpy(pInterfacesList, interfaces, NUM_INTERFACES * sizeof(CK_INTERFACE)); + *pulCount = NUM_INTERFACES; + + sc_log(context, "returned %lu interfaces\n", *pulCount); + return CKR_OK; +} + +CK_RV C_GetInterface(CK_UTF8CHAR_PTR pInterfaceName, /* name of the interface */ + CK_VERSION_PTR pVersion, /* version of the interface */ + CK_INTERFACE_PTR_PTR ppInterface, /* returned interface */ + CK_FLAGS flags) /* flags controlling the semantics + * of the interface */ +{ + int i; + + sc_log(context, "C_GetInterface(%s)", + pInterfaceName == NULL_PTR ? "" : (char *)pInterfaceName); + + if (ppInterface == NULL) { + return CKR_ARGUMENTS_BAD; + } + + if (pInterfaceName == NULL_PTR) { + /* return default interface */ + *ppInterface = &interfaces[DEFAULT_INTERFACE]; + sc_log(context, "Returning default interface\n"); + return CKR_OK; + } + + for (i = 0; i < NUM_INTERFACES; i++) { + CK_VERSION_PTR interface_version = (CK_VERSION_PTR)interfaces[i].pFunctionList; + + /* The interface name is not null here */ + if (strcmp((char *)pInterfaceName, (char *)interfaces[i].pInterfaceName) != 0) { + continue; + } + /* If version is not null, it must match */ + if (pVersion != NULL_PTR && (pVersion->major != interface_version->major || + pVersion->minor != interface_version->minor)) { + continue; + } + /* If any flags specified, it must be supported by the interface */ + if ((flags & interfaces[i].flags) != flags) { + continue; + } + *ppInterface = &interfaces[i]; + sc_log(context, "Returning interface %s\n", (*ppInterface)->pInterfaceName); + return CKR_OK; + } + sc_log(context, "Interface not found: %s, version=%d.%d, flags=%lu\n", + pInterfaceName ? (char *)pInterfaceName : "", + pVersion ? (*pVersion).major : 0, + pVersion ? (*pVersion).minor : 0, + flags); + + return CKR_ARGUMENTS_BAD; +} + /* * Locking functions */ @@ -733,6 +967,8 @@ sc_pkcs11_init_lock(CK_C_INITIALIZE_ARGS_PTR args) if (args->pReserved != NULL_PTR) return CKR_ARGUMENTS_BAD; + app_locking = *args; + /* If the app tells us OS locking is okay, * use that. Otherwise use the supplied functions. */ @@ -748,13 +984,13 @@ sc_pkcs11_init_lock(CK_C_INITIALIZE_ARGS_PTR args) /* Based on PKCS#11 v2.11 11.4 */ if (applock && oslock) { /* Shall be used in threaded environment, prefer app provided locking */ - global_locking = args; + global_locking = &app_locking; } else if (!applock && oslock) { /* Shall be used in threaded environment, must use operating system locking */ global_locking = default_mutex_funcs; } else if (applock && !oslock) { - /* Shall be used in threaded envirnoment, must use app provided locking */ - global_locking = args; + /* Shall be used in threaded environment, must use app provided locking */ + global_locking = &app_locking; } else if (!applock && !oslock) { /* Shall not be used in threaded environment, use operating system locking */ global_locking = default_mutex_funcs; @@ -825,73 +1061,279 @@ void sc_pkcs11_free_lock(void) } CK_FUNCTION_LIST pkcs11_function_list = { - { 2, 11 }, /* Note: NSS/Firefox ignores this version number and uses C_GetInfo() */ - C_Initialize, - C_Finalize, - C_GetInfo, - C_GetFunctionList, - C_GetSlotList, - C_GetSlotInfo, - C_GetTokenInfo, - C_GetMechanismList, - C_GetMechanismInfo, - C_InitToken, - C_InitPIN, - C_SetPIN, - C_OpenSession, - C_CloseSession, - C_CloseAllSessions, - C_GetSessionInfo, - C_GetOperationState, - C_SetOperationState, - C_Login, - C_Logout, - C_CreateObject, - C_CopyObject, - C_DestroyObject, - C_GetObjectSize, - C_GetAttributeValue, - C_SetAttributeValue, - C_FindObjectsInit, - C_FindObjects, - C_FindObjectsFinal, - C_EncryptInit, - C_Encrypt, - C_EncryptUpdate, - C_EncryptFinal, - C_DecryptInit, - C_Decrypt, - C_DecryptUpdate, - C_DecryptFinal, - C_DigestInit, - C_Digest, - C_DigestUpdate, - C_DigestKey, - C_DigestFinal, - C_SignInit, - C_Sign, - C_SignUpdate, - C_SignFinal, - C_SignRecoverInit, - C_SignRecover, - C_VerifyInit, - C_Verify, - C_VerifyUpdate, - C_VerifyFinal, - C_VerifyRecoverInit, - C_VerifyRecover, - C_DigestEncryptUpdate, - C_DecryptDigestUpdate, - C_SignEncryptUpdate, - C_DecryptVerifyUpdate, - C_GenerateKey, - C_GenerateKeyPair, - C_WrapKey, - C_UnwrapKey, - C_DeriveKey, - C_SeedRandom, - C_GenerateRandom, - C_GetFunctionStatus, - C_CancelFunction, - C_WaitForSlotEvent + {2, 20}, /* Note: NSS/Firefox ignores this version number and uses C_GetInfo() */ + C_Initialize, + C_Finalize, + C_GetInfoV2, + C_GetFunctionList, + C_GetSlotList, + C_GetSlotInfo, + C_GetTokenInfo, + C_GetMechanismList, + C_GetMechanismInfo, + C_InitToken, + C_InitPIN, + C_SetPIN, + C_OpenSession, + C_CloseSession, + C_CloseAllSessions, + C_GetSessionInfo, + C_GetOperationState, + C_SetOperationState, + C_Login, + C_Logout, + C_CreateObject, + C_CopyObject, + C_DestroyObject, + C_GetObjectSize, + C_GetAttributeValue, + C_SetAttributeValue, + C_FindObjectsInit, + C_FindObjects, + C_FindObjectsFinal, + C_EncryptInit, + C_Encrypt, + C_EncryptUpdate, + C_EncryptFinal, + C_DecryptInit, + C_Decrypt, + C_DecryptUpdate, + C_DecryptFinal, + C_DigestInit, + C_Digest, + C_DigestUpdate, + C_DigestKey, + C_DigestFinal, + C_SignInit, + C_Sign, + C_SignUpdate, + C_SignFinal, + C_SignRecoverInit, + C_SignRecover, + C_VerifyInit, + C_Verify, + C_VerifyUpdate, + C_VerifyFinal, + C_VerifyRecoverInit, + C_VerifyRecover, + C_DigestEncryptUpdate, + C_DecryptDigestUpdate, + C_SignEncryptUpdate, + C_DecryptVerifyUpdate, + C_GenerateKey, + C_GenerateKeyPair, + C_WrapKey, + C_UnwrapKey, + C_DeriveKey, + C_SeedRandom, + C_GenerateRandom, + C_GetFunctionStatus, + C_CancelFunction, + C_WaitForSlotEvent +}; + +/* Returned from getInterface */ +CK_FUNCTION_LIST_3_0 pkcs11_function_list_3_0 = { + {3, 0}, + C_Initialize, + C_Finalize, + C_GetInfo, + C_GetFunctionList, + C_GetSlotList, + C_GetSlotInfo, + C_GetTokenInfo, + C_GetMechanismList, + C_GetMechanismInfo, + C_InitToken, + C_InitPIN, + C_SetPIN, + C_OpenSession, + C_CloseSession, + C_CloseAllSessions, + C_GetSessionInfo, + C_GetOperationState, + C_SetOperationState, + C_Login, + C_Logout, + C_CreateObject, + C_CopyObject, + C_DestroyObject, + C_GetObjectSize, + C_GetAttributeValue, + C_SetAttributeValue, + C_FindObjectsInit, + C_FindObjects, + C_FindObjectsFinal, + C_EncryptInit, + C_Encrypt, + C_EncryptUpdate, + C_EncryptFinal, + C_DecryptInit, + C_Decrypt, + C_DecryptUpdate, + C_DecryptFinal, + C_DigestInit, + C_Digest, + C_DigestUpdate, + C_DigestKey, + C_DigestFinal, + C_SignInit, + C_Sign, + C_SignUpdate, + C_SignFinal, + C_SignRecoverInit, + C_SignRecover, + C_VerifyInit, + C_Verify, + C_VerifyUpdate, + C_VerifyFinal, + C_VerifyRecoverInit, + C_VerifyRecover, + C_DigestEncryptUpdate, + C_DecryptDigestUpdate, + C_SignEncryptUpdate, + C_DecryptVerifyUpdate, + C_GenerateKey, + C_GenerateKeyPair, + C_WrapKey, + C_UnwrapKey, + C_DeriveKey, + C_SeedRandom, + C_GenerateRandom, + C_GetFunctionStatus, + C_CancelFunction, + C_WaitForSlotEvent, + C_GetInterfaceList, + C_GetInterface, + C_LoginUser, + C_SessionCancel, + C_MessageEncryptInit, + C_EncryptMessage, + C_EncryptMessageBegin, + C_EncryptMessageNext, + C_MessageEncryptFinal, + C_MessageDecryptInit, + C_DecryptMessage, + C_DecryptMessageBegin, + C_DecryptMessageNext, + C_MessageDecryptFinal, + C_MessageSignInit, + C_SignMessage, + C_SignMessageBegin, + C_SignMessageNext, + C_MessageSignFinal, + C_MessageVerifyInit, + C_VerifyMessage, + C_VerifyMessageBegin, + C_VerifyMessageNext, + C_MessageVerifyFinal +}; + +/* Returned from getInterface */ +CK_FUNCTION_LIST_3_2 pkcs11_function_list_3_2 = { + {3, 2}, + C_Initialize, + C_Finalize, + C_GetInfo, + C_GetFunctionList, + C_GetSlotList, + C_GetSlotInfo, + C_GetTokenInfo, + C_GetMechanismList, + C_GetMechanismInfo, + C_InitToken, + C_InitPIN, + C_SetPIN, + C_OpenSession, + C_CloseSession, + C_CloseAllSessions, + C_GetSessionInfo, + C_GetOperationState, + C_SetOperationState, + C_Login, + C_Logout, + C_CreateObject, + C_CopyObject, + C_DestroyObject, + C_GetObjectSize, + C_GetAttributeValue, + C_SetAttributeValue, + C_FindObjectsInit, + C_FindObjects, + C_FindObjectsFinal, + C_EncryptInit, + C_Encrypt, + C_EncryptUpdate, + C_EncryptFinal, + C_DecryptInit, + C_Decrypt, + C_DecryptUpdate, + C_DecryptFinal, + C_DigestInit, + C_Digest, + C_DigestUpdate, + C_DigestKey, + C_DigestFinal, + C_SignInit, + C_Sign, + C_SignUpdate, + C_SignFinal, + C_SignRecoverInit, + C_SignRecover, + C_VerifyInit, + C_Verify, + C_VerifyUpdate, + C_VerifyFinal, + C_VerifyRecoverInit, + C_VerifyRecover, + C_DigestEncryptUpdate, + C_DecryptDigestUpdate, + C_SignEncryptUpdate, + C_DecryptVerifyUpdate, + C_GenerateKey, + C_GenerateKeyPair, + C_WrapKey, + C_UnwrapKey, + C_DeriveKey, + C_SeedRandom, + C_GenerateRandom, + C_GetFunctionStatus, + C_CancelFunction, + C_WaitForSlotEvent, + C_GetInterfaceList, + C_GetInterface, + C_LoginUser, + C_SessionCancel, + C_MessageEncryptInit, + C_EncryptMessage, + C_EncryptMessageBegin, + C_EncryptMessageNext, + C_MessageEncryptFinal, + C_MessageDecryptInit, + C_DecryptMessage, + C_DecryptMessageBegin, + C_DecryptMessageNext, + C_MessageDecryptFinal, + C_MessageSignInit, + C_SignMessage, + C_SignMessageBegin, + C_SignMessageNext, + C_MessageSignFinal, + C_MessageVerifyInit, + C_VerifyMessage, + C_VerifyMessageBegin, + C_VerifyMessageNext, + C_MessageVerifyFinal, + C_EncapsulateKey, + C_DecapsulateKey, + C_VerifySignatureInit, + C_VerifySignature, + C_VerifySignatureUpdate, + C_VerifySignatureFinal, + C_GetSessionValidationFlags, + C_AsyncComplete, + C_AsyncGetID, + C_AsyncJoin, + C_WrapKeyAuthenticated, + C_UnwrapKeyAuthenticated, }; diff --git a/src/pkcs11/pkcs11-object.c b/src/pkcs11/pkcs11-object.c index e5125e134a..c166811ce1 100644 --- a/src/pkcs11/pkcs11-object.c +++ b/src/pkcs11/pkcs11-object.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -31,7 +31,7 @@ static void sc_find_release(sc_pkcs11_operation_t *operation); static sc_pkcs11_mechanism_type_t find_mechanism = { 0, /* mech */ {0,0,0}, /* mech_info */ - 0, /* key_type */ + { -1 }, /* key_types */ sizeof(struct sc_pkcs11_find_operation), /* obj_size */ sc_find_release, /* release */ NULL, /* md_init */ @@ -46,9 +46,18 @@ static sc_pkcs11_mechanism_type_t find_mechanism = { NULL, /* verif_final */ NULL, /* decrypt_init */ NULL, /* decrypt */ + NULL, /* decrypt_update */ + NULL, /* decrypt_final */ NULL, /* derive */ + NULL, /* wrap */ + NULL, /* unwrap */ + NULL, /* encrypt init */ + NULL, /* encrypt */ + NULL, /* ecnrypt_update */ + NULL, /* encrypt_final */ NULL, /* mech_data */ NULL, /* free_mech_data */ + NULL, /* copy_mech_data */ }; static void @@ -56,7 +65,6 @@ sc_find_release(sc_pkcs11_operation_t *operation) { struct sc_pkcs11_find_operation *fop = (struct sc_pkcs11_find_operation *)operation; - sc_log(context,"freeing %d handles used %d at %p", fop->allocated_handles, fop->num_handles, fop->handles); if (fop->handles) { free(fop->handles); fop->handles = NULL; @@ -95,6 +103,7 @@ CK_RV sc_create_object_int(CK_SESSION_HANDLE hSession, /* the session's handle * CK_RV rv = CKR_OK; struct sc_pkcs11_session *session; struct sc_pkcs11_card *card; + CK_BBOOL is_token = FALSE; LOG_FUNC_CALLED(context); if (pTemplate == NULL_PTR || ulCount == 0) @@ -114,6 +123,22 @@ CK_RV sc_create_object_int(CK_SESSION_HANDLE hSession, /* the session's handle * goto out; } + rv = attr_find(pTemplate, ulCount, CKA_TOKEN, &is_token, NULL); + if (rv != CKR_TEMPLATE_INCOMPLETE && rv != CKR_OK) { + goto out; + } + + if (is_token == TRUE) { + if (session->slot->token_info.flags & CKF_WRITE_PROTECTED) { + rv = CKR_TOKEN_WRITE_PROTECTED; + goto out; + } + if (!(session->flags & CKF_RW_SESSION)) { + rv = CKR_SESSION_READ_ONLY; + goto out; + } + } + card = session->slot->p11card; if (card->framework->create_object == NULL) rv = CKR_FUNCTION_NOT_SUPPORTED; @@ -123,9 +148,9 @@ CK_RV sc_create_object_int(CK_SESSION_HANDLE hSession, /* the session's handle * out: if (use_lock) sc_pkcs11_unlock(); - LOG_FUNC_RETURN(context, rv); -} + return rv; +} CK_RV C_CreateObject(CK_SESSION_HANDLE hSession, /* the session's handle */ @@ -156,7 +181,7 @@ C_DestroyObject(CK_SESSION_HANDLE hSession, /* the session's handle */ struct sc_pkcs11_session *session; struct sc_pkcs11_object *object; CK_BBOOL is_token = FALSE; - CK_ATTRIBUTE token_attribure = {CKA_TOKEN, &is_token, sizeof(is_token)}; + CK_ATTRIBUTE token_attribute = {CKA_TOKEN, &is_token, sizeof(is_token)}; rv = sc_pkcs11_lock(); if (rv != CKR_OK) @@ -167,10 +192,16 @@ C_DestroyObject(CK_SESSION_HANDLE hSession, /* the session's handle */ if (rv != CKR_OK) goto out; - object->ops->get_attribute(session, object, &token_attribure); - if (is_token == TRUE && !(session->flags & CKF_RW_SESSION)) { - rv = CKR_SESSION_READ_ONLY; - goto out; + object->ops->get_attribute(session, object, &token_attribute); + if (is_token == TRUE) { + if (session->slot->token_info.flags & CKF_WRITE_PROTECTED) { + rv = CKR_TOKEN_WRITE_PROTECTED; + goto out; + } + if (!(session->flags & CKF_RW_SESSION)) { + rv = CKR_SESSION_READ_ONLY; + goto out; + } } if (object->ops->destroy_object == NULL) @@ -199,7 +230,7 @@ C_GetAttributeValue(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_ATTRIBUTE_PTR pTemplate, /* specifies attributes, gets values */ CK_ULONG ulCount) /* attributes in template */ { - static int precedence[] = { + static CK_RV precedence[] = { CKR_OK, CKR_BUFFER_TOO_SMALL, CKR_ATTRIBUTE_TYPE_INVALID, @@ -207,12 +238,14 @@ C_GetAttributeValue(CK_SESSION_HANDLE hSession, /* the session's handle */ -1 }; char object_name[64]; - int j; + CK_RV j; CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_object *object; - int res, res_type; + CK_RV res; + CK_RV res_type; unsigned int i; + const char *name; if (pTemplate == NULL_PTR || ulCount == 0) return CKR_ARGUMENTS_BAD; @@ -245,7 +278,7 @@ C_GetAttributeValue(CK_SESSION_HANDLE hSession, /* the session's handle */ * should be handled - we give them highest * precedence */ - for (j = 0; precedence[j] != -1; j++) { + for (j = 0; precedence[j] != (CK_RV) -1; j++) { if (precedence[j] == res) break; } @@ -255,8 +288,15 @@ C_GetAttributeValue(CK_SESSION_HANDLE hSession, /* the session's handle */ } } -out: sc_log(context, "C_GetAttributeValue(hSession=0x%lx, hObject=0x%lx) = %s", - hSession, hObject, lookup_enum ( RV_T, rv )); +out: + name = lookup_enum (RV_T, rv ); + if (name) + sc_log(context, "C_GetAttributeValue(hSession=0x%lx, hObject=0x%lx) = %s", + hSession, hObject, name); + else + sc_log(context, "C_GetAttributeValue(hSession=0x%lx, hObject=0x%lx) = 0x%lx", + hSession, hObject, rv); + sc_pkcs11_unlock(); return rv; } @@ -321,6 +361,7 @@ C_FindObjectsInit(CK_SESSION_HANDLE hSession, /* the session's handle */ struct sc_pkcs11_object *object; struct sc_pkcs11_find_operation *operation; struct sc_pkcs11_slot *slot; + struct sc_pkcs11_operation *op = NULL; if (pTemplate == NULL_PTR && ulCount > 0) return CKR_ARGUMENTS_BAD; @@ -337,7 +378,8 @@ C_FindObjectsInit(CK_SESSION_HANDLE hSession, /* the session's handle */ dump_template(SC_LOG_DEBUG_NORMAL, "C_FindObjectsInit()", pTemplate, ulCount); rv = session_start_operation(session, SC_PKCS11_OPERATION_FIND, - &find_mechanism, (struct sc_pkcs11_operation **)&operation); + &find_mechanism, &op); + operation = (struct sc_pkcs11_find_operation *) op; if (rv != CKR_OK) goto out; @@ -349,7 +391,7 @@ C_FindObjectsInit(CK_SESSION_HANDLE hSession, /* the session's handle */ /* Check whether we should hide private objects */ hide_private = 0; - if (slot->login_user != CKU_USER && (slot->token_info.flags & CKF_LOGIN_REQUIRED)) + if ((slot->login_user == -1) && (slot->token_info.flags & CKF_LOGIN_REQUIRED)) hide_private = 1; /* For each object in token do */ @@ -425,6 +467,7 @@ C_FindObjects(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_ULONG to_return; struct sc_pkcs11_session *session; struct sc_pkcs11_find_operation *operation; + struct sc_pkcs11_operation *op = NULL; if (phObject == NULL_PTR || ulMaxObjectCount == 0 || pulObjectCount == NULL_PTR) return CKR_ARGUMENTS_BAD; @@ -437,7 +480,8 @@ C_FindObjects(CK_SESSION_HANDLE hSession, /* the session's handle */ if (rv != CKR_OK) goto out; - rv = session_get_operation(session, SC_PKCS11_OPERATION_FIND, (sc_pkcs11_operation_t **) & operation); + rv = session_get_operation(session, SC_PKCS11_OPERATION_FIND, &op); + operation = (struct sc_pkcs11_find_operation *) op; if (rv != CKR_OK) goto out; @@ -501,7 +545,7 @@ C_DigestInit(CK_SESSION_HANDLE hSession, /* the session's handle */ if (rv == CKR_OK) rv = sc_pkcs11_md_init(session, pMechanism); - sc_log(context, "C_DigestInit() = %s", lookup_enum ( RV_T, rv )); + SC_LOG_RV("C_DigestInit() = %s", rv); sc_pkcs11_unlock(); return rv; } @@ -546,7 +590,7 @@ C_Digest(CK_SESSION_HANDLE hSession, /* the session's handle */ rv = sc_pkcs11_md_final(session, pDigest, pulDigestLen); out: - sc_log(context, "C_Digest() = %s", lookup_enum ( RV_T, rv )); + SC_LOG_RV("C_Digest = %s", rv); sc_pkcs11_unlock(); return rv; } @@ -568,7 +612,7 @@ C_DigestUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ if (rv == CKR_OK) rv = sc_pkcs11_md_update(session, pPart, ulPartLen); - sc_log(context, "C_DigestUpdate() == %s", lookup_enum ( RV_T, rv )); + SC_LOG_RV("C_DigestUpdate() = %s", rv); sc_pkcs11_unlock(); return rv; } @@ -598,7 +642,7 @@ C_DigestFinal(CK_SESSION_HANDLE hSession, /* the session's handle */ if (rv == CKR_OK) rv = sc_pkcs11_md_final(session, pDigest, pulDigestLen); - sc_log(context, "C_DigestFinal() = %s", lookup_enum ( RV_T, rv )); + SC_LOG_RV("C_DigestFinal() = %s", rv); sc_pkcs11_unlock(); return rv; } @@ -650,7 +694,7 @@ C_SignInit(CK_SESSION_HANDLE hSession, /* the session's handle */ rv = sc_pkcs11_sign_init(session, pMechanism, object, key_type); out: - sc_log(context, "C_SignInit() = %s", lookup_enum ( RV_T, rv )); + SC_LOG_RV("C_SignInit() = %s", rv); sc_pkcs11_unlock(); return rv; } @@ -698,7 +742,7 @@ C_Sign(CK_SESSION_HANDLE hSession, /* the session's handle */ } out: - sc_log(context, "C_Sign() = %s", lookup_enum ( RV_T, rv )); + SC_LOG_RV("C_Sign() = %s", rv); sc_pkcs11_unlock(); return rv; } @@ -720,7 +764,7 @@ C_SignUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ if (rv == CKR_OK) rv = sc_pkcs11_sign_update(session, pPart, ulPartLen); - sc_log(context, "C_SignUpdate() = %s", lookup_enum ( RV_T, rv )); + SC_LOG_RV("C_SignUpdate() = %s", rv); sc_pkcs11_unlock(); return rv; } @@ -762,7 +806,7 @@ C_SignFinal(CK_SESSION_HANDLE hSession, /* the session's handle */ } out: - sc_log(context, "C_SignFinal() = %s", lookup_enum ( RV_T, rv )); + SC_LOG_RV("C_SignFinal() = %s", rv); sc_pkcs11_unlock(); return rv; } @@ -793,7 +837,48 @@ C_EncryptInit(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_MECHANISM_PTR pMechanism, /* the encryption mechanism */ CK_OBJECT_HANDLE hKey) /* handle of encryption key */ { - return CKR_FUNCTION_NOT_SUPPORTED; + CK_BBOOL can_encrypt; + CK_KEY_TYPE key_type; + CK_ATTRIBUTE encrypt_attribute = {CKA_ENCRYPT, &can_encrypt, sizeof(can_encrypt)}; + CK_ATTRIBUTE key_type_attr = {CKA_KEY_TYPE, &key_type, sizeof(key_type)}; + struct sc_pkcs11_session *session; + struct sc_pkcs11_object *object; + CK_RV rv; + + if (pMechanism == NULL_PTR) + return CKR_ARGUMENTS_BAD; + + rv = sc_pkcs11_lock(); + if (rv != CKR_OK) + return rv; + + rv = get_object_from_session(hSession, hKey, &session, &object); + if (rv != CKR_OK) { + if (rv == CKR_OBJECT_HANDLE_INVALID) + rv = CKR_KEY_HANDLE_INVALID; + goto out; + } + + if (object->ops->encrypt == NULL_PTR) { + rv = CKR_KEY_TYPE_INCONSISTENT; + goto out; + } + + rv = object->ops->get_attribute(session, object, &encrypt_attribute); + if (rv != CKR_OK || !can_encrypt) { + rv = CKR_KEY_TYPE_INCONSISTENT; + goto out; + } + rv = object->ops->get_attribute(session, object, &key_type_attr); + if (rv != CKR_OK) { + rv = CKR_KEY_TYPE_INCONSISTENT; + goto out; + } + rv = sc_pkcs11_encr_init(session, pMechanism, object, key_type); +out: + SC_LOG_RV("C_EncryptInit() = %s", rv); + sc_pkcs11_unlock(); + return rv; } @@ -803,7 +888,24 @@ CK_RV C_Encrypt(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pEncryptedData, /* receives encrypted data */ CK_ULONG_PTR pulEncryptedDataLen) { /* receives encrypted byte count */ - return CKR_FUNCTION_NOT_SUPPORTED; + CK_RV rv; + struct sc_pkcs11_session *session; + + rv = sc_pkcs11_lock(); + if (rv != CKR_OK) + return rv; + + rv = get_session(hSession, &session); + if (rv == CKR_OK) { + rv = restore_login_state(session->slot); + if (rv == CKR_OK) + rv = sc_pkcs11_encr(session, pData, ulDataLen, pEncryptedData, pulEncryptedDataLen); + rv = reset_login_state(session->slot, rv); + } + + SC_LOG_RV("C_Encrypt() = %s", rv); + sc_pkcs11_unlock(); + return rv; } CK_RV C_EncryptUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ @@ -812,14 +914,45 @@ CK_RV C_EncryptUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pEncryptedPart, /* receives encrypted data */ CK_ULONG_PTR pulEncryptedPartLen) { /* receives encrypted byte count */ - return CKR_FUNCTION_NOT_SUPPORTED; + CK_RV rv; + struct sc_pkcs11_session *session; + + rv = sc_pkcs11_lock(); + if (rv != CKR_OK) + return rv; + + rv = get_session(hSession, &session); + if (rv == CKR_OK) + rv = sc_pkcs11_encr_update(session, pPart, ulPartLen, + pEncryptedPart, pulEncryptedPartLen); + + SC_LOG_RV("C_EncryptUpdate() = %s", rv); + sc_pkcs11_unlock(); + return rv; } CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pLastEncryptedPart, /* receives encrypted last part */ CK_ULONG_PTR pulLastEncryptedPartLen) { /* receives byte count */ - return CKR_FUNCTION_NOT_SUPPORTED; + CK_RV rv; + struct sc_pkcs11_session *session; + + rv = sc_pkcs11_lock(); + if (rv != CKR_OK) + return rv; + + rv = get_session(hSession, &session); + if (rv == CKR_OK) { + rv = restore_login_state(session->slot); + if (rv == CKR_OK) + rv = sc_pkcs11_encr_final(session, pLastEncryptedPart, pulLastEncryptedPartLen); + rv = reset_login_state(session->slot, rv); + } + + SC_LOG_RV("C_EncryptFinal() = %s", rv); + sc_pkcs11_unlock(); + return rv; } CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession, /* the session's handle */ @@ -872,17 +1005,18 @@ CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession, /* the session's handle */ rv = sc_pkcs11_decr_init(session, pMechanism, object, key_type); out: - sc_log(context, "C_DecryptInit() = %s", lookup_enum ( RV_T, rv )); + SC_LOG_RV("C_DecryptInit() = %s", rv); sc_pkcs11_unlock(); return rv; } -CK_RV C_Decrypt(CK_SESSION_HANDLE hSession, /* the session's handle */ - CK_BYTE_PTR pEncryptedData, /* input encrypted data */ - CK_ULONG ulEncryptedDataLen, /* count of bytes of input */ - CK_BYTE_PTR pData, /* receives decrypted output */ - CK_ULONG_PTR pulDataLen) -{ /* receives decrypted byte count */ +CK_RV +C_Decrypt(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pEncryptedData, /* input encrypted data */ + CK_ULONG ulEncryptedDataLen, /* count of bytes of input */ + CK_BYTE_PTR pData, /* receives decrypted output */ + CK_ULONG_PTR pulDataLen) /* receives decrypted byte count */ +{ CK_RV rv; struct sc_pkcs11_session *session; @@ -900,25 +1034,64 @@ CK_RV C_Decrypt(CK_SESSION_HANDLE hSession, /* the session's handle */ rv = reset_login_state(session->slot, rv); } - sc_log(context, "C_Decrypt() = %s", lookup_enum ( RV_T, rv )); + /* do not log error code to prevent side channel attack */ + SC_LOG("C_Decrypt()"); sc_pkcs11_unlock(); return rv; } -CK_RV C_DecryptUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ - CK_BYTE_PTR pEncryptedPart, /* input encrypted data */ - CK_ULONG ulEncryptedPartLen, /* count of bytes of input */ - CK_BYTE_PTR pPart, /* receives decrypted output */ - CK_ULONG_PTR pulPartLen) -{ /* receives decrypted byte count */ - return CKR_FUNCTION_NOT_SUPPORTED; +CK_RV +C_DecryptUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pEncryptedPart, /* input encrypted data */ + CK_ULONG ulEncryptedPartLen, /* count of bytes of input */ + CK_BYTE_PTR pPart, /* receives decrypted output */ + CK_ULONG_PTR pulPartLen) /* receives decrypted byte count */ +{ + CK_RV rv; + struct sc_pkcs11_session *session; + + rv = sc_pkcs11_lock(); + if (rv != CKR_OK) + return rv; + + rv = get_session(hSession, &session); + if (rv == CKR_OK) + rv = sc_pkcs11_decr_update(session, pEncryptedPart, ulEncryptedPartLen, + pPart, pulPartLen); + + /* do not log error code to prevent side channel attack */ + SC_LOG("C_DecryptUpdate()"); + sc_pkcs11_unlock(); + return rv; } -CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession, /* the session's handle */ - CK_BYTE_PTR pLastPart, /* receives decrypted output */ - CK_ULONG_PTR pulLastPartLen) -{ /* receives decrypted byte count */ - return CKR_FUNCTION_NOT_SUPPORTED; +CK_RV +C_DecryptFinal(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pLastPart, /* receives decrypted output */ + CK_ULONG_PTR pulLastPartLen) /* receives decrypted byte count */ +{ + CK_RV rv; + struct sc_pkcs11_session *session; + + rv = sc_pkcs11_lock(); + if (rv != CKR_OK) + return rv; + + rv = get_session(hSession, &session); + if (rv == CKR_OK) { + rv = restore_login_state(session->slot); + if (rv == CKR_OK) { + rv = sc_pkcs11_decr_final(session, + pLastPart, + pulLastPartLen); + } + rv = reset_login_state(session->slot, rv); + } + + /* do not log error code to prevent side channel attack */ + SC_LOG("C_DecryptFinal()"); + sc_pkcs11_unlock(); + return rv; } CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ @@ -1001,7 +1174,8 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, /* the session's handle */ } slot = session->slot; - if (slot->p11card->framework->gen_keypair == NULL) + if (slot == NULL || slot->p11card == NULL || slot->p11card->framework == NULL + || slot->p11card->framework->gen_keypair == NULL) rv = CKR_FUNCTION_NOT_SUPPORTED; else { rv = restore_login_state(slot); @@ -1026,7 +1200,71 @@ CK_RV C_WrapKey(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pWrappedKey, /* receives the wrapped key */ CK_ULONG_PTR pulWrappedKeyLen) { /* receives byte size of wrapped key */ - return CKR_FUNCTION_NOT_SUPPORTED; + CK_RV rv; + CK_BBOOL can_wrap, + can_be_wrapped; + CK_KEY_TYPE key_type; + CK_ATTRIBUTE wrap_attribute = { CKA_WRAP, &can_wrap, sizeof(can_wrap) }; + CK_ATTRIBUTE extractable_attribute = { CKA_EXTRACTABLE, &can_be_wrapped, sizeof(can_be_wrapped) }; + CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE, &key_type, sizeof(key_type) }; + struct sc_pkcs11_session *session; + struct sc_pkcs11_object *wrapping_object; + struct sc_pkcs11_object *key_object; + + if (pMechanism == NULL_PTR) + return CKR_ARGUMENTS_BAD; + + rv = sc_pkcs11_lock(); + if (rv != CKR_OK) + return rv; + + /* Check if the wrapping key is OK to do wrapping */ + rv = get_object_from_session(hSession, hWrappingKey, &session, &wrapping_object); + if (rv != CKR_OK) { + if (rv == CKR_OBJECT_HANDLE_INVALID) + rv = CKR_KEY_HANDLE_INVALID; + goto out; + } + if (wrapping_object->ops->wrap_key == NULL_PTR) { + rv = CKR_KEY_TYPE_INCONSISTENT; + goto out; + } + + rv = wrapping_object->ops->get_attribute(session, wrapping_object, &wrap_attribute); + if (rv != CKR_OK || !can_wrap) { + rv = CKR_KEY_TYPE_INCONSISTENT; + goto out; + } + rv = wrapping_object->ops->get_attribute(session, wrapping_object, &key_type_attr); + if (rv != CKR_OK) { + rv = CKR_KEY_TYPE_INCONSISTENT; + goto out; + } + + /* Check if the key to be wrapped exists and is extractable*/ + rv = get_object_from_session(hSession, hKey, &session, &key_object); + if (rv != CKR_OK) { + if (rv == CKR_OBJECT_HANDLE_INVALID) + rv = CKR_KEY_HANDLE_INVALID; + goto out; + } + + rv = key_object->ops->get_attribute(session, key_object, &extractable_attribute); + if (rv != CKR_OK || !can_be_wrapped) { + rv = CKR_KEY_TYPE_INCONSISTENT; + goto out; + } + + rv = restore_login_state(session->slot); + if (rv == CKR_OK) + rv = sc_pkcs11_wrap(session, pMechanism, wrapping_object, key_type, + key_object, pWrappedKey, pulWrappedKeyLen); + + rv = reset_login_state(session->slot, rv); + +out: + sc_pkcs11_unlock(); + return rv; } CK_RV C_UnwrapKey(CK_SESSION_HANDLE hSession, /* the session's handle */ @@ -1038,7 +1276,67 @@ CK_RV C_UnwrapKey(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_ULONG ulAttributeCount, /* # of attributes in template */ CK_OBJECT_HANDLE_PTR phKey) { /* gets handle of recovered key */ - return CKR_FUNCTION_NOT_SUPPORTED; + CK_RV rv; + CK_BBOOL can_unwrap; + CK_KEY_TYPE key_type; + CK_ATTRIBUTE unwrap_attribute = { CKA_UNWRAP, &can_unwrap, sizeof(can_unwrap) }; + CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE, &key_type, sizeof(key_type) }; + struct sc_pkcs11_session *session; + struct sc_pkcs11_object *object; + struct sc_pkcs11_object *key_object; + + if (pMechanism == NULL_PTR) + return CKR_ARGUMENTS_BAD; + + rv = sc_pkcs11_lock(); + if (rv != CKR_OK) + return rv; + + rv = get_object_from_session(hSession, hUnwrappingKey, &session, &object); + if (rv != CKR_OK) { + if (rv == CKR_OBJECT_HANDLE_INVALID) + rv = CKR_KEY_HANDLE_INVALID; + goto out; + } + if (object->ops->unwrap_key == NULL_PTR) { + rv = CKR_KEY_TYPE_INCONSISTENT; + goto out; + } + + rv = object->ops->get_attribute(session, object, &unwrap_attribute); + if (rv != CKR_OK || !can_unwrap) { + rv = CKR_KEY_TYPE_INCONSISTENT; + goto out; + } + rv = object->ops->get_attribute(session, object, &key_type_attr); + if (rv != CKR_OK) { + rv = CKR_KEY_TYPE_INCONSISTENT; + goto out; + } + + /* Create the target object in memory */ + rv = sc_create_object_int(hSession, pTemplate, ulAttributeCount, phKey, 0); + + if (rv != CKR_OK) + goto out; + + rv = get_object_from_session(hSession, *phKey, &session, &key_object); + if (rv != CKR_OK) { + if (rv == CKR_OBJECT_HANDLE_INVALID) + rv = CKR_KEY_HANDLE_INVALID; + goto out; + } + + rv = restore_login_state(session->slot); + if (rv == CKR_OK) + rv = sc_pkcs11_unwrap(session, pMechanism, object, key_type, + pWrappedKey, ulWrappedKeyLen, key_object); + /* TODO if (rv != CK_OK) need to destroy the object */ + rv = reset_login_state(session->slot, rv); + +out: + sc_pkcs11_unlock(); + return rv; } CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, /* the session's handle */ @@ -1094,6 +1392,7 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, /* the session's handle */ switch(key_type) { case CKK_EC: + case CKK_EC_MONTGOMERY: rv = sc_create_object_int(hSession, pTemplate, ulAttributeCount, phKey, 0); if (rv != CKR_OK) @@ -1145,12 +1444,14 @@ CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession, /* the session's handle */ rv = get_session(hSession, &session); if (rv == CKR_OK) { slot = session->slot; - if (slot->p11card->framework->get_random == NULL) + if (slot == NULL || slot->p11card == NULL || slot->p11card->framework == NULL + || slot->p11card->framework->get_random == NULL) rv = CKR_RANDOM_NO_RNG; else rv = slot->p11card->framework->get_random(slot, RandomData, ulRandomLen); } + SC_LOG_RV("C_GenerateRandom() = %s", rv); sc_pkcs11_unlock(); return rv; } @@ -1201,7 +1502,7 @@ CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession, /* the session's handle */ rv = sc_pkcs11_verif_init(session, pMechanism, object, key_type); out: - sc_log(context, "C_VerifyInit() = %s", lookup_enum ( RV_T, rv )); + SC_LOG_RV("C_VerifyInit() = %s", rv); sc_pkcs11_unlock(); return rv; #endif @@ -1236,7 +1537,7 @@ CK_RV C_Verify(CK_SESSION_HANDLE hSession, /* the session's handle */ } out: - sc_log(context, "C_Verify() = %s", lookup_enum ( RV_T, rv )); + SC_LOG_RV("C_Verify() = %s", rv); sc_pkcs11_unlock(); return rv; #endif @@ -1260,7 +1561,7 @@ CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ if (rv == CKR_OK) rv = sc_pkcs11_verif_update(session, pPart, ulPartLen); - sc_log(context, "C_VerifyUpdate() = %s", lookup_enum ( RV_T, rv )); + SC_LOG_RV("C_VerifyUpdate() = %s", rv); sc_pkcs11_unlock(); return rv; #endif @@ -1288,7 +1589,7 @@ CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession, /* the session's handle */ rv = reset_login_state(session->slot, rv); } - sc_log(context, "C_VerifyFinal() = %s", lookup_enum ( RV_T, rv )); + SC_LOG_RV("C_VerifyFinal() = %s", rv); sc_pkcs11_unlock(); return rv; #endif @@ -1310,17 +1611,307 @@ CK_RV C_VerifyRecover(CK_SESSION_HANDLE hSession, /* the session's handle */ return CKR_FUNCTION_NOT_SUPPORTED; } +/* PKCS #11 3.0 only */ +CK_RV C_MessageEncryptInit(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the encryption mechanism */ + CK_OBJECT_HANDLE hKey) /* handle of encryption key */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_EncryptMessage(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_VOID_PTR pParameter, /* message specific parameter */ + CK_ULONG ulParameterLen, /* length of message specific parameter */ + CK_BYTE_PTR pAssociatedData, /* AEAD Associated data */ + CK_ULONG ulAssociatedDataLen, /* AEAD Associated data length */ + CK_BYTE_PTR pPlaintext, /* plain text */ + CK_ULONG ulPlaintextLen, /* plain text length */ + CK_BYTE_PTR pCiphertext, /* gets cipher text */ + CK_ULONG_PTR pulCiphertextLen) /* gets cipher text length */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_EncryptMessageBegin(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_VOID_PTR pParameter, /* message specific parameter */ + CK_ULONG ulParameterLen, /* length of message specific parameter */ + CK_BYTE_PTR pAssociatedData, /* AEAD Associated data */ + CK_ULONG ulAssociatedDataLen) /* AEAD Associated data length */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_EncryptMessageNext(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_VOID_PTR pParameter, /* message specific parameter */ + CK_ULONG ulParameterLen, /* length of message specific parameter */ + CK_BYTE_PTR pPlaintextPart, /* plain text */ + CK_ULONG ulPlaintextPartLen, /* plain text length */ + CK_BYTE_PTR pCiphertextPart, /* gets cipher text */ + CK_ULONG_PTR pulCiphertextPartLen, /* gets cipher text length */ + CK_FLAGS flags) /* multi mode flag */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_MessageEncryptFinal(CK_SESSION_HANDLE hSession) /* the session's handle */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_MessageDecryptInit(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the decryption mechanism */ + CK_OBJECT_HANDLE hKey) /* handle of decryption key */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_DecryptMessage(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_VOID_PTR pParameter, /* message specific parameter */ + CK_ULONG ulParameterLen, /* length of message specific parameter */ + CK_BYTE_PTR pAssociatedData, /* AEAD Associated data */ + CK_ULONG ulAssociatedDataLen, /* AEAD Associated data length */ + CK_BYTE_PTR pCiphertext, /* cipher text */ + CK_ULONG ulCiphertextLen, /* cipher text length */ + CK_BYTE_PTR pPlaintext, /* gets plain text */ + CK_ULONG_PTR pulPlaintextLen) /* gets plain text length */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_DecryptMessageBegin(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_VOID_PTR pParameter, /* message specific parameter */ + CK_ULONG ulParameterLen, /* length of message specific parameter */ + CK_BYTE_PTR pAssociatedData, /* AEAD Associated data */ + CK_ULONG ulAssociatedDataLen) /* AEAD Associated data length */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_DecryptMessageNext(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_VOID_PTR pParameter, /* message specific parameter */ + CK_ULONG ulParameterLen, /* length of message specific parameter */ + CK_BYTE_PTR pCiphertextPart, /* cipher text */ + CK_ULONG ulCiphertextPartLen, /* cipher text length */ + CK_BYTE_PTR pPlaintextPart, /* gets plain text */ + CK_ULONG_PTR pulPlaintextPartLen, /* gets plain text length */ + CK_FLAGS flags) /* multi mode flag */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_MessageDecryptFinal(CK_SESSION_HANDLE hSession) /* the session's handle */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_MessageSignInit(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the signing mechanism */ + CK_OBJECT_HANDLE hKey) /* handle of signing key */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_SignMessage(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_VOID_PTR pParameter, /* message specific parameter */ + CK_ULONG ulParameterLen, /* length of message specific parameter */ + CK_BYTE_PTR pData, /* data to sign */ + CK_ULONG ulDataLen, /* data to sign length */ + CK_BYTE_PTR pSignature, /* gets signature */ + CK_ULONG_PTR pulSignatureLen) /* gets signature length */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_SignMessageBegin(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_VOID_PTR pParameter, /* message specific parameter */ + CK_ULONG ulParameterLen) /* length of message specific parameter */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_SignMessageNext(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_VOID_PTR pParameter, /* message specific parameter */ + CK_ULONG ulParameterLen, /* length of message specific parameter */ + CK_BYTE_PTR pData, /* data to sign */ + CK_ULONG ulDataLen, /* data to sign length */ + CK_BYTE_PTR pSignature, /* gets signature */ + CK_ULONG_PTR pulSignatureLen) /* gets signature length */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_MessageSignFinal(CK_SESSION_HANDLE hSession) /* the session's handle */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_MessageVerifyInit(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the signing mechanism */ + CK_OBJECT_HANDLE hKey) /* handle of signing key */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_VerifyMessage(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_VOID_PTR pParameter, /* message specific parameter */ + CK_ULONG ulParameterLen, /* length of message specific parameter */ + CK_BYTE_PTR pData, /* data to sign */ + CK_ULONG ulDataLen, /* data to sign length */ + CK_BYTE_PTR pSignature, /* signature */ + CK_ULONG ulSignatureLen) /* signature length */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_VerifyMessageBegin(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_VOID_PTR pParameter, /* message specific parameter */ + CK_ULONG ulParameterLen) /* length of message specific parameter */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_VerifyMessageNext(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_VOID_PTR pParameter, /* message specific parameter */ + CK_ULONG ulParameterLen, /* length of message specific parameter */ + CK_BYTE_PTR pData, /* data to sign */ + CK_ULONG ulDataLen, /* data to sign length */ + CK_BYTE_PTR pSignature, /* signature */ + CK_ULONG ulSignatureLen) /* signature length */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV C_MessageVerifyFinal(CK_SESSION_HANDLE hSession) /* the session's handle */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_EncapsulateKey(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hPublicKey, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_BYTE_PTR pCiphertext, + CK_ULONG_PTR pulCiphertextLen, + CK_OBJECT_HANDLE_PTR phKey) +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_DecapsulateKey(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hPrivateKey, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_BYTE_PTR pCiphertext, + CK_ULONG ulCiphertextLen, + CK_OBJECT_HANDLE_PTR phKey) +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_VerifySignatureInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey, + CK_BYTE_PTR pSignature, + CK_ULONG ulSignatureLen) +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_VerifySignature(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen) +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_VerifySignatureUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_VerifySignatureFinal(CK_SESSION_HANDLE hSession) +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_GetSessionValidationFlags(CK_SESSION_HANDLE hSession, + CK_SESSION_VALIDATION_FLAGS_TYPE type, + CK_FLAGS_PTR pFlags) +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_AsyncComplete(CK_SESSION_HANDLE hSession, + CK_UTF8CHAR_PTR pFunctionName, + CK_ASYNC_DATA_PTR pResult) +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_AsyncGetID(CK_SESSION_HANDLE hSession, + CK_UTF8CHAR_PTR pFunctionName, + CK_ULONG_PTR pulID) +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_AsyncJoin(CK_SESSION_HANDLE hSession, + CK_UTF8CHAR_PTR pFunctionName, + CK_ULONG ulID, + CK_BYTE_PTR pData, + CK_ULONG ulData) +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_WrapKeyAuthenticated(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hWrappingKey, + CK_OBJECT_HANDLE hKey, + CK_BYTE_PTR pAssociatedData, + CK_ULONG ulAssociatedDataLen, + CK_BYTE_PTR pWrappedKey, + CK_ULONG_PTR pulWrappedKeyLen) +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_UnwrapKeyAuthenticated(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hUnwrappingKey, + CK_BYTE_PTR pWrappedKey, + CK_ULONG ulWrappedKeyLen, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_BYTE_PTR pAssociatedData, + CK_ULONG ulAssociatedDataLen, + CK_OBJECT_HANDLE_PTR phKey) +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + /* * Helper function to compare attributes on any sort of object */ -int sc_pkcs11_any_cmp_attribute(struct sc_pkcs11_session *session, void *ptr, CK_ATTRIBUTE_PTR attr) +CK_RV sc_pkcs11_any_cmp_attribute(struct sc_pkcs11_session *session, void *ptr, CK_ATTRIBUTE_PTR attr) { - int rv; + CK_RV rv; struct sc_pkcs11_object *object; u8 temp1[1024]; u8 *temp2 = NULL; /* dynamic allocation for large attributes */ CK_ATTRIBUTE temp_attr; - int res; object = (struct sc_pkcs11_object *)ptr; temp_attr.type = attr->type; @@ -1344,7 +1935,7 @@ int sc_pkcs11_any_cmp_attribute(struct sc_pkcs11_session *session, void *ptr, CK /* Get the attribute */ rv = object->ops->get_attribute(session, object, &temp_attr); if (rv != CKR_OK) { - res = 0; + rv = 0; goto done; } #ifdef DEBUG @@ -1355,12 +1946,12 @@ int sc_pkcs11_any_cmp_attribute(struct sc_pkcs11_session *session, void *ptr, CK dump_template(SC_LOG_DEBUG_NORMAL, foo, &temp_attr, 1); } #endif - res = temp_attr.ulValueLen == attr->ulValueLen + rv = temp_attr.ulValueLen == attr->ulValueLen && !memcmp(temp_attr.pValue, attr->pValue, attr->ulValueLen); done: if (temp2 != NULL) free(temp2); - return res; + return rv; } diff --git a/src/pkcs11/pkcs11-opensc.h b/src/pkcs11/pkcs11-opensc.h index 33beb6a3d6..9975e99fc2 100644 --- a/src/pkcs11/pkcs11-opensc.h +++ b/src/pkcs11/pkcs11-opensc.h @@ -2,13 +2,29 @@ #define PKCS11_OPENSC_H /* OpenSC specific extensions */ +/* + * define OpenSC specific Vendor Defined extensions + * to make unique OpenSC flags, attributes, mechanisms, etc. + * + * Netscape used NSSCK_VENDOR_NSS 0x4E534350 "NSCP" + */ + +#define SC_VENDOR_DEFINED 0x4F534300 /* OSC */ + /* * In PKCS#11 there is no CKA_ attribute dedicated to the NON-REPUDIATION flag. - * We need this flag in PKCS#15/libopensc to make dinstinction between + * We need this flag in PKCS#15/libopensc to make distinction between * 'signature' and 'qualified signature' key slots. */ -#define CKA_OPENSC_NON_REPUDIATION (CKA_VENDOR_DEFINED | 1UL) +#define CKA_OPENSC_NON_REPUDIATION (CKA_VENDOR_DEFINED | SC_VENDOR_DEFINED | 1UL) + +#define CKA_SPKI (CKA_VENDOR_DEFINED | SC_VENDOR_DEFINED | 2UL) -#define CKA_SPKI (CKA_VENDOR_DEFINED | 2UL) +/* In PKCS#11 CKA_ALWAYS_AUTHENTICATE attribute is only associated with private keys. + * The corresponding userConsent field in PKCS#15 is allowed for any object type. This attribute can be used + * to set userConsent=1 for other objects than private keys via PKCS#11. */ +#define CKA_OPENSC_ALWAYS_AUTH_ANY_OBJECT (CKA_VENDOR_DEFINED | SC_VENDOR_DEFINED | 3UL) +/* PKCS11 does not define an unknown CKK. Define one for internal OpenSC and pkcs11-tool.c */ +#define CKK_OPENSC_UNDEFINED (CKK_VENDOR_DEFINED | SC_VENDOR_DEFINED | 0xffUL) #endif diff --git a/src/pkcs11/pkcs11-session.c b/src/pkcs11/pkcs11-session.c index d088b2bc1d..99e80f312c 100644 --- a/src/pkcs11/pkcs11-session.c +++ b/src/pkcs11/pkcs11-session.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -61,7 +61,7 @@ CK_RV C_OpenSession(CK_SLOT_ID slotID, /* the slot's ID */ if (rv != CKR_OK) goto out; - /* Check that no conflictions sessions exist */ + /* Check that no conflicting sessions exist */ if (!(flags & CKF_RW_SESSION) && (slot->login_user == CKU_SO)) { rv = CKR_SESSION_READ_WRITE_SO_EXISTS; goto out; @@ -94,7 +94,7 @@ CK_RV C_OpenSession(CK_SLOT_ID slotID, /* the slot's ID */ sc_log(context, "C_OpenSession handle: 0x%lx", session->handle); out: - sc_log(context, "C_OpenSession() = %s", lookup_enum(RV_T, rv)); + SC_LOG_RV("C_OpenSession() = %s", rv); sc_pkcs11_unlock(); return rv; } @@ -120,9 +120,14 @@ static CK_RV sc_pkcs11_close_session(CK_SESSION_HANDLE hSession) slot->login_user = -1; if (sc_pkcs11_conf.atomic) pop_all_login_states(slot); - else + else { + if (slot->p11card == NULL) + return CKR_TOKEN_NOT_RECOGNIZED; slot->p11card->framework->logout(slot); + } } + for (size_t i = 0; i < SC_PKCS11_OPERATION_MAX; i++) + sc_pkcs11_release_operation(&session->operation[i]); if (list_delete(&sessions, session) != 0) sc_log(context, "Could not delete session from list!"); @@ -185,13 +190,70 @@ CK_RV C_CloseAllSessions(CK_SLOT_ID slotID) return rv; } +/* PKCS #11 3.0 only */ +CK_RV C_SessionCancel(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_FLAGS flags) /* flags control which sessions are cancelled */ +{ + struct sc_pkcs11_session *session; + CK_RV rv; + + rv = sc_pkcs11_lock(); + if (rv != CKR_OK) + return rv; + + rv = get_session(hSession, &session); + if (rv != CKR_OK) + goto out; + + /* Ignore return value of the cancel operation as it is valid to + * cancel not started operation and it can not fail for other reasons */ + if (flags & CKF_ENCRYPT) { + /* unused */ + } + if (flags & CKF_DECRYPT) { + session_stop_operation(session, SC_PKCS11_OPERATION_DECRYPT); + } + if (flags & CKF_DIGEST) { + session_stop_operation(session, SC_PKCS11_OPERATION_DIGEST); + } + if (flags & CKF_SIGN) { + session_stop_operation(session, SC_PKCS11_OPERATION_SIGN); + } + if (flags & CKF_SIGN_RECOVER) { + /* unused */ + } + if (flags & CKF_VERIFY) { + session_stop_operation(session, SC_PKCS11_OPERATION_VERIFY); + } + if (flags & CKF_VERIFY_RECOVER) { + /* unused */ + } + if (flags & CKF_GENERATE || flags & CKF_GENERATE_KEY_PAIR) { + /* unused */ + } + if (flags & CKF_WRAP) { + session_stop_operation(session, SC_PKCS11_OPERATION_WRAP); + } + if (flags & CKF_UNWRAP) { + session_stop_operation(session, SC_PKCS11_OPERATION_UNWRAP); + } + if (flags & CKF_DERIVE) { + session_stop_operation(session, SC_PKCS11_OPERATION_DERIVE); + } + +out: + sc_pkcs11_unlock(); + return rv; +} + CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_SESSION_INFO_PTR pInfo) { /* receives session information */ CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; - int logged_out; + const char *name; + int card_status = 0, logged_out = 0; if (pInfo == NULL_PTR) return CKR_ARGUMENTS_BAD; @@ -214,10 +276,20 @@ CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, /* the session's handle */ pInfo->ulDeviceError = 0; slot = session->slot; - logged_out = (slot_get_logged_in_state(slot) == SC_PIN_STATE_LOGGED_OUT); + card_status = slot_get_card_state(slot); + if (!(card_status & SC_READER_CARD_PRESENT) || card_status & SC_READER_CARD_CHANGED) { + /* Card was removed or reinserted, invalidate all sessions */ + slot->login_user = -1; + sc_pkcs11_close_all_sessions(session->slot->id); + rv = CKR_SESSION_HANDLE_INVALID; + goto out; + } + + /* Check whether the user is logged in the card */ + logged_out = (slot_get_logged_in_state(slot) & SC_PIN_STATE_LOGGED_OUT); if (slot->login_user == CKU_SO && !logged_out) { pInfo->state = CKS_RW_SO_FUNCTIONS; - } else if ((slot->login_user == CKU_USER && !logged_out) || (!(slot->token_info.flags & CKF_LOGIN_REQUIRED))) { + } else if ((slot->login_user == CKU_USER && !logged_out) || !(slot->token_info.flags & CKF_LOGIN_REQUIRED)) { pInfo->state = (session->flags & CKF_RW_SESSION) ? CKS_RW_USER_FUNCTIONS : CKS_RO_USER_FUNCTIONS; } else { @@ -226,7 +298,11 @@ CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, /* the session's handle */ } out: - sc_log(context, "C_GetSessionInfo(0x%lx) = %s", hSession, lookup_enum(RV_T, rv)); + name = lookup_enum(RV_T, rv); + if (name) + sc_log(context, "C_GetSessionInfo(0x%lx) = %s", hSession, name); + else + sc_log(context, "C_GetSessionInfo(0x%lx) = 0x%lx", hSession, rv); sc_pkcs11_unlock(); return rv; } @@ -289,7 +365,7 @@ CK_RV C_Login(CK_SESSION_HANDLE hSession, /* the session's handle */ } else { rv = restore_login_state(slot); - if (rv == CKR_OK) + if (rv == CKR_OK && slot->p11card && slot->p11card->framework) rv = slot->p11card->framework->login(slot, userType, pPin, ulPinLen); rv = reset_login_state(slot, rv); } @@ -307,13 +383,15 @@ CK_RV C_Login(CK_SESSION_HANDLE hSession, /* the session's handle */ rv = restore_login_state(slot); if (rv == CKR_OK) { sc_log(context, "C_Login() userType %li", userType); + if (slot->p11card == NULL) + return CKR_TOKEN_NOT_RECOGNIZED; rv = slot->p11card->framework->login(slot, userType, pPin, ulPinLen); sc_log(context, "fLogin() rv %li", rv); } if (rv == CKR_OK) rv = push_login_state(slot, userType, pPin, ulPinLen); if (rv == CKR_OK) { - slot->login_user = userType; + slot->login_user = (int) userType; } rv = reset_login_state(slot, rv); } @@ -323,6 +401,17 @@ CK_RV C_Login(CK_SESSION_HANDLE hSession, /* the session's handle */ return rv; } +/* PKCS #11 3.0 only */ +CK_RV C_LoginUser(CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_USER_TYPE userType, /* the user type */ + CK_CHAR_PTR pPin, /* the user's PIN */ + CK_ULONG ulPinLen, /* the length of the PIN */ + CK_UTF8CHAR_PTR pUsername, /* the user's name */ + CK_ULONG ulUsernameLen) /*the length of the user's name */ +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + CK_RV C_Logout(CK_SESSION_HANDLE hSession) { /* the session's handle */ CK_RV rv; @@ -347,8 +436,11 @@ CK_RV C_Logout(CK_SESSION_HANDLE hSession) slot->login_user = -1; if (sc_pkcs11_conf.atomic) pop_all_login_states(slot); - else + else { + if (!slot->p11card) + return CKR_TOKEN_NOT_RECOGNIZED; rv = slot->p11card->framework->logout(slot); + } } else rv = CKR_USER_NOT_LOGGED_IN; @@ -385,7 +477,7 @@ CK_RV C_InitPIN(CK_SESSION_HANDLE hSession, CK_CHAR_PTR pPin, CK_ULONG ulPinLen) slot = session->slot; if (slot->login_user != CKU_SO) { rv = CKR_USER_NOT_LOGGED_IN; - } else if (slot->p11card->framework->init_pin == NULL) { + } else if (slot->p11card == NULL || slot->p11card->framework->init_pin == NULL) { rv = CKR_FUNCTION_NOT_SUPPORTED; } else { rv = restore_login_state(slot); @@ -430,8 +522,11 @@ CK_RV C_SetPIN(CK_SESSION_HANDLE hSession, } rv = restore_login_state(slot); - if (rv == CKR_OK) + if (rv == CKR_OK) { + if (slot->p11card == NULL) + return CKR_TOKEN_NOT_RECOGNIZED; rv = slot->p11card->framework->change_pin(slot, pOldPin, ulOldLen, pNewPin, ulNewLen); + } rv = reset_login_state(slot, rv); out: diff --git a/src/pkcs11/pkcs11-spy.c b/src/pkcs11/pkcs11-spy.c index 1aed63898a..78f0ac48bd 100644 --- a/src/pkcs11/pkcs11-spy.c +++ b/src/pkcs11/pkcs11-spy.c @@ -13,49 +13,217 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA */ #include "config.h" -#include +#include #include +#include +#include #ifdef _WIN32 #include #include #include #else +#ifdef HAVE_SYS_TIME_H #include +#endif #include +#include +#endif +#ifdef HAVE_PTHREAD +#include #endif #define CRYPTOKI_EXPORTS #include "pkcs11-display.h" +#include "common/libpkcs11.h" #define __PASTE(x,y) x##y -extern void *C_LoadModule(const char *name, CK_FUNCTION_LIST_PTR_PTR); -extern CK_RV C_UnloadModule(void *module); - /* Declare all spy_* Cryptoki function */ /* Spy Module Function List */ static CK_FUNCTION_LIST_PTR pkcs11_spy = NULL; +static CK_FUNCTION_LIST_3_0_PTR pkcs11_spy_3_0 = NULL; +static CK_FUNCTION_LIST_3_2_PTR pkcs11_spy_3_2 = NULL; /* Real Module Function List */ -static CK_FUNCTION_LIST_PTR po = NULL; +/** Function pointer list of the module that is spied on + * + * If this pointer is `NULL`, then PKCS#11 Spy was not yet initialized. */ +static CK_FUNCTION_LIST_3_2_PTR po = NULL; +/* Real module interface list */ +static CK_INTERFACE_PTR orig_interfaces = NULL; +static unsigned long num_orig_interfaces = 0; /* Dynamic Module Handle */ static void *modhandle = NULL; /* Spy module output */ static FILE *spy_output = NULL; -/* Inits the spy. If successfull, po != NULL */ +static void * +allocate_function_list(int v) +{ + CK_FUNCTION_LIST_PTR list = NULL; + CK_FUNCTION_LIST_3_0_PTR list_3_0 = NULL; + CK_FUNCTION_LIST_3_2_PTR list_3_2 = NULL; + + if (v == 30) { + list = (CK_FUNCTION_LIST_PTR)malloc(sizeof(CK_FUNCTION_LIST_3_0)); + } else if (v == 32) { + list = (CK_FUNCTION_LIST_PTR)malloc(sizeof(CK_FUNCTION_LIST_3_2)); + } else { + list = malloc(sizeof(CK_FUNCTION_LIST)); + } + if (list == NULL) { + return NULL; + } + /* with our own pkcs11.h we need to maintain this ourself */ + list->version.major = 2; + list->version.minor = 11; + list->C_Initialize = C_Initialize; + list->C_Finalize = C_Finalize; + list->C_GetInfo = C_GetInfo; + list->C_GetFunctionList = C_GetFunctionList; + list->C_GetSlotList = C_GetSlotList; + list->C_GetSlotInfo = C_GetSlotInfo; + list->C_GetTokenInfo = C_GetTokenInfo; + list->C_GetMechanismList = C_GetMechanismList; + list->C_GetMechanismInfo = C_GetMechanismInfo; + list->C_InitToken = C_InitToken; + list->C_InitPIN = C_InitPIN; + list->C_SetPIN = C_SetPIN; + list->C_OpenSession = C_OpenSession; + list->C_CloseSession = C_CloseSession; + list->C_CloseAllSessions = C_CloseAllSessions; + list->C_GetSessionInfo = C_GetSessionInfo; + list->C_GetOperationState = C_GetOperationState; + list->C_SetOperationState = C_SetOperationState; + list->C_Login = C_Login; + list->C_Logout = C_Logout; + list->C_CreateObject = C_CreateObject; + list->C_CopyObject = C_CopyObject; + list->C_DestroyObject = C_DestroyObject; + list->C_GetObjectSize = C_GetObjectSize; + list->C_GetAttributeValue = C_GetAttributeValue; + list->C_SetAttributeValue = C_SetAttributeValue; + list->C_FindObjectsInit = C_FindObjectsInit; + list->C_FindObjects = C_FindObjects; + list->C_FindObjectsFinal = C_FindObjectsFinal; + list->C_EncryptInit = C_EncryptInit; + list->C_Encrypt = C_Encrypt; + list->C_EncryptUpdate = C_EncryptUpdate; + list->C_EncryptFinal = C_EncryptFinal; + list->C_DecryptInit = C_DecryptInit; + list->C_Decrypt = C_Decrypt; + list->C_DecryptUpdate = C_DecryptUpdate; + list->C_DecryptFinal = C_DecryptFinal; + list->C_DigestInit = C_DigestInit; + list->C_Digest = C_Digest; + list->C_DigestUpdate = C_DigestUpdate; + list->C_DigestKey = C_DigestKey; + list->C_DigestFinal = C_DigestFinal; + list->C_SignInit = C_SignInit; + list->C_Sign = C_Sign; + list->C_SignUpdate = C_SignUpdate; + list->C_SignFinal = C_SignFinal; + list->C_SignRecoverInit = C_SignRecoverInit; + list->C_SignRecover = C_SignRecover; + list->C_VerifyInit = C_VerifyInit; + list->C_Verify = C_Verify; + list->C_VerifyUpdate = C_VerifyUpdate; + list->C_VerifyFinal = C_VerifyFinal; + list->C_VerifyRecoverInit = C_VerifyRecoverInit; + list->C_VerifyRecover = C_VerifyRecover; + list->C_DigestEncryptUpdate = C_DigestEncryptUpdate; + list->C_DecryptDigestUpdate = C_DecryptDigestUpdate; + list->C_SignEncryptUpdate = C_SignEncryptUpdate; + list->C_DecryptVerifyUpdate = C_DecryptVerifyUpdate; + list->C_GenerateKey = C_GenerateKey; + list->C_GenerateKeyPair = C_GenerateKeyPair; + list->C_WrapKey = C_WrapKey; + list->C_UnwrapKey = C_UnwrapKey; + list->C_DeriveKey = C_DeriveKey; + list->C_SeedRandom = C_SeedRandom; + list->C_GenerateRandom = C_GenerateRandom; + list->C_GetFunctionStatus = C_GetFunctionStatus; + list->C_CancelFunction = C_CancelFunction; + list->C_WaitForSlotEvent = C_WaitForSlotEvent; + if (v < 30) { + return list; + } + + /* Add also PKCS #11 3.0 functions if requested and fixup version */ + list_3_0 = (CK_FUNCTION_LIST_3_0_PTR) list; + list_3_0->version.major = 3; + list_3_0->version.minor = 0; + list_3_0->C_GetInterfaceList = C_GetInterfaceList; + list_3_0->C_GetInterface = C_GetInterface; + list_3_0->C_LoginUser = C_LoginUser; + list_3_0->C_SessionCancel = C_SessionCancel; + list_3_0->C_MessageEncryptInit = C_MessageEncryptInit; + list_3_0->C_EncryptMessage = C_EncryptMessage; + list_3_0->C_EncryptMessageBegin = C_EncryptMessageBegin; + list_3_0->C_EncryptMessageNext = C_EncryptMessageNext; + list_3_0->C_MessageEncryptFinal = C_MessageEncryptFinal; + list_3_0->C_MessageDecryptInit = C_MessageDecryptInit; + list_3_0->C_DecryptMessage = C_DecryptMessage; + list_3_0->C_DecryptMessageBegin = C_DecryptMessageBegin; + list_3_0->C_DecryptMessageNext = C_DecryptMessageNext; + list_3_0->C_MessageDecryptFinal = C_MessageDecryptFinal; + list_3_0->C_MessageSignInit = C_MessageSignInit; + list_3_0->C_SignMessage = C_SignMessage; + list_3_0->C_SignMessageBegin = C_SignMessageBegin; + list_3_0->C_SignMessageNext = C_SignMessageNext; + list_3_0->C_MessageSignFinal = C_MessageSignFinal; + list_3_0->C_MessageVerifyInit = C_MessageVerifyInit; + list_3_0->C_VerifyMessage = C_VerifyMessage; + list_3_0->C_VerifyMessageBegin = C_VerifyMessageBegin; + list_3_0->C_VerifyMessageNext = C_VerifyMessageNext; + list_3_0->C_MessageVerifyFinal = C_MessageVerifyFinal; + + if (v < 32) { + return list_3_0; + } + + /* Add also PKCS #11 3.2 functions if requested and fixup version */ + list_3_2 = (CK_FUNCTION_LIST_3_2_PTR)list; + list_3_2->version.major = 3; + list_3_2->version.minor = 2; + list_3_2->C_EncapsulateKey = C_EncapsulateKey; + list_3_2->C_DecapsulateKey = C_DecapsulateKey; + list_3_2->C_VerifySignatureInit = C_VerifySignatureInit; + list_3_2->C_VerifySignature = C_VerifySignature; + list_3_2->C_VerifySignatureUpdate = C_VerifySignatureUpdate; + list_3_2->C_VerifySignatureFinal = C_VerifySignatureFinal; + list_3_2->C_GetSessionValidationFlags = C_GetSessionValidationFlags; + list_3_2->C_AsyncComplete = C_AsyncComplete; + list_3_2->C_AsyncGetID = C_AsyncGetID; + list_3_2->C_AsyncJoin = C_AsyncJoin; + list_3_2->C_WrapKeyAuthenticated = C_WrapKeyAuthenticated; + list_3_2->C_UnwrapKeyAuthenticated = C_UnwrapKeyAuthenticated; + return list_3_2; +} + +/* The compatibility interfaces that can be returned from Interface functions + * if the V3 API is used, but the proxied module does not support V3 API */ +#define NUM_INTERFACES 1 +CK_INTERFACE compat_interfaces[NUM_INTERFACES] = { + {(CK_UTF8CHAR_PTR) "PKCS 11", NULL, 0} +}; + +CK_INTERFACE spy_interface = {(CK_UTF8CHAR_PTR) "PKCS 11", NULL, 0}; + +/* Inits the spy. If successful, po != NULL */ static CK_RV init_spy(void) { + CK_FUNCTION_LIST_PTR po_v2 = NULL; const char *output, *module; - int rv = CKR_OK; + CK_RV rv = CKR_GENERAL_ERROR; #ifdef _WIN32 char temp_path[PATH_MAX], expanded_path[PATH_MAX]; DWORD temp_len, expanded_len; @@ -64,84 +232,16 @@ init_spy(void) #endif /* Allocates and initializes the pkcs11_spy structure */ - pkcs11_spy = malloc(sizeof(CK_FUNCTION_LIST)); - if (pkcs11_spy) { - /* with our own pkcs11.h we need to maintain this ourself */ - pkcs11_spy->version.major = 2; - pkcs11_spy->version.minor = 11; - pkcs11_spy->C_Initialize = C_Initialize; - pkcs11_spy->C_Finalize = C_Finalize; - pkcs11_spy->C_GetInfo = C_GetInfo; - pkcs11_spy->C_GetFunctionList = C_GetFunctionList; - pkcs11_spy->C_GetSlotList = C_GetSlotList; - pkcs11_spy->C_GetSlotInfo = C_GetSlotInfo; - pkcs11_spy->C_GetTokenInfo = C_GetTokenInfo; - pkcs11_spy->C_GetMechanismList = C_GetMechanismList; - pkcs11_spy->C_GetMechanismInfo = C_GetMechanismInfo; - pkcs11_spy->C_InitToken = C_InitToken; - pkcs11_spy->C_InitPIN = C_InitPIN; - pkcs11_spy->C_SetPIN = C_SetPIN; - pkcs11_spy->C_OpenSession = C_OpenSession; - pkcs11_spy->C_CloseSession = C_CloseSession; - pkcs11_spy->C_CloseAllSessions = C_CloseAllSessions; - pkcs11_spy->C_GetSessionInfo = C_GetSessionInfo; - pkcs11_spy->C_GetOperationState = C_GetOperationState; - pkcs11_spy->C_SetOperationState = C_SetOperationState; - pkcs11_spy->C_Login = C_Login; - pkcs11_spy->C_Logout = C_Logout; - pkcs11_spy->C_CreateObject = C_CreateObject; - pkcs11_spy->C_CopyObject = C_CopyObject; - pkcs11_spy->C_DestroyObject = C_DestroyObject; - pkcs11_spy->C_GetObjectSize = C_GetObjectSize; - pkcs11_spy->C_GetAttributeValue = C_GetAttributeValue; - pkcs11_spy->C_SetAttributeValue = C_SetAttributeValue; - pkcs11_spy->C_FindObjectsInit = C_FindObjectsInit; - pkcs11_spy->C_FindObjects = C_FindObjects; - pkcs11_spy->C_FindObjectsFinal = C_FindObjectsFinal; - pkcs11_spy->C_EncryptInit = C_EncryptInit; - pkcs11_spy->C_Encrypt = C_Encrypt; - pkcs11_spy->C_EncryptUpdate = C_EncryptUpdate; - pkcs11_spy->C_EncryptFinal = C_EncryptFinal; - pkcs11_spy->C_DecryptInit = C_DecryptInit; - pkcs11_spy->C_Decrypt = C_Decrypt; - pkcs11_spy->C_DecryptUpdate = C_DecryptUpdate; - pkcs11_spy->C_DecryptFinal = C_DecryptFinal; - pkcs11_spy->C_DigestInit = C_DigestInit; - pkcs11_spy->C_Digest = C_Digest; - pkcs11_spy->C_DigestUpdate = C_DigestUpdate; - pkcs11_spy->C_DigestKey = C_DigestKey; - pkcs11_spy->C_DigestFinal = C_DigestFinal; - pkcs11_spy->C_SignInit = C_SignInit; - pkcs11_spy->C_Sign = C_Sign; - pkcs11_spy->C_SignUpdate = C_SignUpdate; - pkcs11_spy->C_SignFinal = C_SignFinal; - pkcs11_spy->C_SignRecoverInit = C_SignRecoverInit; - pkcs11_spy->C_SignRecover = C_SignRecover; - pkcs11_spy->C_VerifyInit = C_VerifyInit; - pkcs11_spy->C_Verify = C_Verify; - pkcs11_spy->C_VerifyUpdate = C_VerifyUpdate; - pkcs11_spy->C_VerifyFinal = C_VerifyFinal; - pkcs11_spy->C_VerifyRecoverInit = C_VerifyRecoverInit; - pkcs11_spy->C_VerifyRecover = C_VerifyRecover; - pkcs11_spy->C_DigestEncryptUpdate = C_DigestEncryptUpdate; - pkcs11_spy->C_DecryptDigestUpdate = C_DecryptDigestUpdate; - pkcs11_spy->C_SignEncryptUpdate = C_SignEncryptUpdate; - pkcs11_spy->C_DecryptVerifyUpdate = C_DecryptVerifyUpdate; - pkcs11_spy->C_GenerateKey = C_GenerateKey; - pkcs11_spy->C_GenerateKeyPair = C_GenerateKeyPair; - pkcs11_spy->C_WrapKey = C_WrapKey; - pkcs11_spy->C_UnwrapKey = C_UnwrapKey; - pkcs11_spy->C_DeriveKey = C_DeriveKey; - pkcs11_spy->C_SeedRandom = C_SeedRandom; - pkcs11_spy->C_GenerateRandom = C_GenerateRandom; - pkcs11_spy->C_GetFunctionStatus = C_GetFunctionStatus; - pkcs11_spy->C_CancelFunction = C_CancelFunction; - pkcs11_spy->C_WaitForSlotEvent = C_WaitForSlotEvent; - } - else { - return CKR_HOST_MEMORY; + pkcs11_spy = allocate_function_list(2); + pkcs11_spy_3_0 = allocate_function_list(30); + pkcs11_spy_3_2 = allocate_function_list(32); + if (pkcs11_spy == NULL || pkcs11_spy_3_0 == NULL || pkcs11_spy_3_2 == NULL) { + rv = CKR_HOST_MEMORY; + goto err; } + compat_interfaces[0].pFunctionList = pkcs11_spy; + /* * Don't use getenv() as the last parameter for scconf_get_str(), * as we want to be able to override configuration file via @@ -151,14 +251,17 @@ init_spy(void) if (output) spy_output = fopen(output, "a"); + + + #ifdef _WIN32 if (!spy_output) { - /* try for the machine version first, as we may be runing + /* try for the machine version first, as we may be running * without a user during login */ - rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\OpenSC Project\\PKCS11-Spy", 0, KEY_QUERY_VALUE, &hKey ); + rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\" OPENSC_VS_FF_COMPANY_NAME "\\PKCS11-Spy" OPENSC_ARCH_SUFFIX, 0, KEY_QUERY_VALUE, &hKey); if (rc != ERROR_SUCCESS ) - rc = RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\OpenSC Project\\PKCS11-Spy", 0, KEY_QUERY_VALUE, &hKey ); + rc = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\" OPENSC_VS_FF_COMPANY_NAME "\\PKCS11-Spy" OPENSC_ARCH_SUFFIX, 0, KEY_QUERY_VALUE, &hKey); if( rc == ERROR_SUCCESS ) { temp_len = PATH_MAX; @@ -172,7 +275,7 @@ init_spy(void) } } - if( (rc == ERROR_SUCCESS) && (temp_len < PATH_MAX) ) + if( (rc == ERROR_SUCCESS) && (temp_len < PATH_MAX) ) output = temp_path; RegCloseKey( hKey ); } @@ -188,14 +291,14 @@ init_spy(void) module = getenv("PKCS11SPY"); #ifdef _WIN32 if (!module) { - /* try for the machine version first, as we may be runing + /* try for the machine version first, as we may be running * without a user during login */ - rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\OpenSC Project\\PKCS11-Spy", - 0, KEY_QUERY_VALUE, &hKey ); + rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\" OPENSC_VS_FF_COMPANY_NAME "\\PKCS11-Spy" OPENSC_ARCH_SUFFIX, + 0, KEY_QUERY_VALUE, &hKey); if (rc != ERROR_SUCCESS) - rc = RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\OpenSC Project\\PKCS11-Spy", - 0, KEY_QUERY_VALUE, &hKey ); + rc = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\" OPENSC_VS_FF_COMPANY_NAME "\\PKCS11-Spy" OPENSC_ARCH_SUFFIX, + 0, KEY_QUERY_VALUE, &hKey); if (rc == ERROR_SUCCESS) { temp_len = PATH_MAX; @@ -217,20 +320,43 @@ init_spy(void) #endif if (module == NULL) { fprintf(spy_output, "Error: no module specified. Please set PKCS11SPY environment.\n"); - free(pkcs11_spy); - return CKR_DEVICE_ERROR; + rv = CKR_DEVICE_ERROR; + goto err; } - modhandle = C_LoadModule(module, &po); - if (modhandle && po) { - fprintf(spy_output, "Loaded: \"%s\"\n", module); + modhandle = C_LoadModule(module, &po_v2); + if (modhandle == NULL) { + fprintf(spy_output, "Error: Could not load PKCS#11 interfaces from \"%s\".\n", module); + rv = CKR_DEVICE_ERROR; + goto err; } - else { - po = NULL; - free(pkcs11_spy); - rv = CKR_GENERAL_ERROR; + + /* Make sure we do not overrun underlying list if broken + * module returns version 3 from GetFuntionList() + * https://github.com/softhsm/SoftHSMv2/issues/839 + */ + po = calloc(1, sizeof(CK_FUNCTION_LIST_3_2)); + if (po == NULL) { + rv = CKR_HOST_MEMORY; + goto err; } + memcpy(po, po_v2, sizeof(CK_FUNCTION_LIST)); + fprintf(spy_output, "Loaded: \"%s\"\n", module); + + return CKR_OK; + +err: + po = NULL; + C_UnloadModule(modhandle); + modhandle = NULL; + free(pkcs11_spy); + pkcs11_spy = NULL; + free(pkcs11_spy_3_0); + pkcs11_spy_3_0 = NULL; + free(pkcs11_spy_3_2); + pkcs11_spy_3_2 = NULL; + return rv; } @@ -249,14 +375,17 @@ enter(const char *function) fprintf(spy_output, "\n%d: %s\n", count++, function); #ifdef _WIN32 - GetLocalTime(&st); - fprintf(spy_output, "%i-%02i-%02i %02i:%02i:%02i.%03i\n", st.wYear, st.wMonth, st.wDay, - st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); + GetLocalTime(&st); + fprintf(spy_output, "P:%lu; T:%lu %i-%02i-%02i %02i:%02i:%02i.%03i\n", + (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), + st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); #else gettimeofday (&tv, NULL); tm = localtime (&tv.tv_sec); strftime (time_string, sizeof(time_string), "%F %H:%M:%S", tm); - fprintf(spy_output, "%s.%03ld\n", time_string, (long)tv.tv_usec / 1000); + fprintf(spy_output, "P:%lu; T:0x%lu %s.%03ld\n", + (unsigned long)getpid(), (unsigned long)pthread_self(), + time_string, (long)tv.tv_usec / 1000); #endif } @@ -264,7 +393,7 @@ enter(const char *function) static CK_RV retne(CK_RV rv) { - fprintf(spy_output, "Returned: %ld %s\n", (unsigned long) rv, lookup_enum ( RV_T, rv )); + fprintf(spy_output, "Returned: %ld %s\n", (unsigned long) rv, lookup_enum (RV_T, rv )); fflush(spy_output); return rv; } @@ -274,14 +403,14 @@ static void spy_dump_string_in(const char *name, CK_VOID_PTR data, CK_ULONG size) { fprintf(spy_output, "[in] %s ", name); - print_generic(spy_output, 0, data, size, NULL); + print_generic(spy_output, 0, data, size, NULL, -1); } static void spy_dump_string_out(const char *name, CK_VOID_PTR data, CK_ULONG size) { fprintf(spy_output, "[out] %s ", name); - print_generic(spy_output, 0, data, size, NULL); + print_generic(spy_output, 0, data, size, NULL, -1); } static void @@ -332,12 +461,235 @@ spy_attribute_list_out(const char *name, CK_ATTRIBUTE_PTR pTemplate, print_attribute_list(spy_output, pTemplate, ulCount); } +static void +spy_dump_mechanism_in(const char *name, CK_MECHANISM_PTR pMechanism, bool is_c_message) +{ + char param_name[64]; + const char *mec_name; + + if (!pMechanism) { + fprintf(spy_output, "[in] %s = NULL\n", name); + return; + } + + mec_name = lookup_enum(MEC_T, pMechanism->mechanism); + if (mec_name) + fprintf(spy_output, "[in] %s->type = %s\n", name, mec_name); + else { + size_t needed = snprintf(NULL, 0, "0x%08lX", pMechanism->mechanism) + 1; + char *buffer = malloc(needed); + if (buffer) { + sprintf(buffer, "0x%08lX", pMechanism->mechanism); + fprintf(spy_output, "[in] %s->type = %s\n", name, buffer); + free(buffer); + } + } + + /* This is common case for all mechanisms */ + if (pMechanism->pParameter == NULL) { + fprintf(spy_output, "[in] %s->pParameter = NULL\n", name); + return; + } + + switch (pMechanism->mechanism) { + case CKM_AES_GCM: { + if (!is_c_message && pMechanism->ulParameterLen == sizeof(CK_GCM_PARAMS)) { + CK_GCM_PARAMS *param = (CK_GCM_PARAMS *)pMechanism->pParameter; + snprintf(param_name, sizeof(param_name), "%s->pParameter->pIv[ulIvLen]", name); + spy_dump_string_in(param_name, param->pIv, param->ulIvLen); + snprintf(param_name, sizeof(param_name), "%s->pParameter->ulIvBits", name); + spy_dump_ulong_in(param_name, param->ulIvBits); + snprintf(param_name, sizeof(param_name), "%s->pParameter->pAAD[ulAADLen]", name); + spy_dump_string_in(param_name, param->pAAD, param->ulAADLen); + fprintf(spy_output, "[in] %s->pParameter->ulTagBits = %lu\n", name, param->ulTagBits); + } else if (is_c_message && pMechanism->ulParameterLen == sizeof(CK_GCM_MESSAGE_PARAMS)) { + CK_GCM_MESSAGE_PARAMS *param = (CK_GCM_MESSAGE_PARAMS *)pMechanism->pParameter; + snprintf(param_name, sizeof(param_name), "%s->pParameter->pIv[ulIvLen]", name); + spy_dump_string_in(param_name, param->pIv, param->ulIvLen); + snprintf(param_name, sizeof(param_name), "%s->pParameter->ulIvFixedBits", name); + spy_dump_ulong_in(param_name, param->ulIvFixedBits); + fprintf(spy_output, "[in] %s->pParameter->ivGenerator = %s\n", name, + lookup_enum(GENERATE_T, param->ivGenerator)); + snprintf(param_name, sizeof(param_name), "%s->pParameter->pTag", name); + spy_dump_string_in(param_name, param->pTag, param->ulTagBits / 8); + } else { + snprintf(param_name, sizeof(param_name), "%s->pParameter[ulParameterLen]", name); + spy_dump_string_in(param_name, pMechanism->pParameter, pMechanism->ulParameterLen); + fprintf(spy_output, "(unknown type -- the length does not match any known type\n"); + } + break; + } + case CKM_AES_CCM: { + if (pMechanism->ulParameterLen != sizeof(CK_CCM_PARAMS)) { + snprintf(param_name, sizeof(param_name), "%s->pParameter[ulParameterLen]", name); + spy_dump_string_in(param_name, pMechanism->pParameter, pMechanism->ulParameterLen); + fprintf(spy_output, "(unknown type -- the length does not match any known type\n"); + break; + } + + CK_CCM_PARAMS *param = (CK_CCM_PARAMS *)pMechanism->pParameter; + snprintf(param_name, sizeof(param_name), "%s->pParameter->ulDataLen", name); + spy_dump_ulong_in(param_name, param->ulDataLen); + snprintf(param_name, sizeof(param_name), "%s->pParameter->pNonce[ulNonceLen]", name); + spy_dump_string_in(param_name, param->pNonce, param->ulNonceLen); + snprintf(param_name, sizeof(param_name), "%s->pParameter->pAAD[ulAADLen]", name); + spy_dump_string_in(param_name, param->pAAD, param->ulAADLen); + fprintf(spy_output, "[in] %s->pParameter->ulMacLen = %lu\n", name, param->ulMACLen); + break; + } + case CKM_ECDH1_DERIVE: + case CKM_ECDH1_COFACTOR_DERIVE: { + if (pMechanism->ulParameterLen != sizeof(CK_ECDH1_DERIVE_PARAMS)) { + snprintf(param_name, sizeof(param_name), "%s->pParameter[ulParameterLen]", name); + spy_dump_string_in(param_name, pMechanism->pParameter, pMechanism->ulParameterLen); + fprintf(spy_output, "(unknown type -- the length does not match any known type\n"); + break; + } + + CK_ECDH1_DERIVE_PARAMS *param = (CK_ECDH1_DERIVE_PARAMS *)pMechanism->pParameter; + fprintf(spy_output, "[in] %s->pParameter->kdf = %s\n", name, lookup_enum(CKD_T, param->kdf)); + fprintf(spy_output, "[in] %s->pParameter->pSharedData[ulSharedDataLen] = ", name); + print_generic(spy_output, 0, param->pSharedData, param->ulSharedDataLen, NULL, -1); + fprintf(spy_output, "[in] %s->pParameter->pPublicData[ulPublicDataLen] = ", name); + print_generic(spy_output, 0, param->pPublicData, param->ulPublicDataLen, NULL, -1); + break; + } + case CKM_ECMQV_DERIVE: { + if (pMechanism->ulParameterLen != sizeof(CK_ECMQV_DERIVE_PARAMS)) { + snprintf(param_name, sizeof(param_name), "%s->pParameter[ulParameterLen]", name); + spy_dump_string_in(param_name, pMechanism->pParameter, pMechanism->ulParameterLen); + fprintf(spy_output, "(unknown type -- the length does not match any known type\n"); + break; + } + + CK_ECMQV_DERIVE_PARAMS *param = (CK_ECMQV_DERIVE_PARAMS *)pMechanism->pParameter; + fprintf(spy_output, "[in] %s->pParameter->kdf = %s\n", name, lookup_enum(CKD_T, param->kdf)); + fprintf(spy_output, "%s->pParameter->pSharedData[ulSharedDataLen] = ", name); + print_generic(spy_output, 0, param->pSharedData, param->ulSharedDataLen, NULL, -1); + fprintf(spy_output, "%s->pParameter->pPublicData[ulPublicDataLen] = ", name); + print_generic(spy_output, 0, param->pPublicData, param->ulPublicDataLen, NULL, -1); + fprintf(spy_output, "%s->pParameter->ulPrivateDataLen = %lu", name, param->ulPrivateDataLen); + fprintf(spy_output, "%s->pParameter->hPrivateData = %lu", name, param->hPrivateData); + fprintf(spy_output, "%s->pParameter->pPublicData2[ulPublicDataLen2] = ", name); + print_generic(spy_output, 0, param->pPublicData2, param->ulPublicDataLen2, NULL, -1); + fprintf(spy_output, "%s->pParameter->publicKey = %lu", name, param->publicKey); + break; + } + case CKM_RSA_PKCS_OAEP: { + if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_OAEP_PARAMS)) { + snprintf(param_name, sizeof(param_name), "%s->pParameter[ulParameterLen]", name); + spy_dump_string_in(param_name, pMechanism->pParameter, pMechanism->ulParameterLen); + fprintf(spy_output, "(unknown type -- the length does not match any known type\n"); + break; + } + CK_RSA_PKCS_OAEP_PARAMS *param = (CK_RSA_PKCS_OAEP_PARAMS *)pMechanism->pParameter; + fprintf(spy_output, "[in] %s->pParameter->hashAlg = %s\n", name, + lookup_enum(MEC_T, param->hashAlg)); + fprintf(spy_output, "[in] %s->pParameter->mgf = %s\n", name, lookup_enum(MGF_T, param->mgf)); + fprintf(spy_output, "[in] %s->pParameter->source = %lu\n", name, param->source); + snprintf(param_name, sizeof(param_name), "%s->pParameter->pSourceData[ulSourceDalaLen]", name); + spy_dump_string_in(param_name, param->pSourceData, param->ulSourceDataLen); + break; + } + case CKM_RSA_PKCS_PSS: + case CKM_SHA1_RSA_PKCS_PSS: + case CKM_SHA256_RSA_PKCS_PSS: + case CKM_SHA384_RSA_PKCS_PSS: + case CKM_SHA512_RSA_PKCS_PSS: { + if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS)) { + snprintf(param_name, sizeof(param_name), "%s->pParameter[ulParameterLen]", name); + spy_dump_string_in(param_name, pMechanism->pParameter, pMechanism->ulParameterLen); + fprintf(spy_output, "(unknown type -- the length does not match any known type\n"); + break; + } + CK_RSA_PKCS_PSS_PARAMS *param = (CK_RSA_PKCS_PSS_PARAMS *)pMechanism->pParameter; + fprintf(spy_output, "[in] %s->pParameter->hashAlg = %s\n", name, + lookup_enum(MEC_T, param->hashAlg)); + fprintf(spy_output, "[in] %s->pParameter->mgf = %s\n", name, lookup_enum(MGF_T, param->mgf)); + fprintf(spy_output, "[in] %s->pParameter->sLen = %lu\n", name, param->sLen); + break; + } + case CKM_ML_DSA: + case CKM_HASH_ML_DSA_SHA224: + case CKM_HASH_ML_DSA_SHA256: + case CKM_HASH_ML_DSA_SHA384: + case CKM_HASH_ML_DSA_SHA512: + case CKM_HASH_ML_DSA_SHA3_224: + case CKM_HASH_ML_DSA_SHA3_256: + case CKM_HASH_ML_DSA_SHA3_384: + case CKM_HASH_ML_DSA_SHA3_512: + case CKM_HASH_ML_DSA_SHAKE128: + case CKM_HASH_ML_DSA_SHAKE256: + case CKM_SLH_DSA: + case CKM_HASH_SLH_DSA_SHA224: + case CKM_HASH_SLH_DSA_SHA256: + case CKM_HASH_SLH_DSA_SHA384: + case CKM_HASH_SLH_DSA_SHA512: + case CKM_HASH_SLH_DSA_SHA3_224: + case CKM_HASH_SLH_DSA_SHA3_256: + case CKM_HASH_SLH_DSA_SHA3_384: + case CKM_HASH_SLH_DSA_SHA3_512: + case CKM_HASH_SLH_DSA_SHAKE128: + case CKM_HASH_SLH_DSA_SHAKE256: { + if (pMechanism->ulParameterLen != sizeof(CK_SIGN_ADDITIONAL_CONTEXT)) { + snprintf(param_name, sizeof(param_name), "%s->pParameter[ulParameterLen]", name); + spy_dump_string_in(param_name, pMechanism->pParameter, pMechanism->ulParameterLen); + fprintf(spy_output, "(unknown type -- the length does not match any known type\n"); + break; + } + CK_SIGN_ADDITIONAL_CONTEXT *param = (CK_SIGN_ADDITIONAL_CONTEXT *)pMechanism->pParameter; + fprintf(spy_output, "[in] %s->pParameter->hedgeVariant = %s\n", name, + lookup_enum(HG_T, param->hedgeVariant)); + fprintf(spy_output, "%s->pParameter->pContent[ulContextLen] = ", name); + print_generic(spy_output, 0, param->pContext, param->ulContextLen, NULL, 1); + break; + } + case CKM_HASH_ML_DSA: + case CKM_HASH_SLH_DSA: { + if (pMechanism->ulParameterLen != sizeof(CK_HASH_SIGN_ADDITIONAL_CONTEXT)) { + snprintf(param_name, sizeof(param_name), "%s->pParameter[ulParameterLen]", name); + spy_dump_string_in(param_name, pMechanism->pParameter, pMechanism->ulParameterLen); + fprintf(spy_output, "(unknown type -- the length does not match any known type\n"); + break; + } + CK_HASH_SIGN_ADDITIONAL_CONTEXT *param = (CK_HASH_SIGN_ADDITIONAL_CONTEXT *)pMechanism->pParameter; + fprintf(spy_output, "[in] %s->pParameter->hedgeVariant = %s\n", name, + lookup_enum(HG_T, param->hedgeVariant)); + fprintf(spy_output, "%s->pParameter->pContent[ulContextLen] = ", name); + print_generic(spy_output, 0, param->pContext, param->ulContextLen, NULL, -1); + fprintf(spy_output, "[in] %s->pParameter->hash = %s\n", name, + lookup_enum(MEC_T, param->hash)); + break; + } + default: + snprintf(param_name, sizeof(param_name), "%s->pParameter[ulParameterLen]", name); + spy_dump_string_in(param_name, pMechanism->pParameter, pMechanism->ulParameterLen); + break; + } +} + static void print_ptr_in(const char *name, CK_VOID_PTR ptr) { fprintf(spy_output, "[in] %s = %p\n", name, ptr); } +#define FPRINTF_LOOKUP_ENUM(fmt, category, type)\ +do {\ + const char *name = lookup_enum((category), (type));\ + if (name)\ + fprintf(spy_output, (fmt), (name));\ + else {\ + size_t needed = snprintf(NULL, 0, "0x%08lX", (type)) + 1;\ + char *buffer = malloc(needed);\ + if (buffer) {\ + sprintf(buffer, "0x%08lX", (type));\ + fprintf(spy_output, (fmt), buffer);\ + free(buffer);\ + }\ + }\ +} while(0) + CK_RV C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR ppFunctionList) { @@ -348,6 +700,8 @@ CK_RV C_GetFunctionList } enter("C_GetFunctionList"); + if (ppFunctionList == NULL) + return retne(CKR_ARGUMENTS_BAD); *ppFunctionList = pkcs11_spy; return retne(CKR_OK); } @@ -472,14 +826,10 @@ C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) { CK_RV rv; - const char *name = lookup_enum(MEC_T, type); enter("C_GetMechanismInfo"); spy_dump_ulong_in("slotID", slotID); - if (name) - fprintf(spy_output, "%30s \n", name); - else - fprintf(spy_output, " Unknown Mechanism (%08lx) \n", type); + FPRINTF_LOOKUP_ENUM("[in] type = %s\n", MEC_T, type); rv = po->C_GetMechanismInfo(slotID, type, pInfo); if(rv == CKR_OK) { @@ -538,10 +888,13 @@ C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, enter("C_OpenSession"); spy_dump_ulong_in("slotID", slotID); spy_dump_ulong_in("flags", flags); - fprintf(spy_output, "pApplication=%p\n", pApplication); - fprintf(spy_output, "Notify=%p\n", (void *)Notify); + fprintf(spy_output, "[in] pApplication = %p\n", pApplication); + fprintf(spy_output, "[in] Notify = %p\n", (void *)Notify); rv = po->C_OpenSession(slotID, flags, pApplication, Notify, phSession); - spy_dump_ulong_out("*phSession", *phSession); + if (phSession) + spy_dump_ulong_out("*phSession", *phSession); + else + fprintf(spy_output, "[out] phSession = %p\n", phSession); return retne(rv); } @@ -619,8 +972,7 @@ C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, enter("C_Login"); spy_dump_ulong_in("hSession", hSession); - fprintf(spy_output, "[in] userType = %s\n", - lookup_enum(USR_T, userType)); + FPRINTF_LOOKUP_ENUM("[in] userType = %s\n", USR_T, userType); spy_dump_string_in("pPin[ulPinLen]", pPin, ulPinLen); rv = po->C_Login(hSession, userType, pPin, ulPinLen); return retne(rv); @@ -785,7 +1137,7 @@ C_EncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT enter("C_EncryptInit"); spy_dump_ulong_in("hSession", hSession); - fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); + spy_dump_mechanism_in("pMechanism", pMechanism, false); spy_dump_ulong_in("hKey", hKey); rv = po->C_EncryptInit(hSession, pMechanism, hKey); return retne(rv); @@ -801,8 +1153,11 @@ C_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pData[ulDataLen]", pData, ulDataLen); rv = po->C_Encrypt(hSession, pData, ulDataLen, pEncryptedData, pulEncryptedDataLen); - if (rv == CKR_OK) + if (rv == CKR_OK) { spy_dump_string_out("pEncryptedData[*pulEncryptedDataLen]", pEncryptedData, *pulEncryptedDataLen); + } else if (rv == CKR_BUFFER_TOO_SMALL) { + spy_dump_ulong_out("pulEncryptedDataLen", *pulEncryptedDataLen); + } return retne(rv); } @@ -829,9 +1184,12 @@ C_EncryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastEncryptedPart, CK_UL enter("C_EncryptFinal"); spy_dump_ulong_in("hSession", hSession); rv = po->C_EncryptFinal(hSession, pLastEncryptedPart, pulLastEncryptedPartLen); - if (rv == CKR_OK) + if (rv == CKR_OK) { spy_dump_string_out("pLastEncryptedPart[*pulLastEncryptedPartLen]", pLastEncryptedPart, *pulLastEncryptedPartLen); + } else if (rv == CKR_BUFFER_TOO_SMALL) { + spy_dump_ulong_out("pulLastEncryptedPartLen", *pulLastEncryptedPartLen); + } return retne(rv); } @@ -843,7 +1201,7 @@ C_DecryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT enter("C_DecryptInit"); spy_dump_ulong_in("hSession", hSession); - fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); + spy_dump_mechanism_in("pMechanism", pMechanism, false); spy_dump_ulong_in("hKey", hKey); rv = po->C_DecryptInit(hSession, pMechanism, hKey); return retne(rv); @@ -859,8 +1217,11 @@ C_Decrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEn spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pEncryptedData[ulEncryptedDataLen]", pEncryptedData, ulEncryptedDataLen); rv = po->C_Decrypt(hSession, pEncryptedData, ulEncryptedDataLen, pData, pulDataLen); - if (rv == CKR_OK) + if (rv == CKR_OK) { spy_dump_string_out("pData[*pulDataLen]", pData, *pulDataLen); + } else if (rv == CKR_BUFFER_TOO_SMALL) { + spy_dump_ulong_out("pulDataLen", *pulDataLen); + } return retne(rv); } @@ -889,8 +1250,11 @@ C_DecryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, CK_ULONG_PTR p enter("C_DecryptFinal"); spy_dump_ulong_in("hSession", hSession); rv = po->C_DecryptFinal(hSession, pLastPart, pulLastPartLen); - if (rv == CKR_OK) + if (rv == CKR_OK) { spy_dump_string_out("pLastPart[*pulLastPartLen]", pLastPart, *pulLastPartLen); + } else if (rv == CKR_BUFFER_TOO_SMALL) { + spy_dump_ulong_out("pulLastPartLen", *pulLastPartLen); + } return retne(rv); } @@ -902,7 +1266,7 @@ C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) enter("C_DigestInit"); spy_dump_ulong_in("hSession", hSession); - fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); + spy_dump_mechanism_in("pMechanism", pMechanism, false); rv = po->C_DigestInit(hSession, pMechanism); return retne(rv); } @@ -968,7 +1332,7 @@ C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HA enter("C_SignInit"); spy_dump_ulong_in("hSession", hSession); - fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); + spy_dump_mechanism_in("pMechanism", pMechanism, false); spy_dump_ulong_in("hKey", hKey); rv = po->C_SignInit(hSession, pMechanism, hKey); return retne(rv); @@ -984,8 +1348,11 @@ C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pData[ulDataLen]", pData, ulDataLen); rv = po->C_Sign(hSession, pData, ulDataLen, pSignature, pulSignatureLen); - if (rv == CKR_OK) + if (rv == CKR_OK) { spy_dump_string_out("pSignature[*pulSignatureLen]", pSignature, *pulSignatureLen); + } else if (rv == CKR_BUFFER_TOO_SMALL) { + spy_dump_ulong_out("pulSignatureLen", *pulSignatureLen); + } return retne(rv); } @@ -1010,8 +1377,11 @@ C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG_PTR pul enter("C_SignFinal"); spy_dump_ulong_in("hSession", hSession); rv = po->C_SignFinal(hSession, pSignature, pulSignatureLen); - if (rv == CKR_OK) + if (rv == CKR_OK) { spy_dump_string_out("pSignature[*pulSignatureLen]", pSignature, *pulSignatureLen); + } else if (rv == CKR_BUFFER_TOO_SMALL) { + spy_dump_ulong_out("pulSignatureLen", *pulSignatureLen); + } return retne(rv); } @@ -1023,8 +1393,7 @@ C_SignRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OB enter("C_SignRecoverInit"); spy_dump_ulong_in("hSession", hSession); - fprintf(spy_output, "pMechanism->type=%s\n", - lookup_enum(MEC_T, pMechanism->mechanism)); + spy_dump_mechanism_in("pMechanism", pMechanism, false); spy_dump_ulong_in("hKey", hKey); rv = po->C_SignRecoverInit(hSession, pMechanism, hKey); return retne(rv); @@ -1040,8 +1409,11 @@ C_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pData[ulDataLen]", pData, ulDataLen); rv = po->C_SignRecover(hSession, pData, ulDataLen, pSignature, pulSignatureLen); - if (rv == CKR_OK) + if (rv == CKR_OK) { spy_dump_string_out("pSignature[*pulSignatureLen]", pSignature, *pulSignatureLen); + } else if (rv == CKR_BUFFER_TOO_SMALL) { + spy_dump_ulong_out("pulSignatureLen", *pulSignatureLen); + } return retne(rv); } @@ -1052,7 +1424,7 @@ C_VerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_ enter("C_VerifyInit"); spy_dump_ulong_in("hSession", hSession); - fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); + spy_dump_mechanism_in("pMechanism", pMechanism, false); spy_dump_ulong_in("hKey", hKey); rv = po->C_VerifyInit(hSession, pMechanism, hKey); return retne(rv); @@ -1105,7 +1477,7 @@ C_VerifyRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, enter("C_VerifyRecoverInit"); spy_dump_ulong_in("hSession", hSession); - fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); + spy_dump_mechanism_in("pMechanism", pMechanism, false); spy_dump_ulong_in("hKey", hKey); rv = po->C_VerifyRecoverInit(hSession, pMechanism, hKey); return retne(rv); @@ -1198,7 +1570,7 @@ C_GenerateKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, enter("C_GenerateKey"); spy_dump_ulong_in("hSession", hSession); - fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); + spy_dump_mechanism_in("pMechanism", pMechanism, false); spy_attribute_list_in("pTemplate", pTemplate, ulCount); rv = po->C_GenerateKey(hSession, pMechanism, pTemplate, ulCount, phKey); if (rv == CKR_OK) @@ -1217,7 +1589,7 @@ C_GenerateKeyPair(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, enter("C_GenerateKeyPair"); spy_dump_ulong_in("hSession", hSession); - fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); + spy_dump_mechanism_in("pMechanism", pMechanism, false); spy_attribute_list_in("pPublicKeyTemplate", pPublicKeyTemplate, ulPublicKeyAttributeCount); spy_attribute_list_in("pPrivateKeyTemplate", pPrivateKeyTemplate, ulPrivateKeyAttributeCount); rv = po->C_GenerateKeyPair(hSession, pMechanism, @@ -1240,12 +1612,15 @@ C_WrapKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, enter("C_WrapKey"); spy_dump_ulong_in("hSession", hSession); - fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); + spy_dump_mechanism_in("pMechanism", pMechanism, false); spy_dump_ulong_in("hWrappingKey", hWrappingKey); spy_dump_ulong_in("hKey", hKey); rv = po->C_WrapKey(hSession, pMechanism, hWrappingKey, hKey, pWrappedKey, pulWrappedKeyLen); - if (rv == CKR_OK) + if (rv == CKR_OK) { spy_dump_string_out("pWrappedKey[*pulWrappedKeyLen]", pWrappedKey, *pulWrappedKeyLen); + } else if (rv == CKR_BUFFER_TOO_SMALL) { + spy_dump_ulong_out("pulWrappedKeyLen", *pulWrappedKeyLen); + } return retne(rv); } @@ -1260,7 +1635,7 @@ C_UnwrapKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, enter("C_UnwrapKey"); spy_dump_ulong_in("hSession", hSession); - fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); + spy_dump_mechanism_in("pMechanism", pMechanism, false); spy_dump_ulong_in("hUnwrappingKey", hUnwrappingKey); spy_dump_string_in("pWrappedKey[ulWrappedKeyLen]", pWrappedKey, ulWrappedKeyLen); spy_attribute_list_in("pTemplate", pTemplate, ulAttributeCount); @@ -1279,7 +1654,7 @@ C_DeriveKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_H enter("C_DeriveKey"); spy_dump_ulong_in("hSession", hSession); - fprintf(spy_output, "pMechanism->type=%s\n", lookup_enum(MEC_T, pMechanism->mechanism)); + spy_dump_mechanism_in("pMechanism", pMechanism, false); spy_dump_ulong_in("hBaseKey", hBaseKey); spy_attribute_list_in("pTemplate", pTemplate, ulAttributeCount); rv = po->C_DeriveKey(hSession, pMechanism, hBaseKey, pTemplate, ulAttributeCount, phKey); @@ -1343,6 +1718,766 @@ C_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pRserved) enter("C_WaitForSlotEvent"); spy_dump_ulong_in("flags", flags); + if (pSlot != NULL) { + spy_dump_ulong_in("pSlot", *pSlot); + } rv = po->C_WaitForSlotEvent(flags, pSlot, pRserved); return retne(rv); } + +/* Returns spied PKCS #11 3.0 interface based on the interface version returned from the + * underlying pkcs11 module, respecting major versions */ +static void +spy_interface_function_list(CK_INTERFACE_PTR pInterface, CK_INTERFACE_PTR_PTR retInterface) +{ + CK_VERSION *version; + + /* Do not touch unknown interfaces. We can not do anything with these */ + if (strcmp((char *)pInterface->pInterfaceName, "PKCS 11") != 0) { + *retInterface = pInterface; + return; + } + + version = (CK_VERSION *)pInterface->pFunctionList; + if (version->major == 3 && version->minor == 0) { + (*retInterface)->pFunctionList = pkcs11_spy_3_0; + } else { + (*retInterface)->pFunctionList = pkcs11_spy; + } +} + +CK_RV +C_GetInterfaceList(CK_INTERFACE_PTR pInterfacesList, CK_ULONG_PTR pulCount) +{ + CK_RV rv; + + if (po == NULL) { + CK_RV rv = init_spy(); + if (rv != CKR_OK) + return rv; + } + + enter("C_GetInterfaceList"); + if (po->version.major < 3 || po->C_GetInterfaceList == NULL) { + fprintf(spy_output, "[compat]\n"); + + if (pulCount == NULL_PTR) + return retne(CKR_ARGUMENTS_BAD); + + if (pInterfacesList == NULL_PTR) { + *pulCount = NUM_INTERFACES; + spy_dump_ulong_out("*pulCount", *pulCount); + return retne(CKR_OK); + } + spy_dump_ulong_in("*pulCount", *pulCount); + if (*pulCount < NUM_INTERFACES) { + *pulCount = NUM_INTERFACES; + spy_dump_ulong_out("*pulCount", *pulCount); + return retne(CKR_BUFFER_TOO_SMALL); + } + + memcpy(pInterfacesList, compat_interfaces, NUM_INTERFACES * sizeof(CK_INTERFACE)); + *pulCount = NUM_INTERFACES; + + spy_dump_desc_out("pInterfacesList"); + print_interfaces_list(spy_output, pInterfacesList, *pulCount); + spy_dump_ulong_out("*pulCount", *pulCount); + return retne(CKR_OK); + } + rv = po->C_GetInterfaceList(pInterfacesList, pulCount); + if (rv == CKR_OK) { + spy_dump_desc_out("pInterfacesList (original)"); + print_interfaces_list(spy_output, pInterfacesList, *pulCount); + + if (pInterfacesList != NULL) { + unsigned long i; + /* Record the module interface so we can transparently proxy the GetInterface calls */ + free(orig_interfaces); + num_orig_interfaces = 0; + orig_interfaces = malloc(*pulCount * sizeof(CK_INTERFACE)); + if (orig_interfaces == NULL) { + return CKR_HOST_MEMORY; + } + memcpy(orig_interfaces, pInterfacesList, *pulCount * sizeof(CK_INTERFACE)); + num_orig_interfaces = *pulCount; + + /* Now, replace function lists of known interfaces (PKCS 11, v 2.x and 3.0) */ + for (i = 0; i < *pulCount; i++) { + CK_INTERFACE_PTR pInterface = &pInterfacesList[i]; + spy_interface_function_list(pInterface, &pInterface); + } + } + + spy_dump_desc_out("pInterfacesList (faked)"); + print_interfaces_list(spy_output, pInterfacesList, *pulCount); + spy_dump_ulong_out("*pulCount", *pulCount); + } + return retne(rv); +} + +CK_RV +C_GetInterface(CK_UTF8CHAR_PTR pInterfaceName, CK_VERSION_PTR pVersion, + CK_INTERFACE_PTR_PTR ppInterface, CK_FLAGS flags) +{ + CK_RV rv; + + if (po == NULL) { + CK_RV rv = init_spy(); + if (rv != CKR_OK) + return rv; + } + + enter("C_GetInterface"); + if (po->version.major < 3 || po->C_GetInterface == NULL) { + fprintf(spy_output, "[compat]\n"); + } + if (pInterfaceName != NULL) { + spy_dump_string_in("pInterfaceName", pInterfaceName, strlen((char *)pInterfaceName)); + } else { + fprintf(spy_output, "[in] pInterfaceName = NULL\n"); + } + if (pVersion != NULL) { + fprintf(spy_output, "[in] pVersion = %d.%d\n", pVersion->major, pVersion->minor); + } else { + fprintf(spy_output, "[in] pVersion = NULL\n"); + } + fprintf(spy_output, "[in] flags = %s\n", + (flags & CKF_INTERFACE_FORK_SAFE ? "CKF_INTERFACE_FORK_SAFE" : "")); + if (po->version.major >= 3 && po->C_GetInterface != NULL) { + CK_VERSION in_version = {0, 0}; + CK_VERSION_PTR fakeVersion = NULL; + CK_INTERFACE_PTR rInterface = NULL; + + /* make a copy of the in parameter to avoid modifying it directly */ + if (pVersion) { + in_version = *pVersion; + fakeVersion = &in_version; + } + + /* We can not assume the version we told the caller matches the version in the underlying + * pkcs11 module so map it back to the known ones */ + if ((pInterfaceName == NULL || strcmp((char *)pInterfaceName, "PKCS 11") == 0) && pVersion) { + for (unsigned long i = 0; i < num_orig_interfaces; i++) { + CK_VERSION *v = (CK_VERSION *)orig_interfaces[i].pFunctionList; + /* We found the same major version. Copy the minor and call it a day */ + if (v->major == pVersion->major) { + in_version.major = v->major; + in_version.minor = v->minor; + fprintf(spy_output, "[in] fakeVersion = %d.%d (faked pVersion)\n", + in_version.major, in_version.minor); + break; + } + } + /* If not found, see what we will get */ + } + + rv = po->C_GetInterface(pInterfaceName, fakeVersion, &rInterface, flags); + if (rv == CKR_OK && rInterface != NULL) { + *ppInterface = &spy_interface; + spy_interface_function_list(rInterface, ppInterface); + } + } else { + if ((pInterfaceName == NULL_PTR || strcmp((char *)pInterfaceName, "PKCS 11") == 0) && + (pVersion == NULL_PTR || (pVersion->major == 2 && pVersion->minor == 11)) && + flags == 0) { + *ppInterface = &compat_interfaces[0]; + return retne(CKR_OK); + } + /* We can not serve this particular interface */ + return retne(CKR_ARGUMENTS_BAD); + } + + return retne(rv); +} + +CK_RV +C_LoginUser(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, + CK_CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pUsername, CK_ULONG ulUsernameLen) +{ + CK_RV rv; + + enter("C_LoginUser"); + spy_dump_ulong_in("hSession", hSession); + FPRINTF_LOOKUP_ENUM("[in] userType = %s\n", USR_T, userType); + spy_dump_string_in("pPin[ulPinLen]", pPin, ulPinLen); + spy_dump_string_in("pUsername[ulUsernameLen]", pUsername, ulUsernameLen); + rv = po->C_LoginUser(hSession, userType, pPin, ulPinLen, pUsername, ulUsernameLen); + + return retne(rv); +} + +CK_RV C_SessionCancel(CK_SESSION_HANDLE hSession, CK_FLAGS flags) +{ + CK_RV rv; + + enter("C_SessionCancel"); + spy_dump_ulong_in("hSession", hSession); + fprintf(spy_output, "[in] flags = %s%s%s%s%s%s%s%s%s%s%s%s\n", + (flags & CKF_ENCRYPT) ? "Encrypt " : "", + (flags & CKF_DECRYPT) ? "Decrypt " : "", + (flags & CKF_DIGEST) ? "Digest " : "", + (flags & CKF_SIGN) ? "Sign " : "", + (flags & CKF_SIGN_RECOVER) ? "SigRecov " : "", + (flags & CKF_VERIFY) ? "Verify " : "", + (flags & CKF_VERIFY_RECOVER) ? "VerRecov " : "", + (flags & CKF_GENERATE) ? "Generate " : "", + (flags & CKF_GENERATE_KEY_PAIR) ? "KeyPair " : "", + (flags & CKF_WRAP) ? "Wrap " : "", + (flags & CKF_UNWRAP) ? "Unwrap " : "", + (flags & CKF_DERIVE) ? "Derive " : ""); + rv = po->C_SessionCancel(hSession, flags); + + return retne(rv); +} + +CK_RV +C_MessageEncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + CK_RV rv; + + enter("C_MessageEncryptInit"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_mechanism_in("pMechanism", pMechanism, true); + spy_dump_ulong_in("hKey", hKey); + rv = po->C_MessageEncryptInit(hSession, pMechanism, hKey); + return retne(rv); +} + +CK_RV +C_EncryptMessage(CK_SESSION_HANDLE hSession, CK_VOID_PTR pParameter, CK_ULONG ulParameterLen, + CK_BYTE_PTR pAssociatedData, CK_ULONG ulAssociatedDataLen, + CK_BYTE_PTR pPlaintext, CK_ULONG ulPlaintextLen, + CK_BYTE_PTR pCiphertext, CK_ULONG_PTR pulCiphertextLen) +{ + CK_RV rv; + + enter("C_EncryptMessage"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pParameter[ulParameterLen]", pParameter, ulParameterLen); + spy_dump_string_in("pAssociatedData[ulAssociatedDataLen]", pAssociatedData, ulAssociatedDataLen); + spy_dump_string_in("pPlaintext[ulPlaintextLen]", pPlaintext, ulPlaintextLen); + rv = po->C_EncryptMessage(hSession, pParameter, ulParameterLen, + pAssociatedData, ulAssociatedDataLen, pPlaintext, ulPlaintextLen, + pCiphertext, pulCiphertextLen); + if (rv == CKR_OK) { + spy_dump_string_out("pCiphertext[*pulCiphertextLen]", pCiphertext, *pulCiphertextLen); + } + return retne(rv); +} + +CK_RV +C_EncryptMessageBegin(CK_SESSION_HANDLE hSession, CK_VOID_PTR pParameter, CK_ULONG ulParameterLen, + CK_BYTE_PTR pAssociatedData, CK_ULONG ulAssociatedDataLen) +{ + CK_RV rv; + + enter("C_EncryptMessageBegin"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pParameter[ulParameterLen]", pParameter, ulParameterLen); + spy_dump_string_in("pAssociatedData[ulAssociatedDataLen]", pAssociatedData, ulAssociatedDataLen); + rv = po->C_EncryptMessageBegin(hSession, pParameter, ulParameterLen, + pAssociatedData, ulAssociatedDataLen); + return retne(rv); +} + +CK_RV +C_EncryptMessageNext(CK_SESSION_HANDLE hSession, CK_VOID_PTR pParameter, CK_ULONG ulParameterLen, + CK_BYTE_PTR pPlaintextPart, CK_ULONG ulPlaintextPartLen, + CK_BYTE_PTR pCiphertextPart, CK_ULONG_PTR pulCiphertextPartLen, CK_FLAGS flags) +{ + CK_RV rv; + + enter("C_EncryptMessageNext"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pParameter[ulParameterLen]", pParameter, ulParameterLen); + spy_dump_string_in("pPlaintextPart[ulPlaintextPartLen]", pPlaintextPart, ulPlaintextPartLen); + rv = po->C_EncryptMessageNext(hSession, pParameter, ulParameterLen, + pPlaintextPart, ulPlaintextPartLen, pCiphertextPart, pulCiphertextPartLen, flags); + if (rv == CKR_OK) { + spy_dump_string_out("pCiphertextPart[*pulCiphertextPartLen]", + pCiphertextPart, *pulCiphertextPartLen); + } + fprintf(spy_output, "[in] flags = %s\n", + (flags & CKF_END_OF_MESSAGE ? "CKF_END_OF_MESSAGE" : "")); + return retne(rv); +} + +CK_RV +C_MessageEncryptFinal(CK_SESSION_HANDLE hSession) +{ + CK_RV rv; + + enter("C_MessageEncryptFinal"); + spy_dump_ulong_in("hSession", hSession); + rv = po->C_MessageEncryptFinal(hSession); + return retne(rv); +} + +CK_RV +C_MessageDecryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + CK_RV rv; + + enter("C_MessageDecryptInit"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_mechanism_in("pMechanism", pMechanism, true); + spy_dump_ulong_in("hKey", hKey); + rv = po->C_MessageDecryptInit(hSession, pMechanism, hKey); + return retne(rv); +} + +CK_RV +C_DecryptMessage(CK_SESSION_HANDLE hSession, CK_VOID_PTR pParameter, CK_ULONG ulParameterLen, + CK_BYTE_PTR pAssociatedData, CK_ULONG ulAssociatedDataLen, + CK_BYTE_PTR pCiphertext, CK_ULONG ulCiphertextLen, + CK_BYTE_PTR pPlaintext, CK_ULONG_PTR pulPlaintextLen) +{ + CK_RV rv; + + enter("C_DecryptMessage"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pParameter[ulParameterLen]", pParameter, ulParameterLen); + spy_dump_string_in("pAssociatedData[ulAssociatedDataLen]", pAssociatedData, ulAssociatedDataLen); + spy_dump_string_in("pCiphertext[ulCiphertextLen]", pCiphertext, ulCiphertextLen); + rv = po->C_DecryptMessage(hSession, pParameter, ulParameterLen, + pAssociatedData, ulAssociatedDataLen, pCiphertext, ulCiphertextLen, + pPlaintext, pulPlaintextLen); + if (rv == CKR_OK) { + spy_dump_string_out("pPlaintext[*pulPlaintextLen]", pPlaintext, *pulPlaintextLen); + } + return retne(rv); +} + +CK_RV +C_DecryptMessageBegin(CK_SESSION_HANDLE hSession, CK_VOID_PTR pParameter, CK_ULONG ulParameterLen, + CK_BYTE_PTR pAssociatedData, CK_ULONG ulAssociatedDataLen) +{ + CK_RV rv; + + enter("C_DecryptMessageBegin"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pParameter[ulParameterLen]", pParameter, ulParameterLen); + spy_dump_string_in("pAssociatedData[ulAssociatedDataLen]", pAssociatedData, ulAssociatedDataLen); + rv = po->C_DecryptMessageBegin(hSession, pParameter, ulParameterLen, + pAssociatedData, ulAssociatedDataLen); + return retne(rv); +} + +CK_RV +C_DecryptMessageNext(CK_SESSION_HANDLE hSession, CK_VOID_PTR pParameter, CK_ULONG ulParameterLen, + CK_BYTE_PTR pCiphertextPart, CK_ULONG ulCiphertextPartLen, + CK_BYTE_PTR pPlaintextPart, CK_ULONG_PTR pulPlaintextPartLen, CK_FLAGS flags) +{ + CK_RV rv; + + enter("C_DecryptMessageNext"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pParameter[ulParameterLen]", pParameter, ulParameterLen); + spy_dump_string_in("pCiphertextPart[ulCiphertextPartLen]", pCiphertextPart, ulCiphertextPartLen); + rv = po->C_DecryptMessageNext(hSession, pParameter, ulParameterLen, + pCiphertextPart, ulCiphertextPartLen, pPlaintextPart, pulPlaintextPartLen, flags); + if (rv == CKR_OK) { + spy_dump_string_out("pPlaintextPart[*pulPlaintextPartLen]", + pPlaintextPart, *pulPlaintextPartLen); + } + fprintf(spy_output, "[in] flags = %s\n", + (flags & CKF_END_OF_MESSAGE ? "CKF_END_OF_MESSAGE" : "")); + return retne(rv); +} + +CK_RV +C_MessageDecryptFinal(CK_SESSION_HANDLE hSession) +{ + CK_RV rv; + + enter("C_MessageDecryptFinal"); + spy_dump_ulong_in("hSession", hSession); + rv = po->C_MessageDecryptFinal(hSession); + return retne(rv); +} + +CK_RV +C_MessageSignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + CK_RV rv; + + enter("C_MessageSignInit"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_mechanism_in("pMechanism", pMechanism, true); + spy_dump_ulong_in("hKey", hKey); + rv = po->C_MessageSignInit(hSession, pMechanism, hKey); + return retne(rv); +} + +CK_RV +C_SignMessage(CK_SESSION_HANDLE hSession, CK_VOID_PTR pParameter, CK_ULONG ulParameterLen, + CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) +{ + CK_RV rv; + + enter("C_SignMessage"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pParameter[ulParameterLen]", pParameter, ulParameterLen); + spy_dump_string_in("pData[ulDataLen]", pData, ulDataLen); + rv = po->C_SignMessage(hSession, pParameter, ulParameterLen, + pData, ulDataLen, pSignature, pulSignatureLen); + if (rv == CKR_OK) { + spy_dump_string_out("pSignature[*pulSignatureLen]", pSignature, *pulSignatureLen); + } + return retne(rv); +} + +CK_RV +C_SignMessageBegin(CK_SESSION_HANDLE hSession, CK_VOID_PTR pParameter, CK_ULONG ulParameterLen) +{ + CK_RV rv; + + enter("C_SignMessageBegin"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pParameter[ulParameterLen]", pParameter, ulParameterLen); + rv = po->C_SignMessageBegin(hSession, pParameter, ulParameterLen); + return retne(rv); +} + +CK_RV +C_SignMessageNext(CK_SESSION_HANDLE hSession, CK_VOID_PTR pParameter, CK_ULONG ulParameterLen, + CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) +{ + CK_RV rv; + + enter("C_SignMessageNext"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pParameter[ulParameterLen]", pParameter, ulParameterLen); + spy_dump_string_in("pData[ulDataLen]", pData, ulDataLen); + rv = po->C_SignMessageNext(hSession, pParameter, ulParameterLen, + pData, ulDataLen, pSignature, pulSignatureLen); + if (rv == CKR_OK) { + spy_dump_string_out("pSignature[*pulSignatureLen]", pSignature, *pulSignatureLen); + } + return retne(rv); +} + +CK_RV +C_MessageSignFinal(CK_SESSION_HANDLE hSession) +{ + CK_RV rv; + + enter("C_MessageSignFinal"); + spy_dump_ulong_in("hSession", hSession); + rv = po->C_MessageSignFinal(hSession); + return retne(rv); +} + +CK_RV +C_MessageVerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + CK_RV rv; + + enter("C_MessageVerifyInit"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_mechanism_in("pMechanism", pMechanism, true); + spy_dump_ulong_in("hKey", hKey); + rv = po->C_MessageVerifyInit(hSession, pMechanism, hKey); + return retne(rv); +} + +CK_RV +C_VerifyMessage(CK_SESSION_HANDLE hSession, CK_VOID_PTR pParameter, CK_ULONG ulParameterLen, + CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) +{ + CK_RV rv; + + enter("C_VerifyMessage"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pParameter[ulParameterLen]", pParameter, ulParameterLen); + spy_dump_string_in("pData[ulDataLen]", pData, ulDataLen); + spy_dump_string_in("pSignature[ulSignatureLen]", pSignature, ulSignatureLen); + rv = po->C_VerifyMessage(hSession, pParameter, ulParameterLen, + pData, ulDataLen, pSignature, ulSignatureLen); + return retne(rv); +} + +CK_RV +C_VerifyMessageBegin(CK_SESSION_HANDLE hSession, CK_VOID_PTR pParameter, CK_ULONG ulParameterLen) +{ + CK_RV rv; + + enter("C_VerifyMessageBegin"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pParameter[ulParameterLen]", pParameter, ulParameterLen); + rv = po->C_VerifyMessageBegin(hSession, pParameter, ulParameterLen); + return retne(rv); +} + +CK_RV C_VerifyMessageNext(CK_SESSION_HANDLE hSession, CK_VOID_PTR pParameter, CK_ULONG ulParameterLen, + CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) +{ + CK_RV rv; + + enter("C_VerifyMessageNext"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pParameter[ulParameterLen]", pParameter, ulParameterLen); + spy_dump_string_in("pData[ulDataLen]", pData, ulDataLen); + spy_dump_string_in("pSignature[ulSignatureLen]", pSignature, ulSignatureLen); + rv = po->C_VerifyMessageNext(hSession, pParameter, ulParameterLen, + pData, ulDataLen, pSignature, ulSignatureLen); + return retne(rv); +} + +CK_RV C_MessageVerifyFinal(CK_SESSION_HANDLE hSession) +{ + CK_RV rv; + + enter("C_MessageVerifyFinal"); + spy_dump_ulong_in("hSession", hSession); + rv = po->C_MessageVerifyFinal(hSession); + return retne(rv); +} + +CK_RV +C_EncapsulateKey(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hPublicKey, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_BYTE_PTR pCiphertext, + CK_ULONG_PTR pulCiphertextLen, + CK_OBJECT_HANDLE_PTR phKey) +{ + CK_RV rv; + + enter("C_EncapsulateKey"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_mechanism_in("pMechanism", pMechanism, false); + spy_dump_ulong_in("hPublicKey", hPublicKey); + spy_attribute_list_in("pTemplate", pTemplate, ulAttributeCount); + rv = po->C_EncapsulateKey(hSession, pMechanism, hPublicKey, pTemplate, ulAttributeCount, + pCiphertext, pulCiphertextLen, phKey); + if (rv == CKR_OK) { + spy_dump_string_out("pCiphertext[*pulCiphertextLen]", + pCiphertext, *pulCiphertextLen); + spy_dump_ulong_out("phKey", *phKey); + } + return retne(rv); +} + +CK_RV +C_DecapsulateKey(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hPrivateKey, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_BYTE_PTR pCiphertext, + CK_ULONG ulCiphertextLen, + CK_OBJECT_HANDLE_PTR phKey) +{ + CK_RV rv; + + enter("C_DecapsulateKey"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_mechanism_in("pMechanism", pMechanism, false); + spy_dump_ulong_in("hPrivateKey", hPrivateKey); + spy_attribute_list_in("pTemplate", pTemplate, ulAttributeCount); + spy_dump_string_in("pCiphertext[ulCiphertextLen]", + pCiphertext, ulCiphertextLen); + rv = po->C_DecapsulateKey(hSession, pMechanism, hPrivateKey, pTemplate, ulAttributeCount, + pCiphertext, ulCiphertextLen, phKey); + if (rv == CKR_OK) { + spy_dump_ulong_out("phKey", *phKey); + } + return retne(rv); +} + +CK_RV +C_VerifySignatureInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey, + CK_BYTE_PTR pSignature, + CK_ULONG ulSignatureLen) +{ + CK_RV rv; + + enter("C_VerifySignatureInit"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_mechanism_in("pMechanism", pMechanism, false); + spy_dump_ulong_in("hKey", hKey); + spy_dump_string_in("pSignature[ulSignatureLen]", + pSignature, ulSignatureLen); + rv = po->C_VerifySignatureInit(hSession, pMechanism, hKey, + pSignature, ulSignatureLen); + return retne(rv); +} + +CK_RV +C_VerifySignature(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen) +{ + CK_RV rv; + + enter("C_VerifySignature"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pData[ulDataLen]", pData, ulDataLen); + rv = po->C_VerifySignature(hSession, pData, ulDataLen); + return retne(rv); +} + +CK_RV +C_VerifySignatureUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) +{ + CK_RV rv; + + enter("C_VerifySignatureUpdate"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pPart[ulPartLen]", pPart, ulPartLen); + rv = po->C_VerifySignatureUpdate(hSession, pPart, ulPartLen); + return retne(rv); +} + +CK_RV +C_VerifySignatureFinal(CK_SESSION_HANDLE hSession) +{ + CK_RV rv; + + enter("C_VerifySignatureFinal"); + spy_dump_ulong_in("hSession", hSession); + rv = po->C_VerifySignatureFinal(hSession); + return retne(rv); +} + +CK_RV +C_GetSessionValidationFlags(CK_SESSION_HANDLE hSession, + CK_SESSION_VALIDATION_FLAGS_TYPE type, + CK_FLAGS_PTR pFlags) +{ + CK_RV rv; + + enter("C_GetSessionValidationFlags"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_ulong_in("type", type); + rv = po->C_GetSessionValidationFlags(hSession, type, pFlags); + if (rv == CKR_OK) { + spy_dump_ulong_out("pFlags", *pFlags); + } + return retne(rv); +} + +CK_RV +C_AsyncComplete(CK_SESSION_HANDLE hSession, + CK_UTF8CHAR_PTR pFunctionName, + CK_ASYNC_DATA_PTR pResult) +{ + CK_RV rv; + + enter("C_AsyncComplete"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pFunctionName", pFunctionName, strlen((char *)pFunctionName)); + rv = po->C_AsyncComplete(hSession, pFunctionName, pResult); + if (rv == CKR_OK) { + if (pResult == NULL) { + fprintf(spy_output, "[out] pResult = NULL\n"); + } else { + spy_dump_ulong_out("pResult->ulVersion", pResult->ulVersion); + spy_dump_string_out("pResult->pValue[pResult->ulValue]", + pResult->pValue, pResult->ulValue); + spy_dump_ulong_out("pResult->hObject", pResult->hObject); + spy_dump_ulong_out("pResult->hAdditionalObject", pResult->hAdditionalObject); + } + } + return retne(rv); +} + +CK_RV +C_AsyncGetID(CK_SESSION_HANDLE hSession, + CK_UTF8CHAR_PTR pFunctionName, + CK_ULONG_PTR pulID) +{ + CK_RV rv; + + enter("C_AsyncGetID"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pFunctionName", pFunctionName, strlen((char *)pFunctionName)); + rv = po->C_AsyncGetID(hSession, pFunctionName, pulID); + if (rv == CKR_OK) { + spy_dump_ulong_out("pulID", *pulID); + } + return retne(rv); +} + +CK_RV +C_AsyncJoin(CK_SESSION_HANDLE hSession, + CK_UTF8CHAR_PTR pFunctionName, + CK_ULONG ulID, + CK_BYTE_PTR pData, + CK_ULONG ulData) +{ + CK_RV rv; + + enter("C_AsyncJoin"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_string_in("pFunctionName", pFunctionName, strlen((char *)pFunctionName)); + spy_dump_ulong_in("ulID", ulID); + rv = po->C_AsyncJoin(hSession, pFunctionName, ulID, pData, ulData); + if (rv == CKR_OK) { + spy_dump_string_out("pData[ulData]", pData, ulData); + } + return retne(rv); +} + +CK_RV +C_WrapKeyAuthenticated(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hWrappingKey, + CK_OBJECT_HANDLE hKey, + CK_BYTE_PTR pAssociatedData, + CK_ULONG ulAssociatedDataLen, + CK_BYTE_PTR pWrappedKey, + CK_ULONG_PTR pulWrappedKeyLen) +{ + CK_RV rv; + + enter("C_WrapKeyAuthenticated"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_mechanism_in("pMechanism", pMechanism, false); + spy_dump_ulong_in("hWrappingKey", hWrappingKey); + spy_dump_ulong_in("hKey", hKey); + spy_dump_string_in("pAssociatedData[ulAssociatedDataLen]", + pAssociatedData, ulAssociatedDataLen); + rv = po->C_WrapKeyAuthenticated(hSession, pMechanism, hWrappingKey, hKey, + pAssociatedData, ulAssociatedDataLen, pWrappedKey, pulWrappedKeyLen); + if (rv == CKR_OK) { + spy_dump_string_out("pWrappedKey[*pulWrappedKeyLen]", pWrappedKey, *pulWrappedKeyLen); + } else if (rv == CKR_BUFFER_TOO_SMALL) { + spy_dump_ulong_out("pulWrappedKeyLen", *pulWrappedKeyLen); + } + + return retne(rv); +} + +CK_RV +C_UnwrapKeyAuthenticated(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hUnwrappingKey, + CK_BYTE_PTR pWrappedKey, + CK_ULONG ulWrappedKeyLen, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_BYTE_PTR pAssociatedData, + CK_ULONG ulAssociatedDataLen, + CK_OBJECT_HANDLE_PTR phKey) +{ + CK_RV rv; + + enter("C_UnwrapKeyAuthenticated"); + spy_dump_ulong_in("hSession", hSession); + spy_dump_mechanism_in("pMechanism", pMechanism, false); + spy_dump_ulong_in("hUnwrappingKey", hUnwrappingKey); + spy_dump_string_in("pWrappedKey[ulWrappedKeyLen]", pWrappedKey, ulWrappedKeyLen); + spy_attribute_list_in("pTemplate", pTemplate, ulAttributeCount); + spy_dump_string_in("pAssociatedData[ulAssociatedDataLen]", + pAssociatedData, ulAssociatedDataLen); + rv = po->C_UnwrapKeyAuthenticated(hSession, pMechanism, hUnwrappingKey, + pWrappedKey, ulWrappedKeyLen, pTemplate, ulAttributeCount, + pAssociatedData, ulAssociatedDataLen, phKey); + if (rv == CKR_OK) + spy_dump_ulong_out("hKey", *phKey); + return retne(rv); +} diff --git a/src/pkcs11/pkcs11.exports b/src/pkcs11/pkcs11.exports index 9b9b01c5e1..cff97d1356 100644 --- a/src/pkcs11/pkcs11.exports +++ b/src/pkcs11/pkcs11.exports @@ -66,5 +66,39 @@ C_GenerateRandom C_GetFunctionStatus C_CancelFunction C_WaitForSlotEvent -C_Initialize -C_Finalize +C_GetInterfaceList +C_GetInterface +C_LoginUser +C_SessionCancel +C_MessageEncryptInit +C_EncryptMessage +C_EncryptMessageBegin +C_EncryptMessageNext +C_MessageEncryptFinal +C_MessageDecryptInit +C_DecryptMessage +C_DecryptMessageBegin +C_DecryptMessageNext +C_MessageDecryptFinal +C_MessageSignInit +C_SignMessage +C_SignMessageBegin +C_SignMessageNext +C_MessageSignFinal +C_MessageVerifyInit +C_VerifyMessage +C_VerifyMessageBegin +C_VerifyMessageNext +C_MessageVerifyFinal +C_EncapsulateKey +C_DecapsulateKey +C_VerifySignatureInit +C_VerifySignature +C_VerifySignatureUpdate +C_VerifySignatureFinal +C_GetSessionValidationFlags +C_AsyncComplete +C_AsyncGetID +C_AsyncJoin +C_WrapKeyAuthenticated +C_UnwrapKeyAuthenticated diff --git a/src/pkcs11/pkcs11.h b/src/pkcs11/pkcs11.h index 74b13c6894..763d212448 100644 --- a/src/pkcs11/pkcs11.h +++ b/src/pkcs11/pkcs11.h @@ -1,54 +1,20 @@ -/* pkcs11.h - Copyright 2006, 2007 g10 Code GmbH - Copyright 2006 Andreas Jellinghaus - - This file is free software; as a special exception the author gives - unlimited permission to copy and/or distribute it, with or without - modifications, as long as this notice is preserved. - - This file is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY, to the extent permitted by law; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. */ - -/* Please submit changes back to the Scute project at - http://www.scute.org/ (or send them to marcus@g10code.com), so that - they can be picked up by other projects from there as well. */ - -/* This file is a modified implementation of the PKCS #11 standard by - RSA Security Inc. It is mostly a drop-in replacement, with the - following change: - - This header file does not require any macro definitions by the user - (like CK_DEFINE_FUNCTION etc). In fact, it defines those macros - for you (if useful, some are missing, let me know if you need - more). - - There is an additional API available that does comply better to the - GNU coding standard. It can be switched on by defining - CRYPTOKI_GNU before including this header file. For this, the - following changes are made to the specification: - - All structure types are changed to a "struct ck_foo" where CK_FOO - is the type name in PKCS #11. - - All non-structure types are changed to ck_foo_t where CK_FOO is the - lowercase version of the type name in PKCS #11. The basic types - (CK_ULONG et al.) are removed without substitute. - - All members of structures are modified in the following way: Type - indication prefixes are removed, and underscore characters are - inserted before words. Then the result is lowercased. - - Note that function names are still in the original case, as they - need for ABI compatibility. - - CK_FALSE, CK_TRUE and NULL_PTR are removed without substitute. Use - . - - If CRYPTOKI_COMPAT is defined before including this header file, - then none of the API changes above take place, and the API is the - one defined by the PKCS #11 standard. */ +/* + * pkcs11.h: OpenSC project's PKCS#11 header + * + * 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 + */ #ifndef PKCS11_H #define PKCS11_H 1 @@ -57,27 +23,7 @@ extern "C" { #endif - -/* The version of cryptoki we implement. The revision is changed with - each modification of this file. If you do not use the "official" - version of this file, please consider deleting the revision macro - (you may use a macro with a different name to keep track of your - versions). */ -#define CRYPTOKI_VERSION_MAJOR 2 -#define CRYPTOKI_VERSION_MINOR 20 -#define CRYPTOKI_VERSION_REVISION 6 - - -/* Compatibility interface is default, unless CRYPTOKI_GNU is - given. */ -#ifndef CRYPTOKI_GNU -#ifndef CRYPTOKI_COMPAT -#define CRYPTOKI_COMPAT 1 -#endif -#endif - /* System dependencies. */ - #if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32) /* There is a matching pop below. */ @@ -95,1163 +41,8 @@ extern "C" { #endif -#ifdef CRYPTOKI_COMPAT - /* If we are in compatibility mode, switch all exposed names to the - PKCS #11 variant. There are corresponding #undefs below. */ - -#define ck_flags_t CK_FLAGS -#define ck_version _CK_VERSION - -#define ck_info _CK_INFO -#define cryptoki_version cryptokiVersion -#define manufacturer_id manufacturerID -#define library_description libraryDescription -#define library_version libraryVersion - -#define ck_notification_t CK_NOTIFICATION -#define ck_slot_id_t CK_SLOT_ID - -#define ck_slot_info _CK_SLOT_INFO -#define slot_description slotDescription -#define hardware_version hardwareVersion -#define firmware_version firmwareVersion - -#define ck_token_info _CK_TOKEN_INFO -#define serial_number serialNumber -#define max_session_count ulMaxSessionCount -#define session_count ulSessionCount -#define max_rw_session_count ulMaxRwSessionCount -#define rw_session_count ulRwSessionCount -#define max_pin_len ulMaxPinLen -#define min_pin_len ulMinPinLen -#define total_public_memory ulTotalPublicMemory -#define free_public_memory ulFreePublicMemory -#define total_private_memory ulTotalPrivateMemory -#define free_private_memory ulFreePrivateMemory -#define utc_time utcTime - -#define ck_session_handle_t CK_SESSION_HANDLE -#define ck_user_type_t CK_USER_TYPE -#define ck_state_t CK_STATE - -#define ck_session_info _CK_SESSION_INFO -#define slot_id slotID -#define device_error ulDeviceError - -#define ck_object_handle_t CK_OBJECT_HANDLE -#define ck_object_class_t CK_OBJECT_CLASS -#define ck_hw_feature_type_t CK_HW_FEATURE_TYPE -#define ck_key_type_t CK_KEY_TYPE -#define ck_certificate_type_t CK_CERTIFICATE_TYPE -#define ck_attribute_type_t CK_ATTRIBUTE_TYPE - -#define ck_attribute _CK_ATTRIBUTE -#define value pValue -#define value_len ulValueLen - -#define ck_date _CK_DATE - -#define ck_mechanism_type_t CK_MECHANISM_TYPE - -#define ck_mechanism _CK_MECHANISM -#define parameter pParameter -#define parameter_len ulParameterLen - -#define ck_mechanism_info _CK_MECHANISM_INFO -#define min_key_size ulMinKeySize -#define max_key_size ulMaxKeySize - -#define ck_rv_t CK_RV -#define ck_notify_t CK_NOTIFY - -#define ck_function_list _CK_FUNCTION_LIST - -#define ck_createmutex_t CK_CREATEMUTEX -#define ck_destroymutex_t CK_DESTROYMUTEX -#define ck_lockmutex_t CK_LOCKMUTEX -#define ck_unlockmutex_t CK_UNLOCKMUTEX - -#define ck_c_initialize_args _CK_C_INITIALIZE_ARGS -#define create_mutex CreateMutex -#define destroy_mutex DestroyMutex -#define lock_mutex LockMutex -#define unlock_mutex UnlockMutex -#define reserved pReserved - -#endif /* CRYPTOKI_COMPAT */ - - -typedef unsigned long ck_flags_t; - -struct ck_version -{ - unsigned char major; - unsigned char minor; -}; - - -struct ck_info -{ - struct ck_version cryptoki_version; - unsigned char manufacturer_id[32]; - ck_flags_t flags; - unsigned char library_description[32]; - struct ck_version library_version; -}; - - -typedef unsigned long ck_notification_t; - -#define CKN_SURRENDER (0UL) - - -typedef unsigned long ck_slot_id_t; - - -struct ck_slot_info -{ - unsigned char slot_description[64]; - unsigned char manufacturer_id[32]; - ck_flags_t flags; - struct ck_version hardware_version; - struct ck_version firmware_version; -}; - - -#define CKF_TOKEN_PRESENT (1UL << 0) -#define CKF_REMOVABLE_DEVICE (1UL << 1) -#define CKF_HW_SLOT (1UL << 2) -#define CKF_ARRAY_ATTRIBUTE (1UL << 30) - - -struct ck_token_info -{ - unsigned char label[32]; - unsigned char manufacturer_id[32]; - unsigned char model[16]; - unsigned char serial_number[16]; - ck_flags_t flags; - unsigned long max_session_count; - unsigned long session_count; - unsigned long max_rw_session_count; - unsigned long rw_session_count; - unsigned long max_pin_len; - unsigned long min_pin_len; - unsigned long total_public_memory; - unsigned long free_public_memory; - unsigned long total_private_memory; - unsigned long free_private_memory; - struct ck_version hardware_version; - struct ck_version firmware_version; - unsigned char utc_time[16]; -}; - - -#define CKF_RNG (1UL << 0) -#define CKF_WRITE_PROTECTED (1UL << 1) -#define CKF_LOGIN_REQUIRED (1UL << 2) -#define CKF_USER_PIN_INITIALIZED (1UL << 3) -#define CKF_RESTORE_KEY_NOT_NEEDED (1UL << 5) -#define CKF_CLOCK_ON_TOKEN (1UL << 6) -#define CKF_PROTECTED_AUTHENTICATION_PATH (1UL << 8) -#define CKF_DUAL_CRYPTO_OPERATIONS (1UL << 9) -#define CKF_TOKEN_INITIALIZED (1UL << 10) -#define CKF_SECONDARY_AUTHENTICATION (1UL << 11) -#define CKF_USER_PIN_COUNT_LOW (1UL << 16) -#define CKF_USER_PIN_FINAL_TRY (1UL << 17) -#define CKF_USER_PIN_LOCKED (1UL << 18) -#define CKF_USER_PIN_TO_BE_CHANGED (1UL << 19) -#define CKF_SO_PIN_COUNT_LOW (1UL << 20) -#define CKF_SO_PIN_FINAL_TRY (1UL << 21) -#define CKF_SO_PIN_LOCKED (1UL << 22) -#define CKF_SO_PIN_TO_BE_CHANGED (1UL << 23) - -#define CK_UNAVAILABLE_INFORMATION ((unsigned long) -1) -#define CK_EFFECTIVELY_INFINITE (0UL) - - -typedef unsigned long ck_session_handle_t; - -#define CK_INVALID_HANDLE (0UL) - - -typedef unsigned long ck_user_type_t; - -#define CKU_SO (0UL) -#define CKU_USER (1UL) -#define CKU_CONTEXT_SPECIFIC (2UL) - - -typedef unsigned long ck_state_t; - -#define CKS_RO_PUBLIC_SESSION (0UL) -#define CKS_RO_USER_FUNCTIONS (1UL) -#define CKS_RW_PUBLIC_SESSION (2UL) -#define CKS_RW_USER_FUNCTIONS (3UL) -#define CKS_RW_SO_FUNCTIONS (4UL) - - -struct ck_session_info -{ - ck_slot_id_t slot_id; - ck_state_t state; - ck_flags_t flags; - unsigned long device_error; -}; - -#define CKF_RW_SESSION (1UL << 1) -#define CKF_SERIAL_SESSION (1UL << 2) - - -typedef unsigned long ck_object_handle_t; - - -typedef unsigned long ck_object_class_t; - -#define CKO_DATA (0UL) -#define CKO_CERTIFICATE (1UL) -#define CKO_PUBLIC_KEY (2UL) -#define CKO_PRIVATE_KEY (3UL) -#define CKO_SECRET_KEY (4UL) -#define CKO_HW_FEATURE (5UL) -#define CKO_DOMAIN_PARAMETERS (6UL) -#define CKO_MECHANISM (7UL) -#define CKO_VENDOR_DEFINED (1UL << 31) - - -typedef unsigned long ck_hw_feature_type_t; - -#define CKH_MONOTONIC_COUNTER (1UL) -#define CKH_CLOCK (2UL) -#define CKH_USER_INTERFACE (3UL) -#define CKH_VENDOR_DEFINED (1UL << 31) - - -typedef unsigned long ck_key_type_t; - -#define CKK_RSA (0UL) -#define CKK_DSA (1UL) -#define CKK_DH (2UL) -#define CKK_ECDSA (3UL) -#define CKK_EC (3UL) -#define CKK_X9_42_DH (4UL) -#define CKK_KEA (5UL) -#define CKK_GENERIC_SECRET (0x10UL) -#define CKK_RC2 (0x11UL) -#define CKK_RC4 (0x12UL) -#define CKK_DES (0x13UL) -#define CKK_DES2 (0x14UL) -#define CKK_DES3 (0x15UL) -#define CKK_CAST (0x16UL) -#define CKK_CAST3 (0x17UL) -#define CKK_CAST128 (0x18UL) -#define CKK_RC5 (0x19UL) -#define CKK_IDEA (0x1aUL) -#define CKK_SKIPJACK (0x1bUL) -#define CKK_BATON (0x1cUL) -#define CKK_JUNIPER (0x1dUL) -#define CKK_CDMF (0x1eUL) -#define CKK_AES (0x1fUL) -#define CKK_BLOWFISH (0x20UL) -#define CKK_TWOFISH (0x21UL) -#define CKK_GOSTR3410 (0x30UL) -#define CKK_GOSTR3411 (0x31UL) -#define CKK_GOST28147 (0x32UL) -#define CKK_VENDOR_DEFINED (1UL << 31) - - -typedef unsigned long ck_certificate_type_t; - -#define CKC_X_509 (0UL) -#define CKC_X_509_ATTR_CERT (1UL) -#define CKC_WTLS (2UL) -#define CKC_VENDOR_DEFINED (1UL << 31) - - -typedef unsigned long ck_attribute_type_t; - -#define CKA_CLASS (0UL) -#define CKA_TOKEN (1UL) -#define CKA_PRIVATE (2UL) -#define CKA_LABEL (3UL) -#define CKA_APPLICATION (0x10UL) -#define CKA_VALUE (0x11UL) -#define CKA_OBJECT_ID (0x12UL) -#define CKA_CERTIFICATE_TYPE (0x80UL) -#define CKA_ISSUER (0x81UL) -#define CKA_SERIAL_NUMBER (0x82UL) -#define CKA_AC_ISSUER (0x83UL) -#define CKA_OWNER (0x84UL) -#define CKA_ATTR_TYPES (0x85UL) -#define CKA_TRUSTED (0x86UL) -#define CKA_CERTIFICATE_CATEGORY (0x87UL) -#define CKA_JAVA_MIDP_SECURITY_DOMAIN (0x88UL) -#define CKA_URL (0x89UL) -#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY (0x8aUL) -#define CKA_HASH_OF_ISSUER_PUBLIC_KEY (0x8bUL) -#define CKA_CHECK_VALUE (0x90UL) -#define CKA_KEY_TYPE (0x100UL) -#define CKA_SUBJECT (0x101UL) -#define CKA_ID (0x102UL) -#define CKA_SENSITIVE (0x103UL) -#define CKA_ENCRYPT (0x104UL) -#define CKA_DECRYPT (0x105UL) -#define CKA_WRAP (0x106UL) -#define CKA_UNWRAP (0x107UL) -#define CKA_SIGN (0x108UL) -#define CKA_SIGN_RECOVER (0x109UL) -#define CKA_VERIFY (0x10aUL) -#define CKA_VERIFY_RECOVER (0x10bUL) -#define CKA_DERIVE (0x10cUL) -#define CKA_START_DATE (0x110UL) -#define CKA_END_DATE (0x111UL) -#define CKA_MODULUS (0x120UL) -#define CKA_MODULUS_BITS (0x121UL) -#define CKA_PUBLIC_EXPONENT (0x122UL) -#define CKA_PRIVATE_EXPONENT (0x123UL) -#define CKA_PRIME_1 (0x124UL) -#define CKA_PRIME_2 (0x125UL) -#define CKA_EXPONENT_1 (0x126UL) -#define CKA_EXPONENT_2 (0x127UL) -#define CKA_COEFFICIENT (0x128UL) -#define CKA_PRIME (0x130UL) -#define CKA_SUBPRIME (0x131UL) -#define CKA_BASE (0x132UL) -#define CKA_PRIME_BITS (0x133UL) -#define CKA_SUB_PRIME_BITS (0x134UL) -#define CKA_VALUE_BITS (0x160UL) -#define CKA_VALUE_LEN (0x161UL) -#define CKA_EXTRACTABLE (0x162UL) -#define CKA_LOCAL (0x163UL) -#define CKA_NEVER_EXTRACTABLE (0x164UL) -#define CKA_ALWAYS_SENSITIVE (0x165UL) -#define CKA_KEY_GEN_MECHANISM (0x166UL) -#define CKA_MODIFIABLE (0x170UL) -#define CKA_ECDSA_PARAMS (0x180UL) -#define CKA_EC_PARAMS (0x180UL) -#define CKA_EC_POINT (0x181UL) -#define CKA_SECONDARY_AUTH (0x200UL) -#define CKA_AUTH_PIN_FLAGS (0x201UL) -#define CKA_ALWAYS_AUTHENTICATE (0x202UL) -#define CKA_WRAP_WITH_TRUSTED (0x210UL) -#define CKA_GOSTR3410_PARAMS (0x250UL) -#define CKA_GOSTR3411_PARAMS (0x251UL) -#define CKA_GOST28147_PARAMS (0x252UL) -#define CKA_HW_FEATURE_TYPE (0x300UL) -#define CKA_RESET_ON_INIT (0x301UL) -#define CKA_HAS_RESET (0x302UL) -#define CKA_PIXEL_X (0x400UL) -#define CKA_PIXEL_Y (0x401UL) -#define CKA_RESOLUTION (0x402UL) -#define CKA_CHAR_ROWS (0x403UL) -#define CKA_CHAR_COLUMNS (0x404UL) -#define CKA_COLOR (0x405UL) -#define CKA_BITS_PER_PIXEL (0x406UL) -#define CKA_CHAR_SETS (0x480UL) -#define CKA_ENCODING_METHODS (0x481UL) -#define CKA_MIME_TYPES (0x482UL) -#define CKA_MECHANISM_TYPE (0x500UL) -#define CKA_REQUIRED_CMS_ATTRIBUTES (0x501UL) -#define CKA_DEFAULT_CMS_ATTRIBUTES (0x502UL) -#define CKA_SUPPORTED_CMS_ATTRIBUTES (0x503UL) -#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x211UL) -#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x212UL) -#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE | 0x600UL) -#define CKA_VENDOR_DEFINED (1UL << 31) +/* Miscellaneous */ - -struct ck_attribute -{ - ck_attribute_type_t type; - void *value; - unsigned long value_len; -}; - - -struct ck_date -{ - unsigned char year[4]; - unsigned char month[2]; - unsigned char day[2]; -}; - - -typedef unsigned long ck_mechanism_type_t; - -#define CKM_RSA_PKCS_KEY_PAIR_GEN (0UL) -#define CKM_RSA_PKCS (1UL) -#define CKM_RSA_9796 (2UL) -#define CKM_RSA_X_509 (3UL) -#define CKM_MD2_RSA_PKCS (4UL) -#define CKM_MD5_RSA_PKCS (5UL) -#define CKM_SHA1_RSA_PKCS (6UL) -#define CKM_RIPEMD128_RSA_PKCS (7UL) -#define CKM_RIPEMD160_RSA_PKCS (8UL) -#define CKM_RSA_PKCS_OAEP (9UL) -#define CKM_RSA_X9_31_KEY_PAIR_GEN (0xaUL) -#define CKM_RSA_X9_31 (0xbUL) -#define CKM_SHA1_RSA_X9_31 (0xcUL) -#define CKM_RSA_PKCS_PSS (0xdUL) -#define CKM_SHA1_RSA_PKCS_PSS (0xeUL) -#define CKM_DSA_KEY_PAIR_GEN (0x10UL) -#define CKM_DSA (0x11UL) -#define CKM_DSA_SHA1 (0x12UL) -#define CKM_DH_PKCS_KEY_PAIR_GEN (0x20UL) -#define CKM_DH_PKCS_DERIVE (0x21UL) -#define CKM_X9_42_DH_KEY_PAIR_GEN (0x30UL) -#define CKM_X9_42_DH_DERIVE (0x31UL) -#define CKM_X9_42_DH_HYBRID_DERIVE (0x32UL) -#define CKM_X9_42_MQV_DERIVE (0x33UL) -#define CKM_SHA256_RSA_PKCS (0x40UL) -#define CKM_SHA384_RSA_PKCS (0x41UL) -#define CKM_SHA512_RSA_PKCS (0x42UL) -#define CKM_SHA256_RSA_PKCS_PSS (0x43UL) -#define CKM_SHA384_RSA_PKCS_PSS (0x44UL) -#define CKM_SHA512_RSA_PKCS_PSS (0x45UL) -#define CKM_RC2_KEY_GEN (0x100UL) -#define CKM_RC2_ECB (0x101UL) -#define CKM_RC2_CBC (0x102UL) -#define CKM_RC2_MAC (0x103UL) -#define CKM_RC2_MAC_GENERAL (0x104UL) -#define CKM_RC2_CBC_PAD (0x105UL) -#define CKM_RC4_KEY_GEN (0x110UL) -#define CKM_RC4 (0x111UL) -#define CKM_DES_KEY_GEN (0x120UL) -#define CKM_DES_ECB (0x121UL) -#define CKM_DES_CBC (0x122UL) -#define CKM_DES_MAC (0x123UL) -#define CKM_DES_MAC_GENERAL (0x124UL) -#define CKM_DES_CBC_PAD (0x125UL) -#define CKM_DES2_KEY_GEN (0x130UL) -#define CKM_DES3_KEY_GEN (0x131UL) -#define CKM_DES3_ECB (0x132UL) -#define CKM_DES3_CBC (0x133UL) -#define CKM_DES3_MAC (0x134UL) -#define CKM_DES3_MAC_GENERAL (0x135UL) -#define CKM_DES3_CBC_PAD (0x136UL) -#define CKM_CDMF_KEY_GEN (0x140UL) -#define CKM_CDMF_ECB (0x141UL) -#define CKM_CDMF_CBC (0x142UL) -#define CKM_CDMF_MAC (0x143UL) -#define CKM_CDMF_MAC_GENERAL (0x144UL) -#define CKM_CDMF_CBC_PAD (0x145UL) -#define CKM_MD2 (0x200UL) -#define CKM_MD2_HMAC (0x201UL) -#define CKM_MD2_HMAC_GENERAL (0x202UL) -#define CKM_MD5 (0x210UL) -#define CKM_MD5_HMAC (0x211UL) -#define CKM_MD5_HMAC_GENERAL (0x212UL) -#define CKM_SHA_1 (0x220UL) -#define CKM_SHA_1_HMAC (0x221UL) -#define CKM_SHA_1_HMAC_GENERAL (0x222UL) -#define CKM_RIPEMD128 (0x230UL) -#define CKM_RIPEMD128_HMAC (0x231UL) -#define CKM_RIPEMD128_HMAC_GENERAL (0x232UL) -#define CKM_RIPEMD160 (0x240UL) -#define CKM_RIPEMD160_HMAC (0x241UL) -#define CKM_RIPEMD160_HMAC_GENERAL (0x242UL) -#define CKM_SHA256 (0x250UL) -#define CKM_SHA256_HMAC (0x251UL) -#define CKM_SHA256_HMAC_GENERAL (0x252UL) -#define CKM_SHA384 (0x260UL) -#define CKM_SHA384_HMAC (0x261UL) -#define CKM_SHA384_HMAC_GENERAL (0x262UL) -#define CKM_SHA512 (0x270UL) -#define CKM_SHA512_HMAC (0x271UL) -#define CKM_SHA512_HMAC_GENERAL (0x272UL) -#define CKM_CAST_KEY_GEN (0x300UL) -#define CKM_CAST_ECB (0x301UL) -#define CKM_CAST_CBC (0x302UL) -#define CKM_CAST_MAC (0x303UL) -#define CKM_CAST_MAC_GENERAL (0x304UL) -#define CKM_CAST_CBC_PAD (0x305UL) -#define CKM_CAST3_KEY_GEN (0x310UL) -#define CKM_CAST3_ECB (0x311UL) -#define CKM_CAST3_CBC (0x312UL) -#define CKM_CAST3_MAC (0x313UL) -#define CKM_CAST3_MAC_GENERAL (0x314UL) -#define CKM_CAST3_CBC_PAD (0x315UL) -#define CKM_CAST5_KEY_GEN (0x320UL) -#define CKM_CAST128_KEY_GEN (0x320UL) -#define CKM_CAST5_ECB (0x321UL) -#define CKM_CAST128_ECB (0x321UL) -#define CKM_CAST5_CBC (0x322UL) -#define CKM_CAST128_CBC (0x322UL) -#define CKM_CAST5_MAC (0x323UL) -#define CKM_CAST128_MAC (0x323UL) -#define CKM_CAST5_MAC_GENERAL (0x324UL) -#define CKM_CAST128_MAC_GENERAL (0x324UL) -#define CKM_CAST5_CBC_PAD (0x325UL) -#define CKM_CAST128_CBC_PAD (0x325UL) -#define CKM_RC5_KEY_GEN (0x330UL) -#define CKM_RC5_ECB (0x331UL) -#define CKM_RC5_CBC (0x332UL) -#define CKM_RC5_MAC (0x333UL) -#define CKM_RC5_MAC_GENERAL (0x334UL) -#define CKM_RC5_CBC_PAD (0x335UL) -#define CKM_IDEA_KEY_GEN (0x340UL) -#define CKM_IDEA_ECB (0x341UL) -#define CKM_IDEA_CBC (0x342UL) -#define CKM_IDEA_MAC (0x343UL) -#define CKM_IDEA_MAC_GENERAL (0x344UL) -#define CKM_IDEA_CBC_PAD (0x345UL) -#define CKM_GENERIC_SECRET_KEY_GEN (0x350UL) -#define CKM_CONCATENATE_BASE_AND_KEY (0x360UL) -#define CKM_CONCATENATE_BASE_AND_DATA (0x362UL) -#define CKM_CONCATENATE_DATA_AND_BASE (0x363UL) -#define CKM_XOR_BASE_AND_DATA (0x364UL) -#define CKM_EXTRACT_KEY_FROM_KEY (0x365UL) -#define CKM_SSL3_PRE_MASTER_KEY_GEN (0x370UL) -#define CKM_SSL3_MASTER_KEY_DERIVE (0x371UL) -#define CKM_SSL3_KEY_AND_MAC_DERIVE (0x372UL) -#define CKM_SSL3_MASTER_KEY_DERIVE_DH (0x373UL) -#define CKM_TLS_PRE_MASTER_KEY_GEN (0x374UL) -#define CKM_TLS_MASTER_KEY_DERIVE (0x375UL) -#define CKM_TLS_KEY_AND_MAC_DERIVE (0x376UL) -#define CKM_TLS_MASTER_KEY_DERIVE_DH (0x377UL) -#define CKM_SSL3_MD5_MAC (0x380UL) -#define CKM_SSL3_SHA1_MAC (0x381UL) -#define CKM_MD5_KEY_DERIVATION (0x390UL) -#define CKM_MD2_KEY_DERIVATION (0x391UL) -#define CKM_SHA1_KEY_DERIVATION (0x392UL) -#define CKM_PBE_MD2_DES_CBC (0x3a0UL) -#define CKM_PBE_MD5_DES_CBC (0x3a1UL) -#define CKM_PBE_MD5_CAST_CBC (0x3a2UL) -#define CKM_PBE_MD5_CAST3_CBC (0x3a3UL) -#define CKM_PBE_MD5_CAST5_CBC (0x3a4UL) -#define CKM_PBE_MD5_CAST128_CBC (0x3a4UL) -#define CKM_PBE_SHA1_CAST5_CBC (0x3a5UL) -#define CKM_PBE_SHA1_CAST128_CBC (0x3a5UL) -#define CKM_PBE_SHA1_RC4_128 (0x3a6UL) -#define CKM_PBE_SHA1_RC4_40 (0x3a7UL) -#define CKM_PBE_SHA1_DES3_EDE_CBC (0x3a8UL) -#define CKM_PBE_SHA1_DES2_EDE_CBC (0x3a9UL) -#define CKM_PBE_SHA1_RC2_128_CBC (0x3aaUL) -#define CKM_PBE_SHA1_RC2_40_CBC (0x3abUL) -#define CKM_PKCS5_PBKD2 (0x3b0UL) -#define CKM_PBA_SHA1_WITH_SHA1_HMAC (0x3c0UL) -#define CKM_KEY_WRAP_LYNKS (0x400UL) -#define CKM_KEY_WRAP_SET_OAEP (0x401UL) -#define CKM_SKIPJACK_KEY_GEN (0x1000UL) -#define CKM_SKIPJACK_ECB64 (0x1001UL) -#define CKM_SKIPJACK_CBC64 (0x1002UL) -#define CKM_SKIPJACK_OFB64 (0x1003UL) -#define CKM_SKIPJACK_CFB64 (0x1004UL) -#define CKM_SKIPJACK_CFB32 (0x1005UL) -#define CKM_SKIPJACK_CFB16 (0x1006UL) -#define CKM_SKIPJACK_CFB8 (0x1007UL) -#define CKM_SKIPJACK_WRAP (0x1008UL) -#define CKM_SKIPJACK_PRIVATE_WRAP (0x1009UL) -#define CKM_SKIPJACK_RELAYX (0x100aUL) -#define CKM_KEA_KEY_PAIR_GEN (0x1010UL) -#define CKM_KEA_KEY_DERIVE (0x1011UL) -#define CKM_FORTEZZA_TIMESTAMP (0x1020UL) -#define CKM_BATON_KEY_GEN (0x1030UL) -#define CKM_BATON_ECB128 (0x1031UL) -#define CKM_BATON_ECB96 (0x1032UL) -#define CKM_BATON_CBC128 (0x1033UL) -#define CKM_BATON_COUNTER (0x1034UL) -#define CKM_BATON_SHUFFLE (0x1035UL) -#define CKM_BATON_WRAP (0x1036UL) -#define CKM_ECDSA_KEY_PAIR_GEN (0x1040UL) -#define CKM_EC_KEY_PAIR_GEN (0x1040UL) -#define CKM_ECDSA (0x1041UL) -#define CKM_ECDSA_SHA1 (0x1042UL) -#define CKM_ECDSA_SHA224 (0x1043UL) -#define CKM_ECDSA_SHA256 (0x1044UL) -#define CKM_ECDSA_SHA384 (0x1045UL) -#define CKM_ECDSA_SHA512 (0x1046UL) -#define CKM_ECDH1_DERIVE (0x1050UL) -#define CKM_ECDH1_COFACTOR_DERIVE (0x1051UL) -#define CKM_ECMQV_DERIVE (0x1052UL) -#define CKM_JUNIPER_KEY_GEN (0x1060UL) -#define CKM_JUNIPER_ECB128 (0x1061UL) -#define CKM_JUNIPER_CBC128 (0x1062UL) -#define CKM_JUNIPER_COUNTER (0x1063UL) -#define CKM_JUNIPER_SHUFFLE (0x1064UL) -#define CKM_JUNIPER_WRAP (0x1065UL) -#define CKM_FASTHASH (0x1070UL) -#define CKM_AES_KEY_GEN (0x1080UL) -#define CKM_AES_ECB (0x1081UL) -#define CKM_AES_CBC (0x1082UL) -#define CKM_AES_MAC (0x1083UL) -#define CKM_AES_MAC_GENERAL (0x1084UL) -#define CKM_AES_CBC_PAD (0x1085UL) -#define CKM_AES_CTR (0x1086UL) -#define CKM_AES_GCM (0x1087UL) -#define CKM_AES_CCM (0x1088UL) -#define CKM_AES_CTS (0x1089UL) -#define CKM_BLOWFISH_KEY_GEN (0x1090UL) -#define CKM_BLOWFISH_CBC (0x1091UL) -#define CKM_TWOFISH_KEY_GEN (0x1092UL) -#define CKM_TWOFISH_CBC (0x1093UL) -#define CKM_GOSTR3410_KEY_PAIR_GEN (0x1200UL) -#define CKM_GOSTR3410 (0x1201UL) -#define CKM_GOSTR3410_WITH_GOSTR3411 (0x1202UL) -#define CKM_GOSTR3410_KEY_WRAP (0x1203UL) -#define CKM_GOSTR3410_DERIVE (0x1204UL) -#define CKM_GOSTR3411 (0x1210UL) -#define CKM_GOSTR3411_HMAC (0x1211UL) -#define CKM_GOST28147_KEY_GEN (0x1220UL) -#define CKM_GOST28147_ECB (0x1221UL) -#define CKM_GOST28147 (0x1222UL) -#define CKM_GOST28147_MAC (0x1223UL) -#define CKM_GOST28147_KEY_WRAP (0x1224UL) - -#define CKM_DSA_PARAMETER_GEN (0x2000UL) -#define CKM_DH_PKCS_PARAMETER_GEN (0x2001UL) -#define CKM_X9_42_DH_PARAMETER_GEN (0x2002UL) -#define CKM_VENDOR_DEFINED (1UL << 31) - - -struct ck_mechanism -{ - ck_mechanism_type_t mechanism; - void *parameter; - unsigned long parameter_len; -}; - - -struct ck_mechanism_info -{ - unsigned long min_key_size; - unsigned long max_key_size; - ck_flags_t flags; -}; - -#define CKF_HW (1UL << 0) -#define CKF_ENCRYPT (1UL << 8) -#define CKF_DECRYPT (1UL << 9) -#define CKF_DIGEST (1UL << 10) -#define CKF_SIGN (1UL << 11) -#define CKF_SIGN_RECOVER (1UL << 12) -#define CKF_VERIFY (1UL << 13) -#define CKF_VERIFY_RECOVER (1UL << 14) -#define CKF_GENERATE (1UL << 15) -#define CKF_GENERATE_KEY_PAIR (1UL << 16) -#define CKF_WRAP (1UL << 17) -#define CKF_UNWRAP (1UL << 18) -#define CKF_DERIVE (1UL << 19) -#define CKF_EXTENSION (1UL << 31) - -#define CKF_EC_F_P (1UL << 20) -#define CKF_EC_F_2M (1UL << 21) -#define CKF_EC_ECPARAMETERS (1UL << 22) -#define CKF_EC_NAMEDCURVE (1UL << 23) -#define CKF_EC_UNCOMPRESS (1UL << 24) -#define CKF_EC_COMPRESS (1UL << 25) - -/* Flags for C_WaitForSlotEvent. */ -#define CKF_DONT_BLOCK (1UL) - -/* Flags for Key derivation */ -#define CKD_NULL (1UL << 0) - -typedef struct CK_ECDH1_DERIVE_PARAMS { - unsigned long kdf; - unsigned long ulSharedDataLen; - unsigned char * pSharedData; - unsigned long ulPublicDataLen; - unsigned char * pPublicData; -} CK_ECDH1_DERIVE_PARAMS; - - -typedef unsigned long ck_rv_t; - - -typedef ck_rv_t (*ck_notify_t) (ck_session_handle_t session, - ck_notification_t event, void *application); - -/* Forward reference. */ -struct ck_function_list; - -#define _CK_DECLARE_FUNCTION(name, args) \ -typedef ck_rv_t (*CK_ ## name) args; \ -ck_rv_t CK_SPEC name args - -_CK_DECLARE_FUNCTION (C_Initialize, (void *init_args)); -_CK_DECLARE_FUNCTION (C_Finalize, (void *reserved)); -_CK_DECLARE_FUNCTION (C_GetInfo, (struct ck_info *info)); -_CK_DECLARE_FUNCTION (C_GetFunctionList, - (struct ck_function_list **function_list)); - -_CK_DECLARE_FUNCTION (C_GetSlotList, - (unsigned char token_present, ck_slot_id_t *slot_list, - unsigned long *count)); -_CK_DECLARE_FUNCTION (C_GetSlotInfo, - (ck_slot_id_t slot_id, struct ck_slot_info *info)); -_CK_DECLARE_FUNCTION (C_GetTokenInfo, - (ck_slot_id_t slot_id, struct ck_token_info *info)); -_CK_DECLARE_FUNCTION (C_WaitForSlotEvent, - (ck_flags_t flags, ck_slot_id_t *slot, void *reserved)); -_CK_DECLARE_FUNCTION (C_GetMechanismList, - (ck_slot_id_t slot_id, - ck_mechanism_type_t *mechanism_list, - unsigned long *count)); -_CK_DECLARE_FUNCTION (C_GetMechanismInfo, - (ck_slot_id_t slot_id, ck_mechanism_type_t type, - struct ck_mechanism_info *info)); -_CK_DECLARE_FUNCTION (C_InitToken, - (ck_slot_id_t slot_id, unsigned char *pin, - unsigned long pin_len, unsigned char *label)); -_CK_DECLARE_FUNCTION (C_InitPIN, - (ck_session_handle_t session, unsigned char *pin, - unsigned long pin_len)); -_CK_DECLARE_FUNCTION (C_SetPIN, - (ck_session_handle_t session, unsigned char *old_pin, - unsigned long old_len, unsigned char *new_pin, - unsigned long new_len)); - -_CK_DECLARE_FUNCTION (C_OpenSession, - (ck_slot_id_t slot_id, ck_flags_t flags, - void *application, ck_notify_t notify, - ck_session_handle_t *session)); -_CK_DECLARE_FUNCTION (C_CloseSession, (ck_session_handle_t session)); -_CK_DECLARE_FUNCTION (C_CloseAllSessions, (ck_slot_id_t slot_id)); -_CK_DECLARE_FUNCTION (C_GetSessionInfo, - (ck_session_handle_t session, - struct ck_session_info *info)); -_CK_DECLARE_FUNCTION (C_GetOperationState, - (ck_session_handle_t session, - unsigned char *operation_state, - unsigned long *operation_state_len)); -_CK_DECLARE_FUNCTION (C_SetOperationState, - (ck_session_handle_t session, - unsigned char *operation_state, - unsigned long operation_state_len, - ck_object_handle_t encryption_key, - ck_object_handle_t authentiation_key)); -_CK_DECLARE_FUNCTION (C_Login, - (ck_session_handle_t session, ck_user_type_t user_type, - unsigned char *pin, unsigned long pin_len)); -_CK_DECLARE_FUNCTION (C_Logout, (ck_session_handle_t session)); - -_CK_DECLARE_FUNCTION (C_CreateObject, - (ck_session_handle_t session, - struct ck_attribute *templ, - unsigned long count, ck_object_handle_t *object)); -_CK_DECLARE_FUNCTION (C_CopyObject, - (ck_session_handle_t session, ck_object_handle_t object, - struct ck_attribute *templ, unsigned long count, - ck_object_handle_t *new_object)); -_CK_DECLARE_FUNCTION (C_DestroyObject, - (ck_session_handle_t session, - ck_object_handle_t object)); -_CK_DECLARE_FUNCTION (C_GetObjectSize, - (ck_session_handle_t session, - ck_object_handle_t object, - unsigned long *size)); -_CK_DECLARE_FUNCTION (C_GetAttributeValue, - (ck_session_handle_t session, - ck_object_handle_t object, - struct ck_attribute *templ, - unsigned long count)); -_CK_DECLARE_FUNCTION (C_SetAttributeValue, - (ck_session_handle_t session, - ck_object_handle_t object, - struct ck_attribute *templ, - unsigned long count)); -_CK_DECLARE_FUNCTION (C_FindObjectsInit, - (ck_session_handle_t session, - struct ck_attribute *templ, - unsigned long count)); -_CK_DECLARE_FUNCTION (C_FindObjects, - (ck_session_handle_t session, - ck_object_handle_t *object, - unsigned long max_object_count, - unsigned long *object_count)); -_CK_DECLARE_FUNCTION (C_FindObjectsFinal, - (ck_session_handle_t session)); - -_CK_DECLARE_FUNCTION (C_EncryptInit, - (ck_session_handle_t session, - struct ck_mechanism *mechanism, - ck_object_handle_t key)); -_CK_DECLARE_FUNCTION (C_Encrypt, - (ck_session_handle_t session, - unsigned char *data, unsigned long data_len, - unsigned char *encrypted_data, - unsigned long *encrypted_data_len)); -_CK_DECLARE_FUNCTION (C_EncryptUpdate, - (ck_session_handle_t session, - unsigned char *part, unsigned long part_len, - unsigned char *encrypted_part, - unsigned long *encrypted_part_len)); -_CK_DECLARE_FUNCTION (C_EncryptFinal, - (ck_session_handle_t session, - unsigned char *last_encrypted_part, - unsigned long *last_encrypted_part_len)); - -_CK_DECLARE_FUNCTION (C_DecryptInit, - (ck_session_handle_t session, - struct ck_mechanism *mechanism, - ck_object_handle_t key)); -_CK_DECLARE_FUNCTION (C_Decrypt, - (ck_session_handle_t session, - unsigned char *encrypted_data, - unsigned long encrypted_data_len, - unsigned char *data, unsigned long *data_len)); -_CK_DECLARE_FUNCTION (C_DecryptUpdate, - (ck_session_handle_t session, - unsigned char *encrypted_part, - unsigned long encrypted_part_len, - unsigned char *part, unsigned long *part_len)); -_CK_DECLARE_FUNCTION (C_DecryptFinal, - (ck_session_handle_t session, - unsigned char *last_part, - unsigned long *last_part_len)); - -_CK_DECLARE_FUNCTION (C_DigestInit, - (ck_session_handle_t session, - struct ck_mechanism *mechanism)); -_CK_DECLARE_FUNCTION (C_Digest, - (ck_session_handle_t session, - unsigned char *data, unsigned long data_len, - unsigned char *digest, - unsigned long *digest_len)); -_CK_DECLARE_FUNCTION (C_DigestUpdate, - (ck_session_handle_t session, - unsigned char *part, unsigned long part_len)); -_CK_DECLARE_FUNCTION (C_DigestKey, - (ck_session_handle_t session, ck_object_handle_t key)); -_CK_DECLARE_FUNCTION (C_DigestFinal, - (ck_session_handle_t session, - unsigned char *digest, - unsigned long *digest_len)); - -_CK_DECLARE_FUNCTION (C_SignInit, - (ck_session_handle_t session, - struct ck_mechanism *mechanism, - ck_object_handle_t key)); -_CK_DECLARE_FUNCTION (C_Sign, - (ck_session_handle_t session, - unsigned char *data, unsigned long data_len, - unsigned char *signature, - unsigned long *signature_len)); -_CK_DECLARE_FUNCTION (C_SignUpdate, - (ck_session_handle_t session, - unsigned char *part, unsigned long part_len)); -_CK_DECLARE_FUNCTION (C_SignFinal, - (ck_session_handle_t session, - unsigned char *signature, - unsigned long *signature_len)); -_CK_DECLARE_FUNCTION (C_SignRecoverInit, - (ck_session_handle_t session, - struct ck_mechanism *mechanism, - ck_object_handle_t key)); -_CK_DECLARE_FUNCTION (C_SignRecover, - (ck_session_handle_t session, - unsigned char *data, unsigned long data_len, - unsigned char *signature, - unsigned long *signature_len)); - -_CK_DECLARE_FUNCTION (C_VerifyInit, - (ck_session_handle_t session, - struct ck_mechanism *mechanism, - ck_object_handle_t key)); -_CK_DECLARE_FUNCTION (C_Verify, - (ck_session_handle_t session, - unsigned char *data, unsigned long data_len, - unsigned char *signature, - unsigned long signature_len)); -_CK_DECLARE_FUNCTION (C_VerifyUpdate, - (ck_session_handle_t session, - unsigned char *part, unsigned long part_len)); -_CK_DECLARE_FUNCTION (C_VerifyFinal, - (ck_session_handle_t session, - unsigned char *signature, - unsigned long signature_len)); -_CK_DECLARE_FUNCTION (C_VerifyRecoverInit, - (ck_session_handle_t session, - struct ck_mechanism *mechanism, - ck_object_handle_t key)); -_CK_DECLARE_FUNCTION (C_VerifyRecover, - (ck_session_handle_t session, - unsigned char *signature, - unsigned long signature_len, - unsigned char *data, - unsigned long *data_len)); - -_CK_DECLARE_FUNCTION (C_DigestEncryptUpdate, - (ck_session_handle_t session, - unsigned char *part, unsigned long part_len, - unsigned char *encrypted_part, - unsigned long *encrypted_part_len)); -_CK_DECLARE_FUNCTION (C_DecryptDigestUpdate, - (ck_session_handle_t session, - unsigned char *encrypted_part, - unsigned long encrypted_part_len, - unsigned char *part, - unsigned long *part_len)); -_CK_DECLARE_FUNCTION (C_SignEncryptUpdate, - (ck_session_handle_t session, - unsigned char *part, unsigned long part_len, - unsigned char *encrypted_part, - unsigned long *encrypted_part_len)); -_CK_DECLARE_FUNCTION (C_DecryptVerifyUpdate, - (ck_session_handle_t session, - unsigned char *encrypted_part, - unsigned long encrypted_part_len, - unsigned char *part, - unsigned long *part_len)); - -_CK_DECLARE_FUNCTION (C_GenerateKey, - (ck_session_handle_t session, - struct ck_mechanism *mechanism, - struct ck_attribute *templ, - unsigned long count, - ck_object_handle_t *key)); -_CK_DECLARE_FUNCTION (C_GenerateKeyPair, - (ck_session_handle_t session, - struct ck_mechanism *mechanism, - struct ck_attribute *public_key_template, - unsigned long public_key_attribute_count, - struct ck_attribute *private_key_template, - unsigned long private_key_attribute_count, - ck_object_handle_t *public_key, - ck_object_handle_t *private_key)); -_CK_DECLARE_FUNCTION (C_WrapKey, - (ck_session_handle_t session, - struct ck_mechanism *mechanism, - ck_object_handle_t wrapping_key, - ck_object_handle_t key, - unsigned char *wrapped_key, - unsigned long *wrapped_key_len)); -_CK_DECLARE_FUNCTION (C_UnwrapKey, - (ck_session_handle_t session, - struct ck_mechanism *mechanism, - ck_object_handle_t unwrapping_key, - unsigned char *wrapped_key, - unsigned long wrapped_key_len, - struct ck_attribute *templ, - unsigned long attribute_count, - ck_object_handle_t *key)); -_CK_DECLARE_FUNCTION (C_DeriveKey, - (ck_session_handle_t session, - struct ck_mechanism *mechanism, - ck_object_handle_t base_key, - struct ck_attribute *templ, - unsigned long attribute_count, - ck_object_handle_t *key)); - -_CK_DECLARE_FUNCTION (C_SeedRandom, - (ck_session_handle_t session, unsigned char *seed, - unsigned long seed_len)); -_CK_DECLARE_FUNCTION (C_GenerateRandom, - (ck_session_handle_t session, - unsigned char *random_data, - unsigned long random_len)); - -_CK_DECLARE_FUNCTION (C_GetFunctionStatus, (ck_session_handle_t session)); -_CK_DECLARE_FUNCTION (C_CancelFunction, (ck_session_handle_t session)); - - -struct ck_function_list -{ - struct ck_version version; - CK_C_Initialize C_Initialize; - CK_C_Finalize C_Finalize; - CK_C_GetInfo C_GetInfo; - CK_C_GetFunctionList C_GetFunctionList; - CK_C_GetSlotList C_GetSlotList; - CK_C_GetSlotInfo C_GetSlotInfo; - CK_C_GetTokenInfo C_GetTokenInfo; - CK_C_GetMechanismList C_GetMechanismList; - CK_C_GetMechanismInfo C_GetMechanismInfo; - CK_C_InitToken C_InitToken; - CK_C_InitPIN C_InitPIN; - CK_C_SetPIN C_SetPIN; - CK_C_OpenSession C_OpenSession; - CK_C_CloseSession C_CloseSession; - CK_C_CloseAllSessions C_CloseAllSessions; - CK_C_GetSessionInfo C_GetSessionInfo; - CK_C_GetOperationState C_GetOperationState; - CK_C_SetOperationState C_SetOperationState; - CK_C_Login C_Login; - CK_C_Logout C_Logout; - CK_C_CreateObject C_CreateObject; - CK_C_CopyObject C_CopyObject; - CK_C_DestroyObject C_DestroyObject; - CK_C_GetObjectSize C_GetObjectSize; - CK_C_GetAttributeValue C_GetAttributeValue; - CK_C_SetAttributeValue C_SetAttributeValue; - CK_C_FindObjectsInit C_FindObjectsInit; - CK_C_FindObjects C_FindObjects; - CK_C_FindObjectsFinal C_FindObjectsFinal; - CK_C_EncryptInit C_EncryptInit; - CK_C_Encrypt C_Encrypt; - CK_C_EncryptUpdate C_EncryptUpdate; - CK_C_EncryptFinal C_EncryptFinal; - CK_C_DecryptInit C_DecryptInit; - CK_C_Decrypt C_Decrypt; - CK_C_DecryptUpdate C_DecryptUpdate; - CK_C_DecryptFinal C_DecryptFinal; - CK_C_DigestInit C_DigestInit; - CK_C_Digest C_Digest; - CK_C_DigestUpdate C_DigestUpdate; - CK_C_DigestKey C_DigestKey; - CK_C_DigestFinal C_DigestFinal; - CK_C_SignInit C_SignInit; - CK_C_Sign C_Sign; - CK_C_SignUpdate C_SignUpdate; - CK_C_SignFinal C_SignFinal; - CK_C_SignRecoverInit C_SignRecoverInit; - CK_C_SignRecover C_SignRecover; - CK_C_VerifyInit C_VerifyInit; - CK_C_Verify C_Verify; - CK_C_VerifyUpdate C_VerifyUpdate; - CK_C_VerifyFinal C_VerifyFinal; - CK_C_VerifyRecoverInit C_VerifyRecoverInit; - CK_C_VerifyRecover C_VerifyRecover; - CK_C_DigestEncryptUpdate C_DigestEncryptUpdate; - CK_C_DecryptDigestUpdate C_DecryptDigestUpdate; - CK_C_SignEncryptUpdate C_SignEncryptUpdate; - CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate; - CK_C_GenerateKey C_GenerateKey; - CK_C_GenerateKeyPair C_GenerateKeyPair; - CK_C_WrapKey C_WrapKey; - CK_C_UnwrapKey C_UnwrapKey; - CK_C_DeriveKey C_DeriveKey; - CK_C_SeedRandom C_SeedRandom; - CK_C_GenerateRandom C_GenerateRandom; - CK_C_GetFunctionStatus C_GetFunctionStatus; - CK_C_CancelFunction C_CancelFunction; - CK_C_WaitForSlotEvent C_WaitForSlotEvent; -}; - - -typedef ck_rv_t (*ck_createmutex_t) (void **mutex); -typedef ck_rv_t (*ck_destroymutex_t) (void *mutex); -typedef ck_rv_t (*ck_lockmutex_t) (void *mutex); -typedef ck_rv_t (*ck_unlockmutex_t) (void *mutex); - - -struct ck_c_initialize_args -{ - ck_createmutex_t create_mutex; - ck_destroymutex_t destroy_mutex; - ck_lockmutex_t lock_mutex; - ck_unlockmutex_t unlock_mutex; - ck_flags_t flags; - void *reserved; -}; - - -#define CKF_LIBRARY_CANT_CREATE_OS_THREADS (1UL << 0) -#define CKF_OS_LOCKING_OK (1UL << 1) - -#define CKR_OK (0UL) -#define CKR_CANCEL (1UL) -#define CKR_HOST_MEMORY (2UL) -#define CKR_SLOT_ID_INVALID (3UL) -#define CKR_GENERAL_ERROR (5UL) -#define CKR_FUNCTION_FAILED (6UL) -#define CKR_ARGUMENTS_BAD (7UL) -#define CKR_NO_EVENT (8UL) -#define CKR_NEED_TO_CREATE_THREADS (9UL) -#define CKR_CANT_LOCK (0xaUL) -#define CKR_ATTRIBUTE_READ_ONLY (0x10UL) -#define CKR_ATTRIBUTE_SENSITIVE (0x11UL) -#define CKR_ATTRIBUTE_TYPE_INVALID (0x12UL) -#define CKR_ATTRIBUTE_VALUE_INVALID (0x13UL) -#define CKR_DATA_INVALID (0x20UL) -#define CKR_DATA_LEN_RANGE (0x21UL) -#define CKR_DEVICE_ERROR (0x30UL) -#define CKR_DEVICE_MEMORY (0x31UL) -#define CKR_DEVICE_REMOVED (0x32UL) -#define CKR_ENCRYPTED_DATA_INVALID (0x40UL) -#define CKR_ENCRYPTED_DATA_LEN_RANGE (0x41UL) -#define CKR_FUNCTION_CANCELED (0x50UL) -#define CKR_FUNCTION_NOT_PARALLEL (0x51UL) -#define CKR_FUNCTION_NOT_SUPPORTED (0x54UL) -#define CKR_KEY_HANDLE_INVALID (0x60UL) -#define CKR_KEY_SIZE_RANGE (0x62UL) -#define CKR_KEY_TYPE_INCONSISTENT (0x63UL) -#define CKR_KEY_NOT_NEEDED (0x64UL) -#define CKR_KEY_CHANGED (0x65UL) -#define CKR_KEY_NEEDED (0x66UL) -#define CKR_KEY_INDIGESTIBLE (0x67UL) -#define CKR_KEY_FUNCTION_NOT_PERMITTED (0x68UL) -#define CKR_KEY_NOT_WRAPPABLE (0x69UL) -#define CKR_KEY_UNEXTRACTABLE (0x6aUL) -#define CKR_MECHANISM_INVALID (0x70UL) -#define CKR_MECHANISM_PARAM_INVALID (0x71UL) -#define CKR_OBJECT_HANDLE_INVALID (0x82UL) -#define CKR_OPERATION_ACTIVE (0x90UL) -#define CKR_OPERATION_NOT_INITIALIZED (0x91UL) -#define CKR_PIN_INCORRECT (0xa0UL) -#define CKR_PIN_INVALID (0xa1UL) -#define CKR_PIN_LEN_RANGE (0xa2UL) -#define CKR_PIN_EXPIRED (0xa3UL) -#define CKR_PIN_LOCKED (0xa4UL) -#define CKR_SESSION_CLOSED (0xb0UL) -#define CKR_SESSION_COUNT (0xb1UL) -#define CKR_SESSION_HANDLE_INVALID (0xb3UL) -#define CKR_SESSION_PARALLEL_NOT_SUPPORTED (0xb4UL) -#define CKR_SESSION_READ_ONLY (0xb5UL) -#define CKR_SESSION_EXISTS (0xb6UL) -#define CKR_SESSION_READ_ONLY_EXISTS (0xb7UL) -#define CKR_SESSION_READ_WRITE_SO_EXISTS (0xb8UL) -#define CKR_SIGNATURE_INVALID (0xc0UL) -#define CKR_SIGNATURE_LEN_RANGE (0xc1UL) -#define CKR_TEMPLATE_INCOMPLETE (0xd0UL) -#define CKR_TEMPLATE_INCONSISTENT (0xd1UL) -#define CKR_TOKEN_NOT_PRESENT (0xe0UL) -#define CKR_TOKEN_NOT_RECOGNIZED (0xe1UL) -#define CKR_TOKEN_WRITE_PROTECTED (0xe2UL) -#define CKR_UNWRAPPING_KEY_HANDLE_INVALID (0xf0UL) -#define CKR_UNWRAPPING_KEY_SIZE_RANGE (0xf1UL) -#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT (0xf2UL) -#define CKR_USER_ALREADY_LOGGED_IN (0x100UL) -#define CKR_USER_NOT_LOGGED_IN (0x101UL) -#define CKR_USER_PIN_NOT_INITIALIZED (0x102UL) -#define CKR_USER_TYPE_INVALID (0x103UL) -#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN (0x104UL) -#define CKR_USER_TOO_MANY_TYPES (0x105UL) -#define CKR_WRAPPED_KEY_INVALID (0x110UL) -#define CKR_WRAPPED_KEY_LEN_RANGE (0x112UL) -#define CKR_WRAPPING_KEY_HANDLE_INVALID (0x113UL) -#define CKR_WRAPPING_KEY_SIZE_RANGE (0x114UL) -#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT (0x115UL) -#define CKR_RANDOM_SEED_NOT_SUPPORTED (0x120UL) -#define CKR_RANDOM_NO_RNG (0x121UL) -#define CKR_DOMAIN_PARAMS_INVALID (0x130UL) -#define CKR_BUFFER_TOO_SMALL (0x150UL) -#define CKR_SAVED_STATE_INVALID (0x160UL) -#define CKR_INFORMATION_SENSITIVE (0x170UL) -#define CKR_STATE_UNSAVEABLE (0x180UL) -#define CKR_CRYPTOKI_NOT_INITIALIZED (0x190UL) -#define CKR_CRYPTOKI_ALREADY_INITIALIZED (0x191UL) -#define CKR_MUTEX_BAD (0x1a0UL) -#define CKR_MUTEX_NOT_LOCKED (0x1a1UL) -#define CKR_FUNCTION_REJECTED (0x200UL) -#define CKR_VENDOR_DEFINED (1UL << 31) - - -/* Compatibility layer. */ - -#ifdef CRYPTOKI_COMPAT - -#undef CK_DEFINE_FUNCTION -#define CK_DEFINE_FUNCTION(retval, name) retval CK_SPEC name - -/* For NULL. */ -#include - -typedef unsigned char CK_BYTE; -typedef unsigned char CK_CHAR; -typedef unsigned char CK_UTF8CHAR; -typedef unsigned char CK_BBOOL; -typedef unsigned long int CK_ULONG; -typedef long int CK_LONG; -typedef CK_BYTE *CK_BYTE_PTR; -typedef CK_CHAR *CK_CHAR_PTR; -typedef CK_UTF8CHAR *CK_UTF8CHAR_PTR; -typedef CK_ULONG *CK_ULONG_PTR; -typedef void *CK_VOID_PTR; -typedef void **CK_VOID_PTR_PTR; -#define CK_FALSE 0 -#define CK_TRUE 1 #ifndef CK_DISABLE_TRUE_FALSE #ifndef FALSE #define FALSE 0 @@ -1261,133 +52,27 @@ typedef void **CK_VOID_PTR_PTR; #endif #endif -typedef struct ck_version CK_VERSION; -typedef struct ck_version *CK_VERSION_PTR; - -typedef struct ck_info CK_INFO; -typedef struct ck_info *CK_INFO_PTR; - -typedef ck_slot_id_t *CK_SLOT_ID_PTR; - -typedef struct ck_slot_info CK_SLOT_INFO; -typedef struct ck_slot_info *CK_SLOT_INFO_PTR; - -typedef struct ck_token_info CK_TOKEN_INFO; -typedef struct ck_token_info *CK_TOKEN_INFO_PTR; - -typedef ck_session_handle_t *CK_SESSION_HANDLE_PTR; - -typedef struct ck_session_info CK_SESSION_INFO; -typedef struct ck_session_info *CK_SESSION_INFO_PTR; - -typedef ck_object_handle_t *CK_OBJECT_HANDLE_PTR; - -typedef ck_object_class_t *CK_OBJECT_CLASS_PTR; - -typedef struct ck_attribute CK_ATTRIBUTE; -typedef struct ck_attribute *CK_ATTRIBUTE_PTR; - -typedef struct ck_date CK_DATE; -typedef struct ck_date *CK_DATE_PTR; - -typedef ck_mechanism_type_t *CK_MECHANISM_TYPE_PTR; - -typedef struct ck_mechanism CK_MECHANISM; -typedef struct ck_mechanism *CK_MECHANISM_PTR; - -typedef struct ck_mechanism_info CK_MECHANISM_INFO; -typedef struct ck_mechanism_info *CK_MECHANISM_INFO_PTR; - -typedef struct ck_function_list CK_FUNCTION_LIST; -typedef struct ck_function_list *CK_FUNCTION_LIST_PTR; -typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR; - -typedef struct ck_c_initialize_args CK_C_INITIALIZE_ARGS; -typedef struct ck_c_initialize_args *CK_C_INITIALIZE_ARGS_PTR; - -#define NULL_PTR NULL - -/* Delete the helper macros defined at the top of the file. */ -#undef ck_flags_t -#undef ck_version - -#undef ck_info -#undef cryptoki_version -#undef manufacturer_id -#undef library_description -#undef library_version - -#undef ck_notification_t -#undef ck_slot_id_t - -#undef ck_slot_info -#undef slot_description -#undef hardware_version -#undef firmware_version - -#undef ck_token_info -#undef serial_number -#undef max_session_count -#undef session_count -#undef max_rw_session_count -#undef rw_session_count -#undef max_pin_len -#undef min_pin_len -#undef total_public_memory -#undef free_public_memory -#undef total_private_memory -#undef free_private_memory -#undef utc_time - -#undef ck_session_handle_t -#undef ck_user_type_t -#undef ck_state_t - -#undef ck_session_info -#undef slot_id -#undef device_error - -#undef ck_object_handle_t -#undef ck_object_class_t -#undef ck_hw_feature_type_t -#undef ck_key_type_t -#undef ck_certificate_type_t -#undef ck_attribute_type_t - -#undef ck_attribute -#undef value -#undef value_len - -#undef ck_date - -#undef ck_mechanism_type_t - -#undef ck_mechanism -#undef parameter -#undef parameter_len - -#undef ck_mechanism_info -#undef min_key_size -#undef max_key_size - -#undef ck_rv_t -#undef ck_notify_t - -#undef ck_function_list - -#undef ck_createmutex_t -#undef ck_destroymutex_t -#undef ck_lockmutex_t -#undef ck_unlockmutex_t - -#undef ck_c_initialize_args -#undef create_mutex -#undef destroy_mutex -#undef lock_mutex -#undef unlock_mutex -#undef reserved - -#endif /* CRYPTOKI_COMPAT */ +#define PKCS11_DEPRECATED + +#include "pd-pkcs11.h" + +/* + * A mask for new GOST algorithms. + * For details visit https://tc26.ru/standarts/perevody/guidelines-the-pkcs-11-extensions-for-implementing-the-gost-r-34-10-2012-and-gost-r-34-11-2012-russian-standards-.html + */ +#define NSSCK_VENDOR_PKCS11_RU_TEAM (CKK_VENDOR_DEFINED | 0x54321000) +#define CK_VENDOR_PKCS11_RU_TEAM_TK26 NSSCK_VENDOR_PKCS11_RU_TEAM + +#define CKK_GOSTR3410_512 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x003) +#define CKM_GOSTR3410_512_KEY_PAIR_GEN (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x005) +#define CKM_GOSTR3410_512 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x006) +#define CKM_GOSTR3410_12_DERIVE (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x007) +#define CKM_GOSTR3410_WITH_GOSTR3411_12_256 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x008) +#define CKM_GOSTR3410_WITH_GOSTR3411_12_512 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x009) +#define CKM_GOSTR3411_12_256 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x012) +#define CKM_GOSTR3411_12_512 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x013) +#define CKM_GOSTR3411_12_256_HMAC (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x014) +#define CKM_GOSTR3411_12_512_HMAC (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x015) /* System dependencies. */ #if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32) diff --git a/src/pkcs11/sc-pkcs11.h b/src/pkcs11/sc-pkcs11.h index 834573bfa0..3538f8ae66 100644 --- a/src/pkcs11/sc-pkcs11.h +++ b/src/pkcs11/sc-pkcs11.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __sc_pkcs11_h__ @@ -36,12 +36,6 @@ extern "C" { #endif -#if defined(_WIN32) || defined(USE_CYGWIN) -#define PKCS11_DEFAULT_MODULE_NAME "opensc-pkcs11.dll" -#else -#define PKCS11_DEFAULT_MODULE_NAME "opensc-pkcs11.so" -#endif - #define SC_PKCS11_PIN_UNBLOCK_NOT_ALLOWED 0 #define SC_PKCS11_PIN_UNBLOCK_UNLOGGED_SETPIN 1 #define SC_PKCS11_PIN_UNBLOCK_SCONTEXT_SETPIN 2 @@ -53,9 +47,6 @@ extern "C" { #define SC_PKCS11_SLOT_FOR_PINS (SC_PKCS11_SLOT_FOR_PIN_USER | SC_PKCS11_SLOT_FOR_PIN_SIGN) -extern void *C_LoadModule(const char *name, CK_FUNCTION_LIST_PTR_PTR); -extern CK_RV C_UnloadModule(void *module); - #ifdef __cplusplus } #endif @@ -74,16 +65,13 @@ struct sc_pkcs11_slot; struct sc_pkcs11_card; struct sc_pkcs11_config { - unsigned int plug_and_play; unsigned int max_virtual_slots; unsigned int slots_per_card; - unsigned char hide_empty_tokens; unsigned char lock_login; unsigned char atomic; unsigned char init_sloppy; unsigned int pin_unblock_style; unsigned int create_puk_slot; - unsigned int zero_ckaid_for_ca_certs; unsigned int create_slots_flags; unsigned char ignore_pin_length; }; @@ -99,7 +87,7 @@ struct sc_pkcs11_object_ops { /* Management methods */ CK_RV (*set_attribute)(struct sc_pkcs11_session *, void *, CK_ATTRIBUTE_PTR); CK_RV (*get_attribute)(struct sc_pkcs11_session *, void *, CK_ATTRIBUTE_PTR); - int (*cmp_attribute)(struct sc_pkcs11_session *, void *, CK_ATTRIBUTE_PTR); + CK_RV (*cmp_attribute)(struct sc_pkcs11_session *, void *, CK_ATTRIBUTE_PTR); CK_RV (*destroy_object)(struct sc_pkcs11_session *, void *); CK_RV (*get_size)(struct sc_pkcs11_session *, void *); @@ -112,13 +100,15 @@ struct sc_pkcs11_object_ops { CK_RV (*unwrap_key)(struct sc_pkcs11_session *, void *, CK_MECHANISM_PTR, CK_BYTE_PTR pData, CK_ULONG ulDataLen, - CK_ATTRIBUTE_PTR, CK_ULONG, - void **); + void *targetKey); CK_RV (*decrypt)(struct sc_pkcs11_session *, void *, CK_MECHANISM_PTR, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen); - + CK_RV (*encrypt)(struct sc_pkcs11_session *, void *, + CK_MECHANISM_PTR, + CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen); CK_RV (*derive)(struct sc_pkcs11_session *, void *, CK_MECHANISM_PTR, CK_BYTE_PTR pSeedData, CK_ULONG ulSeedDataLen, @@ -127,6 +117,14 @@ struct sc_pkcs11_object_ops { /* Check compatibility of PKCS#15 object usage and an asked PKCS#11 mechanism. */ CK_RV (*can_do)(struct sc_pkcs11_session *, void *, CK_MECHANISM_TYPE, unsigned int); + /* General validation of mechanism parameters (sign, encrypt, etc) */ + CK_RV (*init_params)(struct sc_pkcs11_session *, CK_MECHANISM_PTR); + + CK_RV (*wrap_key)(struct sc_pkcs11_session *, void *, + CK_MECHANISM_PTR, + void*, + CK_BYTE_PTR pData, CK_ULONG_PTR ulDataLen); + /* Others to be added when implemented */ }; @@ -164,7 +162,7 @@ struct sc_pkcs11_framework_ops { CK_CHAR_PTR, CK_ULONG, CK_CHAR_PTR, CK_ULONG); /* - * In future: functions to create new objects (ie. certificates, private keys) + * In future: functions to create new objects (i.e. certificates, private keys) */ CK_RV (*init_token)(struct sc_pkcs11_slot *, void *, CK_UTF8CHAR_PTR, CK_ULONG, @@ -203,6 +201,9 @@ struct sc_pkcs11_card { /* List of supported mechanisms */ struct sc_pkcs11_mechanism_type **mechanisms; unsigned int nmechanisms; + + /* Number of virtual slots the card occupies */ + unsigned int num_slots; }; /* If the slot did already show with `C_GetSlotList`, then we need to keep this @@ -217,6 +218,7 @@ struct sc_pkcs11_slot { CK_SLOT_INFO slot_info; /* Slot specific information (information about reader) */ CK_TOKEN_INFO token_info; /* Token specific information (information about card) */ sc_reader_t *reader; /* same as card->reader if there's a card present */ + void *reader_events; struct sc_pkcs11_card *p11card; /* The card associated with this slot */ unsigned int events; /* Card events SC_EVENT_CARD_{INSERTED,REMOVED} */ void *fw_data; /* Framework specific data */ /* TODO: get know how it used */ @@ -225,12 +227,40 @@ struct sc_pkcs11_slot { sc_timestamp_t slot_state_expires; int fw_data_idx; /* Index of framework data */ - struct sc_app_info *app_info; /* Application assosiated to slot */ + struct sc_app_info *app_info; /* Application associated to slot */ list_t logins; /* tracks all calls to C_Login if atomic operations are requested */ int flags; + struct pkcs15_any_object *profile; /* keeps track of the profile object */ }; typedef struct sc_pkcs11_slot sc_pkcs11_slot_t; +#define SC_LOG_RV(fmt, rv)\ +do {\ + const char *name = lookup_enum(RV_T, (rv));\ + if (name)\ + sc_log(context, (fmt), name);\ + else {\ + size_t needed = snprintf(NULL, 0, "0x%08lX", (rv)) + 1;\ + char *buffer = malloc(needed);\ + if (buffer) {\ + sprintf(buffer, "0x%08lX", (rv));\ + sc_log(context, (fmt), buffer);\ + free(buffer);\ + }\ + }\ +} while(0) + +#define SC_LOG(fmt) \ + do { \ + sc_log(context, (fmt)); \ + } while (0) + +/* Debug virtual slots. S is slot to be highlighted or NULL + * C is a comment format string and args It will be preceded by "VSS " */ +#define DEBUG_VSS(S, ...) do { sc_log(context,"VSS " __VA_ARGS__); _debug_virtual_slots(S); } while (0) + +/* called by DEBUG_VSS to print table of virtual slots */ +void _debug_virtual_slots(sc_pkcs11_slot_t *p); /* Forward decl */ typedef struct sc_pkcs11_operation sc_pkcs11_operation_t; @@ -241,15 +271,20 @@ enum { SC_PKCS11_OPERATION_VERIFY, SC_PKCS11_OPERATION_DIGEST, SC_PKCS11_OPERATION_DECRYPT, + SC_PKCS11_OPERATION_ENCRYPT, SC_PKCS11_OPERATION_DERIVE, + SC_PKCS11_OPERATION_WRAP, + SC_PKCS11_OPERATION_UNWRAP, SC_PKCS11_OPERATION_MAX }; +#define MAX_KEY_TYPES 2 + /* This describes a PKCS11 mechanism */ struct sc_pkcs11_mechanism_type { - CK_MECHANISM_TYPE mech; /* algorithm: md5, sha1, ... */ - CK_MECHANISM_INFO mech_info; /* mechanism info */ - CK_MECHANISM_TYPE key_type; /* for sign/decipher ops */ + CK_MECHANISM_TYPE mech; /* algorithm: md5, sha1, ... */ + CK_MECHANISM_INFO mech_info; /* mechanism info */ + int key_types[MAX_KEY_TYPES]; /* for sign/decipher ops */ unsigned int obj_size; /* General management */ @@ -278,17 +313,42 @@ struct sc_pkcs11_mechanism_type { CK_BYTE_PTR, CK_ULONG); CK_RV (*decrypt_init)(sc_pkcs11_operation_t *, struct sc_pkcs11_object *); + CK_RV (*decrypt_update)(sc_pkcs11_operation_t *, + CK_BYTE_PTR, CK_ULONG, + CK_BYTE_PTR, CK_ULONG_PTR); + CK_RV (*decrypt_final)(sc_pkcs11_operation_t *, + CK_BYTE_PTR, CK_ULONG_PTR); CK_RV (*decrypt)(sc_pkcs11_operation_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); + CK_RV (*encrypt_init)(sc_pkcs11_operation_t *, + struct sc_pkcs11_object *); + CK_RV (*encrypt)(sc_pkcs11_operation_t *, + CK_BYTE_PTR, CK_ULONG, + CK_BYTE_PTR, CK_ULONG_PTR); + CK_RV (*encrypt_update)(sc_pkcs11_operation_t *, + CK_BYTE_PTR, CK_ULONG, + CK_BYTE_PTR, CK_ULONG_PTR); + CK_RV (*encrypt_final)(sc_pkcs11_operation_t *, + CK_BYTE_PTR, CK_ULONG_PTR); CK_RV (*derive)(sc_pkcs11_operation_t *, struct sc_pkcs11_object *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); + CK_RV (*wrap)(sc_pkcs11_operation_t *, + struct sc_pkcs11_object *, + struct sc_pkcs11_object *, + CK_BYTE_PTR, CK_ULONG_PTR); + CK_RV (*unwrap)(sc_pkcs11_operation_t *, + struct sc_pkcs11_object *, + CK_BYTE_PTR, CK_ULONG, + struct sc_pkcs11_object *); + /* mechanism specific data */ const void * mech_data; /* free mechanism specific data */ void (*free_mech_data)(const void *mech_data); + CK_RV (*copy_mech_data)(const void *mech_data, void **new_data); }; typedef struct sc_pkcs11_mechanism_type sc_pkcs11_mechanism_type_t; @@ -298,6 +358,10 @@ typedef struct sc_pkcs11_mechanism_type sc_pkcs11_mechanism_type_t; struct sc_pkcs11_operation { sc_pkcs11_mechanism_type_t *type; CK_MECHANISM mechanism; + union { + CK_RSA_PKCS_PSS_PARAMS pss; + CK_RSA_PKCS_OAEP_PARAMS oaep; + } mechanism_params; struct sc_pkcs11_session *session; void * priv_data; }; @@ -342,22 +406,24 @@ void strcpy_bp(u8 *dst, const char *src, size_t dstsize); CK_RV sc_to_cryptoki_error(int rc, const char *ctx); void sc_pkcs11_print_attrs(int level, const char *file, unsigned int line, const char *function, const char *info, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount); +void sc_pkcs11_card_free(struct sc_pkcs11_card *p11card); #define dump_template(level, info, pTemplate, ulCount) \ - sc_pkcs11_print_attrs(level, __FILE__, __LINE__, __FUNCTION__, \ + sc_pkcs11_print_attrs(level, FILENAME, __LINE__, __FUNCTION__, \ info, pTemplate, ulCount) /* Slot and card handling functions */ CK_RV card_removed(sc_reader_t *reader); CK_RV card_detect_all(void); CK_RV create_slot(sc_reader_t *reader); -CK_RV initialize_reader(sc_reader_t *reader); -CK_RV card_detect(sc_reader_t *reader); +void init_slot_info(CK_SLOT_INFO_PTR pInfo, sc_reader_t *reader); +CK_RV card_detect(sc_reader_t *reader, void *reader_states); CK_RV slot_get_slot(CK_SLOT_ID id, struct sc_pkcs11_slot **); CK_RV slot_get_token(CK_SLOT_ID id, struct sc_pkcs11_slot **); CK_RV slot_token_removed(CK_SLOT_ID id); CK_RV slot_allocate(struct sc_pkcs11_slot **, struct sc_pkcs11_card *); CK_RV slot_find_changed(CK_SLOT_ID_PTR idp, int mask); int slot_get_logged_in_state(struct sc_pkcs11_slot *slot); +int slot_get_card_state(struct sc_pkcs11_slot *slot); /* Login tracking functions */ CK_RV restore_login_state(struct sc_pkcs11_slot *slot); @@ -383,7 +449,7 @@ CK_RV sc_pkcs11_create_secret_key(struct sc_pkcs11_session *, CK_ATTRIBUTE_PTR, CK_ULONG, struct sc_pkcs11_object **); /* Generic object handling */ -int sc_pkcs11_any_cmp_attribute(struct sc_pkcs11_session *, +CK_RV sc_pkcs11_any_cmp_attribute(struct sc_pkcs11_session *, void *, CK_ATTRIBUTE_PTR); /* Get attributes from template (misc.c) */ @@ -399,7 +465,7 @@ CK_RV attr_extract(CK_ATTRIBUTE_PTR, void *, size_t *); /* Generic Mechanism functions */ CK_RV sc_pkcs11_register_mechanism(struct sc_pkcs11_card *, - sc_pkcs11_mechanism_type_t *); + sc_pkcs11_mechanism_type_t *, sc_pkcs11_mechanism_type_t **); CK_RV sc_pkcs11_get_mechanism_list(struct sc_pkcs11_card *, CK_MECHANISM_TYPE_PTR, CK_ULONG_PTR); CK_RV sc_pkcs11_get_mechanism_info(struct sc_pkcs11_card *, CK_MECHANISM_TYPE, @@ -408,26 +474,37 @@ CK_RV sc_pkcs11_md_init(struct sc_pkcs11_session *, CK_MECHANISM_PTR); CK_RV sc_pkcs11_md_update(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG); CK_RV sc_pkcs11_md_final(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV sc_pkcs11_sign_init(struct sc_pkcs11_session *, CK_MECHANISM_PTR, - struct sc_pkcs11_object *, CK_MECHANISM_TYPE); + struct sc_pkcs11_object *, CK_KEY_TYPE); CK_RV sc_pkcs11_sign_update(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG); CK_RV sc_pkcs11_sign_final(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV sc_pkcs11_sign_size(struct sc_pkcs11_session *, CK_ULONG_PTR); #ifdef ENABLE_OPENSSL CK_RV sc_pkcs11_verif_init(struct sc_pkcs11_session *, CK_MECHANISM_PTR, - struct sc_pkcs11_object *, CK_MECHANISM_TYPE); + struct sc_pkcs11_object *, CK_KEY_TYPE); CK_RV sc_pkcs11_verif_update(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG); CK_RV sc_pkcs11_verif_final(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG); #endif -CK_RV sc_pkcs11_decr_init(struct sc_pkcs11_session *, CK_MECHANISM_PTR, struct sc_pkcs11_object *, CK_MECHANISM_TYPE); +CK_RV sc_pkcs11_decr_init(struct sc_pkcs11_session *, CK_MECHANISM_PTR, struct sc_pkcs11_object *, CK_KEY_TYPE); CK_RV sc_pkcs11_decr(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); +CK_RV sc_pkcs11_decr_update(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); +CK_RV sc_pkcs11_decr_final(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG_PTR); + +CK_RV sc_pkcs11_encr_init(struct sc_pkcs11_session *, CK_MECHANISM_PTR, struct sc_pkcs11_object *, CK_MECHANISM_TYPE); +CK_RV sc_pkcs11_encr(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); +CK_RV sc_pkcs11_encr_update(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); +CK_RV sc_pkcs11_encr_final(struct sc_pkcs11_session *, CK_BYTE_PTR, CK_ULONG_PTR); + +CK_RV sc_pkcs11_wrap(struct sc_pkcs11_session *,CK_MECHANISM_PTR, struct sc_pkcs11_object *, CK_KEY_TYPE, struct sc_pkcs11_object *, CK_BYTE_PTR, CK_ULONG_PTR); +CK_RV sc_pkcs11_unwrap(struct sc_pkcs11_session *,CK_MECHANISM_PTR, struct sc_pkcs11_object *, CK_KEY_TYPE, CK_BYTE_PTR, CK_ULONG, struct sc_pkcs11_object *); CK_RV sc_pkcs11_deri(struct sc_pkcs11_session *, CK_MECHANISM_PTR, struct sc_pkcs11_object *, CK_KEY_TYPE, CK_SESSION_HANDLE, CK_OBJECT_HANDLE, struct sc_pkcs11_object *); sc_pkcs11_mechanism_type_t *sc_pkcs11_find_mechanism(struct sc_pkcs11_card *, - CK_MECHANISM_TYPE, unsigned int); + CK_MECHANISM_TYPE, CK_FLAGS); sc_pkcs11_mechanism_type_t *sc_pkcs11_new_fw_mechanism(CK_MECHANISM_TYPE, CK_MECHANISM_INFO_PTR, CK_KEY_TYPE, - const void *, void (*)(const void *)); + const void *, void (*)(const void *), CK_RV (*)(const void *, void **)); +void sc_pkcs11_free_mechanism(sc_pkcs11_mechanism_type_t **mt); sc_pkcs11_operation_t *sc_pkcs11_new_operation(sc_pkcs11_session_t *, sc_pkcs11_mechanism_type_t *); void sc_pkcs11_release_operation(sc_pkcs11_operation_t **); @@ -440,11 +517,11 @@ CK_RV sc_pkcs11_register_sign_and_hash_mechanism(struct sc_pkcs11_card *, sc_pkcs11_mechanism_type_t *); #ifdef ENABLE_OPENSSL -CK_RV sc_pkcs11_verify_data(const unsigned char *pubkey, int pubkey_len, - const unsigned char *pubkey_params, int pubkey_params_len, - CK_MECHANISM_TYPE mech, sc_pkcs11_operation_t *md, - unsigned char *inp, int inp_len, - unsigned char *signat, int signat_len); +CK_RV sc_pkcs11_verify_data(const CK_BYTE_PTR pubkey, CK_ULONG pubkey_len, + const CK_BYTE_PTR pubkey_params, CK_ULONG pubkey_params_len, + CK_MECHANISM_PTR mech, sc_pkcs11_operation_t *md, + CK_BYTE_PTR inp, CK_ULONG inp_len, + CK_BYTE_PTR signat, CK_ULONG signat_len); #endif /* Load configuration defaults */ diff --git a/src/pkcs11/slot.c b/src/pkcs11/slot.c index 0b6b208b4f..6233d46478 100644 --- a/src/pkcs11/slot.c +++ b/src/pkcs11/slot.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -27,6 +27,32 @@ #include "sc-pkcs11.h" +/* Print virtual_slots list. Called by DEBUG_VSS(S, C) */ +void _debug_virtual_slots(sc_pkcs11_slot_t *p) +{ + int i, vs_size; + sc_pkcs11_slot_t * slot; + + vs_size = list_size(&virtual_slots); + _sc_debug(context, 10, + "VSS size:%d", vs_size); + _sc_debug(context, 10, + "VSS [i] id flags LU events nsessions slot_info.flags reader p11card description"); + for (i = 0; i < vs_size; i++) { + slot = (sc_pkcs11_slot_t *) list_get_at(&virtual_slots, i); + if (slot) { + _sc_debug(context, 10, + "VSS %s[%d] 0x%2.2lx 0x%4.4x %d %d %d %4.4lx %p %p %.64s", + ((slot == p) ? "*" : " "), + i, slot->id, slot->flags, slot->login_user, slot->events, slot->nsessions, + slot->slot_info.flags, + slot->reader, slot->p11card, + slot->slot_info.slotDescription); + } + } + _sc_debug(context, 10, "VSS END"); +} + static struct sc_pkcs11_framework_ops *frameworks[] = { &framework_pkcs15, #ifdef USE_PKCS15_INIT @@ -37,20 +63,32 @@ static struct sc_pkcs11_framework_ops *frameworks[] = { NULL }; -static struct sc_pkcs11_slot * reader_get_slot(sc_reader_t *reader) +static struct sc_pkcs11_slot * reader_reclaim_slot(sc_reader_t *reader) { unsigned int i; + CK_UTF8CHAR slotDescription[64]; + CK_UTF8CHAR manufacturerID[32]; + + if (reader == NULL) + return NULL; + strcpy_bp(slotDescription, reader->name, 64); + strcpy_bp(manufacturerID, reader->vendor, 32); /* Locate a slot related to the reader */ for (i = 0; ireader == reader) + if (slot->reader == NULL + && 0 == memcmp(slot->slot_info.slotDescription, slotDescription, 64) + && 0 == memcmp(slot->slot_info.manufacturerID, manufacturerID, 32) + && slot->slot_info.hardwareVersion.major == reader->version_major + && slot->slot_info.hardwareVersion.minor == reader->version_minor) { return slot; + } } return NULL; } -static void init_slot_info(CK_SLOT_INFO_PTR pInfo, sc_reader_t *reader) +void init_slot_info(CK_SLOT_INFO_PTR pInfo, sc_reader_t *reader) { if (reader) { strcpy_bp(pInfo->slotDescription, reader->name, 64); @@ -82,11 +120,14 @@ static int object_list_seeker(const void *el, const void *key) CK_RV create_slot(sc_reader_t *reader) { - /* find unused virtual hotplug slots */ - struct sc_pkcs11_slot *slot = reader_get_slot(NULL); + unsigned int events; + sc_reader_t *found; + /* find unused slots previously allocated for the same reader */ + struct sc_pkcs11_slot *slot = reader_reclaim_slot(reader); /* create a new slot if no empty slot is available */ if (!slot) { + sc_log(context, "Creating new slot"); if (list_size(&virtual_slots) >= sc_pkcs11_conf.max_virtual_slots) return CKR_FUNCTION_FAILED; @@ -104,6 +145,8 @@ CK_RV create_slot(sc_reader_t *reader) return CKR_HOST_MEMORY; } } else { + DEBUG_VSS(slot, "Reusing this old slot"); + /* reuse the old list of logins/objects since they should be empty */ list_t logins = slot->logins; list_t objects = slot->objects; @@ -117,76 +160,32 @@ CK_RV create_slot(sc_reader_t *reader) slot->login_user = -1; slot->id = (CK_SLOT_ID) list_locate(&virtual_slots, slot); init_slot_info(&slot->slot_info, reader); - sc_log(context, "Initializing slot with id 0x%lx", slot->id); - - if (reader != NULL) { - slot->reader = reader; - strcpy_bp(slot->slot_info.manufacturerID, reader->vendor, 32); - strcpy_bp(slot->slot_info.slotDescription, reader->name, 64); - slot->slot_info.hardwareVersion.major = reader->version_major; - slot->slot_info.hardwareVersion.minor = reader->version_minor; - } + slot->reader = reader; + sc_wait_for_event(context, SC_EVENT_CARD_EVENTS | SC_EVENT_READER_EVENTS, &found, &events, 0, &slot->reader_events); - return CKR_OK; -} + DEBUG_VSS(slot, "Finished initializing this slot"); -void empty_slot(struct sc_pkcs11_slot *slot) -{ - if (slot) { - if (slot->flags & SC_PKCS11_SLOT_FLAG_SEEN) { - /* Keep the slot visible to the application. The slot's state has - * already been reset by `slot_token_removed()`, lists have been - * emptied. We replace the reader with a virtual hotplug slot. */ - slot->reader = NULL; - init_slot_info(&slot->slot_info, NULL); - } else { - list_destroy(&slot->objects); - list_destroy(&slot->logins); - list_delete(&virtual_slots, slot); - free(slot); - } - } + return CKR_OK; } - -/* create slots associated with a reader, called whenever a reader is seen. */ -CK_RV initialize_reader(sc_reader_t *reader) +void sc_pkcs11_card_free(struct sc_pkcs11_card *p11card) { - unsigned int i; - CK_RV rv; - - scconf_block *conf_block = NULL; - const scconf_list *list = NULL; - - conf_block = sc_get_conf_block(context, "pkcs11", NULL, 1); - if (conf_block != NULL) { - list = scconf_find_list(conf_block, "ignored_readers"); - while (list != NULL) { - if (strstr(reader->name, list->data) != NULL) { - sc_log(context, "Ignoring reader \'%s\' because of \'%s\'\n", reader->name, list->data); - return CKR_OK; + if (p11card) { + size_t i; + if (p11card->framework && p11card->framework->unbind) + p11card->framework->unbind(p11card); + sc_disconnect_card(p11card->card); + for (i=0; i < p11card->nmechanisms; ++i) { + if (p11card->mechanisms[i]->free_mech_data) { + p11card->mechanisms[i]->free_mech_data(p11card->mechanisms[i]->mech_data); } - list = list->next; + free(p11card->mechanisms[i]); } + free(p11card->mechanisms); + free(p11card); } - - for (i = 0; i < sc_pkcs11_conf.slots_per_card; i++) { - rv = create_slot(reader); - if (rv != CKR_OK) - return rv; - } - - sc_log(context, "Initialize reader '%s': detect SC card presence", reader->name); - if (sc_detect_card_presence(reader)) { - sc_log(context, "Initialize reader '%s': detect PKCS11 card presence", reader->name); - card_detect(reader); - } - - sc_log(context, "Reader '%s' initialized", reader->name); - return CKR_OK; } - CK_RV card_removed(sc_reader_t * reader) { unsigned int i; @@ -205,35 +204,41 @@ CK_RV card_removed(sc_reader_t * reader) } } - if (p11card) { - p11card->framework->unbind(p11card); - sc_disconnect_card(p11card->card); - for (i=0; i < p11card->nmechanisms; ++i) { - if (p11card->mechanisms[i]->free_mech_data) { - p11card->mechanisms[i]->free_mech_data(p11card->mechanisms[i]->mech_data); - } - free(p11card->mechanisms[i]); - } - free(p11card->mechanisms); - free(p11card); - } + sc_pkcs11_card_free(p11card); return CKR_OK; } - -CK_RV card_detect(sc_reader_t *reader) +CK_RV +card_detect(sc_reader_t *reader, void *reader_states) { struct sc_pkcs11_card *p11card = NULL; + int free_p11card = 0; int rc; + int no_change = 0; CK_RV rv; unsigned int i; int j; - - rv = CKR_OK; + static int retry = 3; sc_log(context, "%s: Detecting smart card", reader->name); /* Check if someone inserted a card */ + + if (reader_states != NULL) { + /* check if any event has occurred since last invocation of `card_detect()` */ + unsigned int mask, events; + sc_reader_t *event_reader; + /* Detect card and reader events */ + mask = SC_EVENT_CARD_EVENTS | SC_EVENT_READER_EVENTS; + int r = sc_wait_for_event(context, mask, &event_reader, &events, 0, &reader_states); + if (r == SC_ERROR_EVENT_TIMEOUT || reader != event_reader) + /* no change happened */ + no_change = 1; + else + /* if some error occurred or there actually was a change, continue with detection routine */ + no_change = 0; + } + again: rc = sc_detect_card_presence(reader); if (rc < 0) { @@ -246,15 +251,22 @@ CK_RV card_detect(sc_reader_t *reader) return CKR_TOKEN_NOT_PRESENT; } + if (no_change == 1) { + sc_log(context, "%s: card present, no change detected", reader->name); + return CKR_OK; + } + /* If the card was changed, disconnect the current one */ if (rc & SC_READER_CARD_CHANGED) { sc_log(context, "%s: Card changed", reader->name); /* The following should never happen - but if it * does we'll be stuck in an endless loop. - * So better be fussy. - if (!retry--) - return CKR_TOKEN_NOT_PRESENT; */ + * So better be fussy.*/ card_removed(reader); + if (!retry--) { + retry = 3; + return CKR_TOKEN_NOT_PRESENT; + } goto again; } @@ -273,15 +285,17 @@ CK_RV card_detect(sc_reader_t *reader) p11card = (struct sc_pkcs11_card *)calloc(1, sizeof(struct sc_pkcs11_card)); if (!p11card) return CKR_HOST_MEMORY; + free_p11card = 1; p11card->reader = reader; } if (p11card->card == NULL) { sc_log(context, "%s: Connecting ... ", reader->name); rc = sc_connect_card(reader, &p11card->card); - if (rc != SC_SUCCESS) { + if (rc != SC_SUCCESS) { sc_log(context, "%s: SC connect card error %i", reader->name, rc); - return sc_to_cryptoki_error(rc, NULL); + rv = sc_to_cryptoki_error(rc, NULL); + goto fail; } /* escape commands are only guaranteed to be working with a card @@ -310,8 +324,10 @@ CK_RV card_detect(sc_reader_t *reader) if (frameworks[i]->bind != NULL) break; /*TODO: only first framework is used: pkcs15init framework is not reachable here */ - if (frameworks[i] == NULL) - return CKR_GENERAL_ERROR; + if (frameworks[i] == NULL) { + rv = CKR_GENERAL_ERROR; + goto fail; + } p11card->framework = frameworks[i]; @@ -319,12 +335,17 @@ CK_RV card_detect(sc_reader_t *reader) sc_log(context, "%s: Detected framework %d. Creating tokens.", reader->name, i); /* Bind 'generic' application or (emulated?) card without applications */ if (app_generic || !p11card->card->app_count) { - scconf_block *atrblock = NULL; + scconf_block *conf_block = NULL; int enable_InitToken = 0; - atrblock = sc_match_atr_block(p11card->card->ctx, NULL, &p11card->reader->atr); - if (atrblock) - enable_InitToken = scconf_get_bool(atrblock, "pkcs11_enable_InitToken", 0); + conf_block = sc_match_atr_block(p11card->card->ctx, NULL, + &p11card->reader->atr); + if (!conf_block) /* check default block */ + conf_block = sc_get_conf_block(context, + "framework", "pkcs15", 1); + + enable_InitToken = scconf_get_bool(conf_block, + "pkcs11_enable_InitToken", 0); sc_log(context, "%s: Try to bind 'generic' token.", reader->name); rv = frameworks[i]->bind(p11card, app_generic); @@ -336,7 +357,7 @@ CK_RV card_detect(sc_reader_t *reader) sc_log(context, "%s: cannot bind 'generic' token: rv 0x%lX", reader->name, rv); - return rv; + goto fail; } sc_log(context, "%s: Creating 'generic' token.", reader->name); @@ -345,8 +366,10 @@ CK_RV card_detect(sc_reader_t *reader) sc_log(context, "%s: create 'generic' token error 0x%lX", reader->name, rv); - return rv; + goto fail; } + /* p11card is now bound to some slot */ + free_p11card = 0; } /* Now bind the rest of applications that are not 'generic' */ @@ -371,38 +394,69 @@ CK_RV card_detect(sc_reader_t *reader) sc_log(context, "%s: create %s token error 0x%lX", reader->name, app_name, rv); - return rv; + goto fail; } + /* p11card is now bound to some slot */ + free_p11card = 0; } } sc_log(context, "%s: Detection ended", reader->name); - return CKR_OK; + rv = CKR_OK; + +fail: + if (free_p11card) { + sc_pkcs11_card_free(p11card); + } + + return rv; } CK_RV card_detect_all(void) { - unsigned int i; + unsigned int i, j; sc_log(context, "Detect all cards"); /* Detect cards in all initialized readers */ for (i=0; i< sc_ctx_get_reader_count(context); i++) { sc_reader_t *reader = sc_ctx_get_reader(context, i); + if (reader->flags & SC_READER_REMOVED) { - struct sc_pkcs11_slot *slot; card_removed(reader); - while ((slot = reader_get_slot(reader))) { - empty_slot(slot); + /* do not remove slots related to this reader which would be + * possible according to PKCS#11 2.20 and later, because NSS can't + * handle a shrinking slot list + * https://bugzilla.mozilla.org/show_bug.cgi?id=1613632 */ + + /* Instead, remove the relation between reader and slot */ + for (j = 0; jreader == reader) { + slot->reader = NULL; + sc_wait_for_event(context, 0, NULL, NULL, 0, &slot->reader_events); + } } - _sc_delete_reader(context, reader); - i--; } else { - if (!reader_get_slot(reader)) - initialize_reader(reader); - else - card_detect(sc_ctx_get_reader(context, i)); + /* Locate a slot related to the reader */ + int found = 0; + for (j = 0; jreader == reader) { + found = 1; + card_detect(slot->reader, slot->reader_events); + break; + } + } + if (!found) { + for (j = 0; j < sc_pkcs11_conf.slots_per_card; j++) { + CK_RV rv = create_slot(reader); + if (rv != CKR_OK) + return rv; + } + card_detect(reader, NULL); + } } } sc_log(context, "All cards detected"); @@ -443,7 +497,7 @@ CK_RV slot_get_slot(CK_SLOT_ID id, struct sc_pkcs11_slot ** slot) CK_RV slot_get_token(CK_SLOT_ID id, struct sc_pkcs11_slot ** slot) { - int rv; + CK_RV rv; sc_log(context, "Slot(id=0x%lX): get token", id); rv = slot_get_slot(id, slot); @@ -454,7 +508,7 @@ CK_RV slot_get_token(CK_SLOT_ID id, struct sc_pkcs11_slot ** slot) if ((*slot)->reader == NULL) return CKR_TOKEN_NOT_PRESENT; sc_log(context, "Slot(id=0x%lX): get token: now detect card", id); - rv = card_detect((*slot)->reader); + rv = card_detect((*slot)->reader, (*slot)->reader_events); if (rv != CKR_OK) return rv; } @@ -469,7 +523,8 @@ CK_RV slot_get_token(CK_SLOT_ID id, struct sc_pkcs11_slot ** slot) CK_RV slot_token_removed(CK_SLOT_ID id) { - int rv, token_was_present; + CK_RV rv; + int token_was_present; struct sc_pkcs11_slot *slot; struct sc_pkcs11_object *object; @@ -490,8 +545,8 @@ CK_RV slot_token_removed(CK_SLOT_ID id) /* Release framework stuff */ if (slot->p11card != NULL) { - if (slot->fw_data != NULL && - slot->p11card->framework != NULL && slot->p11card->framework->release_token != NULL) { + if (slot->fw_data != NULL && slot->p11card->framework != NULL + && slot->p11card->framework->release_token != NULL) { slot->p11card->framework->release_token(slot->p11card, slot->fw_data); slot->fw_data = NULL; } @@ -501,6 +556,7 @@ CK_RV slot_token_removed(CK_SLOT_ID id) /* Reset relevant slot properties */ slot->slot_info.flags &= ~CKF_TOKEN_PRESENT; slot->login_user = -1; + slot->profile = NULL; pop_all_login_states(slot); if (token_was_present) diff --git a/src/pkcs11/versioninfo-pkcs11-spy.rc b/src/pkcs11/versioninfo-pkcs11-spy.rc new file mode 100644 index 0000000000..d39deb8003 --- /dev/null +++ b/src/pkcs11/versioninfo-pkcs11-spy.rc @@ -0,0 +1,41 @@ +#include +#include "config.h" +#define TO_STR_HELPER(x) #x +#define TO_STR(x) TO_STR_HELPER(x) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION OPENSC_VERSION_MAJOR,OPENSC_VERSION_MINOR,OPENSC_VERSION_FIX,OPENSC_VERSION_REVISION + PRODUCTVERSION OPENSC_VERSION_MAJOR,OPENSC_VERSION_MINOR,OPENSC_VERSION_FIX,OPENSC_VERSION_REVISION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x21L +#else + FILEFLAGS 0x20L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", OPENSC_VS_FF_COMMENTS + VALUE "CompanyName", OPENSC_VS_FF_COMPANY_NAME + VALUE "FileVersion", TO_STR(OPENSC_VERSION_MAJOR.OPENSC_VERSION_MINOR.OPENSC_VERSION_FIX.OPENSC_VERSION_REVISION) + VALUE "InternalName", PACKAGE_NAME + VALUE "LegalCopyright", OPENSC_VS_FF_LEGAL_COPYRIGHT + VALUE "LegalTrademarks", "" + VALUE "PrivateBuild", "" + VALUE "ProductName", OPENSC_VS_FF_PRODUCT_NAME + VALUE "ProductVersion", TO_STR(OPENSC_VERSION_MAJOR.OPENSC_VERSION_MINOR.OPENSC_VERSION_FIX.OPENSC_VERSION_REVISION) + VALUE "SpecialBuild", "" + VALUE "FileDescription", "OpenSC PKCS#11 spy module" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/src/pkcs11/versioninfo-pkcs11-spy.rc.in b/src/pkcs11/versioninfo-pkcs11-spy.rc.in deleted file mode 100644 index a4a1a281da..0000000000 --- a/src/pkcs11/versioninfo-pkcs11-spy.rc.in +++ /dev/null @@ -1,38 +0,0 @@ -#include - -VS_VERSION_INFO VERSIONINFO - FILEVERSION @OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@ - PRODUCTVERSION @OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@ - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x21L -#else - FILEFLAGS 0x20L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "@OPENSC_VS_FF_COMMENTS@" - VALUE "CompanyName", "@OPENSC_VS_FF_COMPANY_NAME@" - VALUE "FileVersion", "@OPENSC_VERSION_MAJOR@.@OPENSC_VERSION_MINOR@.@OPENSC_VERSION_FIX@.@OPENSC_VERSION_REVISION@" - VALUE "InternalName", "@PACKAGE_NAME@" - VALUE "LegalCopyright", "@OPENSC_VS_FF_LEGAL_COPYRIGHT@" - VALUE "LegalTrademarks", "" - VALUE "PrivateBuild", "" - VALUE "ProductName", "@OPENSC_VS_FF_PRODUCT_NAME@" - VALUE "ProductVersion", "@OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@" - VALUE "SpecialBuild", "" - VALUE "FileDescription", "OpenSC PKCS#11 spy module" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - diff --git a/src/pkcs11/versioninfo-pkcs11.rc b/src/pkcs11/versioninfo-pkcs11.rc new file mode 100644 index 0000000000..f437f9445d --- /dev/null +++ b/src/pkcs11/versioninfo-pkcs11.rc @@ -0,0 +1,41 @@ +#include +#include "config.h" +#define TO_STR_HELPER(x) #x +#define TO_STR(x) TO_STR_HELPER(x) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION OPENSC_VERSION_MAJOR,OPENSC_VERSION_MINOR,OPENSC_VERSION_FIX,OPENSC_VERSION_REVISION + PRODUCTVERSION OPENSC_VERSION_MAJOR,OPENSC_VERSION_MINOR,OPENSC_VERSION_FIX,OPENSC_VERSION_REVISION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x21L +#else + FILEFLAGS 0x20L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", OPENSC_VS_FF_COMMENTS + VALUE "CompanyName", OPENSC_VS_FF_COMPANY_NAME + VALUE "FileVersion", TO_STR(OPENSC_VERSION_MAJOR.OPENSC_VERSION_MINOR.OPENSC_VERSION_FIX.OPENSC_VERSION_REVISION) + VALUE "InternalName", PACKAGE_NAME + VALUE "LegalCopyright", OPENSC_VS_FF_LEGAL_COPYRIGHT + VALUE "LegalTrademarks", "" + VALUE "PrivateBuild", "" + VALUE "ProductName", OPENSC_VS_FF_PRODUCT_NAME + VALUE "ProductVersion", TO_STR(OPENSC_VERSION_MAJOR.OPENSC_VERSION_MINOR.OPENSC_VERSION_FIX.OPENSC_VERSION_REVISION) + VALUE "SpecialBuild", "" + VALUE "FileDescription", "OpenSC PKCS#11 module" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/src/pkcs11/versioninfo-pkcs11.rc.in b/src/pkcs11/versioninfo-pkcs11.rc.in deleted file mode 100644 index e8bc91d3be..0000000000 --- a/src/pkcs11/versioninfo-pkcs11.rc.in +++ /dev/null @@ -1,38 +0,0 @@ -#include - -VS_VERSION_INFO VERSIONINFO - FILEVERSION @OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@ - PRODUCTVERSION @OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@ - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x21L -#else - FILEFLAGS 0x20L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "@OPENSC_VS_FF_COMMENTS@" - VALUE "CompanyName", "@OPENSC_VS_FF_COMPANY_NAME@" - VALUE "FileVersion", "@OPENSC_VERSION_MAJOR@.@OPENSC_VERSION_MINOR@.@OPENSC_VERSION_FIX@.@OPENSC_VERSION_REVISION@" - VALUE "InternalName", "@PACKAGE_NAME@" - VALUE "LegalCopyright", "@OPENSC_VS_FF_LEGAL_COPYRIGHT@" - VALUE "LegalTrademarks", "" - VALUE "PrivateBuild", "" - VALUE "ProductName", "@OPENSC_VS_FF_PRODUCT_NAME@" - VALUE "ProductVersion", "@OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@" - VALUE "SpecialBuild", "" - VALUE "FileDescription", "OpenSC PKCS#11 module" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - diff --git a/src/pkcs15init/Makefile.am b/src/pkcs15init/Makefile.am index 7d9d5f7214..b4912b2ce3 100644 --- a/src/pkcs15init/Makefile.am +++ b/src/pkcs15init/Makefile.am @@ -4,15 +4,11 @@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak noinst_LTLIBRARIES = libpkcs15init.la -noinst_HEADERS = profile.h pkcs15-init.h pkcs15-oberthur.h +noinst_HEADERS = profile.h pkcs15-init.h pkcs15-oberthur.h pkcs15-iasecc.h dist_pkgdata_DATA = \ cyberflex.profile \ flex.profile \ - gpk.profile \ - miocos.profile \ cardos.profile \ - incrypto34.profile \ - jcop.profile \ oberthur.profile \ starcos.profile \ setcos.profile \ @@ -23,7 +19,7 @@ dist_pkgdata_DATA = \ entersafe.profile \ epass2003.profile \ rutoken_ecp.profile \ - westcos.profile \ + rutoken_lite.profile \ myeid.profile \ authentic.profile \ iasecc.profile \ @@ -32,16 +28,15 @@ dist_pkgdata_DATA = \ openpgp.profile sc-hsm.profile \ isoApplet.profile gids.profile -AM_CPPFLAGS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\" \ +AM_CPPFLAGS = -D'SC_PKCS15_PROFILE_DIRECTORY="$(pkgdatadir)"' \ -I$(top_srcdir)/src AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) libpkcs15init_la_SOURCES = \ pkcs15-lib.c profile.c \ - pkcs15-westcos.c \ - pkcs15-gpk.c pkcs15-miocos.c pkcs15-cflex.c \ - pkcs15-cardos.c pkcs15-jcop.c pkcs15-starcos.c \ - pkcs15-setcos.c pkcs15-incrypto34.c pkcs15-muscle.c \ + pkcs15-cflex.c \ + pkcs15-cardos.c pkcs15-starcos.c \ + pkcs15-setcos.c pkcs15-muscle.c \ pkcs15-asepcos.c pkcs15-rutoken.c \ pkcs15-entersafe.c pkcs15-epass2003.c \ pkcs15-rtecp.c pkcs15-myeid.c \ diff --git a/src/pkcs15init/Makefile.mak b/src/pkcs15init/Makefile.mak index 1eb6857f24..5fc21359bb 100644 --- a/src/pkcs15init/Makefile.mak +++ b/src/pkcs15init/Makefile.mak @@ -2,12 +2,12 @@ TOPDIR = ..\.. TARGET = pkcs15init.lib OBJECTS = pkcs15-lib.obj profile.obj \ - pkcs15-gpk.obj pkcs15-miocos.obj pkcs15-cflex.obj \ - pkcs15-cardos.obj pkcs15-jcop.obj pkcs15-starcos.obj \ + pkcs15-cflex.obj \ + pkcs15-cardos.obj pkcs15-starcos.obj \ pkcs15-oberthur.obj pkcs15-oberthur-awp.obj \ - pkcs15-setcos.obj pkcs15-incrypto34.obj \ + pkcs15-setcos.obj \ pkcs15-muscle.obj pkcs15-asepcos.obj pkcs15-rutoken.obj \ - pkcs15-entersafe.obj pkcs15-rtecp.obj pkcs15-westcos.obj \ + pkcs15-entersafe.obj pkcs15-rtecp.obj \ pkcs15-myeid.obj pkcs15-authentic.obj pkcs15-iasecc.obj \ pkcs15-epass2003.obj pkcs15-openpgp.obj pkcs15-sc-hsm.obj \ pkcs15-isoApplet.obj pkcs15-gids.obj diff --git a/src/pkcs15init/gpk.profile b/src/pkcs15init/gpk.profile deleted file mode 100644 index 4858591b21..0000000000 --- a/src/pkcs15init/gpk.profile +++ /dev/null @@ -1,100 +0,0 @@ -# -# PKCS15 r/w profile for GPK cards -# -cardinfo { - max-pin-length = 8; - pin-encoding = BCD; - pin-pad-char = 0x00; -} - -# Additional filesystem info. -# This is added to the file system info specified in the -# main profile. -filesystem { - DF MF { - ACL = CREATE=PRO1; - - DF PKCS15-AppDF { - # The PIN file. - # The GPK supports just one PIN file per DF, and the file - # can hold up to 8 pins (or 4 PIN/PUK pairs). - # - # Note1: many commands use the short file ID (i.e. the lower - # 5 bits of the FID) so you must be careful when picking FIDs - # for the public key and PIN files. - - EF pinfile { - file-id = 0000; - structure = 0x21; # GPK specific - record-length = 8; - size = 64; # room for 8 pins - ACL = *=NEVER; - } - - # This template defines files for keys, certificates etc. - # - # When instantiating the template, each file id will be - # combined with the last octet of the object's pkcs15 id - # to form a unique file ID. - template key-domain { - # Private key files. - # GPK private key files will never let you read the private key - # part, so it's okay to set READ=NONE. What's more, we may need - # read access so we're able to check the key size/type. - EF private-key { - file-id = 3010; # This is the base FileID - structure = 0x2C; # GPK specific - ACL = *=NEVER, - READ=NONE, - CRYPTO=$PIN, - UPDATE=$PIN, - WRITE=$PIN; - } - - # Extractable private keys are stored in transparent EFs. - # Encryption of the content is performed by libopensc. - EF extractable-key { - file-id = 3100; - structure = transparent; - ACL = *=NEVER, - READ=$PIN, - UPDATE=$PIN, - WRITE=$PIN; - } - - # data objects are stored in transparent EFs. - EF data { - file-id = 3200; - structure = transparent; - ACL = *=NEVER, - READ=NONE, - UPDATE=$PIN, - WRITE=$PIN; - } - - # private data objects are stored in transparent EFs. - EF privdata { - file-id = 3220; - structure = transparent; - ACL = *=NEVER, - READ=$PIN, - UPDATE=$PIN, - WRITE=$PIN; - } - - EF public-key { - file-id = 3300; - structure = transparent; - ACL = *=NONE; - } - - # Certificate template - EF certificate { - file-id = 3400; - structure = transparent; - ACL = *=NONE; - } - } - } - } -} diff --git a/src/pkcs15init/ias_adele_admin1.profile b/src/pkcs15init/ias_adele_admin1.profile index af5ae8edf6..dce404b0a1 100644 --- a/src/pkcs15init/ias_adele_admin1.profile +++ b/src/pkcs15init/ias_adele_admin1.profile @@ -141,7 +141,7 @@ filesystem { # Private DES keys BSO private-des { size = 24; # 192 bits - # READ acl used insted of DECIPHER/ENCIPHER/CHECKSUM + # READ acl used instead of DECIPHER/ENCIPHER/CHECKSUM } # Private data diff --git a/src/pkcs15init/ias_adele_admin2.profile b/src/pkcs15init/ias_adele_admin2.profile index a35fb1b1df..2e08140c7b 100644 --- a/src/pkcs15init/ias_adele_admin2.profile +++ b/src/pkcs15init/ias_adele_admin2.profile @@ -10,7 +10,7 @@ cardinfo { pin-encoding = ascii-numeric; pin-pad-char = 0xFF; - # Delete or not the public key when inconporating the + # Delete or not the public key when incorporating the # corresponding certificate. keep-public-key = yes; # yes/no } @@ -138,7 +138,7 @@ filesystem { # Private DES keys BSO private-des { size = 24; # 192 bits - # READ acl used insted of DECIPHER/ENCIPHER/CHECKSUM + # READ acl used instead of DECIPHER/ENCIPHER/CHECKSUM } # Private data diff --git a/src/pkcs15init/ias_adele_common.profile b/src/pkcs15init/ias_adele_common.profile index 9c5b81fe15..257cb421a3 100644 --- a/src/pkcs15init/ias_adele_common.profile +++ b/src/pkcs15init/ias_adele_common.profile @@ -134,7 +134,7 @@ filesystem { # Private DES keys BSO private-des { size = 24; # 192 bits - # READ acl used insted of DECIPHER/ENCIPHER/CHECKSUM + # READ acl used instead of DECIPHER/ENCIPHER/CHECKSUM } # Private data diff --git a/src/pkcs15init/iasecc.profile b/src/pkcs15init/iasecc.profile index 7b9a8548cd..398edfb9a1 100644 --- a/src/pkcs15init/iasecc.profile +++ b/src/pkcs15init/iasecc.profile @@ -52,7 +52,7 @@ PIN so-pin { reference = 2 } -# CHV5 used for Oberthur's specifique access condition "PIN or SOPIN" +# CHV5 used for Oberthur's specific access condition "PIN or SOPIN" # Any value for this pin can given, when the OpenSC tools are asking for. # Additional filesystem info. diff --git a/src/pkcs15init/iasecc_admin_eid.profile b/src/pkcs15init/iasecc_admin_eid.profile index 395720bc2f..a51b68ec0e 100644 --- a/src/pkcs15init/iasecc_admin_eid.profile +++ b/src/pkcs15init/iasecc_admin_eid.profile @@ -54,7 +54,7 @@ PIN so-pin { reference = 2 } -# CHV5 used for Oberthur's specifique access condition "PIN or SOPIN" +# CHV5 used for Oberthur's specific access condition "PIN or SOPIN" # Any value for this pin can given, when the OpenSC tools are asking for. # Additional filesystem info. @@ -141,7 +141,7 @@ filesystem { # Private DES keys BSO private-des { size = 24; # 192 bits - # READ acl used insted of DECIPHER/ENCIPHER/CHECKSUM + # READ acl used instead of DECIPHER/ENCIPHER/CHECKSUM } # Private data diff --git a/src/pkcs15init/iasecc_generic_oberthur.profile b/src/pkcs15init/iasecc_generic_oberthur.profile index 2c9c093b9f..82f265bc74 100644 --- a/src/pkcs15init/iasecc_generic_oberthur.profile +++ b/src/pkcs15init/iasecc_generic_oberthur.profile @@ -136,7 +136,7 @@ filesystem { # Private DES keys BSO private-des { size = 24; # 192 bits - # READ acl used insted of DECIPHER/ENCIPHER/CHECKSUM + # READ acl used instead of DECIPHER/ENCIPHER/CHECKSUM } # Private data diff --git a/src/pkcs15init/iasecc_generic_pki.profile b/src/pkcs15init/iasecc_generic_pki.profile index da5b0dec13..5f150e4f4d 100644 --- a/src/pkcs15init/iasecc_generic_pki.profile +++ b/src/pkcs15init/iasecc_generic_pki.profile @@ -54,7 +54,7 @@ PIN so-pin { reference = 2 } -# CHV5 used for Oberthur's specifique access condition "PIN or SOPIN" +# CHV5 used for Oberthur's specific access condition "PIN or SOPIN" # Any value for this pin can given, when the OpenSC tools are asking for. # Additional filesystem info. @@ -139,7 +139,7 @@ filesystem { # Private DES keys BSO private-des { size = 24; # 192 bits - # READ acl used insted of DECIPHER/ENCIPHER/CHECKSUM + # READ acl used instead of DECIPHER/ENCIPHER/CHECKSUM } # Private data diff --git a/src/pkcs15init/incrypto34.profile b/src/pkcs15init/incrypto34.profile deleted file mode 100644 index 567ff92d4f..0000000000 --- a/src/pkcs15init/incrypto34.profile +++ /dev/null @@ -1,114 +0,0 @@ -# -# PKCS15 r/w profile for Incard's Incrypto34 (aka Italian CIE/CNS) -# -cardinfo { - max-pin-length = 8; - pin-encoding = ascii-numeric; - pin-pad-char = 0x00; -} - -# Define reasonable limits for PINs and PUK -# We set the reference for SO pin+puk here, because -# those are hard-coded (if a PUK us assigned). -PIN so-pin { - reference = 0; -} -PIN so-puk { - reference = 1; -} -PIN user-pin { - attempts = 3; -} -PIN user-puk { - attempts = 10; -} - -# Additional filesystem info. -# This is added to the file system info specified in the -# main profile. -filesystem { - DF MF { - DF PKCS15-AppDF { - size = 2048; - - # Prevent unauthorized updates of basic security - # objects via PUT DATA OCI. - ACL = UPDATE=NEVER; - - # Bump the size of the EF(PrKDF) - with split - # keys, we may need a little more room. - EF PKCS15-PrKDF { - size = 256; - } - - # This template defines files for keys, certificates etc. - # - # When instantiating the template, each file id will be - # combined with the last octet of the object's pkcs15 id - # to form a unique file ID. - template key-domain { - BSO private-key { - # here ACLs should be defined - } - EF public-key { - file-id = 3003; - structure = transparent; - ACL = *=NEVER, - READ=NONE, - UPDATE=$PIN, - ERASE=$PIN; - } - - # Certificate template - EF certificate { - file-id = 3104; - structure = transparent; - ACL = *=NEVER, - READ=NONE, - UPDATE=$PIN, - ERASE=$PIN; - } - - # Extractable private keys are stored in transparent EFs. - # Encryption of the content is performed by libopensc. - EF extractable-key { - file-id = 3201; - structure = transparent; - ACL = *=NEVER, - READ=$PIN, - UPDATE=$PIN, - ERASE=$PIN; - } - - # data objects are stored in transparent EFs. - EF data { - file-id = 3302; - structure = transparent; - ACL = *=NEVER, - READ=NONE, - UPDATE=$PIN, - ERASE=$PIN; - } - - # private data objects are stored in transparent EFs. - EF privdata { - file-id = 3402; - structure = transparent; - ACL = *=NEVER, - READ=$PIN, - UPDATE=$PIN, - ERASE=$PIN; - } - - } - - # This is needed when generating a key on-card. - EF tempfile { - file-id = 7EAD; - structure = linear-variable-tlv; - ACL = *=NONE; - size = 512; - } - } - } -} diff --git a/src/pkcs15init/isoApplet.profile b/src/pkcs15init/isoApplet.profile index a8c2f7a919..728c9c29b6 100644 --- a/src/pkcs15init/isoApplet.profile +++ b/src/pkcs15init/isoApplet.profile @@ -14,7 +14,7 @@ cardinfo { pkcs15 { # Method to calculate ID of the crypto objects - # mozilla: SHA1(modulus) for RSA, SHA1(pub) for DSA + # mozilla: SHA1(modulus) for RSA # rfc2459: SHA1(SequenceASN1 of public key components as ASN1 integers) # native: 'E' + number_of_present_objects_of_the_same_type # default value: 'native' diff --git a/src/pkcs15init/jcop.profile b/src/pkcs15init/jcop.profile deleted file mode 100644 index eb2ae9524d..0000000000 --- a/src/pkcs15init/jcop.profile +++ /dev/null @@ -1,69 +0,0 @@ -# -# PKCS15 r/w profile for JCOP cards -# -cardinfo { - max-pin-length = 16; - pin-encoding = ascii-numeric; - pin-pad-char = 0x00; -} - -filesystem { - DF MF { - DF PKCS15-AppDF { - acl = *=NONE, CREATE=CHV3; - EF PKCS15-AODF { - file-id = 502E; - } - EF PKCS15-PrKDF { - file-id = 502C; - } - EF PKCS15-PuKDF { - file-id = 502B; - } - EF PKCS15-CDF { - file-id = 502D; - } - EF PKCS15-DODF { - file-id = 502F; - } - template key-domain { - EF private-key { - file-id = 3000; - acl = *=NEVER, UPDATE=$PIN, CRYPTO=$PIN, - ERASE=$SOPIN; - } - EF extractable-key { - file-id = 3100; - acl = *=NEVER, READ=$PIN, UPDATE=$PIN, - ERASE=$SOPIN; - } - EF data { - file-id = 3200; - acl = *=NEVER, UPDATE=$PIN, READ=NONE, - ERASE=$SOPIN; - } - EF privdata { - file-id = 3500; - acl = *=NEVER, UPDATE=$PIN, READ=$PIN, - ERASE=$SOPIN; - } - EF public-key { - file-id = 3300; - acl = *=NEVER, UPDATE=$PIN, READ=NONE, - ERASE=$SOPIN; - } - EF certificate { - file-id = 3400; - acl = *=NEVER, UPDATE=$PIN, READ=NONE, - ERASE=$SOPIN; - } - } - EF temp-pubkey { - file-id = 0000; - acl = *=NEVER, UPDATE=$PIN, READ=NONE, - ERASE=$SOPIN; - } - } - } -} - diff --git a/src/pkcs15init/miocos.profile b/src/pkcs15init/miocos.profile deleted file mode 100644 index d42e35d046..0000000000 --- a/src/pkcs15init/miocos.profile +++ /dev/null @@ -1,53 +0,0 @@ -# -# PKCS15 r/w profile for MioCOS cards -# -cardinfo { - max-pin-length = 8; - pin-encoding = ascii-numeric; - pin-pad-char = 0x00; -} - -# Define reasonable limits for PINs and PUK -# Note that we do not set a file path or reference -# here; that is done dynamically. -PIN user-pin { - attempts = 3; -} -PIN user-puk { - attempts = 10; -} - -# Additional filesystem info. -# This is added to the file system info specified in the -# main profile. -filesystem { - DF MF { - DF PKCS15-AppDF { - EF template-private-key { - type = internal-ef; - file-id = 4B01; # This is the base FileID - size = 266; # 266 is enough for 1024-bit keys - ACL = *=NEVER, CRYPTO=$PIN, UPDATE=$PIN; - } - EF template-public-key { - file-id = 5501; - ACL = *=NEVER, READ=NONE, UPDATE=$PIN; - } - EF template-certificate { - file-id = 4301; - ACL = *=NEVER, READ=NONE, UPDATE=$PIN; - } - EF template-extractable-key { - file-id = 7000; - ACL = *=NEVER, READ=$PIN, UPDATE=$PIN; - } - } - } -} - -# Define an SO pin -# This PIN is not used yet. -#PIN sopin { -# file = sopinfile; -# reference = 0; -#} diff --git a/src/pkcs15init/myeid.profile b/src/pkcs15init/myeid.profile index fb2dc7c083..50ada35c02 100644 --- a/src/pkcs15init/myeid.profile +++ b/src/pkcs15init/myeid.profile @@ -94,6 +94,7 @@ filesystem { DF PKCS15-AppDF { type = DF; file-id = 5015; + aid = A0:00:00:00:63:50:4B:43:53:2D:31:35; acl = DELETE=$PIN, CREATE=$PIN; EF PKCS15-ODF { diff --git a/src/pkcs15init/oberthur.profile b/src/pkcs15init/oberthur.profile index 15888b2602..57baa779fe 100644 --- a/src/pkcs15init/oberthur.profile +++ b/src/pkcs15init/oberthur.profile @@ -42,7 +42,7 @@ PIN so-pin { reference = 4 } -# CHV5 used for Oberthur's specifique access condition "PIN or SOPIN" +# CHV5 used for Oberthur's specific access condition "PIN or SOPIN" # Any value for this pin can given, when the OpenSC tools are asking for. # Additional filesystem info. @@ -84,7 +84,7 @@ filesystem { file-id = 4000; type = internal-ef; size = 24; # 192 bits - # READ acl used insted of DECRYPT/ENCRYPT/CHECKSUM + # READ acl used instead of DECRYPT/ENCRYPT/CHECKSUM ACL = UPDATE=CHV1, READ=CHV1; } diff --git a/src/pkcs15init/pkcs15-asepcos.c b/src/pkcs15init/pkcs15-asepcos.c index 7904fb5af3..a905bfaa22 100644 --- a/src/pkcs15init/pkcs15-asepcos.c +++ b/src/pkcs15init/pkcs15-asepcos.c @@ -13,7 +13,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -80,7 +80,7 @@ static int asepcos_check_verify_tpin(sc_profile_t *profile, sc_pkcs15_card_t *p1 r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_CRYPTO); p15card->card->caps |= SC_CARD_CAP_USE_FCI_AC; sc_file_free(tfile); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "unable to authenticate for 'CRYPTO' operation"); + LOG_TEST_RET(ctx, r, "unable to authenticate for 'CRYPTO' operation"); } return SC_SUCCESS; } @@ -101,7 +101,7 @@ static int asepcos_erase(struct sc_profile *profile, sc_pkcs15_card_t *p15card) * - use EF(DIR) to get the DF of the OpenSC * pkcs15 application. */ - /* Check wether a transport exists and verify it if present */ + /* Check whether a transport exists and verify it if present */ p15card->opts.use_pin_cache = 1; r = asepcos_check_verify_tpin(profile, p15card); @@ -136,8 +136,8 @@ static int asepcos_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *tfile; sc_context_t *ctx = p15card->card->ctx; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - /* Check wether a transport exists and verify it if present */ + LOG_FUNC_CALLED(ctx); + /* Check whether a transport exists and verify it if present */ r = asepcos_check_verify_tpin(profile, p15card); if (r != SC_SUCCESS) return r; @@ -206,7 +206,7 @@ static int asepcos_pinid_to_akn(sc_card_t *card, int fileid, int *akn) if (r != SC_SUCCESS) return r; if (nfile->prop_attr == NULL || nfile->prop_attr_len != 11) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to determine AKN"); + sc_log(card->ctx, "unable to determine AKN"); sc_file_free(nfile); return SC_ERROR_INTERNAL; } @@ -221,12 +221,12 @@ static int asepcos_do_store_pin(sc_profile_t *profile, sc_card_t *card, { sc_file_t *nfile = NULL; u8 buf[64], sbuf[64], *p = buf, *q = sbuf; - int r, akn; + int r, akn = 0; - if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) + if (auth_info == NULL || auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; - /* outter tag */ + /* outer tag */ *p++ = 0x85; p++; /* as a file id for pin with use 0x00: */ @@ -234,7 +234,7 @@ static int asepcos_do_store_pin(sc_profile_t *profile, sc_card_t *card, *p++ = pinid & 0xff; /* pin length */ if (pinlen < 4 || pinlen > 16) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid PIN length"); + sc_log(card->ctx, "invalid PIN length"); return SC_ERROR_INVALID_ARGUMENTS; } *p++ = 0x00; @@ -296,7 +296,7 @@ static int asepcos_do_store_pin(sc_profile_t *profile, sc_card_t *card, r = sc_create_file(card, nfile); sc_file_free(nfile); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to create PIN file"); + sc_log(card->ctx, "unable to create PIN file"); return r; } /* get AKN of the newly created PIN */ @@ -318,7 +318,7 @@ static int asepcos_do_store_pin(sc_profile_t *profile, sc_card_t *card, */ static int have_onepin(sc_profile_t *profile) { - sc_pkcs15_auth_info_t sopin; + sc_pkcs15_auth_info_t sopin = {0}; sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &sopin); @@ -350,14 +350,14 @@ static int asepcos_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *tfile = NULL; sc_context_t *ctx = p15card->card->ctx; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); if (!pin || !pin_len) return SC_ERROR_INVALID_ARGUMENTS; - if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) + if (auth_info == NULL || auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; - pid = (auth_info->attrs.pin.reference & 0xff) | (((tpath.len >> 1) - 1) << 16); + pid = (auth_info->attrs.pin.reference & 0xff) | (int)(((tpath.len >> 1) - 1) << 16); /* get the ACL of the application DF */ r = sc_select_file(card, &df->path, &tfile); @@ -367,7 +367,7 @@ static int asepcos_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_CREATE); sc_file_free(tfile); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to create PIN file, insufficent rights"); + sc_log(card->ctx, "unable to create PIN file, insufficient rights"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } @@ -384,7 +384,7 @@ static int asepcos_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, if (r == SC_SUCCESS) pid += 2; else if (r != SC_ERROR_FILE_NOT_FOUND) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "error selecting PIN file"); + sc_log(card->ctx, "error selecting PIN file"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } } while (r != SC_ERROR_FILE_NOT_FOUND); @@ -393,7 +393,7 @@ static int asepcos_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, /* Create PUK (if specified). Note: we need to create the PUK * the PIN as the PUK fileid is used in the PIN acl. */ - struct sc_pkcs15_auth_info puk_ainfo; + struct sc_pkcs15_auth_info puk_ainfo = {0}; if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PUK, &puk_ainfo); @@ -422,7 +422,7 @@ static int asepcos_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, * first USER PIN has been set we can tighten the ACLs of * the application DF. */ - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "finalizing application DF"); + sc_log(card->ctx, "finalizing application DF"); r = sc_select_file(card, &df->path, NULL); if (r != SC_SUCCESS) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); @@ -432,7 +432,7 @@ static int asepcos_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); r = sc_card_ctl(card, SC_CARDCTL_ASEPCOS_SET_SATTR, df); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to change the security attributes"); + sc_log(card->ctx, "unable to change the security attributes"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } /* finally activate the application DF (fix ACLs) */ @@ -445,7 +445,7 @@ static int asepcos_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, st.is_ef = 0; r = sc_card_ctl(card, SC_CARDCTL_ASEPCOS_ACTIVATE_FILE, &st); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to activate DF"); + sc_log(card->ctx, "unable to activate DF"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } } @@ -477,14 +477,14 @@ static int asepcos_do_authenticate(sc_profile_t *profile, sc_pkcs15_card_t *p15c sc_file_t *prkey = NULL; r = sc_profile_get_file_by_path(profile, path, &prkey); if (r != SC_SUCCESS) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "unable to find file in profile"); + sc_log(p15card->card->ctx, "unable to find file in profile"); return r; } r = sc_pkcs15init_authenticate(profile, p15card, prkey, op); sc_file_free(prkey); if (r != SC_SUCCESS) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "unable to authenticate"); + sc_log(p15card->card->ctx, "unable to authenticate"); return r; } return SC_SUCCESS; @@ -545,7 +545,7 @@ static int asepcos_do_create_key(sc_card_t *card, size_t ksize, int fileid, nfile->id = fileid & 0xffff; r = sc_file_set_prop_attr(nfile, buf, p - buf); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to set key prop. attributes"); + sc_log(card->ctx, "unable to set key prop. attributes"); sc_file_free(nfile); return r; } @@ -553,7 +553,7 @@ static int asepcos_do_create_key(sc_card_t *card, size_t ksize, int fileid, r = sc_create_file(card, nfile); sc_file_free(nfile); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to create key file"); + sc_log(card->ctx, "unable to create key file"); return r; } return r; @@ -565,7 +565,8 @@ static int asepcos_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) { sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *) obj->data; - int r, len; + int r; + size_t len; u8 buf[512], *p = buf; size_t blen = kinfo->modulus_length / 8; int afileid = -1, @@ -580,7 +581,7 @@ static int asepcos_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, r = sc_pkcs15_find_pin_by_auth_id(p15card, &obj->auth_id, &pin); if (r != SC_SUCCESS) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "unable to determine reference for the PIN"); + sc_log(p15card->card->ctx, "unable to determine reference for the PIN"); return r; } @@ -589,7 +590,7 @@ static int asepcos_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, st.akn = auth_info->attrs.pin.reference; r = sc_card_ctl(p15card->card, SC_CARDCTL_ASEPCOS_AKN2FILEID, &st); if (r != SC_SUCCESS) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "unable to determine file id of the PIN"); + sc_log(p15card->card->ctx, "unable to determine file id of the PIN"); return r; } afileid = st.fileid; @@ -640,7 +641,7 @@ static int asepcos_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, r = asepcos_do_create_key(p15card->card, kinfo->modulus_length, fileid, buf, p - buf); if (r != SC_SUCCESS) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "unable to create private key file"); + sc_log(p15card->card->ctx, "unable to create private key file"); return r; } @@ -654,7 +655,8 @@ static int asepcos_do_store_rsa_key(sc_pkcs15_card_t *p15card, sc_profile_t *pro sc_pkcs15_object_t *obj, sc_pkcs15_prkey_info_t *kinfo, struct sc_pkcs15_prkey_rsa *key) { - int r, klen; + int r; + size_t klen; u8 buf[512], *p = buf; sc_path_t tpath; sc_cardctl_asepcos_change_key_t ckdata; @@ -674,7 +676,7 @@ static int asepcos_do_store_rsa_key(sc_pkcs15_card_t *p15card, sc_profile_t *pro tpath.value[1] = kinfo->path.value[kinfo->path.len-1]; r = sc_select_file(p15card->card, &tpath, NULL); if (r != SC_SUCCESS) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "unable to select rsa key file"); + sc_log(p15card->card->ctx, "unable to select rsa key file"); return r; } @@ -705,7 +707,7 @@ static int asepcos_do_store_rsa_key(sc_pkcs15_card_t *p15card, sc_profile_t *pro r = sc_card_ctl(p15card->card, SC_CARDCTL_ASEPCOS_CHANGE_KEY, &ckdata); if (r != SC_SUCCESS) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "unable to change key data"); + sc_log(p15card->card->ctx, "unable to change key data"); return r; } @@ -725,7 +727,7 @@ static int asepcos_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *) obj->data; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "only RSA is currently supported"); + sc_log(p15card->card->ctx, "only RSA is currently supported"); return SC_ERROR_NOT_SUPPORTED; } @@ -763,7 +765,7 @@ static int asepcos_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card tpath.value[1] = kinfo->path.value[kinfo->path.len-1]; r = sc_select_file(card, &tpath, NULL); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to select rsa key file"); + sc_log(card->ctx, "unable to select rsa key file"); return r; } @@ -780,9 +782,9 @@ static int asepcos_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card apdu.resp = rbuf; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "error creating key"); + sc_log(card->ctx, "error creating key"); return SC_ERROR_INTERNAL; } diff --git a/src/pkcs15init/pkcs15-authentic.c b/src/pkcs15init/pkcs15-authentic.c index ccc22c84b0..d480c0590c 100644 --- a/src/pkcs15init/pkcs15-authentic.c +++ b/src/pkcs15init/pkcs15-authentic.c @@ -18,7 +18,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -34,16 +34,6 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" @@ -179,8 +169,10 @@ authentic_pkcs15_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p rv = sc_pkcs15_get_objects(p15card, obj_type, objs, 32); LOG_TEST_RET(ctx, rv, "Failed to get PKCS#15 objects to remove"); - for (ii=0; iicard, &df->path, &file); @@ -191,13 +183,16 @@ authentic_pkcs15_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p rv = sc_erase_binary(p15card->card, 0, file->size, 0); if (rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); + if (rv < 0) + sc_file_free(file); + LOG_TEST_RET(ctx, rv, "'UPDATE' authentication failed"); rv = sc_erase_binary(p15card->card, 0, file->size, 0); } - LOG_TEST_RET(ctx, rv, "Binary erase error"); sc_file_free(file); + LOG_TEST_RET(ctx, rv, "Binary erase error"); profile->dirty = 1; } @@ -355,7 +350,6 @@ authentic_sdo_allocate_prvkey(struct sc_profile *profile, struct sc_card *card, sc_file_free(file); LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate 'sc_authentic_sdo'"); } - *out = sdo; sdo->magic = AUTHENTIC_SDO_MAGIC; sdo->docp.id = key_info->key_reference & ~AUTHENTIC_OBJECT_REF_FLAG_LOCAL; @@ -364,11 +358,16 @@ authentic_sdo_allocate_prvkey(struct sc_profile *profile, struct sc_card *card, rv = authentic_docp_set_acls(card, file, authentic_v3_rsa_ac_ops, sizeof(authentic_v3_rsa_ac_ops)/sizeof(authentic_v3_rsa_ac_ops[0]), &sdo->docp); sc_file_free(file); - LOG_TEST_RET(ctx, rv, "Cannot set key ACLs from file"); + if (rv != SC_SUCCESS) { + free(sdo); + sc_log(ctx, "Cannot set key ACLs from file"); + LOG_FUNC_RETURN(ctx, rv); + } sc_log(ctx, "sdo(mech:%X,id:%X,acls:%s)", sdo->docp.mech, sdo->docp.id, sc_dump_hex(sdo->docp.acl_data, sdo->docp.acl_data_len)); + *out = sdo; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -539,19 +538,26 @@ authentic_pkcs15_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p key_info->key_reference |= AUTHENTIC_OBJECT_REF_FLAG_LOCAL; rv = sc_select_file(card, &file_p_prvkey->path, &parent); + if (rv != SC_SUCCESS) + sc_file_free(file_p_prvkey); LOG_TEST_RET(ctx, rv, "DF for the private objects not defined"); rv = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_CRYPTO); - LOG_TEST_RET(ctx, rv, "SC_AC_OP_CRYPTO authentication failed for parent DF"); - sc_file_free(parent); + if (rv != SC_SUCCESS) + sc_file_free(file_p_prvkey); + LOG_TEST_RET(ctx, rv, "SC_AC_OP_CRYPTO authentication failed for parent DF"); key_info->access_flags = SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE | SC_PKCS15_PRKEY_ACCESS_SENSITIVE; rv = authentic_sdo_allocate_prvkey(profile, card, key_info, &sdo); - LOG_TEST_RET(ctx, rv, "IasEcc: init SDO private key failed"); + if (rv != SC_SUCCESS || sdo == NULL) { + sc_log(ctx, "IasEcc: init SDO private key failed"); + sc_file_free(file_p_prvkey); + LOG_FUNC_RETURN(ctx, rv); + } rv = sc_card_ctl(card, SC_CARDCTL_AUTHENTIC_SDO_CREATE, sdo); if (rv == SC_ERROR_FILE_ALREADY_EXISTS) { @@ -560,20 +566,20 @@ authentic_pkcs15_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p p15card->card->caps &= ~SC_CARD_CAP_USE_FCI_AC; rv = sc_pkcs15init_authenticate(profile, p15card, file_p_prvkey, SC_AC_OP_DELETE); p15card->card->caps = caps; - LOG_TEST_RET(ctx, rv, "SC_AC_OP_CRYPTO authentication failed for parent DF"); + LOG_TEST_GOTO_ERR(ctx, rv, "SC_AC_OP_CRYPTO authentication failed for parent DF"); rv = sc_card_ctl(card, SC_CARDCTL_AUTHENTIC_SDO_DELETE, sdo); - LOG_TEST_RET(ctx, rv, "SC_CARDCTL_AUTHENTIC_SDO_DELETE failed for private key"); + LOG_TEST_GOTO_ERR(ctx, rv, "SC_CARDCTL_AUTHENTIC_SDO_DELETE failed for private key"); rv = sc_card_ctl(card, SC_CARDCTL_AUTHENTIC_SDO_CREATE, sdo); } - LOG_TEST_RET(ctx, rv, "SC_CARDCTL_AUTHENTIC_SDO_CREATE failed"); + LOG_TEST_GOTO_ERR(ctx, rv, "SC_CARDCTL_AUTHENTIC_SDO_CREATE failed"); rv = authentic_pkcs15_fix_access(p15card, file_p_prvkey, object); - LOG_TEST_RET(ctx, rv, "cannot fix access rules for private key"); + LOG_TEST_GOTO_ERR(ctx, rv, "cannot fix access rules for private key"); rv = authentic_pkcs15_fix_usage(p15card, object); - LOG_TEST_RET(ctx, rv, "cannot fix access rules for private key"); + LOG_TEST_GOTO_ERR(ctx, rv, "cannot fix access rules for private key"); /* Here fix the key's supported algorithms, if these ones will be implemented * (see src/libopensc/pkcs15-prkey.c). @@ -583,8 +589,13 @@ authentic_pkcs15_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p sc_log(ctx, "sdo->file:%p", sdo->file); rv = sc_pkcs15_allocate_object_content(ctx, object, (unsigned char *)sdo, sizeof(struct sc_authentic_sdo)); - LOG_TEST_RET(ctx, rv, "Failed to allocate PrvKey SDO as object content"); + LOG_TEST_GOTO_ERR(ctx, rv, "Failed to allocate PrvKey SDO as object content"); +err: + if (sdo == NULL || sdo->file != file_p_prvkey) + sc_file_free(file_p_prvkey); + authentic_free_sdo_data(sdo); + free(sdo); LOG_FUNC_RETURN(ctx, rv); } @@ -636,7 +647,7 @@ authentic_pkcs15_generate_key(struct sc_profile *profile, sc_pkcs15_card_t *p15c LOG_TEST_RET(ctx, rv, "generate key failed"); pubkey->algorithm = SC_ALGORITHM_RSA; - //FIXME: allocate/copy/free to reduce memory likage + //FIXME: allocate/copy/free to reduce memory leakage pubkey->u.rsa.modulus = sdo->data.prvkey->u.rsa.modulus; pubkey->u.rsa.exponent = sdo->data.prvkey->u.rsa.exponent; sdo->data.prvkey = NULL; @@ -843,19 +854,22 @@ authentic_emu_update_tokeninfo(struct sc_profile *profile, struct sc_pkcs15_card struct sc_file *file = NULL; struct sc_path path; unsigned char buffer[8]; - int rv,len; + int rv; + size_t len; - sc_format_path(AUTHENTIC_CACHE_TIMESTAMP_PATH, &path); - rv = sc_select_file(p15card->card, &path, &file); - if (!rv) { + sc_format_path(AUTHENTIC_CACHE_TIMESTAMP_PATH, &path); + rv = sc_select_file(p15card->card, &path, &file); + if (!rv) { rv = sc_get_challenge(p15card->card, buffer, sizeof(buffer)); - LOG_TEST_RET(ctx, rv, "Get challenge error"); + if (rv < 0) { + sc_file_free(file); + LOG_TEST_RET(ctx, rv, "Get challenge error"); + } len = file->size > sizeof(buffer) ? sizeof(buffer) : file->size; - rv = sc_update_binary(p15card->card, 0, buffer, len, 0); - LOG_TEST_RET(ctx, rv, "Get challenge error"); - + rv = sc_update_binary(p15card->card, 0, buffer, len, 0); sc_file_free(file); + LOG_TEST_RET(ctx, rv, "Update binary error"); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); diff --git a/src/pkcs15init/pkcs15-cardos.c b/src/pkcs15init/pkcs15-cardos.c index 1fd5e31cc2..c69a25f130 100644 --- a/src/pkcs15init/pkcs15-cardos.c +++ b/src/pkcs15init/pkcs15-cardos.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -91,19 +91,23 @@ static void tlv_init(struct tlv *tlv, u8 *base, size_t size) tlv->current = tlv->next = base; } -static void tlv_next(struct tlv *tlv, u8 tag) +static int tlv_next(struct tlv *tlv, u8 tag) { - assert(tlv->next + 2 < tlv->end); + if (tlv->next + 2 >= tlv->end) + return SC_ERROR_INTERNAL; tlv->current = tlv->next; *(tlv->next++) = tag; *(tlv->next++) = 0; + return SC_SUCCESS; } -static void tlv_add(struct tlv *tlv, u8 val) +static int tlv_add(struct tlv *tlv, u8 val) { - assert(tlv->next + 1 < tlv->end); + if (tlv->next + 1 >= tlv->end) + return SC_ERROR_INTERNAL; *(tlv->next++) = val; tlv->current[1]++; + return SC_SUCCESS; } static size_t @@ -139,7 +143,7 @@ cardos_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *d return r; /* Create a default security environment for this DF. - * This SE autometically becomes the current SE when the + * This SE automatically becomes the current SE when the * DF is selected. */ if ((r = cardos_create_sec_env(profile, p15card->card, 0x01, 0x00)) < 0) return r; @@ -201,12 +205,12 @@ cardos_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *d if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; - r = sc_select_file(card, &df->path, NULL); + r = sc_select_file(card, auth_info->attrs.pin.reference & 0x80 ? &df->path : sc_get_mf_path(), NULL); if (r < 0) return r; if (puk && puk_len) { - struct sc_pkcs15_auth_info puk_ainfo; + struct sc_pkcs15_auth_info puk_ainfo = {0}; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &puk_ainfo); @@ -263,26 +267,26 @@ cardos_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, int algorithm = 0, r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "CardOS supports RSA keys only."); + sc_log(ctx, "CardOS supports RSA keys only."); return SC_ERROR_NOT_SUPPORTED; } if (cardos_key_algorithm(key_info->usage, key_info->modulus_length, &algorithm) < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "CardOS does not support keys " + sc_log(ctx, "CardOS does not support keys " "that can both sign _and_ decrypt."); return SC_ERROR_NOT_SUPPORTED; } r = sc_select_file(p15card->card, &key_info->path, &file); if (r) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Failed to store key: cannot select parent DF"); + sc_log(ctx, "Failed to store key: cannot select parent DF"); return r; } r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); sc_file_free(file); if (r) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Failed to store key: 'UPDATE' authentication failed"); + sc_log(ctx, "Failed to store key: 'UPDATE' authentication failed"); return r; } @@ -337,7 +341,7 @@ cardos_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, rsa_max_size = (sc_card_find_rsa_alg(p15card->card, 2048) != NULL) ? 2048 : 1024; keybits = key_info->modulus_length & ~7UL; if (keybits > rsa_max_size) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Unable to generate key, max size is %lu", + sc_log(ctx, "Unable to generate key, max size is %lu", (unsigned long) rsa_max_size); return SC_ERROR_INVALID_ARGUMENTS; } @@ -346,13 +350,13 @@ cardos_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, use_ext_rsa = 1; if (cardos_key_algorithm(key_info->usage, keybits, &algorithm) < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "CardOS does not support keys " + sc_log(ctx, "CardOS does not support keys " "that can both sign _and_ decrypt."); return SC_ERROR_NOT_SUPPORTED; } if (sc_profile_get_file(profile, "tempfile", &temp) < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Profile doesn't define temporary file " + sc_log(ctx, "Profile doesn't define temporary file " "for key generation."); return SC_ERROR_NOT_SUPPORTED; } @@ -470,7 +474,8 @@ cardos_store_pin(sc_profile_t *profile, sc_card_t *card, unsigned char buffer[256]; unsigned char pinpadded[256]; struct tlv tlv; - unsigned int attempts, minlen, maxlen; + unsigned int attempts, maxlen; + u8 minlen; int r, hasverifyrc; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) @@ -481,7 +486,7 @@ cardos_store_pin(sc_profile_t *profile, sc_card_t *card, * "no padding required". */ maxlen = MIN(profile->pin_maxlen, sizeof(pinpadded)); if (pin_len > maxlen) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "invalid pin length: %"SC_FORMAT_LEN_SIZE_T"u (max %u)\n", pin_len, maxlen); return SC_ERROR_INVALID_ARGUMENTS; @@ -492,54 +497,65 @@ cardos_store_pin(sc_profile_t *profile, sc_card_t *card, pin = pinpadded; attempts = auth_info->tries_left; - minlen = auth_info->attrs.pin.min_length; + minlen = (u8)auth_info->attrs.pin.min_length; tlv_init(&tlv, buffer, sizeof(buffer)); /* object address: class, id */ - tlv_next(&tlv, 0x83); - tlv_add(&tlv, 0x00); /* class byte: usage TEST, k=0 */ - tlv_add(&tlv, auth_info->attrs.pin.reference); + if (tlv_next(&tlv, 0x83) != SC_SUCCESS + || tlv_add(&tlv, 0x00) != SC_SUCCESS /* class byte: usage TEST, k=0 */ + || tlv_add(&tlv, auth_info->attrs.pin.reference & 0x7f) != SC_SUCCESS) + return SC_ERROR_INTERNAL; /* parameters */ - tlv_next(&tlv, 0x85); - tlv_add(&tlv, 0x02); /* options byte */ + if (tlv_next(&tlv, 0x85) + || tlv_add(&tlv, 0x02) /* options byte */) + return SC_ERROR_INTERNAL; + hasverifyrc = cardos_have_verifyrc_package(card); if (hasverifyrc == 1) /* Use 9 byte OCI parameters to be able to set VerifyRC bit */ - tlv_add(&tlv, 0x04); /* options_2 byte with bit 2 set to return CurrentErrorCounter */ - tlv_add(&tlv, attempts & 0xf); /* flags byte */ - tlv_add(&tlv, CARDOS_ALGO_PIN); /* algorithm = pin-test */ - tlv_add(&tlv, attempts & 0xf); /* errcount = attempts */ + if (tlv_add(&tlv, 0x04) != SC_SUCCESS /* options_2 byte with bit 2 set to return CurrentErrorCounter */) + return SC_ERROR_INTERNAL; + + if (tlv_add(&tlv, attempts & 0xf) != SC_SUCCESS /* flags byte */ + || tlv_add(&tlv, CARDOS_ALGO_PIN) != SC_SUCCESS /* algorithm = pin-test */ + || tlv_add(&tlv, attempts & 0xf) != SC_SUCCESS /* errcount = attempts */) + return SC_ERROR_INTERNAL; /* usecount: not documented, but seems to work like this: * - value of 0xff means pin can be presented any number * of times * - anything less: max # of times before BS object is blocked. */ - tlv_add(&tlv, 0xff); + if (tlv_add(&tlv, 0xff) != SC_SUCCESS) + return SC_ERROR_INTERNAL; /* DEK: not documented, no idea what it means */ - tlv_add(&tlv, 0xff); + if (tlv_add(&tlv, 0xff) != SC_SUCCESS) + return SC_ERROR_INTERNAL; /* ARA counter: number of times the test object can be used before * another verification is required (~ user consent) * (0x00 unlimited usage) */ - tlv_add(&tlv, 0x00); - - tlv_add(&tlv, minlen); /* minlen */ + if (tlv_add(&tlv, 0x00) != SC_SUCCESS + || tlv_add(&tlv, minlen) != SC_SUCCESS /* minlen */) + return SC_ERROR_INTERNAL; /* AC conditions */ - tlv_next(&tlv, 0x86); - tlv_add(&tlv, 0x00); /* use: always */ - tlv_add(&tlv, auth_info->attrs.pin.reference); /* change: PIN */ - tlv_add(&tlv, puk_id); /* unblock: PUK */ + if (tlv_next(&tlv, 0x86) != SC_SUCCESS + || tlv_add(&tlv, 0x00) != SC_SUCCESS /* use: always */ + || tlv_add(&tlv, auth_info->attrs.pin.reference) != SC_SUCCESS /* change: PIN */ + || tlv_add(&tlv, puk_id) != SC_SUCCESS /* unblock: PUK */) + return SC_ERROR_INTERNAL; /* data: PIN */ - tlv_next(&tlv, 0x8f); + if (tlv_next(&tlv, 0x8f) != SC_SUCCESS) + return SC_ERROR_INTERNAL; while (pin_len--) - tlv_add(&tlv, *pin++); + if (tlv_add(&tlv, *pin++) != SC_SUCCESS) + return SC_ERROR_INTERNAL; args.data = buffer; args.len = tlv_len(&tlv); @@ -565,20 +581,19 @@ cardos_create_sec_env(struct sc_profile *profile, sc_card_t *card, int r; tlv_init(&tlv, buffer, sizeof(buffer)); - tlv_next(&tlv, 0x83); - tlv_add(&tlv, se_id); - - tlv_next(&tlv, 0x86); - tlv_add(&tlv, 0); - tlv_add(&tlv, 0); - - tlv_next(&tlv, 0x8f); - tlv_add(&tlv, key_id); - tlv_add(&tlv, key_id); - tlv_add(&tlv, key_id); - tlv_add(&tlv, key_id); - tlv_add(&tlv, key_id); - tlv_add(&tlv, key_id); + if (tlv_next(&tlv, 0x83) != SC_SUCCESS + || tlv_add(&tlv, se_id) != SC_SUCCESS + || tlv_next(&tlv, 0x86) != SC_SUCCESS + || tlv_add(&tlv, 0) != SC_SUCCESS + || tlv_add(&tlv, 0) != SC_SUCCESS + || tlv_next(&tlv, 0x8f) != SC_SUCCESS + || tlv_add(&tlv, key_id) != SC_SUCCESS + || tlv_add(&tlv, key_id) != SC_SUCCESS + || tlv_add(&tlv, key_id) != SC_SUCCESS + || tlv_add(&tlv, key_id) != SC_SUCCESS + || tlv_add(&tlv, key_id) != SC_SUCCESS + || tlv_add(&tlv, key_id) != SC_SUCCESS) + return SC_ERROR_INTERNAL; args.data = buffer; args.len = tlv_len(&tlv); @@ -646,47 +661,55 @@ cardos_store_key_component(sc_card_t *card, tlv_init(&tlv, buffer, sizeof(buffer)); /* Object address */ - tlv_next(&tlv, 0x83); - tlv_add(&tlv, 0x20|num); /* PSO, n-th component */ - tlv_add(&tlv, key_id); + if (tlv_next(&tlv, 0x83) != SC_SUCCESS + || tlv_add(&tlv, 0x20|num) != SC_SUCCESS /* PSO, n-th component */ + || tlv_add(&tlv, key_id) != SC_SUCCESS) + return SC_ERROR_INTERNAL; /* Object parameters */ - tlv_next(&tlv, 0x85); - tlv_add(&tlv, CARDOS_KEY_OPTIONS|(last? 0x00 : 0x20)); - tlv_add(&tlv, CARDOS_KEY_FLAGS); - tlv_add(&tlv, algorithm); - tlv_add(&tlv, 0x00); - tlv_add(&tlv, 0xFF); /* use count */ - tlv_add(&tlv, 0xFF); /* DEK (whatever this is) */ - tlv_add(&tlv, 0x00); - tlv_add(&tlv, 0x00); + if (tlv_next(&tlv, 0x85) != SC_SUCCESS + || tlv_add(&tlv, CARDOS_KEY_OPTIONS|(last? 0x00 : 0x20)) != SC_SUCCESS + || tlv_add(&tlv, CARDOS_KEY_FLAGS) != SC_SUCCESS + || tlv_add(&tlv, algorithm) != SC_SUCCESS + || tlv_add(&tlv, 0x00) != SC_SUCCESS + || tlv_add(&tlv, 0xFF) != SC_SUCCESS /* use count */ + || tlv_add(&tlv, 0xFF) != SC_SUCCESS /* DEK (whatever this is) */ + || tlv_add(&tlv, 0x00) != SC_SUCCESS + || tlv_add(&tlv, 0x00) != SC_SUCCESS) + return SC_ERROR_INTERNAL; /* AC bytes */ - tlv_next(&tlv, 0x86); - tlv_add(&tlv, pin_id); /* AC USE */ - tlv_add(&tlv, pin_id); /* AC CHANGE */ - tlv_add(&tlv, pin_id); /* UNKNOWN */ - tlv_add(&tlv, 0); /* rfu */ - tlv_add(&tlv, 0); /* rfu */ - tlv_add(&tlv, 0); /* rfu */ - tlv_add(&tlv, 0); + if (tlv_next(&tlv, 0x86) != SC_SUCCESS + || tlv_add(&tlv, pin_id) != SC_SUCCESS /* AC USE */ + || tlv_add(&tlv, pin_id) != SC_SUCCESS /* AC CHANGE */ + || tlv_add(&tlv, pin_id) != SC_SUCCESS /* UNKNOWN */ + || tlv_add(&tlv, 0) != SC_SUCCESS /* rfu */ + || tlv_add(&tlv, 0) != SC_SUCCESS /* rfu */ + || tlv_add(&tlv, 0) != SC_SUCCESS /* rfu */ + || tlv_add(&tlv, 0) != SC_SUCCESS) + return SC_ERROR_INTERNAL; #ifdef SET_SM_BYTES /* it shouldn't be necessary to set the default value */ /* SM bytes */ - tlv_next(&tlv, 0x8B); + if (tlv_next(&tlv, 0x8B) != SC_SUCCESS) + return SC_ERROR_INTERNAL; for (n = 0; n < 16; n++) - tlv_add(&tlv, 0xFF); + if (tlv_add(&tlv, 0xFF) != SC_SUCCESS) + return SC_ERROR_INTERNAL; #endif /* key component */ - tlv_next(&tlv, 0x8f); + if (tlv_next(&tlv, 0x8f) != SC_SUCCESS) + return SC_ERROR_INTERNAL; if (use_prefix != 0) { - tlv_add(&tlv, len+1); - tlv_add(&tlv, 0); + if (tlv_add(&tlv, len+1) != SC_SUCCESS + || tlv_add(&tlv, 0) != SC_SUCCESS) + return SC_ERROR_INTERNAL; } while (len--) - tlv_add(&tlv, *data++); + if (tlv_add(&tlv, *data++) != SC_SUCCESS) + return SC_ERROR_INTERNAL; args.data = buffer; args.len = tlv_len(&tlv); @@ -761,13 +784,13 @@ static int parse_ext_pubkey_file(sc_card_t *card, const u8 *data, size_t len, return SC_ERROR_INVALID_ARGUMENTS; data = sc_asn1_find_tag(card->ctx, data, len, 0x7f49, &ilen); if (data == NULL) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid public key data: missing tag"); + sc_log(card->ctx, "invalid public key data: missing tag"); return SC_ERROR_INTERNAL; } p = sc_asn1_find_tag(card->ctx, data, ilen, 0x81, &tlen); if (p == NULL) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid public key data: missing modulus"); + sc_log(card->ctx, "invalid public key data: missing modulus"); return SC_ERROR_INTERNAL; } pubkey->u.rsa.modulus.len = tlen; @@ -778,7 +801,7 @@ static int parse_ext_pubkey_file(sc_card_t *card, const u8 *data, size_t len, p = sc_asn1_find_tag(card->ctx, data, ilen, 0x82, &tlen); if (p == NULL) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid public key data: missing exponent"); + sc_log(card->ctx, "invalid public key data: missing exponent"); return SC_ERROR_INTERNAL; } pubkey->u.rsa.exponent.len = tlen; @@ -797,7 +820,7 @@ do_cardos_extract_pubkey(sc_card_t *card, int nr, u8 tag, u8 buf[256]; int r, count; - r = sc_read_record(card, nr, buf, sizeof(buf), SC_RECORD_BY_REC_NR); + r = sc_read_record(card, nr, 0, buf, sizeof(buf), SC_RECORD_BY_REC_NR); if (r < 0) return r; count = r - 4; @@ -850,7 +873,7 @@ static int cardos_have_verifyrc_package(sc_card_t *card) sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; - const u8 *p = rbuf, *q; + const u8 *p = rbuf, *q, *pp; size_t len, tlen = 0, ilen = 0; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x88); @@ -859,20 +882,20 @@ static int cardos_have_verifyrc_package(sc_card_t *card) apdu.lc = 0; apdu.le = 256; r = sc_transmit_apdu(card, &apdu); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if ((len = apdu.resplen) == 0) /* looks like no package has been installed */ return 0; while (len != 0) { - p = sc_asn1_find_tag(card->ctx, p, len, 0xe1, &tlen); - if (p == NULL) + pp = sc_asn1_find_tag(card->ctx, p, len, 0xe1, &tlen); + if (pp == NULL) return 0; if (card->type == SC_CARD_TYPE_CARDOS_M4_3) { /* the verifyRC package on CardOS 4.3B use Manufacturer ID 0x01 */ /* and Package Number 0x07 */ - q = sc_asn1_find_tag(card->ctx, p, tlen, 0x01, &ilen); + q = sc_asn1_find_tag(card->ctx, pp, tlen, 0x01, &ilen); if (q == NULL || ilen != 4) return 0; if (q[0] == 0x07) @@ -880,7 +903,7 @@ static int cardos_have_verifyrc_package(sc_card_t *card) } else if (card->type == SC_CARD_TYPE_CARDOS_M4_4) { /* the verifyRC package on CardOS 4.4 use Manufacturer ID 0x03 */ /* and Package Number 0x02 */ - q = sc_asn1_find_tag(card->ctx, p, tlen, 0x03, &ilen); + q = sc_asn1_find_tag(card->ctx, pp, tlen, 0x03, &ilen); if (q == NULL || ilen != 4) return 0; if (q[0] == 0x02) diff --git a/src/pkcs15init/pkcs15-cflex.c b/src/pkcs15init/pkcs15-cflex.c index 4117e146d0..03b51c4a1b 100644 --- a/src/pkcs15init/pkcs15-cflex.c +++ b/src/pkcs15init/pkcs15-cflex.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -56,6 +56,9 @@ cflex_delete_file(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *d int r = 0; /* Select the parent DF */ path = df->path; + if (path.len < 2) { + return SC_ERROR_INVALID_ARGUMENTS; + } path.len -= 2; r = sc_select_file(p15card->card, &path, &parent); if (r < 0) @@ -86,7 +89,7 @@ static int cflex_erase_card(struct sc_profile *profile, sc_pkcs15_card_t *p15car sc_file_t *df = profile->df_info->file, *dir, *userpinfile = NULL; int r; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); /* Delete EF(DIR). This may not be very nice * against other applications that use this file, but * extremely useful for testing :) @@ -225,7 +228,7 @@ cflex_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df int ndummies, pin_type, puk_type, r; sc_file_t *file = NULL; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; @@ -246,11 +249,11 @@ cflex_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df /* Get file definition from the profile */ if (sc_profile_get_file(profile, (pin_attrs->reference == 1)? "CHV1" : "CHV2", &file) < 0 && sc_profile_get_file(profile, "CHV", &file) < 0) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND, "profile does not define pin file ACLs"); + LOG_TEST_RET(ctx, SC_ERROR_FILE_NOT_FOUND, "profile does not define pin file ACLs"); ndummies = cflex_create_dummy_chvs(profile, p15card, file, SC_AC_OP_CREATE, dummies); sc_file_free(file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, ndummies, "Unable to create dummy CHV file"); + LOG_TEST_RET(ctx, ndummies, "Unable to create dummy CHV file"); r = cflex_create_pin_file(profile, p15card, &df->path, pin_attrs->reference, pin, pin_len, sc_profile_get_pin_retries(profile, pin_type), @@ -273,7 +276,7 @@ cflex_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_obj int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Cryptoflex supports only RSA keys."); + sc_log(p15card->card->ctx, "Cryptoflex supports only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } @@ -289,16 +292,16 @@ cflex_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_obj case 1024: size = 326; break; case 2048: size = 646; break; default: - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(p15card->card->ctx, "Unsupported key size %"SC_FORMAT_LEN_SIZE_T"u\n", key_info->modulus_length); r = SC_ERROR_INVALID_ARGUMENTS; goto out; } - if (prkf->size < size) + if (prkf && prkf->size < size) prkf->size = size; - if (pukf->size < size + 4) + if (pukf && pukf->size < size + 4) pukf->size = size + 4; /* Now create the files */ @@ -325,13 +328,13 @@ cflex_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, struct sc_cardctl_cryptoflex_genkey_info args; sc_card_t *card = p15card->card; sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; - unsigned int keybits; + size_t keybits; unsigned char raw_pubkey[256]; sc_file_t *prkf = NULL, *pukf = NULL; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Cryptoflex supports only RSA keys."); + sc_log(card->ctx, "Cryptoflex supports only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } /* Get the public and private key file */ @@ -385,13 +388,13 @@ cflex_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, { sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; sc_card_t *card = p15card->card; - sc_file_t *prkf, *pukf; + sc_file_t *prkf = NULL, *pukf = NULL; unsigned char keybuf[1024]; size_t size; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Cryptoflex supports only RSA keys."); + sc_log(card->ctx, "Cryptoflex supports only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } /* Get the public and private key file */ @@ -407,7 +410,7 @@ cflex_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, if (r < 0) goto out; - r = sc_pkcs15init_update_file(profile, p15card, prkf, keybuf, size); + r = sc_pkcs15init_update_file(profile, p15card, prkf, keybuf, (unsigned)size); if (r < 0) goto out; @@ -419,7 +422,7 @@ cflex_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, if (r < 0) goto out; - r = sc_pkcs15init_update_file(profile, p15card, pukf, keybuf, size); + r = sc_pkcs15init_update_file(profile, p15card, pukf, keybuf, (unsigned)size); out: sc_file_free(prkf); sc_file_free(pukf); @@ -439,7 +442,7 @@ cflex_create_dummy_chvs(sc_profile_t *profile, sc_pkcs15_card_t *p15card, const sc_acl_entry_t *acl; int r = 0, ndummies = 0; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); /* See if the DF is supposed to be PIN protected, and if * it is, whether that CHV file actually exists. If it doesn't, * create it. @@ -530,12 +533,15 @@ cflex_create_pin_file(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *dummies[2], *file; int r, ndummies; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); if (file_ret) *file_ret = NULL; /* Build the CHV path */ path = *df_path; + if (path.len > SC_MAX_PATH_SIZE - 2) { + return SC_ERROR_INVALID_ARGUMENTS; + } path.value[path.len++] = ref - 1; path.value[path.len++] = 0; @@ -548,7 +554,7 @@ cflex_create_pin_file(sc_profile_t *profile, sc_pkcs15_card_t *p15card, if (sc_profile_get_file_by_path(profile, &path, &file) < 0 && sc_profile_get_file(profile, (ref == 1)? "CHV1" : "CHV2", &file) < 0 && sc_profile_get_file(profile, "CHV", &file) < 0) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND, "profile does not define pin file ACLs"); + LOG_TEST_RET(ctx, SC_ERROR_FILE_NOT_FOUND, "profile does not define pin file ACLs"); file->path = path; file->size = 23; @@ -568,7 +574,9 @@ cflex_create_pin_file(sc_profile_t *profile, sc_pkcs15_card_t *p15card, * necessary */ ndummies = cflex_create_dummy_chvs(profile, p15card, file, SC_AC_OP_UPDATE, dummies); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, ndummies, "Unable to create dummy CHV file"); + if (ndummies < 0) + sc_file_free(file); + LOG_TEST_RET(ctx, ndummies, "Unable to create dummy CHV file"); if (!unprotected) { struct sc_pin_cmd_data pin_cmd; @@ -580,9 +588,10 @@ cflex_create_pin_file(sc_profile_t *profile, sc_pkcs15_card_t *p15card, pin_cmd.pin1.data = dummy_pin_value; pin_cmd.pin1.len = sizeof(dummy_pin_value); - r = sc_pin_cmd(p15card->card, &pin_cmd, NULL); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot verify dummy PIN"); - + r = sc_pin_cmd(p15card->card, &pin_cmd); + if (r < 0) + sc_file_free(file); + LOG_TEST_RET(ctx, r, "Cannot verify dummy PIN"); }; if (ref == 2) { @@ -593,15 +602,16 @@ cflex_create_pin_file(sc_profile_t *profile, sc_pkcs15_card_t *p15card, } r = sc_pkcs15init_create_file(profile, p15card, file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to create PIN file"); + if (r < 0) + sc_file_free(file); + LOG_TEST_RET(ctx, r, "Failed to create PIN file"); r = sc_update_binary(p15card->card, 0, buffer, 23, 0); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to update PIN file"); - if (r < 0 || file_ret == NULL) sc_file_free(file); else *file_ret = file; + LOG_TEST_RET(ctx, r, "Failed to update PIN file"); /* Delete the dummy CHV files */ cflex_delete_dummy_chvs(profile, p15card, ndummies, dummies); @@ -623,7 +633,7 @@ cflex_create_empty_pin_file(sc_profile_t *profile, sc_pkcs15_card_t *p15card, { int r; - SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(p15card->card->ctx); *file_ret = NULL; r = cflex_create_pin_file(profile, p15card, path, ref, dummy_pin_value, sizeof(dummy_pin_value), 8, @@ -645,6 +655,10 @@ static int cflex_get_keyfiles(sc_profile_t *profile, sc_card_t *card, sc_path_t path = *df_path; int r; + if (path.len <= 2) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + /* Get the private key file */ r = sc_profile_get_file_by_path(profile, &path, prkf); if (r < 0) { @@ -654,7 +668,7 @@ static int cflex_get_keyfiles(sc_profile_t *profile, sc_card_t *card, if (r != SC_SUCCESS) pbuf[0] = '\0'; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Cannot find private key file info " + sc_log(card->ctx, "Cannot find private key file info " "in profile (path=%s).", pbuf); return r; } @@ -664,7 +678,7 @@ static int cflex_get_keyfiles(sc_profile_t *profile, sc_card_t *card, sc_append_file_id(&path, 0x1012); r = sc_profile_get_file_by_path(profile, &path, pukf); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Cannot find public key file info in profile."); + sc_log(card->ctx, "Cannot find public key file info in profile."); sc_file_free(*prkf); return r; } diff --git a/src/pkcs15init/pkcs15-entersafe.c b/src/pkcs15init/pkcs15-entersafe.c index 712a02f397..e39d28f68a 100644 --- a/src/pkcs15init/pkcs15-entersafe.c +++ b/src/pkcs15init/pkcs15-entersafe.c @@ -11,7 +11,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* Initially written by Weitao Sun (weitao@ftsafe.com) 2008*/ /* Disable RSA:512bits by Shengchao Niu (shengchao@ftsafe.com) 2012 */ @@ -77,12 +77,12 @@ static int entersafe_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) {/* MF */ sc_file_t *mf_file; - sc_entersafe_create_data mf_data; + sc_entersafe_create_data mf_data = {0}; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); ret = sc_profile_get_file(profile, "MF", &mf_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,ret,"Get MF info failed"); + LOG_TEST_RET(card->ctx,ret,"Get MF info failed"); mf_data.type = SC_ENTERSAFE_MF_DATA; mf_data.data.df.file_id[0]=0x3F; @@ -98,7 +98,7 @@ static int entersafe_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) sc_file_free(mf_file); ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_CREATE_FILE, &mf_data); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,ret,"Create MF failed"); + LOG_TEST_RET(card->ctx,ret,"Create MF failed"); } {/* EF(DIR) */ @@ -109,7 +109,7 @@ static int entersafe_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) /* get dir profile */ ret = sc_profile_get_file(profile, "dir", &dir_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,ret,"Get EF(DIR) info failed"); + LOG_TEST_RET(card->ctx,ret,"Get EF(DIR) info failed"); fid=dir_file->id; size=dir_file->size; sc_file_free(dir_file); @@ -126,10 +126,12 @@ static int entersafe_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) memset(ef_data.data.ef.sm,0x00,sizeof(ef_data.data.ef.sm)); ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_CREATE_FILE, &ef_data); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,ret,"Create EF(DIR) failed"); + LOG_TEST_RET(card->ctx,ret,"Create EF(DIR) failed"); /* fill file by 0 */ + if (size > MAX_FILE_SIZE) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Initialize EF(DIR) failed with file size too large"); buff = calloc(1,size); if(!buff) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); @@ -137,7 +139,7 @@ static int entersafe_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) ret = sc_update_binary(card,0,buff,size,0); free(buff); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,ret,"Initialize EF(DIR) failed"); + LOG_TEST_RET(card->ctx,ret,"Initialize EF(DIR) failed"); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); @@ -168,7 +170,7 @@ static int entersafe_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card memcpy(df_data.data.df.aid,df->name,df->namelen); ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_CREATE_FILE, &df_data); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,ret,"Crate DF failed"); + LOG_TEST_RET(card->ctx,ret,"Create DF failed"); } {/* GPKF */ @@ -177,7 +179,7 @@ static int entersafe_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card /* get p15_gpkf profile */ ret = sc_profile_get_file(profile, "p15_gpkf", &gpkf_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,ret,"Get GPKF info failed"); + LOG_TEST_RET(card->ctx,ret,"Get GPKF info failed"); ef_data.type=SC_ENTERSAFE_EF_DATA; ef_data.data.ef.file_id[0]=(gpkf_file->id>>8)&0xFF; @@ -193,7 +195,7 @@ static int entersafe_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card sc_file_free(gpkf_file); ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_CREATE_FILE, &ef_data); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,ret,"Create GPKF failed"); + LOG_TEST_RET(card->ctx,ret,"Create GPKF failed"); } {/* p15 efs */ @@ -214,7 +216,7 @@ static int entersafe_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card for(i = 0; create_efs[i]; ++i) { if (sc_profile_get_file(profile, create_efs[i], &file)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Inconsistent profile: cannot find %s", create_efs[i]); + sc_log(card->ctx, "Inconsistent profile: cannot find %s", create_efs[i]); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INCONSISTENT_PROFILE); } @@ -234,13 +236,13 @@ static int entersafe_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card sc_file_free(file); ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_CREATE_FILE, &tmp); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,ret,"Create pkcs15 file failed"); + LOG_TEST_RET(card->ctx,ret,"Create pkcs15 file failed"); } } {/* Preinstall keys */ ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_PREINSTALL_KEYS, 0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,ret,"Preinstall keys failed"); + LOG_TEST_RET(card->ctx,ret,"Preinstall keys failed"); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,ret); @@ -292,6 +294,8 @@ static int entersafe_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card data.key_data.symmetric.key_len=16; r = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_WRITE_KEY, &data); + if (r < 0) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); /* Cache new PIN value. */ sc_pkcs15_pincache_add(p15card, pin_obj, pin, pin_len); @@ -316,7 +320,7 @@ static int entersafe_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int entersafe_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, @@ -354,7 +358,7 @@ static int entersafe_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, if ( key->algorithm != SC_ALGORITHM_RSA ) { - /* ignore DSA keys */ + /* ignore non-RSA keys */ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INVALID_ARGUMENTS); } @@ -380,7 +384,7 @@ static int entersafe_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, r = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; } sc_file_free(tfile); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "cant verify pin"); + LOG_TEST_RET(card->ctx, r, "can't verify pin"); data.key_id = (u8) kinfo->key_reference; data.usage=0x22; @@ -430,14 +434,14 @@ static int entersafe_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15ca r = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; } sc_file_free(tfile); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "cant verify pin"); + LOG_TEST_RET(card->ctx, r, "can't verify pin"); /* generate key pair */ gendat.key_id = (u8) kinfo->key_reference; gendat.key_length = (size_t) kinfo->modulus_length; gendat.modulus = NULL; r = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_GENERATE_KEY, &gendat); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "EnterSafe generate RSA key pair failed"); + LOG_TEST_RET(card->ctx, r, "EnterSafe generate RSA key pair failed"); /* get the modulus via READ PUBLIC KEY */ if (pubkey) { @@ -468,19 +472,19 @@ static int entersafe_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15ca static int entersafe_sanity_check(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { struct sc_context *ctx = p15card->card->ctx; - struct sc_pkcs15_auth_info profile_auth; + struct sc_pkcs15_auth_info profile_auth = {0}; struct sc_pkcs15_object *objs[32]; int rv, nn, ii, update_df = 0; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Check and if needed update PinFlags"); + sc_log(ctx, "Check and if needed update PinFlags"); rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 32); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to get PINs"); + LOG_TEST_RET(ctx, rv, "Failed to get PINs"); nn = rv; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &profile_auth); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to get PIN info"); + LOG_TEST_RET(ctx, rv, "Failed to get PIN info"); for (ii=0; iidata; @@ -491,7 +495,7 @@ static int entersafe_sanity_check(sc_profile_t *profile, sc_pkcs15_card_t *p15ca if (pin_attrs->reference == profile_auth.attrs.pin.reference && pin_attrs->flags != profile_auth.attrs.pin.flags) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Set flags of '%s'(flags:%X,ref:%i,id:%s) to %X", objs[ii]->label, + sc_log(ctx, "Set flags of '%s'(flags:%X,ref:%i,id:%s) to %X", objs[ii]->label, pin_attrs->flags, pin_attrs->reference, sc_pkcs15_print_id(&ainfo->auth_id), profile_auth.attrs.pin.flags); pin_attrs->flags = profile_auth.attrs.pin.flags; @@ -504,9 +508,9 @@ static int entersafe_sanity_check(sc_profile_t *profile, sc_pkcs15_card_t *p15ca while (df != NULL && df->type != SC_PKCS15_AODF) df = df->next; if (!df) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OBJECT_NOT_FOUND, "Cannot find AODF"); + LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "Cannot find AODF"); rv = sc_pkcs15init_update_any_df(p15card, profile, df, 0); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Update AODF error"); + LOG_TEST_RET(ctx, rv, "Update AODF error"); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, rv); diff --git a/src/pkcs15init/pkcs15-epass2003.c b/src/pkcs15init/pkcs15-epass2003.c index 3be5ab856f..3f742579a8 100644 --- a/src/pkcs15init/pkcs15-epass2003.c +++ b/src/pkcs15init/pkcs15-epass2003.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -51,25 +51,25 @@ static int epass2003_pkcs15_init_card(struct sc_profile *profile, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_do_log(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,NULL,0,NULL, - "ePass2003 doesn't surpport SO-PIN and SO-PUK. You can unblock key with PUK. \n"); + "ePass2003 doesn't support SO-PIN and SO-PUK. You can unblock key with PUK. \n"); { /* MF */ struct sc_file *mf_file; struct sc_file *skey_file; ret = sc_profile_get_file(profile, "MF", &mf_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Get MF info failed"); ret = sc_create_file(card, mf_file); sc_file_free(mf_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Create MF failed"); ret = sc_profile_get_file(profile, "SKey-MF", &skey_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Get SKey info failed"); ret = sc_create_file(card, skey_file); sc_file_free(skey_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Create SKey failed"); } @@ -79,11 +79,11 @@ static int epass2003_pkcs15_init_card(struct sc_profile *profile, /* get dir profile */ ret = sc_profile_get_file(profile, "DIR", &dir_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Get EF(DIR) info failed"); ret = sc_create_file(card, dir_file); sc_file_free(dir_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Create EF(DIR) failed"); sc_free_apps(card); @@ -110,36 +110,38 @@ static int epass2003_pkcs15_create_dir(struct sc_profile *profile, u8 so_maxtries = 0; ret = sc_profile_get_file(profile, "PKCS15-AppDF", &df_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Get PKCS15-AppDF info failed"); ret = sc_create_file(card, df_file); sc_file_free(df_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Create PKCS15-AppDF failed"); ret = sc_profile_get_file(profile, "SKey-AppDF", &skey_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Get SKey info failed"); ret = sc_create_file(card, skey_file); sc_file_free(skey_file); + LOG_TEST_RET(card->ctx, ret, + "Create SKey info failed"); ret = sc_profile_get_file(profile, "MAXPIN", &ef_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Get MAXPIN info failed"); ret = sc_create_file(card, ef_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Create MAXPIN failed"); ret = sc_select_file(card, &(ef_file->path), &ef_file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Select MAXPIN failed"); ret = sc_profile_get_pin_id(profile, 2, &id); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Get User PIN id error!"); user_maxtries = (u8) sc_profile_get_pin_retries(profile, id); ret = sc_profile_get_pin_id(profile, 1, &id); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Get User PIN id error!"); so_maxtries = (u8) sc_profile_get_pin_retries(profile, id); @@ -148,7 +150,7 @@ static int epass2003_pkcs15_create_dir(struct sc_profile *profile, ret = sc_update_binary(card, 0, max_counter, 2, 0); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Update MAXPIN failed"); sc_file_free(ef_file); } @@ -170,7 +172,7 @@ static int epass2003_pkcs15_create_dir(struct sc_profile *profile, for (i = 0; create_efs[i]; ++i) { if (sc_profile_get_file(profile, create_efs[i], &file)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Inconsistent profile: cannot find %s", create_efs[i]); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, @@ -178,7 +180,7 @@ static int epass2003_pkcs15_create_dir(struct sc_profile *profile, } ret = sc_create_file(card, file); sc_file_free(file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, ret, + LOG_TEST_RET(card->ctx, ret, "Create pkcs15 file failed"); } } @@ -238,7 +240,7 @@ static int epass2003_pkcs15_create_pin(struct sc_profile *profile, EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_USER; r = sc_profile_get_pin_id(profile, 2, &id); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_RET(card->ctx, r, "Get User PIN id error!"); data.key_data.es_secret.EC = sc_profile_get_pin_retries(profile, id); @@ -250,6 +252,8 @@ static int epass2003_pkcs15_create_pin(struct sc_profile *profile, data.key_data.es_secret.key_len = pin_len; r = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_WRITE_KEY, &data); + if (r < 0) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); if (pin_obj) { /* Cache new PIN value. */ sc_pkcs15_pincache_add(p15card, pin_obj, pin, pin_len); @@ -272,7 +276,7 @@ static int epass2003_pkcs15_create_pin(struct sc_profile *profile, EPASS2003_AC_MAC_EQUAL | EPASS2003_AC_SO; r = sc_profile_get_pin_id(profile, 1, &id); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_RET(card->ctx, r, "Get User PIN id error!"); data.key_data.es_secret.EC = sc_profile_get_pin_retries(profile, id); @@ -294,6 +298,8 @@ static int epass2003_pkcs15_key_reference(struct sc_profile *profile, struct sc_pkcs15_prkey_info *prkey) { SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); + if (prkey->path.len == 0) + SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); prkey->key_reference = prkey->path.value[prkey->path.len - 1]; SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } @@ -303,15 +309,25 @@ static int cosm_new_file(struct sc_profile *profile, struct sc_card *card, unsigned int type, unsigned int num, struct sc_file **out) { - struct sc_file *file; + struct sc_file *file = NULL; const char *_template = NULL, *desc = NULL; unsigned int structure = 0xFFFFFFFF; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "type %X; num %i\n", type, + sc_log(card->ctx, "type %X; num %i\n", type, num); while (1) { switch (type) { + case SC_PKCS15_TYPE_PRKEY_EC: + desc = "EC private key"; + _template = "private-key"; + structure = SC_CARDCTL_OBERTHUR_KEY_EC_CRT; + break; + case SC_PKCS15_TYPE_PUBKEY_EC: + desc = "EC public key"; + _template = "public-key"; + structure = SC_CARDCTL_OBERTHUR_KEY_EC_PUBLIC; + break; case SC_PKCS15_TYPE_PRKEY_RSA: desc = "RSA private key"; _template = "private-key"; @@ -322,10 +338,6 @@ cosm_new_file(struct sc_profile *profile, struct sc_card *card, _template = "public-key"; structure = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; break; - case SC_PKCS15_TYPE_PUBKEY_DSA: - desc = "DSA public key"; - _template = "public-key"; - break; case SC_PKCS15_TYPE_PRKEY: desc = "extractable private key"; _template = "extractable-key"; @@ -346,7 +358,7 @@ cosm_new_file(struct sc_profile *profile, struct sc_card *card, * the generic class (SC_PKCS15_TYPE_CERT) */ if (!(type & ~SC_PKCS15_TYPE_CLASS_MASK)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "File type %X not supported by card driver", type); return SC_ERROR_INVALID_ARGUMENTS; @@ -354,15 +366,20 @@ cosm_new_file(struct sc_profile *profile, struct sc_card *card, type &= SC_PKCS15_TYPE_CLASS_MASK; } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "template %s; num %i\n", + sc_log(card->ctx, "template %s; num %i\n", _template, num); if (sc_profile_get_file(profile, _template, &file) < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Profile doesn't define %s template '%s'\n", desc, _template); return SC_ERROR_NOT_SUPPORTED; } + if (file->path.len < 1) { + sc_file_free(file); + return SC_ERROR_INTERNAL; + } + file->id &= 0xFF00; file->id |= (num & 0x00FF); @@ -370,11 +387,11 @@ cosm_new_file(struct sc_profile *profile, struct sc_card *card, file->type = SC_FILE_TYPE_INTERNAL_EF; file->ef_structure = structure; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "file size %"SC_FORMAT_LEN_SIZE_T"u; ef type %i/%i; id %04X, path_len %"SC_FORMAT_LEN_SIZE_T"u\n", file->size, file->type, file->ef_structure, file->id, file->path.len); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "file path: %s", + sc_log(card->ctx, "file path: %s", sc_print_path(&(file->path))); *out = file; @@ -410,16 +427,16 @@ static int epass2003_pkcs15_store_key(struct sc_profile *profile, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "index %"SC_FORMAT_LEN_SIZE_T"u; id %s\n", idx, sc_pkcs15_print_id(&key_info->id)); if (key->algorithm != SC_ALGORITHM_RSA || key->algorithm != SC_ALGORITHM_RSA) - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, + LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "store key: only support RSA"); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "store key: with ID:%s and path:%s", sc_pkcs15_print_id(&key_info->id), sc_print_path(&key_info->path)); @@ -427,20 +444,20 @@ static int epass2003_pkcs15_store_key(struct sc_profile *profile, /* allocate key object */ r = cosm_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_RET(card->ctx, r, "create key: failed to allocate new key object"); file->size = keybits; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "private key path: %s", + sc_log(card->ctx, "private key path: %s", sc_print_path(&(file->path))); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "private key_info path: %s", + sc_log(card->ctx, "private key_info path: %s", sc_print_path(&(key_info->path))); - r = sc_delete_file(p15card->card, &file->path); + sc_delete_file(p15card->card, &file->path); /* create */ r = sc_pkcs15init_create_file(profile, p15card, file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_RET(card->ctx, r, "create key: failed to create key file"); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "index %"SC_FORMAT_LEN_SIZE_T"u; keybits %"SC_FORMAT_LEN_SIZE_T"u\n", idx, keybits); if (keybits < 1024 || keybits > 2048 || (keybits % 0x20)) { @@ -454,11 +471,11 @@ static int epass2003_pkcs15_store_key(struct sc_profile *profile, path.len -= 2; r = sc_select_file(card, &path, &tfile); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_RET(card->ctx, r, "generate key: no private object DF"); r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_RET(card->ctx, r, "No authorisation to store private key"); sc_file_free(tfile); @@ -470,7 +487,7 @@ static int epass2003_pkcs15_store_key(struct sc_profile *profile, data.key_data.es_key.rsa = (void *)&key->u.rsa; r = sc_card_ctl(p15card->card, SC_CARDCTL_ENTERSAFE_WRITE_KEY, &data); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, + LOG_TEST_RET(card->ctx, r, "store key: cannot update private key"); sc_file_free(file); @@ -488,7 +505,7 @@ static int epass2003_pkcs15_generate_key(struct sc_profile *profile, sc_epass2003_gen_key_data gendat; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)obj->data; - size_t idx = key_info->key_reference; + int idx = key_info->key_reference; size_t keybits = key_info->modulus_length; struct sc_file *tfile = NULL, *pukf = NULL; struct sc_path path; @@ -497,69 +514,88 @@ static int epass2003_pkcs15_generate_key(struct sc_profile *profile, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) + if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA && obj->type != SC_PKCS15_TYPE_PRKEY_EC) return SC_ERROR_NOT_SUPPORTED; + if(obj->type == SC_PKCS15_TYPE_PRKEY_EC && keybits == 0) + keybits = 256; //EC key length is 256 ... + /* allocate key object */ - r = cosm_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, idx, &file); - SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, + r = cosm_new_file(profile, card, obj->type, idx, &file); //replace SC_PKCS15_TYPE_PRKEY_RSA with obj->type + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE, r, "create key: failed to allocate new key object"); file->size = keybits; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "private key path: %s", + sc_log(card->ctx, "private key path: %s", sc_print_path(&file->path)); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "private key_info path: %s", + sc_log(card->ctx, "private key_info path: %s", sc_print_path(&(key_info->path))); r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_DELETE); - SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE, r, "generate key: pkcs15init_authenticate(SC_AC_OP_DELETE) failed"); - r = sc_delete_file(p15card->card, &file->path); + sc_delete_file(p15card->card, &file->path); /* create */ r = sc_pkcs15init_create_file(profile, p15card, file); - SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE, r, "create key: failed to create key file"); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "index %"SC_FORMAT_LEN_SIZE_T"u; keybits %"SC_FORMAT_LEN_SIZE_T"u\n", + sc_log(card->ctx, + "index %u; keybits %"SC_FORMAT_LEN_SIZE_T"u\n", idx, keybits); if (keybits < 1024 || keybits > 2048 || (keybits % 0x20)) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, - "Unsupported key size %"SC_FORMAT_LEN_SIZE_T"u\n", - keybits); - r = SC_ERROR_INVALID_ARGUMENTS; - goto err; + if(obj->type == SC_PKCS15_TYPE_PRKEY_EC && keybits == 256) + { + sc_log(card->ctx, "current Alg is EC,Only support 256 ..\n"); + } + else + { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Unsupported key size %"SC_FORMAT_LEN_SIZE_T"u\n", + keybits); + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } } path = key_info->path; path.len -= 2; r = sc_select_file(card, &path, &tfile); - SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE, r, "generate key: no private object DF"); r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_CRYPTO); - SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE, r, "generate key: pkcs15init_authenticate(SC_AC_OP_CRYPTO) failed"); r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_CREATE); - SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE, r, "generate key: pkcs15init_authenticate(SC_AC_OP_CREATE) failed"); - if ((r = cosm_new_file(profile, card, SC_PKCS15_TYPE_PUBKEY_RSA, idx, - &pukf)) < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA ) + { + r = cosm_new_file(profile, card, SC_PKCS15_TYPE_PUBKEY_EC, idx, &pukf); + } + else + { + r = cosm_new_file(profile, card, SC_PKCS15_TYPE_PUBKEY_RSA, idx, &pukf); + } + + if (r < 0) { + sc_log(card->ctx, "generate key: create temporary pukf failed\n"); goto err; } + pukf->size = keybits; pukf->id = pukf->path.value[pukf->path.len - 2] * 0x100 + pukf->path.value[pukf->path.len - 1]; - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "public key size %"SC_FORMAT_LEN_SIZE_T"u; ef type %i/%i; id %04X; path: %s", pukf->size, pukf->type, pukf->ef_structure, pukf->id, sc_print_path(&pukf->path)); @@ -569,12 +605,12 @@ static int epass2003_pkcs15_generate_key(struct sc_profile *profile, if (r == SC_SUCCESS) { r = sc_pkcs15init_authenticate(profile, p15card, pukf, SC_AC_OP_DELETE); - SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE, r, "generate key - pubkey: pkcs15init_authenticate(SC_AC_OP_DELETE) failed"); r = sc_pkcs15init_delete_by_path(profile, p15card, &pukf->path); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "generate key: failed to delete existing key file\n"); goto err; } @@ -582,14 +618,14 @@ static int epass2003_pkcs15_generate_key(struct sc_profile *profile, /* create */ r = sc_pkcs15init_create_file(profile, p15card, pukf); if (r != SC_SUCCESS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "generate key: pukf create file failed\n"); goto err; } r = sc_pkcs15init_authenticate(profile, p15card, pukf, SC_AC_OP_UPDATE); - SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE, r, "generate key - pubkey: pkcs15init_authenticate(SC_AC_OP_UPDATE) failed"); /* generate key pair */ @@ -600,12 +636,18 @@ static int epass2003_pkcs15_generate_key(struct sc_profile *profile, gendat.pukey_id = pukf->id; gendat.key_length = keybits; gendat.modulus = NULL; + gendat.modulus_len = 0; r = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_GENERATE_KEY, &gendat); - SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE, r, "generate RSA key pair failed"); + if (!gendat.modulus) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + /* get the modulus */ - if (pubkey) { + if (pubkey && (obj->type == SC_PKCS15_TYPE_PRKEY_RSA)) { u8 *buf; struct sc_pkcs15_pubkey_rsa *rsa = &pubkey->u.rsa; /* set the modulus */ @@ -624,7 +666,38 @@ static int epass2003_pkcs15_generate_key(struct sc_profile *profile, rsa->exponent.len = 3; pubkey->algorithm = SC_ALGORITHM_RSA; - } else + } + else if(pubkey && (obj->type == SC_PKCS15_TYPE_PRKEY_EC)){ + struct sc_ec_parameters *ecparams = (struct + sc_ec_parameters *)key_info->params.data; + pubkey->algorithm = SC_ALGORITHM_EC; + pubkey->u.ec.ecpointQ.value = (u8 *)malloc(gendat.modulus_len + 1); + if (!pubkey->u.ec.ecpointQ.value) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + + pubkey->u.ec.ecpointQ.value[0] = 0x04; + memcpy(&pubkey->u.ec.ecpointQ.value[1], gendat.modulus, gendat.modulus_len); + pubkey->u.ec.ecpointQ.len = gendat.modulus_len + 1; + free(gendat.modulus); + + free(pubkey->u.ec.params.named_curve); + pubkey->u.ec.params.named_curve = NULL; + + free(pubkey->u.ec.params.der.value); + pubkey->u.ec.params.der.value = NULL; + pubkey->u.ec.params.der.len = 0; + pubkey->u.ec.params.named_curve = strdup(ecparams->named_curve); + + if (!pubkey->u.ec.params.named_curve){ + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + + r = sc_pkcs15_fix_ec_parameters(card->ctx, &pubkey->u.ec.params); + } + else /* free public key */ free(gendat.modulus); @@ -633,6 +706,13 @@ static int epass2003_pkcs15_generate_key(struct sc_profile *profile, sc_file_free(file); sc_file_free(tfile); + if(r < 0 && pubkey->u.ec.ecpointQ.value) + { + free(pubkey->u.ec.ecpointQ.value); + pubkey->u.ec.ecpointQ.value = NULL; + pubkey->u.ec.ecpointQ.len = 0; + } + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } @@ -649,20 +729,20 @@ static int epass2003_pkcs15_sanity_check(sc_profile_t * profile, sc_pkcs15_card_t * p15card) { struct sc_context *ctx = p15card->card->ctx; - struct sc_pkcs15_auth_info profile_auth; + struct sc_pkcs15_auth_info profile_auth = {0}; struct sc_pkcs15_object *objs[32]; int rv, nn, ii, update_df = 0; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "Check and if needed update PinFlags"); rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 32); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to get PINs"); + LOG_TEST_RET(ctx, rv, "Failed to get PINs"); nn = rv; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &profile_auth); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to get PIN info"); + LOG_TEST_RET(ctx, rv, "Failed to get PIN info"); for (ii = 0; ii < nn; ii++) { struct sc_pkcs15_auth_info *ainfo = @@ -674,7 +754,7 @@ static int epass2003_pkcs15_sanity_check(sc_profile_t * profile, if (pin_attrs->reference == profile_auth.attrs.pin.reference && pin_attrs->flags != profile_auth.attrs.pin.flags) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "Set flags of '%s'(flags:%X,ref:%i,id:%s) to %X", objs[ii]->label, pin_attrs->flags, pin_attrs->reference, @@ -690,11 +770,11 @@ static int epass2003_pkcs15_sanity_check(sc_profile_t * profile, while (df != NULL && df->type != SC_PKCS15_AODF) df = df->next; if (!df) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, + LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "Cannot find AODF"); rv = sc_pkcs15init_update_any_df(p15card, profile, df, 0); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Update AODF error"); + LOG_TEST_RET(ctx, rv, "Update AODF error"); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, rv); diff --git a/src/pkcs15init/pkcs15-gids.c b/src/pkcs15init/pkcs15-gids.c index db013c375c..b40ab61aed 100644 --- a/src/pkcs15init/pkcs15-gids.c +++ b/src/pkcs15init/pkcs15-gids.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H @@ -123,7 +123,7 @@ static int gids_save_certificate(struct sc_pkcs15_card *p15card, struct sc_pkcs1 //TODO save the certificate in the special file LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to find the private key associated to the certificate"); + LOG_TEST_RET(card->ctx, r, "unable to find the private key associated to the certificate"); LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_GIDS_SAVE_CERT, &call)); } diff --git a/src/pkcs15init/pkcs15-gpk.c b/src/pkcs15init/pkcs15-gpk.c deleted file mode 100644 index ac9ba210c5..0000000000 --- a/src/pkcs15init/pkcs15-gpk.c +++ /dev/null @@ -1,1148 +0,0 @@ -/* - * GPK specific operation for PKCS15 initialization - * - * Copyright (C) 2002 Olaf Kirch - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif - -#include "libopensc/opensc.h" -#include "libopensc/cardctl.h" -#include "libopensc/cards.h" -#include "libopensc/log.h" -#include "pkcs15-init.h" -#include "profile.h" - -/* this could be removed once we include libopensc/internal.h */ -#ifndef _WIN32 -#define msleep(t) usleep((t) * 1000) -#else -#include -#define msleep(t) Sleep(t) -#define sleep(t) Sleep((t) * 1000) -#endif - -#define PK_INIT_IMMEDIATELY - -#define GPK_MAX_PINS 8 -#define GPK_PIN_SCOPE 8 -#define GPK_FTYPE_SECRET_CODE 0x21 -#define GPK_FTYPE_PUBLIC_KEY 0x2C - -/* - * Key components (for storing private keys) - */ -struct pkcomp { - unsigned char tag; - u8 * data; - unsigned int size; -}; - -struct pkpart { - struct pkcomp components[7]; - unsigned int count; - unsigned int size; -}; - -struct pkdata { - unsigned int algo; - unsigned int usage; - struct pkpart _public, _private; - unsigned int bits, bytes; -}; - -/* - * Local functions - */ -static int gpk_pkfile_create(sc_profile_t *, sc_pkcs15_card_t *, sc_file_t *); -static int gpk_encode_rsa_key(sc_profile_t *, sc_card_t *, - struct sc_pkcs15_prkey_rsa *, struct pkdata *, - struct sc_pkcs15_prkey_info *); -static int gpk_encode_dsa_key(sc_profile_t *, sc_card_t *, - struct sc_pkcs15_prkey_dsa *, struct pkdata *, - struct sc_pkcs15_prkey_info *); -static int gpk_store_pk(struct sc_profile *, sc_pkcs15_card_t *, - sc_file_t *, struct pkdata *); -static int gpk_init_pinfile(sc_profile_t *, sc_pkcs15_card_t *, sc_file_t *); -static int gpk_pkfile_init_public(sc_profile_t *, sc_pkcs15_card_t *, - sc_file_t *, unsigned int, unsigned int, unsigned int); -static int gpk_pkfile_init_private(sc_card_t *, sc_file_t *, unsigned int); -static int gpk_read_rsa_key(sc_card_t *, struct sc_pkcs15_pubkey_rsa *); - - -/* - * Erase the card - */ -static int -gpk_erase_card(struct sc_profile *pro, sc_pkcs15_card_t *p15card) -{ - int locked; - - if (sc_card_ctl(p15card->card, SC_CARDCTL_GPK_IS_LOCKED, &locked) == 0 - && locked) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, - "This card is already personalized, unable to " - "create PKCS#15 structure."); - return SC_ERROR_NOT_SUPPORTED; - } - return sc_card_ctl(p15card->card, SC_CARDCTL_ERASE_CARD, NULL); -} - -/* - * Create a new DF - * This will usually be the application DF - */ -static int -gpk_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) -{ - struct sc_file *pinfile; - int r, locked; - - SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); - if (sc_card_ctl(p15card->card, SC_CARDCTL_GPK_IS_LOCKED, &locked) == 0 - && locked) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, - "This card is already personalized, unable to " - "create PKCS#15 structure."); - return SC_ERROR_NOT_SUPPORTED; - } - - /* Create the DF. */ - r = sc_pkcs15init_create_file(profile, p15card, df); - if (r < 0) - return r; - - /* See if there's a file called "pinfile" that resides within - * this DF. If so, create it */ - if (sc_profile_get_file(profile, "pinfile", &pinfile) >= 0) { - /* Build the pin file's path from the DF path + its - * file ID */ - pinfile->path = df->path; - sc_append_file_id(&pinfile->path, pinfile->id); - - r = gpk_init_pinfile(profile, p15card, pinfile); - sc_file_free(pinfile); - if (r < 0) - return r; - - /* TODO: What for it was used ? - for (i = 0; i < GPK_MAX_PINS; i++) - * sc_keycache_put_pin(&df->path, GPK_PIN_SCOPE|i, (const u8 *) " "); - */ - } - - SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - -/* - * Select a PIN reference - */ -static int -gpk_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, - sc_pkcs15_auth_info_t *auth_info) -{ - int preferred, current; - - SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); - - if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) - return SC_ERROR_OBJECT_NOT_VALID; - - if ((current = auth_info->attrs.pin.reference) < 0) - current = 0; - - if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) { - preferred = GPK_PIN_SCOPE | 0; - } else { - preferred = current | GPK_PIN_SCOPE; - - if (preferred & 1) - preferred++; - if (preferred < (GPK_PIN_SCOPE | 2)) - preferred = GPK_PIN_SCOPE | 2; - if (preferred > 15) - return SC_ERROR_TOO_MANY_OBJECTS; - } - - if (current > preferred) - return SC_ERROR_TOO_MANY_OBJECTS; - auth_info->attrs.pin.reference = preferred; - SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, 0); -} - -/* - * Store a PIN - */ -static int -gpk_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df, - sc_pkcs15_object_t *pin_obj, - const u8 *pin, size_t pin_len, - const u8 *puk, size_t puk_len) -{ - sc_pkcs15_auth_info_t *auth_info = (sc_pkcs15_auth_info_t *) pin_obj->data; - struct sc_pkcs15_pin_attributes *pin_attrs = &auth_info->attrs.pin; - u8 nulpin[8]; - int r; - - SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); - - if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) - return SC_ERROR_OBJECT_NOT_VALID; - - if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) { - /* SO PIN reference must be 0 */ - if (pin_attrs->reference != (GPK_PIN_SCOPE | 0)) - return SC_ERROR_INVALID_ARGUMENTS; - } else { - /* PIN references must be even numbers - * (the odd numbered PIN entries contain the - * PUKs). - * Returning SC_ERROR_INVALID_PIN_REFERENCE will - * tell the caller to pick a different value. - */ - if ((pin_attrs->reference & 1) || !(pin_attrs->reference & GPK_PIN_SCOPE)) - return SC_ERROR_INVALID_PIN_REFERENCE; - if (pin_attrs->reference >= (GPK_PIN_SCOPE + GPK_MAX_PINS)) - return SC_ERROR_TOO_MANY_OBJECTS; - } - - /* No PUK given, but the PIN file specifies an unblock - * PIN for every PIN. - * Use the same value for the PUK for now. - * Alternatively, we could leave the unblock PIN at the default - * value, but deliberately block it. */ - if (puk == NULL || puk_len == 0) { - puk = pin; - puk_len = pin_len; - } - - r = sc_select_file(p15card->card, &df->path, NULL); - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "select df path: %i", r); - if (r < 0) - return r; - - /* Current PIN is 00:00:00:00:00:00:00:00 */ - memset(nulpin, 0, sizeof(nulpin)); - r = sc_change_reference_data(p15card->card, SC_AC_CHV, - pin_attrs->reference, - nulpin, sizeof(nulpin), - pin, pin_len, NULL); - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "change CHV %i", r); - if (r < 0) - return r; - - /* Current PUK is 00:00:00:00:00:00:00:00 */ - r = sc_change_reference_data(p15card->card, SC_AC_CHV, - pin_attrs->reference + 1, - nulpin, sizeof(nulpin), - puk, puk_len, NULL); - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "change CHV+1 %i", r); - if (r < 0) - return r; - - SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - - -/* - * Lock a file operation - */ -static int -gpk_lock(sc_card_t *card, sc_file_t *file, unsigned int op) -{ - struct sc_cardctl_gpk_lock args; - - args.file = file; - args.operation = op; - return sc_card_ctl(card, SC_CARDCTL_GPK_LOCK, &args); -} - -/* - * Lock the pin file - */ -static int -gpk_lock_pinfile(struct sc_profile *profile, sc_pkcs15_card_t *p15card, - sc_file_t *pinfile) -{ - struct sc_path path; - struct sc_file *parent = NULL; - int r; - - SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); - /* Select the parent DF */ - path = pinfile->path; - if (path.len >= 2) - path.len -= 2; - if (path.len == 0) - sc_format_path("3F00", &path); - if ((r = sc_select_file(p15card->card, &path, &parent)) < 0) - return r; - - /* Present PINs etc as necessary */ - r = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_LOCK); - if (r >= 0) - r = gpk_lock(p15card->card, pinfile, SC_AC_OP_WRITE); - - sc_file_free(parent); - SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - -/* - * Initialize pin file - */ -static int -gpk_init_pinfile(struct sc_profile *profile, sc_pkcs15_card_t *p15card, - sc_file_t *file) -{ - const sc_acl_entry_t *acl; - unsigned char buffer[GPK_MAX_PINS * 8], *blk; - struct sc_file *pinfile; - unsigned int so_attempts[2], user_attempts[2]; - unsigned int npins, i, j, cks; - int r; - - SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); - /* Set defaults */ - so_attempts[0] = sc_profile_get_pin_retries(profile, SC_PKCS15INIT_SO_PIN); - so_attempts[1] = sc_profile_get_pin_retries(profile, SC_PKCS15INIT_SO_PUK); - user_attempts[0] = sc_profile_get_pin_retries(profile, SC_PKCS15INIT_USER_PIN); - user_attempts[1] = sc_profile_get_pin_retries(profile, SC_PKCS15INIT_USER_PUK); - - sc_file_dup(&pinfile, file); - if (pinfile == NULL) - return SC_ERROR_OUT_OF_MEMORY; - - /* Create the PIN file. */ - acl = sc_file_get_acl_entry(pinfile, SC_AC_OP_WRITE); - if (acl->method != SC_AC_NEVER) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, - "PIN file most be protected by WRITE=NEVER"); - sc_file_free(pinfile); - return SC_ERROR_INVALID_ARGUMENTS; - } - sc_file_add_acl_entry(pinfile, SC_AC_OP_WRITE, SC_AC_NONE, 0); - - if (pinfile->size == 0) - pinfile->size = GPK_MAX_PINS * 8; - - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Now create file"); - /* Now create the file */ - if ((r = sc_pkcs15init_create_file(profile, p15card, pinfile)) < 0 - || (r = sc_select_file(p15card->card, &pinfile->path, NULL)) < 0) { - goto out; - } - - /* Set up the PIN file contents. - * We assume the file will contain pairs of PINs/PUKs */ - npins = pinfile->size / 8; - memset(buffer, 0, sizeof(buffer)); - for (i = 0, blk = buffer; i < npins; blk += 8, i += 1) { - /* Determine the number of PIN/PUK presentation - * attempts. If the profile defines a SO PIN, - * it will be stored in the first PIN/PUK pair. - */ - blk[0] = user_attempts[i & 1]; - if (i < 2 && so_attempts[0]) - blk[0] = so_attempts[i & 1]; - if ((i & 1) == 0) { - /* This is a PIN. If there's room in the file, - * the next will be a PUK so take note of the - * unlock code */ - if (i + 1 < npins) - blk[2] = GPK_PIN_SCOPE | (i + 1); - } - - /* Compute the CKS */ - for (j = 0, cks = 0; j < 8; j++) - cks ^= blk[j]; - blk[3] = ~cks; - } - - r = sc_write_binary(p15card->card, 0, buffer, npins * 8, 0); - if (r >= 0) - r = gpk_lock_pinfile(profile, p15card, pinfile); - -out: sc_file_free(pinfile); - SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r); -} - -/* - * Create a key file - */ -static int -gpk_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) -{ - sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; - struct sc_file *keyfile = NULL; - size_t bytes, mod_len, exp_len, prv_len, pub_len; - int r, algo; - - /* The caller is supposed to have chosen a key file path for us */ - if (key_info->path.len == 0 || key_info->modulus_length == 0) - return SC_ERROR_INVALID_ARGUMENTS; - - /* Get the file we're supposed to create */ - r = sc_profile_get_file_by_path(profile, &key_info->path, &keyfile); - if (r < 0) - return r; - - /* Compute the file size. - * We assume private keys are stored as CRT elements. - * - 512, 768 bit keys: all CRT elements fit into one record - * - >= 1024: each CRT element into a record of its own - * - * We also assume the public exponent is 32bit max - * - * Rules - * - private key records must have a length divisible by 8 - */ - mod_len = key_info->modulus_length / 8; - exp_len = 4; - bytes = mod_len / 2; - pub_len = 8 + ((3 + mod_len + 3 + exp_len + 3) & ~3UL); - if (5 * bytes < 256) { - prv_len = 8 + ((3 + 5 * bytes + 7) & ~7UL); - } else { - prv_len = 8 + 5 * ((3 + bytes + 7) & ~7UL); - } - keyfile->size = pub_len + prv_len; - - switch (obj->type) { - case SC_PKCS15_TYPE_PRKEY_RSA: - algo = SC_ALGORITHM_RSA; break; - case SC_PKCS15_TYPE_PRKEY_DSA: - algo = SC_ALGORITHM_DSA; break; - default: - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported public key algorithm"); - return SC_ERROR_NOT_SUPPORTED; - } - - /* Fix up PIN references in file ACL and create the PK file */ - if ((r = sc_pkcs15init_fixup_file(profile, p15card, keyfile)) < 0 - || (r = gpk_pkfile_create(profile, p15card, keyfile)) < 0) - goto done; - -#ifdef PK_INIT_IMMEDIATELY - /* Initialize the public key header */ - r = gpk_pkfile_init_public(profile, p15card, keyfile, algo, - key_info->modulus_length, - key_info->usage); - if (r < 0) - goto done; - - /* Create the private key portion */ - r = gpk_pkfile_init_private(p15card->card, keyfile, prv_len); -#endif - -done: - sc_file_free(keyfile); - return r; -} - -/* - * Store a private key - */ -static int -gpk_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, - sc_pkcs15_object_t *obj, struct sc_pkcs15_prkey *key) -{ - sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; - struct sc_file *keyfile = NULL; - struct pkdata data; - int r; - - /* The caller is supposed to have chosen a key file path for us */ - if (key_info->path.len == 0 || key_info->modulus_length == 0) - return SC_ERROR_INVALID_ARGUMENTS; - - /* Get the file we're supposed to create */ - r = sc_select_file(p15card->card, &key_info->path, &keyfile); - if (r < 0) - return r; - - switch (key->algorithm) { - case SC_ALGORITHM_RSA: - r = gpk_encode_rsa_key(profile, p15card->card, &key->u.rsa, - &data, key_info); - break; - - case SC_ALGORITHM_DSA: - r = gpk_encode_dsa_key(profile, p15card->card, &key->u.dsa, - &data, key_info); - break; - default: - return SC_ERROR_NOT_SUPPORTED; - } - - if (r >= 0) - r = gpk_store_pk(profile, p15card, keyfile, &data); - - sc_file_free(keyfile); - return r; -} - -/* - * On-board key generation. - */ -static int -gpk_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, - sc_pkcs15_object_t *obj, - sc_pkcs15_pubkey_t *pubkey) -{ - struct sc_cardctl_gpk_genkey args; - sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; - unsigned int keybits; - sc_file_t *keyfile; - int r, n; - - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, - "path=%s, %"SC_FORMAT_LEN_SIZE_T"u bits\n", - sc_print_path(&key_info->path), - key_info->modulus_length); - - if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "GPK supports generating only RSA keys."); - return SC_ERROR_NOT_SUPPORTED; - } - - /* The caller is supposed to have chosen a key file path for us */ - if (key_info->path.len == 0 || key_info->modulus_length == 0) - return SC_ERROR_INVALID_ARGUMENTS; - keybits = key_info->modulus_length; - - if ((r = sc_select_file(p15card->card, &key_info->path, &keyfile)) < 0) - return r; - -#ifndef PK_INIT_IMMEDIATELY - r = gpk_pkfile_init_public(profile, p15card, keyfile, SC_ALGORITHM_RSA, - keybits, key_info->usage); - if (r < 0) { - sc_file_free(keyfile); - return r; - } - - if ((r = gpk_pkfile_init_private(p15card->card, keyfile, 5 * ((3 + keybits / 16 + 7) & ~7UL))) < 0) { - sc_file_free(keyfile); - return r; - } -#endif - sc_file_free(keyfile); - - memset(&args, 0, sizeof(args)); - /*args.exponent = 0x10001;*/ - n = key_info->path.len; - args.fid = (key_info->path.value[n-2] << 8) | key_info->path.value[n-1]; - args.privlen = keybits; - - r = sc_card_ctl(p15card->card, SC_CARDCTL_GPK_GENERATE_KEY, &args); - if (r < 0) - return r; - - /* This is fairly weird. The GENERATE RSA KEY command returns - * immediately, but obviously it needs more time to complete. - * This is why we sleep here. */ - sleep(20); - - pubkey->algorithm = SC_ALGORITHM_RSA; - return gpk_read_rsa_key(p15card->card, &pubkey->u.rsa); -} - -/* - * GPK public/private key file handling is hideous. - * 600 lines of coke sweat and tears... - */ -/* - * Create the PK file - * XXX: Handle the UPDATE ACL = NEVER case just like for EFsc files - */ -static int -gpk_pkfile_create(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *file) -{ - struct sc_file *found = NULL; - int r; - - r = sc_select_file(p15card->card, &file->path, &found); - if (r == SC_ERROR_FILE_NOT_FOUND) { - r = sc_pkcs15init_create_file(profile, p15card, file); - if (r >= 0) - r = sc_select_file(p15card->card, &file->path, &found); - } else { - /* XXX: make sure the file has correct type and size? */ - } - - if (r >= 0) - r = sc_pkcs15init_authenticate(profile, p15card, file, - SC_AC_OP_UPDATE); - sc_file_free(found); - - return r; -} - -static int -gpk_pkfile_keybits(unsigned int bits, unsigned char *p) -{ - switch (bits) { - case 512: *p = 0x00; return 0; - case 768: *p = 0x10; return 0; - case 1024: *p = 0x11; return 0; - } - return SC_ERROR_NOT_SUPPORTED; -} - -static int -gpk_pkfile_keyalgo(unsigned int algo, unsigned char *p) -{ - switch (algo) { - case SC_ALGORITHM_RSA: *p = 0x00; return 0; - case SC_ALGORITHM_DSA: *p = 0x01; return 0; - } - return SC_ERROR_NOT_SUPPORTED; -} - -/* - * Set up the public key record for a signature only public key - */ -static int -gpk_pkfile_init_public(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *file, - unsigned int algo, unsigned int bits, - unsigned int usage) -{ - struct sc_context *ctx = p15card->card->ctx; - const sc_acl_entry_t *acl; - sc_file_t *tmp = NULL; - u8 sysrec[7], buffer[256]; - unsigned int n, npins; - int r, card_type; - - /* Find out what sort of GPK we're using */ - if ((r = sc_card_ctl(p15card->card, SC_CARDCTL_GPK_VARIANT, &card_type)) < 0) - return r; - - /* Set up the system record */ - memset(sysrec, 0, sizeof(sysrec)); - - /* Mapping keyUsage to sysrec[2]: - * 0x00 sign & unwrap - * 0x10 sign only - * 0x20 unwrap only - * 0x30 CA key - * - * We start with a value of 0x30. - * If the key allows decryption, clear the sign only bit. - * Likewise, if it allows signing, clear the unwrap only bit. - */ - sysrec[2] = 0x30; - if (usage & (SC_PKCS15_PRKEY_USAGE_DECRYPT|SC_PKCS15_PRKEY_USAGE_UNWRAP)) - sysrec[2] &= ~0x10; - if (usage & (SC_PKCS15_PRKEY_USAGE_SIGN|SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)) - sysrec[2] &= ~0x20; - if (sysrec[2] == 0x30) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Key usage should specify at least one of sign or decipher"); - return SC_ERROR_INVALID_ARGUMENTS; - } - - /* Set the key size and algorithm */ - if ((r = gpk_pkfile_keybits(bits, &sysrec[1])) < 0 - || (r = gpk_pkfile_keyalgo(algo, &sysrec[5])) < 0) - return r; - - /* Set PIN protection if requested. - * As the crypto ACLs are stored inside the file, - * we have to get them from the profile here. */ - r = sc_profile_get_file_by_path(profile, &file->path, &tmp); - if (r < 0) - return r; - /* Fix up PIN references in file ACL */ - if ((r = sc_pkcs15init_fixup_file(profile, p15card, tmp)) < 0) - goto out; - - acl = sc_file_get_acl_entry(tmp, SC_AC_OP_CRYPTO); - for (npins = 0; acl; acl = acl->next) { - if (acl->method == SC_AC_NONE - || acl->method == SC_AC_NEVER) - continue; - if (acl->method != SC_AC_CHV) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Authentication method not " - "supported for private key files.\n"); - r = SC_ERROR_NOT_SUPPORTED; - goto out; - } - if (++npins >= 2) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Too many pins for PrKEY file!\n"); - r = SC_ERROR_NOT_SUPPORTED; - goto out; - } - sysrec[2] += 0x40; - sysrec[3] >>= 4; - sysrec[3] |= acl->key_ref << 4; - } - - /* compute checksum - yet another slightly different - * checksum algorithm courtesy of Gemplus */ - if (card_type >= SC_CARD_TYPE_GPK_GPK8000) { - /* This is according to the gpk reference manual */ - sysrec[6] = 0xA5; - } else { - /* And this is what you have to use for the GPK4000 */ - sysrec[6] = 0xFF; - } - for (n = 0; n < 6; n++) - sysrec[6] ^= sysrec[n]; - - r = sc_read_record(p15card->card, 1, buffer, sizeof(buffer), - SC_RECORD_BY_REC_NR); - if (r >= 0) { - if (r != 7 || buffer[0] != 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "first record of public key file is not Lsys0"); - r = SC_ERROR_OBJECT_NOT_VALID; - goto out; - } - - r = sc_update_record(p15card->card, 1, sysrec, sizeof(sysrec), - SC_RECORD_BY_REC_NR); - } else { - r = sc_append_record(p15card->card, sysrec, sizeof(sysrec), 0); - } - -out: sc_file_free(tmp); - return r; -} - -static int -gpk_pkfile_update_public(struct sc_profile *profile, - sc_pkcs15_card_t *p15card, struct pkpart *part) -{ - struct sc_context *ctx = p15card->card->ctx; - struct pkcomp *pe; - unsigned char buffer[256]; - unsigned int m, n, tag; - int r = 0, found; - - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Updating public key elements\n"); - - /* If we've been given a key with public parts, write them now */ - for (n = 2; n < 256; n++) { - r = sc_read_record(p15card->card, n, buffer, sizeof(buffer), - SC_RECORD_BY_REC_NR); - if (r < 0) { - r = 0; - break; - } - - /* Check for bad record */ - if (r < 2) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "key file format error: " - "record %u too small (%u bytes)\n", - n, r); - return SC_ERROR_OBJECT_NOT_VALID; - } - - tag = buffer[0]; - - for (m = 0, found = 0; m < part->count; m++) { - pe = part->components + m; - if (pe->tag == tag) { - r = sc_update_record(p15card->card, n, - pe->data, pe->size, - SC_RECORD_BY_REC_NR); - if (r < 0) - return r; - pe->tag = 0; /* mark as stored */ - found++; - break; - } - } - - if (!found) - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "GPK unknown PK tag %u\n", tag); - } - - /* Write all remaining elements */ - for (m = 0; r >= 0 && m < part->count; m++) { - pe = part->components + m; - if (pe->tag != 0) - r = sc_append_record(p15card->card, pe->data, pe->size, 0); - } - - return r; -} - -static int -gpk_pkfile_init_private(sc_card_t *card, - sc_file_t *file, unsigned int privlen) -{ - struct sc_cardctl_gpk_pkinit args; - - args.file = file; - args.privlen = privlen; - return sc_card_ctl(card, SC_CARDCTL_GPK_PKINIT, &args); -} - -static int -gpk_pkfile_load_private(sc_card_t *card, sc_file_t *file, - u8 *data, unsigned int len, unsigned int datalen) -{ - struct sc_cardctl_gpk_pkload args; - - args.file = file; - args.data = data; - args.len = len; - args.datalen = datalen; - return sc_card_ctl(card, SC_CARDCTL_GPK_PKLOAD, &args); -} - -static int -gpk_pkfile_update_private(struct sc_profile *profile, - sc_pkcs15_card_t *p15card, sc_file_t *file, - struct pkpart *part) -{ - unsigned int m, size, nb, cks; - struct pkcomp *pe; - u8 data[256]; - int r = 0; - - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Updating private key elements\n"); - - for (m = 0; m < part->count; m++) { - pe = part->components + m; - - if (pe->size + 8 > sizeof(data)) - return SC_ERROR_BUFFER_TOO_SMALL; - memcpy(data, pe->data, pe->size); - size = pe->size; - - /* We must set a secure messaging key before each - * Load Private Key command. Any key will do... - * The GPK _is_ weird. */ - r = sc_pkcs15init_verify_secret(profile, p15card, NULL, SC_AC_PRO, 1); - if (r < 0) - break; - - /* Pad out data to a multiple of 8 and checksum. - * The GPK manual is a bit unclear about whether you - * checksum first and then pad, or vice versa. - * The following code does seem to work though: */ - for (nb = 0, cks = 0xff; nb < size; nb++) - cks ^= data[nb]; - data[nb++] = cks; - while (nb & 7) - data[nb++] = 0; - - r = gpk_pkfile_load_private(p15card->card, file, data, size-1, nb); - if (r < 0) - break; - } - return r; -} - -/* Sum up the size of the public key elements - * Each element is type + tag + bignum - */ -static void -gpk_compute_publen(struct pkpart *part) -{ - unsigned int n, publen = 8; /* length of sysrec0 */ - - for (n = 0; n < part->count; n++) - publen += 2 + part->components[n].size; - part->size = (publen + 3) & ~3UL; -} - -/* Sum up the size of the private key elements - * Each element is type + tag + bignum + checksum, padded to a multiple - * of eight - */ -static void -gpk_compute_privlen(struct pkpart *part) -{ - unsigned int n, privlen = 8; - - for (n = 0; n < part->count; n++) - privlen += (3 + part->components[n].size + 7) & ~7UL; - part->size = privlen; -} - -/* - * Convert BIGNUM to GPK representation, optionally zero padding to size. - * Note that the bignum's we're given are big-endian, while the GPK - * wants them little-endian. - */ -static void -gpk_bn2bin(unsigned char *dest, sc_pkcs15_bignum_t *bn, unsigned int size) -{ - u8 *src; - unsigned int n; - - assert(bn->len <= size); - memset(dest, 0, size); - for (n = bn->len, src = bn->data; n--; src++) - dest[n] = *src; -} - -/* - * Add a BIGNUM component, optionally padding out the number to size bytes - */ -static void -gpk_add_bignum(struct pkpart *part, unsigned int tag, - sc_pkcs15_bignum_t *bn, size_t size) -{ - struct pkcomp *comp; - - if (size == 0) - size = bn->len; - - comp = &part->components[part->count++]; - memset(comp, 0, sizeof(*comp)); - comp->tag = tag; - comp->size = size + 1; - comp->data = malloc(size + 1); - - /* Add the tag */ - comp->data[0] = tag; - - /* Add the BIGNUM */ - gpk_bn2bin(comp->data + 1, bn, size); - - /* printf("TAG 0x%02x, len=%u\n", tag, comp->size); */ -} - -static int gpk_encode_rsa_key(sc_profile_t *profile, sc_card_t *card, - struct sc_pkcs15_prkey_rsa *rsa, struct pkdata *p, - sc_pkcs15_prkey_info_t *info) -{ - if (!rsa->modulus.len || !rsa->exponent.len) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "incomplete RSA public key"); - return SC_ERROR_INVALID_ARGUMENTS; - } - - /* Make sure the exponent is 0x10001 because that's - * the only exponent supported by GPK4000 and GPK8000 */ - if (rsa->exponent.len != 3 - || memcmp(rsa->exponent.data, "\001\000\001", 3)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "unsupported RSA exponent"); - return SC_ERROR_INVALID_ARGUMENTS; - } - - memset(p, 0, sizeof(*p)); - p->algo = SC_ALGORITHM_RSA; - p->usage = info->usage; - p->bytes = rsa->modulus.len; - p->bits = p->bytes << 3; - - /* Set up the list of public elements */ - gpk_add_bignum(&p->_public, 0x01, &rsa->modulus, 0); - gpk_add_bignum(&p->_public, 0x07, &rsa->exponent, 0); - - /* Set up the list of private elements */ - if (!rsa->p.len || !rsa->q.len || !rsa->dmp1.len || !rsa->dmq1.len || !rsa->iqmp.len) { - /* No or incomplete CRT information */ - if (!rsa->d.len) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "incomplete RSA private key"); - return SC_ERROR_INVALID_ARGUMENTS; - } - gpk_add_bignum(&p->_private, 0x04, &rsa->d, 0); - } else if (5 * (p->bytes / 2) < 256) { - /* All CRT elements are stored in one record */ - struct pkcomp *comp; - unsigned int K = p->bytes / 2; - u8 *crtbuf; - - crtbuf = malloc(5 * K + 1); - - crtbuf[0] = 0x05; - gpk_bn2bin(crtbuf + 1 + 0 * K, &rsa->p, K); - gpk_bn2bin(crtbuf + 1 + 1 * K, &rsa->q, K); - gpk_bn2bin(crtbuf + 1 + 2 * K, &rsa->iqmp, K); - gpk_bn2bin(crtbuf + 1 + 3 * K, &rsa->dmp1, K); - gpk_bn2bin(crtbuf + 1 + 4 * K, &rsa->dmq1, K); - - comp = &p->_private.components[p->_private.count++]; - comp->tag = 0x05; - comp->size = 5 * K + 1; - comp->data = crtbuf; - } else { - /* CRT elements stored in individual records. - * Make sure they're all fixed length even if they're - * shorter */ - gpk_add_bignum(&p->_private, 0x51, &rsa->p, p->bytes/2); - gpk_add_bignum(&p->_private, 0x52, &rsa->q, p->bytes/2); - gpk_add_bignum(&p->_private, 0x53, &rsa->iqmp, p->bytes/2); - gpk_add_bignum(&p->_private, 0x54, &rsa->dmp1, p->bytes/2); - gpk_add_bignum(&p->_private, 0x55, &rsa->dmq1, p->bytes/2); - } - - return 0; -} - -/* - * Encode a DSA key. - * Confusingly, the GPK manual says that the GPK8000 can handle - * DSA with 512 as well as 1024 bits, but all byte sizes shown - * in the tables are 512 bits only... - */ -static int gpk_encode_dsa_key(sc_profile_t *profile, sc_card_t *card, - struct sc_pkcs15_prkey_dsa *dsa, struct pkdata *p, - sc_pkcs15_prkey_info_t *info) -{ - if (!dsa->p.len || !dsa->q.len || !dsa->g.len - || !dsa->pub.len || !dsa->priv.len) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "incomplete DSA public key"); - return SC_ERROR_INVALID_ARGUMENTS; - } - - memset(p, 0, sizeof(*p)); - p->algo = SC_ALGORITHM_RSA; - p->usage = info->usage; - p->bytes = dsa->q.len; - p->bits = dsa->q.len << 3; - - /* Make sure the key is either 512 or 1024 bits */ - if (p->bytes <= 64) { - p->bits = 512; - p->bytes = 64; - } else if (p->bytes <= 128) { - p->bits = 1024; - p->bytes = 128; - } else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "incompatible DSA key size (%u bits)", p->bits); - return SC_ERROR_INVALID_ARGUMENTS; - } - - /* Set up the list of public elements */ - gpk_add_bignum(&p->_public, 0x09, &dsa->p, 0); - gpk_add_bignum(&p->_public, 0x0a, &dsa->q, 0); - gpk_add_bignum(&p->_public, 0x0b, &dsa->g, 0); - gpk_add_bignum(&p->_public, 0x0c, &dsa->pub, 0); - - /* Set up the list of private elements */ - gpk_add_bignum(&p->_private, 0x0d, &dsa->priv, 0); - - return 0; -} - -static int -gpk_store_pk(struct sc_profile *profile, sc_pkcs15_card_t *p15card, - sc_file_t *file, struct pkdata *p) -{ - struct sc_context *ctx = p15card->card->ctx; - size_t fsize; - int r; - - /* Compute length of private/public key parts */ - gpk_compute_publen(&p->_public); - gpk_compute_privlen(&p->_private); - - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Storing pk: %u bits, pub %u bytes, priv %u bytes\n", - p->bits, p->_public.size, p->_private.size); - - fsize = p->_public.size + p->_private.size; - if (fsize > file->size) - return SC_ERROR_FILE_TOO_SMALL; - - /* Put the system record */ -#ifndef PK_INIT_IMMEDIATELY - r = gpk_pkfile_init_public(profile, p15card, file, p->algo, - p->bits, p->usage); - if (r < 0) - return r; -#endif - - /* Put the public key elements */ - r = gpk_pkfile_update_public(profile, p15card, &p->_public); - if (r < 0) - return r; - - /* Create the private key part */ -#ifndef PK_INIT_IMMEDIATELY - r = gpk_pkfile_init_private(p15card->card, file, p->_private.size); - if (r < 0) - return r; -#endif - - /* Now store the private key elements */ - r = gpk_pkfile_update_private(profile, p15card, file, &p->_private); - - return r; -} - -static int -gpk_read_rsa_key(sc_card_t *card, struct sc_pkcs15_pubkey_rsa *rsa) -{ - int n, r; - - /* Read modulus and exponent */ - for (n = 2; ; n++) { - sc_pkcs15_bignum_t *bn; - u8 buffer[256]; - size_t m; - - r = sc_read_record(card, n, buffer, sizeof(buffer), - SC_RECORD_BY_REC_NR); - if (r < 1) - break; - - if (buffer[0] == 0x01) - bn = &rsa->modulus; - else if (buffer[0] == 0x07) - bn = &rsa->exponent; - else - continue; - bn->len = r - 1; - bn->data = malloc(bn->len); - for (m = 0; m < bn->len; m++) - bn->data[m] = buffer[bn->len - m]; - } - - return 0; -} - -static struct sc_pkcs15init_operations sc_pkcs15init_gpk_operations = { - gpk_erase_card, - NULL, /* init_card */ - gpk_create_dir, - NULL, /* create_domain */ - gpk_select_pin_reference, - gpk_create_pin, - NULL, /* select_key_reference */ - gpk_create_key, - gpk_store_key, - gpk_generate_key, - NULL, NULL, /* encode private/public key */ - NULL, /* finalize_card */ - NULL, /* delete_object */ - NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ - NULL /* sanity_check */ -}; - -struct sc_pkcs15init_operations *sc_pkcs15init_get_gpk_ops(void) -{ - return &sc_pkcs15init_gpk_operations; -} diff --git a/src/pkcs15init/pkcs15-iasecc.c b/src/pkcs15init/pkcs15-iasecc.c index ce2e8e3f14..dcb525e27a 100644 --- a/src/pkcs15init/pkcs15-iasecc.c +++ b/src/pkcs15init/pkcs15-iasecc.c @@ -17,13 +17,17 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include #endif +#ifndef FIX_UNUSED +#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ +#endif + #ifdef ENABLE_OPENSSL /* empty file without openssl */ #include @@ -33,16 +37,6 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "../libopensc/opensc.h" #include "../libopensc/cardctl.h" #include "../libopensc/log.h" @@ -93,7 +87,7 @@ iasecc_pkcs15_delete_file(struct sc_pkcs15_card *p15card, struct sc_profile *pro rv = sc_pkcs15init_authenticate(profile, p15card, df, SC_AC_OP_DELETE); card->caps = caps; - LOG_TEST_RET(ctx, rv, "Cannnot authenticate SC_AC_OP_DELETE"); + LOG_TEST_RET(ctx, rv, "Cannot authenticate SC_AC_OP_DELETE"); memset(&path, 0, sizeof(path)); path.type = SC_PATH_TYPE_FILE_ID; @@ -121,7 +115,7 @@ iasecc_pkcs15_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15c LOG_FUNC_CALLED(ctx); - if (p15card->app->ddo.aid.len) { + if (p15card->app && p15card->app->ddo.aid.len) { memset(&path, 0, sizeof(struct sc_path)); path.type = SC_PATH_TYPE_DF_NAME; memcpy(path.value, p15card->app->ddo.aid.value, p15card->app->ddo.aid.len); @@ -162,6 +156,7 @@ iasecc_pkcs15_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15c } sc_pkcs15_remove_object(p15card, objs[ii]); + sc_pkcs15_free_object(objs[ii]); } rv = sc_select_file(p15card->card, &df->path, &file); @@ -272,6 +267,7 @@ iasecc_pkcs15_select_key_reference(struct sc_profile *profile, struct sc_pkcs15_ sc_print_path(&key_info->path)); rv = sc_select_file(card, &key_info->path, &file); + sc_file_free(file); LOG_TEST_RET(ctx, rv, "Cannot select DF to select key reference in"); /* 1 <= ObjReference <= 31 */ @@ -302,7 +298,6 @@ iasecc_pkcs15_select_key_reference(struct sc_profile *profile, struct sc_pkcs15_ key_info->key_reference = idx | IASECC_OBJECT_REF_LOCAL; sc_log(ctx, "selected key reference %i", key_info->key_reference); - sc_file_free(file); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -335,7 +330,8 @@ iasecc_file_convert_acls(struct sc_context *ctx, struct sc_profile *profile, str if (acl) { switch (acl->method) { case SC_AC_IDA: - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "'IDA' not actually supported"); + sc_log(ctx, "'IDA' not actually supported"); + return SC_ERROR_NOT_SUPPORTED; case SC_AC_SCB: if ((acl->key_ref & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_USER_AUTH) { acl->method = SC_AC_SEN; @@ -373,7 +369,7 @@ iasecc_sdo_set_key_acls_from_profile(struct sc_profile *profile, struct sc_card /* Get ACLs from profile template */ rv = sc_profile_get_file(profile, template, &file); - LOG_TEST_RET(ctx, rv, "IasEcc: cannot instanciate private key file"); + LOG_TEST_RET(ctx, rv, "IasEcc: cannot instantiate private key file"); /* Convert PKCS15 ACLs to SE ACLs */ rv = iasecc_file_convert_acls(ctx, profile, file); @@ -465,25 +461,33 @@ iasecc_sdo_allocate_prvkey(struct sc_profile *profile, struct sc_card *card, sdo->not_on_card = 1; rv = iasecc_sdo_set_key_acls_from_profile(profile, card, "private-key", sdo); + if (rv != SC_SUCCESS) + iasecc_sdo_free(card, sdo); LOG_TEST_RET(ctx, rv, "IasEcc: cannot set ACLs for SDO from the 'private-key'"); /* FIXME: set here sdo->docp.name and sdo->docp.idata */ sdo->docp.non_repudiation.value = calloc(1, 1); - if (!sdo->docp.non_repudiation.value) + if (!sdo->docp.non_repudiation.value) { + iasecc_sdo_free(card, sdo); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); - sdo->docp.non_repudiation.tag = IASECC_DOCP_TAG_NON_REPUDATION; + } + sdo->docp.non_repudiation.tag = IASECC_DOCP_TAG_NON_REPUDIATION; sdo->docp.non_repudiation.size = 1; sdo->data.prv_key.compulsory.value = calloc(1, 1); - if (!sdo->data.prv_key.compulsory.value) + if (!sdo->data.prv_key.compulsory.value) { + iasecc_sdo_free(card, sdo); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + } sdo->data.prv_key.compulsory.tag = IASECC_SDO_PRVKEY_TAG_COMPULSORY; sdo->data.prv_key.compulsory.size = 1; sdo->docp.size.value = calloc(1, 2); - if (!sdo->docp.size.value) + if (!sdo->docp.size.value) { + iasecc_sdo_free(card, sdo); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + } sdo->docp.size.tag = IASECC_DOCP_TAG_SIZE; sdo->docp.size.size = 2; *(sdo->docp.size.value + 0) = (sz >> 8) & 0xFF; @@ -495,7 +499,7 @@ iasecc_sdo_allocate_prvkey(struct sc_profile *profile, struct sc_card *card, If present, this attribute has to be the same in the 'GENERATE KEY' template data. */ if (!(key_info->access_flags & SC_PKCS15_PRKEY_ACCESS_LOCAL) && (key_info->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)) - sc_log(ctx, "Non fatal error: NON_REPUDATION can be used only for the localy generated keys"); + sc_log(ctx, "Non fatal error: NON_REPUDIATION can be used only for the locally generated keys"); if ((key_info->access_flags & SC_PKCS15_PRKEY_ACCESS_LOCAL) && (key_info->usage & SC_PKCS15_PRKEY_USAGE_SIGN) @@ -507,14 +511,15 @@ iasecc_sdo_allocate_prvkey(struct sc_profile *profile, struct sc_card *card, sc_log(ctx, "non_repudiation %i", *(sdo->docp.non_repudiation.value + 0)); sc_log(ctx, "compulsory 0x%X", *(sdo->data.prv_key.compulsory.value + 0)); } - else { + else if (rv < 0) { + iasecc_sdo_free(card, sdo); LOG_TEST_RET(ctx, rv, "IasEcc: error while getting private key SDO data"); } if (out) *out = sdo; else - free(sdo); + iasecc_sdo_free(card, sdo); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -544,11 +549,15 @@ iasecc_sdo_allocate_pubkey(struct sc_profile *profile, struct sc_card *card, str sdo->not_on_card = 1; rv = iasecc_sdo_set_key_acls_from_profile(profile, card, "public-key", sdo); + if (rv != SC_SUCCESS) + iasecc_sdo_free(card, sdo); LOG_TEST_RET(ctx, rv, "iasecc_sdo_allocate_pubkey() cannot set ACLs for SDO from the 'public-key'"); sdo->docp.size.value = calloc(1, 2); - if (!sdo->docp.size.value) + if (!sdo->docp.size.value) { + iasecc_sdo_free(card, sdo); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + } sdo->docp.size.size = 2; sdo->docp.size.tag = IASECC_DOCP_TAG_SIZE; *(sdo->docp.size.value + 0) = (sz >> 8) & 0xFF; @@ -559,26 +568,31 @@ iasecc_sdo_allocate_pubkey(struct sc_profile *profile, struct sc_card *card, str } else { sdo->data.pub_key.cha.value = calloc(1, 2); - if (!sdo->data.pub_key.cha.value) + if (!sdo->data.pub_key.cha.value) { + iasecc_sdo_free(card, sdo); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + } sdo->data.pub_key.cha.size = 2; sdo->data.pub_key.cha.tag = IASECC_SDO_PUBKEY_TAG_CHA; } sdo->data.pub_key.compulsory.value = calloc(1, 1); - if (!sdo->data.pub_key.compulsory.value) + if (!sdo->data.pub_key.compulsory.value) { + iasecc_sdo_free(card, sdo); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + } sdo->data.pub_key.compulsory.tag = IASECC_SDO_PUBKEY_TAG_COMPULSORY; sdo->data.pub_key.compulsory.size = 1; } - else { + else if (rv < 0) { + iasecc_sdo_free(card, sdo); LOG_TEST_RET(ctx, rv, "iasecc_sdo_allocate_pubkey() error while getting public key SDO data"); } if (out) *out = sdo; else - free(sdo); + iasecc_sdo_free(card, sdo); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -777,7 +791,7 @@ iasecc_pkcs15_fix_file_access(struct sc_pkcs15_card *p15card, struct sc_file *fi } -static int +int iasecc_pkcs15_encode_supported_algos(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; @@ -796,7 +810,8 @@ iasecc_pkcs15_encode_supported_algos(struct sc_pkcs15_card *p15card, struct sc_p LOG_TEST_RET(ctx, rv, "cannot add supported algorithm DECIPHER:CKM_RSA_PKCS"); } - if (prkey_info->usage & SC_PKCS15_PRKEY_USAGE_SIGN) { + if (prkey_info->usage & (SC_PKCS15_PRKEY_USAGE_SIGN | + SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)) { if (prkey_info->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) { algo = sc_pkcs15_get_supported_algo(p15card, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_SHA1_RSA_PKCS); rv = sc_pkcs15_add_supported_algo_ref(object, algo); @@ -1073,7 +1088,7 @@ iasecc_pkcs15_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p15c sc_log(ctx, "iasecc_pkcs15_create_key() sdo_pubkey->not_on_card %i", sdo_pubkey->not_on_card); rv = iasecc_sdo_allocate_prvkey(profile, card, key_info, &sdo_prvkey); - LOG_TEST_RET(ctx, rv, "IasEcc: init SDO private key failed"); + LOG_TEST_GOTO_ERR(ctx, rv, "IasEcc: init SDO private key failed"); sc_log(ctx, "iasecc_pkcs15_create_key() sdo_prvkey->not_on_card %i", sdo_prvkey->not_on_card); if (!sdo_prvkey->not_on_card && !sdo_pubkey->not_on_card) { @@ -1081,18 +1096,20 @@ iasecc_pkcs15_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p15c } else { rv = iasecc_pkcs15_create_key_slot(profile, p15card, sdo_prvkey, sdo_pubkey, key_info); - LOG_TEST_RET(ctx, rv, "Cannot create key slot"); + LOG_TEST_GOTO_ERR(ctx, rv, "Cannot create key slot"); } rv = sc_pkcs15_allocate_object_content(ctx, object, (unsigned char *)sdo_prvkey, sizeof(struct iasecc_sdo)); - LOG_TEST_RET(ctx, rv, "Failed to allocate PrvKey SDO as object content"); + LOG_TEST_GOTO_ERR(ctx, rv, "Failed to allocate PrvKey SDO as object content"); rv = iasecc_pkcs15_fix_private_key_attributes(profile, p15card, object, (struct iasecc_sdo *)object->content.value); - LOG_TEST_RET(ctx, rv, "Failed to fix private key PKCS#15 attributes"); + LOG_TEST_GOTO_ERR(ctx, rv, "Failed to fix private key PKCS#15 attributes"); key_info->path.len = 0; +err: iasecc_sdo_free(card, sdo_pubkey); + iasecc_sdo_free(card, sdo_prvkey); LOG_FUNC_RETURN(ctx, rv); } @@ -1136,7 +1153,7 @@ iasecc_pkcs15_generate_key(struct sc_profile *profile, sc_pkcs15_card_t *p15card return SC_ERROR_INVALID_ARGUMENTS; } - /* TODO: Check if native IAS middleware accepts the meaningfull path value. */ + /* TODO: Check if native IAS middleware accepts the meaningful path value. */ rv = sc_profile_get_parent(profile, "private-key", &file); LOG_TEST_RET(ctx, rv, "IasEcc: cannot get private key parent file"); @@ -1467,7 +1484,7 @@ iasecc_md_gemalto_unset_default(struct sc_pkcs15_card *p15card, struct sc_profil struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)key_obj->data; unsigned char guid[40]; size_t guid_len; - int rv, ii, keys_num; + int rv, ii, keys_num, private_obj; LOG_FUNC_CALLED(ctx); @@ -1481,11 +1498,15 @@ iasecc_md_gemalto_unset_default(struct sc_pkcs15_card *p15card, struct sc_profil if (rv == SC_ERROR_OBJECT_NOT_FOUND) LOG_FUNC_RETURN(ctx, SC_SUCCESS); - rv = sc_pkcs15_read_data_object(p15card, (struct sc_pkcs15_data_info *)data_obj->data, &dod); + private_obj = data_obj->flags & SC_PKCS15_CO_FLAG_PRIVATE; + rv = sc_pkcs15_read_data_object(p15card, (struct sc_pkcs15_data_info *)data_obj->data, private_obj, &dod); LOG_TEST_RET(ctx, rv, "Cannot read from 'CSP/'Default Key Container'"); - if (guid_len != dod->data_len || memcmp(guid, dod->data, guid_len)) + if (guid_len != dod->data_len || memcmp(guid, dod->data, guid_len)) { + sc_pkcs15_free_data_object(dod); LOG_FUNC_RETURN(ctx, SC_SUCCESS); + } + sc_pkcs15_free_data_object(dod); rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, key_objs, 32); LOG_TEST_RET(ctx, rv, "Get private key PKCS#15 objects error"); @@ -1659,7 +1680,8 @@ iasecc_store_pubkey(struct sc_pkcs15_card *p15card, struct sc_profile *profile, pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT ? SC_PKCS15_PRKEY_USAGE_ENCRYPT : 0; pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_UNWRAP ? SC_PKCS15_PRKEY_USAGE_WRAP : 0; - iasecc_pkcs15_add_access_rule(object, SC_PKCS15_ACCESS_RULE_MODE_READ, NULL); + rv = iasecc_pkcs15_add_access_rule(object, SC_PKCS15_ACCESS_RULE_MODE_READ, NULL); + LOG_TEST_RET(ctx, rv, "Too many access rules"); memcpy(&pubkey_info->algo_refs[0], &prkey_info->algo_refs[0], sizeof(pubkey_info->algo_refs)); @@ -1712,15 +1734,15 @@ iasecc_store_data_object(struct sc_pkcs15_card *p15card, struct sc_profile *prof struct sc_card *card = p15card->card; struct sc_pkcs15_object *p15objects[MAX_DATA_OBJS]; struct sc_file *cfile = NULL, *file = NULL, *parent = NULL; - int rv, nn_objs, indx, ii; + int rv, nn_objs, index, ii; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_store_data_object() authID '%s'", sc_pkcs15_print_id(&object->auth_id)); nn_objs = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_DATA_OBJECT, p15objects, MAX_DATA_OBJS); LOG_TEST_RET(ctx, nn_objs, "IasEcc get pkcs15 DATA objects error"); - for(indx = 1; indx < MAX_DATA_OBJS; indx++) { - rv = iasecc_pkcs15_new_file(profile, card, SC_PKCS15_TYPE_DATA_OBJECT, indx, &file); + for(index = 1; index < MAX_DATA_OBJS; index++) { + rv = iasecc_pkcs15_new_file(profile, card, SC_PKCS15_TYPE_DATA_OBJECT, index, &file); LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() pkcs15 new DATA file error"); for (ii=0; iipath)); rv = sc_select_file(card, &parent->path, NULL); - LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() cannot select parent"); + LOG_TEST_GOTO_ERR(ctx, rv, "iasecc_store_data_object() cannot select parent"); rv = sc_select_file(card, &file->path, &cfile); if (!rv) { rv = sc_pkcs15init_authenticate(profile, p15card, cfile, SC_AC_OP_DELETE); - LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() DELETE authentication failed"); + LOG_TEST_GOTO_ERR(ctx, rv, "iasecc_store_data_object() DELETE authentication failed"); rv = iasecc_pkcs15_delete_file(p15card, profile, cfile); - LOG_TEST_RET(ctx, rv, "s_pkcs15init_store_data_object() delete pkcs15 file error"); + LOG_TEST_GOTO_ERR(ctx, rv, "s_pkcs15init_store_data_object() delete pkcs15 file error"); } else if (rv != SC_ERROR_FILE_NOT_FOUND) { - LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() select file error"); + LOG_TEST_GOTO_ERR(ctx, rv, "iasecc_store_data_object() select file error"); } rv = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_CREATE); - LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() parent CREATE authentication failed"); + LOG_TEST_GOTO_ERR(ctx, rv, "iasecc_store_data_object() parent CREATE authentication failed"); file->size = data->len; rv = sc_create_file(card, file); - LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() cannot create DATA file"); + LOG_TEST_GOTO_ERR(ctx, rv, "iasecc_store_data_object() cannot create DATA file"); rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); - LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() data file UPDATE authentication failed"); + LOG_TEST_GOTO_ERR(ctx, rv, "iasecc_store_data_object() data file UPDATE authentication failed"); rv = sc_update_binary(card, 0, data->value, data->len, 0); - LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() update DATA file failed"); + LOG_TEST_GOTO_ERR(ctx, rv, "iasecc_store_data_object() update DATA file failed"); if (path) *path = file->path; - +err: sc_file_free(parent); sc_file_free(file); sc_file_free(cfile); @@ -1876,4 +1899,19 @@ sc_pkcs15init_get_iasecc_ops(void) return &sc_pkcs15init_iasecc_operations; } +#else /* ENABLE_OPENSSL */ +#include "../libopensc/log.h" +#include "pkcs15-init.h" + +int +iasecc_pkcs15_encode_supported_algos(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) +{ + struct sc_context *ctx = p15card->card->ctx; + FIX_UNUSED(object); + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "OpenSC was built without OpenSSL support: skipping"); + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_IMPLEMENTED); +} + #endif /* ENABLE_OPENSSL */ diff --git a/src/pkcs15init/pkcs15-iasecc.h b/src/pkcs15init/pkcs15-iasecc.h new file mode 100644 index 0000000000..7b81e41f00 --- /dev/null +++ b/src/pkcs15init/pkcs15-iasecc.h @@ -0,0 +1,25 @@ +/* + * pkcs15-iasecc.h Support for IAS/ECC smart cards + * + * Copyright (C) 2021 Vincent JARDIN + * + * 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 + */ + +#ifndef pkcs15_iasecc_h +#define pkcs15_iasecc_h + +extern int iasecc_pkcs15_encode_supported_algos(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object); +#endif /* #ifndef pkcs15_iasecc_h*/ diff --git a/src/pkcs15init/pkcs15-incrypto34.c b/src/pkcs15init/pkcs15-incrypto34.c deleted file mode 100644 index 7ecac0bb58..0000000000 --- a/src/pkcs15init/pkcs15-incrypto34.c +++ /dev/null @@ -1,700 +0,0 @@ -/* - * Incrypto34 specific operation for PKCS15 initialization - * - * Copyright (C) 2005 ST Incard srl, Giuseppe Amato - * Copyright (C) 2002 Olaf Kirch - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include "libopensc/opensc.h" -#include "libopensc/cardctl.h" -#include "libopensc/log.h" -#include "pkcs15-init.h" -#include "profile.h" - -#ifndef MIN -# define MIN(a, b) (((a) < (b))? (a) : (b)) -#endif - -struct tlv { - unsigned char * base; - unsigned char * end; - unsigned char * current; - unsigned char * next; -}; -#define RSAKEY_MAX_BITS 1024 -#define RSAKEY_MAX_SIZE (RSAKEY_MAX_BITS/8) -struct rsakey { - struct bignum { - size_t len; - u8 data[RSAKEY_MAX_SIZE]; - } n, d; -}; - -/* - * Local functions - */ -static int incrypto34_store_pin(sc_profile_t *profile, sc_card_t *card, - sc_pkcs15_auth_info_t *auth_info, int puk_id, - const u8 *pin, size_t pin_len); -static int incrypto34_create_sec_env(sc_profile_t *, sc_card_t *, - unsigned int, unsigned int); -static int incrypto34_put_key(struct sc_profile *, struct sc_pkcs15_card *, - int, sc_pkcs15_prkey_info_t *, - struct sc_pkcs15_prkey_rsa *); -static int incrypto34_key_algorithm(unsigned int, int *); -static int incrypto34_extract_pubkey(sc_card_t *, int, - u8, sc_pkcs15_bignum_t *); - -/* Object IDs for PIN objects. - * SO PIN = 0x01, SO PUK = 0x02 - * each user pin is 2*N+1, each corresponding PUK is 2*N+2 - */ -#define INCRYPTO34_PIN_ID_MIN 1 -#define INCRYPTO34_PIN_ID_MAX 15 -#define INCRYPTO34_KEY_ID_MIN 16 -#define INCRYPTO34_KEY_ID_MAX 31 -#define INCRYPTO34_AC_NEVER 0xFF - -#define INCRYPTO34_ALGO_RSA 0x08 -#define INCRYPTO34_ALGO_RSA_PURE 0x0C -#define INCRYPTO34_ALGO_RSA_SIG 0x88 -#define INCRYPTO34_ALGO_RSA_PURE_SIG 0x8C -#define INCRYPTO34_ALGO_RSA_SIG_SHA1 0xC8 -#define INCRYPTO34_ALGO_RSA_PURE_SIG_SHA1 0xCC -#define INCRYPTO34_SIGN_RSA INCRYPTO34_ALGO_RSA_SIG -#define INCRYPTO34_DECIPHER_RSA INCRYPTO34_ALGO_RSA_PURE -#define INCRYPTO34_ALGO_PIN 0x87 - -static void tlv_init(struct tlv *tlv, u8 *base, size_t size) -{ - tlv->base = base; - tlv->end = base + size; - tlv->current = tlv->next = base; -} - -static void tlv_next(struct tlv *tlv, u8 tag) -{ - assert(tlv->next + 2 < tlv->end); - tlv->current = tlv->next; - *(tlv->next++) = tag; - *(tlv->next++) = 0; -} - -static void tlv_add(struct tlv *tlv, u8 val) -{ - assert(tlv->next + 1 < tlv->end); - *(tlv->next++) = val; - tlv->current[1]++; -} - -static size_t -tlv_len(struct tlv *tlv) -{ - return tlv->next - tlv->base; -} - -/* - * Try to delete pkcs15 structure - * This is not quite the same as erasing the whole token, but - * it's close enough to be useful. - */ -static int -incrypto34_erase(struct sc_profile *profile, sc_pkcs15_card_t *p15card) -{ - int r; - struct sc_file *file; - struct sc_path path; - memset(&file, 0, sizeof(file)); - sc_format_path("3F00", &path); - if ((r = sc_select_file(p15card->card, &path, &file)) < 0) - return r; - if (sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_DELETE) < 0) - return sc_pkcs15init_erase_card_recursively(p15card, profile); - else - return sc_card_ctl(p15card->card, SC_CARDCTL_INCRYPTO34_ERASE_FILES, NULL); -} - -/* - * Create the Application DF - */ -static int -incrypto34_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) -{ - int r; - struct sc_file *file; - struct sc_path path; - memset(&file, 0, sizeof(file)); - sc_format_path("3F00", &path); - if ((r = sc_select_file(p15card->card, &path, &file)) < 0) - return r; - if ((r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_CREATE)) < 0) - return r; - /* Create the application DF */ - if ((r = sc_pkcs15init_create_file(profile, p15card, df)) < 0) - return r; - - if ((r = sc_select_file(p15card->card, &df->path, NULL)) < 0) - return r; - - /* Create a security environment for this DF. - */ - if ((r = incrypto34_create_sec_env(profile, p15card->card, 0x01, 0x00)) < 0) - return r; - - return 0; -} - -/* - * Caller passes in a suggested PIN reference. - * See if it's good, and if it isn't, propose something better - */ -static int -incrypto34_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, - sc_pkcs15_auth_info_t *auth_info) -{ - int preferred, current; - - if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) - return SC_ERROR_OBJECT_NOT_VALID; - - if ((current = auth_info->attrs.pin.reference) < 0) - current = INCRYPTO34_PIN_ID_MIN; - - if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) { - preferred = 1; - if (current > preferred) - return SC_ERROR_TOO_MANY_OBJECTS; - } else { - preferred = current; - /* PINs are even numbered, PUKs are odd */ - if (!(preferred & 1)) - preferred++; - } - - if (preferred > INCRYPTO34_PIN_ID_MAX) - return SC_ERROR_TOO_MANY_OBJECTS; - auth_info->attrs.pin.reference = preferred; - - return SC_SUCCESS; -} - -/* - * Store a PIN - */ -static int -incrypto34_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, - sc_file_t *df, sc_pkcs15_object_t *pin_obj, - const u8 *pin, size_t pin_len, - const u8 *puk, size_t puk_len) -{ - sc_pkcs15_auth_info_t *auth_info = (sc_pkcs15_auth_info_t *) pin_obj->data; - unsigned int puk_id = INCRYPTO34_AC_NEVER; - int r; - - if (!pin || !pin_len) - return SC_ERROR_INVALID_ARGUMENTS; - - if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) - return SC_ERROR_OBJECT_NOT_VALID; - - r = sc_select_file(p15card->card, &df->path, NULL); - if (r < 0) - return r; - - if (puk && puk_len) { - struct sc_pkcs15_auth_info puk_ainfo; - - sc_profile_get_pin_info(profile, - SC_PKCS15INIT_USER_PUK, &puk_ainfo); - puk_ainfo.attrs.pin.reference = puk_id = auth_info->attrs.pin.reference + 1; - r = incrypto34_store_pin(profile, p15card->card, - &puk_ainfo, INCRYPTO34_AC_NEVER, - puk, puk_len); - } - - if (r >= 0) { - r = incrypto34_store_pin(profile, p15card->card, - auth_info, puk_id, - pin, pin_len); - } - - return r; -} - -/* - * Select a key reference - */ -static int -incrypto34_select_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, - sc_pkcs15_prkey_info_t *key_info) -{ - if (key_info->key_reference < INCRYPTO34_KEY_ID_MIN) - key_info->key_reference = INCRYPTO34_KEY_ID_MIN; - if (key_info->key_reference > INCRYPTO34_KEY_ID_MAX) - return SC_ERROR_TOO_MANY_OBJECTS; - return 0; -} - -/* - * Create a private key object. - * This is a no-op. - */ -static int -incrypto34_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, - sc_pkcs15_object_t *obj) -{ - return 0; -} - -/* - * Store a private key object. - */ -static int -incrypto34_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, - sc_pkcs15_object_t *obj, - sc_pkcs15_prkey_t *key) -{ - sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; - sc_card_t *card = p15card->card; - int algorithm, r; - - if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Incrypto34 supports RSA keys only."); - return SC_ERROR_NOT_SUPPORTED; - } - - if (incrypto34_key_algorithm(key_info->usage, &algorithm) < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Incrypto34 does not support keys " - "that can both sign _and_ decrypt."); - return SC_ERROR_NOT_SUPPORTED; - } - - r = incrypto34_put_key(profile, p15card, algorithm, key_info, &key->u.rsa); - - return r; -} - -/* - * Key generation - */ -static int -incrypto34_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, - sc_pkcs15_object_t *obj, - sc_pkcs15_pubkey_t *pubkey) -{ - sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; - sc_card_t *card = p15card->card; - struct sc_pkcs15_prkey_rsa key_obj; - struct sc_cardctl_incrypto34_genkey_info args; - struct sc_file *temp; - u8 abignum[RSAKEY_MAX_SIZE]; - unsigned int keybits; - int algorithm, r, delete_it = 0; - - if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Incrypto34 supports only RSA keys."); - return SC_ERROR_NOT_SUPPORTED; - } - - if (incrypto34_key_algorithm(key_info->usage, &algorithm) < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Incrypto34 does not support keys " - "that can both sign _and_ decrypt."); - return SC_ERROR_NOT_SUPPORTED; - } - - keybits = key_info->modulus_length & ~7UL; - if (keybits > RSAKEY_MAX_BITS) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unable to generate key, max size is %d", - RSAKEY_MAX_BITS); - return SC_ERROR_INVALID_ARGUMENTS; - } - - if (sc_profile_get_file(profile, "tempfile", &temp) < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Profile doesn't define temporary file " - "for key generation."); - return SC_ERROR_NOT_SUPPORTED; - } - memset(pubkey, 0, sizeof(*pubkey)); - - if ((r = sc_pkcs15init_create_file(profile, p15card, temp)) < 0) - goto out; - delete_it = 1; - - /* Create a key object, initializing components to 0xff */ - memset(&key_obj, 0, sizeof(key_obj)); - memset(abignum, 0xFF, sizeof(abignum)); - key_obj.modulus.data = abignum; - key_obj.modulus.len = keybits >> 3; - key_obj.d.data = abignum; - key_obj.d.len = keybits >> 3; - r = incrypto34_put_key(profile, p15card, algorithm, key_info, &key_obj); - if (r < 0) - goto out; - - memset(&args, 0, sizeof(args)); - args.key_id = key_info->key_reference; - args.key_bits = keybits; - args.fid = temp->id; - r = sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_GENERATE_KEY, &args); - if (r < 0) - goto out; - - /* extract public key from file and delete it */ - if ((r = sc_select_file(card, &temp->path, NULL)) < 0) - goto out; - r = incrypto34_extract_pubkey(card, 1, 0x10, &pubkey->u.rsa.modulus); - if (r < 0) - goto out; - r = incrypto34_extract_pubkey(card, 2, 0x11, &pubkey->u.rsa.exponent); - if (r < 0) - goto out; - pubkey->algorithm = SC_ALGORITHM_RSA; - -out: if (delete_it) { - sc_pkcs15init_rmdir(p15card, profile, temp); - } - sc_file_free(temp); - if (r < 0) { - if (pubkey->u.rsa.modulus.data) - free (pubkey->u.rsa.modulus.data); - if (pubkey->u.rsa.exponent.data) - free (pubkey->u.rsa.exponent.data); - } - return r; -} - -/* - * Store a PIN or PUK - */ -static int -incrypto34_store_pin(sc_profile_t *profile, sc_card_t *card, - sc_pkcs15_auth_info_t *auth_info, int puk_id, - const u8 *pin, size_t pin_len) -{ - struct sc_cardctl_incrypto34_obj_info args; - unsigned char buffer[256]; - unsigned char pinpadded[16]; - struct tlv tlv; - unsigned int attempts, minlen, maxlen; - - if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) - return SC_ERROR_OBJECT_NOT_VALID; - - /* We need to do padding because pkcs15-lib.c does it. - * Would be nice to have a flag in the profile that says - * "no padding required". */ - maxlen = MIN(profile->pin_maxlen, sizeof(pinpadded)); - if (pin_len > maxlen) - pin_len = maxlen; - memcpy(pinpadded, pin, pin_len); - while (pin_len < maxlen) - pinpadded[pin_len++] = profile->pin_pad_char; - pin = pinpadded; - - attempts = auth_info->tries_left; - minlen = auth_info->attrs.pin.min_length; - - tlv_init(&tlv, buffer, sizeof(buffer)); - - /* object address: class, id */ - tlv_next(&tlv, 0x83); - tlv_add(&tlv, 0x00); /* class byte: usage TEST, k=0 */ - tlv_add(&tlv, auth_info->attrs.pin.reference); - - /* parameters */ - tlv_next(&tlv, 0x85); - tlv_add(&tlv, 0x02); /* options byte */ - tlv_add(&tlv, attempts & 0xf); /* flags byte */ - tlv_add(&tlv, INCRYPTO34_ALGO_PIN); /* algorithm = pin-test */ - tlv_add(&tlv, attempts & 0xf); /* errcount = attempts */ - - /* usecount: not documented, but seems to work like this: - * - value of 0xff means pin can be presented any number - * of times - * - anything less: max # of times before BS object is blocked. - */ - tlv_add(&tlv, 0xff); - - /* DEK: RFU */ - tlv_add(&tlv, 0x00); - - /* ARA counter: the number of times the PIN can be used before the he must be verified - again (0 or ff for unlimited usage) */ - tlv_add(&tlv, 0x00); - - tlv_add(&tlv, minlen); /* minlen */ - - /* AC conditions */ - tlv_next(&tlv, 0x86); - tlv_add(&tlv, 0x00); /* use: always */ - tlv_add(&tlv, auth_info->attrs.pin.reference); /* change: PIN */ - tlv_add(&tlv, puk_id); /* unblock: PUK */ - tlv_add(&tlv, 0xFF); /*RFU*/ - tlv_add(&tlv, 0xFF); /*RFU*/ - tlv_add(&tlv, 0xFF); /*RFU*/ - tlv_add(&tlv, 0xFF); /*unused on pins*/ - tlv_add(&tlv, 0xFF); /*RFU*/ - tlv_add(&tlv, 0xFF); /*RFU*/ - tlv_add(&tlv, 0xFF); /*RFU*/ - - - /* data: PIN */ - tlv_next(&tlv, 0x8f); - while (pin_len--) - tlv_add(&tlv, *pin++); - - args.data = buffer; - args.len = tlv_len(&tlv); - - return sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_PUT_DATA_OCI, &args); -} - -/* - * Create an empty security environment - */ -static int -incrypto34_create_sec_env(struct sc_profile *profile, struct sc_card *card, - unsigned int se_id, unsigned int key_id) -{ - struct sc_cardctl_incrypto34_obj_info args; - struct tlv tlv; - unsigned char buffer[64]; - - tlv_init(&tlv, buffer, sizeof(buffer)); - tlv_next(&tlv, 0x83); - tlv_add(&tlv, se_id); - - tlv_next(&tlv, 0x86); - tlv_add(&tlv, 0); - tlv_add(&tlv, 0); - - tlv_next(&tlv, 0x8f); - tlv_add(&tlv, key_id); - tlv_add(&tlv, key_id); - tlv_add(&tlv, key_id); - tlv_add(&tlv, key_id); - tlv_add(&tlv, key_id); - tlv_add(&tlv, key_id); - - args.data = buffer; - args.len = tlv_len(&tlv); - return sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_PUT_DATA_SECI, &args); -} - -/* - * Determine the key algorithm based on the intended usage - * Note that Incrypto34 does not support keys that can be used - * for signing _and_ decipherment - */ -#define USAGE_ANY_SIGN (SC_PKCS15_PRKEY_USAGE_SIGN|\ - SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) -#define USAGE_ANY_DECIPHER (SC_PKCS15_PRKEY_USAGE_DECRYPT|\ - SC_PKCS15_PRKEY_USAGE_UNWRAP) - -static int -incrypto34_key_algorithm(unsigned int usage, int *algop) -{ - int sign = 0, decipher = 0; - - if (usage & USAGE_ANY_SIGN) { - *algop = INCRYPTO34_SIGN_RSA; - sign = 1; - } - if (usage & USAGE_ANY_DECIPHER) { - *algop = INCRYPTO34_DECIPHER_RSA; - decipher = 1; - } - return (sign == decipher)? -1 : 0; -} - -static int -incrypto34_change_key_data(struct sc_card *card, - unsigned int key_id, - unsigned int num, - const u8 *data, size_t len) -{ - struct sc_cardctl_incrypto34_obj_info args; - args.data = (u8 *)data; - args.len = len; - args.key_id = key_id; - args.key_class = num; - return sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_CHANGE_KEY_DATA, &args); -} - -/* - * Create a private key object - */ -#define INCRYPTO34_KEY_OPTIONS 0x02 -#define INCRYPTO34_KEY_FLAGS 0x00 -static int -incrypto34_store_key_component(struct sc_card *card, - int algorithm, - unsigned int key_id, unsigned int pin_id, - unsigned int num, - const u8 *data, size_t len, - int last) -{ - int r; - struct sc_cardctl_incrypto34_obj_info args; - struct tlv tlv; - unsigned char buffer[256]; - unsigned int n; - - /* Initialize the TLV encoder */ - tlv_init(&tlv, buffer, sizeof(buffer)); - - /* Object address */ - tlv_next(&tlv, 0x83); - tlv_add(&tlv, 0x20|num); /* PSO, n-th component */ - tlv_add(&tlv, key_id); - - /* Object parameters */ - tlv_next(&tlv, 0x85); - tlv_add(&tlv, INCRYPTO34_KEY_OPTIONS|(last? 0x00 : 0x20)); - tlv_add(&tlv, INCRYPTO34_KEY_FLAGS); - tlv_add(&tlv, algorithm); - tlv_add(&tlv, 0x0F); /* Error Counter*/ - tlv_add(&tlv, 0xFF); /* use count */ - tlv_add(&tlv, 0xFF); /* RFU */ - tlv_add(&tlv, 0x00); /* RFU */ - tlv_add(&tlv, 0x00); /* RFU */ - - /* AC bytes */ - tlv_next(&tlv, 0x86); - tlv_add(&tlv, pin_id); /* AC USE */ - tlv_add(&tlv, pin_id); /* AC CHANGE */ - tlv_add(&tlv, 0xFF); /* AC_UNBLOCK */ - tlv_add(&tlv, 0xFF); /* RFU */ - tlv_add(&tlv, 0xFF); /* RFU */ - tlv_add(&tlv, 0xFF); /* RFU */ - tlv_add(&tlv, 0); /* AC_GENKEY */ - tlv_add(&tlv, 0xFF); /* RFU */ - tlv_add(&tlv, 0xFF); /* RFU */ - tlv_add(&tlv, 0xFF); /* RFU */ - - /* SM bytes */ - tlv_next(&tlv, 0x8B); - for (n = 0; n < 16; n++) - tlv_add(&tlv, 0xFF); - - /* key component */ - tlv_next(&tlv, 0x8f); - tlv_add(&tlv, len+1); - tlv_add(&tlv, 0); - while (len--) - tlv_add(&tlv, *data++); - - - args.data = buffer; - args.len = tlv_len(&tlv); - r = sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_PUT_DATA_OCI, &args); - return r; -} - -static int -incrypto34_put_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, - int algorithm, sc_pkcs15_prkey_info_t *key_info, - struct sc_pkcs15_prkey_rsa *key) -{ - int r, key_id, pin_id; - - key_id = key_info->key_reference; - pin_id = sc_pkcs15init_get_pin_reference(p15card, profile, - SC_AC_SYMBOLIC, SC_PKCS15INIT_USER_PIN); - if (pin_id < 0) - pin_id = 0; - - r = incrypto34_store_key_component(p15card->card, algorithm, key_id, pin_id, 0, - key->modulus.data, key->modulus.len, 0); - if (r >= 0) - { - r = incrypto34_store_key_component(p15card->card, algorithm, key_id, pin_id, 1, - key->d.data, key->d.len, 1); - } - - if (SC_ERROR_FILE_ALREADY_EXISTS == r || r >=0) - { - r = incrypto34_change_key_data(p15card->card, 0x80|key_id, 0x20, key->modulus.data, key->modulus.len); - if (r < 0) - return r; - r = incrypto34_change_key_data(p15card->card, 0x80|key_id, 0x21, key->d.data, key->d.len); - } - - return r; -} - -/* - * Extract a key component from the public key file populated by - * GENERATE KEY PAIR - */ -static int -incrypto34_extract_pubkey(sc_card_t *card, int nr, u8 tag, - sc_pkcs15_bignum_t *bn) -{ - u8 buf[256]; - int r, count; - - r = sc_read_record(card, nr, buf, sizeof(buf), SC_RECORD_BY_REC_NR); - if (r < 0) - return r; - count = r - 4; - if (count <= 0 || buf[0] != tag || buf[1] != count + 2 - || buf[2] != count + 1 || buf[3] != 0) - return SC_ERROR_INTERNAL; - bn->len = count; - bn->data = malloc(count); - memcpy(bn->data, buf + 4, count); - return 0; -} - -static int incrypto34_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) -{ - return 0; -} -static struct sc_pkcs15init_operations sc_pkcs15init_incrypto34_operations; - - -static struct sc_pkcs15init_operations sc_pkcs15init_incrypto34_operations = { - incrypto34_erase, - incrypto34_init_card, /* init_card */ - incrypto34_create_dir, - NULL, /* create_domain */ - incrypto34_select_pin_reference, - incrypto34_create_pin, - incrypto34_select_key_reference, - incrypto34_create_key, - incrypto34_store_key, - incrypto34_generate_key, - NULL, NULL, /* encode private/public key */ - NULL, /* finalize_card */ - NULL, /* delete_object */ - NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ - NULL /* sanity_check */ -}; -struct sc_pkcs15init_operations * -sc_pkcs15init_get_incrypto34_ops(void) -{ - return &sc_pkcs15init_incrypto34_operations; -} diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h index 44ab63525a..93dcd0b22f 100644 --- a/src/pkcs15init/pkcs15-init.h +++ b/src/pkcs15init/pkcs15-init.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PKCS15_INIT_H @@ -224,10 +224,11 @@ struct sc_pkcs15init_prkeyargs { char *label; unsigned char *guid; size_t guid_len; - unsigned long usage; + unsigned int usage; unsigned long x509_usage; unsigned int flags; unsigned int access_flags; + int user_consent; union { struct sc_pkcs15init_keyarg_gost_params gost; @@ -245,7 +246,7 @@ struct sc_pkcs15init_pubkeyargs { struct sc_pkcs15_id id; struct sc_pkcs15_id auth_id; const char * label; - unsigned long usage; + unsigned int usage; unsigned long x509_usage; union { @@ -269,12 +270,14 @@ struct sc_pkcs15init_skeyargs { struct sc_pkcs15_id id; struct sc_pkcs15_id auth_id; const char * label; - unsigned long usage; + unsigned int usage; unsigned int flags; unsigned int access_flags; unsigned long algorithm; /* User requested algorithm */ unsigned long value_len; /* User requested length */ - + int session_object; /* If nonzero. this is a session object, which will + be cleared from card when the session is closed.*/ + int user_consent; struct sc_pkcs15_skey key; }; @@ -290,6 +293,7 @@ struct sc_pkcs15init_certargs { #define P15_ATTR_TYPE_LABEL 0 #define P15_ATTR_TYPE_ID 1 +#define P15_ATTR_TYPE_VALUE 2 extern struct sc_pkcs15_object *sc_pkcs15init_new_object(int, const char *, @@ -367,7 +371,7 @@ extern int sc_pkcs15init_delete_object(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *); /* Replace an existing cert with a new one, which is assumed to be - * compatible with the correcsponding private key (e.g. the old and + * compatible with the corresponding private key (e.g. the old and * new cert should have the same public key). */ extern int sc_pkcs15init_update_certificate(struct sc_pkcs15_card *, @@ -379,7 +383,7 @@ extern int sc_pkcs15init_update_certificate(struct sc_pkcs15_card *, extern int sc_pkcs15init_create_file(struct sc_profile *, struct sc_pkcs15_card *, struct sc_file *); extern int sc_pkcs15init_update_file(struct sc_profile *, - struct sc_pkcs15_card *, struct sc_file *, void *, unsigned int); + struct sc_pkcs15_card *, struct sc_file *, void *, size_t); extern int sc_pkcs15init_authenticate(struct sc_profile *, struct sc_pkcs15_card *, struct sc_file *, int); extern int sc_pkcs15init_fixup_file(struct sc_profile *, struct sc_pkcs15_card *, @@ -417,23 +421,24 @@ extern int sc_pkcs15init_sanity_check(struct sc_pkcs15_card *, struct sc_profile extern int sc_pkcs15init_finalize_profile(struct sc_card *card, struct sc_profile *profile, struct sc_aid *aid); -extern struct sc_pkcs15init_operations *sc_pkcs15init_get_gpk_ops(void); -extern struct sc_pkcs15init_operations *sc_pkcs15init_get_miocos_ops(void); +extern int sc_pkcs15init_unwrap_key(struct sc_pkcs15_card *p15card, struct sc_profile *profile, + struct sc_pkcs15_object *key, u8* wrapped_key, size_t wrapped_key_len, + struct sc_pkcs15init_skeyargs *keyargs, struct sc_pkcs15_object **res_obj); + + + extern struct sc_pkcs15init_operations *sc_pkcs15init_get_cryptoflex_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_cyberflex_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_cardos_ops(void); -extern struct sc_pkcs15init_operations *sc_pkcs15init_get_jcop_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_starcos_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_oberthur_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_setcos_ops(void); -extern struct sc_pkcs15init_operations *sc_pkcs15init_get_incrypto34_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_muscle_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_asepcos_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rutoken_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_entersafe_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_epass2003_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rtecp_ops(void); -extern struct sc_pkcs15init_operations *sc_pkcs15init_get_westcos_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_myeid_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_authentic_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_iasecc_ops(void); diff --git a/src/pkcs15init/pkcs15-isoApplet.c b/src/pkcs15init/pkcs15-isoApplet.c index a49bf938ca..48dc15fd27 100644 --- a/src/pkcs15init/pkcs15-isoApplet.c +++ b/src/pkcs15init/pkcs15-isoApplet.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -345,7 +345,7 @@ isoApplet_get_curve(u8 *oid, size_t oid_len, const struct ec_curve **curve_out) * @param[in] pubkey The public key of the generated key pair * returned by the card. * - * @return SC_ERROR_INVALID_ARGURMENTS: Invalid key length. + * @return SC_ERROR_INVALID_ARGUMENTS: Invalid key length. * SC_ERROR_OUT_OF_MEMORY */ static int @@ -358,20 +358,21 @@ isoApplet_generate_key_rsa(sc_pkcs15_prkey_info_t *key_info, sc_card_t *card, LOG_FUNC_CALLED(card->ctx); + memset(&args, 0, sizeof(args)); + /* Check key size: */ keybits = key_info->modulus_length; - if (keybits != 2048) + if (keybits != 2048 && keybits != 4096) { rv = SC_ERROR_INVALID_ARGUMENTS; - sc_log(card->ctx, "%s: RSA private key length is unsupported, correct length is 2048", sc_strerror(rv)); + sc_log(card->ctx, "%s: RSA private key length is unsupported, correct length is 2048 or 4096", sc_strerror(rv)); goto err; } /* Generate the key. * Note: key size is not explicitly passed to the card. - * It assumes 2048 along with the algorithm reference. */ - memset(&args, 0, sizeof(args)); - args.algorithm_ref = SC_ISOAPPLET_ALG_REF_RSA_GEN_2048; + * Its derived from the algorithm reference. */ + args.algorithm_ref = keybits == 2048 ? SC_ISOAPPLET_ALG_REF_RSA_GEN_2048 : SC_ISOAPPLET_ALG_REF_RSA_GEN_4096; args.priv_key_ref = key_info->key_reference; args.pubkey.rsa.modulus.len = keybits / 8; @@ -434,7 +435,7 @@ isoApplet_generate_key_rsa(sc_pkcs15_prkey_info_t *key_info, sc_card_t *card, * @param[in/out] pubkey The public key of the generated key pair * returned by the card. * - * @return SC_ERROR_INVALID_ARGURMENTS: Invalid key length or curve. + * @return SC_ERROR_INVALID_ARGUMENTS: Invalid key length or curve. * SC_ERROR_OUT_OF_MEMORY * SC_ERROR_INCOMPATIBLE_KEY: The data returned by the card * was unexpected and can not be @@ -453,6 +454,8 @@ isoApplet_generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *car LOG_FUNC_CALLED(card->ctx); + memset(&args, 0, sizeof(args)); + /* Check key size: */ if(key_info->field_length == 0) { @@ -471,8 +474,6 @@ isoApplet_generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *car /* Generate the key. * Note: The field size is not explicitly passed to the card. * As we only support FP curves, the field length can be calculated from any parameter. */ - memset(&args, 0, sizeof(args)); - args.pubkey.ec.params.prime.value = curve->prime.value; args.pubkey.ec.params.prime.len = curve->prime.len; args.pubkey.ec.params.coefficientA.value = curve->coefficientA.value; @@ -487,7 +488,7 @@ isoApplet_generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *car args.pubkey.ec.params.coFactor.len = curve->coFactor.len; /* The length of the public key point will be: * Uncompressed tag + 2 * field length in bytes. */ - args.pubkey.ec.ecPointQ.len = 1 + (key_info->field_length + 7) / 8 * 2; + args.pubkey.ec.ecPointQ.len = 1 + BYTES4BITS(key_info->field_length) * 2; args.pubkey.ec.ecPointQ.value = malloc(args.pubkey.ec.ecPointQ.len); if(!args.pubkey.ec.ecPointQ.value) { @@ -533,6 +534,7 @@ isoApplet_generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *car goto out; } pubkey->alg_id->algorithm = SC_ALGORITHM_EC; + sc_init_oid(&pubkey->alg_id->oid); pubkey->alg_id->params = alg_id_params; /* Extract ecpointQ */ @@ -546,6 +548,7 @@ isoApplet_generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *car memcpy(pubkey->u.ec.ecpointQ.value, args.pubkey.ec.ecPointQ.value, args.pubkey.ec.ecPointQ.len); /* The OID is also written to the pubkey->u.ec.params */ + free(pubkey->u.ec.params.der.value); pubkey->u.ec.params.der.value = malloc(alg_id_params->der.len); if(!pubkey->u.ec.params.der.value) { @@ -574,7 +577,12 @@ isoApplet_generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *car pubkey->u.ec.params.der.value = NULL; pubkey->u.ec.params.der.len = 0; } - if(r < 0 && pubkey->u.ec.ecpointQ.value) + if(pubkey->u.ec.params.named_curve) + { + free(pubkey->u.ec.params.named_curve); + pubkey->u.ec.params.named_curve = NULL; + } + if(pubkey->u.ec.ecpointQ.value) { free(pubkey->u.ec.ecpointQ.value); pubkey->u.ec.ecpointQ.value = NULL; @@ -609,7 +617,7 @@ isoApplet_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, /* Authentication stuff. */ r = sc_profile_get_file_by_path(profile, &key_info->path, &privKeyFile); - if(!privKeyFile) + if(r < 0 || !privKeyFile) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); } @@ -692,7 +700,7 @@ isoApplet_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_ /* Authentication stuff. */ r = sc_profile_get_file_by_path(profile, &key_info->path, &privKeyFile); - if(!privKeyFile) + if(r < 0 || !privKeyFile) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); } @@ -736,7 +744,7 @@ isoApplet_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_ args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN; if(key->u.ec.params.der.len == 0 || key->u.ec.params.der.value == NULL) { r = sc_pkcs15_fix_ec_parameters(card->ctx, &key->u.ec.params); - LOG_TEST_RET(card->ctx, r, "EC key storing failed: Unkown curve."); + LOG_TEST_RET(card->ctx, r, "EC key storing failed: Unknown curve."); } r = isoApplet_get_curve(key->u.ec.params.der.value, key->u.ec.params.der.len, &curve); LOG_TEST_RET(card->ctx, r, "EC key generation failed: Unsupported curve"); diff --git a/src/pkcs15init/pkcs15-jcop.c b/src/pkcs15init/pkcs15-jcop.c deleted file mode 100644 index 600e19c3fc..0000000000 --- a/src/pkcs15init/pkcs15-jcop.c +++ /dev/null @@ -1,357 +0,0 @@ -/* - * JCOP specific operation for PKCS15 initialization - * - * Copyright 2003 Chaskiel Grundman - * Copyright (C) 2002 Olaf Kirch - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include "libopensc/opensc.h" -#include "libopensc/cardctl.h" -#include "libopensc/log.h" -#include "pkcs15-init.h" -#include "profile.h" - -#define JCOP_MAX_PINS 3 - -/* - * Erase the card - */ -static int -jcop_erase_card(struct sc_profile *pro, sc_pkcs15_card_t *p15card) { - /* later */ - return SC_ERROR_NOT_SUPPORTED; -} - - -static int -jcop_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *file) -{ - return SC_ERROR_NOT_SUPPORTED; -}; - - -/* - * Select a PIN reference - */ -static int -jcop_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, - sc_pkcs15_auth_info_t *auth_info) { - int preferred, current; - - if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) - return SC_ERROR_OBJECT_NOT_VALID; - - if ((current = auth_info->attrs.pin.reference) < 0) - current = 0; - - if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) { - preferred = 3; - } else { - preferred = current; - if (preferred < 1) - preferred=1; - if (preferred > 2) - return SC_ERROR_TOO_MANY_OBJECTS; - } - if (current > preferred) - return SC_ERROR_TOO_MANY_OBJECTS; - auth_info->attrs.pin.reference = preferred; - return 0; -} - -/* - * Store a PIN - */ -static int -jcop_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df, - sc_pkcs15_object_t *pin_obj, - const unsigned char *pin, size_t pin_len, - const unsigned char *puk, size_t puk_len) -{ - sc_pkcs15_auth_info_t *auth_info = (sc_pkcs15_auth_info_t *) pin_obj->data; - struct sc_pkcs15_pin_attributes *pin_attrs = &auth_info->attrs.pin; - unsigned char nulpin[16]; - unsigned char padpin[16]; - int r; - - if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) - return SC_ERROR_OBJECT_NOT_VALID; - - if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) { - /* SO PIN reference must be 0 */ - if (pin_attrs->reference != 3) - return SC_ERROR_INVALID_ARGUMENTS; - } else { - if (pin_attrs->reference >= 3) - return SC_ERROR_TOO_MANY_OBJECTS; - } - if (puk != NULL && puk_len > 0) { - return SC_ERROR_NOT_SUPPORTED; - } - r = sc_select_file(p15card->card, &df->path, NULL); - if (r < 0) - return r; - - /* Current PIN is 00:00:00:00:00:00:00:00... */ - memset(nulpin, 0, sizeof(nulpin)); - memset(padpin, 0, sizeof(padpin)); - memcpy(padpin, pin, pin_len); - r = sc_change_reference_data(p15card->card, SC_AC_CHV, - pin_attrs->reference, - nulpin, sizeof(nulpin), - padpin, sizeof(padpin), NULL); - if (r < 0) - return r; - - pin_attrs->flags &= ~SC_PKCS15_PIN_FLAG_LOCAL; - return r; -} - -/* - * Create a new key file - */ -static int -jcop_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) -{ - sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; - sc_file_t *keyfile = NULL; - size_t bytes, mod_len, prv_len; - int r; - - if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "JCOP supports only RSA keys."); - return SC_ERROR_NOT_SUPPORTED; - } - /* The caller is supposed to have chosen a key file path for us */ - if (key_info->path.len == 0 || key_info->modulus_length == 0) - return SC_ERROR_INVALID_ARGUMENTS; - - /* Get the file we're supposed to create */ - r = sc_profile_get_file_by_path(profile, &key_info->path, &keyfile); - if (r < 0) - return r; - - mod_len = key_info->modulus_length / 8; - bytes = mod_len / 2; - prv_len = 2 + 5 * bytes; - keyfile->size = prv_len; - - /* Fix up PIN references in file ACL */ - r = sc_pkcs15init_fixup_file(profile, p15card, keyfile); - - if (r >= 0) - r = sc_pkcs15init_create_file(profile, p15card, keyfile); - - sc_file_free(keyfile); - return r; -} - -static void -jcop_bn2bin(unsigned char *dest, sc_pkcs15_bignum_t *bn, unsigned int size) -{ - u8 *src; - unsigned int n; - - assert(bn->len <= size); - memset(dest, 0, size); - for (n = size-bn->len, src = bn->data; n < size; n++,src++) - dest[n] = *src; -} - -/* - * Store a private key - * Private key file formats: (transparent file) - * Non-CRT: - * byte 0 0x05 - * byte 1 Modulus length (in byte/4) - * byte 2 Modulus (n) - * byte 2+x private exponent (d) - * - * CRT: - * byte 0 0x06 - * byte 1 component length (in byte/2; component length is half - * of modulus length - * byte 2 Prime (p) - * byte 2+x Prime (q) - * byte 2+2*x Exponent 1 (d mod (p-1)) - * byte 2+3*x Exponent 2 (d mod (q-1)) - * byte 2+4*x Coefficient ((p ^ -1) mod q - * - * We use the CRT format, since that's what key generation does. - * - * Numbers are stored big endian. - */ -static int -jcop_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, - sc_pkcs15_object_t *obj, - sc_pkcs15_prkey_t *key) -{ - sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; - sc_file_t *keyfile; - unsigned char keybuf[1024]; - size_t size,base; - int r; - - if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "JCOP supports only RSA keys."); - return SC_ERROR_NOT_SUPPORTED; - } - r = sc_profile_get_file_by_path(profile, &key_info->path, &keyfile); - if (r < 0) - return r; - base=key_info->modulus_length / 16; - size=2+5*base; - keybuf[0]=6; - keybuf[1]=base/4; - jcop_bn2bin(&keybuf[2 + 0 * base], &key->u.rsa.p, base); - jcop_bn2bin(&keybuf[2 + 1 * base], &key->u.rsa.q, base); - jcop_bn2bin(&keybuf[2 + 2 * base], &key->u.rsa.dmp1, base); - jcop_bn2bin(&keybuf[2 + 3 * base], &key->u.rsa.dmq1, base); - jcop_bn2bin(&keybuf[2 + 4 * base], &key->u.rsa.iqmp, base); - r = sc_pkcs15init_update_file(profile, p15card, keyfile, keybuf, size); - - sc_file_free(keyfile); - return r; -} - -/* - * Generate a keypair - */ -static int -jcop_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, - sc_pkcs15_object_t *obj, - sc_pkcs15_pubkey_t *pubkey) -{ - sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; - struct sc_cardctl_jcop_genkey args; - sc_file_t *temppubfile=NULL, *keyfile=NULL; - unsigned char *keybuf=NULL; - size_t mod_len, exp_len, pub_len, keybits; - int r,delete_ok=0; - - if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "JCOP supports only RSA keys."); - return SC_ERROR_NOT_SUPPORTED; - } - - r=sc_profile_get_file(profile, "temp-pubkey", &temppubfile); - if (r < 0) - goto out; - - r = sc_select_file(p15card->card, &key_info->path, &keyfile); - if (r < 0) - goto out; - - mod_len = key_info->modulus_length / 8; - exp_len = 4; - pub_len = 2 + mod_len + exp_len; - temppubfile->size = pub_len; - - r = sc_pkcs15init_fixup_file(profile, p15card, temppubfile); - if (r < 0) - goto out; - - r = sc_pkcs15init_create_file(profile, p15card, temppubfile); - if (r < 0) - goto out; - - delete_ok=1; - r = sc_pkcs15init_authenticate(profile, p15card, temppubfile, SC_AC_OP_UPDATE); - if (r < 0) - goto out; - r = sc_pkcs15init_authenticate(profile, p15card, keyfile, SC_AC_OP_UPDATE); - if (r < 0) - goto out; - - keybits = key_info->modulus_length; - - /* generate key */ - /* keysize is _not_ passed to the card at any point. it appears to - infer it from the file size */ - memset(&args, 0, sizeof(args)); - args.exponent = 0x10001; - sc_append_file_id(&args.pub_file_ref, temppubfile->id); - sc_append_file_id(&args.pri_file_ref, keyfile->id); - keybuf = malloc(keybits / 8); - if (!keybuf) { - r=SC_ERROR_OUT_OF_MEMORY; - goto out; - } - args.pubkey = keybuf; - args.pubkey_len = keybits / 8; - - r = sc_card_ctl(p15card->card, SC_CARDCTL_JCOP_GENERATE_KEY, (void *)&args); - if (r < 0) - goto out; - - /* extract public key */ - pubkey->algorithm = SC_ALGORITHM_RSA; - pubkey->u.rsa.modulus.len = keybits / 8; - pubkey->u.rsa.modulus.data = keybuf; - pubkey->u.rsa.exponent.len = 3; - pubkey->u.rsa.exponent.data = malloc(3); - if (!pubkey->u.rsa.exponent.data) { - pubkey->u.rsa.modulus.data = NULL; - r=SC_ERROR_OUT_OF_MEMORY; - goto out; - } - memcpy(pubkey->u.rsa.exponent.data, "\x01\x00\x01", 3); - - out: - if (r < 0 && keybuf) - free(keybuf); - if (delete_ok) - sc_pkcs15init_rmdir(p15card, profile, temppubfile); - sc_file_free(keyfile); - sc_file_free(temppubfile); - return r; -} - - - -static struct sc_pkcs15init_operations sc_pkcs15init_jcop_operations = { - jcop_erase_card, - NULL, /* init_card */ - jcop_create_dir, - NULL, /* create_domain */ - jcop_select_pin_reference, - jcop_create_pin, - NULL, /* select_key_reference */ - jcop_create_key, - jcop_store_key, - jcop_generate_key, - NULL, NULL, /* encode private/public key */ - NULL, /* finalize_card */ - NULL, /* delete_object */ - NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ - NULL /* sanity_check */ -}; - -struct sc_pkcs15init_operations *sc_pkcs15init_get_jcop_ops(void) -{ - return &sc_pkcs15init_jcop_operations; -} - - diff --git a/src/pkcs15init/pkcs15-lib.c b/src/pkcs15init/pkcs15-lib.c index 9339aebbac..faa9037bc7 100644 --- a/src/pkcs15init/pkcs15-lib.c +++ b/src/pkcs15init/pkcs15-lib.c @@ -26,7 +26,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -38,7 +38,7 @@ #include #include #include -#ifdef HAVE_GETTIMEOFDAY +#ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_STRINGS_H @@ -106,27 +106,27 @@ static int sc_pkcs15init_update_lastupdate(struct sc_pkcs15_card *, struct sc_profile *profile); static int sc_pkcs15init_update_odf(struct sc_pkcs15_card *, struct sc_profile *profile); -static int sc_pkcs15init_map_usage(unsigned long, int); +static unsigned int sc_pkcs15init_map_usage(unsigned long, int); static int do_select_parent(struct sc_profile *, struct sc_pkcs15_card *, struct sc_file *, struct sc_file **); static int sc_pkcs15init_create_pin(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *, struct sc_pkcs15init_pinargs *); static int check_keygen_params_consistency(struct sc_card *card, - unsigned int alg, struct sc_pkcs15init_prkeyargs *prkey, + unsigned long alg, struct sc_pkcs15init_prkeyargs *prkey, unsigned int *keybits); -static int check_key_compatibility(struct sc_pkcs15_card *, unsigned int, - struct sc_pkcs15_prkey *, unsigned int, - unsigned int, unsigned int); +static int check_key_compatibility(struct sc_pkcs15_card *, unsigned long, + struct sc_pkcs15_prkey *, unsigned long, + size_t, unsigned long); static int prkey_fixup(struct sc_pkcs15_card *, struct sc_pkcs15_prkey *); static int prkey_bits(struct sc_pkcs15_card *, struct sc_pkcs15_prkey *); -static int key_pkcs15_algo(struct sc_pkcs15_card *, unsigned int); +static int key_pkcs15_algo(struct sc_pkcs15_card *, unsigned long); static int select_id(struct sc_pkcs15_card *, int, struct sc_pkcs15_id *); static int select_object_path(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *, struct sc_path *); static int sc_pkcs15init_get_pin_path(struct sc_pkcs15_card *, struct sc_pkcs15_id *, struct sc_path *); static int sc_pkcs15init_qualify_pin(struct sc_card *, const char *, - unsigned int, struct sc_pkcs15_auth_info *); + size_t, struct sc_pkcs15_auth_info *); static struct sc_pkcs15_df * find_df_by_type(struct sc_pkcs15_card *, unsigned int); static int sc_pkcs15init_read_info(struct sc_card *card, struct sc_profile *); @@ -140,24 +140,20 @@ static struct profile_operations { void *func; } profile_operations[] = { { "rutoken", (void *) sc_pkcs15init_get_rutoken_ops }, - { "gpk", (void *) sc_pkcs15init_get_gpk_ops }, - { "miocos", (void *) sc_pkcs15init_get_miocos_ops }, { "flex", (void *) sc_pkcs15init_get_cryptoflex_ops }, { "cyberflex", (void *) sc_pkcs15init_get_cyberflex_ops }, { "cardos", (void *) sc_pkcs15init_get_cardos_ops }, { "etoken", (void *) sc_pkcs15init_get_cardos_ops }, /* legacy */ - { "jcop", (void *) sc_pkcs15init_get_jcop_ops }, { "starcos", (void *) sc_pkcs15init_get_starcos_ops }, { "oberthur", (void *) sc_pkcs15init_get_oberthur_ops }, { "openpgp", (void *) sc_pkcs15init_get_openpgp_ops }, { "setcos", (void *) sc_pkcs15init_get_setcos_ops }, - { "incrypto34", (void *) sc_pkcs15init_get_incrypto34_ops }, { "muscle", (void*) sc_pkcs15init_get_muscle_ops }, { "asepcos", (void*) sc_pkcs15init_get_asepcos_ops }, { "entersafe",(void*) sc_pkcs15init_get_entersafe_ops }, { "epass2003",(void*) sc_pkcs15init_get_epass2003_ops }, { "rutoken_ecp", (void *) sc_pkcs15init_get_rtecp_ops }, - { "westcos", (void *) sc_pkcs15init_get_westcos_ops }, + { "rutoken_lite", (void *) sc_pkcs15init_get_rtecp_ops }, { "myeid", (void *) sc_pkcs15init_get_myeid_ops }, { "sc-hsm", (void *) sc_pkcs15init_get_sc_hsm_ops }, { "isoApplet", (void *) sc_pkcs15init_get_isoApplet_ops }, @@ -175,8 +171,14 @@ static struct sc_pkcs15init_callbacks callbacks = { NULL, }; -static void sc_pkcs15init_empty_callback(void *ptr) + +static void sc_pkcs15init_free_ec_params(void *ptr) { + struct sc_ec_parameters *ecparams = (struct sc_ec_parameters *)ptr; + if (ecparams) { + sc_clear_ec_params(ecparams); + free(ecparams); + } } /* @@ -386,7 +388,7 @@ sc_pkcs15init_bind(struct sc_card *card, const char *name, const char *profile_o sc_log(ctx, "Failed to finalize profile: %s", sc_strerror(r)); } while (0); - if (r < 0) { + if (r < 0) { sc_profile_free(profile); LOG_TEST_RET(ctx, r, "Load profile error"); } @@ -434,8 +436,6 @@ sc_pkcs15init_unbind(struct sc_profile *profile) if (r < 0) sc_log(ctx, "Failed to update TokenInfo: %s", sc_strerror(r)); } - if (profile->dll) - sc_dlclose(profile->dll); sc_profile_free(profile); } @@ -580,9 +580,9 @@ sc_pkcs15init_delete_by_path(struct sc_profile *profile, struct sc_pkcs15_card * * card (driver and profile) that uses self delete ACL. */ /* Select the file itself */ - path = *file_path; - rv = sc_select_file(p15card->card, &path, &file); - LOG_TEST_RET(ctx, rv, "cannot select file to delete"); + path = *file_path; + rv = sc_select_file(p15card->card, &path, &file); + LOG_TEST_RET(ctx, rv, "cannot select file to delete"); if (sc_file_get_acl_entry(file, SC_AC_OP_DELETE_SELF)) { sc_log(ctx, "Found 'DELETE-SELF' acl"); @@ -601,12 +601,20 @@ sc_pkcs15init_delete_by_path(struct sc_profile *profile, struct sc_pkcs15_card * /* Select the parent DF */ path.len -= 2; rv = sc_select_file(p15card->card, &path, &parent); + if (rv < 0) + sc_file_free(file); LOG_TEST_RET(ctx, rv, "Cannot select parent"); rv = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_DELETE); sc_file_free(parent); + sc_file_free(file); LOG_TEST_RET(ctx, rv, "parent 'DELETE' authentication failed"); } + else { + /* No 'DELETE' ACL of the file and not deleted for parent */ + rv = SC_ERROR_INVALID_ARGUMENTS; + sc_file_free(file); + } } LOG_TEST_RET(ctx, rv, "'DELETE' authentication failed"); @@ -617,6 +625,10 @@ sc_pkcs15init_delete_by_path(struct sc_profile *profile, struct sc_pkcs15_card * memset(&path, 0, sizeof(path)); path.type = SC_PATH_TYPE_FILE_ID; + if (file_path->len < 2) { + sc_file_free(file); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + } path.value[0] = file_path->value[file_path->len - 2]; path.value[1] = file_path->value[file_path->len - 1]; path.len = 2; @@ -631,6 +643,7 @@ sc_pkcs15init_delete_by_path(struct sc_profile *profile, struct sc_pkcs15_card * sc_log(ctx, "Now really delete file"); rv = sc_delete_file(p15card->card, &path); + sc_file_free(file); LOG_FUNC_RETURN(ctx, rv); } @@ -664,6 +677,8 @@ sc_pkcs15init_rmdir(struct sc_pkcs15_card *p15card, struct sc_profile *profile, path = df->path; path.len += 2; + if (path.len > SC_MAX_PATH_SIZE) + return SC_ERROR_INTERNAL; nfids = r / 2; while (r >= 0 && nfids--) { @@ -685,6 +700,8 @@ sc_pkcs15init_rmdir(struct sc_pkcs15_card *p15card, struct sc_profile *profile, /* Select the parent DF */ path = df->path; + if (path.len <= 2) + return SC_ERROR_INVALID_ARGUMENTS; path.len -= 2; r = sc_select_file(p15card->card, &path, &parent); if (r < 0) @@ -734,8 +751,8 @@ sc_pkcs15init_finalize_profile(struct sc_card *card, struct sc_profile *profile, int rv; LOG_FUNC_CALLED(ctx); - if (card->app_count < 0) - sc_enum_apps(card); + if (card->app_count < 0 && SC_SUCCESS != sc_enum_apps(card)) + sc_log(ctx, "Could not enumerate apps"); if (aid) { sc_log(ctx, "finalize profile for AID %s", sc_dump_hex(aid->value, aid->len)); @@ -770,6 +787,7 @@ sc_pkcs15init_add_app(struct sc_card *card, struct sc_profile *profile, struct sc_app_info *app; struct sc_file *df = profile->df_info->file; int r = SC_SUCCESS; + int has_so_pin = args->so_pin_len != 0; LOG_FUNC_CALLED(ctx); p15card->card = card; @@ -783,13 +801,22 @@ sc_pkcs15init_add_app(struct sc_card *card, struct sc_profile *profile, if (card->app_count >= SC_MAX_CARD_APPS) LOG_TEST_RET(ctx, SC_ERROR_TOO_MANY_OBJECTS, "Too many applications on this card."); + /* In case of pinpad readers check if SO PIN is defined in a profile */ + if (!has_so_pin && (card->reader->capabilities & SC_READER_CAP_PIN_PAD)) { + sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_ainfo); + /* If found, assume we want SO PIN */ + has_so_pin = pin_ainfo.attrs.pin.reference != -1; + } + /* If the profile requires an SO PIN, check min/max length */ - if (args->so_pin_len) { + if (has_so_pin) { const char *pin_label; - sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_ainfo); - r = sc_pkcs15init_qualify_pin(card, "SO PIN", args->so_pin_len, &pin_ainfo); - LOG_TEST_RET(ctx, r, "Failed to qualify SO PIN"); + if (args->so_pin_len) { + sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_ainfo); + r = sc_pkcs15init_qualify_pin(card, "SO PIN", args->so_pin_len, &pin_ainfo); + LOG_TEST_RET(ctx, r, "Failed to qualify SO PIN"); + } /* Path encoded only for local SO PIN */ if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_LOCAL) @@ -803,7 +830,7 @@ sc_pkcs15init_add_app(struct sc_card *card, struct sc_profile *profile, sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PUK, &puk_ainfo); r = sc_pkcs15init_qualify_pin(card, "SO PUK", args->so_puk_len, &puk_ainfo); - LOG_TEST_RET(ctx, r, "Failed to qulify SO PUK"); + LOG_TEST_RET(ctx, r, "Failed to qualify SO PUK"); if (!(pin_label = args->so_pin_label)) { if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) @@ -826,25 +853,27 @@ sc_pkcs15init_add_app(struct sc_card *card, struct sc_profile *profile, pin_attrs->flags, pin_attrs->reference, sc_print_path(&pin_ainfo.path)); r = sc_pkcs15_add_object(p15card, pin_obj); - LOG_TEST_RET(ctx, r, "Failed to add 'SOPIN' AUTH object"); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to add 'SOPIN' AUTH object"); } } /* Perform card-specific initialization */ - if (profile->ops->init_card) { r = profile->ops->init_card(profile, p15card); if (r < 0 && pin_obj) { sc_pkcs15_remove_object(p15card, pin_obj); - sc_pkcs15_free_object(pin_obj); } - LOG_TEST_RET(ctx, r, "Card specific init failed"); + LOG_TEST_GOTO_ERR(ctx, r, "Card specific init failed"); } /* Create the application directory */ - if (profile->ops->create_dir) + if (profile->ops->create_dir) { r = profile->ops->create_dir(profile, p15card, df); - LOG_TEST_RET(ctx, r, "Create 'DIR' error"); + if (r < 0 && pin_obj) { + sc_pkcs15_remove_object(p15card, pin_obj); + } + LOG_TEST_GOTO_ERR(ctx, r, "Create 'DIR' error"); + } /* Store SO PIN */ if (pin_obj && profile->ops->create_pin) @@ -856,18 +885,14 @@ sc_pkcs15init_add_app(struct sc_card *card, struct sc_profile *profile, /* Remove 'virtual' AUTH object . */ sc_pkcs15_remove_object(p15card, pin_obj); - if (r < 0) - sc_pkcs15_free_object(pin_obj); - LOG_TEST_RET(ctx, r, "Card specific create application DF failed"); - - /* Store the PKCS15 information on the card - * We cannot use sc_pkcs15_create() because it makes - * all sorts of assumptions about DF and EF names, and - * doesn't work if secure messaging is required for the - * MF (which is the case with the GPK) */ + LOG_TEST_GOTO_ERR(ctx, r, "Card specific create application DF failed"); + + /* Store the PKCS15 information on the card */ app = (struct sc_app_info *)calloc(1, sizeof(*app)); - if (app == NULL) - LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Failed to allocate application info"); + if (app == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "Failed to allocate application info"); + } app->path = p15card->file_app->path; if (p15card->file_app->namelen <= SC_MAX_AID_SIZE) { @@ -893,25 +918,48 @@ sc_pkcs15init_add_app(struct sc_card *card, struct sc_profile *profile, } if (args->label) { - if (p15card->tokeninfo->label) - free(p15card->tokeninfo->label); + free(p15card->tokeninfo->label); p15card->tokeninfo->label = strdup(args->label); } - app->label = strdup(p15card->tokeninfo->label); + if (p15card->tokeninfo->label) + app->label = strdup(p15card->tokeninfo->label); + else + app->label = strdup("Token"); /* See if we've set an SO PIN */ r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_AODF, pin_obj); if (r >= 0) { r = sc_pkcs15init_update_dir(p15card, profile, app); - if (r >= 0) + if (r >= 0) { r = sc_pkcs15init_update_tokeninfo(p15card, profile); - /* FIXME: what to do if sc_pkcs15init_update_dir failed? */ + } else { + /* FIXED: what to do if sc_pkcs15init_update_dir failed? */ + /* sc_pkcs15init_update_dir may add app to card->app[] */ + int found = 0; + int i; + for (i = 0; i < card->app_count; i++) { + if (card->app[i] == app) { + found = 1; + break; + } + } + if (found == 0) { /* not in card->app[] free it */ + free(app->label); + free(app); /* unused */ + } + } } else { + free(app->label); free(app); /* unused */ + LOG_TEST_GOTO_ERR(ctx, r, "Failed to add pin object."); } sc_pkcs15init_write_info(p15card, profile, pin_obj); + pin_obj = NULL; + +err: + sc_pkcs15_free_object(pin_obj); LOG_FUNC_RETURN(ctx, r); } @@ -960,23 +1008,30 @@ sc_pkcs15init_store_puk(struct sc_pkcs15_card *p15card, auth_info = (struct sc_pkcs15_auth_info *) pin_obj->data; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, auth_info); + if (auth_info == NULL) + LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "Failed to retrieve auth_info"); + auth_info->auth_id = args->puk_id; /* Now store the PINs */ - if (profile->ops->create_pin) + if (profile->ops->create_pin) { r = sc_pkcs15init_create_pin(p15card, profile, pin_obj, args); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to create PIN"); + } else { - sc_pkcs15_free_object(pin_obj); - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "In Old API store PUK object is not supported"); + r = SC_ERROR_NOT_SUPPORTED; + LOG_TEST_GOTO_ERR(ctx, r, "In Old API store PUK object is not supported"); } - if (r >= 0) - r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_AODF, pin_obj); - else - sc_pkcs15_free_object(pin_obj); + r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_AODF, pin_obj); + LOG_TEST_GOTO_ERR(ctx, r, "Add pin object error"); profile->dirty = 1; + pin_obj = NULL; + +err: + sc_pkcs15_free_object(pin_obj); LOG_FUNC_RETURN(ctx, r); } @@ -1020,25 +1075,33 @@ sc_pkcs15init_store_pin(struct sc_pkcs15_card *p15card, struct sc_profile *profi auth_info = (struct sc_pkcs15_auth_info *) pin_obj->data; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, auth_info); + if (auth_info == NULL) + LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "Failed to retrieve auth_info"); + auth_info->auth_id = args->auth_id; /* Now store the PINs */ sc_log(ctx, "Store PIN(%.*s,authID:%s)", (int) sizeof pin_obj->label, pin_obj->label, sc_pkcs15_print_id(&auth_info->auth_id)); - r = sc_pkcs15init_create_pin(p15card, profile, pin_obj, args); - if (r < 0) - sc_pkcs15_free_object(pin_obj); - LOG_TEST_RET(ctx, r, "Card specific create PIN failed."); + if (profile->ops->create_pin) { + r = sc_pkcs15init_create_pin(p15card, profile, pin_obj, args); + LOG_TEST_GOTO_ERR(ctx, r, "Card specific create PIN failed."); + } else { + r = SC_ERROR_NOT_SUPPORTED; + LOG_TEST_GOTO_ERR(ctx, r, "Store PIN operation is not supported"); + } r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_AODF, pin_obj); - if (r < 0) - sc_pkcs15_free_object(pin_obj); - LOG_TEST_RET(ctx, r, "Failed to add PIN object"); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to add PIN object"); if (args->puk_id.len) r = sc_pkcs15init_store_puk(p15card, profile, args); profile->dirty = 1; + pin_obj = NULL; + +err: + sc_pkcs15_free_object(pin_obj); LOG_FUNC_RETURN(ctx, r); } @@ -1164,12 +1227,13 @@ sc_pkcs15init_init_prkdf(struct sc_pkcs15_card *p15card, struct sc_profile *prof struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; - struct sc_pkcs15_prkey_info *key_info; + struct sc_pkcs15_prkey_info *key_info = NULL; struct sc_pkcs15_keyinfo_gostparams *keyinfo_gostparams; struct sc_pkcs15_object *object = NULL; const char *label; unsigned int usage; int r = 0, key_type; + struct sc_ec_parameters *new_ecparams = NULL; LOG_FUNC_CALLED(ctx); if (!res_obj || !keybits) { @@ -1196,8 +1260,10 @@ sc_pkcs15init_init_prkdf(struct sc_pkcs15_card *p15card, struct sc_profile *prof LOG_TEST_GOTO_ERR(ctx, r, "Unsupported key type"); object = sc_pkcs15init_new_object(key_type, label, &keyargs->auth_id, NULL); - if (object == NULL) - LOG_TEST_GOTO_ERR(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate new PrKey object"); + if (object == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "Cannot allocate new PrKey object"); + } key_info = (struct sc_pkcs15_prkey_info *) object->data; key_info->usage = usage; @@ -1205,11 +1271,11 @@ sc_pkcs15init_init_prkdf(struct sc_pkcs15_card *p15card, struct sc_profile *prof key_info->key_reference = 0; key_info->modulus_length = keybits; key_info->access_flags = keyargs->access_flags; + object->user_consent = keyargs->user_consent; /* Path is selected below */ if (keyargs->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE) { key_info->access_flags &= ~SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; - key_info->native = 0; } /* Select a Key ID if the user didn't specify one, @@ -1232,11 +1298,22 @@ sc_pkcs15init_init_prkdf(struct sc_pkcs15_card *p15card, struct sc_profile *prof keyinfo_gostparams->gostr3410 = keyargs->params.gost.gostr3410; keyinfo_gostparams->gostr3411 = keyargs->params.gost.gostr3411; keyinfo_gostparams->gost28147 = keyargs->params.gost.gost28147; - } - else if (key->algorithm == SC_ALGORITHM_EC) { + } else if (key->algorithm == SC_ALGORITHM_EC || + key->algorithm == SC_ALGORITHM_EDDSA || + key->algorithm == SC_ALGORITHM_XEDDSA) { + /* keyargs->key.u.ec.params.der.value is allocated in keyargs, which is on stack */ struct sc_ec_parameters *ecparams = &keyargs->key.u.ec.params; - key_info->params.data = &keyargs->key.u.ec.params; - key_info->params.free_params = sc_pkcs15init_empty_callback; + + new_ecparams = calloc(1, sizeof(struct sc_ec_parameters)); + if (!new_ecparams) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "Cannot allocate memory for EC parameters"); + } + r = sc_copy_ec_params(new_ecparams, &keyargs->key.u.ec.params); + LOG_TEST_GOTO_ERR(ctx, r, "Cannot copy EC parameters"); + + key_info->params.data = new_ecparams; + key_info->params.free_params = sc_pkcs15init_free_ec_params; key_info->field_length = ecparams->field_length; key_info->modulus_length = 0; } @@ -1269,11 +1346,16 @@ sc_pkcs15init_init_prkdf(struct sc_pkcs15_card *p15card, struct sc_profile *prof *res_obj = object; object = NULL; + new_ecparams = NULL; r = SC_SUCCESS; err: - if (object) - sc_pkcs15init_free_object(object); + if (new_ecparams) { + sc_clear_ec_params(new_ecparams); + free(new_ecparams); + key_info->params.data = NULL; + } + sc_pkcs15init_free_object(object); LOG_FUNC_RETURN(ctx, r); } @@ -1289,7 +1371,7 @@ sc_pkcs15init_init_skdf(struct sc_pkcs15_card *p15card, struct sc_profile *profi struct sc_pkcs15_object *object = NULL; const char *label; unsigned int usage; - unsigned int keybits = keyargs->value_len; + unsigned long keybits = keyargs->value_len; int r = 0, key_type; LOG_FUNC_CALLED(ctx); @@ -1315,8 +1397,10 @@ sc_pkcs15init_init_skdf(struct sc_pkcs15_card *p15card, struct sc_profile *profi LOG_TEST_GOTO_ERR(ctx, r, "Unsupported key type"); object = sc_pkcs15init_new_object(key_type, label, &keyargs->auth_id, NULL); - if (object == NULL) - LOG_TEST_GOTO_ERR(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate new SKey object"); + if (object == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "Cannot allocate new SKey object"); + } key_info = (struct sc_pkcs15_skey_info *) object->data; key_info->usage = usage; @@ -1324,13 +1408,16 @@ sc_pkcs15init_init_skdf(struct sc_pkcs15_card *p15card, struct sc_profile *profi key_info->key_reference = 0; switch (keyargs->algorithm) { case SC_ALGORITHM_DES: - key_info->key_type = CKM_DES_ECB; + key_info->key_type = CKK_DES; break; case SC_ALGORITHM_3DES: - key_info->key_type = CKM_DES3_ECB; + key_info->key_type = CKK_DES3; break; case SC_ALGORITHM_AES: - key_info->key_type = CKM_AES_ECB; + key_info->key_type = CKK_AES; + break; + default: + key_info->key_type = CKK_GENERIC_SECRET; break; } key_info->value_len = keybits; @@ -1339,9 +1426,13 @@ sc_pkcs15init_init_skdf(struct sc_pkcs15_card *p15card, struct sc_profile *profi if (keyargs->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE) { key_info->access_flags &= ~SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; - key_info->native = 0; } + if (keyargs->session_object > 0) + object->session_object = 1; + + object->user_consent = keyargs->user_consent; + /* Select a Key ID if the user didn't specify one, * otherwise make sure it's compatible with our intended use */ r = select_id(p15card, SC_PKCS15_TYPE_SKEY, &keyargs->id); @@ -1353,16 +1444,17 @@ sc_pkcs15init_init_skdf(struct sc_pkcs15_card *p15card, struct sc_profile *profi LOG_TEST_GOTO_ERR(ctx, r, "Failed to select secret key object path"); /* See if we need to select a key reference for this object */ - if (profile->ops->select_key_reference) - LOG_TEST_GOTO_ERR(ctx, SC_ERROR_NOT_SUPPORTED, "SKey keyreference selection not supported"); + if (profile->ops->select_key_reference) { + r = SC_ERROR_NOT_SUPPORTED; + LOG_TEST_GOTO_ERR(ctx, r, "SKey keyreference selection not supported"); + } *res_obj = object; object = NULL; r = SC_SUCCESS; err: - if (object) - sc_pkcs15init_free_object(object); + sc_pkcs15init_free_object(object); LOG_FUNC_RETURN(ctx, r); } @@ -1436,70 +1528,81 @@ sc_pkcs15init_generate_key(struct sc_pkcs15_card *p15card, struct sc_profile *pr struct sc_pkcs15_prkey_info *key_info = NULL; struct sc_pkcs15_pubkey *pubkey = NULL; int r, caller_supplied_id = 0; + unsigned long algorithm = keygen_args->prkey_args.key.algorithm; LOG_FUNC_CALLED(ctx); + + memset(&pubkey_args, 0, sizeof(pubkey_args)); + /* check supported key size */ r = check_keygen_params_consistency(p15card->card, - keygen_args->prkey_args.key.algorithm, &keygen_args->prkey_args, + algorithm, &keygen_args->prkey_args, &keybits); - LOG_TEST_RET(ctx, r, "Invalid key size"); + LOG_TEST_GOTO_ERR(ctx, r, "Invalid key size"); - if (check_key_compatibility(p15card, keygen_args->prkey_args.key.algorithm, + if (check_key_compatibility(p15card, algorithm, &keygen_args->prkey_args.key, keygen_args->prkey_args.x509_usage, - keybits, SC_ALGORITHM_ONBOARD_KEY_GEN)) - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Cannot generate key with the given parameters"); + keybits, SC_ALGORITHM_ONBOARD_KEY_GEN) != SC_SUCCESS) { + r = SC_ERROR_NOT_SUPPORTED; + LOG_TEST_GOTO_ERR(ctx, r, "Cannot generate key with the given parameters"); + } - if (profile->ops->generate_key == NULL) - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Key generation not supported"); + if (profile->ops->generate_key == NULL) { + r = SC_ERROR_NOT_SUPPORTED; + LOG_TEST_GOTO_ERR(ctx, r, "Key generation not supported"); + } if (keygen_args->prkey_args.id.len) { caller_supplied_id = 1; /* Make sure that private key's ID is the unique inside the PKCS#15 application */ r = sc_pkcs15_find_prkey_by_id(p15card, &keygen_args->prkey_args.id, NULL); - if (!r) - LOG_TEST_RET(ctx, SC_ERROR_NON_UNIQUE_ID, "Non unique ID of the private key object"); - else if (r != SC_ERROR_OBJECT_NOT_FOUND) - LOG_TEST_RET(ctx, r, "Find private key error"); + if (!r) { + r = SC_ERROR_NON_UNIQUE_ID; + LOG_TEST_GOTO_ERR(ctx, r, "Non unique ID of the private key object"); + } + else if (r != SC_ERROR_OBJECT_NOT_FOUND) { + LOG_TEST_GOTO_ERR(ctx, r, "Find private key error"); + } } /* Set up the PrKDF object */ r = sc_pkcs15init_init_prkdf(p15card, profile, &keygen_args->prkey_args, &keygen_args->prkey_args.key, keybits, &object); - LOG_TEST_RET(ctx, r, "Set up private key object error"); + LOG_TEST_GOTO_ERR(ctx, r, "Set up private key object error"); key_info = (struct sc_pkcs15_prkey_info *) object->data; r = _pkcd15init_set_aux_md_data(p15card, &key_info->aux_data, keygen_args->prkey_args.guid, keygen_args->prkey_args.guid_len); - LOG_TEST_RET(ctx, r, "Failed to set aux MD data"); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to set aux MD data"); /* Set up the PuKDF info. The public key will be filled in * by the card driver's generate_key function called below. * Auth.ID of the public key object is left empty. */ - memset(&pubkey_args, 0, sizeof(pubkey_args)); pubkey_args.id = keygen_args->prkey_args.id; pubkey_args.label = keygen_args->pubkey_label ? keygen_args->pubkey_label : object->label; pubkey_args.usage = keygen_args->prkey_args.usage; pubkey_args.x509_usage = keygen_args->prkey_args.x509_usage; + pubkey_args.key.algorithm = algorithm; - if (keygen_args->prkey_args.key.algorithm == SC_ALGORITHM_GOSTR3410) { + if (algorithm == SC_ALGORITHM_GOSTR3410) { pubkey_args.params.gost = keygen_args->prkey_args.params.gost; r = sc_copy_gost_params(&(pubkey_args.key.u.gostr3410.params), &(keygen_args->prkey_args.key.u.gostr3410.params)); - LOG_TEST_RET(ctx, r, "Cannot allocate GOST parameters"); - } - else if (keygen_args->prkey_args.key.algorithm == SC_ALGORITHM_EC) { - pubkey_args.key.u.ec.params = keygen_args->prkey_args.key.u.ec.params; + LOG_TEST_GOTO_ERR(ctx, r, "Cannot allocate GOST parameters"); + } else if (algorithm == SC_ALGORITHM_EC || + algorithm == SC_ALGORITHM_EDDSA || + algorithm == SC_ALGORITHM_XEDDSA) { r = sc_copy_ec_params(&pubkey_args.key.u.ec.params, &keygen_args->prkey_args.key.u.ec.params); - LOG_TEST_RET(ctx, r, "Cannot allocate EC parameters"); + LOG_TEST_GOTO_ERR(ctx, r, "Cannot allocate EC parameters"); } /* Generate the private key on card */ r = profile->ops->create_key(profile, p15card, object); - LOG_TEST_RET(ctx, r, "Cannot generate key: create key failed"); + LOG_TEST_GOTO_ERR(ctx, r, "Cannot generate key: create key failed"); r = profile->ops->generate_key(profile, p15card, object, &pubkey_args.key); - LOG_TEST_RET(ctx, r, "Failed to generate key"); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to generate key"); /* update PrKDF entry */ if (!caller_supplied_id) { @@ -1509,17 +1612,18 @@ sc_pkcs15init_generate_key(struct sc_pkcs15_card *p15card, struct sc_profile *pr * if intrinsic ID can be calculated -- overwrite the native one */ memset(&iid, 0, sizeof(iid)); r = sc_pkcs15init_select_intrinsic_id(p15card, profile, SC_PKCS15_TYPE_PUBKEY, &iid, &pubkey_args.key); - LOG_TEST_RET(ctx, r, "Select intrinsic ID error"); + LOG_TEST_GOTO_ERR(ctx, r, "Select intrinsic ID error"); if (iid.len) key_info->id = iid; } - pubkey = &pubkey_args.key; if (!pubkey->alg_id) { pubkey->alg_id = calloc(1, sizeof(struct sc_algorithm_id)); - if (!pubkey->alg_id) - LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + if (!pubkey->alg_id) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "Can not allocate memory for algorithm id"); + } sc_init_oid(&pubkey->alg_id->oid); pubkey->alg_id->algorithm = pubkey->algorithm; @@ -1527,28 +1631,34 @@ sc_pkcs15init_generate_key(struct sc_pkcs15_card *p15card, struct sc_profile *pr pubkey_args.id = key_info->id; r = sc_pkcs15_encode_pubkey(ctx, pubkey, &object->content.value, &object->content.len); - LOG_TEST_RET(ctx, r, "Failed to encode public key"); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to encode public key"); r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_PRKDF, object); - LOG_TEST_RET(ctx, r, "Failed to add generated private key object"); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to add generated private key object"); if (!r && profile->ops->emu_store_data) { r = profile->ops->emu_store_data(p15card, profile, object, NULL, NULL); if (r == SC_ERROR_NOT_IMPLEMENTED) r = SC_SUCCESS; - LOG_TEST_RET(ctx, r, "Card specific 'store data' failed"); + if (r < 0) + sc_pkcs15_remove_object(p15card, object); + LOG_TEST_GOTO_ERR(ctx, r, "Card specific 'store data' failed"); } r = sc_pkcs15init_store_public_key(p15card, profile, &pubkey_args, NULL); - LOG_TEST_RET(ctx, r, "Failed to store public key"); + if (r < 0) + sc_pkcs15_remove_object(p15card, object); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to store public key"); if (res_obj) *res_obj = object; - - sc_pkcs15_erase_pubkey(&pubkey_args.key); + object = NULL; profile->dirty = 1; +err: + sc_pkcs15_free_object(object); + sc_pkcs15_erase_pubkey(&pubkey_args.key); LOG_FUNC_RETURN(ctx, r); } @@ -1561,7 +1671,7 @@ sc_pkcs15init_generate_secret_key(struct sc_pkcs15_card *p15card, struct sc_prof { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *object = NULL; - unsigned int keybits = skey_args->value_len; + unsigned int keybits = (unsigned int)skey_args->value_len; int r; LOG_FUNC_CALLED(ctx); @@ -1570,7 +1680,7 @@ sc_pkcs15init_generate_secret_key(struct sc_pkcs15_card *p15card, struct sc_prof LOG_TEST_RET(ctx, r, "Invalid key size"); if (check_key_compatibility(p15card, skey_args->algorithm, NULL, 0, - keybits, SC_ALGORITHM_ONBOARD_KEY_GEN)) + keybits, SC_ALGORITHM_ONBOARD_KEY_GEN) != SC_SUCCESS) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Cannot generate key with the given parameters"); if (profile->ops->generate_key == NULL) @@ -1587,30 +1697,33 @@ sc_pkcs15init_generate_secret_key(struct sc_pkcs15_card *p15card, struct sc_prof /* Set up the SKDF object */ r = sc_pkcs15init_init_skdf(p15card, profile, skey_args, &object); - LOG_TEST_RET(ctx, r, "Set up secret key object error"); + LOG_TEST_GOTO_ERR(ctx, r, "Set up secret key object error"); /* Generate the secret key on card */ r = profile->ops->create_key(profile, p15card, object); - LOG_TEST_RET(ctx, r, "Cannot generate key: create key failed"); + LOG_TEST_GOTO_ERR(ctx, r, "Cannot generate key: create key failed"); r = profile->ops->generate_key(profile, p15card, object, NULL); - LOG_TEST_RET(ctx, r, "Failed to generate key"); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to generate key"); r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_SKDF, object); - LOG_TEST_RET(ctx, r, "Failed to add generated secret key object"); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to add generated secret key object"); if (!r && profile->ops->emu_store_data) { r = profile->ops->emu_store_data(p15card, profile, object, NULL, NULL); if (r == SC_ERROR_NOT_IMPLEMENTED) r = SC_SUCCESS; - LOG_TEST_RET(ctx, r, "Card specific 'store data' failed"); + LOG_TEST_GOTO_ERR(ctx, r, "Card specific 'store data' failed"); } if (res_obj) *res_obj = object; + object = NULL; profile->dirty = 1; +err: + sc_pkcs15_free_object(object); LOG_FUNC_RETURN(ctx, r); } @@ -1629,6 +1742,15 @@ sc_pkcs15init_store_private_key(struct sc_pkcs15_card *p15card, struct sc_profil int keybits, r = 0; LOG_FUNC_CALLED(ctx); + + if (keyargs->key.algorithm == SC_ALGORITHM_EC) { + /* Do this before copying the key below, otherwise we would leak the memory + * if some fixing would happen in check_key_compatibility() or elsewhere. + * This should have been done in the sc_pkcs15_convert_prkey() + * or earlier, but the context is not available at that point */ + r = sc_pkcs15_fix_ec_parameters(ctx, &keyargs->key.u.ec.params); + LOG_TEST_RET(ctx, r, "failed to fix EC parameters"); + } /* Create a copy of the key first */ key = keyargs->key; @@ -1638,12 +1760,13 @@ sc_pkcs15init_store_private_key(struct sc_pkcs15_card *p15card, struct sc_profil keybits = prkey_bits(p15card, &key); LOG_TEST_RET(ctx, keybits, "Invalid private key size"); - /* Now check whether the card is able to handle this key */ - if (check_key_compatibility(p15card, key.algorithm, &key, keyargs->x509_usage, keybits, 0)) { + /* Now check whether the card is able to handle this key + * this already modifies the local shallow copy of the key structure! */ + if (check_key_compatibility(p15card, key.algorithm, &key, keyargs->x509_usage, keybits, 0) != SC_SUCCESS) { /* Make sure the caller explicitly tells us to store * the key as extractable. */ if (!(keyargs->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE)) - LOG_TEST_RET(ctx, SC_ERROR_INCOMPATIBLE_KEY, "Card does not support this key."); + LOG_TEST_RET(ctx, SC_ERROR_INCOMPATIBLE_KEY, "Card does not support this key for crypto. Cannot store it as non extractable."); } /* Select a intrinsic Key ID if user didn't specify one */ @@ -1663,40 +1786,43 @@ sc_pkcs15init_store_private_key(struct sc_pkcs15_card *p15card, struct sc_profil LOG_TEST_RET(ctx, r, "Failed to initialize private key object"); r = sc_pkcs15init_encode_prvkey_content(p15card, &key, object); - LOG_TEST_RET(ctx, r, "Failed to encode public key"); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to encode public key"); key_info = (struct sc_pkcs15_prkey_info *) object->data; r = _pkcd15init_set_aux_md_data(p15card, &key_info->aux_data, keyargs->guid, keyargs->guid_len); - LOG_TEST_RET(ctx, r, "Failed to set aux MD data"); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to set aux MD data"); if (profile->ops->create_key) r = profile->ops->create_key(profile, p15card, object); - LOG_TEST_RET(ctx, r, "Card specific 'create key' failed"); + LOG_TEST_GOTO_ERR(ctx, r, "Card specific 'create key' failed"); if (profile->ops->store_key) r = profile->ops->store_key(profile, p15card, object, &key); - LOG_TEST_RET(ctx, r, "Card specific 'store key' failed"); + LOG_TEST_GOTO_ERR(ctx, r, "Card specific 'store key' failed"); sc_pkcs15_free_object_content(object); r = sc_pkcs15init_encode_prvkey_content(p15card, &key, object); - LOG_TEST_RET(ctx, r, "Failed to encode public key"); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to encode public key"); /* Now update the PrKDF */ r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_PRKDF, object); - LOG_TEST_RET(ctx, r, "Failed to add new private key PKCS#15 object"); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to add new private key PKCS#15 object"); if (!r && profile->ops->emu_store_data) { r = profile->ops->emu_store_data(p15card, profile, object, NULL, NULL); if (r == SC_ERROR_NOT_IMPLEMENTED) r = SC_SUCCESS; - LOG_TEST_RET(ctx, r, "Card specific 'store data' failed"); + LOG_TEST_GOTO_ERR(ctx, r, "Card specific 'store data' failed"); } if (r >= 0 && res_obj) *res_obj = object; + object = NULL; profile->dirty = 1; +err: + sc_pkcs15_free_object(object); LOG_FUNC_RETURN(ctx, r); } @@ -1715,63 +1841,114 @@ sc_pkcs15init_store_public_key(struct sc_pkcs15_card *p15card, struct sc_profile struct sc_pkcs15_pubkey key; struct sc_path *path; const char *label; - unsigned int keybits, type = 0, usage; + unsigned int type = 0; + unsigned int usage; + size_t keybits; int r; LOG_FUNC_CALLED(ctx); if (!keyargs) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Store public key aborted"); - /* Create a copy of the key first */ + /* Create shallow a copy of the key first */ key = keyargs->key; + /* Copy algorithm id structure */ + if (keyargs->key.alg_id) { + key.alg_id = calloc(1, sizeof(struct sc_algorithm_id)); + if (!key.alg_id) + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Can not allocate memory for algorithm id"); + + key.alg_id->algorithm = keyargs->key.alg_id->algorithm; + memcpy(&key.alg_id->oid, &keyargs->key.alg_id->oid, sizeof(struct sc_object_id)); + } + + /* Copy algorithm related parameters */ switch (key.algorithm) { case SC_ALGORITHM_RSA: + key.u.rsa.modulus.data = NULL; + key.u.rsa.exponent.data = NULL; + // copy RSA params + if (!(key.u.rsa.modulus.data = malloc(keyargs->key.u.rsa.modulus.len))) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "Failed to copy RSA public key parameters"); + } + memcpy(key.u.rsa.modulus.data, keyargs->key.u.rsa.modulus.data, keyargs->key.u.rsa.modulus.len); + if (!(key.u.rsa.exponent.data = malloc(keyargs->key.u.rsa.exponent.len))) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "Failed to copy RSA public key parameters"); + } + memcpy(key.u.rsa.exponent.data, keyargs->key.u.rsa.exponent.data, keyargs->key.u.rsa.exponent.len); keybits = sc_pkcs15init_keybits(&key.u.rsa.modulus); type = SC_PKCS15_TYPE_PUBKEY_RSA; break; -#ifdef SC_PKCS15_TYPE_PUBKEY_DSA - case SC_ALGORITHM_DSA: - keybits = sc_pkcs15init_keybits(&key.u.dsa.q); - type = SC_PKCS15_TYPE_PUBKEY_DSA; - break; -#endif case SC_ALGORITHM_GOSTR3410: + key.u.gostr3410.xy.data = NULL; + // copy GOSTR params + if (!(key.u.gostr3410.xy.data = malloc(keyargs->key.u.gostr3410.xy.len))) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "Failed to copy GOSTR public key parameters"); + } + memcpy(key.u.gostr3410.xy.data, keyargs->key.u.gostr3410.xy.data, keyargs->key.u.gostr3410.xy.len); keybits = SC_PKCS15_GOSTR3410_KEYSIZE; type = SC_PKCS15_TYPE_PUBKEY_GOSTR3410; break; case SC_ALGORITHM_EC: - type = SC_PKCS15_TYPE_PUBKEY_EC; + case SC_ALGORITHM_EDDSA: + case SC_ALGORITHM_XEDDSA: - key.u.ec.params = keyargs->key.u.ec.params; + r = sc_copy_ec_params(&key.u.ec.params, &keyargs->key.u.ec.params); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to copy EC public key parameters"); r = sc_pkcs15_fix_ec_parameters(ctx, &key.u.ec.params); - LOG_TEST_RET(ctx, r, "Failed to fix EC public key parameters"); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to fix EC public key parameters"); + + if (key.algorithm == SC_ALGORITHM_EC) + type = SC_PKCS15_TYPE_PUBKEY_EC; + else if (key.algorithm == SC_ALGORITHM_EDDSA) + type = SC_PKCS15_TYPE_PUBKEY_EDDSA; + else if (key.algorithm == SC_ALGORITHM_XEDDSA) + type = SC_PKCS15_TYPE_PUBKEY_XEDDSA; keybits = key.u.ec.params.field_length; break; default: - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported key algorithm."); + r = SC_ERROR_NOT_SUPPORTED; + LOG_TEST_GOTO_ERR(ctx, r, "Unsupported key algorithm."); } if ((usage = keyargs->usage) == 0) { - usage = SC_PKCS15_PRKEY_USAGE_VERIFY; - if (keyargs->x509_usage) + if (type == SC_PKCS15_TYPE_PUBKEY_XEDDSA) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Setting default usage to derive"); + usage = SC_PKCS15_PRKEY_USAGE_DERIVE; + } else { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Setting default usage to verify"); + usage = SC_PKCS15_PRKEY_USAGE_VERIFY; + } + if (keyargs->x509_usage) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Setting usage from keyargs->x509_usage"); usage = sc_pkcs15init_map_usage(keyargs->x509_usage, 0); + } } + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Usage: 0x%X", usage); + label = keyargs->label; if (!label) label = "Public Key"; /* Set up the pkcs15 object. */ object = sc_pkcs15init_new_object(type, label, &keyargs->auth_id, NULL); - if (object == NULL) - LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate new public key object"); + if (object == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "Cannot allocate new public key object"); + } key_info = (struct sc_pkcs15_pubkey_info *) object->data; key_info->usage = usage; - key_info->modulus_length = keybits; + key_info->id = keyargs->id; - if (key.algorithm == SC_ALGORITHM_GOSTR3410) { + if (key.algorithm == SC_ALGORITHM_RSA) { + key_info->modulus_length = keybits; + } else if (key.algorithm == SC_ALGORITHM_GOSTR3410) { key_info->params.len = sizeof(*keyinfo_gostparams); /* FIXME: malloc() call in pkcs15init, but free() call * in libopensc (sc_pkcs15_free_prkey_info) */ @@ -1784,9 +1961,16 @@ sc_pkcs15init_store_public_key(struct sc_pkcs15_card *p15card, struct sc_profile keyinfo_gostparams->gostr3410 = keyargs->params.gost.gostr3410; keyinfo_gostparams->gostr3411 = keyargs->params.gost.gostr3411; keyinfo_gostparams->gost28147 = keyargs->params.gost.gost28147; - } - else if (key.algorithm == SC_ALGORITHM_EC) { + } else if (key.algorithm == SC_ALGORITHM_EC || + key.algorithm == SC_ALGORITHM_EDDSA || + key.algorithm == SC_ALGORITHM_XEDDSA) { key_info->field_length = keybits; + /* only SC_ALGORITHM_EC has ec_params with the EC curveName OID + * that becomes part of the SPKI algo params. + * EDDSA and XEDDSA ec_params have the OID of the EDDSA or XEDDSA + * which is used as the alg OID. But we still copy here + * as the asn1 routines will do the right thing based on key.algorithm. + */ if (key.u.ec.params.der.value) { key_info->params.data = malloc(key.u.ec.params.der.len); if (!key_info->params.data) { @@ -1796,6 +1980,15 @@ sc_pkcs15init_store_public_key(struct sc_pkcs15_card *p15card, struct sc_profile key_info->params.len = key.u.ec.params.der.len; memcpy(key_info->params.data, key.u.ec.params.der.value, key.u.ec.params.der.len); } + if (keyargs->key.u.ec.ecpointQ.value) { + key.u.ec.ecpointQ.value = malloc(keyargs->key.u.ec.ecpointQ.len); + if (!key.u.ec.ecpointQ.value) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "Cannot allocate EC public key"); + } + key.u.ec.ecpointQ.len = keyargs->key.u.ec.ecpointQ.len; + memcpy(key.u.ec.ecpointQ.value, keyargs->key.u.ec.ecpointQ.value, key.u.ec.ecpointQ.len); + } } /* Select a intrinsic Key ID if the user didn't specify one */ @@ -1830,7 +2023,9 @@ sc_pkcs15init_store_public_key(struct sc_pkcs15_card *p15card, struct sc_profile LOG_TEST_GOTO_ERR(ctx, r, "SPKI encode public key error"); /* Now create key file and store key */ - if (type == SC_PKCS15_TYPE_PUBKEY_EC) + if (type == SC_PKCS15_TYPE_PUBKEY_EC || + type == SC_PKCS15_TYPE_PUBKEY_EDDSA || + type == SC_PKCS15_TYPE_PUBKEY_XEDDSA) r = sc_pkcs15init_store_data(p15card, profile, object, &key_info->direct.spki, &key_info->path); else r = sc_pkcs15init_store_data(p15card, profile, object, &object->content, &key_info->path); @@ -1844,16 +2039,17 @@ sc_pkcs15init_store_public_key(struct sc_pkcs15_card *p15card, struct sc_profile /* Update the PuKDF */ if (r >= 0) r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_PUKDF, object); + LOG_TEST_GOTO_ERR(ctx, r, "Add object error"); if (r >= 0 && res_obj) *res_obj = object; + object = NULL; profile->dirty = 1; err: - if (object && r < 0) - sc_pkcs15init_free_object(object); - + sc_pkcs15_erase_pubkey(&key); + sc_pkcs15_free_object(object); LOG_FUNC_RETURN(ctx, r); } @@ -1872,11 +2068,11 @@ sc_pkcs15init_store_secret_key(struct sc_pkcs15_card *p15card, struct sc_profile LOG_FUNC_CALLED(ctx); /* Now check whether the card is able to handle this key */ - if (check_key_compatibility(p15card, keyargs->algorithm, NULL, 0, keyargs->key.data_len * 8, 0)) { + if (check_key_compatibility(p15card, keyargs->algorithm, NULL, 0, keyargs->value_len, 0) != SC_SUCCESS) { /* Make sure the caller explicitly tells us to store * the key as extractable. */ if (!(keyargs->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE)) - LOG_TEST_RET(ctx, SC_ERROR_INCOMPATIBLE_KEY, "Card does not support this key."); + LOG_TEST_RET(ctx, SC_ERROR_INCOMPATIBLE_KEY, "Card does not support this key for crypto. Cannot store it as non extractable."); } #ifdef ENABLE_OPENSSL @@ -1901,39 +2097,51 @@ sc_pkcs15init_store_secret_key(struct sc_pkcs15_card *p15card, struct sc_profile if (profile->ops->create_key) r = profile->ops->create_key(profile, p15card, object); - LOG_TEST_RET(ctx, r, "Card specific 'create key' failed"); - - if (profile->ops->store_key) { - struct sc_pkcs15_prkey key; - memset(&key, 0, sizeof(key)); - key.algorithm = keyargs->algorithm; - key.u.secret = keyargs->key; - r = profile->ops->store_key(profile, p15card, object, &key); + LOG_TEST_GOTO_ERR(ctx, r, "Card specific 'create key' failed"); + + /* If no key data, only an empty EF is created. + * It can be used to receive an unwrapped key later. */ + if (keyargs->key.data_len > 0) { + if (profile->ops->store_key) { + struct sc_pkcs15_prkey key; + memset(&key, 0, sizeof(key)); + key.algorithm = keyargs->algorithm; + key.u.secret = keyargs->key; + r = profile->ops->store_key(profile, p15card, object, &key); + } } - LOG_TEST_RET(ctx, r, "Card specific 'store key' failed"); + LOG_TEST_GOTO_ERR(ctx, r, "Card specific 'store key' failed"); sc_pkcs15_free_object_content(object); - /* Now update the SKDF */ - r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_SKDF, object); - LOG_TEST_RET(ctx, r, "Failed to add new secret key PKCS#15 object"); + /* Now update the SKDF, unless it is a session object. + If we have an on card session object, we have created the actual key object on card. + The card handles removing it when the session is finished or during the next reset. + We will maintain the object in the P15 structure in memory for duration of the session, + but we don't want it to be written into SKDF. */ + if (!object->session_object) { + r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_SKDF, object); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to add new secret key PKCS#15 object"); + } - if (!r && profile->ops->emu_store_data) { + if (!r && profile->ops->emu_store_data && !object->session_object) { r = profile->ops->emu_store_data(p15card, profile, object, NULL, NULL); if (r == SC_ERROR_NOT_IMPLEMENTED) r = SC_SUCCESS; - LOG_TEST_RET(ctx, r, "Card specific 'store data' failed"); + LOG_TEST_GOTO_ERR(ctx, r, "Card specific 'store data' failed"); } if (r >= 0 && res_obj) *res_obj = object; + object = NULL; profile->dirty = 1; +err: + sc_pkcs15_free_object(object); LOG_FUNC_RETURN(ctx, r); } - /* * Store a certificate */ @@ -2109,17 +2317,20 @@ sc_pkcs15init_store_data_object(struct sc_pkcs15_card *p15card, sc_der_copy(&data_object_info->data, &args->der_encoded); r = sc_pkcs15init_store_data(p15card, profile, object, &args->der_encoded, &data_object_info->path); - LOG_TEST_RET(ctx, r, "Store 'DATA' object error"); + LOG_TEST_GOTO_ERR(ctx, r, "Store 'DATA' object error"); /* Now update the DDF */ r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_DODF, object); - LOG_TEST_RET(ctx, r, "'DODF' update error"); + LOG_TEST_GOTO_ERR(ctx, r, "'DODF' update error"); if (r >= 0 && res_obj) *res_obj = object; + object = NULL; profile->dirty = 1; +err: + sc_pkcs15_free_object(object); LOG_FUNC_RETURN(ctx, r); } @@ -2280,7 +2491,7 @@ static sc_usage_map x509_to_pkcs15_public_key_usage[16] = { }; -static int +static unsigned int sc_pkcs15init_map_usage(unsigned long x509_usage, int _private) { unsigned int p15_usage = 0, n; @@ -2302,7 +2513,8 @@ sc_pkcs15init_map_usage(unsigned long x509_usage, int _private) static size_t sc_pkcs15init_keybits(struct sc_pkcs15_bignum *bn) { - unsigned int mask, bits; + unsigned int mask; + size_t bits; if (!bn || !bn->len) return 0; @@ -2318,13 +2530,15 @@ sc_pkcs15init_keybits(struct sc_pkcs15_bignum *bn) */ static int check_keygen_params_consistency(struct sc_card *card, - unsigned int alg, struct sc_pkcs15init_prkeyargs *prkey, + unsigned long alg, struct sc_pkcs15init_prkeyargs *prkey, unsigned int *keybits) { struct sc_context *ctx = card->ctx; int i, rv; - if (alg == SC_ALGORITHM_EC && prkey) { + if (prkey && (alg == SC_ALGORITHM_EC || + alg == SC_ALGORITHM_EDDSA || + alg == SC_ALGORITHM_XEDDSA)) { struct sc_ec_parameters *ecparams = &prkey->key.u.ec.params; rv = sc_pkcs15_fix_ec_parameters(ctx, ecparams); @@ -2332,7 +2546,7 @@ check_keygen_params_consistency(struct sc_card *card, sc_log(ctx, "EC parameters: %s", sc_dump_hex(ecparams->der.value, ecparams->der.len)); if (!*keybits) - *keybits = ecparams->field_length; + *keybits = (unsigned int)ecparams->field_length; } for (i = 0; i < card->algorithm_count; i++) { @@ -2347,28 +2561,43 @@ check_keygen_params_consistency(struct sc_card *card, LOG_FUNC_RETURN(ctx, SC_SUCCESS); } + if (prkey && (alg == SC_ALGORITHM_EC || + alg == SC_ALGORITHM_EDDSA || + alg == SC_ALGORITHM_XEDDSA)) { + /* allocated in sc_pkcs15_fix_ec_parameters */ + free(prkey->key.u.ec.params.der.value); + prkey->key.u.ec.params.der.value = NULL; + } + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } - /* * Check whether the card has native crypto support for this key. */ static int -check_key_compatibility(struct sc_pkcs15_card *p15card, unsigned int alg, - struct sc_pkcs15_prkey *prkey, unsigned int x509_usage, - unsigned int key_length, unsigned int flags) +check_key_compatibility(struct sc_pkcs15_card *p15card, unsigned long alg, + struct sc_pkcs15_prkey *prkey, unsigned long x509_usage, + size_t key_length, unsigned long flags) { struct sc_context *ctx = p15card->card->ctx; struct sc_algorithm_info *info; unsigned int count; + unsigned long talg = alg; LOG_FUNC_CALLED(ctx); + + if (alg == SC_ALGORITHM_EDDSA || alg == SC_ALGORITHM_XEDDSA) + talg = SC_ALGORITHM_EC; /* really testing ecparams */ + count = p15card->card->algorithm_count; for (info = p15card->card->algorithms; count--; info++) { /* don't check flags if none was specified */ - if (info->algorithm != alg || info->key_length != key_length) - continue; + + if (alg != SC_ALGORITHM_EDDSA && alg != SC_ALGORITHM_XEDDSA) { + if (info->algorithm != alg || info->key_length != key_length) + continue; + } if (flags != 0 && ((info->flags & flags) != flags)) continue; @@ -2387,8 +2616,7 @@ check_key_compatibility(struct sc_pkcs15_card *p15card, unsigned int alg, if (info->u._rsa.exponent != exponent) continue; } - } - else if (alg == SC_ALGORITHM_EC) { + } else if (talg == SC_ALGORITHM_EC) { /* includes EDDSA and XEDDSA */ if (!sc_valid_oid(&prkey->u.ec.params.id)) if (sc_pkcs15_fix_ec_parameters(ctx, &prkey->u.ec.params)) LOG_FUNC_RETURN(ctx, SC_ERROR_OBJECT_NOT_VALID); @@ -2412,6 +2640,7 @@ static int prkey_fixup_rsa(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey_rsa *key) { struct sc_context *ctx = p15card->card->ctx; + int r = SC_SUCCESS; if (!key->modulus.len || !key->exponent.len || !key->d.len || !key->p.len || !key->q.len) { sc_log(ctx, "Missing private RSA coefficient"); @@ -2430,32 +2659,45 @@ prkey_fixup_rsa(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey_rsa *key) /* We don't really need an RSA structure, only the BIGNUMs */ if (!key->dmp1.len || !key->dmq1.len || !key->iqmp.len) { - BIGNUM *aux; - BN_CTX *bn_ctx; - BIGNUM *rsa_n, *rsa_e, *rsa_d, *rsa_p, *rsa_q, *rsa_dmp1, *rsa_dmq1, *rsa_iqmp; - - rsa_n = BN_bin2bn(key->modulus.data, key->modulus.len, NULL); - rsa_e = BN_bin2bn(key->exponent.data, key->exponent.len, NULL); - rsa_d = BN_bin2bn(key->d.data, key->d.len, NULL); - rsa_p = BN_bin2bn(key->p.data, key->p.len, NULL); - rsa_q = BN_bin2bn(key->q.data, key->q.len, NULL); + BIGNUM *aux = NULL; + BN_CTX *bn_ctx = NULL; + BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL, *rsa_p = NULL, + *rsa_q = NULL, *rsa_dmp1 = NULL, *rsa_dmq1 = NULL, *rsa_iqmp = NULL; + + rsa_n = BN_bin2bn(key->modulus.data, (int)key->modulus.len, NULL); + rsa_e = BN_bin2bn(key->exponent.data, (int)key->exponent.len, NULL); + rsa_d = BN_bin2bn(key->d.data, (int)key->d.len, NULL); + rsa_p = BN_bin2bn(key->p.data, (int)key->p.len, NULL); + rsa_q = BN_bin2bn(key->q.data, (int)key->q.len, NULL); rsa_dmp1 = BN_new(); rsa_dmq1 = BN_new(); rsa_iqmp = BN_new(); + if (!rsa_n || !rsa_e || !rsa_d || !rsa_p || !rsa_q || + !rsa_dmp1 || !rsa_dmq1 || !rsa_iqmp) { + sc_log_openssl(ctx); + r = SC_ERROR_INTERNAL; + goto end; + } + aux = BN_new(); bn_ctx = BN_CTX_new(); - BN_sub(aux, rsa_q, BN_value_one()); - BN_mod(rsa_dmq1, rsa_d, aux, bn_ctx); - - BN_sub(aux, rsa_p, BN_value_one()); - BN_mod(rsa_dmp1, rsa_d, aux, bn_ctx); - - BN_mod_inverse(rsa_iqmp, rsa_q, rsa_p, bn_ctx); + if (!aux || !bn_ctx) { + sc_log_openssl(ctx); + r = SC_ERROR_INTERNAL; + goto end; + } - BN_clear_free(aux); - BN_CTX_free(bn_ctx); + if (BN_sub(aux, rsa_q, BN_value_one()) != 1 || + BN_mod(rsa_dmq1, rsa_d, aux, bn_ctx) != 1 || + BN_sub(aux, rsa_p, BN_value_one()) != 1 || + BN_mod(rsa_dmp1, rsa_d, aux, bn_ctx) != 1 || + !BN_mod_inverse(rsa_iqmp, rsa_q, rsa_p, bn_ctx)) { + sc_log_openssl(ctx); + r = SC_ERROR_INTERNAL; + goto end; + } /* Do not replace, only fill in missing */ if (key->dmp1.data == NULL) { @@ -2486,7 +2728,7 @@ prkey_fixup_rsa(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey_rsa *key) key->iqmp.len = 0; } } - +end: BN_clear_free(rsa_n); BN_clear_free(rsa_e); BN_clear_free(rsa_d); @@ -2495,10 +2737,11 @@ prkey_fixup_rsa(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey_rsa *key) BN_clear_free(rsa_dmp1); BN_clear_free(rsa_dmq1); BN_clear_free(rsa_iqmp); - + BN_clear_free(aux); + BN_CTX_free(bn_ctx); } #endif - return 0; + return r; } @@ -2508,7 +2751,6 @@ prkey_fixup(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey *key) switch (key->algorithm) { case SC_ALGORITHM_RSA: return prkey_fixup_rsa(p15card, &key->u.rsa); - case SC_ALGORITHM_DSA: case SC_ALGORITHM_GOSTR3410: /* for now */ return 0; @@ -2524,9 +2766,7 @@ prkey_bits(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey *key) switch (key->algorithm) { case SC_ALGORITHM_RSA: - return sc_pkcs15init_keybits(&key->u.rsa.modulus); - case SC_ALGORITHM_DSA: - return sc_pkcs15init_keybits(&key->u.dsa.q); + return (int)sc_pkcs15init_keybits(&key->u.rsa.modulus); case SC_ALGORITHM_GOSTR3410: if (sc_pkcs15init_keybits(&key->u.gostr3410.d) > SC_PKCS15_GOSTR3410_KEYSIZE) { sc_log(ctx, @@ -2536,13 +2776,15 @@ prkey_bits(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey *key) } return SC_PKCS15_GOSTR3410_KEYSIZE; case SC_ALGORITHM_EC: - sc_log(ctx, "Private EC key length %"SC_FORMAT_LEN_SIZE_T"u", - key->u.ec.params.field_length); + case SC_ALGORITHM_EDDSA: + case SC_ALGORITHM_XEDDSA: + sc_log(ctx, "Private EC type key length %" SC_FORMAT_LEN_SIZE_T "u", + key->u.ec.params.field_length); if (key->u.ec.params.field_length == 0) { sc_log(ctx, "Invalid EC key length"); return SC_ERROR_OBJECT_NOT_VALID; } - return key->u.ec.params.field_length; + return (int)key->u.ec.params.field_length; } sc_log(ctx, "Unsupported key algorithm."); return SC_ERROR_NOT_SUPPORTED; @@ -2550,24 +2792,27 @@ prkey_bits(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey *key) static int -key_pkcs15_algo(struct sc_pkcs15_card *p15card, unsigned int algorithm) +key_pkcs15_algo(struct sc_pkcs15_card *p15card, unsigned long algorithm) { struct sc_context *ctx = p15card->card->ctx; switch (algorithm) { case SC_ALGORITHM_RSA: return SC_PKCS15_TYPE_PRKEY_RSA; - case SC_ALGORITHM_DSA: - return SC_PKCS15_TYPE_PRKEY_DSA; case SC_ALGORITHM_GOSTR3410: return SC_PKCS15_TYPE_PRKEY_GOSTR3410; case SC_ALGORITHM_EC: return SC_PKCS15_TYPE_PRKEY_EC; + case SC_ALGORITHM_EDDSA: + return SC_PKCS15_TYPE_PRKEY_EDDSA; + case SC_ALGORITHM_XEDDSA: + return SC_PKCS15_TYPE_PRKEY_XEDDSA; case SC_ALGORITHM_DES: return SC_PKCS15_TYPE_SKEY_DES; case SC_ALGORITHM_3DES: return SC_PKCS15_TYPE_SKEY_3DES; case SC_ALGORITHM_AES: + case SC_ALGORITHM_UNDEFINED: return SC_PKCS15_TYPE_SKEY_GENERIC; } sc_log(ctx, "Unsupported key algorithm."); @@ -2612,7 +2857,7 @@ sc_pkcs15init_select_intrinsic_id(struct sc_pkcs15_card *p15card, struct sc_prof if (id_out->len) LOG_FUNC_RETURN(ctx, SC_SUCCESS); - /* Native ID style is not intrisic one */ + /* Native ID style is not intrinsic one */ if (id_style == SC_PKCS15INIT_ID_STYLE_NATIVE) LOG_FUNC_RETURN(ctx, SC_SUCCESS); @@ -2638,11 +2883,9 @@ sc_pkcs15init_select_intrinsic_id(struct sc_pkcs15_card *p15card, struct sc_prof LOG_FUNC_RETURN(ctx, SC_SUCCESS); } - /* Skip silently if key is not inintialized. */ + /* Skip silently if key is not initialized. */ if (pubkey->algorithm == SC_ALGORITHM_RSA && !pubkey->u.rsa.modulus.len) goto done; - else if (pubkey->algorithm == SC_ALGORITHM_DSA && !pubkey->u.dsa.pub.data) - goto done; else if (pubkey->algorithm == SC_ALGORITHM_GOSTR3410 && !pubkey->u.gostr3410.xy.data) goto done; @@ -2658,8 +2901,6 @@ sc_pkcs15init_select_intrinsic_id(struct sc_pkcs15_card *p15card, struct sc_prof case SC_PKCS15INIT_ID_STYLE_MOZILLA: if (pubkey->algorithm == SC_ALGORITHM_RSA) SHA1(pubkey->u.rsa.modulus.data, pubkey->u.rsa.modulus.len, id.value); - else if (pubkey->algorithm == SC_ALGORITHM_DSA) - SHA1(pubkey->u.dsa.pub.data, pubkey->u.dsa.pub.len, id.value); else if (pubkey->algorithm == SC_ALGORITHM_EC) /* ID should be SHA1 of the X coordinate according to PKCS#15 v1.1 */ /* skip the 04 tag and get the X component */ @@ -2690,7 +2931,8 @@ sc_pkcs15init_select_intrinsic_id(struct sc_pkcs15_card *p15card, struct sc_prof done: memcpy(id_out, &id, sizeof(*id_out)); - rv = id_out->len; + rv = (int)id_out->len; + err: if (id_data) free(id_data); @@ -2838,9 +3080,9 @@ select_object_path(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_context *ctx = p15card->card->ctx; struct sc_file *file; struct sc_pkcs15_object *objs[32]; - struct sc_pkcs15_id indx_id; + struct sc_pkcs15_id index_id; struct sc_path obj_path; - int ii, r, nn_objs, indx; + int ii, r, nn_objs, index; const char *name; LOG_FUNC_CALLED(ctx); @@ -2870,18 +3112,18 @@ select_object_path(struct sc_pkcs15_card *p15card, struct sc_profile *profile, sc_log(ctx, "key-domain.%s @%s (auth_id.len=%"SC_FORMAT_LEN_SIZE_T"u)", name, sc_print_path(path), obj->auth_id.len); - indx_id.len = 1; - for (indx = TEMPLATE_INSTANTIATE_MIN_INDEX; indx <= TEMPLATE_INSTANTIATE_MAX_INDEX; indx++) { - indx_id.value[0] = indx; - r = sc_profile_instantiate_template(profile, "key-domain", path, name, &indx_id, &file); + index_id.len = 1; + for (index = TEMPLATE_INSTANTIATE_MIN_INDEX; index <= TEMPLATE_INSTANTIATE_MAX_INDEX; index++) { + index_id.value[0] = index; + r = sc_profile_instantiate_template(profile, "key-domain", path, name, &index_id, &file); if (r == SC_ERROR_TEMPLATE_NOT_FOUND) { /* No template in 'key-domain' -- try to instantiate the template-'object name' * outside of the 'key-domain' scope. */ char t_name[0x40]; snprintf(t_name, sizeof(t_name), "template-%s", name); - sc_log(ctx, "get instance %i of '%s'", indx, t_name); - r = sc_profile_get_file_instance(profile, t_name, indx, &file); + sc_log(ctx, "get instance %i of '%s'", index, t_name); + r = sc_profile_get_file_instance(profile, t_name, index, &file); if (r == SC_ERROR_FILE_NOT_FOUND) LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -2910,10 +3152,10 @@ select_object_path(struct sc_pkcs15_card *p15card, struct sc_profile *profile, sc_file_free(file); - indx_id.value[0] += 1; + index_id.value[0] += 1; } - if (indx > TEMPLATE_INSTANTIATE_MAX_INDEX) + if (index > TEMPLATE_INSTANTIATE_MAX_INDEX) LOG_TEST_RET(ctx, SC_ERROR_TOO_MANY_OBJECTS, "Template instantiation error"); *path = file->path; @@ -3036,6 +3278,8 @@ sc_pkcs15init_update_lastupdate(struct sc_pkcs15_card *p15card, struct sc_profil LOG_TEST_RET(ctx, r, "select object path failed"); r = sc_select_file(p15card->card, &last_update->path, &file); + if (r < 0) + free(buf); LOG_TEST_RET(ctx, r, "select object path failed"); r = sc_pkcs15init_update_file(profile, p15card, file, buf, buflen); @@ -3081,7 +3325,7 @@ sc_pkcs15init_update_any_df(struct sc_pkcs15_card *p15card, struct sc_card *card = p15card->card; struct sc_file *file = NULL; unsigned char *buf = NULL; - size_t bufsize; + size_t bufsize = 0; int update_odf = is_new, r = 0; LOG_FUNC_CALLED(ctx); @@ -3106,7 +3350,7 @@ sc_pkcs15init_update_any_df(struct sc_pkcs15_card *p15card, * every time we parse the CDF. */ if (profile->pkcs15.encode_df_length) { - df->path.count = bufsize; + df->path.count = (int)bufsize; df->path.index = 0; update_odf = 1; } @@ -3148,7 +3392,8 @@ sc_pkcs15init_add_object(struct sc_pkcs15_card *p15card, struct sc_profile *prof } sc_pkcs15_add_df(p15card, df_type, &file->path); df = find_df_by_type(p15card, df_type); - assert(df != NULL); + if (df == NULL) + return SC_ERROR_INTERNAL; is_new = 1; /* Mark the df as enumerated, so libopensc doesn't try @@ -3168,7 +3413,8 @@ sc_pkcs15init_add_object(struct sc_pkcs15_card *p15card, struct sc_profile *prof } else { sc_log(ctx, "Reuse existing object"); - assert(object->df == df); + if (object->df != df) + return SC_ERROR_INTERNAL; } if (profile->ops->emu_update_any_df) @@ -3225,6 +3471,11 @@ sc_pkcs15init_new_object(int type, const char *label, struct sc_pkcs15_id *auth_ if (data_size) { object->data = calloc(1, data_size); + if (!object->data) { + free(object); + return NULL; + } + if (data) memcpy(object->data, data, data_size); } @@ -3255,7 +3506,7 @@ sc_pkcs15init_change_attrib(struct sc_pkcs15_card *p15card, struct sc_profile *p struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; unsigned char *buf = NULL; - size_t bufsize; + size_t bufsize = 0; int df_type, r = 0; struct sc_pkcs15_df *df; struct sc_pkcs15_id new_id = *((struct sc_pkcs15_id *) new_value); @@ -3298,8 +3549,45 @@ sc_pkcs15init_change_attrib(struct sc_pkcs15_card *p15card, struct sc_profile *p LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Cannot change ID attribute"); } break; + case P15_ATTR_TYPE_VALUE: + switch(df_type) { + case SC_PKCS15_DODF: { + u8 *nv; + struct sc_pkcs15_data_info *info = (struct sc_pkcs15_data_info *) object->data; + struct sc_path old_data_path = info->path; + struct sc_path new_data_path; + struct sc_pkcs15_der new_data; + new_data.len = new_len; + new_data.value = (u8 *) new_value; + + /* save new data as a new data file on token */ + r = sc_pkcs15init_store_data(p15card, profile, object, &new_data, &new_data_path); + profile->dirty = 1; + LOG_TEST_RET(ctx, r, "Failed to store new data"); + + nv = (u8 *) malloc (new_len * sizeof(u8)); + if (!nv) { + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + } + memcpy(nv, new_value, new_len * sizeof(u8)); + free(info->data.value); + /* set object members to represent new CKA_VALUE value, + new path will be written to DODF later in this function*/ + info->data.len = new_len; + info->data.value = nv; + info->path = new_data_path; + + /* delete old data file from token */ + r = sc_pkcs15init_delete_by_path(profile, p15card, &old_data_path); + LOG_TEST_RET(ctx, r, "Failed to delete old data"); + break; + } + default: + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Cannot change value attribute"); + } + break; default: - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Only 'LABEL' or 'ID' attributes can be changed"); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Only 'LABEL' or 'ID' or 'VALUE'(for data objects) attributes can be changed"); } if (profile->ops->emu_update_any_df) { @@ -3476,12 +3764,12 @@ sc_pkcs15init_update_certificate(struct sc_pkcs15_card *p15card, /* Fill the remaining space in the EF (if any) with zeros */ if (certlen < file->size) { - unsigned char *tmp = calloc(file->size - certlen, 1); + unsigned char *tmp = calloc(1, file->size - certlen); if (tmp == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto done; } - r = sc_update_binary(p15card->card, certlen, tmp, file->size - certlen, 0); + r = sc_update_binary(p15card->card, (unsigned int)certlen, tmp, file->size - certlen, 0); free(tmp); if (r < 0) sc_log(ctx, "Update cert file error"); @@ -3492,7 +3780,7 @@ sc_pkcs15init_update_certificate(struct sc_pkcs15_card *p15card, path = &((struct sc_pkcs15_cert_info *)obj->data)->path; if (file->size != certlen) { path->index = 0; - path->count = certlen; + path->count = (int)certlen; } else { path->count = -1; @@ -3578,13 +3866,15 @@ sc_pkcs15init_get_transport_key(struct sc_profile *profile, struct sc_pkcs15_car if (callbacks.get_key) { rv = callbacks.get_key(profile, type, reference, defbuf, defsize, pinbuf, pinsize); LOG_TEST_RET(ctx, rv, "Cannot get key"); - } - else if (rv >= 0) { + } else if (rv >= 0) { if (*pinsize < defsize) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Get transport key error"); memcpy(pinbuf, data.key_data, data.len); *pinsize = data.len; + } else { + /* pinbuf and pinsize were not filled */ + LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "Get transport key error"); } memset(&auth_info, 0, sizeof(auth_info)); @@ -3658,15 +3948,15 @@ sc_pkcs15init_verify_secret(struct sc_profile *profile, struct sc_pkcs15_card *p sc_log(ctx, "Symbolic PIN resolved to PIN(type:CHV,reference:%i)", reference); } - if (path && path->len) { + if (path && path->len && path->len <= SC_MAX_PATH_SIZE) { struct sc_path tmp_path = *path; int iter; - r = SC_ERROR_OBJECT_NOT_FOUND; - for (iter = tmp_path.len/2; iter >= 0 && r == SC_ERROR_OBJECT_NOT_FOUND; iter--, tmp_path.len -= 2) + for (iter = (int)tmp_path.len/2; iter >= 0 && r == SC_ERROR_OBJECT_NOT_FOUND; iter--, tmp_path.len -= 2) { r = sc_pkcs15_find_pin_by_type_and_reference(p15card, tmp_path.len ? &tmp_path : NULL, type, reference, &pin_obj); + } } else { r = sc_pkcs15_find_pin_by_type_and_reference(p15card, NULL, type, reference, &pin_obj); @@ -3731,7 +4021,23 @@ sc_pkcs15init_verify_secret(struct sc_profile *profile, struct sc_pkcs15_card *p found: if (pin_obj) { - r = sc_pkcs15_verify_pin(p15card, pin_obj, pinsize ? pinbuf : NULL, pinsize); + /* + * If pin cache is disabled or the reader is using pinpad, we can get here + * with no PIN data. This is ok as we can not asynchronously invoke the prompt + * (unless the pinpad is in use). + * In this case, check if the PIN has been already verified and + * the access condition is still open on card. + */ + if (pinsize == 0) { + r = sc_pkcs15_get_pin_info(p15card, pin_obj); + /* update local copy of auth info */ + memcpy(&auth_info, pin_obj->data, sizeof(auth_info)); + + if (r == SC_SUCCESS && auth_info.logged_in & SC_PIN_STATE_LOGGED_IN) + LOG_FUNC_RETURN(ctx, r); + } + + r = sc_pkcs15_verify_pin(p15card, pin_obj, use_pinpad || pinsize == 0 ? NULL : pinbuf, use_pinpad ? 0 : pinsize); LOG_TEST_RET(ctx, r, "Cannot validate pkcs15 PIN"); } @@ -3750,7 +4056,7 @@ sc_pkcs15init_verify_secret(struct sc_profile *profile, struct sc_pkcs15_card *p pin_cmd.pin1.data = use_pinpad ? NULL : pinbuf; pin_cmd.pin1.len = use_pinpad ? 0: pinsize; - r = sc_pin_cmd(p15card->card, &pin_cmd, NULL); + r = sc_pin_cmd(p15card->card, &pin_cmd); LOG_TEST_RET(ctx, r, "'VERIFY' pin cmd failed"); } @@ -3767,7 +4073,7 @@ sc_pkcs15init_verify_secret(struct sc_profile *profile, struct sc_pkcs15_card *p * * In the latter case, there's a problem here if e.g. the SO PIN * defined by the profile is optional, and hasn't been set. - * On the orther hands, some cards do not return access conditions + * On the other hands, some cards do not return access conditions * in their response to SELECT FILE), so the latter case has been * used in most cards while the first case was added much later. */ @@ -3781,9 +4087,15 @@ sc_pkcs15init_authenticate(struct sc_profile *profile, struct sc_pkcs15_card *p1 int r = 0; LOG_FUNC_CALLED(ctx); - assert(file != NULL); + if (file == NULL) + return SC_ERROR_INTERNAL; sc_log(ctx, "path '%s', op=%u", sc_print_path(&file->path), op); + if (file->acl_inactive) { + sc_log(ctx, "access control mechanism is not active (always allowed)"); + LOG_FUNC_RETURN(ctx, r); + } + if (p15card->card->caps & SC_CARD_CAP_USE_FCI_AC) { r = sc_select_file(p15card->card, &file->path, &file_tmp); LOG_TEST_RET(ctx, r, "Authentication failed: cannot select file."); @@ -3797,6 +4109,7 @@ sc_pkcs15init_authenticate(struct sc_profile *profile, struct sc_pkcs15_card *p1 for (; r == 0 && acl; acl = acl->next) { if (acl->method == SC_AC_NEVER) { + sc_file_free(file_tmp); LOG_TEST_RET(ctx, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED, "Authentication failed: never allowed"); } else if (acl->method == SC_AC_NONE) { @@ -3838,7 +4151,7 @@ do_select_parent(struct sc_profile *profile, struct sc_pkcs15_card *p15card, r = sc_select_file(p15card->card, &path, parent); /* If DF doesn't exist, create it (unless it's the MF, * but then something's badly broken anyway :-) */ - if (r == SC_ERROR_FILE_NOT_FOUND && path.len != 2) { + if (r == SC_ERROR_FILE_NOT_FOUND && path.len > 2) { r = sc_profile_get_file_by_path(profile, &path, parent); if (r < 0) { sc_log(ctx, "no profile template for DF %s", sc_print_path(&path)); @@ -3846,12 +4159,20 @@ do_select_parent(struct sc_profile *profile, struct sc_pkcs15_card *p15card, } r = sc_pkcs15init_create_file(profile, p15card, *parent); + if (r < 0) { + sc_file_free(*parent); + *parent = NULL; + } LOG_TEST_RET(ctx, r, "Cannot create parent DF"); r = sc_select_file(p15card->card, &path, NULL); + if (r < 0) { + sc_file_free(*parent); + *parent = NULL; + } LOG_TEST_RET(ctx, r, "Cannot select parent DF"); } - else if (r == SC_SUCCESS && !strcmp(p15card->card->name, "STARCOS SPK 2.3")) { + else if (r == SC_SUCCESS && !strcmp(p15card->card->name, "STARCOS")) { /* in case of starcos spk 2.3 SELECT FILE does not * give us the ACLs => ask the profile */ sc_file_free(*parent); @@ -3862,6 +4183,7 @@ do_select_parent(struct sc_profile *profile, struct sc_pkcs15_card *p15card, LOG_FUNC_RETURN(ctx, r); } } + LOG_FUNC_RETURN(ctx, r); } @@ -3875,26 +4197,31 @@ sc_pkcs15init_create_file(struct sc_profile *profile, struct sc_pkcs15_card *p15 int r; LOG_FUNC_CALLED(ctx); + if (!file) { + return SC_ERROR_INVALID_ARGUMENTS; + } + sc_log(ctx, "create file '%s'", sc_print_path(&file->path)); /* Select parent DF and verify PINs/key as necessary */ r = do_select_parent(profile, p15card, file, &parent); LOG_TEST_RET(ctx, r, "Cannot create file: select parent error"); r = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_CREATE); - LOG_TEST_RET(ctx, r, "Cannot create file: 'CREATE' authentication failed"); + LOG_TEST_GOTO_ERR(ctx, r, "Cannot create file: 'CREATE' authentication failed"); /* Fix up the file's ACLs */ r = sc_pkcs15init_fixup_file(profile, p15card, file); - LOG_TEST_RET(ctx, r, "Cannot create file: file fixup failed"); + LOG_TEST_GOTO_ERR(ctx, r, "Cannot create file: file fixup failed"); /* ensure we are in the correct lifecycle */ r = sc_pkcs15init_set_lifecycle(p15card->card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r != SC_ERROR_NOT_SUPPORTED) - LOG_TEST_RET(ctx, r, "Cannot create file: failed to set lifecycle 'ADMIN'"); + LOG_TEST_GOTO_ERR(ctx, r, "Cannot create file: failed to set lifecycle 'ADMIN'"); r = sc_create_file(p15card->card, file); - LOG_TEST_RET(ctx, r, "Create file failed"); + LOG_TEST_GOTO_ERR(ctx, r, "Create file failed"); +err: sc_file_free(parent); LOG_FUNC_RETURN(ctx, r); } @@ -3903,7 +4230,7 @@ sc_pkcs15init_create_file(struct sc_profile *profile, struct sc_pkcs15_card *p15 int sc_pkcs15init_update_file(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *file, - void *data, unsigned int datalen) + void *data, size_t datalen) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *selected_file = NULL; @@ -3914,7 +4241,7 @@ sc_pkcs15init_update_file(struct sc_profile *profile, if (!file) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(ctx, "path:%s; datalen:%i", sc_print_path(&file->path), datalen); + sc_log(ctx, "path:%s; datalen:%zu", sc_print_path(&file->path), datalen); r = sc_select_file(p15card->card, &file->path, &selected_file); if (!r) { @@ -3937,7 +4264,7 @@ sc_pkcs15init_update_file(struct sc_profile *profile, if (selected_file->size < datalen) { sc_log(ctx, - "File %s too small (require %u, have %"SC_FORMAT_LEN_SIZE_T"u)", + "File %s too small (require %zu, have %"SC_FORMAT_LEN_SIZE_T"u)", sc_print_path(&file->path), datalen, selected_file->size); sc_file_free(selected_file); @@ -3946,6 +4273,11 @@ sc_pkcs15init_update_file(struct sc_profile *profile, else if (selected_file->size > datalen && need_to_zap) { /* zero out the rest of the file - we may have shrunk * the file contents */ + if (selected_file->size > MAX_FILE_SIZE) { + sc_file_free(selected_file); + LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); + } + copy = calloc(1, selected_file->size); if (copy == NULL) { sc_file_free(selected_file); @@ -3957,7 +4289,7 @@ sc_pkcs15init_update_file(struct sc_profile *profile, } /* Present authentication info needed */ - r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); + r = sc_pkcs15init_authenticate(profile, p15card, selected_file, SC_AC_OP_UPDATE); if (r >= 0 && datalen) r = sc_update_binary(p15card->card, 0, (const unsigned char *) data, datalen, 0); @@ -4155,12 +4487,15 @@ sc_pkcs15init_sanity_check(struct sc_pkcs15_card *p15card, struct sc_profile *pr static int sc_pkcs15init_qualify_pin(struct sc_card *card, const char *pin_name, - unsigned int pin_len, struct sc_pkcs15_auth_info *auth_info) + size_t pin_len, struct sc_pkcs15_auth_info *auth_info) { struct sc_context *ctx = card->ctx; struct sc_pkcs15_pin_attributes *pin_attrs; LOG_FUNC_CALLED(ctx); + if (auth_info == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_OBJECT_NOT_FOUND); + if (pin_len == 0 || auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) LOG_FUNC_RETURN(ctx, SC_SUCCESS); @@ -4200,6 +4535,8 @@ sc_pkcs15init_read_info(struct sc_card *card, struct sc_profile *profile) if (r >= 0) { len = file->size; sc_file_free(file); + if (len > MAX_FILE_SIZE) + return SC_ERROR_INTERNAL; mem = malloc(len); if (mem != NULL) r = sc_read_binary(card, 0, mem, len, 0); @@ -4208,10 +4545,11 @@ sc_pkcs15init_read_info(struct sc_card *card, struct sc_profile *profile) } else { r = 0; + sc_file_free(file); } if (r >= 0) - r = sc_pkcs15init_parse_info(card, mem, len, profile); + r = sc_pkcs15init_parse_info(card, mem, r, profile); if (mem) free(mem); @@ -4251,13 +4589,14 @@ sc_pkcs15init_parse_info(struct sc_card *card, const unsigned char *end; unsigned int nopts = 0; size_t n; + int r = 0; if ((p == NULL) || (len == 0)) return 0; end = p + (len - 1); while (p < end) { /* more bytes to look at */ - int r = 0; + r = 0; tag = *p; p++; if ((tag == 0) || (tag == 0xff) || (p >= end)) @@ -4266,23 +4605,26 @@ sc_pkcs15init_parse_info(struct sc_card *card, n = *p; p++; - if (p >= end || p + n > end) /* invalid length byte n */ + if (p >= end || p + n > end) { /* invalid length byte n */ + r = SC_ERROR_PKCS15INIT; goto error; + } switch (tag) { case OPENSC_INFO_TAG_PROFILE: r = set_info_string(&profile->name, p, n); if (r < 0) - return r; + goto error; break; case OPENSC_INFO_TAG_OPTION: if (nopts >= SC_PKCS15INIT_MAX_OPTIONS - 1) { sc_log(card->ctx, "Too many options in OpenSC Info file"); - return SC_ERROR_PKCS15INIT; + r = SC_ERROR_PKCS15INIT; + goto error; } r = set_info_string(&profile->options[nopts], p, n); if (r < 0) - return r; + goto error; profile->options[++nopts] = NULL; break; default: @@ -4294,7 +4636,16 @@ sc_pkcs15init_parse_info(struct sc_card *card, error: sc_log(card->ctx, "OpenSC info file corrupted"); - return SC_ERROR_PKCS15INIT; + if (profile->name) { + free(profile->name); + profile->name = NULL; + } + for (size_t i = 0; i < nopts; i++) { + if (profile->options[i]) + free(profile->options[i]); + profile->options[i] = NULL; + } + return r; } @@ -4303,9 +4654,9 @@ do_encode_string(unsigned char **memp, unsigned char *end, unsigned char tag, const char *s) { unsigned char *p = *memp; - int n; + size_t n; - n = s? strlen(s) : 0; + n = s ? strlen(s) : 0; if (n > 255) return SC_ERROR_BUFFER_TOO_SMALL; if (p + 2 + n > end) @@ -4367,7 +4718,7 @@ sc_pkcs15init_write_info(struct sc_pkcs15_card *p15card, r = do_encode_string(&p, end, OPENSC_INFO_TAG_OPTION, profile->options[n]); if (r >= 0) - r = sc_pkcs15init_update_file(profile, p15card, file, buffer, file->size); + r = sc_pkcs15init_update_file(profile, p15card, file, buffer, (unsigned int)file->size); sc_file_free(file); return r; diff --git a/src/pkcs15init/pkcs15-miocos.c b/src/pkcs15init/pkcs15-miocos.c deleted file mode 100644 index 5fea3c9ae0..0000000000 --- a/src/pkcs15init/pkcs15-miocos.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * MioCOS specific operation for PKCS15 initialization - * - * Copyright (C) 2002 Juha Yrjölä - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" - -#include -#include - -#include "libopensc/opensc.h" -#include "libopensc/cardctl.h" -#include "libopensc/log.h" -#include "pkcs15-init.h" -#include "profile.h" - -#define MIOCOS_PIN_ID_MIN 1 -#define MIOCOS_PIN_ID_MAX 15 - -/* - * Allocate a file - */ -static int -miocos_new_file(struct sc_profile *profile, sc_card_t *card, - unsigned int type, unsigned int num, - sc_file_t **out) -{ - struct sc_file *file; - struct sc_path *p; - char name[64]; - const char *tag = NULL, *desc = NULL; - - while (1) { - switch (type) { - case SC_PKCS15_TYPE_PRKEY_RSA: - desc = "RSA private key"; - tag = "private-key"; - break; - case SC_PKCS15_TYPE_PUBKEY_RSA: - desc = "RSA public key"; - tag = "public-key"; - break; - case SC_PKCS15_TYPE_PRKEY: - desc = "extractable private key"; - tag = "extractable-key"; - break; - case SC_PKCS15_TYPE_CERT: - desc = "certificate"; - tag = "certificate"; - break; - case SC_PKCS15_TYPE_DATA_OBJECT: - desc = "data object"; - tag = "data"; - break; - } - if (tag) - break; - /* If this is a specific type such as - * SC_PKCS15_TYPE_CERT_FOOBAR, fall back to - * the generic class (SC_PKCS15_TYPE_CERT) - */ - if (!(type & ~SC_PKCS15_TYPE_CLASS_MASK)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "File type not supported by card driver"); - return SC_ERROR_INVALID_ARGUMENTS; - } - type &= SC_PKCS15_TYPE_CLASS_MASK; - } - - snprintf(name, sizeof(name), "template-%s", tag); - if (sc_profile_get_file(profile, name, &file) < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Profile doesn't define %s template (%s)", - desc, name); - return SC_ERROR_NOT_SUPPORTED; - } - - /* Now construct file from template */ - file->id += num; - - p = &file->path; - *p = profile->df_info->file->path; - p->value[p->len++] = file->id >> 8; - p->value[p->len++] = file->id; - - *out = file; - return 0; -} - -static int -miocos_update_private_key(struct sc_profile *profile, sc_card_t *card, - struct sc_pkcs15_prkey_rsa *rsa) -{ - int r; - u8 buf[266]; - - memcpy(buf, "\x30\x82\x01\x06\x80\x81\x80", 7); - memcpy(buf + 7, rsa->modulus.data, 128); - memcpy(buf + 7 + 128, "\x82\x81\x80", 3); - memcpy(buf + 10 + 128, rsa->d.data, 128); - r = sc_update_binary(card, 0, buf, sizeof(buf), 0); - - return r; -} - -/* - * Initialize the Application DF - */ -static int -miocos_create_dir(struct sc_profile *profile, sc_pkcs15_card_t *p15card, - struct sc_file *df) -{ - /* Create the application DF */ - if (sc_pkcs15init_create_file(profile, p15card, profile->df_info->file)) - return 1; - - return 0; -} - -/* - * Validate PIN reference - */ -static int -miocos_select_pin_reference(struct sc_profile *profile, sc_pkcs15_card_t *p15card, - struct sc_pkcs15_auth_info *auth_info) -{ - if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) - return SC_ERROR_OBJECT_NOT_VALID; - - if (auth_info->attrs.pin.reference < MIOCOS_PIN_ID_MIN) - auth_info->attrs.pin.reference = MIOCOS_PIN_ID_MIN; - - return SC_SUCCESS; -} - -/* - * Create new PIN - */ -static int -miocos_create_pin(struct sc_profile *profile, sc_pkcs15_card_t *p15card, struct sc_file *df, - struct sc_pkcs15_object *pin_obj, - const u8 *pin, size_t pin_len, - const u8 *puk, size_t puk_len) -{ - struct sc_context *ctx = p15card->card->ctx; - struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; - struct sc_pkcs15_pin_attributes *pin_attrs = &auth_info->attrs.pin; - struct sc_pkcs15_auth_info tmpinfo; - struct sc_cardctl_miocos_ac_info ac_info; - int r; - - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); - /* Ignore SOPIN */ - if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) - return SC_SUCCESS; - - auth_info->path = profile->df_info->file->path; - r = sc_select_file(p15card->card, &auth_info->path, NULL); - if (r) - return r; - memset(&ac_info, 0, sizeof(ac_info)); - ac_info.ref = pin_attrs->reference; - sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &tmpinfo); - ac_info.max_tries = tmpinfo.tries_left; - sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &tmpinfo); - ac_info.max_unblock_tries = tmpinfo.tries_left; - if (pin_len > 8) - pin_len = 8; - memcpy(ac_info.key_value, pin, pin_len); - if (puk_len > 8) - puk_len = 8; - strncpy((char *) ac_info.unblock_value, (const char *) puk, puk_len); - r = sc_card_ctl(p15card->card, SC_CARDCTL_MIOCOS_CREATE_AC, &ac_info); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Miocos create AC failed"); - - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); -} - - -/* - * Create private key file - */ -static int -miocos_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, - struct sc_pkcs15_object *object) -{ - struct sc_context *ctx = p15card->card->ctx; - struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)object->data; - struct sc_file *file; - int r; - - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); - if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "MioCOS supports only 1024-bit RSA keys."); - - if (key_info->modulus_length != 1024) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "MioCOS supports only 1024-bit RSA keys."); - - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "create private key ID:%s\n", sc_pkcs15_print_id(&key_info->id)); - r = miocos_new_file(profile, p15card->card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot create key: failed to allocate new key object"); - - memcpy(&file->path, &key_info->path, sizeof(file->path)); - file->id = file->path.value[file->path.len - 2] * 0x100 - + file->path.value[file->path.len - 1]; - - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Path of private key file to create %s\n", sc_print_path(&file->path)); - - r = sc_pkcs15init_create_file(profile, p15card, file); - sc_file_free(file); - - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); -} - - -/* - * Store a private key - */ -static int -miocos_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, - struct sc_pkcs15_object *object, - struct sc_pkcs15_prkey *key) -{ - struct sc_context *ctx = p15card->card->ctx; - struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)object->data; - struct sc_pkcs15_prkey_rsa *rsa; - struct sc_file *file = NULL; - int r; - - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); - if (object->type != SC_PKCS15_TYPE_PRKEY_RSA - || key->algorithm != SC_ALGORITHM_RSA) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "MioCOS supports only 1024-bit RSA keys."); - - rsa = &key->u.rsa; - if (rsa->modulus.len != 128) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "MioCOS supports only 1024-bit RSA keys."); - - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "store key with ID:%s and path:%s\n", sc_pkcs15_print_id(&key_info->id), - sc_print_path(&key_info->path)); - - r = sc_select_file(p15card->card, &key_info->path, &file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot store key: select key file failed"); - - r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "No authorisation to store private key"); - - r = miocos_update_private_key(profile, p15card->card, rsa); - - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); -} - -static struct sc_pkcs15init_operations sc_pkcs15init_miocos_operations = { - NULL, /* erase_card */ - NULL, /* init_card */ - miocos_create_dir, - NULL, /* create_domain */ - miocos_select_pin_reference, - miocos_create_pin, - NULL, /* select_key_reference */ - miocos_create_key, - miocos_store_key, - NULL, /* generate_key */ - NULL, NULL, /* encode private/public key */ - NULL, /* finalize_card */ - NULL, /* delete_object */ - NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ - NULL /* sanity_check */ -}; - -struct sc_pkcs15init_operations *sc_pkcs15init_get_miocos_ops(void) -{ - return &sc_pkcs15init_miocos_operations; -} diff --git a/src/pkcs15init/pkcs15-muscle.c b/src/pkcs15init/pkcs15-muscle.c index bcd4fd314b..2669af1877 100644 --- a/src/pkcs15init/pkcs15-muscle.c +++ b/src/pkcs15init/pkcs15-muscle.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -49,8 +49,11 @@ static int muscle_erase_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) sc_format_path("3F00", &path); if ((r = sc_select_file(p15card->card, &path, &file)) < 0) return r; - if ((r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_ERASE)) < 0) + if ((r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_ERASE)) < 0) { + sc_file_free(file); return r; + } + sc_file_free(file); if ((r = sc_delete_file(p15card->card, &path)) < 0) return r; return 0; @@ -72,8 +75,12 @@ muscle_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *d sc_format_path("3F00", &path); if ((r = sc_select_file(p15card->card, &path, &file)) < 0) return r; - if ((r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_CREATE)) < 0) + if ((r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_CREATE)) < 0) { + sc_file_free(file); return r; + } + sc_file_free(file); + /* Create the application DF */ if ((r = sc_pkcs15init_create_file(profile, p15card, df)) < 0) return r; @@ -97,10 +104,13 @@ muscle_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, if ((r = sc_select_file(p15card->card, &df->path, &file)) < 0) return r; - if ((r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_WRITE)) < 0) + if ((r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_WRITE)) < 0) { + sc_file_free(file); return r; + } auth_info->attrs.pin.flags &= ~SC_PKCS15_PIN_FLAG_LOCAL; + sc_file_free(file); return 0; } @@ -171,13 +181,14 @@ muscle_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Muscle supports RSA keys only."); + sc_log(ctx, "Muscle supports RSA keys only."); return SC_ERROR_NOT_SUPPORTED; } /* Verification stuff */ /* Used for verification AND for obtaining private key acls */ r = sc_profile_get_file_by_path(profile, &key_info->path, &prkf); - if(!prkf) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); + if (r < 0 || !prkf) + SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); r = sc_pkcs15init_authenticate(profile, p15card, prkf, SC_AC_OP_CRYPTO); if (r < 0) { sc_file_free(prkf); @@ -209,7 +220,7 @@ muscle_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, r = sc_card_ctl(p15card->card, SC_CARDCTL_MUSCLE_IMPORT_KEY, &info); if (r < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Unable to import key"); + sc_log(ctx, "Unable to import key"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE,r); } return r; @@ -225,23 +236,24 @@ muscle_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; sc_card_t *card = p15card->card; sc_file_t* prkf; - unsigned int keybits; + size_t keybits; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Muscle supports only RSA keys (for now)."); + sc_log(card->ctx, "Muscle supports only RSA keys (for now)."); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); } keybits = key_info->modulus_length & ~7UL; if (keybits > 2048) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unable to generate key, max size is %d", + sc_log(card->ctx, "Unable to generate key, max size is %d", 2048); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INVALID_ARGUMENTS); } /* Verification stuff */ /* Used for verification AND for obtaining private key acls */ r = sc_profile_get_file_by_path(profile, &key_info->path, &prkf); - if(!prkf) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); + if(r < 0 || !prkf) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); r = sc_pkcs15init_authenticate(profile, p15card, prkf, SC_AC_OP_CRYPTO); if (r < 0) { sc_file_free(prkf); @@ -262,7 +274,7 @@ muscle_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, r = sc_card_ctl(card, SC_CARDCTL_MUSCLE_GENERATE_KEY, &args); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unable to generate key"); + sc_log(card->ctx, "Unable to generate key"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); } @@ -273,7 +285,7 @@ muscle_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, extArgs.keyLocation = args.publicKeyLocation; r = sc_card_ctl(card, SC_CARDCTL_MUSCLE_EXTRACT_KEY, &extArgs); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unable to extract the public key"); + sc_log(card->ctx, "Unable to extract the public key"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); } diff --git a/src/pkcs15init/pkcs15-myeid.c b/src/pkcs15init/pkcs15-myeid.c index 4931027d82..612efd7493 100644 --- a/src/pkcs15init/pkcs15-myeid.c +++ b/src/pkcs15init/pkcs15-myeid.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -27,6 +27,7 @@ #include "libopensc/opensc.h" #include "libopensc/cardctl.h" +#include "libopensc/internal.h" #include "libopensc/log.h" #include "pkcs15-init.h" #include "profile.h" @@ -36,11 +37,25 @@ #undef KEEP_AC_NONE_FOR_INIT_APPLET #define MYEID_MAX_PINS 14 -#define MYEID_MAX_RSA_KEY_LEN 2048 +#define MYEID_MAX_RSA_KEY_LEN 4096 unsigned char MYEID_DEFAULT_PUBKEY[] = {0x01, 0x00, 0x01}; #define MYEID_DEFAULT_PUBKEY_LEN sizeof(MYEID_DEFAULT_PUBKEY) +#define MYEID_PROP_INFO_2_EXCTRACTABLE 0x08; +#define MYEID_PROP_INFO_1_TRUSTED 0x04; +#define MYEID_PROP_INFO_1_WRAP_WITH_TRUSTED 0x08; +#define MYEID_PROP_INFO_2_SESSION_OBJECT 0x01; + +static const struct sc_object_id id_aes128_ecb = { { 2, 16, 840, 1, 101, 3, 4, 1, 1, -1 } }; +static const struct sc_object_id id_aes128_cbc = { { 2, 16, 840, 1, 101, 3, 4, 1, 2, -1 } }; +static const struct sc_object_id id_aes256_ecb = { { 2, 16, 840, 1, 101, 3, 4, 1, 41, -1 } }; +static const struct sc_object_id id_aes256_cbc = { { 2, 16, 840, 1, 101, 3, 4, 1, 42, -1 } }; + +static void +_add_supported_algo(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, + unsigned operations, unsigned mechanism, const struct sc_object_id *oid); + /* For Myeid, all objects are files that can be deleted in any order */ static int myeid_delete_object(struct sc_profile *profile, struct sc_pkcs15_card *p15card, @@ -154,6 +169,8 @@ myeid_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) { /* ACLs are not actives if file is not in the operational state */ if (mf->status == SC_FILE_STATUS_ACTIVATED) r = sc_pkcs15init_authenticate(profile, p15card, mf, SC_AC_OP_DELETE); + if (r < 0) + sc_file_free(mf); LOG_TEST_RET(ctx, r, "'DELETE' authentication failed on MF"); data_obj.P1 = 0x01; @@ -162,6 +179,7 @@ myeid_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) { data_obj.DataLen = sizeof (data); r = sc_card_ctl(p15card->card, SC_CARDCTL_MYEID_PUTDATA, &data_obj); + sc_file_free(mf); LOG_FUNC_RETURN(p15card->card->ctx, r); } @@ -173,17 +191,22 @@ myeid_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { struct sc_path path; struct sc_file *file = NULL; - u8 rbuf[256]; + u8 rbuf[256]; int r; LOG_FUNC_CALLED(p15card->card->ctx); p15card->tokeninfo->flags = SC_PKCS15_TOKEN_PRN_GENERATION | SC_PKCS15_TOKEN_EID_COMPLIANT; - r = sc_card_ctl(p15card->card, SC_CARDCTL_GET_SERIALNR, &rbuf); + _add_supported_algo(profile, p15card, NULL, SC_PKCS15_ALGO_OP_DECIPHER|SC_PKCS15_ALGO_OP_ENCIPHER, CKM_AES_ECB, &id_aes128_ecb); + _add_supported_algo(profile, p15card, NULL, SC_PKCS15_ALGO_OP_DECIPHER|SC_PKCS15_ALGO_OP_ENCIPHER, CKM_AES_CBC, &id_aes128_cbc); + _add_supported_algo(profile, p15card, NULL, SC_PKCS15_ALGO_OP_DECIPHER|SC_PKCS15_ALGO_OP_ENCIPHER, CKM_AES_ECB, &id_aes256_ecb); + _add_supported_algo(profile, p15card, NULL, SC_PKCS15_ALGO_OP_DECIPHER|SC_PKCS15_ALGO_OP_ENCIPHER, CKM_AES_CBC, &id_aes256_cbc); + + r = sc_card_ctl(p15card->card, SC_CARDCTL_GET_SERIALNR, &rbuf); LOG_TEST_RET(p15card->card->ctx, r, "Get applet info failed"); - sc_format_path("3F00", &path); + sc_format_path("3F00", &path); r = sc_select_file(p15card->card, &path, &file); sc_file_free(file); @@ -232,6 +255,7 @@ myeid_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df for (ii = 0; create_dfs[ii]; ii++) { sc_log(ctx, "Create '%s'", create_dfs[ii]); + file = NULL; r = sc_profile_get_file(profile, create_dfs[ii], &file); sc_file_free(file); if (r) { @@ -289,7 +313,7 @@ myeid_create_pin(struct sc_profile *profile, struct sc_pkcs15_card *p15card, unsigned char data[20]; struct sc_cardctl_myeid_data_obj data_obj; struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *) pin_obj->data; - struct sc_pkcs15_auth_info puk_ainfo; + struct sc_pkcs15_auth_info puk_ainfo = {0}; int r; LOG_FUNC_CALLED(ctx); @@ -394,6 +418,11 @@ myeid_new_file(sc_profile_t *profile, sc_card_t *card, file->id += num; p = &file->path; *p = profile->df_info->file->path; + if (p->len >= SC_MAX_PATH_SIZE - 2) { + sc_log(card->ctx, "Wrong path length"); + sc_file_free(file); + return SC_ERROR_INTERNAL; + } p->value[p->len++] = (u8) (file->id / 256); p->value[p->len++] = (u8) (file->id % 256); @@ -427,13 +456,23 @@ myeid_encode_public_key(sc_profile_t *profile, sc_card_t *card, } /* + * Add AlgorithmInfo of a supported algorithm to supportedAlgorithms field in tokenInfo. If object != NULL, + * add reference to the algorithmInfo to the passed object. */ static void _add_supported_algo(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, unsigned operations, unsigned mechanism, const struct sc_object_id *oid) { struct sc_supported_algo_info *algo; - algo = sc_pkcs15_get_supported_algo(p15card, operations, mechanism); + struct sc_context *ctx = p15card->card->ctx; + if (oid == NULL) { + sc_log(ctx, "Failed to add algorithms refs - invalid arguments."); + return; + } + algo = sc_pkcs15_get_specific_supported_algo(p15card, operations, mechanism, oid); + int rv; + + LOG_FUNC_CALLED(ctx); if (!algo) { unsigned ref = 1, ii; @@ -451,16 +490,19 @@ _add_supported_algo(struct sc_profile *profile, struct sc_pkcs15_card *p15card, } } - sc_pkcs15_add_supported_algo_ref(object, algo); + if (object != NULL) + rv = sc_pkcs15_add_supported_algo_ref(object, algo); + else + rv = SC_SUCCESS; + + if (rv != SC_SUCCESS) { + sc_log(ctx, "Failed to add algorithms refs"); + } } static void myeid_fixup_supported_algos(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) { - static const struct sc_object_id id_aes128_ecb = { { 2, 16, 840, 1, 101, 3, 4, 1, 1, -1 } }; - static const struct sc_object_id id_aes128_cbc = { { 2, 16, 840, 1, 101, 3, 4, 1, 2, -1 } }; - static const struct sc_object_id id_aes256_ecb = { { 2, 16, 840, 1, 101, 3, 4, 1, 41, -1 } }; - static const struct sc_object_id id_aes256_cbc = { { 2, 16, 840, 1, 101, 3, 4, 1, 42, -1 } }; struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_skey_info *skey_info = (struct sc_pkcs15_skey_info *) object->data; @@ -468,11 +510,11 @@ myeid_fixup_supported_algos(struct sc_profile *profile, struct sc_pkcs15_card *p switch (object->type) { case SC_PKCS15_TYPE_SKEY_GENERIC: switch (skey_info->key_type | (skey_info->value_len << 16)) { - case CKM_AES_ECB | (128 << 16): + case CKK_AES | (128 << 16): _add_supported_algo(profile, p15card, object, SC_PKCS15_ALGO_OP_DECIPHER|SC_PKCS15_ALGO_OP_ENCIPHER, CKM_AES_ECB, &id_aes128_ecb); _add_supported_algo(profile, p15card, object, SC_PKCS15_ALGO_OP_DECIPHER|SC_PKCS15_ALGO_OP_ENCIPHER, CKM_AES_CBC, &id_aes128_cbc); break; - case CKM_AES_ECB | (256 << 16): + case CKK_AES | (256 << 16): _add_supported_algo(profile, p15card, object, SC_PKCS15_ALGO_OP_DECIPHER|SC_PKCS15_ALGO_OP_ENCIPHER, CKM_AES_ECB, &id_aes256_ecb); _add_supported_algo(profile, p15card, object, SC_PKCS15_ALGO_OP_DECIPHER|SC_PKCS15_ALGO_OP_ENCIPHER, CKM_AES_CBC, &id_aes256_cbc); break; @@ -499,7 +541,10 @@ myeid_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *pin_object = NULL; struct sc_pkcs15_auth_info *pkcs15_auth_info = NULL; unsigned char sec_attrs[] = {0xFF, 0xFF, 0xFF}; - int r, ef_structure = 0, keybits = 0, pin_reference = -1; + int r, ef_structure = 0, pin_reference = -1; + size_t keybits = 0; + unsigned char prop_info[] = {0x00, 0x00}; + int extractable = FALSE; LOG_FUNC_CALLED(card->ctx); @@ -516,16 +561,24 @@ myeid_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, case SC_PKCS15_TYPE_SKEY_3DES: ef_structure = SC_CARDCTL_MYEID_KEY_DES; keybits = skey_info->value_len; + if ((skey_info->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE) == SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE) + extractable = TRUE; break; case SC_PKCS15_TYPE_SKEY_GENERIC: keybits = skey_info->value_len; + if ((skey_info->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE) == SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE) + extractable = TRUE; switch (skey_info->key_type) { - case CKM_AES_ECB: + case CKK_AES: ef_structure = SC_CARDCTL_MYEID_KEY_AES; break; - case CKM_DES_ECB: + case CKK_DES: ef_structure = SC_CARDCTL_MYEID_KEY_DES; break; + default: + if (object->type == SC_PKCS15_TYPE_SKEY_GENERIC) + ef_structure = SC_CARDCTL_MYEID_KEY_GENERIC_SECRET; + break; } break; } @@ -552,10 +605,12 @@ myeid_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, r = myeid_new_file(profile, card, object->type, *key_reference, &file); LOG_TEST_RET(ctx, r, "Cannot get new MyEID key file"); - if (!file || !file->path.len) + if (!file || !file->path.len || file->path.len > SC_MAX_PATH_SIZE) { + sc_file_free(file); LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Cannot determine key file"); + } - sc_log(ctx, "Key file size %d", keybits); + sc_log(ctx, "Key file size %zu", keybits); file->size = keybits; file->ef_structure = ef_structure; @@ -597,6 +652,22 @@ myeid_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid AuthID value for a private key."); } + /* TODO: fill all proprietary attributes here based on the object */ + + if (object->user_consent != 0 && pin_reference >= 1) + prop_info[0] |= (pin_reference << 4); + + if (extractable) + prop_info[1] |= MYEID_PROP_INFO_2_EXCTRACTABLE; + + if (object->session_object != 0) /* Object will be removed during next reset. */ + prop_info[1] |= MYEID_PROP_INFO_2_SESSION_OBJECT; + + /* TODO: add other flags, like CKA_TRUSTED and CKA_WRAP_WITH_TRUSTED */ + + r = sc_file_set_prop_attr(file, prop_info, 2); + LOG_TEST_RET(ctx, r, "Cannot create MyEID key file"); + /* Now create the key file */ r = sc_pkcs15init_create_file(profile, p15card, file); sc_file_free(file); @@ -639,9 +710,8 @@ myeid_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, LOG_TEST_RET(ctx, r, "Cannot store MyEID key: select key file failed"); r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); - LOG_TEST_RET(ctx, r, "No authorisation to store MyEID private key"); - sc_file_free(file); + LOG_TEST_RET(ctx, r, "No authorisation to store MyEID private key"); /* Fill in data structure */ memset(&args, 0, sizeof (args)); @@ -712,7 +782,7 @@ myeid_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, int r; unsigned int cla,tag; size_t taglen; - size_t keybits = key_info->modulus_length; + unsigned int keybits = (unsigned int)key_info->modulus_length; u8 raw_pubkey[MYEID_MAX_RSA_KEY_LEN / 8]; u8* dataptr; @@ -729,20 +799,19 @@ myeid_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, case SC_PKCS15_TYPE_PRKEY_EC: /* EC is supported in MyEID v > 3.5. TODO: set correct return value if older MyEID version. */ /* Here the information about curve is not available, that's why supported algorithm is checked - without curve OID. */ - + without curve OID. */ + if(key_info->field_length != 0) - keybits = key_info->field_length; + keybits = (unsigned int)key_info->field_length; else key_info->field_length = keybits; - + if (sc_card_find_ec_alg(p15card->card, keybits, NULL) == NULL) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unsupported EC key size"); - + break; default: LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unsupported key type"); - break; } sc_log(ctx, "Generate key with ID:%s and path:%s", @@ -752,6 +821,8 @@ myeid_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, LOG_TEST_RET(ctx, r, "Cannot generate key: failed to select key file"); r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_GENERATE); + if (r < 0) + sc_file_free(file); LOG_TEST_RET(ctx, r, "No authorisation to generate private key"); /* Fill in data structure */ @@ -768,22 +839,30 @@ myeid_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, /* Generate the key */ r = sc_card_ctl(card, SC_CARDCTL_MYEID_GENERATE_STORE_KEY, &args); + if (r < 0) + sc_file_free(file); LOG_TEST_RET(ctx, r, "Card control 'MYEID_GENERATE_STORE_KEY' failed"); - /* Keypair generation -> collect public key info */ + /* Key pair generation -> collect public key info */ if (pubkey != NULL) { struct sc_cardctl_myeid_data_obj data_obj; if (object->type == SC_PKCS15_TYPE_PRKEY_RSA) { pubkey->algorithm = SC_ALGORITHM_RSA; - pubkey->u.rsa.modulus.len = (keybits + 7) / 8; + pubkey->u.rsa.modulus.len = BYTES4BITS(keybits); pubkey->u.rsa.modulus.data = malloc(pubkey->u.rsa.modulus.len); + if (pubkey->u.rsa.modulus.data == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); pubkey->u.rsa.exponent.len = MYEID_DEFAULT_PUBKEY_LEN; pubkey->u.rsa.exponent.data = malloc(MYEID_DEFAULT_PUBKEY_LEN); + if (pubkey->u.rsa.exponent.data == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(pubkey->u.rsa.exponent.data, MYEID_DEFAULT_PUBKEY, MYEID_DEFAULT_PUBKEY_LEN); /* Get public key modulus */ r = sc_select_file(card, &file->path, NULL); + sc_file_free(file); + file = NULL; LOG_TEST_RET(ctx, r, "Cannot get key modulus: select key file failed"); data_obj.P1 = 0x01; @@ -809,6 +888,8 @@ myeid_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, pubkey->algorithm = SC_ALGORITHM_EC; r = sc_select_file(card, &file->path, NULL); + sc_file_free(file); + file = NULL; LOG_TEST_RET(ctx, r, "Cannot get public key: select key file failed"); data_obj.P1 = 0x01; @@ -821,6 +902,8 @@ myeid_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, dataptr = data_obj.Data; r = sc_asn1_read_tag((const u8 **)&dataptr, data_obj.DataLen, &cla, &tag, &taglen); + if (dataptr == NULL) + r = SC_ERROR_ASN1_OBJECT_NOT_FOUND; LOG_TEST_RET(ctx, r, "Invalid EC public key data. Cannot parse DER structure."); if (taglen == 0) @@ -846,9 +929,14 @@ myeid_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, pubkey->u.ec.params.der.len = 0; pubkey->u.ec.params.named_curve = strdup(ecparams->named_curve); - if (!pubkey->u.ec.params.named_curve) + if (!pubkey->u.ec.params.named_curve) { + free(pubkey->u.ec.ecpointQ.value); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + } + r = sc_pkcs15_fix_ec_parameters(ctx, &pubkey->u.ec.params); + if (r < 0) + free(pubkey->u.ec.ecpointQ.value); LOG_TEST_RET(ctx, r, "Cannot fix EC parameters"); } } diff --git a/src/pkcs15init/pkcs15-oberthur-awp.c b/src/pkcs15init/pkcs15-oberthur-awp.c index f55f3d33b5..ced9eb9765 100644 --- a/src/pkcs15init/pkcs15-oberthur-awp.c +++ b/src/pkcs15init/pkcs15-oberthur-awp.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * best view with tabstop=4 * @@ -37,6 +37,7 @@ #include "libopensc/asn1.h" #ifdef ENABLE_OPENSSL +#include "libopensc/sc-ossl-compat.h" struct awp_lv zero_lv = { 0, NULL }; struct awp_lv x30_lv = { 0x10, (unsigned char *)"0000000000000000" }; @@ -50,21 +51,21 @@ awp_get_commonName(X509 *x) r = X509_NAME_get_index_by_NID(X509_get_subject_name(x), NID_commonName, -1); if (r >= 0) { - X509_NAME_ENTRY *ne; - ASN1_STRING *a_str; + const X509_NAME_ENTRY *ne; + const ASN1_STRING *a_str; if (!(ne = X509_NAME_get_entry(X509_get_subject_name(x), r))) ; else if (!(a_str = X509_NAME_ENTRY_get_data(ne))) ; - else if (a_str->type == 0x0C) { - ret = malloc(a_str->length + 1); + else if (ASN1_STRING_type(a_str) == V_ASN1_UTF8STRING) { + ret = malloc(ASN1_STRING_length(a_str) + 1); if (ret) { - memcpy(ret, a_str->data, a_str->length); - *(ret + a_str->length) = '\0'; + memcpy(ret, ASN1_STRING_get0_data(a_str), ASN1_STRING_length(a_str)); + + *(ret + ASN1_STRING_length(a_str)) = '\0'; } - } - else { + } else { unsigned char *tmp = NULL; r = ASN1_STRING_to_UTF8(&tmp, a_str); @@ -94,8 +95,8 @@ awp_new_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, char name[NAME_MAX_LEN]; const char *itag=NULL, *otag=NULL; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "type 0x%X; num %i; info %p; obj %p", type, num, info_out, obj_out); + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "type 0x%X; num %i; info %p; obj %p", type, num, info_out, obj_out); switch (type) { case SC_PKCS15_TYPE_CERT_X509: itag = "certificate-info"; @@ -142,18 +143,18 @@ awp_new_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, if (itag) { snprintf(name, sizeof(name),"%s-%s", COSM_TITLE, itag); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "info template %s",name); + sc_log(ctx, "info template %s",name); if (sc_profile_get_file(profile, name, &ifile) < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "profile does not defines template '%s'", name); + sc_log(ctx, "profile does not defines template '%s'", name); return SC_ERROR_INCONSISTENT_PROFILE; } } if (otag) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "obj template %s",otag); + sc_log(ctx, "obj template %s",otag); if (sc_profile_get_file(profile, otag, &ofile) < 0) { sc_file_free(ifile); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "profile does not defines template '%s'", name); + sc_log(ctx, "profile does not defines template '%s'", name); return SC_ERROR_INCONSISTENT_PROFILE; } @@ -170,8 +171,8 @@ awp_new_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, ifile->path.value[ifile->path.len-2] |= 0x01; } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "info_file(id:%04X,size:%"SC_FORMAT_LEN_SIZE_T"u,rlen:%i)", + sc_log(ctx, + "info_file(id:%04X,size:%"SC_FORMAT_LEN_SIZE_T"u,rlen:%"SC_FORMAT_LEN_SIZE_T"u)", ifile->id, ifile->size, ifile->record_length); *info_out = ifile; } @@ -181,7 +182,7 @@ awp_new_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, } if (ofile) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "obj file %04X; size %"SC_FORMAT_LEN_SIZE_T"u; ", ofile->id, ofile->size); if (obj_out) @@ -190,7 +191,7 @@ awp_new_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, sc_file_free(ofile); } - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -201,7 +202,7 @@ awp_update_blob(struct sc_context *ctx, { unsigned char *pp; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); switch (type) { case TLV_TYPE_LLV : if (!(pp = realloc(*blob, *blob_size + 2 + lv->len))) @@ -219,36 +220,38 @@ awp_update_blob(struct sc_context *ctx, *blob_size += 1 + lv->len; break; case TLV_TYPE_V : + if (0 == *blob_size + lv->len) + return SC_ERROR_INVALID_DATA; if (!(pp = realloc(*blob, *blob_size + lv->len))) return SC_ERROR_OUT_OF_MEMORY; memcpy(pp + *blob_size, lv->value, lv->len); *blob_size += lv->len; break; default: - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Invalid tlv type %i",type); + sc_log(ctx, "Invalid tlv type %i",type); return SC_ERROR_INCORRECT_PARAMETERS; } *blob = pp; - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int -awp_new_container_entry(struct sc_pkcs15_card *p15card, unsigned char *buff, int len) +awp_new_container_entry(struct sc_pkcs15_card *p15card, unsigned char *buff, size_t len) { struct sc_context *ctx = p15card->card->ctx; int mm, rv = 0; unsigned ii, marks[5] = {4,6,8,10,0}; unsigned char rand_buf[0x10]; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); if (len<0x34) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCORRECT_PARAMETERS, "Invalid container update size"); + LOG_TEST_RET(ctx, SC_ERROR_INCORRECT_PARAMETERS, "Invalid container update size"); rv = sc_get_challenge(p15card->card, rand_buf, sizeof(rand_buf)); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot get challenge"); + LOG_TEST_RET(ctx, rv, "Cannot get challenge"); *(buff + 12) = 0x26; *(buff + 13) = '{'; @@ -261,7 +264,7 @@ awp_new_container_entry(struct sc_pkcs15_card *p15card, unsigned char *buff, int } *(buff + 14 + ii*2 + mm) = (unsigned char)'}'; - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -273,20 +276,21 @@ awp_create_container_record (struct sc_pkcs15_card *p15card, struct sc_profile * int rv; unsigned char *buff = NULL; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "container file(file-id:%X,rlen:%i,rcount:%i)", + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "container file(file-id:%X,rlen:%"SC_FORMAT_LEN_SIZE_T"u,rcount:%"SC_FORMAT_LEN_SIZE_T"u)", list_file->id, list_file->record_length, list_file->record_count); buff = malloc(list_file->record_length); if (!buff) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memset(buff, 0, list_file->record_length); rv = awp_new_container_entry(p15card, buff, list_file->record_length); - if (rv < 0) { + if (rv < 0) { free(buff); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot create container"); + sc_log(ctx, "Cannot create container"); + LOG_FUNC_RETURN(ctx, rv); } *(buff + 0) = (acc->pubkey_id >> 8) & 0xFF; @@ -304,7 +308,7 @@ awp_create_container_record (struct sc_pkcs15_card *p15card, struct sc_profile * rv = sc_append_record(p15card->card, buff, list_file->record_length, SC_RECORD_BY_REC_NR); free(buff); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -316,48 +320,48 @@ awp_create_container(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *clist = NULL, *file = NULL; int rv = 0; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "create container(%X:%X:%X)", acc->prkey_id, acc->cert_id, acc->pubkey_id); + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "create container(%X:%X:%X)", acc->prkey_id, acc->cert_id, acc->pubkey_id); rv = awp_new_file(p15card, profile, COSM_CONTAINER_LIST, 0, &clist, NULL); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Create container failed"); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "contaner cfile(rcount:%i,rlength:%i)", clist->record_count, clist->record_length); + LOG_TEST_RET(ctx, rv, "Create container failed"); + sc_log(ctx, "container cfile(rcount:%"SC_FORMAT_LEN_SIZE_T"u,rlength:%"SC_FORMAT_LEN_SIZE_T"u)", clist->record_count, clist->record_length); rv = sc_select_file(p15card->card, &clist->path, &file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Create container failed: cannot select container's list"); + LOG_TEST_RET(ctx, rv, "Create container failed: cannot select container's list"); file->record_length = clist->record_length; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "contaner file(rcount:%i,rlength:%i)", file->record_count, file->record_length); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Append new record %i for private key", file->record_count + 1); + sc_log(ctx, "container file(rcount:%"SC_FORMAT_LEN_SIZE_T"u,rlength:%"SC_FORMAT_LEN_SIZE_T"u)", file->record_count, file->record_length); + sc_log(ctx, "Append new record %"SC_FORMAT_LEN_SIZE_T"u for private key", file->record_count + 1); rv = awp_create_container_record(p15card, profile, file, acc); sc_file_free(file); sc_file_free(clist); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } static int awp_update_container_entry (struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *list_file, int type, int file_id, - int rec, int offs) + size_t rec, int offs) { struct sc_context *ctx = p15card->card->ctx; int rv; unsigned char *buff = NULL; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "update container entry(type:%X,id %i,rec %i,offs %i", + LOG_FUNC_CALLED(ctx); + sc_log(ctx, + "update container entry(type:%X,id %i,rec %"SC_FORMAT_LEN_SIZE_T"u,offs %i", type, file_id, rec, offs); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "container file(file-id:%X,rlen:%i,rcount:%i)", + sc_log(ctx, "container file(file-id:%X,rlen:%"SC_FORMAT_LEN_SIZE_T"u,rcount:%"SC_FORMAT_LEN_SIZE_T"u)", list_file->id, list_file->record_length, list_file->record_count); buff = malloc(list_file->record_length); if (!buff) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memset(buff, 0, list_file->record_length); @@ -367,18 +371,18 @@ awp_update_container_entry (struct sc_pkcs15_card *p15card, struct sc_profile *p else { rv = sc_select_file(p15card->card, &list_file->path, NULL); if (!rv) - rv = sc_read_record(p15card->card, rec, buff, list_file->record_length, SC_RECORD_BY_REC_NR); + rv = sc_read_record(p15card->card, (unsigned int)rec, 0, buff, list_file->record_length, SC_RECORD_BY_REC_NR); } if (rv < 0) { free(buff); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } switch (type) { case SC_PKCS15_TYPE_PUBKEY_RSA: case COSM_TYPE_PUBKEY_RSA: if (*(buff + offs + 4)) - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Insert public key to container that contains certificate %02X%02X", + sc_log(ctx, "Insert public key to container that contains certificate %02X%02X", *(buff + offs + 4), *(buff + offs + 5)); *(buff + offs + 0) = (file_id >> 8) & 0xFF; *(buff + offs + 1) = file_id & 0xFF; @@ -387,7 +391,7 @@ awp_update_container_entry (struct sc_pkcs15_card *p15card, struct sc_profile *p case COSM_TYPE_PRKEY_RSA: if (*(buff + offs + 2)) { free(buff); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_CARD, "private key exists already"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_CARD, "private key exists already"); } *(buff + offs + 2) = (file_id >> 8) & 0xFF; @@ -399,7 +403,7 @@ awp_update_container_entry (struct sc_pkcs15_card *p15card, struct sc_profile *p break; default: free(buff); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCORRECT_PARAMETERS); + LOG_FUNC_RETURN(ctx, SC_ERROR_INCORRECT_PARAMETERS); } if (rec > list_file->record_count) { @@ -411,11 +415,11 @@ awp_update_container_entry (struct sc_pkcs15_card *p15card, struct sc_profile *p rv = sc_append_record(p15card->card, buff, list_file->record_length, SC_RECORD_BY_REC_NR); } else { - rv = sc_update_record(p15card->card, rec, buff, list_file->record_length, SC_RECORD_BY_REC_NR); + rv = sc_update_record(p15card->card, (unsigned int)rec, 0, buff, list_file->record_length, SC_RECORD_BY_REC_NR); } free(buff); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -426,11 +430,13 @@ awp_update_container(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_context *ctx = p15card->card->ctx; struct sc_file *clist = NULL, *file = NULL; struct sc_path private_path; - int rv = 0, rec, rec_offs; + int rv = 0; + size_t rec; + int rec_offs; unsigned char *list = NULL; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "update container(type:%X,obj_id:%X)", type, obj_id); + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "update container(type:%X,obj_id:%X)", type, obj_id); if (prkey_id) *prkey_id = 0; @@ -472,7 +478,7 @@ awp_update_container(struct sc_pkcs15_card *p15card, struct sc_profile *profile, for (rec=0; rec < file->record_count; rec++) { unsigned char tmp[256]; - rv = sc_read_record(p15card->card, rec + 1, tmp, sizeof(tmp), SC_RECORD_BY_REC_NR); + rv = sc_read_record(p15card->card, (unsigned int)rec + 1, 0, tmp, sizeof(tmp), SC_RECORD_BY_REC_NR); if (rv >= AWP_CONTAINER_RECORD_LEN) memcpy(list + rec*AWP_CONTAINER_RECORD_LEN, tmp, AWP_CONTAINER_RECORD_LEN); else @@ -483,8 +489,8 @@ awp_update_container(struct sc_pkcs15_card *p15card, struct sc_profile *profile, for (rec_offs=0; !rv && rec_offs<12; rec_offs+=6) { int offs; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "rec %i; rec_offs %i", rec, rec_offs); - offs = rec*AWP_CONTAINER_RECORD_LEN + rec_offs; + sc_log(ctx, "rec %zu; rec_offs %d", rec, rec_offs); + offs = (int)rec*AWP_CONTAINER_RECORD_LEN + rec_offs; if (*(list + offs + 2)) { unsigned char *buff = NULL; int id_offs; @@ -515,7 +521,7 @@ awp_update_container(struct sc_pkcs15_card *p15card, struct sc_profile *profile, if (key_id->len == *(buff + id_offs) && !memcmp(key_id->value, buff + id_offs + 1, key_id->len)) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "found key file friend"); + sc_log(ctx, "found key file friend"); if (!rv) rv = awp_update_container_entry(p15card, profile, file, type, obj_id, rec + 1, rec_offs); @@ -539,7 +545,7 @@ awp_update_container(struct sc_pkcs15_card *p15card, struct sc_profile *profile, sc_file_free(file); if (list) free(list); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -564,7 +570,7 @@ awp_set_certificate_info (struct sc_pkcs15_card *p15card, unsigned char *blob; const char *default_cert_label = "Certificate"; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); blob_size = 2; if (!(blob = malloc(blob_size))) { r = SC_ERROR_OUT_OF_MEMORY; @@ -623,7 +629,7 @@ awp_set_certificate_info (struct sc_pkcs15_card *p15card, if (blob) free(blob); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(ctx, r); } @@ -638,9 +644,10 @@ awp_update_object_list(struct sc_pkcs15_card *p15card, struct sc_profile *profil unsigned char *buff = NULL; int rv; unsigned ii; + unsigned long flags; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "type %i, num %i", type, num); + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "type %i, num %i", type, num); switch (type) { case SC_PKCS15_TYPE_CERT_X509: snprintf(obj_name, NAME_MAX_LEN, "template-certificate"); @@ -665,19 +672,19 @@ awp_update_object_list(struct sc_pkcs15_card *p15card, struct sc_profile *profil snprintf(lst_name, NAME_MAX_LEN,"%s-private-list", COSM_TITLE); break; default: - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Not supported file type %X", type); + sc_log(ctx, "Not supported file type %X", type); return SC_ERROR_INVALID_ARGUMENTS; } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "obj_name %s; num 0x%X",obj_name, num); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "lst_name %s",lst_name); + sc_log(ctx, "obj_name %s; num 0x%X",obj_name, num); + sc_log(ctx, "lst_name %s",lst_name); if (sc_profile_get_file(profile, obj_name, &obj_file) < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "No profile template '%s'", obj_name); + sc_log(ctx, "No profile template '%s'", obj_name); rv = SC_ERROR_NOT_SUPPORTED; goto done; } else if (sc_profile_get_file(profile, lst_name, &lst_file) < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "No profile template '%s'", lst_name); + sc_log(ctx, "No profile template '%s'", lst_name); rv = SC_ERROR_NOT_SUPPORTED; goto done; } @@ -717,11 +724,17 @@ awp_update_object_list(struct sc_pkcs15_card *p15card, struct sc_profile *profil if (rv < 0) goto done; - rv = sc_read_binary(p15card->card, 0, buff, lst_file->size, lst_file->ef_structure); + if (lst_file->size < 5) { + rv = SC_ERROR_UNKNOWN_DATA_RECEIVED; + goto done; + } + + flags = lst_file->ef_structure; + rv = sc_read_binary(p15card->card, 0, buff, lst_file->size, &flags); if (rv < 0) goto done; - for (ii=0; ii < lst_file->size; ii+=5) + for (ii=0; ii <= lst_file->size-5; ii+=5) if (*(buff + ii) != COSM_LIST_TAG) break; if (ii>=lst_file->size) { @@ -729,7 +742,7 @@ awp_update_object_list(struct sc_pkcs15_card *p15card, struct sc_profile *profil goto done; } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "ii %i, rv %i; %X; %"SC_FORMAT_LEN_SIZE_T"u", ii, rv, file->id, file->size); *(buff + ii) = COSM_LIST_TAG; @@ -739,7 +752,7 @@ awp_update_object_list(struct sc_pkcs15_card *p15card, struct sc_profile *profil *(buff + ii + 4) = file->size & 0xFF; rv = sc_update_binary(p15card->card, ii, buff + ii, 5, 0); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "rv %i",rv); + sc_log(ctx, "rv %i",rv); if (rv < 0) goto done; @@ -751,7 +764,7 @@ awp_update_object_list(struct sc_pkcs15_card *p15card, struct sc_profile *profil sc_file_free(obj_file); sc_file_free(file); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -763,13 +776,11 @@ awp_encode_key_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj struct sc_pkcs15_prkey_info *key_info; int r = 0; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - ERR_load_ERR_strings(); - ERR_load_crypto_strings(); + LOG_FUNC_CALLED(ctx); key_info = (struct sc_pkcs15_prkey_info *)obj->data; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "object(%s,type:%X)", obj->label, obj->type); + sc_log(ctx, "object(%s,type:%X)", obj->label, obj->type); if (obj->type == SC_PKCS15_TYPE_PUBKEY_RSA || obj->type == COSM_TYPE_PUBKEY_RSA ) ki->flags = COSM_TAG_PUBKEY_RSA; else if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA || obj->type == COSM_TYPE_PRKEY_RSA) @@ -782,14 +793,14 @@ awp_encode_key_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj ki->label.value = (unsigned char *)strdup(obj->label); ki->label.len = strlen(obj->label); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "cosm_encode_key_info() label(%u):%s", + sc_log(ctx, + "cosm_encode_key_info() label(%zu):%s", ki->label.len, ki->label.value); /* * Oberthur saves modulus value without tag and length. */ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "pubkey->modulus.len %"SC_FORMAT_LEN_SIZE_T"u", pubkey->modulus.len); ki->modulus.value = malloc(pubkey->modulus.len); @@ -816,27 +827,22 @@ awp_encode_key_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj */ ki->id.value = calloc(1, key_info->id.len); if (!ki->id.value) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP encode cert failed: ID allocation error"); + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "AWP encode cert failed: ID allocation error"); memcpy(ki->id.value, key_info->id.value, key_info->id.len); ki->id.len = key_info->id.len; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "cosm_encode_key_info() label:%s",ki->label.value); + sc_log(ctx, "cosm_encode_key_info() label:%s",ki->label.value); done: - ERR_load_ERR_strings(); - ERR_load_crypto_strings(); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(ctx, r); } static void awp_free_key_info(struct awp_key_info *ki) { - if (ki->modulus.value) - free(ki->modulus.value); - if (ki->exponent.value) - free(ki->exponent.value); - if (ki->id.value) - free(ki->id.value); + free(ki->modulus.value); + free(ki->exponent.value); + free(ki->id.value); } @@ -848,14 +854,14 @@ awp_set_key_info (struct sc_pkcs15_card *p15card, struct sc_profile *profile, st int r = 0, blob_size; unsigned char *blob; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file:%p, kinfo:%p, cinfo:%p", file, ki, ci); + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "file:%p, kinfo:%p, cinfo:%p", file, ki, ci); blob_size = 2; blob = malloc(blob_size); if (!blob) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP set key info failed: blob allocation error"); + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "AWP set key info failed: blob allocation error"); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "label:%s",ki->label.value); + sc_log(ctx, "label:%s",ki->label.value); *blob = (ki->flags >> 8) & 0xFF; *(blob + 1) = ki->flags & 0xFF; @@ -913,7 +919,7 @@ awp_set_key_info (struct sc_pkcs15_card *p15card, struct sc_profile *profile, st if (blob) free(blob); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(ctx, r); } @@ -929,17 +935,14 @@ awp_encode_cert_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *ob BIO *mem = NULL; X509 *x = NULL; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - - ERR_load_ERR_strings(); - ERR_load_crypto_strings(); + LOG_FUNC_CALLED(ctx); if (!obj || !ci) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "AWP encode cert failed: invalid parameters"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "AWP encode cert failed: invalid parameters"); cert_info = (struct sc_pkcs15_cert_info *)obj->data; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "Encode cert(%s,id:%s,der(%p,%"SC_FORMAT_LEN_SIZE_T"u))", obj->label, sc_pkcs15_print_id(&cert_info->id), obj->content.value, obj->content.len); @@ -948,24 +951,30 @@ awp_encode_cert_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *ob ci->label.value = (unsigned char *)strdup(obj->label); ci->label.len = strlen(obj->label); - mem = BIO_new_mem_buf(obj->content.value, obj->content.len); - if (!mem) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA, "AWP encode cert failed: invalid data"); + mem = BIO_new_mem_buf(obj->content.value, (int)obj->content.len); + if (!mem) { + sc_log_openssl(ctx); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "AWP encode cert failed: invalid data"); + } x = d2i_X509_bio(mem, NULL); - if (!x) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA, "AWP encode cert failed: x509 parse error"); + if (!x) { + sc_log_openssl(ctx); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "AWP encode cert failed: x509 parse error"); + } buff = OPENSSL_malloc(i2d_X509(x,NULL) + EVP_MAX_MD_SIZE); if (!buff) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP encode cert failed: memory allocation error"); + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "AWP encode cert failed: memory allocation error"); /* * subject commonName. */ ptr = awp_get_commonName(x); - if (!ptr) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL, "AWP encode cert failed: cannot get CommonName"); + if (!ptr) { + r = SC_ERROR_INTERNAL; + LOG_TEST_GOTO_ERR(ctx, r, "AWP encode cert failed: cannot get CommonName"); + } ci->cn.value = ptr; ci->cn.len = strlen((char *)ptr); @@ -974,12 +983,17 @@ awp_encode_cert_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *ob */ ptr = buff; r = i2d_X509_NAME(X509_get_subject_name(x),&ptr); - if (r<=0) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL, "AWP encode cert failed: cannot get SubjectName"); + if (r<=0) { + sc_log_openssl(ctx); + r = SC_ERROR_INTERNAL; + LOG_TEST_GOTO_ERR(ctx, r, "AWP encode cert failed: cannot get SubjectName"); + } ci->subject.value = malloc(r); - if (!ci->subject.value) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP encode cert failed: subject allocation error"); + if (!ci->subject.value) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "AWP encode cert failed: subject allocation error"); + } memcpy(ci->subject.value, buff, r); ci->subject.len = r; @@ -988,12 +1002,17 @@ awp_encode_cert_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *ob */ ptr = buff; r = i2d_X509_NAME(X509_get_issuer_name(x),&ptr); - if (r <= 0) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL, "AWP encode cert failed: cannot get IssuerName"); + if (r <= 0) { + sc_log_openssl(ctx); + r = SC_ERROR_INTERNAL; + LOG_TEST_GOTO_ERR(ctx, r, "AWP encode cert failed: cannot get IssuerName"); + } ci->issuer.value = malloc(r); - if (!ci->issuer.value) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP encode cert failed: issuer allocation error"); + if (!ci->issuer.value) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "AWP encode cert failed: issuer allocation error"); + } memcpy(ci->issuer.value, buff, r); ci->issuer.len = r; @@ -1001,8 +1020,10 @@ awp_encode_cert_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *ob * ID */ ci->id.value = calloc(1, cert_info->id.len); - if (!ci->id.value) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP encode cert failed: ID allocation error"); + if (!ci->id.value) { + r = SC_ERROR_OUT_OF_MEMORY; + LOG_TEST_GOTO_ERR(ctx, r, "AWP encode cert failed: ID allocation error"); + } memcpy(ci->id.value, cert_info->id.value, cert_info->id.len); ci->id.len = cert_info->id.len; @@ -1010,12 +1031,12 @@ awp_encode_cert_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *ob * serial number */ #if OPENSSL_VERSION_NUMBER >= 0x10100000L - - /* TODO the der encoding of a ANS1_INTEGER is a TLV, the original code only as using the - * i2c_ASN1_INTEGER which is not in OpenSSL 1.1 + + /* TODO the der encoding of a ANS1_INTEGER is a TLV, the original code only as using the + * i2c_ASN1_INTEGER which is not in OpenSSL 1.1 * It was adding the tag V_ASN1_INTEGER and the one byte length back in in effect creating * a DER encoded ASN1_INTEGER - * So we can simplifty the code and make compatable with OpenSSL 1.1. This needs to be tested + * So we can simplify the code and make compatible with OpenSSL 1.1. This needs to be tested */ ci->serial.len = 0; ci->serial.value = NULL; @@ -1025,12 +1046,12 @@ awp_encode_cert_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *ob if (!(ci->serial.value = malloc(ci->serial.len))) { ci->serial.len = 0; r = SC_ERROR_OUT_OF_MEMORY; - goto done; + goto err; } ci->serial.len = i2d_ASN1_INTEGER(X509_get_serialNumber(x), &ci->serial.value); } /* if len == 0, and value == NULL, then the cert did not have a serial number.*/ - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "cert. serial encoded length %i", ci->serial.len); + sc_log(ctx, "cert. serial encoded length %zu", ci->serial.len); #else do { @@ -1042,7 +1063,7 @@ awp_encode_cert_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *ob if (!(ci->serial.value = malloc(encoded_len + 3))) { r = SC_ERROR_OUT_OF_MEMORY; - goto done; + goto err; } memcpy(ci->serial.value + 2, encoded, encoded_len); @@ -1050,44 +1071,45 @@ awp_encode_cert_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *ob *(ci->serial.value + 1) = encoded_len; ci->serial.len = encoded_len + 2; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "cert. serial encoded length %i", encoded_len); + sc_log(ctx, "cert. serial encoded length %i", encoded_len); } while (0); #endif ci->x509 = X509_dup(x); -done: +err: ERR_print_errors_fp(stderr); ERR_clear_error(); - ERR_free_strings(); if (pubkey.exponent.data) free(pubkey.exponent.data); if (pubkey.modulus.data) free(pubkey.modulus.data); - if (x) X509_free(x); + if (x) X509_free(x); if (mem) BIO_free(mem); if (buff) OPENSSL_free(buff); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(ctx, r); } static void awp_free_cert_info(struct awp_cert_info *ci) { - if (ci->cn.len && ci->cn.value) - free(ci->cn.value); + if (ci) { + if (ci->cn.len && ci->cn.value) + free(ci->cn.value); - if (ci->id.len && ci->id.value) - free(ci->id.value); + if (ci->id.len && ci->id.value) + free(ci->id.value); - if (ci->subject.len && ci->subject.value) - free(ci->subject.value); + if (ci->subject.len && ci->subject.value) + free(ci->subject.value); - if (ci->issuer.len && ci->issuer.value) - free(ci->issuer.value); + if (ci->issuer.len && ci->issuer.value) + free(ci->issuer.value); - if (ci->x509) - X509_free(ci->x509); + if (ci->x509) + X509_free(ci->x509); - memset(ci,0,sizeof(struct awp_cert_info)); + memset(ci,0,sizeof(struct awp_cert_info)); + } } @@ -1101,14 +1123,14 @@ awp_encode_data_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *ob unsigned char *buf = NULL; size_t buflen; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); if (!obj || !di) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "AWP encode data failed: invalid parameters"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "AWP encode data failed: invalid parameters"); data_info = (struct sc_pkcs15_data_info *)obj->data; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "Encode data(%s,id:%s,der(%p,%"SC_FORMAT_LEN_SIZE_T"u))", obj->label, sc_pkcs15_print_id(&data_info->id), obj->content.value, obj->content.len); @@ -1122,17 +1144,17 @@ awp_encode_data_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *ob if (di->app.len) { di->app.value = (unsigned char *)strdup(data_info->app_label); if (!di->app.value) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } r = sc_asn1_encode_object_id(&buf, &buflen, &data_info->app_oid); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "AWP encode data failed: cannot encode OID"); + LOG_TEST_RET(ctx, r, "AWP encode data failed: cannot encode OID"); di->oid.len = buflen + 2; di->oid.value = malloc(di->oid.len); if (!di->oid.value) { free(buf); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP encode data failed: cannot allocate OID"); + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "AWP encode data failed: cannot allocate OID"); } *(di->oid.value + 0) = 0x06; @@ -1140,7 +1162,7 @@ awp_encode_data_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *ob memcpy(di->oid.value + 2, buf, buflen); free(buf); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(ctx, r); } @@ -1168,8 +1190,8 @@ awp_set_data_info (struct sc_pkcs15_card *p15card, struct sc_profile *profile, int r = 0, blob_size; unsigned char *blob; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - sc_debug (ctx, SC_LOG_DEBUG_NORMAL, "Set 'DATA' info %p", di); + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "Set 'DATA' info %p", di); blob_size = 2; if (!(blob = malloc(blob_size))) { r = SC_ERROR_OUT_OF_MEMORY; @@ -1204,7 +1226,7 @@ awp_set_data_info (struct sc_pkcs15_card *p15card, struct sc_profile *profile, if (blob) free(blob); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(ctx, r); } @@ -1249,27 +1271,27 @@ awp_parse_key_info(struct sc_context *ctx, unsigned char *buf, size_t buf_len, size_t offs; int len; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); offs = 0; /* Flags */ if (buf_len - offs < 2) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); ikey->flags = *(buf + offs) * 0x100 + *(buf + offs + 1); offs += 2; /* Label */ len = awp_get_lv(ctx, buf, buf_len, offs, 2, &ikey->label); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, len, "AWP parse key info failed: label"); + LOG_TEST_RET(ctx, len, "AWP parse key info failed: label"); if (!len) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); offs += len; /* Ignore Key ID */ len = awp_get_lv(ctx, buf, buf_len, offs, 2, &ikey->id); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, len, "AWP parse key info failed: ID"); + LOG_TEST_RET(ctx, len, "AWP parse key info failed: ID"); if (!len) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); offs += len; while (*(buf + offs) == '0') @@ -1277,9 +1299,9 @@ awp_parse_key_info(struct sc_context *ctx, unsigned char *buf, size_t buf_len, /* Subject */ len = awp_get_lv(ctx, buf, buf_len, offs, 2, &ikey->subject); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, len, "AWP parse key info failed: subject"); + LOG_TEST_RET(ctx, len, "AWP parse key info failed: subject"); if (!len) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); offs += len; /* Modulus */ @@ -1289,18 +1311,18 @@ awp_parse_key_info(struct sc_context *ctx, unsigned char *buf, size_t buf_len, len = awp_get_lv(ctx, buf, buf_len, offs, 128, &ikey->modulus); else len = awp_get_lv(ctx, buf, buf_len, offs, 256, &ikey->modulus); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, len, "AWP parse key info failed: modulus"); + LOG_TEST_RET(ctx, len, "AWP parse key info failed: modulus"); if (!len) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); offs += len; /* Exponent */ len = awp_get_lv(ctx, buf, buf_len, offs, 1, &ikey->exponent); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, len, "AWP parse key info failed: exponent"); + LOG_TEST_RET(ctx, len, "AWP parse key info failed: exponent"); if (!len) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -1315,31 +1337,31 @@ awp_update_key_info(struct sc_pkcs15_card *p15card, struct sc_profile *profile, unsigned char *buf; size_t buf_len; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); rv = awp_new_file(p15card, profile, SC_PKCS15_TYPE_PRKEY_RSA, prvkey_id & 0xFF, &info_file, &key_file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP update key info failed: instantiation error"); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "key id %X; info id%X", key_file->id, info_file->id); + LOG_TEST_RET(ctx, rv, "AWP update key info failed: instantiation error"); + sc_log(ctx, "key id %X; info id%X", key_file->id, info_file->id); rv = sc_pkcs15init_authenticate(profile, p15card, info_file, SC_AC_OP_READ); if (rv) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update key info failed: 'READ' authentication error"); + sc_log(ctx, "AWP update key info failed: 'READ' authentication error"); goto done; } rv = sc_select_file(p15card->card, &info_file->path, &file); if (rv) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update key info failed: cannot select info file"); + sc_log(ctx, "AWP update key info failed: cannot select info file"); goto done; } buf = calloc(1,file->size); if (!buf) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP update key info failed: allocation error"); + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "AWP update key info failed: allocation error"); rv = sc_read_binary(p15card->card, 0, buf, file->size, 0); if (rv < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update key info failed: read info file error"); + sc_log(ctx, "AWP update key info failed: read info file error"); goto done; } buf_len = rv; @@ -1347,19 +1369,19 @@ awp_update_key_info(struct sc_pkcs15_card *p15card, struct sc_profile *profile, memset(&ikey, 0, sizeof(ikey)); rv = awp_parse_key_info(ctx, buf, buf_len, &ikey); if (rv < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update key info failed: parse key info error"); + sc_log(ctx, "AWP update key info failed: parse key info error"); goto done; } free(buf); rv = awp_set_key_info(p15card, profile, info_file, &ikey, ci); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP update key info failed: set key info error"); + LOG_TEST_RET(ctx, rv, "AWP update key info failed: set key info error"); done: sc_file_free(file); sc_file_free(key_file); sc_file_free(info_file); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -1375,42 +1397,43 @@ awp_update_df_create_cert(struct sc_pkcs15_card *p15card, struct sc_profile *pro unsigned prvkey_id, obj_id; int rv; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); der = obj->content; path = ((struct sc_pkcs15_cert_info *)obj->data)->path; obj_id = (path.value[path.len-1] & 0xFF) + (path.value[path.len-2] & 0xFF) * 0x100; rv = awp_new_file(p15card, profile, SC_PKCS15_TYPE_CERT_X509, obj_id & 0xFF, &info_file, &obj_file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "COSM new file error"); + LOG_TEST_RET(ctx, rv, "COSM new file error"); memset(&icert, 0, sizeof(icert)); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "Cert Der(%p,%"SC_FORMAT_LEN_SIZE_T"u)", der.value, der.len); rv = awp_encode_cert_info(p15card, obj, &icert); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Cert' update DF failed: cannot encode info"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "'Create Cert' update DF failed: cannot encode info"); rv = awp_set_certificate_info(p15card, profile, info_file, &icert); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Cert' update DF failed: cannot set info"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "'Create Cert' update DF failed: cannot set info"); rv = awp_update_object_list(p15card, profile, SC_PKCS15_TYPE_CERT_X509, obj_id & 0xFF); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Cert' update DF failed: cannot update list"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "'Create Cert' update DF failed: cannot update list"); rv = awp_update_container(p15card, profile, SC_PKCS15_TYPE_CERT_X509, &icert.id, obj_id, &prvkey_id); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Cert' update DF failed: cannot update container"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "'Create Cert' update DF failed: cannot update container"); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PrvKeyID:%04X", prvkey_id); + sc_log(ctx, "PrvKeyID:%04X", prvkey_id); if (prvkey_id) rv = awp_update_key_info(p15card, profile, prvkey_id, &icert); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Cert' update DF failed: cannot update key info"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "'Create Cert' update DF failed: cannot update key info"); awp_free_cert_info(&icert); +err: sc_file_free(info_file); sc_file_free(obj_file); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -1431,9 +1454,10 @@ awp_update_df_create_prvkey(struct sc_pkcs15_card *p15card, struct sc_profile *p struct sc_pkcs15_cert *p15cert = NULL; int rv; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); memset(&ikey, 0, sizeof(ikey)); + memset(&icert, 0, sizeof(icert)); key_info = (struct sc_pkcs15_prkey_info *)key_obj->data; der = key_obj->content; @@ -1445,18 +1469,19 @@ awp_update_df_create_prvkey(struct sc_pkcs15_card *p15card, struct sc_profile *p rv = sc_pkcs15_find_cert_by_id(p15card, &key_info->id, &cert_obj); if (!rv) { struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) cert_obj->data; + int private_obj = cert_obj->flags & SC_PKCS15_CO_FLAG_PRIVATE; path = cert_info->path; cc.cert_id = (path.value[path.len-1] & 0xFF) + (path.value[path.len-2] & 0xFF) * 0x100; - rv = sc_pkcs15_read_certificate(p15card, cert_info, &p15cert); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update private key' DF failed: cannot get certificate"); + rv = sc_pkcs15_read_certificate(p15card, cert_info, private_obj, &p15cert); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "AWP 'update private key' DF failed: cannot get certificate"); rv = sc_pkcs15_allocate_object_content(ctx, cert_obj, p15cert->data.value, p15cert->data.len); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update private key' DF failed: cannot allocate content"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "AWP 'update private key' DF failed: cannot allocate content"); rv = awp_encode_cert_info(p15card, cert_obj, &icert); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update private key' DF failed: cannot encode cert info"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "AWP 'update private key' DF failed: cannot encode cert info"); sc_pkcs15_free_certificate(p15cert); p15cert = NULL; @@ -1469,25 +1494,25 @@ awp_update_df_create_prvkey(struct sc_pkcs15_card *p15card, struct sc_profile *p } rv = awp_new_file(p15card, profile, key_obj->type, cc.prkey_id & 0xFF, &info_file, NULL); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "New private key info file error"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "New private key info file error"); pubkey.algorithm = SC_ALGORITHM_RSA; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "PrKey Der(%p,%"SC_FORMAT_LEN_SIZE_T"u)", der.value, der.len); rv = sc_pkcs15_decode_pubkey(ctx, &pubkey, der.value, der.len); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update private key' DF failed: decode public key error"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "AWP 'update private key' DF failed: decode public key error"); rv = awp_encode_key_info(p15card, key_obj, &pubkey.u.rsa, &ikey); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update private key' DF failed: encode info error"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "AWP 'update private key' DF failed: encode info error"); rv = awp_set_key_info(p15card, profile, info_file, &ikey, cert_obj ? &icert : NULL); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update private key' DF failed: set info error"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "AWP 'update private key' DF failed: set info error"); rv = awp_update_object_list(p15card, profile, key_obj->type, cc.prkey_id & 0xFF); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update private key' DF failed: update object list error"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "AWP 'update private key' DF failed: update object list error"); rv = awp_create_container(p15card, profile, key_obj->type, &ikey.id, &cc); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update private key' DF failed: update container error"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "AWP 'update private key' DF failed: update container error"); err: if (p15cert) @@ -1495,9 +1520,9 @@ awp_update_df_create_prvkey(struct sc_pkcs15_card *p15card, struct sc_profile *p sc_file_free(info_file); if (cert_obj) awp_free_cert_info(&icert); - awp_free_key_info(&ikey); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + + LOG_FUNC_RETURN(ctx, rv); } @@ -1514,40 +1539,40 @@ awp_update_df_create_pubkey(struct sc_pkcs15_card *p15card, struct sc_profile *p unsigned obj_id; int index, rv; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); path = ((struct sc_pkcs15_pubkey_info *)obj->data)->path; der = obj->content; index = path.value[path.len-1] & 0xFF; obj_id = (path.value[path.len-1] & 0xFF) + (path.value[path.len-2] & 0xFF) * 0x100; + memset(&ikey, 0, sizeof(ikey)); + rv = awp_new_file(p15card, profile, obj->type, index, &info_file, NULL); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "New public key info file error"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "New public key info file error"); pubkey.algorithm = SC_ALGORITHM_RSA; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "PrKey Der(%p,%"SC_FORMAT_LEN_SIZE_T"u)", der.value, der.len); rv = sc_pkcs15_decode_pubkey(ctx, &pubkey, der.value, der.len); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update public key' DF failed: decode public key error"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "AWP 'update public key' DF failed: decode public key error"); - memset(&ikey, 0, sizeof(ikey)); rv = awp_encode_key_info(p15card, obj, &pubkey.u.rsa, &ikey); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update public key' DF failed: encode info error"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "AWP 'update public key' DF failed: encode info error"); rv = awp_set_key_info(p15card, profile, info_file, &ikey, NULL); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update public key' DF failed: set info error"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "AWP 'update public key' DF failed: set info error"); rv = awp_update_object_list(p15card, profile, obj->type, index); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update public key' DF failed: update object list error"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "AWP 'update public key' DF failed: update object list error"); rv = awp_update_container(p15card, profile, obj->type, &ikey.id, obj_id, NULL); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'update public key' DF failed: update container error"); - - awp_free_key_info(&ikey); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "AWP 'update public key' DF failed: update container error"); err: + awp_free_key_info(&ikey); sc_file_free(info_file); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -1558,38 +1583,34 @@ awp_update_df_create_data(struct sc_pkcs15_card *p15card, struct sc_profile *pro struct sc_context *ctx = p15card->card->ctx; struct sc_file *info_file=NULL, *obj_file=NULL; struct awp_data_info idata; - struct sc_pkcs15_der der; struct sc_path path; unsigned obj_id, obj_type = obj->auth_id.len ? COSM_TYPE_PRIVDATA_OBJECT : SC_PKCS15_TYPE_DATA_OBJECT; int rv; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); + memset(&idata, 0, sizeof(idata)); - der = obj->content; path = ((struct sc_pkcs15_data_info *)obj->data)->path; obj_id = (path.value[path.len-1] & 0xFF) + (path.value[path.len-2] & 0xFF) * 0x100; rv = awp_new_file(p15card, profile, obj_type, obj_id & 0xFF, &info_file, &obj_file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "COSM new file error"); + LOG_TEST_GOTO_ERR(ctx, rv, "COSM new file error"); - memset(&idata, 0, sizeof(idata)); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "Data Der(%p,%"SC_FORMAT_LEN_SIZE_T"u)", der.value, der.len); rv = awp_encode_data_info(p15card, obj, &idata); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Data' update DF failed: cannot encode info"); + LOG_TEST_GOTO_ERR(ctx, rv, "'Create Data' update DF failed: cannot encode info"); rv = awp_set_data_info(p15card, profile, info_file, &idata); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Data' update DF failed: cannot set info"); + LOG_TEST_GOTO_ERR(ctx, rv, "'Create Data' update DF failed: cannot set info"); rv = awp_update_object_list(p15card, profile, obj_type, obj_id & 0xFF); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'Create Data' update DF failed: cannot update list"); + LOG_TEST_GOTO_ERR(ctx, rv, "'Create Data' update DF failed: cannot update list"); +err: awp_free_data_info(&idata); - sc_file_free(info_file); sc_file_free(obj_file); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -1600,9 +1621,9 @@ awp_update_df_create(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_context *ctx = p15card->card->ctx; int rv = SC_ERROR_INTERNAL; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); if (!object) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); switch (object->type) { case SC_PKCS15_TYPE_AUTH_PIN: @@ -1621,10 +1642,10 @@ awp_update_df_create(struct sc_pkcs15_card *p15card, struct sc_profile *profile, rv = awp_update_df_create_data(p15card, profile, object); break; default: - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "'Create' update DF failed: unsupported object type"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "'Create' update DF failed: unsupported object type"); } - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -1638,23 +1659,23 @@ awp_delete_from_container(struct sc_pkcs15_card *p15card, int rv = 0, ii; unsigned char *buff=NULL; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "update container entry (type:%X,file-id:%X)", type, file_id); + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "update container entry (type:%X,file-id:%X)", type, file_id); rv = awp_new_file(p15card, profile, COSM_CONTAINER_LIST, 0, &clist, NULL); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP update contaner entry: cannot get allocate AWP file"); + LOG_TEST_RET(ctx, rv, "AWP update container entry: cannot get allocate AWP file"); rv = sc_select_file(p15card->card, &clist->path, &file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP update contaner entry: cannot select container list file"); + LOG_TEST_RET(ctx, rv, "AWP update container entry: cannot select container list file"); buff = malloc(file->record_length); if (!buff) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP update container entry: allocation error"); + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "AWP update container entry: allocation error"); for (rec = 1; rec <= (unsigned)file->record_count; rec++) { - rv = sc_read_record(p15card->card, rec, buff, file->record_length, SC_RECORD_BY_REC_NR); + rv = sc_read_record(p15card->card, rec, 0, buff, file->record_length, SC_RECORD_BY_REC_NR); if (rv < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update contaner entry: read record error %i", rv); + sc_log(ctx, "AWP update container entry: read record error %i", rv); break; } rec_len = rv; @@ -1673,26 +1694,26 @@ awp_delete_from_container(struct sc_pkcs15_card *p15card, if (!memcmp(buff,"\0\0\0\0\0\0\0\0\0\0\0\0",12)) { rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_ERASE); if (rv < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update contaner entry: 'erase' authentication error %i", rv); + sc_log(ctx, "AWP update container entry: 'erase' authentication error %i", rv); break; } rv = sc_delete_record(p15card->card, rec); if (rv < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update contaner entry: delete record error %i", rv); + sc_log(ctx, "AWP update container entry: delete record error %i", rv); break; } } else { rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); if (rv < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update contaner entry: 'update' authentication error %i", rv); + sc_log(ctx, "AWP update container entry: 'update' authentication error %i", rv); break; } - rv = sc_update_record(p15card->card, rec, buff, rec_len, SC_RECORD_BY_REC_NR); + rv = sc_update_record(p15card->card, rec, 0, buff, rec_len, SC_RECORD_BY_REC_NR); if (rv < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update contaner entry: update record error %i", rv); + sc_log(ctx, "AWP update container entry: update record error %i", rv); break; } } @@ -1705,7 +1726,7 @@ awp_delete_from_container(struct sc_pkcs15_card *p15card, sc_file_free(clist); sc_file_free(file); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -1721,8 +1742,8 @@ awp_remove_from_object_list( struct sc_pkcs15_card *p15card, struct sc_profile * unsigned char *buff=NULL; unsigned char id[2]; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "type %X; obj_id %X",type, obj_id); + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "type %X; obj_id %X",type, obj_id); switch (type) { case SC_PKCS15_TYPE_PRKEY_RSA: @@ -1736,22 +1757,22 @@ awp_remove_from_object_list( struct sc_pkcs15_card *p15card, struct sc_profile * snprintf(lst_name, NAME_MAX_LEN,"%s-public-list", COSM_TITLE); break; default: - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCORRECT_PARAMETERS, "AWP update object list: invalid type"); + LOG_TEST_RET(ctx, SC_ERROR_INCORRECT_PARAMETERS, "AWP update object list: invalid type"); } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "AWP update object list: select '%s' file", lst_name); + sc_log(ctx, "AWP update object list: select '%s' file", lst_name); rv = sc_profile_get_file(profile, lst_name, &lst_file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP update object list: cannot instantiate list file"); + LOG_TEST_RET(ctx, rv, "AWP update object list: cannot instantiate list file"); rv = sc_select_file(p15card->card, &lst_file->path, &lst); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP update object list: cannot select list file"); + LOG_TEST_RET(ctx, rv, "AWP update object list: cannot select list file"); rv = sc_pkcs15init_authenticate(profile, p15card, lst, SC_AC_OP_READ); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP update object list: 'read' authentication failed"); + LOG_TEST_RET(ctx, rv, "AWP update object list: 'read' authentication failed"); buff = malloc(lst->size); if (!buff) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "AWP update object list: allocation error"); + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "AWP update object list: allocation error"); rv = sc_read_binary(p15card->card, 0, buff, lst->size, 0); if (rv != (int)lst->size) @@ -1780,7 +1801,7 @@ awp_remove_from_object_list( struct sc_pkcs15_card *p15card, struct sc_profile * sc_file_free(lst); sc_file_free(lst_file); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -1794,27 +1815,27 @@ awp_update_df_delete_cert(struct sc_pkcs15_card *p15card, struct sc_profile *pro int rv = SC_ERROR_NOT_SUPPORTED; unsigned file_id; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); path = ((struct sc_pkcs15_cert_info *) obj->data)->path; file_id = path.value[path.len-2] * 0x100 + path.value[path.len-1]; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file-id:%X", file_id); + sc_log(ctx, "file-id:%X", file_id); rv = awp_new_file(p15card, profile, obj->type, file_id & 0xFF, &info_file, NULL); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete cert' update DF failed: cannt get allocate new AWP file"); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "info file-id:%X", info_file->id); + LOG_TEST_RET(ctx, rv, "AWP 'delete cert' update DF failed: cannot get allocate new AWP file"); + sc_log(ctx, "info file-id:%X", info_file->id); rv = cosm_delete_file(p15card, profile, info_file); if (rv != SC_ERROR_FILE_NOT_FOUND) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete cert' update DF failed: delete info file error"); + LOG_TEST_RET(ctx, rv, "AWP 'delete cert' update DF failed: delete info file error"); rv = awp_delete_from_container(p15card, profile, obj->type, file_id); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete cert' update DF failed: cannot update container"); + LOG_TEST_RET(ctx, rv, "AWP 'delete cert' update DF failed: cannot update container"); rv = awp_remove_from_object_list(p15card, profile, obj->type, file_id); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete cert' update DF failed: cannot remove object"); + LOG_TEST_RET(ctx, rv, "AWP 'delete cert' update DF failed: cannot remove object"); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -1828,27 +1849,27 @@ awp_update_df_delete_prvkey(struct sc_pkcs15_card *p15card, struct sc_profile *p int rv = SC_ERROR_NOT_SUPPORTED; unsigned file_id; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); path = ((struct sc_pkcs15_prkey_info *) obj->data)->path; file_id = path.value[path.len-2] * 0x100 + path.value[path.len-1]; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file-id:%X", file_id); + sc_log(ctx, "file-id:%X", file_id); rv = awp_new_file(p15card, profile, obj->type, file_id & 0xFF, &info_file, NULL); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete prkey' update DF failed: cannt get allocate new AWP file"); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "info file-id:%X", info_file->id); + LOG_TEST_RET(ctx, rv, "AWP 'delete prkey' update DF failed: cannot get allocate new AWP file"); + sc_log(ctx, "info file-id:%X", info_file->id); rv = cosm_delete_file(p15card, profile, info_file); if (rv != SC_ERROR_FILE_NOT_FOUND) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete prkey' update DF failed: delete info file error"); + LOG_TEST_RET(ctx, rv, "AWP 'delete prkey' update DF failed: delete info file error"); rv = awp_delete_from_container(p15card, profile, obj->type, file_id); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete prkey' update DF failed: cannot update container"); + LOG_TEST_RET(ctx, rv, "AWP 'delete prkey' update DF failed: cannot update container"); rv = awp_remove_from_object_list(p15card, profile, obj->type, file_id); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete prkey' update DF failed: cannot remove object"); + LOG_TEST_RET(ctx, rv, "AWP 'delete prkey' update DF failed: cannot remove object"); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -1862,27 +1883,27 @@ awp_update_df_delete_pubkey(struct sc_pkcs15_card *p15card, struct sc_profile *p int rv = SC_ERROR_NOT_SUPPORTED; unsigned file_id; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); path = ((struct sc_pkcs15_pubkey_info *) obj->data)->path; file_id = path.value[path.len-2] * 0x100 + path.value[path.len-1]; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file-id:%X", file_id); + sc_log(ctx, "file-id:%X", file_id); rv = awp_new_file(p15card, profile, obj->type, file_id & 0xFF, &info_file, NULL); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete pubkey' update DF failed: cannt get allocate new AWP file"); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "info file-id:%X", info_file->id); + LOG_TEST_RET(ctx, rv, "AWP 'delete pubkey' update DF failed: cannot get allocate new AWP file"); + sc_log(ctx, "info file-id:%X", info_file->id); rv = cosm_delete_file(p15card, profile, info_file); if (rv != SC_ERROR_FILE_NOT_FOUND) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete pubkey' update DF failed: delete info file error"); + LOG_TEST_RET(ctx, rv, "AWP 'delete pubkey' update DF failed: delete info file error"); rv = awp_delete_from_container(p15card, profile, obj->type, file_id); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete pubkey' update DF failed: cannot update container"); + LOG_TEST_RET(ctx, rv, "AWP 'delete pubkey' update DF failed: cannot update container"); rv = awp_remove_from_object_list(p15card, profile, obj->type, file_id); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete pubkey' update DF failed: cannot remove object"); + LOG_TEST_RET(ctx, rv, "AWP 'delete pubkey' update DF failed: cannot remove object"); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -1896,24 +1917,24 @@ awp_update_df_delete_data(struct sc_pkcs15_card *p15card, struct sc_profile *pro int rv = SC_ERROR_NOT_SUPPORTED; unsigned file_id; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); path = ((struct sc_pkcs15_data_info *) obj->data)->path; file_id = path.value[path.len-2] * 0x100 + path.value[path.len-1]; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file-id:%X", file_id); + sc_log(ctx, "file-id:%X", file_id); rv = awp_new_file(p15card, profile, obj->type, file_id & 0xFF, &info_file, NULL); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete DATA' update DF failed: cannt get allocate new AWP file"); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "info file-id:%X", info_file->id); + LOG_TEST_RET(ctx, rv, "AWP 'delete DATA' update DF failed: cannot get allocate new AWP file"); + sc_log(ctx, "info file-id:%X", info_file->id); rv = cosm_delete_file(p15card, profile, info_file); if (rv != SC_ERROR_FILE_NOT_FOUND) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete DATA' update DF failed: delete info file error"); + LOG_TEST_RET(ctx, rv, "AWP 'delete DATA' update DF failed: delete info file error"); rv = awp_remove_from_object_list(p15card, profile, obj->type, file_id); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "AWP 'delete DATA' update DF failed: cannot remove object"); + LOG_TEST_RET(ctx, rv, "AWP 'delete DATA' update DF failed: cannot remove object"); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -1924,9 +1945,9 @@ awp_update_df_delete(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_context *ctx = p15card->card->ctx; int rv = SC_ERROR_INTERNAL; - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL); + LOG_FUNC_CALLED(ctx); if (!object) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); switch (object->type) { case SC_PKCS15_TYPE_CERT_X509: @@ -1942,7 +1963,7 @@ awp_update_df_delete(struct sc_pkcs15_card *p15card, struct sc_profile *profile, rv = awp_update_df_delete_data(p15card, profile, object); break; default: - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "'Create' update DF failed: unsupported object type"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "'Create' update DF failed: unsupported object type"); } SC_FUNC_RETURN(ctx, 1, rv); diff --git a/src/pkcs15init/pkcs15-oberthur.c b/src/pkcs15init/pkcs15-oberthur.c index f090cd5eb7..7395055551 100644 --- a/src/pkcs15init/pkcs15-oberthur.c +++ b/src/pkcs15init/pkcs15-oberthur.c @@ -1,7 +1,7 @@ /* * Oberthur specific operation for PKCS #15 initialization * - * Copyright (C) 2002 Juha Yrjölä + * Copyright (C) 2002 Juha Yrjölä * Copyright (C) 2009 Viktor Tarasov , * OpenTrust * @@ -17,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pkcs15-oberthur.h" @@ -70,21 +70,20 @@ cosm_write_tokeninfo (struct sc_pkcs15_card *p15card, struct sc_profile *profile ctx = p15card->card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "cosm_write_tokeninfo() label '%s'; flags 0x%X", label, flags); if (sc_profile_get_file(profile, COSM_TITLE"-token-info", &file)) { rv = SC_ERROR_INCONSISTENT_PROFILE; - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot find "COSM_TITLE"-token-info"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "Cannot find "COSM_TITLE"-token-info"); } if (file->size < 16) { rv = SC_ERROR_INCONSISTENT_PROFILE; - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "Unsufficient size of the "COSM_TITLE"-token-info file"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "Insufficient size of the "COSM_TITLE"-token-info file"); } buffer = calloc(1, file->size); if (!buffer) { rv = SC_ERROR_OUT_OF_MEMORY; - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "Allocation error in cosm_write_tokeninfo()"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "Allocation error in cosm_write_tokeninfo()"); } if (label) @@ -100,7 +99,7 @@ cosm_write_tokeninfo (struct sc_pkcs15_card *p15card, struct sc_profile *profile if (sz < file->size - 4) memset(buffer + sz, ' ', file->size - sz); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "cosm_write_tokeninfo() token label '%s'; oberthur flags 0x%X", buffer, flags); + sc_log(ctx, "cosm_write_tokeninfo() token label '%s'; oberthur flags 0x%X", buffer, flags); memset(buffer + file->size - 4, 0, 4); *(buffer + file->size - 1) = flags & 0xFF; @@ -113,7 +112,7 @@ cosm_write_tokeninfo (struct sc_pkcs15_card *p15card, struct sc_profile *profile err: sc_file_free(file); free(buffer); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -127,22 +126,25 @@ cosm_delete_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, int rv = 0; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "id %04X", df->id); + sc_log(ctx, "id %04X", df->id); if (df->type==SC_FILE_TYPE_DF) { rv = sc_pkcs15init_authenticate(profile, p15card, df, SC_AC_OP_DELETE); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot authenticate SC_AC_OP_DELETE"); + LOG_TEST_RET(ctx, rv, "Cannot authenticate SC_AC_OP_DELETE"); } /* Select the parent DF */ path = df->path; + if (path.len < 2) { + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + } path.len -= 2; rv = sc_select_file(p15card->card, &path, &parent); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannnot select parent"); + LOG_TEST_RET(ctx, rv, "Cannot select parent"); rv = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_DELETE); sc_file_free(parent); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannnot authenticate SC_AC_OP_DELETE"); + LOG_TEST_RET(ctx, rv, "Cannot authenticate SC_AC_OP_DELETE"); memset(&path, 0, sizeof(path)); path.type = SC_PATH_TYPE_FILE_ID; @@ -152,7 +154,7 @@ cosm_delete_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, rv = sc_delete_file(p15card->card, &path); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -174,18 +176,20 @@ cosm_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) * it *after* the DF. * */ if (sc_profile_get_file(profile, "DIR", &dir) >= 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "erase file dir %04X",dir->id); + sc_log(ctx, "erase file dir %04X",dir->id); rv = cosm_delete_file(p15card, profile, dir); sc_file_free(dir); if (rv < 0 && rv != SC_ERROR_FILE_NOT_FOUND) goto done; } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "erase file ddf %04X",df->id); + sc_log(ctx, "erase file ddf %04X",df->id); rv = cosm_delete_file(p15card, profile, df); + if (rv < 0 && rv != SC_ERROR_FILE_NOT_FOUND) + goto done; if (sc_profile_get_file(profile, "private-DF", &dir) >= 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "erase file dir %04X",dir->id); + sc_log(ctx, "erase file dir %04X",dir->id); rv = cosm_delete_file(p15card, profile, dir); sc_file_free(dir); if (rv < 0 && rv != SC_ERROR_FILE_NOT_FOUND) @@ -193,7 +197,7 @@ cosm_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) } if (sc_profile_get_file(profile, "public-DF", &dir) >= 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "erase file dir %04X",dir->id); + sc_log(ctx, "erase file dir %04X",dir->id); rv = cosm_delete_file(p15card, profile, dir); sc_file_free(dir); if (rv < 0 && rv != SC_ERROR_FILE_NOT_FOUND) @@ -202,7 +206,7 @@ cosm_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) rv = sc_profile_get_file(profile, COSM_TITLE"-AppDF", &dir); if (!rv) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "delete %s; r %i", COSM_TITLE"-AppDF", rv); + sc_log(ctx, "delete %s; r %i", COSM_TITLE"-AppDF", rv); rv = cosm_delete_file(p15card, profile, dir); sc_file_free(dir); } @@ -212,7 +216,7 @@ cosm_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) if (rv == SC_ERROR_FILE_NOT_FOUND) rv = 0; - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -242,20 +246,20 @@ cosm_create_dir(struct sc_profile *profile, struct sc_pkcs15_card *p15card, /* Create private objects DF */ for (ii = 0; create_dfs[ii]; ii++) { if (sc_profile_get_file(profile, create_dfs[ii], &file)) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Inconsistent profile: cannot find %s", create_dfs[ii]); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCONSISTENT_PROFILE, "Profile do not contains Oberthur AWP file"); + sc_log(ctx, "Inconsistent profile: cannot find %s", create_dfs[ii]); + LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, "Profile do not contains Oberthur AWP file"); } rv = sc_pkcs15init_create_file(profile, p15card, file); sc_file_free(file); if (rv != SC_ERROR_FILE_ALREADY_EXISTS) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to create Oberthur AWP file"); + LOG_TEST_RET(ctx, rv, "Failed to create Oberthur AWP file"); } rv = cosm_write_tokeninfo(p15card, profile, NULL, COSM_TOKEN_FLAG_TOKEN_INITIALIZED | COSM_TOKEN_FLAG_PRN_GENERATION); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -267,7 +271,7 @@ cosm_create_reference_data(struct sc_profile *profile, struct sc_pkcs15_card *p1 { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; - struct sc_pkcs15_auth_info profile_auth_pin, profile_auth_puk; + struct sc_pkcs15_auth_info profile_auth_pin = {0}, profile_auth_puk = {0}; struct sc_cardctl_oberthur_createpin_info args; int rv; unsigned char oberthur_puk[16] = { @@ -276,7 +280,7 @@ cosm_create_reference_data(struct sc_profile *profile, struct sc_pkcs15_card *p1 }; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "pin lens %"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u", pin_len, puk_len); if (!pin || pin_len>0x40) @@ -287,7 +291,7 @@ cosm_create_reference_data(struct sc_profile *profile, struct sc_pkcs15_card *p1 return SC_ERROR_OBJECT_NOT_VALID; rv = sc_select_file(card, &ainfo->path, NULL); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot select file"); + LOG_TEST_RET(ctx, rv, "Cannot select file"); sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &profile_auth_pin); sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &profile_auth_puk); @@ -311,22 +315,22 @@ cosm_create_reference_data(struct sc_profile *profile, struct sc_pkcs15_card *p1 } rv = sc_card_ctl(card, SC_CARDCTL_OBERTHUR_CREATE_PIN, &args); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "'CREATE_PIN' card specific command failed"); + LOG_TEST_RET(ctx, rv, "'CREATE_PIN' card specific command failed"); if (!(ainfo->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) && (profile_auth_puk.tries_left > 0)) { struct sc_file *file = NULL; if (sc_profile_get_file(profile, COSM_TITLE"-puk-file", &file)) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCONSISTENT_PROFILE, "Cannot find PUKFILE"); + LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, "Cannot find PUKFILE"); rv = sc_pkcs15init_update_file(profile, p15card, file, oberthur_puk, sizeof(oberthur_puk)); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to update pukfile"); + LOG_TEST_RET(ctx, rv, "Failed to update pukfile"); sc_file_free(file); } - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -345,27 +349,27 @@ cosm_update_pin(struct sc_profile *profile, struct sc_pkcs15_card *p15card, if (ainfo->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "ref %i; flags 0x%X", ainfo->attrs.pin.reference, ainfo->attrs.pin.flags); + sc_log(ctx, "ref %i; flags 0x%X", ainfo->attrs.pin.reference, ainfo->attrs.pin.flags); if (ainfo->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) { if (ainfo->attrs.pin.reference != 4) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_PIN_REFERENCE, "cosm_update_pin() invalid SOPIN reference"); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Update SOPIN ignored"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_PIN_REFERENCE, "cosm_update_pin() invalid SOPIN reference"); + sc_log(ctx, "Update SOPIN ignored"); rv = SC_SUCCESS; } else { rv = cosm_create_reference_data(profile, p15card, ainfo, pin, pin_len, puk, puk_len); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "cosm_update_pin() failed to change PIN"); + LOG_TEST_RET(ctx, rv, "cosm_update_pin() failed to change PIN"); rv = cosm_write_tokeninfo(p15card, profile, NULL, COSM_TOKEN_FLAG_TOKEN_INITIALIZED | COSM_TOKEN_FLAG_PRN_GENERATION | COSM_TOKEN_FLAG_LOGIN_REQUIRED | COSM_TOKEN_FLAG_USER_PIN_INITIALIZED); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "cosm_update_pin() failed to update tokeninfo"); + LOG_TEST_RET(ctx, rv, "cosm_update_pin() failed to update tokeninfo"); } - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -383,9 +387,9 @@ cosm_select_pin_reference(struct sc_profile *profile, struct sc_pkcs15_card *p15 pin_attrs = &auth_info->attrs.pin; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "ref %i; flags %X", pin_attrs->reference, pin_attrs->flags); + sc_log(ctx, "ref %i; flags %X", pin_attrs->reference, pin_attrs->flags); if (sc_profile_get_file(profile, COSM_TITLE "-AppDF", &pinfile) < 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Profile doesn't define \"%s\"", COSM_TITLE "-AppDF"); + sc_log(ctx, "Profile doesn't define \"%s\"", COSM_TITLE "-AppDF"); return SC_ERROR_INCONSISTENT_PROFILE; } @@ -406,7 +410,7 @@ cosm_select_pin_reference(struct sc_profile *profile, struct sc_pkcs15_card *p15 pin_attrs->reference |= 0x80; } - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -431,9 +435,9 @@ cosm_create_pin(struct sc_profile *profile, struct sc_pkcs15_card *p15card, pin_attrs = &auth_info->attrs.pin; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "create '%.*s'; ref 0x%X; flags %X", (int) sizeof pin_obj->label, pin_obj->label, pin_attrs->reference, pin_attrs->flags); + sc_log(ctx, "create '%.*s'; ref 0x%X; flags %X", (int) sizeof pin_obj->label, pin_obj->label, pin_attrs->reference, pin_attrs->flags); if (sc_profile_get_file(profile, COSM_TITLE "-AppDF", &pin_file) < 0) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCONSISTENT_PROFILE, "\""COSM_TITLE"-AppDF\" not defined"); + LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, "\""COSM_TITLE"-AppDF\" not defined"); if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_LOCAL) auth_info->path = pin_file->path; @@ -442,30 +446,30 @@ cosm_create_pin(struct sc_profile *profile, struct sc_pkcs15_card *p15card, if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) { if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) { - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "SOPIN unblocking is not supported"); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "SOPIN unblocking is not supported"); } else { if (pin_attrs->reference != 4) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_PIN_REFERENCE, "Invalid SOPIN reference"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_PIN_REFERENCE, "Invalid SOPIN reference"); } } else { if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) { if (pin_attrs->reference != 0x84) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_PIN_REFERENCE, "Invalid User PUK reference"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_PIN_REFERENCE, "Invalid User PUK reference"); } else { if (pin_attrs->reference != 0x81) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_PIN_REFERENCE, "Invalid User PIN reference"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_PIN_REFERENCE, "Invalid User PIN reference"); } } if (pin && pin_len) { rv = cosm_update_pin(profile, p15card, auth_info, pin, pin_len, puk, puk_len); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Update PIN failed"); + LOG_TEST_RET(ctx, rv, "Update PIN failed"); } - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -481,7 +485,7 @@ cosm_new_file(struct sc_profile *profile, struct sc_card *card, unsigned int structure = 0xFFFFFFFF; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cosm_new_file() type %X; num %i",type, num); + sc_log(card->ctx, "cosm_new_file() type %X; num %i",type, num); while (1) { switch (type) { case SC_PKCS15_TYPE_PRKEY_RSA: @@ -496,10 +500,6 @@ cosm_new_file(struct sc_profile *profile, struct sc_card *card, _template = "template-public-key"; structure = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; break; - case SC_PKCS15_TYPE_PUBKEY_DSA: - desc = "DSA public key"; - _template = "template-public-key"; - break; case SC_PKCS15_TYPE_CERT: desc = "certificate"; _template = "template-certificate"; @@ -516,32 +516,34 @@ cosm_new_file(struct sc_profile *profile, struct sc_card *card, * the generic class (SC_PKCS15_TYPE_CERT) */ if (!(type & ~SC_PKCS15_TYPE_CLASS_MASK)) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "File type %X not supported by card driver", + sc_log(card->ctx, "File type %X not supported by card driver", type); return SC_ERROR_INVALID_ARGUMENTS; } type &= SC_PKCS15_TYPE_CLASS_MASK; } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cosm_new_file() template %s; num %i",_template, num); + sc_log(card->ctx, "cosm_new_file() template %s; num %i",_template, num); if (sc_profile_get_file(profile, _template, &file) < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Profile doesn't define %s template '%s'", + sc_log(card->ctx, "Profile doesn't define %s template '%s'", desc, _template); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } file->id |= (num & 0xFF); - file->path.value[file->path.len-1] |= (num & 0xFF); + if (file->path.len) { + file->path.value[file->path.len - 1] |= (num & 0xFF); + } if (file->type == SC_FILE_TYPE_INTERNAL_EF) { file->ef_structure = structure; } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "cosm_new_file() file size %"SC_FORMAT_LEN_SIZE_T"u; ef type %i/%i; id %04X", file->size, file->type, file->ef_structure, file->id); *out = file; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } @@ -556,11 +558,11 @@ cosm_get_temporary_public_key_file(struct sc_card *card, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (!pubkey_file || !prvkey_file) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); file = sc_file_new(); if (!file) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->status = SC_FILE_STATUS_ACTIVATED; file->type = SC_FILE_TYPE_INTERNAL_EF; @@ -572,6 +574,10 @@ cosm_get_temporary_public_key_file(struct sc_card *card, file->size = prvkey_file->size; entry = sc_file_get_acl_entry(prvkey_file, SC_AC_OP_UPDATE); + if (!entry) { + sc_file_free(file); + LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "Failed to find ACL entry"); + } rv = sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, entry->method, entry->key_ref); if (!rv) rv = sc_file_add_acl_entry(file, SC_AC_OP_PSO_ENCRYPT, SC_AC_NONE, 0); @@ -581,11 +587,11 @@ cosm_get_temporary_public_key_file(struct sc_card *card, rv = sc_file_add_acl_entry(file, SC_AC_OP_EXTERNAL_AUTHENTICATE, SC_AC_NONE, 0); if (rv < 0) sc_file_free(file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to add ACL entry to the temporary public key file"); + LOG_TEST_RET(ctx, rv, "Failed to add ACL entry to the temporary public key file"); *pubkey_file = file; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(card->ctx, rv); } @@ -604,31 +610,46 @@ cosm_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Generate key failed: RSA only supported"); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Generate key failed: RSA only supported"); + + if (key_info->path.len < 4) { + LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_VALID, + "The path needs to be at least four bytes long"); + } path = key_info->path; path.len -= 2; rv = sc_select_file(p15card->card, &path, &tmpf); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot generate key: failed to select private object DF"); + LOG_TEST_RET(ctx, rv, "Cannot generate key: failed to select private object DF"); rv = sc_pkcs15init_authenticate(profile, p15card, tmpf, SC_AC_OP_CRYPTO); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot generate key: 'CRYPTO' authentication failed"); + if (rv != SC_SUCCESS) { + sc_file_free(tmpf); + LOG_TEST_RET(ctx, rv, "Cannot generate key: 'CRYPTO' authentication failed"); + } rv = sc_pkcs15init_authenticate(profile, p15card, tmpf, SC_AC_OP_CREATE); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot generate key: 'CREATE' authentication failed"); - sc_file_free(tmpf); + tmpf = NULL; + LOG_TEST_RET(ctx, rv, "Cannot generate key: 'CREATE' authentication failed"); rv = sc_select_file(p15card->card, &key_info->path, &prkf); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to generate key: cannot select private key file"); + LOG_TEST_RET(ctx, rv, "Failed to generate key: cannot select private key file"); /* In the private key DF create the temporary public RSA file. */ rv = cosm_get_temporary_public_key_file(p15card->card, prkf, &tmpf); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Error while getting temporary public key file"); + if (rv != SC_SUCCESS) { + sc_file_free(prkf); + LOG_TEST_RET(ctx, rv, "Error while getting temporary public key file"); + } rv = sc_pkcs15init_create_file(profile, p15card, tmpf); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "cosm_generate_key() failed to create temporary public key EF"); + if (rv != SC_SUCCESS) { + sc_file_free(prkf); + sc_file_free(tmpf); + LOG_TEST_RET(ctx, rv, "cosm_generate_key() failed to create temporary public key EF"); + } memset(&args, 0, sizeof(args)); args.id_prv = prkf->id; @@ -637,37 +658,56 @@ cosm_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, args.key_bits = key_info->modulus_length; args.pubkey_len = key_info->modulus_length / 8; args.pubkey = malloc(key_info->modulus_length / 8); - if (!args.pubkey) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "cosm_generate_key() cannot allocate pubkey"); + if (!args.pubkey) { + sc_file_free(prkf); + sc_file_free(tmpf); + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "cosm_generate_key() cannot allocate pubkey"); + } rv = sc_card_ctl(p15card->card, SC_CARDCTL_OBERTHUR_GENERATE_KEY, &args); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "cosm_generate_key() CARDCTL_OBERTHUR_GENERATE_KEY failed"); + if (rv != SC_SUCCESS) { + sc_file_free(prkf); + sc_file_free(tmpf); + free(args.pubkey); + } + LOG_TEST_RET(ctx, rv, "cosm_generate_key() CARDCTL_OBERTHUR_GENERATE_KEY failed"); /* extract public key */ pubkey->algorithm = SC_ALGORITHM_RSA; pubkey->u.rsa.modulus.len = key_info->modulus_length / 8; pubkey->u.rsa.modulus.data = malloc(key_info->modulus_length / 8); - if (!pubkey->u.rsa.modulus.data) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "cosm_generate_key() cannot allocate modulus buf"); + if (!pubkey->u.rsa.modulus.data) { + sc_file_free(prkf); + sc_file_free(tmpf); + free(args.pubkey); + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "cosm_generate_key() cannot allocate modulus buf"); + } /* FIXME and if the exponent length is not 3? */ pubkey->u.rsa.exponent.len = 3; pubkey->u.rsa.exponent.data = malloc(3); - if (!pubkey->u.rsa.exponent.data) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY, "cosm_generate_key() cannot allocate exponent buf"); + if (!pubkey->u.rsa.exponent.data) { + sc_file_free(prkf); + sc_file_free(tmpf); + free(args.pubkey); + free(pubkey->u.rsa.modulus.data); + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "cosm_generate_key() cannot allocate exponent buf"); + } memcpy(pubkey->u.rsa.exponent.data, "\x01\x00\x01", 3); memcpy(pubkey->u.rsa.modulus.data, args.pubkey, args.pubkey_len); + pubkey->u.rsa.modulus.len = args.pubkey_len; key_info->key_reference = prkf->path.value[prkf->path.len - 1] & 0xFF; key_info->path = prkf->path; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "cosm_generate_key() now delete temporary public key"); + sc_log(ctx, "cosm_generate_key() now delete temporary public key"); rv = cosm_delete_file(p15card, profile, tmpf); sc_file_free(tmpf); sc_file_free(prkf); + free(args.pubkey); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -685,39 +725,44 @@ cosm_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Create key failed: RSA only supported"); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Create key failed: RSA only supported"); + + if (key_info->path.len < 2) { + LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_VALID, + "The path needs to be at least two bytes long"); + } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "create private key ID:%s", sc_pkcs15_print_id(&key_info->id)); + sc_log(ctx, "create private key ID:%s", sc_pkcs15_print_id(&key_info->id)); /* Here, the path of private key file should be defined. - * Neverthelles, we need to instanciate private key to get the ACLs. */ + * Nevertheless, we need to instantiate private key to get the ACLs. */ rv = cosm_new_file(profile, p15card->card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot create key: failed to allocate new key object"); + LOG_TEST_RET(ctx, rv, "Cannot create key: failed to allocate new key object"); file->size = key_info->modulus_length; memcpy(&file->path, &key_info->path, sizeof(file->path)); file->id = file->path.value[file->path.len - 2] * 0x100 + file->path.value[file->path.len - 1]; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Path of private key file to create %s", sc_print_path(&file->path)); + sc_log(ctx, "Path of private key file to create %s", sc_print_path(&file->path)); rv = sc_select_file(p15card->card, &file->path, NULL); if (rv == 0) { rv = cosm_delete_file(p15card, profile, file); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to delete private key file"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "Failed to delete private key file"); } else if (rv != SC_ERROR_FILE_NOT_FOUND) { - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "Select private key file error"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "Select private key file error"); } rv = sc_pkcs15init_create_file(profile, p15card, file); - SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_NORMAL, rv, "Failed to create private key file"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "Failed to create private key file"); key_info->key_reference = file->path.value[file->path.len - 1]; err: sc_file_free(file); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -737,19 +782,19 @@ cosm_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA || prkey->algorithm != SC_ALGORITHM_RSA) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Store key failed: RSA only supported"); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Store key failed: RSA only supported"); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "store key with ID:%s and path:%s", sc_pkcs15_print_id(&key_info->id), + sc_log(ctx, "store key with ID:%s and path:%s", sc_pkcs15_print_id(&key_info->id), sc_print_path(&key_info->path)); rv = sc_select_file(p15card->card, &key_info->path, &file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot store key: select key file failed"); + LOG_TEST_RET(ctx, rv, "Cannot store key: select key file failed"); rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "No authorisation to store private key"); + LOG_TEST_RET(ctx, rv, "No authorisation to store private key"); if (key_info->id.len > sizeof(update_info.id)) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); memset(&update_info, 0, sizeof(update_info)); update_info.type = SC_CARDCTL_OBERTHUR_KEY_RSA_CRT; @@ -759,11 +804,11 @@ cosm_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, memcpy(update_info.id, key_info->id.value, update_info.id_len); rv = sc_card_ctl(p15card->card, SC_CARDCTL_OBERTHUR_UPDATE_KEY, &update_info); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot update private key"); + LOG_TEST_RET(ctx, rv, "Cannot update private key"); sc_file_free(file); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, rv); + LOG_FUNC_RETURN(ctx, rv); } @@ -788,11 +833,11 @@ cosm_emu_update_any_df(struct sc_profile *profile, struct sc_pkcs15_card *p15car SC_FUNC_CALLED(ctx, 1); switch(op) { case SC_AC_OP_ERASE: - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Update DF; erase object('%.*s',type:%X)", (int) sizeof object->label, object->label, object->type); + sc_log(ctx, "Update DF; erase object('%.*s',type:%X)", (int) sizeof object->label, object->label, object->type); rv = awp_update_df_delete(p15card, profile, object); break; case SC_AC_OP_CREATE: - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Update DF; create object('%.*s',type:%X)", (int) sizeof object->label, object->label, object->type); + sc_log(ctx, "Update DF; create object('%.*s',type:%X)", (int) sizeof object->label, object->label, object->type); rv = awp_update_df_create(p15card, profile, object); break; } @@ -806,13 +851,14 @@ cosm_emu_update_tokeninfo(struct sc_profile *profile, struct sc_pkcs15_card *p15 { struct sc_context *ctx = p15card->card->ctx; struct sc_file *file = NULL; - int rv, flags = 0, label_len; + int rv, flags = 0; + size_t label_len; unsigned char *buf = NULL; SC_FUNC_CALLED(ctx, 1); if (sc_profile_get_file(profile, COSM_TITLE"-token-info", &file)) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INCONSISTENT_PROFILE, "cannot find "COSM_TITLE"-token-info"); + LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, "cannot find "COSM_TITLE"-token-info"); buf = calloc(1, file->size); if (!buf) { @@ -836,7 +882,7 @@ cosm_emu_update_tokeninfo(struct sc_profile *profile, struct sc_pkcs15_card *p15 *(buf + file->size - 1) = flags % 0x100; *(buf + file->size - 2) = (flags % 0x10000) / 0x100; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Update token info (label:'%s',flags:%X,p15card->flags:%X)", buf, flags, p15card->flags); + sc_log(ctx, "Update token info (label:'%s',flags:%X,p15card->flags:%X)", buf, flags, p15card->flags); rv = sc_pkcs15init_update_file(profile, p15card, file, buf, file->size); free(buf); sc_file_free(file); diff --git a/src/pkcs15init/pkcs15-oberthur.h b/src/pkcs15init/pkcs15-oberthur.h index 6528e59540..1a679d7ffc 100644 --- a/src/pkcs15init/pkcs15-oberthur.h +++ b/src/pkcs15init/pkcs15-oberthur.h @@ -69,7 +69,7 @@ struct awp_crypto_container { }; struct awp_lv { - unsigned len; + size_t len; unsigned char *value; }; diff --git a/src/pkcs15init/pkcs15-openpgp.c b/src/pkcs15init/pkcs15-openpgp.c index 4fcaec922a..d16ad6b59a 100644 --- a/src/pkcs15init/pkcs15-openpgp.c +++ b/src/pkcs15init/pkcs15-openpgp.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -26,6 +26,7 @@ #include "libopensc/opensc.h" #include "libopensc/cardctl.h" +#include "libopensc/internal.h" #include "libopensc/log.h" #include "libopensc/cards.h" #include "libopensc/asn1.h" @@ -102,9 +103,65 @@ static int openpgp_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, LOG_FUNC_CALLED(p15card->card->ctx); LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } +/** + * Set algorithm and check if card supports it + * @param p15card sc_pkcs15_card_t + * @param type SC_PKCS15_TYPE_* + * @param key_id openspgp id + * @param &algorithm SC_OPENPGP_KEYALGO_* + + * @returns 0 or error + */ + +static int +openpgp_set_algorithm(sc_pkcs15_card_t *p15card, + u8 key_id, unsigned long type, u8 *algo) +{ + sc_card_t *card = p15card->card; + + if (card->type != SC_CARD_TYPE_OPENPGP_GNUK && + card->type < SC_CARD_TYPE_OPENPGP_V3) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "only RSA is supported on this card"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + + switch (key_id) { + case SC_OPENPGP_KEY_SIGN: + case SC_OPENPGP_KEY_AUTH: + switch (type) { + case SC_PKCS15_TYPE_PRKEY_EC: + case SC_PKCS15_TYPE_PUBKEY_EC: + *algo = SC_OPENPGP_KEYALGO_ECDSA; + return SC_SUCCESS; + break; + case SC_PKCS15_TYPE_PRKEY_EDDSA: + case SC_PKCS15_TYPE_PUBKEY_EDDSA: + *algo = SC_OPENPGP_KEYALGO_EDDSA; + return SC_SUCCESS; + break; + } + break; + case SC_OPENPGP_KEY_ENCR: + switch (type) { + case SC_PKCS15_TYPE_PRKEY_EC: + case SC_PKCS15_TYPE_PUBKEY_EC: + *algo = SC_OPENPGP_KEYALGO_ECDH; + return SC_SUCCESS; + break; + case SC_PKCS15_TYPE_PRKEY_XEDDSA: + case SC_PKCS15_TYPE_PUBKEY_XEDDSA: + *algo = SC_OPENPGP_KEYALGO_ECDH; + return SC_SUCCESS; + break; + } + break; + } + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid algorithm of openpgp slot"); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); +} /** - * Stores an external (RSA) on the card. + * Stores an external key on the card. * @param profile profile information for this card * @param card sc_card_t object to use * @param obj sc_pkcs15_object_t object with pkcs15 information @@ -116,46 +173,81 @@ static int openpgp_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, { sc_card_t *card = p15card->card; sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *) obj->data; - struct sc_pkcs15_prkey_rsa *rsa = &(key->u.rsa); - sc_cardctl_openpgp_keystore_info_t key_info; + sc_cardctl_openpgp_key_gen_store_info_t key_info = {0}; int r; LOG_FUNC_CALLED(card->ctx); - if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "only RSA is currently supported"); - return SC_ERROR_NOT_SUPPORTED; - } + switch(obj->type) + { + case SC_PKCS15_TYPE_PRKEY_RSA: + memset(&key_info, 0, sizeof(sc_cardctl_openpgp_key_gen_store_info_t)); + key_info.algorithm = SC_OPENPGP_KEYALGO_RSA; + key_info.key_id = kinfo->id.value[0]; + key_info.u.rsa.exponent = key->u.rsa.exponent.data; + key_info.u.rsa.exponent_len = key->u.rsa.exponent.len * 8; /* use bits instead of bytes */ + key_info.u.rsa.p = key->u.rsa.p.data; + key_info.u.rsa.p_len = key->u.rsa.p.len; + key_info.u.rsa.q = key->u.rsa.q.data; + key_info.u.rsa.q_len = key->u.rsa.q.len; + key_info.u.rsa.modulus = key->u.rsa.modulus.data; + key_info.u.rsa.modulus_len = key->u.rsa.modulus.len * 8; /* use bits instead of bytes */ + r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_STORE_KEY, &key_info); + + /* nothing to free in key_info for RSA */ + break; - memset(&key_info, 0, sizeof(sc_cardctl_openpgp_keystore_info_t)); - key_info.keytype = kinfo->id.value[0]; - key_info.e = rsa->exponent.data; - key_info.e_len = rsa->exponent.len; - key_info.p = rsa->p.data; - key_info.p_len = rsa->p.len; - key_info.q = rsa->q.data; - key_info.q_len = rsa->q.len; - key_info.n = rsa->modulus.data; - key_info.n_len = rsa->modulus.len; - r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_STORE_KEY, &key_info); + case SC_PKCS15_TYPE_PRKEY_EC: + case SC_PKCS15_TYPE_PRKEY_EDDSA: + case SC_PKCS15_TYPE_PRKEY_XEDDSA: + memset(&key_info, 0, sizeof(sc_cardctl_openpgp_key_gen_store_info_t)); + + r = openpgp_set_algorithm(p15card, kinfo->id.value[0], obj->type, &key_info.algorithm); + LOG_TEST_GOTO_ERR(card->ctx, r, "Key type not valid for key id"); + + key_info.key_id = kinfo->id.value[0]; + key_info.u.ec.privateD = key->u.ec.privateD.data; + key_info.u.ec.privateD_len = key->u.ec.privateD.len; + /* + * key->u.ec.ecpointQ.len is optional with private key + * PKCS11 does not pass it when creting private key object. + * pkcs15init/pkcs15-lib.c will attempt to derive it from private key + */ + if (key->u.ec.ecpointQ.len) { + key_info.u.ec.ecpointQ = malloc(key->u.ec.ecpointQ.len); + if (!key_info.u.ec.ecpointQ) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + memcpy(key_info.u.ec.ecpointQ, key->u.ec.ecpointQ.value, key->u.ec.ecpointQ.len); + } + key_info.u.ec.ecpointQ_len = key->u.ec.ecpointQ.len; + key_info.u.ec.oid = key->u.ec.params.id; + key_info.key_type = key->algorithm; /* SC_SC_ALGORITHM_* */ + r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_STORE_KEY, &key_info); + + free(key_info.u.ec.ecpointQ); + break; + default: + r = SC_ERROR_NOT_SUPPORTED; + sc_log(card->ctx, "%s: Key generation failed: Unknown/unsupported key type.", strerror(r)); + } + +err: LOG_FUNC_RETURN(card->ctx, r); } /** - * Generates a new (RSA) key pair using an existing key file. - * @param profile IN profile information for this card + * Generates a new RSA key pair on card. * @param card IN sc_card_t object to use * @param obj IN sc_pkcs15_object_t object with pkcs15 information * @param pukkey OUT the newly created public key * @return SC_SUCCESS on success and an error code otherwise **/ -static int openpgp_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, - sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) +static int openpgp_generate_key_rsa(sc_card_t *card, sc_pkcs15_object_t *obj, + sc_pkcs15_pubkey_t *pubkey) { - sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; - sc_cardctl_openpgp_keygen_info_t key_info; + sc_cardctl_openpgp_key_gen_store_info_t key_info; sc_pkcs15_prkey_info_t *required = (sc_pkcs15_prkey_info_t *)obj->data; sc_pkcs15_id_t *kid = &(required->id); int r; @@ -169,51 +261,212 @@ static int openpgp_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card /* Default key is authentication key. We choose this because the common use * is to generate from PKCS#11 (Firefox/Thunderbird) */ sc_log(ctx, "Authentication key is to be generated."); - key_info.keytype = 3; + key_info.key_id = 3; } - if (!key_info.keytype && (kid->len > 1 || kid->value[0] > 3)) { + if (!key_info.key_id && (kid->len > 1 || kid->value[0] > 3)) { sc_log(ctx, "Key ID must be 1, 2 or 3!"); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } - if (!key_info.keytype) - key_info.keytype = kid->value[0]; + if (!key_info.key_id) + key_info.key_id = kid->value[0]; + + if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { + sc_log(card->ctx, "only RSA is currently supported"); + return SC_ERROR_NOT_SUPPORTED; + } + + + key_info.algorithm = SC_OPENPGP_KEYALGO_RSA; /* Prepare buffer */ - key_info.modulus_len = required->modulus_length; - key_info.modulus = calloc(required->modulus_length >> 3, 1); - if (key_info.modulus == NULL) + key_info.u.rsa.modulus_len = required->modulus_length; + key_info.u.rsa.modulus = calloc(1, BYTES4BITS(required->modulus_length)); + if (key_info.u.rsa.modulus == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_ENOUGH_MEMORY); /* The OpenPGP supports only 32-bit exponent. */ - key_info.exponent_len = 32; - key_info.exponent = calloc(key_info.exponent_len>>3, 1); /* 1/8 */ - if (key_info.exponent == NULL) + key_info.u.rsa.exponent_len = 32; + key_info.u.rsa.exponent = calloc(1, BYTES4BITS(key_info.u.rsa.exponent_len)); + if (key_info.u.rsa.exponent == NULL) { + free(key_info.u.rsa.modulus); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_ENOUGH_MEMORY); + } r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_GENERATE_KEY, &key_info); - if (r < 0) - goto out; + LOG_TEST_GOTO_ERR(card->ctx, r, "on-card EC key generation failed"); + pubkey->algorithm = SC_ALGORITHM_RSA; sc_log(ctx, "Set output modulus info"); - pubkey->u.rsa.modulus.len = key_info.modulus_len; - pubkey->u.rsa.modulus.data = calloc(key_info.modulus_len, 1); + pubkey->u.rsa.modulus.len = BYTES4BITS(key_info.u.rsa.modulus_len); + pubkey->u.rsa.modulus.data = calloc(1, pubkey->u.rsa.modulus.len); if (pubkey->u.rsa.modulus.data == NULL) - goto out; - memcpy(pubkey->u.rsa.modulus.data, key_info.modulus, key_info.modulus_len); + goto err; + memcpy(pubkey->u.rsa.modulus.data, key_info.u.rsa.modulus, BYTES4BITS(key_info.u.rsa.modulus_len)); sc_log(ctx, "Set output exponent info"); - pubkey->u.rsa.exponent.len = key_info.exponent_len; - pubkey->u.rsa.exponent.data = calloc(key_info.exponent_len>>3, 1); /* 1/8 */ + pubkey->u.rsa.exponent.len = BYTES4BITS(key_info.u.rsa.exponent_len); + pubkey->u.rsa.exponent.data = calloc(1, pubkey->u.rsa.exponent.len); if (pubkey->u.rsa.exponent.data == NULL) - goto out; - memcpy(pubkey->u.rsa.exponent.data, key_info.exponent, key_info.exponent_len>>3); /* 1/8 */ - -out: - if (key_info.modulus) - free(key_info.modulus); - if (key_info.exponent) - free(key_info.exponent); + goto err; + memcpy(pubkey->u.rsa.exponent.data, key_info.u.rsa.exponent, pubkey->u.rsa.exponent.len); + +err: + free(key_info.u.rsa.modulus); + key_info.u.rsa.modulus = NULL; + free(key_info.u.rsa.exponent); + key_info.u.rsa.exponent = NULL; + free(key_info.data); + LOG_FUNC_RETURN(ctx, r); +} + + +/** + * Generates a new ECC key pair on card. + * @param card IN sc_card_t object to use + * @param obj IN sc_pkcs15_object_t object with pkcs15 information + * @param pukkey OUT the newly created public key + * @return SC_SUCCESS on success and an error code otherwise + **/ +static int openpgp_generate_key_ec(sc_card_t *card, sc_pkcs15_object_t *obj, + sc_pkcs15_pubkey_t *pubkey) +{ + sc_context_t *ctx = card->ctx; + sc_cardctl_openpgp_key_gen_store_info_t key_info; + sc_pkcs15_prkey_info_t *required = (sc_pkcs15_prkey_info_t *)obj->data; + sc_pkcs15_id_t *kid = &(required->id); + struct sc_ec_parameters *info_ec = + (struct sc_ec_parameters *)required->params.data; + int r; + + LOG_FUNC_CALLED(ctx); + memset(&key_info, 0, sizeof(key_info)); + + sc_log(ctx, "Key ID to be generated: %s", sc_dump_hex(kid->value, kid->len)); + + /* Accept KeyID = 45, which is default value set by pkcs15init */ + if (kid->len == 1 && kid->value[0] == 0x45) { + /* Default key is authentication key. We choose this because the common use + * is to generate from PKCS#11 (Firefox/Thunderbird) */ + sc_log(ctx, "Authentication key is to be generated."); + key_info.key_id = 3; + } + if (!key_info.key_id && (kid->len > 1 || kid->value[0] > 3)) { + sc_log(ctx, "Key ID must be 1, 2 or 3!"); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + if (!key_info.key_id) + key_info.key_id = kid->value[0]; + key_info.key_type = pubkey->algorithm; + /* set algorithm id based on key reference and key type */ + switch (pubkey->algorithm) { + /* EC is in 04||x||y format + * (field_length + 7)/8 * 2 + 1 in bytes + * len is ecpoint length + format byte + * see section 7.2.14 of 3.3.1 specs + * EDDSA and XEDDSA have no format byte and one number + * (field_length + 7)/8 in bytes + */ + + case SC_ALGORITHM_EC: + key_info.algorithm = (key_info.key_id == SC_OPENPGP_KEY_ENCR) + ? SC_OPENPGP_KEYALGO_ECDH /* ECDH for slot 2 only */ + : SC_OPENPGP_KEYALGO_ECDSA; /* ECDSA for slot 1 and 3 */ + key_info.u.ec.ecpointQ_len = 1 + 2 * BYTES4BITS(required->field_length); + break; + case SC_ALGORITHM_EDDSA: + key_info.algorithm = SC_OPENPGP_KEYALGO_EDDSA; /* only sign */ + key_info.u.ec.ecpointQ_len = BYTES4BITS(required->field_length); + break; + case SC_ALGORITHM_XEDDSA: + /* TODO may need to look at MSE, and how sign XEDDSA certificate */ + key_info.algorithm = SC_OPENPGP_KEYALGO_ECDH; /* but could be used to sign too */ + key_info.u.ec.ecpointQ_len = BYTES4BITS(required->field_length); + break; + } + + /* copying info_ec.id works for any EC ECDH EdDSA keys */ + if (info_ec->der.len > 2 && info_ec->der.len == (size_t)(info_ec->der.value[1] + 2)) + key_info.u.ec.oidv_len = info_ec->der.value[1]; + else + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + for (size_t i = 0; (i < key_info.u.ec.oidv_len) && (i + 2 < info_ec->der.len); i++) { + key_info.u.ec.oidv.value[i] = info_ec->der.value[i + 2]; + } + key_info.u.ec.oidv.value[key_info.data_len] = -1; + + /* copy id also */ + key_info.u.ec.oid = info_ec->id; + + /* generate key on card */ + r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_GENERATE_KEY, &key_info); + LOG_TEST_GOTO_ERR(card->ctx, r, "on-card EC key generation failed"); + + /* set pubkey according to response of card */ + sc_log(ctx, "Set output ecpoint info"); + + pubkey->algorithm = key_info.key_type; + pubkey->u.ec.ecpointQ.len = key_info.u.ec.ecpointQ_len; + pubkey->u.ec.ecpointQ.value = malloc(key_info.u.ec.ecpointQ_len); + if (pubkey->u.ec.ecpointQ.value == NULL) { + r = SC_ERROR_NOT_ENOUGH_MEMORY; + goto err; + } + + memcpy(pubkey->u.ec.ecpointQ.value, key_info.u.ec.ecpointQ, key_info.u.ec.ecpointQ_len); + +err: + free(key_info.u.ec.ecpointQ); + free(key_info.data); + + LOG_FUNC_RETURN(ctx, r); +} + + +/** + * Generates a new key pair using an existing key file. + * @param profile IN profile information for this card + * @param card IN sc_card_t object to use + * @param obj IN sc_pkcs15_object_t object with pkcs15 information + * @param pukkey OUT the newly created public key + * @return SC_SUCCESS on success and an error code otherwise + **/ +static int openpgp_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, + sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) +{ + sc_card_t *card = p15card->card; + sc_context_t *ctx = card->ctx; + int r; + + LOG_FUNC_CALLED(ctx); + + switch(obj->type) + { + case SC_PKCS15_TYPE_PRKEY_RSA: + r = openpgp_generate_key_rsa(card, obj, pubkey); + break; + case SC_PKCS15_TYPE_PRKEY_EC: + if (card->type < SC_CARD_TYPE_OPENPGP_V3) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "only RSA is supported on this card"); + return SC_ERROR_NOT_SUPPORTED; + } + r = openpgp_generate_key_ec(card, obj, pubkey); + break; + case SC_PKCS15_TYPE_PRKEY_EDDSA: + case SC_PKCS15_TYPE_PRKEY_XEDDSA: + if (card->type != SC_CARD_TYPE_OPENPGP_GNUK && card->type < SC_CARD_TYPE_OPENPGP_V3) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "EdDSA or XEDDSA are not supported on this card"); + return SC_ERROR_NOT_SUPPORTED; + } + r = openpgp_generate_key_ec(card, obj, pubkey); + break; + default: + r = SC_ERROR_NOT_SUPPORTED; + sc_log(card->ctx, "%s: Key generation failed: Unknown/unsupported key type.", strerror(r)); + } + LOG_FUNC_RETURN(ctx, r); } @@ -241,7 +494,7 @@ static int openpgp_store_data(struct sc_pkcs15_card *p15card, struct sc_profile { sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; - sc_file_t *file; + sc_file_t *file = NULL; sc_pkcs15_cert_info_t *cinfo; sc_pkcs15_id_t *cid; sc_pkcs15_data_info_t *dinfo; @@ -261,6 +514,7 @@ static int openpgp_store_data(struct sc_pkcs15_card *p15card, struct sc_profile case SC_PKCS15_TYPE_CERT: cinfo = (sc_pkcs15_cert_info_t *) obj->data; cid = &(cinfo->id); + unsigned int tag = 0x7F21; if (cid->len != 1) { sc_log(card->ctx, "ID=%s is not valid.", sc_dump_hex(cid->value, cid->len)); @@ -268,22 +522,37 @@ static int openpgp_store_data(struct sc_pkcs15_card *p15card, struct sc_profile } /* OpenPGP card v.2 contains only 1 certificate */ - if (cid->value[0] != 3) { + if (cid->value[0] != 3 && p15card->card->type < SC_CARD_TYPE_OPENPGP_V3) { sc_log(card->ctx, "This version does not support certificate ID = %d (only ID=3 is supported).", cid->value[0]); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } - /* Just update the certificate DO */ + + /* OpenPGP card < v.3 does not support SELECT DATA calls */ + if (p15card->card->type >= SC_CARD_TYPE_OPENPGP_V3) { + /* Mapping [3..1] passed --id to [0..2] for param */ + u8 param = (u8) (2 - (cid->value[0] - 1)); + /* check for unsigned underflow */ + if (param > 2) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + + /* Just update the certificate DO */ + r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_SELECT_DATA, ¶m); + LOG_TEST_RET(card->ctx, r, "Failed OpenPGP - select data"); + } + sc_format_path("7F21", path); r = sc_select_file(card, path, &file); + LOG_TEST_RET(card->ctx, r, "Cannot select cert file"); r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); sc_log(card->ctx, "Data to write is %"SC_FORMAT_LEN_SIZE_T"u long", content->len); if (r >= 0 && content->len) - r = sc_put_data(p15card->card, 0x7F21, (const unsigned char *) content->value, content->len); + r = sc_put_data(p15card->card, tag, (const unsigned char *) content->value, content->len); break; case SC_PKCS15_TYPE_DATA_OBJECT: @@ -323,29 +592,30 @@ static int openpgp_store_data(struct sc_pkcs15_card *p15card, struct sc_profile r = SC_ERROR_NOT_IMPLEMENTED; } + sc_file_free(file); LOG_FUNC_RETURN(card->ctx, r); } static struct sc_pkcs15init_operations sc_pkcs15init_openpgp_operations = { - openpgp_erase, - NULL, /* init_card */ - openpgp_create_dir, - NULL, /* create_domain */ - openpgp_select_pin_reference, - openpgp_create_pin, - NULL, /* select key reference */ - openpgp_create_key, - openpgp_store_key, - openpgp_generate_key, - NULL, NULL, /* encode private/public key */ - NULL, /* finalize_card */ - NULL, /* delete_object */ - NULL, - openpgp_emu_update_any_df, - openpgp_emu_update_tokeninfo, - NULL, /* emu_write_info */ - openpgp_store_data, /* emu_store_data */ - NULL /* sanity_check */ + openpgp_erase, + NULL, /* init_card */ + openpgp_create_dir, + NULL, /* create_domain */ + openpgp_select_pin_reference, + openpgp_create_pin, + NULL, /* select key reference */ + openpgp_create_key, + openpgp_store_key, + openpgp_generate_key, + NULL, NULL, /* encode private/public key */ + NULL, /* finalize_card */ + NULL, /* delete_object */ + NULL, + openpgp_emu_update_any_df, + openpgp_emu_update_tokeninfo, + NULL, /* emu_write_info */ + openpgp_store_data, /* emu_store_data */ + NULL /* sanity_check */ }; struct sc_pkcs15init_operations *sc_pkcs15init_get_openpgp_ops(void) diff --git a/src/pkcs15init/pkcs15-rtecp.c b/src/pkcs15init/pkcs15-rtecp.c index d23d07a007..dc013613ae 100644 --- a/src/pkcs15init/pkcs15-rtecp.c +++ b/src/pkcs15init/pkcs15-rtecp.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -55,13 +55,18 @@ static int create_sysdf(sc_profile_t *profile, sc_card_t *card, const char *name sc_path_t path; int r; - assert(profile && card && card->ctx && name); + if (profile == NULL || card == NULL || card->ctx == NULL || name == NULL) + return SC_ERROR_INTERNAL; r = sc_profile_get_file(profile, name, &file); if (r == SC_SUCCESS) { - assert(file); + if (!file) + return SC_ERROR_INTERNAL; path = file->path; - assert(path.len > 2); + if (path.len <= 2) { + sc_file_free(file); + return SC_ERROR_INTERNAL; + } if (path.len > 2) path.len -= 2; r = sc_select_file(card, &path, NULL); @@ -75,8 +80,7 @@ static int create_sysdf(sc_profile_t *profile, sc_card_t *card, const char *name r = sc_create_file(card, file); sc_file_free(file); } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, - "Create %s failed: %s\n", name, sc_strerror(r)); + sc_log(card->ctx, "Create %s failed: %s\n", name, sc_strerror(r)); return r; } @@ -95,18 +99,20 @@ static int rtecp_init(sc_profile_t *profile, sc_pkcs15_card_t *p15card) card = p15card->card; r = sc_profile_get_file(profile, "MF", &file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Get MF info failed"); - assert(file); + LOG_TEST_RET(card->ctx, r, "Get MF info failed"); + if (file == NULL) + return SC_ERROR_INTERNAL; r = sc_create_file(card, file); sc_file_free(file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Create MF failed"); + LOG_TEST_RET(card->ctx, r, "Create MF failed"); r = sc_profile_get_file(profile, "DIR", &file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Get DIR file info failed"); - assert(file); + LOG_TEST_RET(card->ctx, r, "Get DIR file info failed"); + if (!file) + return SC_ERROR_INTERNAL; r = sc_create_file(card, file); sc_file_free(file); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Create DIR file failed"); + LOG_TEST_RET(card->ctx, r, "Create DIR file failed"); create_sysdf(profile, card, "Sys-DF"); create_sysdf(profile, card, "SysKey-DF"); @@ -120,6 +126,8 @@ static int rtecp_init(sc_profile_t *profile, sc_pkcs15_card_t *p15card) create_sysdf(profile, card, "Resrv2-DF"); create_sysdf(profile, card, "Resrv3-DF"); create_sysdf(profile, card, "Resrv4-DF"); + create_sysdf(profile, card, "Resrv5-DF"); + create_sysdf(profile, card, "Resrv6-DF"); return sc_select_file(card, sc_get_mf_path(), NULL); } @@ -153,7 +161,7 @@ static int rtecp_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p else pin_ref = RTECP_USER_PIN_REF; if (auth_info->attrs.pin.reference != pin_ref) - SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_NOT_SUPPORTED); return SC_SUCCESS; } @@ -186,7 +194,7 @@ static int rtecp_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, if (puk_len != 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Do not enter User unblocking PIN (PUK): %s\n", + sc_log(ctx, "Do not enter User unblocking PIN (PUK): %s\n", sc_strerror(SC_ERROR_NOT_SUPPORTED)); return SC_ERROR_NOT_SUPPORTED; } @@ -198,7 +206,7 @@ static int rtecp_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, if (auth_info->attrs.pin.reference != RTECP_SO_PIN_REF && auth_info->attrs.pin.reference != RTECP_USER_PIN_REF) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PIN reference %i not found in standard" + sc_log(ctx, "PIN reference %i not found in standard" " (Rutoken ECP) PINs\n", auth_info->attrs.pin.reference); return SC_ERROR_NOT_SUPPORTED; } @@ -212,11 +220,11 @@ static int rtecp_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, r = sc_pkcs15init_fixup_file(profile, p15card, file); if (r < 0) sc_file_free(file); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot fixup the ACLs of PIN file"); + LOG_TEST_RET(p15card->card->ctx, r, "Cannot fixup the ACLs of PIN file"); acl = sc_file_get_acl_entry(file, SC_AC_OP_PIN_RESET); if (acl && acl->method == SC_AC_CHV && acl->key_ref == RTECP_SO_PIN_REF) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Allow reset of User PIN with SoPIN\n"); + sc_log(ctx, "Allow reset of User PIN with SoPIN\n"); reset_by_sopin = 1; } sc_file_free(file); @@ -225,16 +233,16 @@ static int rtecp_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, file = sc_file_new(); if (!file) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); file->id = auth_info->attrs.pin.reference; file->size = pin_len; - assert(sizeof(sec)/sizeof(sec[0]) > 2); + static_assert(sizeof(prop) / sizeof(prop[0]) > 3, "internal error"); sec[1] = (auth_info->attrs.pin.reference == RTECP_SO_PIN_REF) ? 0xFF : RTECP_SO_PIN_REF; sec[2] = (unsigned char)auth_info->attrs.pin.reference | (reset_by_sopin ? RTECP_SO_PIN_REF : 0); r = sc_file_set_sec_attr(file, sec, sizeof(sec)); if (r == SC_SUCCESS) { - assert(sizeof(prop)/sizeof(prop[0]) > 3); + static_assert(sizeof(prop) / sizeof(prop[0]) > 3, "internal error"); prop[1] = (unsigned char)auth_info->attrs.pin.min_length; prop[3] = 0x11 * (unsigned char)(auth_info->tries_left & 0x0F); r = sc_file_set_prop_attr(file, prop, sizeof(prop)); @@ -248,7 +256,7 @@ static int rtecp_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, if (r == SC_SUCCESS) r = sc_change_reference_data(p15card->card, SC_AC_CHV, auth_info->attrs.pin.reference, NULL, 0, pin, pin_len, NULL); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(ctx, r); } /* @@ -269,8 +277,9 @@ static int rtecp_select_key_reference(sc_profile_t *profile, return SC_ERROR_TOO_MANY_OBJECTS; r = sc_profile_get_file(profile, "PrKey-DF", &df); - SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, "Get PrKey-DF info failed"); - assert(df); + LOG_TEST_RET(p15card->card->ctx, r, "Get PrKey-DF info failed"); + if (!df) + return SC_ERROR_INTERNAL; key_info->path = df->path; sc_file_free(df); r = sc_append_file_id(&key_info->path, key_info->key_reference); @@ -314,16 +323,15 @@ static int rtecp_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, auth_id = obj->auth_id.value[0]; key_info = (sc_pkcs15_prkey_info_t *)obj->data; - assert(key_info); + if (!key_info) + return SC_ERROR_INTERNAL; if ((obj->type == SC_PKCS15_TYPE_PRKEY_RSA && key_info->modulus_length % 128 != 0) || (obj->type == SC_PKCS15_TYPE_PRKEY_GOSTR3410 && key_info->modulus_length != SC_PKCS15_GOSTR3410_KEYSIZE)) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, - "Unsupported key size %"SC_FORMAT_LEN_SIZE_T"u\n", - key_info->modulus_length); + sc_log(ctx, "Unsupported key size %" SC_FORMAT_LEN_SIZE_T "u\n", key_info->modulus_length); return SC_ERROR_INVALID_ARGUMENTS; } if (obj->type == SC_PKCS15_TYPE_PRKEY_GOSTR3410) @@ -334,23 +342,23 @@ static int rtecp_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, || ((int*)key_info->params.data)[0] > 3) return SC_ERROR_INVALID_ARGUMENTS; paramset = ((unsigned int*)key_info->params.data)[0] & 0x03; - assert(sizeof(prgkey_prop)/sizeof(prgkey_prop[0]) > 1); - assert(sizeof(pbgkey_prop)/sizeof(pbgkey_prop[0]) > 1); + static_assert(sizeof(prgkey_prop) / sizeof(prgkey_prop[0]) > 1, "internal error"); + static_assert(sizeof(pbgkey_prop) / sizeof(pbgkey_prop[0]) > 1, "internal error"); prgkey_prop[1] = 0x10 + (paramset << 4); pbgkey_prop[1] = prgkey_prop[1]; } r = sc_profile_get_file(profile, "PKCS15-AppDF", &file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Get PKCS15-AppDF info failed"); + LOG_TEST_RET(ctx, r, "Get PKCS15-AppDF info failed"); r = sc_file_add_acl_entry(file, SC_AC_OP_CREATE, SC_AC_CHV, auth_id); if (r == SC_SUCCESS) r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_CREATE); sc_file_free(file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Authenticate failed"); + LOG_TEST_RET(ctx, r, "Authenticate failed"); file = sc_file_new(); if (!file) - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); file->id = key_info->key_reference; r = sc_file_set_type_attr(file, (const u8*)"\x10\x00", 2); @@ -361,7 +369,7 @@ static int rtecp_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, file->size = key_info->modulus_length / 8; if (r == SC_SUCCESS) { - assert(sizeof(prkey_sec)/sizeof(prkey_sec[0]) > 7); + static_assert(sizeof(prkey_sec) / sizeof(prkey_sec[0]) > 7, "internal error"); prkey_sec[2] = auth_id; prkey_sec[3] = auth_id; prkey_sec[7] = auth_id; @@ -385,7 +393,7 @@ static int rtecp_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, file->size = key_info->modulus_length / 8 * 2; if (r == SC_SUCCESS) { - assert(sizeof(pbkey_sec)/sizeof(pbkey_sec[0]) > 7); + static_assert(sizeof(pbkey_sec) / sizeof(pbkey_sec[0]) > 7, "internal error"); pbkey_sec[2] = auth_id; pbkey_sec[7] = auth_id; r = sc_file_set_sec_attr(file, pbkey_sec, sizeof(pbkey_sec)); @@ -402,7 +410,7 @@ static int rtecp_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, r = sc_create_file(p15card->card, file); } sc_file_free(file); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(ctx, r); } /* @@ -432,18 +440,21 @@ static int rtecp_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, return SC_ERROR_NOT_SUPPORTED; key_info = (sc_pkcs15_prkey_info_t *)obj->data; - assert(key_info); + if (!key_info) + return SC_ERROR_INTERNAL; if (key->algorithm == SC_ALGORITHM_RSA) { - assert(key_info->modulus_length % 128 == 0); + if (key_info->modulus_length % 128 != 0) + return SC_ERROR_INTERNAL; len = key_info->modulus_length / 8 / 2; key_len = len * 5 + 8; buf_len = key_len; } else { - assert(key_info->modulus_length == SC_PKCS15_GOSTR3410_KEYSIZE); + if (key_info->modulus_length != SC_PKCS15_GOSTR3410_KEYSIZE) + return SC_ERROR_INTERNAL; len = key_info->modulus_length / 8; key_len = len; buf_len = len; @@ -462,8 +473,9 @@ static int rtecp_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, return SC_ERROR_INVALID_ARGUMENTS; buf = calloc(1, buf_len); if (!buf) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); - assert(key_len <= buf_len); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + if (key_len > buf_len) + return SC_ERROR_INTERNAL; if (key->algorithm == SC_ALGORITHM_RSA) { /* p */ @@ -493,14 +505,14 @@ static int rtecp_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, r = sc_select_file(card, &path, NULL); if (r == SC_SUCCESS) r = sc_change_reference_data(card, 0, 0, NULL, 0, buf, key_len, NULL); - assert(buf); sc_mem_clear(buf, key_len); /* store public key */ if (key->algorithm == SC_ALGORITHM_RSA) key_len = len * 3; else goto end; - assert(key_len <= buf_len); + if (key_len > buf_len) + return SC_ERROR_INTERNAL; if (key->algorithm == SC_ALGORITHM_RSA) { /* modulus */ @@ -516,13 +528,14 @@ static int rtecp_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, r = sc_profile_get_file(profile, "PuKey-DF", &pukey_df); if (r == SC_SUCCESS) { - assert(pukey_df); + if (!pukey_df) + return SC_ERROR_INTERNAL; path = pukey_df->path; r = sc_append_file_id(&path, key_info->key_reference); sc_file_free(pukey_df); } else if (card->ctx->debug >= 2) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n", "Get PuKey-DF info failed"); + sc_log(card->ctx, "%s\n", "Get PuKey-DF info failed"); } if (r == SC_SUCCESS) { @@ -531,12 +544,11 @@ static int rtecp_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, r = sc_change_reference_data(card, 0, 0, NULL, 0, buf, key_len, NULL); if (r && card->ctx->debug >= 2) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s\n", "Store public key failed"); + sc_log(card->ctx, "%s\n", "Store public key failed"); } end: - assert(buf); free(buf); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(card->ctx, r); } /* @@ -568,13 +580,16 @@ static int rtecp_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, return SC_ERROR_NOT_SUPPORTED; } key_info = (sc_pkcs15_prkey_info_t *)obj->data; - assert(key_info); + if (!key_info) + return SC_ERROR_INTERNAL; data.key_id = key_info->key_reference; - assert(data.key_id != 0); + if (data.key_id == 0) + return SC_ERROR_INTERNAL; switch (data.type) { case SC_ALGORITHM_RSA: - assert(key_info->modulus_length % 128 == 0); + if (key_info->modulus_length % 128 != 0) + return SC_ERROR_INTERNAL; data.u.rsa.modulus_len = key_info->modulus_length / 8; data.u.rsa.modulus = calloc(1, data.u.rsa.modulus_len); data.u.rsa.exponent_len = key_info->modulus_length / 8 / 2; @@ -583,26 +598,26 @@ static int rtecp_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, { free(data.u.rsa.modulus); free(data.u.rsa.exponent); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } break; case SC_ALGORITHM_GOSTR3410: - assert(key_info->modulus_length == SC_PKCS15_GOSTR3410_KEYSIZE); + if (key_info->modulus_length != SC_PKCS15_GOSTR3410_KEYSIZE) + return SC_ERROR_INTERNAL; data.u.gostr3410.xy_len = key_info->modulus_length / 8 * 2; data.u.gostr3410.xy = calloc(1, data.u.gostr3410.xy_len); if (!data.u.gostr3410.xy) { free(data.u.gostr3410.xy); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } break; default: - assert(0); + return SC_ERROR_INTERNAL; } r = sc_card_ctl(p15card->card, SC_CARDCTL_RTECP_GENERATE_KEY, &data); if (r == SC_SUCCESS) { - assert(pubkey); pubkey->algorithm = data.type; switch (data.type) { @@ -618,7 +633,7 @@ static int rtecp_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, break; } } - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(ctx, r); } /* @@ -632,10 +647,9 @@ static int rtecp_finalize(sc_card_t *card) return sc_card_ctl(card, SC_CARDCTL_RTECP_INIT_END, NULL); } - /* * Delete object - * + * * Applied to private key: used to delete public part internal file */ static int rtecp_delete_object(struct sc_profile *profile, struct sc_pkcs15_card *p15card, diff --git a/src/pkcs15init/pkcs15-rutoken.c b/src/pkcs15init/pkcs15-rutoken.c index 1638ef6140..fc83954d65 100644 --- a/src/pkcs15init/pkcs15-rutoken.c +++ b/src/pkcs15init/pkcs15-rutoken.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -99,7 +99,7 @@ rutoken_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, pin_ref = auth_info->attrs.pin.reference; so_pin_flag = auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN; - sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "PIN reference %i%s\n", + sc_log(p15card->card->ctx, "PIN reference %i%s\n", pin_ref, so_pin_flag ? " SO PIN flag" : ""); if ((pin_ref == SC_RUTOKEN_DEF_ID_GCHV_ADMIN && so_pin_flag) @@ -133,7 +133,7 @@ rutoken_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, if (puk_len != 0) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "Do not enter User unblocking PIN (PUK): %s\n", sc_strerror(SC_ERROR_NOT_SUPPORTED)); return SC_ERROR_NOT_SUPPORTED; @@ -152,11 +152,11 @@ rutoken_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, return SC_SUCCESS; else { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Incorrect PIN\n"); + sc_log(ctx, "Incorrect PIN\n"); break; } } - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "PIN reference %i not found in standard (Rutoken) PINs\n", auth_info->attrs.pin.reference); return SC_ERROR_NOT_SUPPORTED; @@ -181,14 +181,8 @@ static int create_pins(sc_card_t *card) param_do.HDR.OP.byObjectFlags = do_pins[i].flags; param_do.HDR.OP.byObjectTry = do_pins[i].try; param_do.HDR.wDOBodyLen = sizeof(do_pins[i].pass); - /* assert(do_pins[i].p_sattr != NULL); */ - /* assert(sizeof(*param_do.HDR.SA_V2)) */ - /* assert(sizeof(param_do.HDR.SA_V2) == sizeof(*do_pins[i].p_sattr)); */ memcpy(param_do.HDR.SA_V2, *do_pins[i].p_sattr, sizeof(*do_pins[i].p_sattr)); - /* assert(do_pins[i].pass); */ - /* assert(sizeof(*param_do.abyDOBody)) */ - /* assert(sizeof(param_do.abyDOBody) >= sizeof(do_pins[i].pass)); */ memcpy(param_do.abyDOBody, do_pins[i].pass, sizeof(do_pins[i].pass)); r = sc_card_ctl(card, SC_CARDCTL_RUTOKEN_CREATE_DO, ¶m_do); @@ -292,7 +286,7 @@ rutoken_erase(struct sc_profile *profile, sc_pkcs15_card_t *p15card) { ret = create_typical_fs(card); if (ret != SC_SUCCESS) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Failed to create typical fs: %s\n", sc_strerror(ret)); ret_end = sc_card_ctl(card, SC_CARDCTL_RUTOKEN_FORMAT_END, NULL); @@ -300,7 +294,7 @@ rutoken_erase(struct sc_profile *profile, sc_pkcs15_card_t *p15card) ret = ret_end; } if (ret != SC_SUCCESS) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + sc_log(card->ctx, "Failed to erase: %s\n", sc_strerror(ret)); else sc_free_apps(card); diff --git a/src/pkcs15init/pkcs15-sc-hsm.c b/src/pkcs15init/pkcs15-sc-hsm.c index 26bff9608d..c425bbeb05 100644 --- a/src/pkcs15init/pkcs15-sc-hsm.c +++ b/src/pkcs15init/pkcs15-sc-hsm.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H @@ -39,6 +39,7 @@ #include "../libopensc/pkcs15.h" #include "common/compat_strlcpy.h" +#include "common/compat_strlcat.h" #include "pkcs15-init.h" #include "profile.h" @@ -139,7 +140,7 @@ static int sc_hsm_determine_free_id(struct sc_pkcs15_card *p15card, u8 range) LOG_TEST_RET(card->ctx, filelistlength, "Could not enumerate file and key identifier"); for (j = 0; j < 256; j++) { - for (i = 0; i < filelistlength; i += 2) { + for (i = 0; i + 1 < filelistlength; i += 2) { if ((filelist[i] == range) && (filelist[i + 1] == j)) { break; } @@ -153,7 +154,7 @@ static int sc_hsm_determine_free_id(struct sc_pkcs15_card *p15card, u8 range) -static int sc_hsm_encode_gakp_rsa(struct sc_pkcs15_card *p15card, sc_cvc_t *cvc, int keysize) { +static int sc_hsm_encode_gakp_rsa(struct sc_pkcs15_card *p15card, sc_cvc_t *cvc, size_t keysize) { struct sc_object_id rsa15withSHA256 = { { 0,4,0,127,0,7,2,2,2,1,2,-1 } }; LOG_FUNC_CALLED(p15card->card->ctx); @@ -166,7 +167,7 @@ static int sc_hsm_encode_gakp_rsa(struct sc_pkcs15_card *p15card, sc_cvc_t *cvc, memcpy(cvc->coefficientAorExponent, pubexp, sizeof(pubexp)); cvc->pukoid = rsa15withSHA256; - cvc->modulusSize = keysize; + cvc->modulusSize = (int)keysize; LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } @@ -247,6 +248,7 @@ static int sc_hsm_generate_key(struct sc_profile *profile, struct sc_pkcs15_card struct sc_pkcs15_pubkey *pubkey) { struct sc_card *card = p15card->card; + sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)object->data; sc_cardctl_sc_hsm_keygen_info_t sc_hsm_keyinfo; sc_cvc_t cvc; @@ -263,7 +265,11 @@ static int sc_hsm_generate_key(struct sc_profile *profile, struct sc_pkcs15_card memset(&cvc, 0, sizeof(cvc)); strlcpy(cvc.car, "UTCA00001", sizeof cvc.car); - strlcpy(cvc.chr, "UTTM00001", sizeof cvc.chr); + cvc.carLen = strlen(cvc.car); + if (priv->serialno) + strlcpy(cvc.chr, priv->serialno, sizeof cvc.chr); + strlcat(cvc.chr, "00001", sizeof cvc.chr); + cvc.chrLen = strlen(cvc.chr); switch(object->type) { case SC_PKCS15_TYPE_PRKEY_RSA: @@ -276,7 +282,10 @@ static int sc_hsm_generate_key(struct sc_profile *profile, struct sc_pkcs15_card r = SC_ERROR_NOT_IMPLEMENTED; break; } - LOG_TEST_RET(p15card->card->ctx, r, "Could not encode GAKP cdata"); + if (r != SC_SUCCESS) { + sc_pkcs15emu_sc_hsm_free_cvc(&cvc); + LOG_TEST_RET(p15card->card->ctx, r, "Could not encode GAKP cdata"); + } r = sc_pkcs15emu_sc_hsm_encode_cvc(p15card, &cvc, &cvcbin, &cvclen); sc_pkcs15emu_sc_hsm_free_cvc(&cvc); @@ -565,7 +574,7 @@ static int sc_hsm_emu_update_any_df(struct sc_profile *profile, struct sc_pkcs15 SC_FUNC_CALLED(ctx, 1); switch(op) { case SC_AC_OP_ERASE: - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Update DF; erase object('%s',type:%X)", object->label, object->type); + sc_log(ctx, "Update DF; erase object('%s',type:%X)", object->label, object->type); switch(object->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: rv = sc_hsm_delete_ef(p15card, PRKD_PREFIX, ((struct sc_pkcs15_prkey_info *)object->data)->key_reference); @@ -583,7 +592,7 @@ static int sc_hsm_emu_update_any_df(struct sc_profile *profile, struct sc_pkcs15 break; case SC_AC_OP_UPDATE: case SC_AC_OP_CREATE: - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Update DF; create object('%s',type:%X)", object->label, object->type); + sc_log(ctx, "Update DF; create object('%s',type:%X)", object->label, object->type); switch(object->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PUBKEY: rv = SC_SUCCESS; diff --git a/src/pkcs15init/pkcs15-setcos.c b/src/pkcs15init/pkcs15-setcos.c index 0a00627e39..3f4d4f6684 100644 --- a/src/pkcs15init/pkcs15-setcos.c +++ b/src/pkcs15init/pkcs15-setcos.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -26,6 +26,7 @@ #include "libopensc/opensc.h" #include "libopensc/cardctl.h" +#include "libopensc/internal.h" #include "libopensc/log.h" #include "pkcs15-init.h" #include "profile.h" @@ -42,7 +43,7 @@ static int setcos_create_pin_internal(sc_profile_t *, sc_pkcs15_card_t *, static int setcos_puk_retries(sc_profile_t *profile, int pin_ref) { - sc_pkcs15_auth_info_t auth_info; + sc_pkcs15_auth_info_t auth_info = {0}; auth_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; auth_info.attrs.pin.reference = 1; /* Default SO PIN ref. */ @@ -101,31 +102,31 @@ setcos_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) /* Create the MF if it doesn't exist yet */ r = sc_select_file(p15card->card, &mf->path, NULL); if (r == SC_ERROR_FILE_NOT_FOUND) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "MF doesn't exist, creating now"); + sc_log(ctx, "MF doesn't exist, creating now"); /* Fix up the file's ACLs */ r = sc_pkcs15init_fixup_file(profile, p15card, mf); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "MF fixup failed"); + LOG_TEST_RET(ctx, r, "MF fixup failed"); mf->status = SC_FILE_STATUS_CREATION; r = sc_create_file(p15card->card, mf); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "MF creation failed"); + LOG_TEST_RET(ctx, r, "MF creation failed"); } - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot select MF"); + LOG_TEST_RET(ctx, r, "Cannot select MF"); /* Create the global pin file if it doesn't exist yet */ r = sc_profile_get_file(profile, "pinfile", &pinfile); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot get 'pinfile' from profile"); + LOG_TEST_RET(ctx, r, "Cannot get 'pinfile' from profile"); r = sc_select_file(p15card->card, &pinfile->path, NULL); if (r == SC_ERROR_FILE_NOT_FOUND) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Global pin file doesn't exist, creating now"); + sc_log(ctx, "Global pin file doesn't exist, creating now"); /* Fix up the file's ACLs */ r = sc_pkcs15init_fixup_file(profile, p15card, pinfile); if (r < 0) sc_file_free(pinfile); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Pinfile fixup failed"); + LOG_TEST_RET(ctx, r, "Pinfile fixup failed"); /* Set life cycle state to SC_FILE_STATUS_CREATION, * which means that all ACs are ignored. */ @@ -133,12 +134,12 @@ setcos_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) r = sc_create_file(p15card->card, pinfile); if (r < 0) sc_file_free(pinfile); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Pinfile creation failed"); + LOG_TEST_RET(ctx, r, "Pinfile creation failed"); } sc_file_free(pinfile); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Select pinfile failed"); + LOG_TEST_RET(ctx, r, "Select pinfile failed"); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(ctx, r); } /* @@ -153,12 +154,12 @@ setcos_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *d SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); r = sc_pkcs15init_fixup_file(profile, p15card, df); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "SetCOS file ACL fixup failed"); + LOG_TEST_RET(ctx, r, "SetCOS file ACL fixup failed"); r = sc_create_file(p15card->card, df); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "SetCOS create file failed"); + LOG_TEST_RET(ctx, r, "SetCOS create file failed"); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(ctx, r); } @@ -169,22 +170,25 @@ static int setcos_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { - sc_pkcs15_auth_info_t auth_info_prof; + sc_pkcs15_auth_info_t auth_info_prof = {0}; auth_info_prof.attrs.pin.reference = 1; /* Default SO PIN ref. */ auth_info_prof.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &auth_info_prof); /* For the SO pin, we take the first available pin reference = 1 */ - if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) + if (auth_info != NULL && auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) auth_info->attrs.pin.reference = auth_info_prof.attrs.pin.reference; /* sc_pkcs15init_create_pin() starts checking if -1 is an acceptable * pin reference, which isn't for the SetCOS cards. And since the * value 1 has been assigned to the SO pin, we'll jump to 2. */ - else if (auth_info->attrs.pin.reference <= 0) + else if (auth_info != NULL && auth_info->attrs.pin.reference <= 0) { + if (auth_info_prof.attrs.pin.reference != 1) + return SC_ERROR_INVALID_PIN_REFERENCE; auth_info->attrs.pin.reference = auth_info_prof.attrs.pin.reference + 1; + } - return 0; + return SC_SUCCESS; } /* @@ -199,7 +203,7 @@ setcos_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, { struct sc_context *ctx = p15card->card->ctx; sc_pkcs15_auth_info_t *auth_info = (sc_pkcs15_auth_info_t *) pin_obj->data; - sc_file_t *pinfile = NULL; + sc_file_t *pinfile = NULL, *tmp_pinfile = NULL; int r, ignore_ac = 0; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); @@ -207,15 +211,16 @@ setcos_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; - /* Create the global pin file if it doesn't exist yet */ - r = sc_profile_get_file(profile, "pinfile", &pinfile); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "No 'pinfile' template in profile"); + /* Create the global pin file if it doesn't exist yet */ + r = sc_profile_get_file(profile, "pinfile", &tmp_pinfile); + LOG_TEST_RET(ctx, r, "No 'pinfile' template in profile"); - r = sc_select_file(p15card->card, &pinfile->path, &pinfile); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot select 'pinfile'"); + r = sc_select_file(p15card->card, &tmp_pinfile->path, &pinfile); + sc_file_free(tmp_pinfile); + LOG_TEST_RET(ctx, r, "Cannot select 'pinfile'"); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "pinfile->status:%X", pinfile->status); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "create PIN with reference:%X, flags:%X, path:%s", + sc_log(ctx, "pinfile->status:%X", pinfile->status); + sc_log(ctx, "create PIN with reference:%X, flags:%X, path:%s", auth_info->attrs.pin.reference, auth_info->attrs.pin.flags, sc_print_path(&auth_info->path)); if (pinfile->status == SC_FILE_STATUS_CREATION) @@ -227,22 +232,21 @@ setcos_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, /* If pinfile is in 'Creation' state and SOPIN has been created, * change status of MF and 'pinfile' to 'Operational:Activated' */ + sc_file_free(pinfile); if (ignore_ac && (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)) { sc_file_t *mf = profile->mf_info->file; r = sc_card_ctl(p15card->card, SC_CARDCTL_SETCOS_ACTIVATE_FILE, NULL); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot set 'pinfile' into the activated state"); + LOG_TEST_RET(ctx, r, "Cannot set 'pinfile' into the activated state"); r = sc_select_file(p15card->card, &mf->path, NULL); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot select MF"); + LOG_TEST_RET(ctx, r, "Cannot select MF"); r = sc_card_ctl(p15card->card, SC_CARDCTL_SETCOS_ACTIVATE_FILE, NULL); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot set MF into the activated state"); + LOG_TEST_RET(ctx, r, "Cannot set MF into the activated state"); } - sc_file_free(pinfile); - - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(ctx, r); } /* @@ -254,7 +258,7 @@ setcos_new_file(sc_profile_t *profile, sc_card_t *card, unsigned int num, /* number of objects of this type already on the card */ sc_file_t **out) { - sc_file_t *file; + sc_file_t *file = NULL; sc_path_t *p; char name[64]; const char *tag; @@ -269,14 +273,14 @@ setcos_new_file(sc_profile_t *profile, sc_card_t *card, else if ((type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_DATA_OBJECT) tag = "data"; else { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported file type"); + sc_log(card->ctx, "Unsupported file type"); return SC_ERROR_INVALID_ARGUMENTS; } /* Get template from profile */ snprintf(name, sizeof(name), "template-%s", tag); if (sc_profile_get_file(profile, name, &file) < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Profile doesn't define %s", name); + sc_log(card->ctx, "Profile doesn't define %s", name); return SC_ERROR_NOT_SUPPORTED; } @@ -284,6 +288,10 @@ setcos_new_file(sc_profile_t *profile, sc_card_t *card, file->id += num; p = &file->path; *p = profile->df_info->file->path; + if (p->len + 2 > SC_MAX_PATH_SIZE) { + sc_file_free(file); + return SC_ERROR_INVALID_DATA; + } p->value[p->len++] = (u8) (file->id / 256); p->value[p->len++] = (u8) (file->id % 256); @@ -324,50 +332,58 @@ setcos_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)object->data; struct sc_file *file = NULL; - int keybits = key_info->modulus_length, r; + size_t keybits = key_info->modulus_length; + int r; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Create key failed: RSA only supported"); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Create key failed: RSA only supported"); /* Parameter check */ - if ( (keybits < 512) || (keybits > 1024) || (keybits & 0x7)) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "Invalid key length"); + if ((keybits < 512) || (keybits > 1024) || (keybits & 0x7)) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid key length"); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "create private key ID:%s\n", sc_pkcs15_print_id(&key_info->id)); + sc_log(ctx, "create private key ID:%s\n", sc_pkcs15_print_id(&key_info->id)); /* Get the private key file */ r = setcos_new_file(profile, p15card->card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot get new private key file"); + LOG_TEST_RET(ctx, r, "Cannot get new private key file"); /* Take enough room for a 1024 bit key */ if (file->size < 512) file->size = 512; /* Replace the path of instantiated key template by the path from the object data. */ - memcpy(&file->path, &key_info->path, sizeof(file->path)); - file->id = file->path.value[file->path.len - 2] * 0x100 - + file->path.value[file->path.len - 1]; + memcpy(&file->path, &key_info->path, sizeof(file->path)); + if (file->path.len < 2) { + sc_file_free(file); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid path"); + } + file->id = file->path.value[file->path.len - 2] * 0x100 + + file->path.value[file->path.len - 1]; key_info->key_reference = file->path.value[file->path.len - 1] & 0xFF; - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Path of private key file to create %s\n", sc_print_path(&file->path)); + sc_log(ctx, "Path of private key file to create %s\n", sc_print_path(&file->path)); - r = sc_select_file(p15card->card, &file->path, NULL); - if (!r) { + r = sc_select_file(p15card->card, &file->path, NULL); + if (!r) { r = sc_pkcs15init_delete_by_path(profile, p15card, &file->path); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to delete private key file"); - } - else if (r != SC_ERROR_FILE_NOT_FOUND) { - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Select private key file error"); + if (r != SC_SUCCESS) + sc_file_free(file); + LOG_TEST_RET(ctx, r, "Failed to delete private key file"); + } else if (r != SC_ERROR_FILE_NOT_FOUND) { + sc_file_free(file); + file = NULL; + LOG_TEST_RET(ctx, r, "Select private key file error"); } /* Now create the key file */ r = sc_pkcs15init_create_file(profile, p15card, file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot create private key file"); - sc_file_free(file); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_TEST_RET(ctx, r, "Cannot create private key file"); + + LOG_FUNC_RETURN(ctx, r); } @@ -383,24 +399,25 @@ setcos_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)object->data; struct sc_cardctl_setcos_gen_store_key_info args; struct sc_file *file = NULL; - int r, keybits = key_info->modulus_length; + int r; + size_t keybits = key_info->modulus_length; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Store key failed: RSA only supported"); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Store key failed: RSA only supported"); /* Parameter check */ if ( (keybits < 512) || (keybits > 1024) || (keybits & 0x7)) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "Invalid key length"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid key length"); - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "store key with ID:%s and path:%s\n", sc_pkcs15_print_id(&key_info->id), + sc_log(ctx, "store key with ID:%s and path:%s\n", sc_pkcs15_print_id(&key_info->id), sc_print_path(&key_info->path)); r = sc_select_file(p15card->card, &key_info->path, &file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot store key: select key file failed"); + LOG_TEST_RET(ctx, r, "Cannot store key: select key file failed"); r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "No authorisation to store private key"); + LOG_TEST_RET(ctx, r, "No authorisation to store private key"); /* Fill in data structure */ memset(&args, 0, sizeof(args)); @@ -415,11 +432,11 @@ setcos_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, /* Generate/store rsa key */ r = sc_card_ctl(p15card->card, SC_CARDCTL_SETCOS_GENERATE_STORE_KEY, &args); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Card control 'GENERATE_STORE_KEY' failed"); + LOG_TEST_RET(ctx, r, "Card control 'GENERATE_STORE_KEY' failed"); sc_file_free(file); - SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); + LOG_FUNC_RETURN(ctx, r); } @@ -439,18 +456,21 @@ setcos_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED, "Generate key failed: RSA only supported"); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Generate key failed: RSA only supported"); /* Parameter check */ if ( (keybits < 512) || (keybits > 1024) || (keybits & 0x7)) - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "Invalid key length"); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid key length"); r = sc_select_file(p15card->card, &key_info->path, &file); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot store key: select key file failed"); + LOG_TEST_RET(ctx, r, "Cannot store key: select key file failed"); /* Authenticate */ r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "No authorisation to store private key"); + if (r != SC_SUCCESS) { + sc_file_free(file); + LOG_TEST_RET(ctx, r, "No authorisation to store private key"); + } /* Fill in data structure */ memset(&args, 0, sizeof(args)); @@ -461,20 +481,28 @@ setcos_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, /* Generate/store rsa key */ r = sc_card_ctl(p15card->card, SC_CARDCTL_SETCOS_GENERATE_STORE_KEY, &args); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Card control 'GENERATE_STORE_KEY' failed"); + if (r != SC_SUCCESS) { + sc_file_free(file); + LOG_TEST_RET(ctx, r, "Card control 'GENERATE_STORE_KEY' failed"); + } - /* Keypair generation -> collect public key info */ + /* Key pair generation -> collect public key info */ if (pubkey != NULL) { pubkey->algorithm = SC_ALGORITHM_RSA; - pubkey->u.rsa.modulus.len = (keybits + 7) / 8; + pubkey->u.rsa.modulus.len = BYTES4BITS(keybits); pubkey->u.rsa.modulus.data = malloc(pubkey->u.rsa.modulus.len); + if (pubkey->u.rsa.modulus.data == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); pubkey->u.rsa.exponent.len = SETCOS_DEFAULT_PUBKEY_LEN; pubkey->u.rsa.exponent.data = malloc(SETCOS_DEFAULT_PUBKEY_LEN); + if (pubkey->u.rsa.exponent.data == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(pubkey->u.rsa.exponent.data, SETCOS_DEFAULT_PUBKEY, SETCOS_DEFAULT_PUBKEY_LEN); /* Get public key modulus */ r = sc_select_file(p15card->card, &file->path, NULL); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot get key modulus: select key file failed"); + sc_file_free(file); + LOG_TEST_RET(ctx, r, "Cannot get key modulus: select key file failed"); data_obj.P1 = 0x01; data_obj.P2 = 0x01; @@ -482,19 +510,23 @@ setcos_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, data_obj.DataLen = sizeof(raw_pubkey); r = sc_card_ctl(p15card->card, SC_CARDCTL_SETCOS_GETDATA, &data_obj); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot get key modulus: 'SETCOS_GETDATA' failed"); + LOG_TEST_RET(ctx, r, "Cannot get key modulus: 'SETCOS_GETDATA' failed"); + + if (data_obj.DataLen < 3 || data_obj.DataLen < pubkey->u.rsa.modulus.len) + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Cannot get key modulus: wrong length of raw key"); keybits = ((raw_pubkey[0] * 256) + raw_pubkey[1]); /* modulus bit length */ if (keybits != key_info->modulus_length) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, + sc_log(ctx, "key-size from card[%"SC_FORMAT_LEN_SIZE_T"u] does not match[%"SC_FORMAT_LEN_SIZE_T"u]\n", keybits, key_info->modulus_length); - SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_PKCS15INIT, "Failed to generate key"); + LOG_TEST_RET(ctx, SC_ERROR_PKCS15INIT, "Failed to generate key"); } - memcpy (pubkey->u.rsa.modulus.data, &raw_pubkey[2], pubkey->u.rsa.modulus.len); + memcpy(pubkey->u.rsa.modulus.data, &raw_pubkey[2], pubkey->u.rsa.modulus.len); + } else { + sc_file_free(file); } - sc_file_free(file); return r; } diff --git a/src/pkcs15init/pkcs15-starcos.c b/src/pkcs15init/pkcs15-starcos.c index f540abc7fa..f20e8b5e07 100644 --- a/src/pkcs15init/pkcs15-starcos.c +++ b/src/pkcs15init/pkcs15-starcos.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -83,7 +83,7 @@ static int starcos_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) sc_file_t *mf_file, *isf_file, *ipf_file; sc_path_t tpath; u8 *p = mf_data.data.mf.header, tmp = 0; - sc_pkcs15_auth_info_t sopin; + sc_pkcs15_auth_info_t sopin = {0}; /* test if we already have a MF */ memset(&tpath, 0, sizeof(sc_path_t)); @@ -158,7 +158,7 @@ static int starcos_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) *p = ipf_file->size & 0xff; ret = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_FILE, &ipf_data); if (ret != SC_SUCCESS) { - free(ipf_file); + sc_file_free(ipf_file); return ret; } /* init IPF */ @@ -180,7 +180,7 @@ static int starcos_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_starcos_create_data df_data, ipf_data; sc_file_t *isf_file, *ipf_file; u8 *p = df_data.data.df.header, tmp = 0; - sc_pkcs15_auth_info_t sopin; + sc_pkcs15_auth_info_t sopin = {0}; sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &sopin); @@ -239,7 +239,7 @@ static int starcos_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, *p = ipf_file->size & 0xff; ret = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_FILE, &ipf_data); if (ret != SC_SUCCESS) { - free(ipf_file); + sc_file_free(ipf_file); return ret; } /* init IPF */ @@ -255,7 +255,7 @@ static int starcos_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, static int have_onepin(sc_profile_t *profile) { - sc_pkcs15_auth_info_t sopin; + sc_pkcs15_auth_info_t sopin = {0}; sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &sopin); @@ -428,7 +428,7 @@ static int starcos_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, return r; if (puk && puk_len) { - sc_pkcs15_auth_info_t puk_info; + sc_pkcs15_auth_info_t puk_info = {0}; if (puk_len > 8) return SC_ERROR_INVALID_ARGUMENTS; @@ -500,11 +500,13 @@ static int starcos_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15car return SC_SUCCESS; } +/* FIXME This buffer does not fit the 2k nor 3k RSA key components. For them to work, + * this constant needs to be bumped to 1024 but we do not have a way to test it */ #define STARCOS_MAX_PR_KEYSIZE 370 static int starcos_encode_prkey(struct sc_pkcs15_prkey_rsa *rsa, u8 *buf) { - size_t i = 0; + size_t i = 0, len; u8 *p = buf; /* clear key buffer */ @@ -513,9 +515,12 @@ static int starcos_encode_prkey(struct sc_pkcs15_prkey_rsa *rsa, u8 *buf) if (rsa->p.len && rsa->q.len && rsa->dmp1.len && rsa->dmq1.len && rsa->iqmp.len) { /* CRT RSA key */ + len = 13 + rsa->q.len + rsa->p.len + 16 + rsa->dmq1.len + 16 + rsa->dmp1.len + rsa->p.len; + if (len > STARCOS_MAX_PR_KEYSIZE) { + return SC_ERROR_INTERNAL; + } /* get number of 0x00 bytes */ - i = STARCOS_MAX_PR_KEYSIZE - rsa->p.len - rsa->q.len - - rsa->dmp1.len - rsa->dmq1.len - 45 - rsa->p.len; + i = STARCOS_MAX_PR_KEYSIZE - len; /* key format list */ *p++ = 0x0c; @@ -538,11 +543,11 @@ static int starcos_encode_prkey(struct sc_pkcs15_prkey_rsa *rsa, u8 *buf) *p++ = rsa->p.data[i - 1]; for (i = 16; i != 0; i--) *p++ = 0x00; - for (i = rsa->dmp1.len; i != 0; i--) + for (i = rsa->dmq1.len; i != 0; i--) *p++ = rsa->dmq1.data[i - 1]; for (i = 16; i != 0; i--) *p++ = 0x00; - for (i = rsa->dmq1.len; i != 0; i--) + for (i = rsa->dmp1.len; i != 0; i--) *p++ = rsa->dmp1.data[i - 1]; for (i = rsa->iqmp.len; i != 0; i--) *p++ = rsa->iqmp.data[i - 1]; @@ -550,8 +555,12 @@ static int starcos_encode_prkey(struct sc_pkcs15_prkey_rsa *rsa, u8 *buf) *p++ = 0x00; } else if (rsa->modulus.len && rsa->d.len) { /* normal RSA key */ - i = STARCOS_MAX_PR_KEYSIZE - 7 - rsa->modulus.len - - rsa->d.len - 16; + len = 7 + rsa->modulus.len + 16 + rsa->d.len; + if (len > STARCOS_MAX_PR_KEYSIZE) { + return SC_ERROR_INTERNAL; + } + /* get number of 0x00 bytes */ + i = STARCOS_MAX_PR_KEYSIZE - len; /* key format list */ *p++ = 6; *p++ = 0x90; @@ -611,7 +620,7 @@ static int starcos_encode_pukey(struct sc_pkcs15_prkey_rsa *rsa, u8 *buf, if (!rsa) { if (!buf) /* if buf == NULL return length of the encoded key */ - return (int) 12 + (kinfo->modulus_length >> 3); + return 12 + (int)(kinfo->modulus_length >> 3); *p++ = 0x06; /* length key header */ *p++ = 0x01; /* CHA byte */ *p++ = 0x01; @@ -622,11 +631,11 @@ static int starcos_encode_pukey(struct sc_pkcs15_prkey_rsa *rsa, u8 *buf, *p = (u8) kinfo->key_reference; /* CHA byte */ } else { /* encode normal public key */ - size_t mod_len = rsa->modulus.len & 0xff, - exp_len = rsa->exponent.len & 0xff; + int mod_len = (int)rsa->modulus.len & 0xff, + exp_len = (int)rsa->exponent.len & 0xff; if (!buf) - return (int) 8 + mod_len + exp_len + 1; + return 8 + mod_len + exp_len + 1; *p++ = 0x06; /* length key header */ *p++ = 0x01; /* CHA byte */ @@ -652,7 +661,7 @@ static int starcos_write_pukey(sc_profile_t *profile, sc_card_t *card, { int r; size_t len, keylen, endpos; - u8 *buf, key[280], *p, num_keys; + u8 *buf, key[512], *p, num_keys; sc_file_t *tfile = NULL; sc_path_t tpath; @@ -670,23 +679,32 @@ static int starcos_write_pukey(sc_profile_t *profile, sc_card_t *card, return r; len = tfile->size; sc_file_free(tfile); + if (len == 0) + return SC_ERROR_INTERNAL; buf = malloc(len); if (!buf) return SC_ERROR_OUT_OF_MEMORY; /* read the complete IPF */ r = sc_read_binary(card, 0, buf, len, 0); - if (r < 0 || r != (int)len) + if (r < 0 || r != (int)len) { + free(buf); return r; + } /* get/fix number of keys */ num_keys = buf[0]; if (num_keys == 0xff) num_keys = 0; /* encode public key */ - keylen = starcos_encode_pukey(rsa, NULL, kinfo); + keylen = starcos_encode_pukey(rsa, NULL, kinfo); if (!keylen) { free(buf); return SC_ERROR_INTERNAL; } + /* The fixed buffer `key` should fit 3k RSA key + the header and footer added in this function */ + if (keylen + 12 > sizeof(key)) { + free(buf); + return SC_ERROR_INTERNAL; + } p = key; *p++ = (u8) kinfo->key_reference; *p++ = (keylen >> 8) & 0xff; @@ -709,11 +727,13 @@ static int starcos_write_pukey(sc_profile_t *profile, sc_card_t *card, /* updated IPF (XXX: currently append only) */ num_keys++; r = sc_update_binary(card, 0, &num_keys, 1, 0); - if (r < 0) + if (r < 0) { + free(buf); return r; + } endpos = starcos_ipf_get_lastpos(buf, len); free(buf); - return sc_update_binary(card, endpos, key, keylen + 12, 0); + return sc_update_binary(card, (unsigned)endpos, key, keylen + 12, 0); } static int starcos_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, @@ -763,7 +783,7 @@ static int starcos_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, tkey.key_header[4] = 0x0f; /* do state transition */ else tkey.key_header[4] = 0x8f; /* no state transition */ - tkey.key_header[5] = 0x11; /* requiere local state == 1 to update key */ + tkey.key_header[5] = 0x11; /* require local state == 1 to update key */ tkey.key_header[6] = 0x33; tkey.key_header[7] = 0x00; tkey.key_header[8] = 0x09; @@ -798,7 +818,7 @@ static int starcos_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_starcos_wkey_data tkey; if (key->algorithm != SC_ALGORITHM_RSA) - /* ignore DSA keys */ + /* ignore non-RSA keys */ return SC_ERROR_INVALID_ARGUMENTS; /* create sc_starcos_wkey_data */ @@ -911,7 +931,7 @@ static int starcos_finalize_card(sc_card_t *card) tfile.id = 0x3f00; r = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_END, &tfile); if (r < 0) - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "failed to call CREATE END for the MF\n"); + sc_log(card->ctx, "failed to call CREATE END for the MF\n"); /* call CREATE END for the apps (pkcs15) DF */ tfile.type = SC_FILE_TYPE_DF; tfile.id = 0x5015; diff --git a/src/pkcs15init/pkcs15-westcos.c b/src/pkcs15init/pkcs15-westcos.c deleted file mode 100644 index 4f0632065b..0000000000 --- a/src/pkcs15init/pkcs15-westcos.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * pkcs15-westcos.c: pkcs15 support for westcos card - * - * Copyright (C) 2009 francois.leblanc@cev-sa.com - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" - -#include -#include -#include - -#ifdef ENABLE_OPENSSL -#include -#include -#include -#include -#include -#include -#include -#endif - -#include "libopensc/sc-ossl-compat.h" -#include "libopensc/opensc.h" -#include "libopensc/cardctl.h" -#include "pkcs15-init.h" -#include "profile.h" - -static int westcos_pkcs15init_init_card(sc_profile_t *profile, - sc_pkcs15_card_t *p15card) -{ - int r; - struct sc_path path; - - sc_format_path("3F00", &path); - r = sc_select_file(p15card->card, &path, NULL); - if(r) return (r); - - return r; -} - -static int westcos_pkcs15init_create_dir(sc_profile_t *profile, - sc_pkcs15_card_t *p15card, - sc_file_t *df) -{ - int r; - - /* Create the application DF */ - r = sc_pkcs15init_create_file(profile, p15card, df); - - r = sc_select_file(p15card->card, &df->path, NULL); - if(r) return r; - - return 0; -} - -/* - * Select the PIN reference - */ -static int westcos_pkcs15_select_pin_reference(sc_profile_t *profile, - sc_pkcs15_card_t *p15card, - sc_pkcs15_auth_info_t *auth_info) -{ - - if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) - return SC_ERROR_OBJECT_NOT_VALID; - - if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) { - auth_info->attrs.pin.reference = 1; - } else { - auth_info->attrs.pin.reference = 0; - } - - return 0; -} - -/* - * Create a new PIN inside a DF - */ -static int westcos_pkcs15_create_pin(sc_profile_t *profile, - sc_pkcs15_card_t *p15card, - sc_file_t *df, - sc_pkcs15_object_t *pin_obj, - const u8 *pin, size_t pin_len, - const u8 *puk, size_t puk_len) -{ - int r; - sc_file_t *pinfile = NULL; - - if(pin_len>9 || puk_len>9) - return SC_ERROR_INVALID_ARGUMENTS; - - r = sc_profile_get_file(profile, "PINFILE", &pinfile); - if(r < 0) return r; - - r = sc_create_file(p15card->card, pinfile); - if(r) - { - if(r != SC_ERROR_FILE_ALREADY_EXISTS) - return (r); - - r = sc_select_file(p15card->card, &pinfile->path, NULL); - if(r) return (r); - } - - sc_file_free(pinfile); - - if(pin != NULL) - { - sc_changekey_t ck; - struct sc_pin_cmd_pin pin_cmd; - int ret; - - memset(&pin_cmd, 0, sizeof(pin_cmd)); - memset(&ck, 0, sizeof(ck)); - - memcpy(ck.key_template, "\x1e\x00\x00\x10", 4); - - pin_cmd.encoding = SC_PIN_ENCODING_GLP; - pin_cmd.len = pin_len; - pin_cmd.data = pin; - pin_cmd.max_length = 8; - - ret = sc_build_pin(ck.new_key.key_value, - sizeof(ck.new_key.key_value), &pin_cmd, 1); - if(ret < 0) - return SC_ERROR_CARD_CMD_FAILED; - - ck.new_key.key_len = ret; - r = sc_card_ctl(p15card->card, SC_CARDCTL_WESTCOS_CHANGE_KEY, &ck); - if(r) return r; - } - - if(puk != NULL) - { - sc_changekey_t ck; - struct sc_pin_cmd_pin puk_cmd; - int ret; - - memset(&puk_cmd, 0, sizeof(puk_cmd)); - memset(&ck, 0, sizeof(ck)); - - memcpy(ck.key_template, "\x1e\x00\x00\x20", 4); - - puk_cmd.encoding = SC_PIN_ENCODING_GLP; - puk_cmd.len = puk_len; - puk_cmd.data = puk; - puk_cmd.max_length = 8; - - ret = sc_build_pin(ck.new_key.key_value, - sizeof(ck.new_key.key_value), &puk_cmd, 1); - if(ret < 0) - return SC_ERROR_CARD_CMD_FAILED; - - ck.new_key.key_len = ret; - r = sc_card_ctl(p15card->card, SC_CARDCTL_WESTCOS_CHANGE_KEY, &ck); - if(r) return r; - } - - return 0; -} - -/* - * Create a new key file - */ -static int westcos_pkcs15init_create_key(sc_profile_t *profile, - sc_pkcs15_card_t *p15card, - sc_pkcs15_object_t *obj) -{ - - if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - return SC_ERROR_NOT_SUPPORTED; - } - - return 0; -} - - -/* - * Store a private key - */ -static int westcos_pkcs15init_store_key(sc_profile_t *profile, - sc_pkcs15_card_t *p15card, - sc_pkcs15_object_t *obj, - sc_pkcs15_prkey_t *key) -{ - return SC_ERROR_NOT_SUPPORTED; -} - -/* - * Generate key - */ -static int westcos_pkcs15init_generate_key(sc_profile_t *profile, - sc_pkcs15_card_t *p15card, - sc_pkcs15_object_t *obj, - sc_pkcs15_pubkey_t *pubkey) -{ -#ifndef ENABLE_OPENSSL - return SC_ERROR_NOT_SUPPORTED; -#else - int r = SC_ERROR_UNKNOWN; - long lg; - u8 *p; - sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; - RSA *rsa = NULL; - BIGNUM *bn = NULL; - BIO *mem = NULL; - - sc_file_t *prkf = NULL; - - if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { - return SC_ERROR_NOT_SUPPORTED; - } - -#if OPENSSL_VERSION_NUMBER>=0x00908000L - rsa = RSA_new(); - bn = BN_new(); - mem = BIO_new(BIO_s_mem()); - - if(rsa == NULL || bn == NULL || mem == NULL) - { - r = SC_ERROR_OUT_OF_MEMORY; - goto out; - } - - if(!BN_set_word(bn, RSA_F4) || - !RSA_generate_key_ex(rsa, key_info->modulus_length, bn, NULL)) -#else - mem = BIO_new(BIO_s_mem()); - - if(mem == NULL) - { - r = SC_ERROR_OUT_OF_MEMORY; - goto out; - } - - rsa = RSA_generate_key(key_info->modulus_length, RSA_F4, NULL, NULL); - if (!rsa) -#endif - { - r = SC_ERROR_UNKNOWN; - goto out; - } - - RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); - - if(pubkey != NULL) - { - if(!i2d_RSAPublicKey_bio(mem, rsa)) - { - r = SC_ERROR_UNKNOWN; - goto out; - } - - lg = BIO_get_mem_data(mem, &p); - - pubkey->algorithm = SC_ALGORITHM_RSA; - - r = sc_pkcs15_decode_pubkey(p15card->card->ctx, pubkey, p, lg); - if (r < 0) - goto out; - } - - (void) BIO_reset(mem); - - if(!i2d_RSAPrivateKey_bio(mem, rsa)) - { - r = SC_ERROR_UNKNOWN; - goto out; - } - - lg = BIO_get_mem_data(mem, &p); - - /* Get the private key file */ - r = sc_profile_get_file_by_path(profile, &key_info->path, &prkf); - if (r < 0) - { - char pbuf[SC_MAX_PATH_STRING_SIZE]; - - r = sc_path_print(pbuf, sizeof(pbuf), &key_info->path); - if (r != SC_SUCCESS) - pbuf[0] = '\0'; - - goto out; - } - - prkf->size = lg; - - r = sc_pkcs15init_create_file(profile, p15card, prkf); - if(r) goto out; - - r = sc_pkcs15init_update_file(profile, p15card, prkf, p, lg); - if(r) goto out; - -out: - if(mem) - BIO_free(mem); - if(bn) - BN_free(bn); - if(rsa) - RSA_free(rsa); - sc_file_free(prkf); - - return r; -#endif -} - -static int westcos_pkcs15init_finalize_card(sc_card_t *card) -{ - int r; - - /* be sure authentificate card */ - r = sc_card_ctl(card, SC_CARDCTL_WESTCOS_AUT_KEY, NULL); - if(r) return (r); - - return sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_USER); -} - -static struct sc_pkcs15init_operations sc_pkcs15init_westcos_operations = { - NULL, /* erase_card */ - westcos_pkcs15init_init_card, /* init_card */ - westcos_pkcs15init_create_dir, /* create_dir */ - NULL, /* create_domain */ - westcos_pkcs15_select_pin_reference, /* select_pin_reference */ - westcos_pkcs15_create_pin, /* create_pin */ - NULL, /* select_key_reference */ - westcos_pkcs15init_create_key, /* create_key */ - westcos_pkcs15init_store_key, /* store_key */ - westcos_pkcs15init_generate_key, /* generate_key */ - NULL, NULL, /* encode private/public key */ - westcos_pkcs15init_finalize_card, /* finalize_card */ - NULL, /* delete_object */ - NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ - NULL /* sanity_check */ -}; - -struct sc_pkcs15init_operations* sc_pkcs15init_get_westcos_ops(void) -{ - return &sc_pkcs15init_westcos_operations; -} diff --git a/src/pkcs15init/pkcs15.profile b/src/pkcs15init/pkcs15.profile index c98319fd63..b199438030 100644 --- a/src/pkcs15init/pkcs15.profile +++ b/src/pkcs15init/pkcs15.profile @@ -24,7 +24,7 @@ pkcs15 { do-last-update = yes; # Method to calculate ID of the crypto objects # native: 'E' + number_of_present_objects_of_the_same_type - # mozilla: SHA1(modulus) for RSA, SHA1(pub) for DSA + # mozilla: SHA1(modulus) for RSA # rfc2459: SHA1(SequenceASN1 of public key components as ASN1 integers) # default value: 'native' pkcs15-id-style = mozilla; diff --git a/src/pkcs15init/profile.c b/src/pkcs15init/profile.c index 2ae105b3ca..13258a4868 100644 --- a/src/pkcs15init/profile.c +++ b/src/pkcs15init/profile.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Random notes * - the "key" command should go away, it's obsolete @@ -43,18 +43,20 @@ #endif #include "common/compat_strlcpy.h" -#include "scconf/scconf.h" +#include "common/libscdl.h" #include "libopensc/log.h" #include "libopensc/pkcs15.h" #include "pkcs15-init.h" #include "profile.h" +#include "scconf/scconf.h" #define DEF_PRKEY_RSA_ACCESS 0x1D -#define DEF_PRKEY_DSA_ACCESS 0x12 #define DEF_PUBKEY_ACCESS 0x12 #define TEMPLATE_FILEID_MIN_DIFF 0x20 +#define WORD_SIZE 64 + /* #define DEBUG_PROFILE */ @@ -260,8 +262,9 @@ static struct auth_info * new_key(struct sc_profile *, unsigned int, unsigned int); static void set_pin_defaults(struct sc_profile *, struct pin_info *); -static void new_macro(sc_profile_t *, const char *, scconf_list *); +static int new_macro(sc_profile_t *, const char *, scconf_list *); static sc_macro_t * find_macro(sc_profile_t *, const char *); +static int is_macro_character(char c); static sc_file_t * init_file(unsigned int type) @@ -309,9 +312,8 @@ sc_profile_new(void) p15card->file_unusedspace = init_file(SC_FILE_TYPE_WORKING_EF); } - /* Assume card does RSA natively, but no DSA */ + /* Assume card does RSA natively */ pro->rsa_access_flags = DEF_PRKEY_RSA_ACCESS; - pro->dsa_access_flags = DEF_PRKEY_DSA_ACCESS; pro->pin_encoding = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; pro->pin_minlen = 4; pro->pin_maxlen = 8; @@ -342,11 +344,12 @@ sc_profile_load(struct sc_profile *profile, const char *filename) if (!profile_dir) { #ifdef _WIN32 - temp_len = PATH_MAX; - res = sc_ctx_win32_get_config_value(NULL, "ProfileDir", "Software\\OpenSC Project\\OpenSC", + temp_len = PATH_MAX - 1; + res = sc_ctx_win32_get_config_value(NULL, "ProfileDir", "Software\\" OPENSC_VS_FF_COMPANY_NAME "\\OpenSC" OPENSC_ARCH_SUFFIX, temp_path, &temp_len); if (res) LOG_FUNC_RETURN(ctx, res); + temp_path[temp_len] = '\0'; profile_dir = temp_path; #else profile_dir = SC_PKCS15_PROFILE_DIRECTORY; @@ -451,6 +454,8 @@ sc_profile_free(struct sc_profile *profile) if (profile->name) free(profile->name); + if (profile->driver) + free(profile->driver); free_file_list(&profile->ef_list); @@ -482,9 +487,16 @@ sc_profile_free(struct sc_profile *profile) free(pi); } + for (int i = 0; profile->options[i]; i++) { + free(profile->options[i]); + } + if (profile->p15_spec) sc_pkcs15_card_free(profile->p15_spec); - memset(profile, 0, sizeof(*profile)); + + if (profile->dll) + sc_dlclose(profile->dll); + free(profile); } @@ -574,7 +586,7 @@ sc_profile_get_file_instance(struct sc_profile *profile, const char *name, if ((fi = sc_profile_find_file(profile, NULL, name)) == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_FILE_NOT_FOUND); sc_file_dup(&file, fi->file); - sc_log(ctx, "ident '%s'; parent '%s'", fi->ident, fi->parent->ident); + sc_log(ctx, "ident '%s'; parent '%s'", fi->ident, fi->parent ? fi->parent->ident : "(null)"); if (file == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); sc_log(ctx, "file (type:%X, path:'%s')", file->type, sc_print_path(&file->path)); @@ -640,8 +652,7 @@ sc_profile_add_file(sc_profile_t *profile, const char *name, sc_file_t *file) LOG_FUNC_CALLED(ctx); if (!path.len) { parent = profile->df_info; - } - else { + } else { path.len -= 2; parent = sc_profile_find_file_by_path(profile, &path); } @@ -709,7 +720,8 @@ sc_profile_instantiate_template(sc_profile_t *profile, /* This loop relies on the fact that new files are always * appended to the list, after the parent files they refer to */ - assert(base_file->instance); + if (base_file->instance == NULL) + return SC_ERROR_INTERNAL; for (fi = tmpl->ef_list; fi; fi = fi->next) { struct file_info *parent, *instance; unsigned int skew = 0; @@ -831,6 +843,7 @@ init_state(struct state *cur_state, struct state *new_state) static int do_card_driver(struct state *cur, int argc, char **argv) { + free(cur->profile->driver); cur->profile->driver = strdup(argv[0]); return 0; } @@ -980,6 +993,10 @@ do_key_value(struct state *cur, int argc, char **argv) if (key[0] == '=') { ++key; key_len = strlen(key); + if (key_len > sizeof(keybuf)) { + parse_error(cur, "Key value too long (%zu > %zu)\n", key_len, sizeof(keybuf)); + return 1; + } memcpy(keybuf, key, key_len); } else { key_len = sizeof(keybuf); @@ -1059,19 +1076,34 @@ template_sanity_check(struct state *cur, struct sc_profile *templ) for (fi = templ->ef_list; fi; fi = fi->next) { struct sc_path fi_path = fi->file->path; - int fi_id = fi_path.value[fi_path.len - 2] * 0x100 - + fi_path.value[fi_path.len - 1]; + int fi_id; if (fi->file->type == SC_FILE_TYPE_BSO) continue; + + if (fi_path.len < 2) { + parse_error(cur, "Template insane: file-path length should not be less than 2 bytes"); + return 1; + } + + fi_id = fi_path.value[fi_path.len - 2] * 0x100 + + fi_path.value[fi_path.len - 1]; + for (ffi = templ->ef_list; ffi; ffi = ffi->next) { struct sc_path ffi_path = ffi->file->path; - int dlt, ffi_id = ffi_path.value[ffi_path.len - 2] * 0x100 - + ffi_path.value[ffi_path.len - 1]; + int dlt, ffi_id; if (ffi->file->type == SC_FILE_TYPE_BSO) continue; + if (ffi_path.len < 2) { + parse_error(cur, "Template insane: file-path length should not be less than 2 bytes"); + return 1; + } + + ffi_id = ffi_path.value[ffi_path.len - 2] * 0x100 + + ffi_path.value[ffi_path.len - 1]; + dlt = fi_id > ffi_id ? fi_id - ffi_id : ffi_id - fi_id; if (strcmp(ffi->ident, fi->ident)) { if (dlt >= TEMPLATE_FILEID_MIN_DIFF) @@ -1186,6 +1218,7 @@ free_file_list(struct file_info **list) if (fi->dont_free == 0) sc_file_free(fi->file); + free(fi->profile_extension); free(fi->ident); free(fi); } @@ -1202,6 +1235,7 @@ new_file(struct state *cur, const char *name, unsigned int type) struct file_info *info; sc_file_t *file; unsigned int df_type = 0, dont_free = 0; + int free_file = 0; if ((info = sc_profile_find_file(profile, NULL, name)) != NULL) return info; @@ -1210,32 +1244,48 @@ new_file(struct state *cur, const char *name, unsigned int type) * by the PKCS15 logic */ if (strncasecmp(name, "PKCS15-", 7)) { file = init_file(type); + free_file = 1; } else if (!strcasecmp(name+7, "TokenInfo")) { + if (!profile->p15_spec) { + parse_error(cur, "no pkcs15 spec in profile"); + return NULL; + } file = profile->p15_spec->file_tokeninfo; dont_free = 1; } else if (!strcasecmp(name+7, "ODF")) { + if (!profile->p15_spec) { + parse_error(cur, "no pkcs15 spec in profile"); + return NULL; + } file = profile->p15_spec->file_odf; dont_free = 1; } else if (!strcasecmp(name+7, "UnusedSpace")) { + if (!profile->p15_spec) { + parse_error(cur, "no pkcs15 spec in profile"); + return NULL; + } file = profile->p15_spec->file_unusedspace; dont_free = 1; } else if (!strcasecmp(name+7, "AppDF")) { file = init_file(SC_FILE_TYPE_DF); + free_file = 1; } else { - if (map_str2int(cur, name+7, &df_type, pkcs15DfNames)) + if (map_str2int(cur, name+7, &df_type, pkcs15DfNames) + || df_type >= SC_PKCS15_DF_TYPE_COUNT) return NULL; file = init_file(SC_FILE_TYPE_WORKING_EF); profile->df[df_type] = file; + free_file = 1; } - assert(file); + if (!file) + return NULL; if (file->type != type) { parse_error(cur, "inconsistent file type (should be %s)", file->type == SC_FILE_TYPE_DF ? "DF" : file->type == SC_FILE_TYPE_BSO ? "BS0" : "EF"); - if (strncasecmp(name, "PKCS15-", 7) || - !strcasecmp(name+7, "AppDF")) + if (free_file) sc_file_free(file); return NULL; } @@ -1254,6 +1304,11 @@ do_file_type(struct state *cur, int argc, char **argv) { unsigned int type; + if (!cur->file) { + parse_error(cur, "Invalid state\n"); + return 1; + } + if (map_str2int(cur, argv[0], &type, fileTypeNames)) return 1; cur->file->file->type = type; @@ -1263,8 +1318,15 @@ do_file_type(struct state *cur, int argc, char **argv) static int do_file_path(struct state *cur, int argc, char **argv) { - struct sc_file *file = cur->file->file; - struct sc_path *path = &file->path; + struct sc_file *file = NULL; + struct sc_path *path = NULL; + + if (!cur->file) { + parse_error(cur, "Invalid state\n"); + return 1; + } + file = cur->file->file; + path = &file->path; /* sc_format_path doesn't return an error indication * when it's unable to parse the path */ @@ -1281,8 +1343,15 @@ static int do_fileid(struct state *cur, int argc, char **argv) { struct file_info *fi; - struct sc_file *df, *file = cur->file->file; - struct sc_path temp, *path = &file->path; + struct sc_file *df, *file = NULL; + struct sc_path temp, *path = NULL; + + if (!cur->file) { + parse_error(cur, "Invalid state\n"); + return 1; + } + file = cur->file->file; + path = &file->path; /* sc_format_path doesn't return an error indication * when it's unable to parse the path */ @@ -1304,6 +1373,10 @@ do_fileid(struct state *cur, int argc, char **argv) } *path = df->path; } + if (path->len + 2 > sizeof(path->value)) { + parse_error(cur, "File path too long\n"); + return 1; + } memcpy(path->value + path->len, temp.value, 2); path->len += 2; @@ -1316,6 +1389,11 @@ do_structure(struct state *cur, int argc, char **argv) { unsigned int ef_structure; + if (!cur->file) { + parse_error(cur, "Invalid state\n"); + return 1; + } + if (map_str2int(cur, argv[0], &ef_structure, fileStructureNames)) return 1; cur->file->file->ef_structure = ef_structure; @@ -1327,6 +1405,11 @@ do_size(struct state *cur, int argc, char **argv) { unsigned int size; + if (!cur->file) { + parse_error(cur, "Invalid state\n"); + return 1; + } + if (get_uint_eval(cur, argc, argv, &size)) return 1; cur->file->file->size = size; @@ -1338,6 +1421,11 @@ do_reclength(struct state *cur, int argc, char **argv) { unsigned int reclength; + if (!cur->file) { + parse_error(cur, "Invalid state\n"); + return 1; + } + if (get_uint(cur, argv[0], &reclength)) return 1; cur->file->file->record_length = reclength; @@ -1347,10 +1435,18 @@ do_reclength(struct state *cur, int argc, char **argv) static int do_content(struct state *cur, int argc, char **argv) { - struct sc_file *file = cur->file->file; + struct sc_file *file = NULL; size_t len = (strlen(argv[0]) + 1) / 2; int rv = 0; + if (!cur->file) { + parse_error(cur, "Invalid state\n"); + return 1; + } + file = cur->file->file; + + free(file->encoded_content); + file->encoded_content = malloc(len); if (!file->encoded_content) return 1; @@ -1362,10 +1458,17 @@ do_content(struct state *cur, int argc, char **argv) static int do_prop_attr(struct state *cur, int argc, char **argv) { - struct sc_file *file = cur->file->file; + struct sc_file *file = NULL; size_t len = (strlen(argv[0]) + 1) / 2; int rv = 0; + if (!cur->file) { + parse_error(cur, "Invalid state\n"); + return 1; + } + file = cur->file->file; + + free(file->prop_attr); file->prop_attr = malloc(len); if (!file->prop_attr) return 1; @@ -1377,11 +1480,17 @@ do_prop_attr(struct state *cur, int argc, char **argv) static int do_aid(struct state *cur, int argc, char **argv) { - struct sc_file *file = cur->file->file; + struct sc_file *file = NULL; const char *name = argv[0]; - unsigned int len; + size_t len; int res = 0; + if (!cur->file) { + parse_error(cur, "Invalid state\n"); + return 1; + } + file = cur->file->file; + if (*name == '=') { len = strlen(++name); if (len > sizeof(file->name)) { @@ -1401,11 +1510,17 @@ do_aid(struct state *cur, int argc, char **argv) static int do_exclusive_aid(struct state *cur, int argc, char **argv) { - struct sc_file *file = cur->file->file; + struct sc_file *file = NULL; const char *name = argv[0]; - unsigned int len; + size_t len; int res = 0; + if (!cur->file) { + parse_error(cur, "Invalid state\n"); + return 1; + } + file = cur->file->file; + #ifdef DEBUG_PROFILE printf("do_exclusive_aid(): exclusive-aid '%s'\n", name); printf("do_exclusive_aid(): current file '%s' (path:%s)\n", cur->file->ident, sc_print_path(&file->path)); @@ -1444,6 +1559,10 @@ do_exclusive_aid(struct state *cur, int argc, char **argv) static int do_profile_extension(struct state *cur, int argc, char **argv) { + if (!cur->file) { + parse_error(cur, "Invalid state\n"); + return 1; + } return setstr(&cur->file->profile_extension, argv[0]); } @@ -1458,14 +1577,21 @@ do_profile_extension(struct state *cur, int argc, char **argv) static int do_acl(struct state *cur, int argc, char **argv) { - struct sc_file *file = cur->file->file; + struct sc_file *file = NULL; char oper[64], *what = NULL; - memset(oper, 0, sizeof(oper)); + + if (!cur->file) + goto bad; + file = cur->file->file; + while (argc--) { unsigned int op, method, id; + if (strlen(*argv) >= sizeof(oper)) + goto bad; strlcpy(oper, *argv++, sizeof(oper)); + if ((what = strchr(oper, '=')) == NULL) goto bad; *what++ = '\0'; @@ -1489,7 +1615,8 @@ do_acl(struct state *cur, int argc, char **argv) if (map_str2int(cur, oper, &op, fileOpNames)) goto bad; - acl = sc_file_get_acl_entry(file, op); + if (!(acl = sc_file_get_acl_entry(file, op))) + goto bad; if (acl->method == SC_AC_NEVER || acl->method == SC_AC_NONE || acl->method == SC_AC_UNKNOWN) @@ -1583,6 +1710,7 @@ static void set_pin_defaults(struct sc_profile *profile, struct pin_info *pi) static int do_pin_file(struct state *cur, int argc, char **argv) { + free(cur->pin->file_name); cur->pin->file_name = strdup(argv[0]); return 0; } @@ -1692,7 +1820,7 @@ do_pin_storedlength(struct state *cur, int argc, char **argv) static int do_pin_flags(struct state *cur, int argc, char **argv) { - unsigned int flags; + unsigned int flags = 0; int i, r; if (cur->pin->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) @@ -1714,35 +1842,55 @@ process_macros(struct state *cur, struct block *info, { scconf_item *item; const char *name; + int r; for (item = blk->items; item; item = item->next) { + char *s = item->key; name = item->key; - if (item->type != SCCONF_ITEM_TYPE_VALUE) + if (item->type != SCCONF_ITEM_TYPE_VALUE || !name) continue; + + /* make sure the macro name consist only of allowed characters. + * This is not guaranteed by the tokenizer */ + while (is_macro_character(*s)) { + s++; + } + if (*s != '\0') { +#ifdef DEBUG_PROFILE + printf("Invalid macro name %s\n", name); +#endif + return SC_ERROR_SYNTAX_ERROR; + } #ifdef DEBUG_PROFILE printf("Defining %s\n", name); #endif - new_macro(cur->profile, name, item->value.list); + r = new_macro(cur->profile, name, item->value.list); + if (r != SC_SUCCESS) + return r; } - return 0; + return SC_SUCCESS; } -static void +static int new_macro(sc_profile_t *profile, const char *name, scconf_list *value) { sc_macro_t *mac; + if (!profile || !name || !value) + return SC_ERROR_INVALID_ARGUMENTS; + if ((mac = find_macro(profile, name)) == NULL) { mac = calloc(1, sizeof(*mac)); if (mac == NULL) - return; + return SC_ERROR_OUT_OF_MEMORY; mac->name = strdup(name); mac->next = profile->macro_list; profile->macro_list = mac; } mac->value = value; + return SC_SUCCESS; } static sc_macro_t * @@ -1863,13 +2011,78 @@ static struct block root_ops = { "root", process_block, NULL, root_blocks }; +static int +is_macro_character(char c) +{ + if (isalnum(c) || c == '-' || c == '_') + return 1; + return 0; +} + +static int +get_inner_word(const char *str, char word[WORD_SIZE]) +{ + const char *inner = NULL; + size_t len = 0; + + inner = str; + + while (is_macro_character(*inner)) { + inner++; + len++; + } + if (len >= WORD_SIZE) + return 1; + memcpy(word, str, len); + word[len] = '\0'; + return 0; +} + +/* + * Checks for a reference loop for macro named start_name in macro definitions. + * Function returns 1 if a reference loop is detected, 0 otherwise. + */ +static int +check_macro_reference_loop(const char *start_name, sc_macro_t *macro, sc_profile_t *profile, int depth) +{ + scconf_list *value; + const char *name = NULL; + sc_macro_t *m; + char word[WORD_SIZE]; + + if (!start_name || !macro || !profile || depth == 16) + return 1; + + /* For some reason, the macro value is a list where we need to check for references */ + for (value = macro->value; value != NULL; value = value->next) { + /* Find name in macro value */ + char *macro_value = value->data; + if (!(name = strchr(macro_value, '$'))) + continue; + /* Extract the macro name from the string */ + if (get_inner_word(name + 1, word)) + return 1; + /* Find whether name corresponds to some other macro */ + if (!(m = find_macro(profile, word))) + continue; + /* Check for loop */ + if (!strcmp(m->name, start_name)) + return 1; + /* Reference loop was found to the original macro name */ + if (check_macro_reference_loop(start_name, m, profile, depth + 1) == 1) { + return 1; + } + } + return 0; +} + static int build_argv(struct state *cur, const char *cmdname, scconf_list *list, char **argv, unsigned int max) { unsigned int argc; const char *str; - sc_macro_t *mac; + sc_macro_t *macro; int r; for (argc = 0; list; list = list->next) { @@ -1880,28 +2093,48 @@ build_argv(struct state *cur, const char *cmdname, str = list->data; if (str[0] != '$') { + /* When str contains macro inside, macro reference loop needs to be checked */ + const char *macro_name = NULL; + if ((macro_name = strchr(str, '$'))) { + /* Macro does not have to start at the first position */ + char word[WORD_SIZE]; + if (get_inner_word(macro_name + 1, word) != 0) { + return SC_ERROR_SYNTAX_ERROR; + } + if ((macro = find_macro(cur->profile, word)) + && check_macro_reference_loop(macro->name, macro, cur->profile, 0)) { + return SC_ERROR_SYNTAX_ERROR; + } + } + argv[argc++] = list->data; continue; } /* Expand macro reference */ - if (!(mac = find_macro(cur->profile, str + 1))) { + if (!(macro = find_macro(cur->profile, str + 1))) { parse_error(cur, "%s: unknown macro \"%s\"", cmdname, str); return SC_ERROR_SYNTAX_ERROR; } + if (list == macro->value) { + return SC_ERROR_SYNTAX_ERROR; + } + if (check_macro_reference_loop(macro->name, macro, cur->profile, 0)) { + return SC_ERROR_SYNTAX_ERROR; + } #ifdef DEBUG_PROFILE { scconf_list *list; - printf("Expanding macro %s:", mac->name); - for (list = mac->value; list; list = list->next) + printf("Expanding macro %s:", macro->name); + for (list = macro->value; list; list = list->next) printf(" %s", list->data); printf("\n"); } #endif - r = build_argv(cur, cmdname, mac->value, + r = build_argv(cur, cmdname, macro->value, argv + argc, max - argc); if (r < 0) return r; @@ -1970,6 +2203,10 @@ process_block(struct state *cur, struct block *info, cmd = item->key; if (item->type == SCCONF_ITEM_TYPE_COMMENT) continue; + if (!cmd) { + parse_error(cur, "Command can not be processed."); + return SC_ERROR_SYNTAX_ERROR; + } if (item->type == SCCONF_ITEM_TYPE_BLOCK) { scconf_list *nlist; @@ -2023,13 +2260,15 @@ sc_profile_find_file(struct sc_profile *pro, const sc_path_t *path, const char *name) { struct file_info *fi; - unsigned int len; + size_t len; + const u8 *value; - len = path? path->len : 0; + value = path ? path->value : (const u8*) ""; + len = path ? path->len : 0; for (fi = pro->ef_list; fi; fi = fi->next) { sc_path_t *fpath = &fi->file->path; - if (!strcasecmp(fi->ident, name) && fpath->len >= len && !memcmp(fpath->value, path->value, len)) + if (!strcasecmp(fi->ident, name) && fpath->len >= len && !memcmp(fpath->value, value, len)) return fi; } return NULL; @@ -2123,11 +2362,14 @@ get_authid(struct state *cur, const char *value, char temp[16]; size_t n; - if (isdigit((int) *value)) { + if (isdigit((unsigned char) *value)) { *num = 0; return get_uint(cur, value, type); } + if (strlen(value) >= sizeof(temp)) + return 1; + n = strcspn(value, "0123456789x"); strlcpy(temp, value, (sizeof(temp) > n) ? n + 1 : sizeof(temp)); @@ -2143,17 +2385,23 @@ static int get_uint(struct state *cur, const char *value, unsigned int *vp) { char *ep; + unsigned long tmp; if (strstr(value, "0x") == value) - *vp = strtoul(value + 2, &ep, 16); + tmp = strtoul(value + 2, &ep, 16); else if (strstr(value, "x") == value) - *vp = strtoul(value + 1, &ep, 16); + tmp = strtoul(value + 1, &ep, 16); else - *vp = strtoul(value, &ep, 0); + tmp = strtoul(value, &ep, 0); if (*ep != '\0') { parse_error(cur, "invalid integer argument \"%s\"\n", value); return 1; } + if (tmp > INT_MAX) { + parse_error(cur, "the number \"%s\" is too large\n", value); + return 1; + } + *vp = (int)tmp; return 0; } @@ -2183,7 +2431,7 @@ map_str2int(struct state *cur, const char *value, unsigned int n; const char *what; - if (isdigit((int) *value)) + if (isdigit((unsigned char) *value)) return get_uint(cur, value, vp); for (n = 0; map[n].name; n++) { if (!strcasecmp(value, map[n].name)) { @@ -2224,7 +2472,7 @@ struct num_exp_ctx { jmp_buf error; int j; - char word[64]; + char word[WORD_SIZE]; char * unget; char * str; @@ -2232,7 +2480,7 @@ struct num_exp_ctx { char ** argv; }; -static void expr_eval(struct num_exp_ctx *, unsigned int *, unsigned int); +static void expr_eval(struct num_exp_ctx *, unsigned int *, unsigned int, int); static void expr_fail(struct num_exp_ctx *ctx) @@ -2259,8 +2507,9 @@ __expr_get(struct num_exp_ctx *ctx, int eof_okay) } ctx->j = 0; + s = ctx->str; do { - if ((s = ctx->str) == NULL || *s == '\0') { + if (s == NULL || *s == '\0') { if (ctx->argc == 0) { if (eof_okay) return NULL; @@ -2270,17 +2519,17 @@ __expr_get(struct num_exp_ctx *ctx, int eof_okay) ctx->argc--; } - while (isspace(*s)) + while (isspace((unsigned char)*s)) s++; } while (*s == '\0'); - if (isdigit(*s)) { - while (isdigit(*s)) + if (isdigit((unsigned char)*s)) { + while (isdigit((unsigned char)*s)) expr_put(ctx, *s++); } else if (*s == '$') { expr_put(ctx, *s++); - while (isalnum(*s) || *s == '-' || *s == '_') + while (is_macro_character(*s)) expr_put(ctx, *s++); } else if (strchr("*/+-()|&", *s)) { @@ -2319,22 +2568,31 @@ expr_expect(struct num_exp_ctx *ctx, int c) expr_fail(ctx); } +#define MAX_BRACKETS 32 static void -expr_term(struct num_exp_ctx *ctx, unsigned int *vp) +expr_term(struct num_exp_ctx *ctx, unsigned int *vp, int opening_brackets) { char *tok; tok = expr_get(ctx); if (*tok == '(') { - expr_eval(ctx, vp, 1); + if (opening_brackets + 1 > MAX_BRACKETS) { + parse_error(ctx->state, "Too many \"%s\" in expression", tok); + expr_fail(ctx); + } + expr_eval(ctx, vp, 1, opening_brackets + 1); expr_expect(ctx, ')'); } - else if (isdigit(*tok)) { + else if (isdigit((unsigned char)*tok)) { char *ep; + unsigned long tmp; - *vp = strtoul(tok, &ep, 0); + tmp = strtoul(tok, &ep, 0); if (*ep) expr_fail(ctx); + if (tmp > UINT_MAX) + expr_fail(ctx); + *vp = (unsigned int)tmp; } else if (*tok == '$') { sc_macro_t *mac; @@ -2354,12 +2612,12 @@ expr_term(struct num_exp_ctx *ctx, unsigned int *vp) } static void -expr_eval(struct num_exp_ctx *ctx, unsigned int *vp, unsigned int pri) +expr_eval(struct num_exp_ctx *ctx, unsigned int *vp, unsigned int pri, int opening_brackets) { unsigned int left, right, new_pri; char *tok, op; - expr_term(ctx, &left); + expr_term(ctx, &left, opening_brackets); while (1) { tok = __expr_get(ctx, 1); @@ -2396,10 +2654,13 @@ expr_eval(struct num_exp_ctx *ctx, unsigned int *vp, unsigned int pri) } pri = new_pri; - expr_eval(ctx, &right, new_pri + 1); + expr_eval(ctx, &right, new_pri + 1, opening_brackets); switch (op) { case '*': left *= right; break; - case '/': left /= right; break; + case '/': + if (right == 0) + expr_fail(ctx); + left /= right; break; case '+': left += right; break; case '-': left -= right; break; case '&': left &= right; break; @@ -2426,7 +2687,7 @@ get_uint_eval(struct state *cur, int argc, char **argv, unsigned int *vp) return SC_ERROR_SYNTAX_ERROR; } - expr_eval(&ctx, vp, 0); + expr_eval(&ctx, vp, 0, 0); if (ctx.str[0] || ctx.argc) expr_fail(&ctx); diff --git a/src/pkcs15init/profile.h b/src/pkcs15init/profile.h index f9f1077625..2d27b161c0 100644 --- a/src/pkcs15init/profile.h +++ b/src/pkcs15init/profile.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_PROFILE_H @@ -120,7 +120,6 @@ struct sc_profile { unsigned int pin_attempts; unsigned int puk_attempts; unsigned int rsa_access_flags; - unsigned int dsa_access_flags; struct { unsigned int direct_certificates; diff --git a/src/pkcs15init/rutoken_ecp.profile b/src/pkcs15init/rutoken_ecp.profile index 3ff4d3fa97..f74726ece9 100644 --- a/src/pkcs15init/rutoken_ecp.profile +++ b/src/pkcs15init/rutoken_ecp.profile @@ -48,7 +48,7 @@ PIN user-pin { auth-id = 2; reference = 2; attempts = 5; - min-length = 4; + min-length = 6; max-length = 32; flags = case-sensitive, initialized; } @@ -110,6 +110,14 @@ filesystem { DF Resrv1-DF { file-id = 1001; + + DF Resrv5-DF { + file-id = 8001; + } + + DF Resrv6-DF { + file-id = 8002; + } } DF Resrv2-DF { file-id = 1002; diff --git a/src/pkcs15init/rutoken_lite.profile b/src/pkcs15init/rutoken_lite.profile new file mode 100644 index 0000000000..d6be963d35 --- /dev/null +++ b/src/pkcs15init/rutoken_lite.profile @@ -0,0 +1,210 @@ +# +# PKCS15 profile, generic information. +# This profile is loaded before any card specific profile. +# + +cardinfo { + label = "Rutoken Lite"; + manufacturer = "Aktiv Co."; + + max-pin-length = 32; + min-pin-length = 1; + pin-encoding = ascii-numeric; +} + +# +# The following controls some aspects of the PKCS15 we put onto +# the card. +# +pkcs15 { + # Put certificates into the CDF itself? + direct-certificates = no; + # Put the DF length into the ODF file? + encode-df-length = no; + # Have a lastUpdate field in the EF(TokenInfo)? + do-last-update = yes; + + pkcs15-id-style = mozilla; +} + +# Default settings. +# This option block will always be processed. +option default { + macros { + ti-size = 128; + odf-size = 128; + aodf-size = 256; + dodf-size = 2048; + cdf-size = 2048; + prkdf-size = 2048; + pukdf-size = 2048; + } +} + +# Define reasonable limits for PINs and PUK +# Note that we do not set a file path or reference +# for the user pin; that is done dynamically. +PIN user-pin { + auth-id = 2; + reference = 2; + attempts = 5; + min-length = 4; + max-length = 32; + flags = case-sensitive, initialized; +} +PIN user-puk { + min-length = 0; + max-length = 0; +} + +PIN so-pin { + auth-id = 1; + reference = 1; + attempts = 10; + min-length = 8; + max-length = 32; + flags = case-sensitive, initialized, soPin; +} +PIN so-puk { + min-length = 0; + max-length = 0; +} + +filesystem { + EF CHV2 { + file-id = 0002; + ACL = *=NEVER, UPDATE=$SOPIN, PIN-RESET=$SOPIN; + } + + DF MF { + path = 3F00; + type = DF; + acl = *=NEVER, SELECT=NONE, DELETE=NEVER, CREATE=CHV2, READ=NONE; + + DF Sys-DF { + file-id = 1000; + + DF SysKey-DF { + file-id = 1000; + } + + DF Resrv1-DF { + file-id = 1001; + + DF Resrv5-DF { + file-id = 8001; + } + + DF Resrv6-DF { + file-id = 8002; + } + } + DF Resrv2-DF { + file-id = 1002; + } + DF Resrv3-DF { + file-id = 1003; + } + DF Resrv4-DF { + file-id = 1004; + } + } + + EF DIR { + type = EF; + file-id = 2F00; + size = 128; + acl = *=NEVER, READ=NONE, UPDATE=CHV1, WRITE=CHV1, DELETE=CHV1; + } + + # Here comes the application DF + DF PKCS15-AppDF { + type = DF; + file-id = 5000; + acl = *=NONE, DELETE=CHV2; + + EF PKCS15-ODF { + file-id = 5031; + size = $odf-size; + acl = *=NONE, DELETE=$SOPIN; + } + + EF PKCS15-TokenInfo { + file-id = 5032; + size = $ti-size; + acl = *=NONE, DELETE=CHV2; + } + + EF PKCS15-AODF { + file-id = 6005; + size = $aodf-size; + acl = *=NEVER, READ=NONE, UPDATE=$SOPIN, WRITE=$SOPIN, DELETE=$SOPIN; + } + + EF PKCS15-PrKDF { + file-id = 6002; + size = $prkdf-size; + acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; + } + + EF PKCS15-PuKDF { + file-id = 6001; + size = $pukdf-size; + acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; + } + + EF PKCS15-CDF { + file-id = 6004; + size = $cdf-size; + acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; + } + + EF PKCS15-DODF { + file-id = 6006; + size = $dodf-size; + acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; + } + + # This template defines files for keys, certificates etc. + # + # When instantiating the template, each file id will be + # combined with the last octet of the object's pkcs15 id + # to form a unique file ID. + template key-domain { + EF private-key { + file-id = 0100; + structure = transparent; + acl = *=NEVER, READ=$PIN, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; + } + + EF public-key { + file-id = 0200; + structure = transparent; + acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; + } + + # Certificate template + EF certificate { + file-id = 0300; + structure = transparent; + acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; + } + + # data objects are stored in transparent EFs. + EF data { + file-id = 0400; + structure = transparent; + acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; + } + + # private data objects are stored in transparent EFs. + EF privdata { + file-id = 0500; + structure = transparent; + acl = *=NEVER, READ=$PIN, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; + } + } + } + } +} + diff --git a/src/pkcs15init/setcos.profile b/src/pkcs15init/setcos.profile index cd58b73aa0..f566b0e312 100644 --- a/src/pkcs15init/setcos.profile +++ b/src/pkcs15init/setcos.profile @@ -7,7 +7,7 @@ cardinfo { pin-pad-char = 0x00; } -# Addtional default settings +# Additional default settings option default { macros { protected = *=$SOPIN, READ=NONE; @@ -20,7 +20,7 @@ option default { } } -# Addtional onepin option settings +# Additional onepin option settings option onepin { macros { protected = *=$PIN, READ=NONE; diff --git a/src/pkcs15init/starcos.profile b/src/pkcs15init/starcos.profile index af4d6174b7..90505ca933 100644 --- a/src/pkcs15init/starcos.profile +++ b/src/pkcs15init/starcos.profile @@ -10,7 +10,7 @@ cardinfo { option default { macros { so-pin-flags = initialized, needs-padding, soPin; - isf_acl = WRITE=$SOPIN; + isf_acl = WRITE=$SOPIN, CREATE=$SOPIN; df_acl = *=$SOPIN; } } @@ -18,7 +18,7 @@ option default { option onepin { macros { so-pin-flags = initialized, needs-padding; - isf_acl = WRITE=$PIN; + isf_acl = WRITE=$PIN, CREATE=$PIN; df_acl = *=$PIN; } } diff --git a/src/pkcs15init/westcos.profile b/src/pkcs15init/westcos.profile deleted file mode 100644 index d2b33ae886..0000000000 --- a/src/pkcs15init/westcos.profile +++ /dev/null @@ -1,191 +0,0 @@ - -cardinfo { - label = "westcos"; - manufacturer = "CEV"; - - max-pin-length = 8; - min-pin-length = 4; - pin-encoding = BCD; - pin-pad-char = 0xff; - -} - -# Default settings. -# This option block will always be processed. -option default { - macros { - protected = *=$PIN, READ=NONE; - unprotected = *=NONE; - private = *=$PIN; - so-pin-flags = local, initialized, needs-padding; #, soPin; - so-min-pin-length = 6; - so-pin-attempts = 2; - so-auth-id = 1; #FF; - so-puk-attempts = 4; - so-min-puk-length = 6; - unusedspace-size = 128; - odf-size = 256; - aodf-size = 256; - cdf-size = 512; - prkdf-size = 256; - pukdf-size = 256; - dodf-size = 256; - } -} - -PIN so-pin { - auth-id = 1; - reference = 1; - attempts = 3; - min-length = 4; - max-length = 8; - flags = local, initialized, needs-padding; -} -PIN so-puk { - auth-id = 2; - reference = 2; - attempts = 10; - min-length = 4; - max-length = 8; - flags = local, initialized, needs-padding; -} -PIN user-pin { - auth-id = 1; - reference = 1; - attempts = 3; - min-length = 4; - max-length = 8; - flags = local, initialized, needs-padding; -} -PIN user-puk { - auth-id = 2; - reference = 2; - attempts = 10; - min-length = 4; - max-length = 8; - flags = local, initialized, needs-padding; -} - -filesystem { - DF MF { - path = 3F00; - type = DF; - - # This is the DIR file - EF DIR { - type = EF; - file-id = 2F00; - size = 128; - acl = $unprotected; - } - - # Here comes the application DF - DF PKCS15-AppDF { - type = DF; - file-id = 5015; - aid = A0:00:00:00:63:50:4B:43:53:2D:31:35; - acl = $unprotected; - size = 5000; - - EF PINFILE { - file-id = AAAA; - type = INTERNAL-EF; - structure = TRANSPARENT; - size = 100; - acl = *=NEVER; - } - - EF PKCS15-ODF { - file-id = 5031; - size = $odf-size; - acl = $unprotected; - } - - EF PKCS15-TokenInfo { - file-id = 5032; - acl = $unprotected; - } - - EF PKCS15-UnusedSpace { - file-id = 5033; - size = $unusedspace-size; - acl = $unprotected; - } - - EF PKCS15-AODF { - file-id = 4401; - size = $aodf-size; - acl = $protected; - } - - EF PKCS15-PrKDF { - file-id = 4402; - size = $prkdf-size; - acl = $protected; - } - - EF PKCS15-PuKDF { - file-id = 4403; - size = $pukdf-size; - acl = $protected; - } - - EF PKCS15-CDF { - file-id = 4404; - size = $cdf-size; - acl = $protected; - } - - EF PKCS15-DODF { - file-id = 4405; - size = $dodf-size; - ACL = $protected; - } - - - # This template defines files for keys, certificates etc. - # - # When instantiating the template, each file id will be - # combined with the last octet of the object's pkcs15 id - # to form a unique file ID. - template key-domain { - EF private-key { - file-id = 0100; - structure = transparent; - acl = *=NEVER, READ=$PIN, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; - } - - EF public-key { - file-id = 0200; - structure = transparent; - acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; - } - - # Certificate template - EF certificate { - file-id = 0300; - structure = transparent; - acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; - } - - # data objects are stored in transparent EFs. - EF data { - file-id = 0400; - structure = transparent; - acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; - } - - # private data objects are stored in transparent EFs. - EF privdata { - file-id = 0500; - structure = transparent; - acl = *=NEVER, READ=$PIN, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; - } - } - - } - } -} - - - diff --git a/src/scconf/Makefile.am b/src/scconf/Makefile.am index af5927dd03..7837945208 100644 --- a/src/scconf/Makefile.am +++ b/src/scconf/Makefile.am @@ -11,3 +11,4 @@ noinst_LTLIBRARIES = libscconf.la AM_CPPFLAGS = -I$(top_srcdir)/src libscconf_la_SOURCES = scconf.c parse.c write.c sclex.c +libscconf_la_LIBADD = $(top_builddir)/src/common/libcompat.la diff --git a/src/scconf/README.scconf b/src/scconf/README.scconf index f30e382304..1653789430 100644 --- a/src/scconf/README.scconf +++ b/src/scconf/README.scconf @@ -18,7 +18,7 @@ It isn't It doesn't have - anything else but data. No locking, no threads etc. -It has heirarchical data blocks, it has lists. +It has hierarchical data blocks, it has lists. Similar, but different: - .ini files. scconf is block structured, has lists and arrays @@ -132,9 +132,9 @@ const scconf_block *scconf_find_block(const scconf_context * config, const scconf_block * block, const char *item_name); This finds a block in the given context. This function doesn't descend -the heirarchy, it only finds blocks in the top level of either +the hierarchy, it only finds blocks in the top level of either the context (the root block) or of the block given in the block -paramter (if not NULL). +parameter (if not NULL). The block pointer returned points to data held by the context, hence the const qualifier. diff --git a/src/scconf/internal.h b/src/scconf/internal.h index e7db8e7cbe..97d1ddf0b2 100644 --- a/src/scconf/internal.h +++ b/src/scconf/internal.h @@ -16,12 +16,14 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SCCONF_INTERNAL_H #define _SCCONF_INTERNAL_H +#include "scconf.h" + #ifdef __cplusplus extern "C" { #endif @@ -31,6 +33,8 @@ extern "C" { #define TOKEN_TYPE_STRING 2 #define TOKEN_TYPE_PUNCT 3 +#define DEPTH_LIMIT 16 + typedef struct _scconf_parser { scconf_context *config; @@ -47,11 +51,13 @@ typedef struct _scconf_parser { unsigned int error:1; unsigned int warnings:1; char emesg[256]; + size_t nested_blocks; } scconf_parser; extern int scconf_lex_parse(scconf_parser * parser, const char *filename); extern int scconf_lex_parse_string(scconf_parser * parser, const char *config_string); +extern void scconf_skip_block(scconf_parser * parser); extern void scconf_parse_token(scconf_parser * parser, int token_type, const char *token); #ifdef __cplusplus diff --git a/src/scconf/lex-parse.l b/src/scconf/lex-parse.l index 39b5ffe959..81f2d63529 100644 --- a/src/scconf/lex-parse.l +++ b/src/scconf/lex-parse.l @@ -17,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H diff --git a/src/scconf/parse.c b/src/scconf/parse.c index c8bf141961..343e9af973 100644 --- a/src/scconf/parse.c +++ b/src/scconf/parse.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -30,8 +30,8 @@ #include #include "common/compat_strlcpy.h" -#include "scconf.h" #include "internal.h" +#include "scconf.h" #define STATE_NAME 0x01 #define STATE_VALUE 0x02 @@ -99,9 +99,7 @@ static scconf_item *scconf_item_add_internal(scconf_parser * parser, int type) /* if item with same key already exists, use it */ item = scconf_item_find(parser); if (item) { - if (parser->key) { - free(parser->key); - } + free(parser->key); parser->key = NULL; parser->current_item = item; return item; @@ -147,8 +145,7 @@ scconf_item *scconf_item_add(scconf_context * config, scconf_block * block, scco scconf_block_copy((const scconf_block *) data, &dst); scconf_list_copy(dst->name, &parser.name); } - scconf_item_add_internal(&parser, type); - if (parser.current_item) { + if (scconf_item_add_internal(&parser, type)) { switch (parser.current_item->type) { case SCCONF_ITEM_TYPE_COMMENT: parser.current_item->value.comment = strdup((const char *) data); @@ -166,6 +163,8 @@ scconf_item *scconf_item_add(scconf_context * config, scconf_block * block, scco } } else { /* FIXME is it an error if item is NULL? */ + free(parser.key); + parser.key = NULL; } return parser.current_item; } @@ -242,10 +241,16 @@ static void scconf_parse_reset_state(scconf_parser * parser) } } +void scconf_skip_block(scconf_parser * parser) +{ + scconf_parse_error(parser, "too many nested blocks"); + scconf_parse_reset_state(parser); +} + void scconf_parse_token(scconf_parser * parser, int token_type, const char *token) { scconf_item *item; - int len; + size_t len; if (parser->error) { /* fatal error */ @@ -299,7 +304,7 @@ void scconf_parse_token(scconf_parser * parser, int token_type, const char *toke /* name */ parser->state |= STATE_SET; scconf_list_add(&parser->name, stoken); - } else if (parser->state == STATE_VALUE) { + } else if (parser->state == STATE_VALUE && parser->current_item->type == SCCONF_ITEM_TYPE_VALUE) { /* value */ parser->state |= STATE_SET; scconf_list_add(&parser->current_item->value.list, @@ -321,10 +326,12 @@ void scconf_parse_token(scconf_parser * parser, int token_type, const char *toke scconf_parse_error_not_expect(parser, "{"); break; } + parser->nested_blocks++; scconf_block_add_internal(parser); scconf_parse_reset_state(parser); break; case '}': + parser->nested_blocks--; if (parser->state != 0) { if ((parser->state & STATE_VALUE) == 0 || (parser->state & STATE_SET) == 0) { @@ -382,6 +389,7 @@ int scconf_parse(scconf_context * config) p.config = config; p.block = config->root; p.line = 1; + p.nested_blocks = 0; if (!scconf_lex_parse(&p, config->filename)) { snprintf(buffer, sizeof(buffer), @@ -410,6 +418,7 @@ int scconf_parse_string(scconf_context * config, const char *string) p.config = config; p.block = config->root; p.line = 1; + p.nested_blocks = 0; if (!scconf_lex_parse_string(&p, string)) { snprintf(buffer, sizeof(buffer), @@ -422,6 +431,8 @@ int scconf_parse_string(scconf_context * config, const char *string) r = 1; } + scconf_parse_reset_state(&p); + if (r <= 0) config->errmsg = buffer; return r; diff --git a/src/scconf/scconf.c b/src/scconf/scconf.c index 0849560d2a..ca13c3c9f4 100644 --- a/src/scconf/scconf.c +++ b/src/scconf/scconf.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -28,7 +28,9 @@ #include #endif #include +#include +#include "common/compat_strlcat.h" #include "scconf.h" scconf_context *scconf_new(const char *filename) @@ -147,7 +149,7 @@ const char *scconf_get_str(const scconf_block * block, const char *option, const if (!list) return def; - /* ignore non 'auto-configurated' values */ + /* ignore non 'auto-configured' values */ if (*list->data == '@' && *(list->data + strlen(list->data) - 1) == '@') return def; @@ -157,9 +159,17 @@ const char *scconf_get_str(const scconf_block * block, const char *option, const int scconf_get_int(const scconf_block * block, const char *option, int def) { const scconf_list *list; + long res; list = scconf_find_list(block, option); - return !list ? def : strtol(list->data, NULL, 0); + if (!list) { + return def; + } + res = strtol(list->data, NULL, 0); + if (res <= INT_MAX) { + return (int)res; + } + return def; } int scconf_get_bool(const scconf_block * block, const char *option, int def) @@ -415,9 +425,9 @@ char *scconf_list_strdup(const scconf_list * list, const char *filler) return NULL; } while (list && list->data) { - strcat(buf, list->data); + strlcat(buf, list->data, len); if (filler) { - strcat(buf, filler); + strlcat(buf, filler, len); } list = list->next; } diff --git a/src/scconf/scconf.h b/src/scconf/scconf.h index f6cdd9f53b..083e79916e 100644 --- a/src/scconf/scconf.h +++ b/src/scconf/scconf.h @@ -18,7 +18,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SC_CONF_H diff --git a/src/scconf/sclex.c b/src/scconf/sclex.c index 44b1a47205..55a6702d99 100644 --- a/src/scconf/sclex.c +++ b/src/scconf/sclex.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -56,7 +56,7 @@ static void buf_init(BUFHAN * bp, FILE * fp, const char *saved_string) static void buf_addch(BUFHAN * bp, char ch) { - if (bp->bufcur >= bp->bufmax) { + if (bp->bufcur + 1 >= bp->bufmax) { char *p = (char *) realloc(bp->buf, bp->bufmax + 256); if (!p) return; @@ -144,6 +144,12 @@ static int scconf_lex_engine(scconf_parser * parser, BUFHAN * bp) continue; case ',': case '{': + if (parser->nested_blocks >= DEPTH_LIMIT) { + /* reached the limit, this whole block */ + scconf_skip_block(parser); + continue; + } + /* fall through */ case '}': case '=': case ';': diff --git a/src/scconf/write.c b/src/scconf/write.c index 7ef3a2d706..4a3603b727 100644 --- a/src/scconf/write.c +++ b/src/scconf/write.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -76,7 +76,7 @@ static int string_need_quotes(const char *str) static char *scconf_list_get_string(scconf_list * list) { char *buffer = NULL, *tmp; - int datalen, len, alloc_len, quote; + size_t datalen, len, alloc_len, quote; if (!list) { return strdup(""); diff --git a/src/sm/Makefile.am b/src/sm/Makefile.am index 215013c1f2..de375393dd 100644 --- a/src/sm/Makefile.am +++ b/src/sm/Makefile.am @@ -8,7 +8,6 @@ noinst_LTLIBRARIES = libsmiso.la libsmeac.la noinst_HEADERS = \ sm-iso-internal.h \ sm-iso.h \ - sslutil.h \ sm-eac.h if ENABLE_OPENSSL @@ -20,6 +19,8 @@ AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_READLINE_CFLAGS) AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src/include libsm_la_SOURCES = sm-common.c sm-common.h +libsm_la_LIBADD = $(OPENSSL_LIBS) +libsm_la_CFLAGS = $(OPENSSL_CFLAGS) libsmiso_la_SOURCES = sm-iso.c diff --git a/src/sm/sm-common.c b/src/sm/sm-common.c index 49647b7aba..1a9f5d24b8 100644 --- a/src/sm/sm-common.c +++ b/src/sm/sm-common.c @@ -17,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H @@ -40,35 +40,45 @@ #error "Need OpenSSL" #endif +#include #include #include #include "libopensc/opensc.h" #include "libopensc/asn1.h" #include "libopensc/log.h" +#include "libopensc/sc-ossl-compat.h" #include "sm-common.h" +#if OPENSSL_VERSION_NUMBER < 0x30000000L /* * From crypto/des/des_locl.h of OpenSSL . */ -#define c2l(c,l) (l =((DES_LONG)(*((c)++))) , \ - l|=((DES_LONG)(*((c)++)))<< 8L, \ - l|=((DES_LONG)(*((c)++)))<<16L, \ - l|=((DES_LONG)(*((c)++)))<<24L) +#define c2l(c,l) (l =((unsigned int)(*((c)++))) , \ + l|=((unsigned int)(*((c)++)))<< 8L, \ + l|=((unsigned int)(*((c)++)))<<16L, \ + l|=((unsigned int)(*((c)++)))<<24L) #define c2ln(c,l1,l2,n) { \ c+=n; \ l1=l2=0; \ switch (n) { \ - case 8: l2 =((DES_LONG)(*(--(c))))<<24L; \ - case 7: l2|=((DES_LONG)(*(--(c))))<<16L; \ - case 6: l2|=((DES_LONG)(*(--(c))))<< 8L; \ - case 5: l2|=((DES_LONG)(*(--(c)))); \ - case 4: l1 =((DES_LONG)(*(--(c))))<<24L; \ - case 3: l1|=((DES_LONG)(*(--(c))))<<16L; \ - case 2: l1|=((DES_LONG)(*(--(c))))<< 8L; \ - case 1: l1|=((DES_LONG)(*(--(c)))); \ + case 8: l2 =((unsigned int)(*(--(c))))<<24L; \ + /* fall through */ \ + case 7: l2|=((unsigned int)(*(--(c))))<<16L; \ + /* fall through */ \ + case 6: l2|=((unsigned int)(*(--(c))))<< 8L; \ + /* fall through */ \ + case 5: l2|=((unsigned int)(*(--(c)))); \ + /* fall through */ \ + case 4: l1 =((unsigned int)(*(--(c))))<<24L; \ + /* fall through */ \ + case 3: l1|=((unsigned int)(*(--(c))))<<16L; \ + /* fall through */ \ + case 2: l1|=((unsigned int)(*(--(c))))<< 8L; \ + /* fall through */ \ + case 1: l1|=((unsigned int)(*(--(c)))); \ } \ } @@ -82,13 +92,13 @@ * Inspired by or taken from OpenSSL crypto/des/cbc3_enc.c */ static void -DES_3cbc_encrypt(DES_cblock *input, DES_cblock *output, long length, - DES_key_schedule *ks1, DES_key_schedule *ks2, DES_cblock *iv, +DES_3cbc_encrypt(sm_des_cblock *input, sm_des_cblock *output, long length, + DES_key_schedule *ks1, DES_key_schedule *ks2, sm_des_cblock *iv, int enc) { int off=((int)length-1)/8; long l8=((length+7)/8)*8; - DES_cblock icv_out; + sm_des_cblock icv_out; memset(&icv_out, 0, sizeof(icv_out)); if (enc == DES_ENCRYPT) { @@ -98,12 +108,12 @@ DES_3cbc_encrypt(DES_cblock *input, DES_cblock *output, long length, (unsigned char*)output,l8,ks2,iv,!enc); DES_cbc_encrypt((unsigned char*)output, (unsigned char*)output,l8,ks1,iv,enc); - if ((unsigned)length >= sizeof(DES_cblock)) - memcpy(icv_out,output[off],sizeof(DES_cblock)); + if ((unsigned)length >= sizeof(sm_des_cblock)) + memcpy(icv_out,output[off],sizeof(sm_des_cblock)); } else { - if ((unsigned)length >= sizeof(DES_cblock)) - memcpy(icv_out,input[off],sizeof(DES_cblock)); + if ((unsigned)length >= sizeof(sm_des_cblock)) + memcpy(icv_out,input[off],sizeof(sm_des_cblock)); DES_cbc_encrypt((unsigned char*)input, (unsigned char*)output,l8,ks1,iv,enc); DES_cbc_encrypt((unsigned char*)output, @@ -111,20 +121,30 @@ DES_3cbc_encrypt(DES_cblock *input, DES_cblock *output, long length, DES_cbc_encrypt((unsigned char*)output, (unsigned char*)output,length,ks1,iv,enc); } - memcpy(*iv,icv_out,sizeof(DES_cblock)); + memcpy(*iv,icv_out,sizeof(sm_des_cblock)); } +#endif -DES_LONG -DES_cbc_cksum_3des_emv96(const unsigned char *in, DES_cblock *output, - long length, DES_key_schedule *schedule, DES_key_schedule *schedule2, - const_DES_cblock *ivec) +unsigned int +DES_cbc_cksum_3des_emv96(struct sc_context *ctx, + const unsigned char *in, sm_des_cblock *output, + long length, unsigned char *key, + sm_const_des_cblock *ivec) { - register DES_LONG tout0,tout1,tin0,tin1; register long l=length; - DES_LONG tin[2]; unsigned char *out = &(*output)[0]; const unsigned char *iv = &(*ivec)[0]; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + register unsigned int tout0,tout1,tin0,tin1; + unsigned int tin[2]; + sm_des_cblock kk, k2; + DES_key_schedule ks,ks2; + + memcpy(&kk, key, 8); + memcpy(&k2, key + 8, 8); + DES_set_key_unchecked(&kk,&ks); + DES_set_key_unchecked(&k2,&ks2); c2l(iv,tout0); c2l(iv,tout1); @@ -140,7 +160,7 @@ DES_cbc_cksum_3des_emv96(const unsigned char *in, DES_cblock *output, tin0^=tout0; tin[0]=tin0; tin1^=tout1; tin[1]=tin1; - DES_encrypt1((DES_LONG *)tin,schedule, DES_ENCRYPT); + DES_encrypt1((unsigned int *)tin, &ks, DES_ENCRYPT); tout0=tin[0]; tout1=tin[1]; } @@ -154,16 +174,13 @@ DES_cbc_cksum_3des_emv96(const unsigned char *in, DES_cblock *output, tin0^=tout0; tin[0]=tin0; tin1^=tout1; tin[1]=tin1; - DES_encrypt3((DES_LONG *)tin,schedule,schedule2,schedule); - tout0=tin[0]; + DES_encrypt3((unsigned int *)tin, &ks, &ks2, &ks); tout1=tin[1]; - if (out != NULL) { l2c(tout0,out); l2c(tout1,out); } - tout0=tin0=tin1=tin[0]=tin[1]=0; /* Transform the data in tout1 so that it will match the return value that the MIT Kerberos @@ -174,22 +191,103 @@ DES_cbc_cksum_3des_emv96(const unsigned char *in, DES_cblock *output, | ((tout1 << 8L) & 0x00FF0000) | ((tout1 << 24L) & 0xFF000000); return(tout1); +#else + EVP_CIPHER_CTX *cctx = NULL; + EVP_CIPHER *alg = NULL; + unsigned char outv[8], tmpout[4]; + int tmplen; + + /* Prepare IV */ + memcpy(outv, iv, sizeof outv); + + cctx = EVP_CIPHER_CTX_new(); + if (l > 8) { + alg = sc_evp_cipher(ctx, "DES-CBC"); + if (!EVP_EncryptInit_ex2(cctx, alg, key, iv, NULL)) { + sc_log_openssl(ctx); + EVP_CIPHER_CTX_free(cctx); + sc_evp_cipher_free(alg); + return SC_ERROR_INTERNAL; + } + /* Disable padding, otherwise it will fail to decrypt non-padded inputs */ + EVP_CIPHER_CTX_set_padding(cctx, 0); + for (; l > 8; l -= 8, in += 8) { + if (!EVP_EncryptUpdate(cctx, outv, &tmplen, in, 8)) { + sc_log_openssl(ctx); + EVP_CIPHER_CTX_free(cctx); + sc_evp_cipher_free(alg); + return SC_ERROR_INTERNAL; + } + } + if (!EVP_EncryptFinal_ex(cctx, outv + tmplen, &tmplen)) { + sc_log_openssl(ctx); + EVP_CIPHER_CTX_free(cctx); + sc_evp_cipher_free(alg); + return SC_ERROR_INTERNAL; + } + sc_evp_cipher_free(alg); + alg = NULL; + } + + /* We need to return first 4 bytes from here */ + memcpy(tmpout, outv, 4); + alg = sc_evp_cipher(ctx, "DES-EDE-CBC"); + if (!EVP_EncryptInit_ex2(cctx, alg, key, outv, NULL)) { + sc_log_openssl(ctx); + EVP_CIPHER_CTX_free(cctx); + sc_evp_cipher_free(alg); + return SC_ERROR_INTERNAL; + } + /* Disable padding, otherwise it will fail to decrypt non-padded inputs */ + EVP_CIPHER_CTX_set_padding(cctx, 0); + if (!EVP_EncryptUpdate(cctx, outv, &tmplen, in, (int)l)) { + sc_log_openssl(ctx); + EVP_CIPHER_CTX_free(cctx); + sc_evp_cipher_free(alg); + return SC_ERROR_INTERNAL; + } + if (!EVP_EncryptFinal_ex(cctx, outv + tmplen, &tmplen)) { + sc_log_openssl(ctx); + EVP_CIPHER_CTX_free(cctx); + sc_evp_cipher_free(alg); + return SC_ERROR_INTERNAL; + } + if (out != NULL) { + memcpy(out, tmpout, 4); + memcpy(out+4, outv+4, 4); + } + EVP_CIPHER_CTX_free(cctx); + sc_evp_cipher_free(alg); + return ((outv[7] << 0L) & 0x000000FF) | + ((outv[6] << 8L) & 0x0000FF00) | + ((outv[5] << 16L) & 0x00FF0000) | + ((outv[4] << 24L) & 0xFF000000); +#endif } -DES_LONG -DES_cbc_cksum_3des(const unsigned char *in, DES_cblock *output, - long length, DES_key_schedule *schedule, DES_key_schedule *schedule2, - const_DES_cblock *ivec) +unsigned int +DES_cbc_cksum_3des(struct sc_context *ctx, + const unsigned char *in, sm_des_cblock *output, + long length, unsigned char *key, + sm_const_des_cblock *ivec) { - register DES_LONG tout0,tout1,tin0,tin1; register long l=length; - DES_LONG tin[2]; unsigned char *out = &(*output)[0]; const unsigned char *iv = &(*ivec)[0]; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + register unsigned int tout0,tout1,tin0,tin1; + unsigned int tin[2]; + sm_des_cblock kk, k2; + DES_key_schedule ks,ks2; - c2l(iv,tout0); - c2l(iv,tout1); + memcpy(&kk, key, 8); + memcpy(&k2, key + 8, 8); + DES_set_key_unchecked(&kk,&ks); + DES_set_key_unchecked(&k2,&ks2); + + c2l(iv, tout0); + c2l(iv, tout1); for (; l>0; l-=8) { @@ -203,7 +301,7 @@ DES_cbc_cksum_3des(const unsigned char *in, DES_cblock *output, tin0^=tout0; tin[0]=tin0; tin1^=tout1; tin[1]=tin1; - DES_encrypt3((DES_LONG *)tin,schedule,schedule2,schedule); + DES_encrypt3((unsigned int *)tin, &ks, &ks2, &ks); /* fix 15/10/91 eay - thanks to keithr@sco.COM */ tout0=tin[0]; tout1=tin[1]; @@ -213,7 +311,6 @@ DES_cbc_cksum_3des(const unsigned char *in, DES_cblock *output, l2c(tout0,out); l2c(tout1,out); } - tout0=tin0=tin1=tin[0]=tin[1]=0; /* Transform the data in tout1 so that it will match the return value that the MIT Kerberos @@ -224,22 +321,70 @@ DES_cbc_cksum_3des(const unsigned char *in, DES_cblock *output, | ((tout1 << 8L) & 0x00FF0000) | ((tout1 << 24L) & 0xFF000000); return(tout1); +#else + EVP_CIPHER_CTX *cctx = NULL; + EVP_CIPHER *alg = NULL; + unsigned char outv[8]; + int tmplen = 0; + + /* Prepare IV */ + memcpy(outv, iv, sizeof outv); + + cctx = EVP_CIPHER_CTX_new(); + alg = sc_evp_cipher(ctx, "DES-EDE-CBC"); + if (!EVP_EncryptInit_ex2(cctx, alg, key, iv, NULL)) { + sc_log_openssl(ctx); + EVP_CIPHER_CTX_free(cctx); + sc_evp_cipher_free(alg); + return SC_ERROR_INTERNAL; + } + /* Disable padding, otherwise it will fail to decrypt non-padded inputs */ + EVP_CIPHER_CTX_set_padding(cctx, 0); + for (; l > 0; l -= 8, in += 8) { + if (!EVP_EncryptUpdate(cctx, outv, &tmplen, in, 8)) { + sc_log_openssl(ctx); + EVP_CIPHER_CTX_free(cctx); + sc_evp_cipher_free(alg); + return SC_ERROR_INTERNAL; + } + } + if (!EVP_EncryptFinal_ex(cctx, outv + tmplen, &tmplen)) { + sc_log_openssl(ctx); + EVP_CIPHER_CTX_free(cctx); + sc_evp_cipher_free(alg); + return SC_ERROR_INTERNAL; + } + if (out != NULL) { + memcpy(out, outv, sizeof outv); + } + EVP_CIPHER_CTX_free(cctx); + sc_evp_cipher_free(alg); + return ((outv[7] << 0L) & 0x000000FF) | + ((outv[6] << 8L) & 0x0000FF00) | + ((outv[5] << 16L) & 0x00FF0000) | + ((outv[4] << 24L) & 0xFF000000); +#endif } int -sm_encrypt_des_ecb3(unsigned char *key, unsigned char *data, int data_len, +sm_encrypt_des_ecb3(struct sc_context *ctx, + unsigned char *key, unsigned char *data, int data_len, unsigned char **out, int *out_len) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L int ii; - DES_cblock kk,k2; + sm_des_cblock kk,k2; DES_key_schedule ks,ks2; - +#else + EVP_CIPHER_CTX *cctx = NULL; + EVP_CIPHER *alg = NULL; + int tmplen; +#endif if (!out || !out_len) return -1; - *out_len = data_len + 7; *out_len -= *out_len % 8; @@ -247,6 +392,7 @@ sm_encrypt_des_ecb3(unsigned char *key, unsigned char *data, int data_len, if (!(*out)) return -1; +#if OPENSSL_VERSION_NUMBER < 0x30000000L memcpy(&kk, key, 8); memcpy(&k2, key + 8, 8); @@ -254,10 +400,41 @@ sm_encrypt_des_ecb3(unsigned char *key, unsigned char *data, int data_len, DES_set_key_unchecked(&k2,&ks2); for (ii=0; ii= 0; ii--) { + for (ii = (long)ssc_len - 1; ii >= 0; ii--) { *(ssc + ii) += 1; if (*(ssc + ii) != 0) break; diff --git a/src/sm/sm-common.h b/src/sm/sm-common.h index 7e8301456e..16be17ac47 100644 --- a/src/sm/sm-common.h +++ b/src/sm/sm-common.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SM_COMMON_H @@ -30,12 +30,15 @@ extern "C" { #include "libopensc/sm.h" -DES_LONG DES_cbc_cksum_3des(const unsigned char *in, DES_cblock *output, long length, - DES_key_schedule *schedule, DES_key_schedule *schedule2, const_DES_cblock *ivec); -DES_LONG DES_cbc_cksum_3des_emv96(const unsigned char *in, DES_cblock *output, - long length, DES_key_schedule *schedule, DES_key_schedule *schedule2, - const_DES_cblock *ivec); -int sm_encrypt_des_ecb3(unsigned char *key, unsigned char *data, int data_len, +unsigned int DES_cbc_cksum_3des(struct sc_context *ctx, + const unsigned char *in, sm_des_cblock *output, long length, + unsigned char *key, sm_const_des_cblock *ivec); +unsigned int DES_cbc_cksum_3des_emv96(struct sc_context *ctx, + const unsigned char *in, sm_des_cblock *output, + long length, unsigned char *key, + sm_const_des_cblock *ivec); +int sm_encrypt_des_ecb3(struct sc_context *ctx, + unsigned char *key, unsigned char *data, int data_len, unsigned char **out, int *out_len); int sm_encrypt_des_cbc3(struct sc_context *ctx, unsigned char *key, const unsigned char *in, size_t in_len, diff --git a/src/sm/sm-eac.c b/src/sm/sm-eac.c index a556105569..9ba3f81cda 100644 --- a/src/sm/sm-eac.c +++ b/src/sm/sm-eac.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 Frank Morgner + * Copyright (C) 2011-2018 Frank Morgner * * This file is part of OpenSC. * @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -26,7 +26,6 @@ #include "libopensc/log.h" #include "libopensc/opensc.h" #include "sm-eac.h" -#include "sslutil.h" #include #include @@ -34,29 +33,14 @@ #include #endif -char npa_default_flags = 0; +char eac_default_flags = 0; #define ISO_MSE 0x22 -#if defined(ENABLE_OPENPACE) -#include - -#define ASN1_APP_IMP_OPT(stname, field, type, tag) ASN1_EX_TYPE(ASN1_TFLG_IMPTAG|ASN1_TFLG_APPLICATION|ASN1_TFLG_OPTIONAL, tag, stname, field, type) -#define ASN1_APP_IMP(stname, field, type, tag) ASN1_EX_TYPE(ASN1_TFLG_IMPTAG|ASN1_TFLG_APPLICATION, tag, stname, field, type) - -/* 0x67 - * Auxiliary authenticated data */ -ASN1_ITEM_TEMPLATE(ASN1_AUXILIARY_DATA) = - ASN1_EX_TEMPLATE_TYPE( - ASN1_TFLG_SEQUENCE_OF|ASN1_TFLG_IMPTAG|ASN1_TFLG_APPLICATION, - 7, AuxiliaryAuthenticatedData, CVC_DISCRETIONARY_DATA_TEMPLATE) -ASN1_ITEM_TEMPLATE_END(ASN1_AUXILIARY_DATA) -IMPLEMENT_ASN1_FUNCTIONS(ASN1_AUXILIARY_DATA) -#endif - #if defined(ENABLE_OPENPACE) && defined(ENABLE_SM) #include #include #include +#include #include #include #include @@ -66,195 +50,10 @@ IMPLEMENT_ASN1_FUNCTIONS(ASN1_AUXILIARY_DATA) #include -/* - * MSE:Set AT - */ - -typedef struct npa_mse_cd_st { - ASN1_OBJECT *cryptographic_mechanism_reference; - ASN1_OCTET_STRING *key_reference1; - ASN1_OCTET_STRING *key_reference2; - ASN1_OCTET_STRING *eph_pub_key; - ASN1_AUXILIARY_DATA *auxiliary_data; - CVC_CHAT *chat; -} NPA_MSE_C; -/* Note that we can not use ASN1_AUXILIARY_DATA for the auxiliary_data element - * here. Due to limitations of OpenSSL it is not possible to *encode* an - * optional item template (such as auxiliary_data) in an other item template - * (such as ASN1_AUXILIARY_DATA). However, we can do - * - * NPA_MSE_C->auxiliary_data = d2i_ASN1_AUXILIARY_DATA(...) - * - * because they both use the same underlying struct. - * - * See also openssl/crypto/asn1/tasn_dec.c:183 - */ -ASN1_SEQUENCE(NPA_MSE_C) = { - /* 0x80 - * Cryptographic mechanism reference */ - ASN1_IMP_OPT(NPA_MSE_C, cryptographic_mechanism_reference, ASN1_OBJECT, 0), - /* 0x83 - * Reference of a public key / secret key */ - ASN1_IMP_OPT(NPA_MSE_C, key_reference1, ASN1_OCTET_STRING, 3), - /* 0x84 - * Reference of a private key / Reference for computing a session key */ - ASN1_IMP_OPT(NPA_MSE_C, key_reference2, ASN1_OCTET_STRING, 4), - /* 0x91 - * Ephemeral Public Key */ - ASN1_IMP_OPT(NPA_MSE_C, eph_pub_key, ASN1_OCTET_STRING, 0x11), - /* 0x67 - * Auxiliary authenticated data. See note above. */ - ASN1_APP_IMP_SEQUENCE_OF_OPT(NPA_MSE_C, auxiliary_data, CVC_DISCRETIONARY_DATA_TEMPLATE, 7), - /* Certificate Holder Authorization Template */ - ASN1_OPT(NPA_MSE_C, chat, CVC_CHAT), -} ASN1_SEQUENCE_END(NPA_MSE_C) -DECLARE_ASN1_FUNCTIONS(NPA_MSE_C) -IMPLEMENT_ASN1_FUNCTIONS(NPA_MSE_C) - - -/* - * General Authenticate for PACE - */ - -/* Protocol Command Data */ -typedef struct npa_gen_auth_pace_cd_st { - ASN1_OCTET_STRING *mapping_data; - ASN1_OCTET_STRING *eph_pub_key; - ASN1_OCTET_STRING *auth_token; -} NPA_GEN_AUTH_PACE_C_BODY; -ASN1_SEQUENCE(NPA_GEN_AUTH_PACE_C_BODY) = { - /* 0x81 - * Mapping Data */ - ASN1_IMP_OPT(NPA_GEN_AUTH_PACE_C_BODY, mapping_data, ASN1_OCTET_STRING, 1), - /* 0x83 - * Ephemeral Public Key */ - ASN1_IMP_OPT(NPA_GEN_AUTH_PACE_C_BODY, eph_pub_key, ASN1_OCTET_STRING, 3), - /* 0x85 - * Authentication Token */ - ASN1_IMP_OPT(NPA_GEN_AUTH_PACE_C_BODY, auth_token, ASN1_OCTET_STRING, 5), -} ASN1_SEQUENCE_END(NPA_GEN_AUTH_PACE_C_BODY) -DECLARE_ASN1_FUNCTIONS(NPA_GEN_AUTH_PACE_C_BODY) -IMPLEMENT_ASN1_FUNCTIONS(NPA_GEN_AUTH_PACE_C_BODY) - -typedef NPA_GEN_AUTH_PACE_C_BODY NPA_GEN_AUTH_PACE_C; -/* 0x7C - * Dynamic Authentication Data */ -ASN1_ITEM_TEMPLATE(NPA_GEN_AUTH_PACE_C) = - ASN1_EX_TEMPLATE_TYPE( - ASN1_TFLG_IMPTAG|ASN1_TFLG_APPLICATION, - 0x1c, NPA_GEN_AUTH_PACE_C, NPA_GEN_AUTH_PACE_C_BODY) -ASN1_ITEM_TEMPLATE_END(NPA_GEN_AUTH_PACE_C) -DECLARE_ASN1_FUNCTIONS(NPA_GEN_AUTH_PACE_C) -IMPLEMENT_ASN1_FUNCTIONS(NPA_GEN_AUTH_PACE_C) - -/* Protocol Response Data */ -typedef struct npa_gen_auth_pace_rapdu_body_st { - ASN1_OCTET_STRING *enc_nonce; - ASN1_OCTET_STRING *mapping_data; - ASN1_OCTET_STRING *eph_pub_key; - ASN1_OCTET_STRING *auth_token; - ASN1_OCTET_STRING *cur_car; - ASN1_OCTET_STRING *prev_car; -} NPA_GEN_AUTH_PACE_R_BODY; -ASN1_SEQUENCE(NPA_GEN_AUTH_PACE_R_BODY) = { - /* 0x80 - * Encrypted Nonce */ - ASN1_IMP_OPT(NPA_GEN_AUTH_PACE_R_BODY, enc_nonce, ASN1_OCTET_STRING, 0), - /* 0x82 - * Mapping Data */ - ASN1_IMP_OPT(NPA_GEN_AUTH_PACE_R_BODY, mapping_data, ASN1_OCTET_STRING, 2), - /* 0x84 - * Ephemeral Public Key */ - ASN1_IMP_OPT(NPA_GEN_AUTH_PACE_R_BODY, eph_pub_key, ASN1_OCTET_STRING, 4), - /* 0x86 - * Authentication Token */ - ASN1_IMP_OPT(NPA_GEN_AUTH_PACE_R_BODY, auth_token, ASN1_OCTET_STRING, 6), - /* 0x87 - * Most recent Certification Authority Reference */ - ASN1_IMP_OPT(NPA_GEN_AUTH_PACE_R_BODY, cur_car, ASN1_OCTET_STRING, 7), - /* 0x88 - * Previous Certification Authority Reference */ - ASN1_IMP_OPT(NPA_GEN_AUTH_PACE_R_BODY, prev_car, ASN1_OCTET_STRING, 8), -} ASN1_SEQUENCE_END(NPA_GEN_AUTH_PACE_R_BODY) -DECLARE_ASN1_FUNCTIONS(NPA_GEN_AUTH_PACE_R_BODY) -IMPLEMENT_ASN1_FUNCTIONS(NPA_GEN_AUTH_PACE_R_BODY) - -typedef NPA_GEN_AUTH_PACE_R_BODY NPA_GEN_AUTH_PACE_R; -/* 0x7C - * Dynamic Authentication Data */ -ASN1_ITEM_TEMPLATE(NPA_GEN_AUTH_PACE_R) = - ASN1_EX_TEMPLATE_TYPE( - ASN1_TFLG_IMPTAG|ASN1_TFLG_APPLICATION, - 0x1c, NPA_GEN_AUTH_PACE_R, NPA_GEN_AUTH_PACE_R_BODY) -ASN1_ITEM_TEMPLATE_END(NPA_GEN_AUTH_PACE_R) -DECLARE_ASN1_FUNCTIONS(NPA_GEN_AUTH_PACE_R) -IMPLEMENT_ASN1_FUNCTIONS(NPA_GEN_AUTH_PACE_R) - - -/* - * General Authenticate for CA - */ - -/* Protocol Command Data */ -typedef struct npa_gen_auth_ca_cd_st { - ASN1_OCTET_STRING *eph_pub_key; -} NPA_GEN_AUTH_CA_C_BODY; -ASN1_SEQUENCE(NPA_GEN_AUTH_CA_C_BODY) = { - /* 0x80 - * Ephemeral Public Key */ - ASN1_IMP_OPT(NPA_GEN_AUTH_CA_C_BODY, eph_pub_key, ASN1_OCTET_STRING, 0), -} ASN1_SEQUENCE_END(NPA_GEN_AUTH_CA_C_BODY) -DECLARE_ASN1_FUNCTIONS(NPA_GEN_AUTH_CA_C_BODY) -IMPLEMENT_ASN1_FUNCTIONS(NPA_GEN_AUTH_CA_C_BODY) - -typedef NPA_GEN_AUTH_CA_C_BODY NPA_GEN_AUTH_CA_C; -/* 0x7C - * Dynamic Authentication Data */ -ASN1_ITEM_TEMPLATE(NPA_GEN_AUTH_CA_C) = - ASN1_EX_TEMPLATE_TYPE( - ASN1_TFLG_IMPTAG|ASN1_TFLG_APPLICATION, - 0x1c, NPA_GEN_AUTH_CA_C, NPA_GEN_AUTH_CA_C_BODY) -ASN1_ITEM_TEMPLATE_END(NPA_GEN_AUTH_CA_C) -DECLARE_ASN1_FUNCTIONS(NPA_GEN_AUTH_CA_C) -IMPLEMENT_ASN1_FUNCTIONS(NPA_GEN_AUTH_CA_C) - -/* Protocol Response Data */ -typedef struct npa_gen_auth_ca_rapdu_body_st { - ASN1_OCTET_STRING *nonce; - ASN1_OCTET_STRING *auth_token; -} NPA_GEN_AUTH_CA_R_BODY; -ASN1_SEQUENCE(NPA_GEN_AUTH_CA_R_BODY) = { - /* 0x81 - * Nonce */ - ASN1_IMP_OPT(NPA_GEN_AUTH_CA_R_BODY, nonce, ASN1_OCTET_STRING, 1), - /* 0x82 - * Authentication Token */ - ASN1_IMP_OPT(NPA_GEN_AUTH_CA_R_BODY, auth_token, ASN1_OCTET_STRING, 2), -} ASN1_SEQUENCE_END(NPA_GEN_AUTH_CA_R_BODY) -DECLARE_ASN1_FUNCTIONS(NPA_GEN_AUTH_CA_R_BODY) -IMPLEMENT_ASN1_FUNCTIONS(NPA_GEN_AUTH_CA_R_BODY) - -typedef NPA_GEN_AUTH_CA_R_BODY NPA_GEN_AUTH_CA_R; -/* 0x7C - * Dynamic Authentication Data */ -ASN1_ITEM_TEMPLATE(NPA_GEN_AUTH_CA_R) = - ASN1_EX_TEMPLATE_TYPE( - ASN1_TFLG_IMPTAG|ASN1_TFLG_APPLICATION, - 0x1c, NPA_GEN_AUTH_CA_R, NPA_GEN_AUTH_CA_R_BODY) -ASN1_ITEM_TEMPLATE_END(NPA_GEN_AUTH_CA_R) -DECLARE_ASN1_FUNCTIONS(NPA_GEN_AUTH_CA_R) -IMPLEMENT_ASN1_FUNCTIONS(NPA_GEN_AUTH_CA_R) - - - -#define maxresp SC_MAX_APDU_BUFFER_SIZE - 2 - -/** @brief NPA secure messaging context */ -struct npa_sm_ctx { +/** @brief EAC secure messaging context */ +struct eac_sm_ctx { /** @brief EAC context */ EAC_CTX *ctx; - /** @brief Certificate Description given on initialization of PACE */ - BUF_MEM *certificate_description; /** @brief picc's compressed ephemeral public key of PACE */ BUF_MEM *id_icc; /** @brief PCD's compressed ephemeral public key of CA */ @@ -270,46 +69,36 @@ extern BUF_MEM *BUF_MEM_create(size_t len); extern BUF_MEM *BUF_MEM_create_init(const void *buf, size_t len); -static int npa_sm_encrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, +static int eac_sm_encrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, const u8 *data, size_t datalen, u8 **enc); -static int npa_sm_decrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, +static int eac_sm_decrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, const u8 *enc, size_t enclen, u8 **data); -static int npa_sm_authenticate(sc_card_t *card, const struct iso_sm_ctx *ctx, +static int eac_sm_authenticate(sc_card_t *card, const struct iso_sm_ctx *ctx, const u8 *data, size_t datalen, u8 **outdata); -static int npa_sm_verify_authentication(sc_card_t *card, const struct iso_sm_ctx *ctx, +static int eac_sm_verify_authentication(sc_card_t *card, const struct iso_sm_ctx *ctx, const u8 *mac, size_t maclen, const u8 *macdata, size_t macdatalen); -static int npa_sm_pre_transmit(sc_card_t *card, const struct iso_sm_ctx *ctx, +static int eac_sm_pre_transmit(sc_card_t *card, const struct iso_sm_ctx *ctx, sc_apdu_t *apdu); -static int npa_sm_post_transmit(sc_card_t *card, const struct iso_sm_ctx *ctx, +static int eac_sm_post_transmit(sc_card_t *card, const struct iso_sm_ctx *ctx, sc_apdu_t *sm_apdu); -static int npa_sm_finish(sc_card_t *card, const struct iso_sm_ctx *ctx, +static int eac_sm_finish(sc_card_t *card, const struct iso_sm_ctx *ctx, sc_apdu_t *apdu); -static void npa_sm_clear_free(const struct iso_sm_ctx *ctx); +static void eac_sm_clear_free(const struct iso_sm_ctx *ctx); -static struct npa_sm_ctx * -npa_sm_ctx_create(EAC_CTX *ctx, const unsigned char *certificate_description, - size_t certificate_description_length, +static struct eac_sm_ctx * +eac_sm_ctx_create(EAC_CTX *ctx, const unsigned char *id_icc, size_t id_icc_length) { - struct npa_sm_ctx *out = malloc(sizeof *out); + struct eac_sm_ctx *out = malloc(sizeof *out); if (!out) goto err; out->ctx = ctx; - if (certificate_description && certificate_description_length) { - out->certificate_description = - BUF_MEM_create_init(certificate_description, - certificate_description_length); - if (!out->certificate_description) - goto err; - } else - out->certificate_description = NULL; - if (id_icc && id_icc_length) { out->id_icc = BUF_MEM_create_init(id_icc, id_icc_length); if (!out->id_icc) @@ -320,10 +109,10 @@ npa_sm_ctx_create(EAC_CTX *ctx, const unsigned char *certificate_description, out->eph_pub_key = NULL; out->auxiliary_data = NULL; - out->flags = npa_default_flags; - if (out->flags & NPA_FLAG_DISABLE_CHECK_TA) + out->flags = eac_default_flags; + if (out->flags & EAC_FLAG_DISABLE_CHECK_TA) TA_disable_checks(out->ctx); - if (out->flags & NPA_FLAG_DISABLE_CHECK_CA) + if (out->flags & EAC_FLAG_DISABLE_CHECK_CA) CA_disable_passive_authentication(out->ctx); return out; @@ -334,9 +123,7 @@ npa_sm_ctx_create(EAC_CTX *ctx, const unsigned char *certificate_description, } static int -npa_sm_start(sc_card_t *card, EAC_CTX *eac_ctx, - const unsigned char *certificate_description, - size_t certificate_description_length, +eac_sm_start(sc_card_t *card, EAC_CTX *eac_ctx, const unsigned char *id_icc, size_t id_icc_length) { int r; @@ -353,22 +140,21 @@ npa_sm_start(sc_card_t *card, EAC_CTX *eac_ctx, goto err; } - sctx->priv_data = npa_sm_ctx_create(eac_ctx, - certificate_description, certificate_description_length, + sctx->priv_data = eac_sm_ctx_create(eac_ctx, id_icc, id_icc_length); if (!sctx->priv_data) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } - sctx->authenticate = npa_sm_authenticate; - sctx->encrypt = npa_sm_encrypt; - sctx->decrypt = npa_sm_decrypt; - sctx->verify_authentication = npa_sm_verify_authentication; - sctx->pre_transmit = npa_sm_pre_transmit; - sctx->post_transmit = npa_sm_post_transmit; - sctx->finish = npa_sm_finish; - sctx->clear_free = npa_sm_clear_free; + sctx->authenticate = eac_sm_authenticate; + sctx->encrypt = eac_sm_encrypt; + sctx->decrypt = eac_sm_decrypt; + sctx->verify_authentication = eac_sm_verify_authentication; + sctx->pre_transmit = eac_sm_pre_transmit; + sctx->post_transmit = eac_sm_post_transmit; + sctx->finish = eac_sm_finish; + sctx->clear_free = eac_sm_clear_free; sctx->padding_indicator = SM_ISO_PADDING; sctx->block_length = EVP_CIPHER_block_size(eac_ctx->key_ctx->cipher); @@ -387,123 +173,96 @@ static int get_ef_card_access(sc_card_t *card, return iso7816_read_binary_sfid(card, SFID_EF_CARDACCESS, ef_cardaccess, length_ef_cardaccess); } -static int format_mse_cdata(struct sc_context *ctx, int protocol, +/* + * MSE:Set AT + */ +static int encode_mse_cdata(struct sc_context *ctx, int protocol, const unsigned char *key_reference1, size_t key_reference1_len, const unsigned char *key_reference2, size_t key_reference2_len, const unsigned char *eph_pub_key, size_t eph_pub_key_len, const unsigned char *auxiliary_data, size_t auxiliary_data_len, const CVC_CHAT *chat, unsigned char **cdata) { - NPA_MSE_C *data = NULL; - unsigned char *data_sequence = NULL; - const unsigned char *data_no_sequence; - unsigned char *p; - long length; - int r, class, tag; + unsigned char *data = NULL, *encoded_chat = NULL, oid[16], *p = NULL; + size_t data_len = 0, oid_len = 0; + int r, encoded_chat_len = 0; + + struct sc_asn1_entry capdu_eac_mse[] = { + { "Cryptographic mechanism reference", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x00, SC_ASN1_OPTIONAL, NULL, NULL }, + { "Reference of a public key / secret key", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x03, SC_ASN1_OPTIONAL, NULL, NULL }, + { "Reference of a private key / Reference for computing a session key", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x04, SC_ASN1_OPTIONAL, NULL, NULL }, + { "Ephemeral Public Key", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x11, SC_ASN1_OPTIONAL, NULL, NULL }, + { "Auxiliary authenticated data", + SC_ASN1_OCTET_STRING, SC_ASN1_APP|SC_ASN1_CONS|0x07, SC_ASN1_OPTIONAL, NULL, NULL }, + /* "Certificate Holder Authorization Template", */ + { NULL , 0 , 0 , 0 , NULL , NULL } + }; if (!cdata) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } - data = NPA_MSE_C_new(); - if (!data) { - ssl_error(ctx); - r = SC_ERROR_INTERNAL; - goto err; - } - if (protocol) { - data->cryptographic_mechanism_reference = OBJ_nid2obj(protocol); - if (!data->cryptographic_mechanism_reference) { + ASN1_OBJECT *object = NULL; +#ifndef HAVE_EAC_OBJ_NID2OBJ + object = OBJ_nid2obj(protocol); +#else + object = EAC_OBJ_nid2obj(protocol); +#endif + if (!object) { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Error setting Cryptographic mechanism reference of MSE:Set AT data"); r = SC_ERROR_INTERNAL; goto err; } + oid_len = OBJ_length(object); + memcpy(oid, OBJ_get0_data(object), oid_len); } - if (key_reference1 && key_reference1_len) { - data->key_reference1 = ASN1_OCTET_STRING_new(); - if (!data->key_reference1 - || !M_ASN1_OCTET_STRING_set( - data->key_reference1, key_reference1, key_reference1_len)) { - sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Error setting key reference 1 of MSE:Set AT data"); - r = SC_ERROR_INTERNAL; - goto err; - } - } + sc_format_asn1_entry(capdu_eac_mse + 0, oid, &oid_len, oid_len > 0); + sc_format_asn1_entry(capdu_eac_mse + 1, (unsigned char *) key_reference1, &key_reference1_len, key_reference1 && key_reference1_len); + sc_format_asn1_entry(capdu_eac_mse + 2, (unsigned char *) key_reference2, &key_reference2_len, key_reference2 && key_reference2_len); + sc_format_asn1_entry(capdu_eac_mse + 3, (unsigned char *) eph_pub_key, &eph_pub_key_len, eph_pub_key && eph_pub_key_len); + sc_format_asn1_entry(capdu_eac_mse + 4, (unsigned char *) auxiliary_data, &auxiliary_data_len, auxiliary_data && auxiliary_data_len); - if (key_reference2 && key_reference2_len) { - data->key_reference2 = ASN1_OCTET_STRING_new(); - if (!data->key_reference2 - || !M_ASN1_OCTET_STRING_set( - data->key_reference2, key_reference2, key_reference2_len)) { - sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Error setting key reference 2 of MSE:Set AT data"); - r = SC_ERROR_INTERNAL; - goto err; - } - } - - if (eph_pub_key && eph_pub_key_len) { - data->eph_pub_key = ASN1_OCTET_STRING_new(); - if (!data->eph_pub_key - || !M_ASN1_OCTET_STRING_set( - data->eph_pub_key, eph_pub_key, eph_pub_key_len)) { - sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Error setting ephemeral Public Key of MSE:Set AT data"); - r = SC_ERROR_INTERNAL; - goto err; - } - } + r = sc_asn1_encode(ctx, capdu_eac_mse, &data, &data_len); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, r, "Error encoding MSE:Set AT APDU data"); - if (auxiliary_data && auxiliary_data_len) { - if (!d2i_ASN1_AUXILIARY_DATA(&data->auxiliary_data, &auxiliary_data, auxiliary_data_len)) { - sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Error setting authenticated auxiliary data of MSE:Set AT data"); - ssl_error(ctx); + if (chat) { + encoded_chat_len = i2d_CVC_CHAT((CVC_CHAT *) chat, &encoded_chat); + if (encoded_chat_len < 0) { + sc_log_openssl(ctx); r = SC_ERROR_INTERNAL; goto err; } } - data->chat = (CVC_CHAT *) chat; - - - length = i2d_NPA_MSE_C(data, &data_sequence); - data_no_sequence = data_sequence; - if (length < 0 - || (0x80 & ASN1_get_object(&data_no_sequence, &length, &tag, &class, length))) { - sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Error encoding MSE:Set AT APDU data"); - ssl_error(ctx); - r = SC_ERROR_INTERNAL; - goto err; - } - if (length < 0) { - r = SC_ERROR_INTERNAL; - goto err; - } - sc_debug_hex(ctx, SC_LOG_DEBUG_NORMAL, "MSE command data", data_no_sequence, length); - - p = realloc(*cdata, length); + p = realloc(*cdata, data_len + encoded_chat_len); if (!p) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } - memcpy(p, data_no_sequence, length); + memcpy(p, data, data_len); + memcpy(p+data_len, encoded_chat, encoded_chat_len); *cdata = p; - r = length; + r = (int)(data_len + encoded_chat_len); + + sc_debug_hex(ctx, SC_LOG_DEBUG_SM, "MSE command data", p, r); err: - if (data) { - /* do not free the functions parameter chat */ - data->chat = NULL; - NPA_MSE_C_free(data); - } - OPENSSL_free(data_sequence); + free(data); + if (encoded_chat) + OPENSSL_free(encoded_chat); return r; } -static int npa_mse(sc_card_t *card, +static int eac_mse(sc_card_t *card, unsigned char p1, unsigned char p2, int protocol, const unsigned char *key_reference1, size_t key_reference1_len, const unsigned char *key_reference2, size_t key_reference2_len, @@ -520,18 +279,14 @@ static int npa_mse(sc_card_t *card, goto err; } - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, ISO_MSE, p1, p2); - - r = format_mse_cdata(card->ctx, protocol, key_reference1, + r = encode_mse_cdata(card->ctx, protocol, key_reference1, key_reference1_len, key_reference2, key_reference2_len, eph_pub_key, eph_pub_key_len, auxiliary_data, auxiliary_data_len, chat, &d); if (r < 0) goto err; - apdu.data = d; - apdu.datalen = r; - apdu.lc = r; - + sc_format_apdu_ex(&apdu, 0x00, ISO_MSE, p1, p2, + d, r, NULL, 0); r = sc_transmit_apdu(card, &apdu); if (r < 0) @@ -555,27 +310,29 @@ static int npa_mse(sc_card_t *card, return r; } -static int npa_mse_set_at(sc_card_t *card, unsigned char p1, int protocol, +static int eac_mse_set_at(sc_card_t *card, unsigned char p1, int protocol, const unsigned char *key_reference1, size_t key_reference1_len, const unsigned char *key_reference2, size_t key_reference2_len, const unsigned char *eph_pub_key, size_t eph_pub_key_len, const unsigned char *auxiliary_data, size_t auxiliary_data_len, const CVC_CHAT *chat, u8 *sw1, u8 *sw2) { - return npa_mse(card, p1, 0xA4, protocol, key_reference1, + return eac_mse(card, p1, 0xA4, protocol, key_reference1, key_reference1_len, key_reference2, key_reference2_len, eph_pub_key, eph_pub_key_len, auxiliary_data, auxiliary_data_len, chat, sw1, sw2); } -static int npa_mse_set_at_pace(sc_card_t *card, int protocol, +static int eac_mse_set_at_pace(sc_card_t *card, int protocol, enum s_type secret_key, const CVC_CHAT *chat, u8 *sw1, u8 *sw2) { int r, tries; unsigned char key = secret_key; - - r = npa_mse_set_at(card, 0xC1, protocol, &key, sizeof key, NULL, + + r = eac_mse_set_at(card, 0xC1, protocol, &key, sizeof key, NULL, 0, NULL, 0, NULL, 0, chat, sw1, sw2); + if (0 > r) + goto err; if (*sw1 == 0x63) { if ((*sw2 & 0xc0) == 0xc0) { @@ -583,7 +340,7 @@ static int npa_mse_set_at_pace(sc_card_t *card, int protocol, if (tries <= 1) { /* this is only a warning... */ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Remaining tries: %d (%s must be %s)\n", - tries, npa_secret_name(secret_key), + tries, eac_secret_name(secret_key), tries ? "resumed" : "unblocked"); } r = SC_SUCCESS; @@ -599,45 +356,118 @@ static int npa_mse_set_at_pace(sc_card_t *card, int protocol, r = sc_check_sw(card, *sw1, *sw2); } +err: return r; } +/* + * General Authenticate + */ +static int encode_gen_auth_cdata(struct sc_context *ctx, + const unsigned char *ca_eph_pub_key, size_t ca_eph_pub_key_len, + const unsigned char *mapping_data, size_t mapping_data_len, + const unsigned char *eph_pub_key, size_t eph_pub_key_len, + const unsigned char *auth_token, size_t auth_token_len, + unsigned char **cdata) +{ + size_t data_len = 0; + int r; + + struct sc_asn1_entry capdu_eac_gen_auth_data[] = { + { "Ephemeral Public Key (CA)", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x00, SC_ASN1_OPTIONAL, NULL, NULL }, + { "Mapping Data", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x01, SC_ASN1_OPTIONAL, NULL, NULL }, + { "Ephemeral Public Key (PACE)", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x03, SC_ASN1_OPTIONAL, NULL, NULL }, + { "Authentication Token", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x05, SC_ASN1_OPTIONAL, NULL, NULL }, + { NULL , 0 , 0 , 0 , NULL , NULL } + }; + + struct sc_asn1_entry capdu_eac_gen_auth[] = { + { "Dynamic Authentication Data", SC_ASN1_STRUCT, SC_ASN1_APP|SC_ASN1_CONS|0x1c, 0, NULL, NULL }, + { NULL , 0 , 0 , 0 , NULL , NULL } + }; + + if (!cdata) { + r = SC_ERROR_INVALID_ARGUMENTS; + goto err; + } + + sc_format_asn1_entry(capdu_eac_gen_auth + 0, capdu_eac_gen_auth_data, &capdu_eac_gen_auth_data, 1); + + sc_format_asn1_entry(capdu_eac_gen_auth_data + 0, (unsigned char *) ca_eph_pub_key, &ca_eph_pub_key_len, ca_eph_pub_key && ca_eph_pub_key_len); + sc_format_asn1_entry(capdu_eac_gen_auth_data + 1, (unsigned char *) mapping_data, &mapping_data_len, mapping_data && mapping_data_len); + sc_format_asn1_entry(capdu_eac_gen_auth_data + 2, (unsigned char *) eph_pub_key, &eph_pub_key_len, eph_pub_key && eph_pub_key_len); + sc_format_asn1_entry(capdu_eac_gen_auth_data + 3, (unsigned char *) auth_token, &auth_token_len, auth_token && auth_token_len); + + r = sc_asn1_encode(ctx, capdu_eac_gen_auth, cdata, &data_len); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, r, "Error encoding General Authenticate APDU data"); + r = (int)data_len; + +err: + return r; +} +static int decode_gen_auth_rdata(struct sc_context *ctx, + const unsigned char *rdata, size_t rdata_len, + unsigned char **enc_nonce, size_t *enc_nonce_len, + unsigned char **mapping_data, size_t *mapping_data_len, + unsigned char **eph_pub_key, size_t *eph_pub_key_len, + unsigned char **auth_token, size_t *auth_token_len, + unsigned char **cur_car, size_t *cur_car_len, + unsigned char **prev_car, size_t *prev_car_len) +{ + struct sc_asn1_entry rapdu_eac_gen_auth_data[] = { + { "Encrypted Nonce", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x00, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, + { "Mapping Data", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x02, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, + { "Ephemeral Public Key", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x04, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, + { "Authentication Token", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x06, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, + { "Most recent Certification Authority Reference", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x07, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, + { "Pverious recent Certification Authority Reference", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x07, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, + { NULL , 0 , 0 , 0 , NULL , NULL } + }; + + struct sc_asn1_entry rapdu_eac_gen_auth[] = { + { "Dynamic Authentication Data", SC_ASN1_STRUCT, SC_ASN1_APP|SC_ASN1_CONS|0x1c, 0, NULL, NULL }, + { NULL , 0 , 0 , 0 , NULL , NULL } + }; + + sc_format_asn1_entry(rapdu_eac_gen_auth + 0, rapdu_eac_gen_auth_data, &rapdu_eac_gen_auth_data, 1); + + sc_format_asn1_entry(rapdu_eac_gen_auth_data + 0, enc_nonce, enc_nonce_len, 0); + sc_format_asn1_entry(rapdu_eac_gen_auth_data + 1, mapping_data, mapping_data_len, 0); + sc_format_asn1_entry(rapdu_eac_gen_auth_data + 2, eph_pub_key, eph_pub_key_len, 0); + sc_format_asn1_entry(rapdu_eac_gen_auth_data + 3, auth_token, auth_token_len, 0); + sc_format_asn1_entry(rapdu_eac_gen_auth_data + 4, cur_car, cur_car_len, 0); + sc_format_asn1_entry(rapdu_eac_gen_auth_data + 5, prev_car, prev_car_len, 0); + + return sc_asn1_decode(ctx, rapdu_eac_gen_auth, rdata, rdata_len, NULL, NULL); +} #define ISO_GENERAL_AUTHENTICATE 0x86 #define ISO_COMMAND_CHAINING 0x10 -static int npa_gen_auth_1_encrypted_nonce(sc_card_t *card, +static int eac_gen_auth_1_encrypted_nonce(sc_card_t *card, u8 **enc_nonce, size_t *enc_nonce_len) { sc_apdu_t apdu; - NPA_GEN_AUTH_PACE_C *c_data = NULL; - NPA_GEN_AUTH_PACE_R *r_data = NULL; - unsigned char *d = NULL, *p; - int r, l; - unsigned char resp[maxresp]; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, ISO_GENERAL_AUTHENTICATE, - 0x00, 0x00); - apdu.cla = ISO_COMMAND_CHAINING; + unsigned char *d = NULL; + int r; + unsigned char resp[SC_MAX_APDU_RESP_SIZE]; - c_data = NPA_GEN_AUTH_PACE_C_new(); - if (!c_data) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } - r = i2d_NPA_GEN_AUTH_PACE_C(c_data, &d); - if (r < 0) { - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; + r = encode_gen_auth_cdata(card->ctx, NULL, 0, NULL, 0, NULL, 0, NULL, 0, &d); + if (r < 0) goto err; - } - apdu.data = d; - apdu.datalen = r; - apdu.lc = r; - - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "General authenticate (Encrypted Nonce) command data", apdu.data, apdu.datalen); + sc_format_apdu_ex(&apdu, 0x00, ISO_GENERAL_AUTHENTICATE, 0x00, 0x00, + d, r, resp, sizeof resp); + apdu.cla = ISO_COMMAND_CHAINING; - apdu.resplen = sizeof resp; - apdu.resp = resp; r = sc_transmit_apdu(card, &apdu); if (r < 0) goto err; @@ -645,91 +475,35 @@ static int npa_gen_auth_1_encrypted_nonce(sc_card_t *card, r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r < 0) goto err; - - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "General authenticate (Encrypted Nonce) response data", apdu.resp, apdu.resplen); - - if (!d2i_NPA_GEN_AUTH_PACE_R(&r_data, - (const unsigned char **) &apdu.resp, apdu.resplen)) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not parse general authenticate response data."); - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; - goto err; - } - - if (!r_data->enc_nonce - || r_data->mapping_data - || r_data->eph_pub_key - || r_data->auth_token - || r_data->cur_car - || r_data->prev_car) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Response data of general authenticate for " - "step 1 should (only) contain the encrypted nonce."); - r = SC_ERROR_UNKNOWN_DATA_RECEIVED; - goto err; - } - p = r_data->enc_nonce->data; - l = r_data->enc_nonce->length; - - *enc_nonce = malloc(l); - if (!*enc_nonce) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } - /* Flawfinder: ignore */ - memcpy(*enc_nonce, p, l); - *enc_nonce_len = l; + r = decode_gen_auth_rdata(card->ctx, apdu.resp, apdu.resplen, + enc_nonce, enc_nonce_len, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL); err: - if (c_data) - NPA_GEN_AUTH_PACE_C_free(c_data); - OPENSSL_free(d); - if (r_data) - NPA_GEN_AUTH_PACE_R_free(r_data); + free(d); return r; } -static int npa_gen_auth_2_map_nonce(sc_card_t *card, +static int eac_gen_auth_2_map_nonce(sc_card_t *card, const u8 *in, size_t in_len, u8 **map_data_out, size_t *map_data_out_len) { sc_apdu_t apdu; - NPA_GEN_AUTH_PACE_C *c_data = NULL; - NPA_GEN_AUTH_PACE_R *r_data = NULL; - unsigned char *d = NULL, *p; - int r, l; - unsigned char resp[maxresp]; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, ISO_GENERAL_AUTHENTICATE, - 0x00, 0x00); - apdu.cla = ISO_COMMAND_CHAINING; + unsigned char *d = NULL; + int r; + unsigned char resp[SC_MAX_APDU_RESP_SIZE]; - c_data = NPA_GEN_AUTH_PACE_C_new(); - if (!c_data) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } - c_data->mapping_data = ASN1_OCTET_STRING_new(); - if (!c_data->mapping_data - || !M_ASN1_OCTET_STRING_set( - c_data->mapping_data, in, in_len)) { - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; - goto err; - } - r = i2d_NPA_GEN_AUTH_PACE_C(c_data, &d); - if (r < 0) { - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; + r = encode_gen_auth_cdata(card->ctx, NULL, 0, in, in_len, NULL, 0, NULL, 0, &d); + if (r < 0) goto err; - } - apdu.data = d; - apdu.datalen = r; - apdu.lc = r; - - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "General authenticate (Map Nonce) command data", apdu.data, apdu.datalen); + sc_format_apdu_ex(&apdu, 0x00, ISO_GENERAL_AUTHENTICATE, 0x00, 0x00, + d, r, resp, sizeof resp); + apdu.cla = ISO_COMMAND_CHAINING; - apdu.resplen = sizeof resp; - apdu.resp = resp; r = sc_transmit_apdu(card, &apdu); if (r < 0) goto err; @@ -738,90 +512,35 @@ static int npa_gen_auth_2_map_nonce(sc_card_t *card, if (r < 0) goto err; - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "General authenticate (Map Nonce) response data", apdu.resp, apdu.resplen); - - if (!d2i_NPA_GEN_AUTH_PACE_R(&r_data, - (const unsigned char **) &apdu.resp, apdu.resplen)) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not parse general authenticate response data."); - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; - goto err; - } - - if (r_data->enc_nonce - || !r_data->mapping_data - || r_data->eph_pub_key - || r_data->auth_token - || r_data->cur_car - || r_data->prev_car) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Response data of general authenticate for " - "step 2 should (only) contain the mapping data."); - r = SC_ERROR_UNKNOWN_DATA_RECEIVED; - goto err; - } - p = r_data->mapping_data->data; - l = r_data->mapping_data->length; - - *map_data_out = malloc(l); - if (!*map_data_out) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } - /* Flawfinder: ignore */ - memcpy(*map_data_out, p, l); - *map_data_out_len = l; + r = decode_gen_auth_rdata(card->ctx, apdu.resp, apdu.resplen, + NULL, NULL, + map_data_out, map_data_out_len, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL); err: - if (c_data) - NPA_GEN_AUTH_PACE_C_free(c_data); - OPENSSL_free(d); - if (r_data) - NPA_GEN_AUTH_PACE_R_free(r_data); + free(d); return r; } -static int npa_gen_auth_3_perform_key_agreement(sc_card_t *card, +static int eac_gen_auth_3_perform_key_agreement(sc_card_t *card, const u8 *in, size_t in_len, u8 **eph_pub_key_out, size_t *eph_pub_key_out_len) { sc_apdu_t apdu; - NPA_GEN_AUTH_PACE_C *c_data = NULL; - NPA_GEN_AUTH_PACE_R *r_data = NULL; - unsigned char *d = NULL, *p; - int r, l; - unsigned char resp[maxresp]; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, ISO_GENERAL_AUTHENTICATE, - 0x00, 0x00); - apdu.cla = ISO_COMMAND_CHAINING; + unsigned char *d = NULL; + int r; + unsigned char resp[SC_MAX_APDU_RESP_SIZE]; - c_data = NPA_GEN_AUTH_PACE_C_new(); - if (!c_data) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } - c_data->eph_pub_key = ASN1_OCTET_STRING_new(); - if (!c_data->eph_pub_key - || !M_ASN1_OCTET_STRING_set( - c_data->eph_pub_key, in, in_len)) { - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; - goto err; - } - r = i2d_NPA_GEN_AUTH_PACE_C(c_data, &d); - if (r < 0) { - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; + r = encode_gen_auth_cdata(card->ctx, NULL, 0, NULL, 0, in, in_len, NULL, 0, &d); + if (r < 0) goto err; - } - apdu.data = d; - apdu.datalen = r; - apdu.lc = r; - - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "General authenticate (Perform Key Agreement) command data", apdu.data, apdu.datalen); + sc_format_apdu_ex(&apdu, 0x00, ISO_GENERAL_AUTHENTICATE, 0x00, 0x00, + d, r, resp, sizeof resp); + apdu.cla = ISO_COMMAND_CHAINING; - apdu.resplen = sizeof resp; - apdu.resp = resp; r = sc_transmit_apdu(card, &apdu); if (r < 0) goto err; @@ -830,91 +549,36 @@ static int npa_gen_auth_3_perform_key_agreement(sc_card_t *card, if (r < 0) goto err; - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "General authenticate (Perform Key Agreement) response data", apdu.resp, apdu.resplen); - - if (!d2i_NPA_GEN_AUTH_PACE_R(&r_data, - (const unsigned char **) &apdu.resp, apdu.resplen)) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not parse general authenticate response data."); - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; - goto err; - } - - if (r_data->enc_nonce - || r_data->mapping_data - || !r_data->eph_pub_key - || r_data->auth_token - || r_data->cur_car - || r_data->prev_car) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Response data of general authenticate for " - "step 3 should (only) contain the ephemeral public key."); - r = SC_ERROR_UNKNOWN_DATA_RECEIVED; - goto err; - } - p = r_data->eph_pub_key->data; - l = r_data->eph_pub_key->length; - - *eph_pub_key_out = malloc(l); - if (!*eph_pub_key_out) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } - /* Flawfinder: ignore */ - memcpy(*eph_pub_key_out, p, l); - *eph_pub_key_out_len = l; + r = decode_gen_auth_rdata(card->ctx, apdu.resp, apdu.resplen, + NULL, NULL, + NULL, NULL, + eph_pub_key_out, eph_pub_key_out_len, + NULL, NULL, + NULL, NULL, + NULL, NULL); err: - if (c_data) - NPA_GEN_AUTH_PACE_C_free(c_data); - OPENSSL_free(d); - if (r_data) - NPA_GEN_AUTH_PACE_R_free(r_data); + free(d); return r; } -static int npa_gen_auth_4_mutual_authentication(sc_card_t *card, +static int eac_gen_auth_4_mutual_authentication(sc_card_t *card, const u8 *in, size_t in_len, u8 **auth_token_out, size_t *auth_token_out_len, - u8 **recent_car, size_t *recent_car_len, - u8 **prev_car, size_t *prev_car_len) + u8 **recent_car_out, size_t *recent_car_len, + u8 **prev_car_out, size_t *prev_car_len) { sc_apdu_t apdu; - NPA_GEN_AUTH_PACE_C *c_data = NULL; - NPA_GEN_AUTH_PACE_R *r_data = NULL; - unsigned char *d = NULL, *p; - int r, l; - unsigned char resp[maxresp]; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, ISO_GENERAL_AUTHENTICATE, - 0x00, 0x00); + unsigned char *d = NULL; + int r; + unsigned char resp[SC_MAX_APDU_RESP_SIZE]; - c_data = NPA_GEN_AUTH_PACE_C_new(); - if (!c_data) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } - c_data->auth_token = ASN1_OCTET_STRING_new(); - if (!c_data->auth_token - || !M_ASN1_OCTET_STRING_set( - c_data->auth_token, in, in_len)) { - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; - goto err; - } - r = i2d_NPA_GEN_AUTH_PACE_C(c_data, &d); - if (r < 0) { - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; + r = encode_gen_auth_cdata(card->ctx, NULL, 0, NULL, 0, NULL, 0, in, in_len, &d); + if (r < 0) goto err; - } - apdu.data = d; - apdu.datalen = r; - apdu.lc = r; - - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "General authenticate (Perform Key Agreement) command data", apdu.data, apdu.datalen); + sc_format_apdu_ex(&apdu, 0x00, ISO_GENERAL_AUTHENTICATE, 0x00, 0x00, + d, r, resp, sizeof resp); - apdu.resplen = sizeof resp; - apdu.resp = resp; r = sc_transmit_apdu(card, &apdu); if (r < 0) goto err; @@ -923,69 +587,16 @@ static int npa_gen_auth_4_mutual_authentication(sc_card_t *card, if (r < 0) goto err; - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "General authenticate (Perform Key Agreement) response data", apdu.resp, apdu.resplen); - - if (!d2i_NPA_GEN_AUTH_PACE_R(&r_data, - (const unsigned char **) &apdu.resp, apdu.resplen)) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not parse general authenticate response data."); - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; - goto err; - } - - if (r_data->enc_nonce - || r_data->mapping_data - || r_data->eph_pub_key - || !r_data->auth_token) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Response data of general authenticate for " - "step 4 should (only) contain the authentication token."); - r = SC_ERROR_UNKNOWN_DATA_RECEIVED; - goto err; - } - p = r_data->auth_token->data; - l = r_data->auth_token->length; - if (r_data->cur_car) { - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Most recent Certificate Authority Reference", - r_data->cur_car->data, r_data->cur_car->length); - *recent_car = malloc(r_data->cur_car->length); - if (!*recent_car) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } - /* Flawfinder: ignore */ - memcpy(*recent_car, r_data->cur_car->data, r_data->cur_car->length); - *recent_car_len = r_data->cur_car->length; - } else - *recent_car_len = 0; - if (r_data->prev_car) { - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Previous Certificate Authority Reference", - r_data->prev_car->data, r_data->prev_car->length); - *prev_car = malloc(r_data->prev_car->length); - if (!*prev_car) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } - /* Flawfinder: ignore */ - memcpy(*prev_car, r_data->prev_car->data, r_data->prev_car->length); - *prev_car_len = r_data->prev_car->length; - } else - *prev_car_len = 0; - - *auth_token_out = malloc(l); - if (!*auth_token_out) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } - /* Flawfinder: ignore */ - memcpy(*auth_token_out, p, l); - *auth_token_out_len = l; + r = decode_gen_auth_rdata(card->ctx, apdu.resp, apdu.resplen, + NULL, NULL, + NULL, NULL, + NULL, NULL, + auth_token_out, auth_token_out_len, + recent_car_out, recent_car_len, + prev_car_out, prev_car_len); err: - if (c_data) - NPA_GEN_AUTH_PACE_C_free(c_data); - OPENSSL_free(d); - if (r_data) - NPA_GEN_AUTH_PACE_R_free(r_data); + free(d); return r; } @@ -995,34 +606,37 @@ get_psec(sc_card_t *card, const char *pin, size_t length_pin, enum s_type pin_id { char *p = NULL; PACE_SEC *r; - /* Flawfinder: ignore */ - char buf[MAX_MRZ_LEN > 32 ? MAX_MRZ_LEN : 32]; + char buf[EAC_MAX_MRZ_LEN > 32 ? EAC_MAX_MRZ_LEN : 32]; if (!length_pin || !pin) { if (0 > snprintf(buf, sizeof buf, "Please enter your %s: ", - npa_secret_name(pin_id))) { + eac_secret_name(pin_id))) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not create password prompt.\n"); return NULL; } - p = malloc(MAX_MRZ_LEN+1); + p = malloc(EAC_MAX_MRZ_LEN+1); if (!p) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Not enough memory for %s.\n", - npa_secret_name(pin_id)); + eac_secret_name(pin_id)); return NULL; } - if (0 > EVP_read_pw_string_min(p, 0, MAX_MRZ_LEN, buf, 0)) { + if (0 > EVP_read_pw_string_min(p, 0, EAC_MAX_MRZ_LEN, buf, 0)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not read %s.\n", - npa_secret_name(pin_id)); + eac_secret_name(pin_id)); return NULL; } length_pin = strlen(p); - if (length_pin > MAX_MRZ_LEN) { + if (length_pin > EAC_MAX_MRZ_LEN) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "MRZ too long"); return NULL; } pin = p; } + if (pin_id != PACE_PIN && pin_id != PACE_CAN && pin_id != PACE_MRZ && pin_id != PACE_PUK) + pin_id = PACE_RAW; + r = PACE_SEC_new(pin, length_pin, pin_id); if (p) { @@ -1062,8 +676,8 @@ int perform_pace(sc_card_t *card, pp = pace_input.certificate_description; if (!d2i_CVC_CERTIFICATE_DESCRIPTION(&desc, &pp, pace_input.certificate_description_length)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not parse certificate description."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1071,8 +685,8 @@ int perform_pace(sc_card_t *card, if (!bio_stdout) { bio_stdout = BIO_new_fp(stdout, BIO_NOCLOSE); if (!bio_stdout) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not create output buffer."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1081,8 +695,8 @@ int perform_pace(sc_card_t *card, printf("Certificate Description\n"); switch(certificate_description_print(bio_stdout, desc, 8)) { case 0: + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not print certificate description."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; break; @@ -1117,8 +731,8 @@ int perform_pace(sc_card_t *card, if (!bio_stdout) { bio_stdout = BIO_new_fp(stdout, BIO_NOCLOSE); if (!bio_stdout) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not create output buffer."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1126,16 +740,16 @@ int perform_pace(sc_card_t *card, pp = pace_input.chat; if (!d2i_CVC_CHAT(&chat, &pp, pace_input.chat_length)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not parse card holder authorization template (CHAT)."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } printf("Card holder authorization template (CHAT)\n"); if (!cvc_chat_print(bio_stdout, chat, 8)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not print card holder authorization template (CHAT)."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1156,65 +770,61 @@ int perform_pace(sc_card_t *card, } } - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "EF.CardAccess", pace_output->ef_cardaccess, + sc_debug_hex(card->ctx, SC_LOG_DEBUG_SM, "EF.CardAccess", pace_output->ef_cardaccess, pace_output->ef_cardaccess_length); - /* XXX Card capabilities should be determined by the OpenSC card driver. We - * set it here to be able to use the nPA without patching OpenSC. By - * now we have read the EF.CardAccess so the assumption to have an nPA - * seems valid. */ - card->caps |= SC_CARD_CAP_APDU_EXT; - + EAC_init(); eac_ctx = EAC_CTX_new(); if (!eac_ctx || !EAC_CTX_init_ef_cardaccess(pace_output->ef_cardaccess, pace_output->ef_cardaccess_length, eac_ctx) || !eac_ctx->pace_ctx) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not parse EF.CardAccess."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } eac_ctx->tr_version = tr_version; - r = npa_mse_set_at_pace(card, eac_ctx->pace_ctx->protocol, + r = eac_mse_set_at_pace(card, eac_ctx->pace_ctx->protocol, pace_input.pin_id, chat, &pace_output->mse_set_at_sw1, &pace_output->mse_set_at_sw2); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not select protocol proberties " + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not select protocol properties " "(MSE: Set AT failed)."); goto err; } enc_nonce = BUF_MEM_new(); if (!enc_nonce) { - ssl_error(card->ctx); + sc_log_openssl(card->ctx); r = SC_ERROR_OUT_OF_MEMORY; goto err; } - r = npa_gen_auth_1_encrypted_nonce(card, (u8 **) &enc_nonce->data, - &enc_nonce->length); + p = (u8 *) enc_nonce->data; + r = eac_gen_auth_1_encrypted_nonce(card, &p, &enc_nonce->length); + enc_nonce->data = (char *) p; if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not get encrypted nonce from card " "(General Authenticate step 1 failed)."); goto err; } - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Encrypted nonce from MRTD", (u8 *)enc_nonce->data, enc_nonce->length); + sc_debug_hex(card->ctx, SC_LOG_DEBUG_SM, "Encrypted nonce from MRTD", (u8 *)enc_nonce->data, enc_nonce->length); enc_nonce->max = enc_nonce->length; sec = get_psec(card, (char *) pace_input.pin, pace_input.pin_length, pace_input.pin_id); if (!sec) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not encode PACE secret."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } if (!PACE_STEP2_dec_nonce(eac_ctx, sec, enc_nonce)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not decrypt MRTD's nonce."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1222,24 +832,26 @@ int perform_pace(sc_card_t *card, mdata_opp = BUF_MEM_new(); mdata = PACE_STEP3A_generate_mapping_data(eac_ctx); if (!mdata || !mdata_opp) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not generate mapping data."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } - r = npa_gen_auth_2_map_nonce(card, (u8 *) mdata->data, mdata->length, - (u8 **) &mdata_opp->data, &mdata_opp->length); + p = (u8 *) mdata_opp->data; + r = eac_gen_auth_2_map_nonce(card, (u8 *) mdata->data, mdata->length, + &p, &mdata_opp->length); + mdata_opp->data = (char *) p; if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not exchange mapping data with card " "(General Authenticate step 2 failed)."); goto err; } mdata_opp->max = mdata_opp->length; - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Mapping data from MRTD", (u8 *) mdata_opp->data, mdata_opp->length); + sc_debug_hex(card->ctx, SC_LOG_DEBUG_SM, "Mapping data from MRTD", (u8 *) mdata_opp->data, mdata_opp->length); if (!PACE_STEP3A_map_generator(eac_ctx, mdata_opp)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not map generator."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1247,43 +859,47 @@ int perform_pace(sc_card_t *card, pub = PACE_STEP3B_generate_ephemeral_key(eac_ctx); pub_opp = BUF_MEM_new(); if (!pub || !pub_opp) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not generate ephemeral domain parameter or " "ephemeral key pair."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } - r = npa_gen_auth_3_perform_key_agreement(card, (u8 *) pub->data, pub->length, - (u8 **) &pub_opp->data, &pub_opp->length); + p = (u8 *) pub_opp->data; + r = eac_gen_auth_3_perform_key_agreement(card, (u8 *) pub->data, pub->length, + &p, &pub_opp->length); + pub_opp->data = (char *) p; if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not exchange ephemeral public key with card " "(General Authenticate step 3 failed)."); goto err; } pub_opp->max = pub_opp->length; - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Ephemeral public key from MRTD", (u8 *) pub_opp->data, pub_opp->length); + sc_debug_hex(card->ctx, SC_LOG_DEBUG_SM, "Ephemeral public key from MRTD", (u8 *) pub_opp->data, pub_opp->length); if (!PACE_STEP3B_compute_shared_secret(eac_ctx, pub_opp) || !PACE_STEP3C_derive_keys(eac_ctx)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not compute ephemeral shared secret or " "derive keys for encryption and authentication."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } token = PACE_STEP3D_compute_authentication_token(eac_ctx, pub_opp); token_opp = BUF_MEM_new(); if (!token || !token_opp) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not compute authentication token."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } - r = npa_gen_auth_4_mutual_authentication(card, (u8 *) token->data, token->length, - (u8 **) &token_opp->data, &token_opp->length, + p = (u8 *) token_opp->data; + r = eac_gen_auth_4_mutual_authentication(card, (u8 *) token->data, token->length, + &p, &token_opp->length, &pace_output->recent_car, &pace_output->recent_car_length, &pace_output->previous_car, &pace_output->previous_car_length); + token_opp->data = (char *) p; if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not exchange authentication token with card " @@ -1293,16 +909,16 @@ int perform_pace(sc_card_t *card, token_opp->max = token_opp->length; if (!PACE_STEP3D_verify_authentication_token(eac_ctx, token_opp)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not verify authentication token."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } /* Initialize secure channel */ if (!EAC_CTX_set_encryption_ctx(eac_ctx, EAC_ID_PACE)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not initialize encryption."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1311,8 +927,12 @@ int perform_pace(sc_card_t *card, comp_pub = EAC_Comp(eac_ctx, EAC_ID_PACE, pub); comp_pub_opp = EAC_Comp(eac_ctx, EAC_ID_PACE, pub_opp); if (!comp_pub || !comp_pub_opp) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not compress public keys for identification."); - ssl_error(card->ctx); + r = SC_ERROR_INTERNAL; + goto err; + } + if (comp_pub_opp->length == 0) { r = SC_ERROR_INTERNAL; goto err; } @@ -1324,10 +944,13 @@ int perform_pace(sc_card_t *card, } pace_output->id_icc = p; pace_output->id_icc_length = comp_pub_opp->length; - /* Flawfinder: ignore */ memcpy(pace_output->id_icc, comp_pub_opp->data, comp_pub_opp->length); - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "ID ICC", pace_output->id_icc, + sc_debug_hex(card->ctx, SC_LOG_DEBUG_SM, "ID ICC", pace_output->id_icc, pace_output->id_icc_length); + if (comp_pub->length == 0) { + r = SC_ERROR_INTERNAL; + goto err; + } p = realloc(pace_output->id_pcd, comp_pub->length); if (!p) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Not enough memory for ID PCD.\n"); @@ -1336,14 +959,11 @@ int perform_pace(sc_card_t *card, } pace_output->id_pcd = p; pace_output->id_pcd_length = comp_pub->length; - /* Flawfinder: ignore */ memcpy(pace_output->id_pcd, comp_pub->data, comp_pub->length); - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "ID PCD", pace_output->id_pcd, + sc_debug_hex(card->ctx, SC_LOG_DEBUG_SM, "ID PCD", pace_output->id_pcd, pace_output->id_pcd_length); - r = npa_sm_start(card, eac_ctx, pace_input.certificate_description, - pace_input.certificate_description_length, pace_output->id_icc, - pace_output->id_icc_length); + r = eac_sm_start(card, eac_ctx, pace_output->id_icc, pace_output->id_icc_length); } err: @@ -1376,27 +996,27 @@ int perform_pace(sc_card_t *card, if (r < 0) EAC_CTX_clear_free(eac_ctx); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, r); } -static int npa_mse_set_at_ta(sc_card_t *card, int protocol, +static int eac_mse_set_at_ta(sc_card_t *card, int protocol, const unsigned char *chr, size_t chr_len, const unsigned char *eph_pub_key, size_t eph_pub_key_len, const unsigned char *auxiliary_data, size_t auxiliary_data_len) { - return npa_mse_set_at(card, 0x81, protocol, chr, chr_len, NULL, 0, + return eac_mse_set_at(card, 0x81, protocol, chr, chr_len, NULL, 0, eph_pub_key, eph_pub_key_len, auxiliary_data, auxiliary_data_len, NULL, NULL, NULL); } -static int npa_mse_set_dst(sc_card_t *card, +static int eac_mse_set_dst(sc_card_t *card, const unsigned char *chr, size_t chr_len) { - return npa_mse(card, 0x81, 0xb6, 0, chr, chr_len, NULL, 0, NULL, 0, NULL, + return eac_mse(card, 0x81, 0xb6, 0, chr, chr_len, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, NULL); } -static int npa_get_challenge(sc_card_t *card, +static int eac_get_challenge(sc_card_t *card, unsigned char *challenge, size_t len) { sc_apdu_t apdu; @@ -1407,10 +1027,7 @@ static int npa_get_challenge(sc_card_t *card, goto err; } - sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0x00, 0x00); - apdu.le = len; - apdu.resplen = len; - apdu.resp = challenge; + sc_format_apdu_ex(&apdu, 0x00, 0x84, 0x00, 0x00, NULL, 0, challenge, len); r = sc_transmit_apdu(card, &apdu); if (r < 0) @@ -1422,7 +1039,7 @@ static int npa_get_challenge(sc_card_t *card, return r; } -static int npa_verify(sc_card_t *card, +static int eac_verify(sc_card_t *card, const unsigned char *cert, size_t cert_len) { sc_apdu_t apdu; @@ -1436,17 +1053,14 @@ static int npa_verify(sc_card_t *card, goto err; } - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_EXT, 0x2A, 0x00, 0xbe); - - apdu.data = cert; - if (0x80 & ASN1_get_object(&apdu.data, &length, &tag, &class, cert_len)) { + if (0x80 & ASN1_get_object(&cert, &length, &tag, &class, cert_len)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error decoding Certificate"); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } - apdu.datalen = length; - apdu.lc = length; + + sc_format_apdu_ex(&apdu, 0x00, 0x2A, 0x00, 0xbe, (unsigned char *) cert, length, NULL, 0); r = sc_transmit_apdu(card, &apdu); if (r < 0) @@ -1458,7 +1072,7 @@ static int npa_verify(sc_card_t *card, return r; } -static int npa_external_authenticate(sc_card_t *card, +static int eac_external_authenticate(sc_card_t *card, unsigned char *signature, size_t signature_len) { int r; @@ -1470,11 +1084,7 @@ static int npa_external_authenticate(sc_card_t *card, goto err; } - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x82, 0x00, 0x00); - - apdu.data = signature; - apdu.datalen = signature_len; - apdu.lc = signature_len; + sc_format_apdu_ex(&apdu, 0x00, 0x82, 0x00, 0x00, signature, signature_len, NULL, 0); r = sc_transmit_apdu(card, &apdu); if (r < 0) @@ -1486,6 +1096,16 @@ static int npa_external_authenticate(sc_card_t *card, return r; } +static void eac_sm_clear_free_without_ctx(const struct iso_sm_ctx *ctx) +{ + if (ctx) { + struct eac_sm_ctx *eacsmctx = ctx->priv_data; + if (eacsmctx) + eacsmctx->ctx = NULL; + eac_sm_clear_free(ctx); + } +} + #define TA_NONCE_LENGTH 8 int perform_terminal_authentication(sc_card_t *card, const unsigned char **certs, const size_t *certs_lens, @@ -1498,9 +1118,11 @@ int perform_terminal_authentication(sc_card_t *card, CVC_CERT *cvc_cert = NULL; BUF_MEM *nonce = NULL, *signature = NULL; struct iso_sm_ctx *isosmctx = NULL; - struct npa_sm_ctx *eacsmctx = NULL; + struct eac_sm_ctx *eacsmctx = NULL; unsigned char *ef_cardaccess = NULL; EAC_CTX *eac_ctx = NULL; + const unsigned char *chr = NULL; + size_t chr_len = 0; if (!card || !certs_lens || !certs) { r = SC_ERROR_INVALID_ARGUMENTS; @@ -1508,6 +1130,7 @@ int perform_terminal_authentication(sc_card_t *card, } if (!card->sm_ctx.info.cmd_data) { card->sm_ctx.info.cmd_data = iso_sm_ctx_create(); + card->sm_ctx.ops.close = iso_sm_close; } if (!card->sm_ctx.info.cmd_data) { r = SC_ERROR_INTERNAL; @@ -1522,30 +1145,29 @@ int perform_terminal_authentication(sc_card_t *card, goto err; } - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "EF.CardAccess", ef_cardaccess, + sc_debug_hex(card->ctx, SC_LOG_DEBUG_SM, "EF.CardAccess", ef_cardaccess, ef_cardaccess_length); - /* XXX Card capabilities should be determined by the OpenSC card driver. We - * set it here to be able to use the nPA without patching OpenSC. By - * now we have read the EF.CardAccess so the assumption to have an nPA - * seems valid. */ - card->caps |= SC_CARD_CAP_APDU_EXT; - + EAC_init(); eac_ctx = EAC_CTX_new(); if (!eac_ctx || !EAC_CTX_init_ef_cardaccess(ef_cardaccess, ef_cardaccess_length, eac_ctx)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not parse EF.CardAccess."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } - isosmctx->priv_data = npa_sm_ctx_create(eac_ctx, NULL, 0, NULL, 0); + isosmctx->priv_data = eac_sm_ctx_create(eac_ctx, NULL, 0); if (!isosmctx->priv_data) { r = SC_ERROR_INTERNAL; goto err; } + /* when iso_sm_ctx_clear_free is called, we want everything to be freed + * except the EAC_CTX, because it is needed for performing SM *after* + * iso_sm_start was called. */ + isosmctx->clear_free = eac_sm_clear_free_without_ctx; eac_ctx = NULL; } eacsmctx = isosmctx->priv_data; @@ -1557,33 +1179,36 @@ int perform_terminal_authentication(sc_card_t *card, if (!CVC_d2i_CVC_CERT(&cvc_cert, &cert, cert_len) || !cvc_cert || !cvc_cert->body || !cvc_cert->body->certificate_authority_reference || !cvc_cert->body->certificate_holder_reference) { - ssl_error(card->ctx); + sc_log_openssl(card->ctx); r = SC_ERROR_INVALID_DATA; goto err; } cert = *certs; - r = npa_mse_set_dst(card, - cvc_cert->body->certificate_authority_reference->data, - cvc_cert->body->certificate_authority_reference->length); + r = eac_mse_set_dst(card, + ASN1_STRING_get0_data(cvc_cert->body->certificate_authority_reference), + ASN1_STRING_length(cvc_cert->body->certificate_authority_reference)); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not select protocol proberties " + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not select protocol properties " "(MSE: Set AT failed)."); goto err; } - r = npa_verify(card, cert, cert_len); + r = eac_verify(card, cert, cert_len); if (r < 0) goto err; + chr = ASN1_STRING_get0_data(cvc_cert->body->certificate_holder_reference); + chr_len = ASN1_STRING_length(cvc_cert->body->certificate_holder_reference); + certs++; certs_lens++; } if (!EAC_CTX_init_ta(eacsmctx->ctx, privkey, privkey_len, cert, cert_len)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not initialize TA."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1593,38 +1218,36 @@ int perform_terminal_authentication(sc_card_t *card, BUF_MEM_free(eacsmctx->eph_pub_key); eacsmctx->eph_pub_key = TA_STEP3_generate_ephemeral_key(eacsmctx->ctx); if (!eacsmctx->eph_pub_key) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not generate CA ephemeral key."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } - r = npa_mse_set_at_ta(card, eacsmctx->ctx->ta_ctx->protocol, - cvc_cert->body->certificate_holder_reference->data, - cvc_cert->body->certificate_holder_reference->length, + r = eac_mse_set_at_ta(card, eacsmctx->ctx->ta_ctx->protocol, chr, chr_len, (unsigned char *) eacsmctx->eph_pub_key->data, eacsmctx->eph_pub_key->length, auxiliary_data, auxiliary_data_len); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not select protocol proberties " + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not select protocol properties " "(MSE: Set AT failed)."); goto err; } nonce = BUF_MEM_create(TA_NONCE_LENGTH); if (!nonce) { - ssl_error(card->ctx); + sc_log_openssl(card->ctx); r = SC_ERROR_INTERNAL; goto err; } - r = npa_get_challenge(card, (unsigned char *) nonce->data, nonce->length); + r = eac_get_challenge(card, (unsigned char *) nonce->data, nonce->length); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not get nonce for TA."); goto err; } if (!TA_STEP4_set_nonce(eacsmctx->ctx, nonce)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not set nonce for TA."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1633,15 +1256,17 @@ int perform_terminal_authentication(sc_card_t *card, BUF_MEM_free(eacsmctx->auxiliary_data); eacsmctx->auxiliary_data = BUF_MEM_create_init(auxiliary_data, auxiliary_data_len); + if (!eacsmctx->id_icc) + eacsmctx->id_icc = BUF_MEM_new(); signature = TA_STEP5_sign(eacsmctx->ctx, eacsmctx->eph_pub_key, eacsmctx->id_icc, eacsmctx->auxiliary_data); if (!signature) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not generate signature."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } - r = npa_external_authenticate(card, (unsigned char *) signature->data, + r = eac_external_authenticate(card, (unsigned char *) signature->data, signature->length); err: @@ -1653,57 +1278,61 @@ int perform_terminal_authentication(sc_card_t *card, BUF_MEM_clear_free(signature); if (card) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, r); else return r; } -static int npa_mse_set_at_ca(sc_card_t *card, int protocol) +static int eac_mse_set_at_ca(sc_card_t *card, int protocol) { - return npa_mse_set_at(card, 0x41, protocol, NULL, 0, NULL, 0, NULL, 0, + return eac_mse_set_at(card, 0x41, protocol, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, NULL); } -static int npa_gen_auth_ca(sc_card_t *card, const BUF_MEM *eph_pub_key, +static int decode_gen_auth_ca_rdata(struct sc_context *ctx, + const unsigned char *rdata, size_t rdata_len, + unsigned char **nonce, size_t *enc_nonce_len, + unsigned char **auth_token, size_t *auth_token_len) +{ + struct sc_asn1_entry rapdu_eac_gen_auth_ca_data[] = { + { "Nonce", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x01, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, + { "Authentication Token", + SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x02, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, + { NULL , 0 , 0 , 0 , NULL , NULL } + }; + + struct sc_asn1_entry rapdu_eac_gen_auth_ca[] = { + { "Dynamic Authentication Data", SC_ASN1_STRUCT, SC_ASN1_APP|SC_ASN1_CONS|0x1c, 0, NULL, NULL }, + { NULL , 0 , 0 , 0 , NULL , NULL } + }; + + sc_format_asn1_entry(rapdu_eac_gen_auth_ca + 0, rapdu_eac_gen_auth_ca_data, &rapdu_eac_gen_auth_ca_data, 1); + + sc_format_asn1_entry(rapdu_eac_gen_auth_ca_data + 0, nonce, enc_nonce_len, 0); + sc_format_asn1_entry(rapdu_eac_gen_auth_ca_data + 1, auth_token, auth_token_len, 0); + + return sc_asn1_decode(ctx, rapdu_eac_gen_auth_ca, rdata, rdata_len, NULL, NULL); +} +static int eac_gen_auth_ca(sc_card_t *card, const BUF_MEM *eph_pub_key, BUF_MEM **nonce, BUF_MEM **token) { sc_apdu_t apdu; - NPA_GEN_AUTH_CA_C *c_data = NULL; - NPA_GEN_AUTH_CA_R *r_data = NULL; - unsigned char *d = NULL; + unsigned char *d = NULL, *p, *q; int r; - unsigned char resp[maxresp]; + unsigned char resp[SC_MAX_APDU_RESP_SIZE]; + BUF_MEM *nonce_out = NULL, *token_out = NULL; - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, ISO_GENERAL_AUTHENTICATE, - 0, 0); - - c_data = NPA_GEN_AUTH_CA_C_new(); - if (!c_data) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } - c_data->eph_pub_key = ASN1_OCTET_STRING_new(); - if (!c_data->eph_pub_key - || !M_ASN1_OCTET_STRING_set( c_data->eph_pub_key, - eph_pub_key->data, eph_pub_key->length)) { - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; - goto err; - } - r = i2d_NPA_GEN_AUTH_CA_C(c_data, &d); - if (r < 0) { - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; + if (!eph_pub_key) { + r = SC_ERROR_INVALID_ARGUMENTS; goto err; } - apdu.data = d; - apdu.datalen = r; - apdu.lc = r; - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "General authenticate (Perform Key Agreement) command data", apdu.data, apdu.datalen); + r = encode_gen_auth_cdata(card->ctx, (unsigned char *) eph_pub_key->data, eph_pub_key->length, NULL, 0, NULL, 0, NULL, 0, &d); + if (r < 0) + goto err; + sc_format_apdu_ex(&apdu, 0x00, ISO_GENERAL_AUTHENTICATE, 0, 0, d, r, resp, sizeof resp); - apdu.resplen = sizeof resp; - apdu.resp = resp; r = sc_transmit_apdu(card, &apdu); if (r < 0) goto err; @@ -1712,42 +1341,36 @@ static int npa_gen_auth_ca(sc_card_t *card, const BUF_MEM *eph_pub_key, if (r < 0) goto err; - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "General authenticate (Perform Key Agreement) response data", apdu.resp, apdu.resplen); - - if (!d2i_NPA_GEN_AUTH_CA_R(&r_data, - (const unsigned char **) &apdu.resp, apdu.resplen)) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not parse general authenticate response data."); - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; + nonce_out = BUF_MEM_new(); + token_out = BUF_MEM_new(); + if (!nonce_out || !token_out) { + r = SC_ERROR_NOT_ENOUGH_MEMORY; goto err; } - if (!r_data->nonce || !r_data->auth_token) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Response data of general authenticate for CA" - "should contain the nonce and the authentication token."); - r = SC_ERROR_UNKNOWN_DATA_RECEIVED; + p = (u8 *) nonce_out->data; + q = (u8 *) token_out->data; + r = decode_gen_auth_ca_rdata(card->ctx, apdu.resp, apdu.resplen, + &p, &nonce_out->length, + &q, &token_out->length); + nonce_out->data = (char *) p; + token_out->data = (char *) q; + if (r < 0) goto err; - } if (*nonce) BUF_MEM_free(*nonce); - *nonce = BUF_MEM_create_init(r_data->nonce->data, - r_data->nonce->length); if (*token) BUF_MEM_free(*token); - *token = BUF_MEM_create_init(r_data->auth_token->data, - r_data->auth_token->length); - if (!*nonce || !*token) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } + *nonce = nonce_out; + *token = token_out; + nonce_out = NULL; + token_out = NULL; err: - if (c_data) - NPA_GEN_AUTH_CA_C_free(c_data); - if (r_data) - NPA_GEN_AUTH_CA_R_free(r_data); - OPENSSL_free(d); + BUF_MEM_free(nonce_out); + BUF_MEM_free(token_out); + free(d); return r; } @@ -1764,7 +1387,7 @@ int perform_chip_authentication(sc_card_t *card, int r; BUF_MEM *picc_pubkey = NULL; struct iso_sm_ctx *isosmctx; - struct npa_sm_ctx *eacsmctx; + struct eac_sm_ctx *eacsmctx; if (!card || !ef_cardsecurity || !ef_cardsecurity_len) { r = SC_ERROR_INVALID_ARGUMENTS; @@ -1787,8 +1410,8 @@ int perform_chip_authentication(sc_card_t *card, } picc_pubkey = CA_get_pubkey(eacsmctx->ctx, *ef_cardsecurity, *ef_cardsecurity_len); if (!picc_pubkey) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not verify EF.CardSecurity."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1800,7 +1423,7 @@ int perform_chip_authentication(sc_card_t *card, BUF_MEM_clear_free(picc_pubkey); if (card) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, r); else return r; } @@ -1821,16 +1444,16 @@ int perform_chip_authentication_ex(sc_card_t *card, void *eac_ctx, picc_pubkey_buf = BUF_MEM_create_init(picc_pubkey, picc_pubkey_len); if (!picc_pubkey_buf) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not verify EF.CardSecurity."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } - r = npa_mse_set_at_ca(card, ctx->ca_ctx->protocol); + r = eac_mse_set_at_ca(card, ctx->ca_ctx->protocol); if (r < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not select protocol proberties " + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not select protocol properties " "(MSE: Set AT failed)."); goto err; } @@ -1838,12 +1461,12 @@ int perform_chip_authentication_ex(sc_card_t *card, void *eac_ctx, eph_pub_key = CA_STEP2_get_eph_pubkey(ctx); if (!eph_pub_key) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not derive keys."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } - r = npa_gen_auth_ca(card, eph_pub_key, &nonce, &token); + r = eac_gen_auth_ca(card, eph_pub_key, &nonce, &token); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "(General Authenticate failed)."); goto err; @@ -1851,16 +1474,16 @@ int perform_chip_authentication_ex(sc_card_t *card, void *eac_ctx, if (!CA_STEP4_compute_shared_secret(ctx, picc_pubkey_buf)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not compute shared secret."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } if (!CA_STEP6_derive_keys(ctx, nonce, token)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not derive keys."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1868,14 +1491,14 @@ int perform_chip_authentication_ex(sc_card_t *card, void *eac_ctx, /* Initialize secure channel */ if (!EAC_CTX_set_encryption_ctx(ctx, EAC_ID_CA)) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not initialize encryption."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } if (card->sm_ctx.sm_mode != SM_MODE_TRANSMIT) { - r = npa_sm_start(card, ctx, NULL, 0, NULL, 0); + r = eac_sm_start(card, ctx, NULL, 0); } err: @@ -1885,13 +1508,13 @@ int perform_chip_authentication_ex(sc_card_t *card, void *eac_ctx, BUF_MEM_clear_free(eph_pub_key); if (card) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, r); else return r; } static int -increment_ssc(struct npa_sm_ctx *eacsmctx) +increment_ssc(struct eac_sm_ctx *eacsmctx) { if (!eacsmctx) return SC_ERROR_INVALID_ARGUMENTS; @@ -1903,13 +1526,13 @@ increment_ssc(struct npa_sm_ctx *eacsmctx) } static int -npa_sm_encrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, +eac_sm_encrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, const u8 *data, size_t datalen, u8 **enc) { BUF_MEM *encbuf = NULL, *databuf = NULL; u8 *p = NULL; int r; - struct npa_sm_ctx *eacsmctx; + struct eac_sm_ctx *eacsmctx; if (!card || !ctx || !enc || !ctx->priv_data) { r = SC_ERROR_INVALID_ARGUMENTS; @@ -1919,9 +1542,9 @@ npa_sm_encrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, databuf = BUF_MEM_create_init(data, datalen); encbuf = EAC_encrypt(eacsmctx->ctx, databuf); - if (!databuf || !encbuf) { + if (!databuf || !encbuf || !encbuf->length) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not encrypt data."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1932,9 +1555,8 @@ npa_sm_encrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, goto err; } *enc = p; - /* Flawfinder: ignore */ memcpy(*enc, encbuf->data, encbuf->length); - r = encbuf->length; + r = (int)encbuf->length; err: BUF_MEM_clear_free(databuf); @@ -1945,13 +1567,13 @@ npa_sm_encrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, } static int -npa_sm_decrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, +eac_sm_decrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, const u8 *enc, size_t enclen, u8 **data) { BUF_MEM *encbuf = NULL, *databuf = NULL; u8 *p = NULL; int r; - struct npa_sm_ctx *eacsmctx; + struct eac_sm_ctx *eacsmctx; if (!card || !ctx || !enc || !ctx->priv_data || !data) { r = SC_ERROR_INVALID_ARGUMENTS; @@ -1961,9 +1583,9 @@ npa_sm_decrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, encbuf = BUF_MEM_create_init(enc, enclen); databuf = EAC_decrypt(eacsmctx->ctx, encbuf); - if (!encbuf || !databuf) { + if (!encbuf || !databuf || !databuf->length) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not decrypt data."); - ssl_error(card->ctx); + sc_log_openssl(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -1974,9 +1596,8 @@ npa_sm_decrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, goto err; } *data = p; - /* Flawfinder: ignore */ memcpy(*data, databuf->data, databuf->length); - r = databuf->length; + r = (int)databuf->length; err: BUF_MEM_clear_free(databuf); @@ -1987,13 +1608,13 @@ npa_sm_decrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, } static int -npa_sm_authenticate(sc_card_t *card, const struct iso_sm_ctx *ctx, +eac_sm_authenticate(sc_card_t *card, const struct iso_sm_ctx *ctx, const u8 *data, size_t datalen, u8 **macdata) { BUF_MEM *inbuf = NULL, *macbuf = NULL; u8 *p = NULL; int r; - struct npa_sm_ctx *eacsmctx; + struct eac_sm_ctx *eacsmctx; if (!card || !ctx || !ctx->priv_data || !macdata) { r = SC_ERROR_INVALID_ARGUMENTS; @@ -2008,10 +1629,10 @@ npa_sm_authenticate(sc_card_t *card, const struct iso_sm_ctx *ctx, } macbuf = EAC_authenticate(eacsmctx->ctx, inbuf); - if (!macbuf) { + if (!macbuf || !macbuf->length) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not compute message authentication code (MAC)."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -2022,9 +1643,8 @@ npa_sm_authenticate(sc_card_t *card, const struct iso_sm_ctx *ctx, goto err; } *macdata = p; - /* Flawfinder: ignore */ memcpy(*macdata, macbuf->data, macbuf->length); - r = macbuf->length; + r = (int)macbuf->length; err: if (inbuf) @@ -2036,13 +1656,13 @@ npa_sm_authenticate(sc_card_t *card, const struct iso_sm_ctx *ctx, } static int -npa_sm_verify_authentication(sc_card_t *card, const struct iso_sm_ctx *ctx, +eac_sm_verify_authentication(sc_card_t *card, const struct iso_sm_ctx *ctx, const u8 *mac, size_t maclen, const u8 *macdata, size_t macdatalen) { int r; BUF_MEM *inbuf = NULL, *my_mac = NULL; - struct npa_sm_ctx *eacsmctx; + struct eac_sm_ctx *eacsmctx; if (!card || !ctx || !ctx->priv_data) { r = SC_ERROR_INVALID_ARGUMENTS; @@ -2056,11 +1676,11 @@ npa_sm_verify_authentication(sc_card_t *card, const struct iso_sm_ctx *ctx, goto err; } - my_mac = EAC_authenticate(eacsmctx->ctx, inbuf); + my_mac = EAC_authenticate(eacsmctx->ctx, inbuf); if (!my_mac) { + sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not compute message authentication code (MAC) for verification."); - ssl_error(card->ctx); r = SC_ERROR_INTERNAL; goto err; } @@ -2073,7 +1693,7 @@ npa_sm_verify_authentication(sc_card_t *card, const struct iso_sm_ctx *ctx, goto err; } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Authentication data verified"); + sc_debug(card->ctx, SC_LOG_DEBUG_SM, "Authentication data verified"); r = SC_SUCCESS; @@ -2087,42 +1707,14 @@ npa_sm_verify_authentication(sc_card_t *card, const struct iso_sm_ctx *ctx, } static int -add_tag(unsigned char **asn1new, int constructed, int tag, - int xclass, const unsigned char *data, size_t len) -{ - unsigned char *p; - int newlen; - - if (!asn1new || !data) - return -1; - - newlen = ASN1_object_size(constructed, len, tag); - if (newlen < 0) - return newlen; - - p = OPENSSL_realloc(*asn1new, newlen); - if (!p) - return -1; - *asn1new = p; - - ASN1_put_object(&p, constructed, len, tag, xclass); - memcpy(p, data, len); - - return newlen; -} -static int -npa_sm_pre_transmit(sc_card_t *card, const struct iso_sm_ctx *ctx, +eac_sm_pre_transmit(sc_card_t *card, const struct iso_sm_ctx *ctx, sc_apdu_t *apdu) { int r; CVC_CERT *cvc_cert = NULL; unsigned char *cert = NULL; - int len; BUF_MEM *signature = NULL; unsigned char *sequence = NULL; - NPA_MSE_C *msesetat = NULL; - const unsigned char *p; - struct npa_sm_ctx *eacsmctx; if (!card) return SC_ERROR_INVALID_ARGUMENTS; @@ -2130,165 +1722,6 @@ npa_sm_pre_transmit(sc_card_t *card, const struct iso_sm_ctx *ctx, r = SC_ERROR_INVALID_ARGUMENTS; goto err; } - eacsmctx = ctx->priv_data; - - if (!(eacsmctx->flags & NPA_FLAG_DISABLE_CHECK_ALL)) { - if (apdu->ins == 0x2a && apdu->p1 == 0x00 && apdu->p2 == 0xbe) { - /* PSO:Verify Certificate - * check certificate description to match given certificate */ - - len = add_tag(&cert, 1, 0x21, V_ASN1_APPLICATION, apdu->data, apdu->datalen); - p = cert; - if (len < 0 || !CVC_d2i_CVC_CERT(&cvc_cert, &p, len) - || !cvc_cert || !cvc_cert->body) { - r = SC_ERROR_INVALID_DATA; - goto err; - } - - switch (CVC_get_role(cvc_cert->body->chat)) { - case CVC_CVCA: - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Processing CVCA certificate"); - break; - - case CVC_DV: - case CVC_DocVer: - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Processing DV certificate"); - break; - - case CVC_Terminal: - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Processing Terminal certificate"); - - if (eacsmctx->certificate_description) { - switch (CVC_check_description(cvc_cert, - (unsigned char *) eacsmctx->certificate_description->data, - eacsmctx->certificate_description->length)) { - case 1: - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, - "Certificate Description matches Certificate"); - break; - case 0: - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, - "Certificate Description doesn't match Certificate"); - r = SC_ERROR_INVALID_DATA; - goto err; - break; - default: - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, - "Error verifying Certificate Description"); - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; - goto err; - break; - } - } else { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, - "Warning: Certificate Description missing"); - } - break; - - default: - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Unknown type of certificate"); - r = SC_ERROR_INVALID_DATA; - goto err; - break; - } - - if (!TA_STEP2_import_certificate(eacsmctx->ctx, cert, len)) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, - "Error importing certificate"); - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; - goto err; - } - - } else if (apdu->ins == ISO_MSE && apdu->p2 == 0xa4) { - /* MSE:Set AT */ - - len = add_tag(&sequence, 1, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL, apdu->data, apdu->datalen); - p = sequence; - if (len < 0 || !d2i_NPA_MSE_C(&msesetat, &p, len)) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not parse MSE:Set AT."); - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; - goto err; - } - - if (apdu->p1 == 0x81) { - /* CA: fetch auxiliary data and terminal's compressed ephemeral - * public key */ - - if (msesetat->auxiliary_data) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Saving terminal's auxiliary data"); - if (eacsmctx->auxiliary_data) - BUF_MEM_free(eacsmctx->auxiliary_data); - eacsmctx->auxiliary_data = BUF_MEM_new(); - if (!eacsmctx->auxiliary_data) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } - eacsmctx->auxiliary_data->length = i2d_ASN1_AUXILIARY_DATA( - msesetat->auxiliary_data, - (unsigned char **) &eacsmctx->auxiliary_data->data); - if ((int) eacsmctx->auxiliary_data->length < 0) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error encoding auxiliary data."); - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; - goto err; - } - eacsmctx->auxiliary_data->max = eacsmctx->auxiliary_data->length; - } - if (msesetat->eph_pub_key) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Saving terminal's compressed ephemeral public key"); - if (eacsmctx->eph_pub_key) - BUF_MEM_free(eacsmctx->eph_pub_key); - eacsmctx->eph_pub_key = - BUF_MEM_create_init(msesetat->eph_pub_key->data, - msesetat->eph_pub_key->length); - if (!eacsmctx->eph_pub_key) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } - } - } else if (apdu->p1 == 0x41) { - /* TA: Set CAR */ - - if (msesetat->key_reference1 && msesetat->key_reference1->data && - msesetat->key_reference1->length) { - /* do nothing. The trust anchor matching this CAR will be - * looked up when the certificate chain is imported */ - } - } - } else if (apdu->ins == 0x82 && apdu->p1 == 0x00 && apdu->p2 == 0x00) { - /* External Authenticate - * check terminal's signature */ - - signature = BUF_MEM_create_init(apdu->data, apdu->datalen); - if (!signature) { - r = SC_ERROR_OUT_OF_MEMORY; - goto err; - } - switch (TA_STEP6_verify(eacsmctx->ctx, eacsmctx->eph_pub_key, - eacsmctx->id_icc, eacsmctx->auxiliary_data, signature)) { - case 1: - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, - "Verified Terminal's signature"); - break; - case 0: - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, - "Terminal's signature not verified"); - r = SC_ERROR_INVALID_DATA; - goto err; - break; - default: - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, - "Error verifying terminal's signature"); - ssl_error(card->ctx); - r = SC_ERROR_INTERNAL; - goto err; - break; - } - } - } r = increment_ssc(ctx->priv_data); @@ -2301,74 +1734,46 @@ npa_sm_pre_transmit(sc_card_t *card, const struct iso_sm_ctx *ctx, OPENSSL_free(cert); if (sequence) OPENSSL_free(sequence); - if (msesetat) - NPA_MSE_C_free(msesetat); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, r); } static int -npa_sm_post_transmit(sc_card_t *card, const struct iso_sm_ctx *ctx, +eac_sm_post_transmit(sc_card_t *card, const struct iso_sm_ctx *ctx, sc_apdu_t *sm_apdu) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, increment_ssc(ctx->priv_data)); } static int -npa_sm_finish(sc_card_t *card, const struct iso_sm_ctx *ctx, +eac_sm_finish(sc_card_t *card, const struct iso_sm_ctx *ctx, sc_apdu_t *apdu) { - struct npa_sm_ctx *eacsmctx; if (!card) return SC_ERROR_INVALID_ARGUMENTS; if(!ctx || !ctx->priv_data || !apdu) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, SC_ERROR_INVALID_ARGUMENTS); - eacsmctx = ctx->priv_data; - if (!(eacsmctx->flags & NPA_FLAG_DISABLE_CHECK_ALL)) { - if (apdu->sw1 == 0x90 && apdu->sw2 == 0x00) { - if (apdu->ins == 0x84 && apdu->p1 == 0x00 && apdu->p2 == 0x00 - && apdu->le == 8 && apdu->resplen == 8) { - BUF_MEM *nonce; - int r; - /* Get Challenge - * copy challenge to EAC context */ - - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Saving MRTD's nonce to later verify Terminal's signature"); - - nonce = BUF_MEM_create_init(apdu->resp, apdu->resplen); - r = TA_STEP4_set_nonce(eacsmctx->ctx, nonce); - if (nonce) - BUF_MEM_free(nonce); - - if (!r) { - ssl_error(card->ctx); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); - } - } - } - } - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, SC_SUCCESS); } static void -npa_sm_clear_free(const struct iso_sm_ctx *ctx) +eac_sm_clear_free(const struct iso_sm_ctx *ctx) { if (ctx) { - struct npa_sm_ctx *eacsmctx = ctx->priv_data; - EAC_CTX_clear_free(eacsmctx->ctx); - if (eacsmctx->certificate_description) - BUF_MEM_free(eacsmctx->certificate_description); - if (eacsmctx->id_icc) - BUF_MEM_free(eacsmctx->id_icc); - if (eacsmctx->eph_pub_key) - BUF_MEM_free(eacsmctx->eph_pub_key); - if (eacsmctx->auxiliary_data) - BUF_MEM_free(eacsmctx->auxiliary_data); - free(eacsmctx); + struct eac_sm_ctx *eacsmctx = ctx->priv_data; + if (eacsmctx) { + EAC_CTX_clear_free(eacsmctx->ctx); + if (eacsmctx->id_icc) + BUF_MEM_free(eacsmctx->id_icc); + if (eacsmctx->eph_pub_key) + BUF_MEM_free(eacsmctx->eph_pub_key); + if (eacsmctx->auxiliary_data) + BUF_MEM_free(eacsmctx->auxiliary_data); + free(eacsmctx); + } } } @@ -2381,7 +1786,10 @@ int perform_pace(sc_card_t *card, { int r; - if (card && card->reader + if (!card) + return SC_ERROR_INVALID_ARGUMENTS; + + if (card->reader && card->reader->capabilities & SC_READER_CAP_PACE_GENERIC && card->reader->ops->perform_pace) { r = card->reader->ops->perform_pace(card->reader, &pace_input, pace_output); @@ -2389,7 +1797,7 @@ int perform_pace(sc_card_t *card, r = SC_ERROR_NOT_SUPPORTED; } - return r; + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, r); } int perform_terminal_authentication(sc_card_t *card, @@ -2397,19 +1805,19 @@ int perform_terminal_authentication(sc_card_t *card, const unsigned char *privkey, size_t privkey_len, const unsigned char *auxiliary_data, size_t auxiliary_data_len) { - return SC_ERROR_NOT_SUPPORTED; + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, SC_ERROR_NOT_SUPPORTED); } int perform_chip_authentication(sc_card_t *card, unsigned char **ef_cardsecurity, size_t *ef_cardsecurity_len) { - return SC_ERROR_NOT_SUPPORTED; + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, SC_ERROR_NOT_SUPPORTED); } int perform_chip_authentication_ex(sc_card_t *card, void *eac_ctx, unsigned char *picc_pubkey, size_t picc_pubkey_len) { - return SC_ERROR_NOT_SUPPORTED; + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, SC_ERROR_NOT_SUPPORTED); } #endif @@ -2419,7 +1827,7 @@ static const char *PIN_name = "eID PIN"; static const char *PUK_name = "PUK"; static const char *CAN_name = "CAN"; static const char *UNDEF_name = "UNDEF"; -const char *npa_secret_name(enum s_type pin_id) { +const char *eac_secret_name(enum s_type pin_id) { switch (pin_id) { case PACE_MRZ: return MRZ_name; @@ -2434,76 +1842,7 @@ const char *npa_secret_name(enum s_type pin_id) { } } -int -npa_reset_retry_counter(sc_card_t *card, enum s_type pin_id, - int ask_for_secret, const char *new, size_t new_len) -{ - sc_apdu_t apdu; - char *p = NULL; - int r; - - if (ask_for_secret && (!new || !new_len)) { - if (!(SC_READER_CAP_PIN_PAD & card->reader->capabilities)) { -#if OPENSSL_VERSION_NUMBER >= 0x10000000L - p = malloc(MAX_PIN_LEN+1); - if (!p) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Not enough memory for new PIN.\n"); - return SC_ERROR_OUT_OF_MEMORY; - } - if (0 > EVP_read_pw_string_min(p, - MIN_PIN_LEN, MAX_PIN_LEN+1, - "Please enter your new PIN: ", 0)) { - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not read new PIN.\n"); - free(p); - return SC_ERROR_INTERNAL; - } - new_len = strlen(p); - if (new_len > MAX_PIN_LEN) - return SC_ERROR_INVALID_PIN_LENGTH; - new = p; -#else - return SC_ERROR_NOT_SUPPORTED; -#endif - } - } - - sc_format_apdu(card, &apdu, 0, 0x2C, 0, pin_id); - apdu.data = (u8 *) new; - apdu.datalen = new_len; - apdu.lc = apdu.datalen; - - if (new_len || ask_for_secret) { - apdu.p1 = 0x02; - apdu.cse = SC_APDU_CASE_3_SHORT; - } else { - apdu.p1 = 0x03; - apdu.cse = SC_APDU_CASE_1; - } - - if (ask_for_secret && !new_len) { - struct sc_pin_cmd_data data; - data.apdu = &apdu; - data.cmd = SC_PIN_CMD_CHANGE; - data.flags = SC_PIN_CMD_IMPLICIT_CHANGE; - data.pin2.encoding = SC_PIN_ENCODING_ASCII; - data.pin2.length_offset = 0; - data.pin2.offset = 5; - data.pin2.max_length = MAX_PIN_LEN; - data.pin2.min_length = MIN_PIN_LEN; - data.pin2.pad_length = 0; - r = card->reader->ops->perform_verify(card->reader, &data); - } else - r = sc_transmit_apdu(card, &apdu); - - if (p) { - sc_mem_clear(p, new_len); - free(p); - } - - return r; -} - -int npa_pace_get_tries_left(sc_card_t *card, +int eac_pace_get_tries_left(sc_card_t *card, enum s_type pin_id, int *tries_left) { int r; @@ -2511,10 +1850,10 @@ int npa_pace_get_tries_left(sc_card_t *card, if (tries_left) { #if defined(ENABLE_OPENPACE) && defined(ENABLE_SM) - r = npa_mse_set_at_pace(card, 0, pin_id, 0, &sw1, &sw2); + r = eac_mse_set_at_pace(card, 0, pin_id, 0, &sw1, &sw2); #else sc_apdu_t apdu; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, ISO_MSE, 0xC1, 0xA4); + sc_format_apdu_ex(&apdu, 0x00, ISO_MSE, 0xC1, 0xA4, NULL, 0, NULL, 0); r = sc_transmit_apdu(card, &apdu); sw1 = apdu.sw1; sw2 = apdu.sw2; @@ -2531,14 +1870,3 @@ int npa_pace_get_tries_left(sc_card_t *card, return r; } - -int get_pace_capabilities(u8 *bitmap) -{ - if (!bitmap) - return SC_ERROR_INVALID_ARGUMENTS; - - /* BitMap */ - *bitmap = NPA_BITMAP_PACE|NPA_BITMAP_EID|NPA_BITMAP_ESIGN; - - return SC_SUCCESS; -} diff --git a/src/sm/sm-eac.h b/src/sm/sm-eac.h index fcb35773f2..dff65aa4f7 100644 --- a/src/sm/sm-eac.h +++ b/src/sm/sm-eac.h @@ -15,11 +15,11 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file - * @defgroup npa Interface to German identity card (neuer Personalausweis, nPA) + * @defgroup eac Interface to Extended Access Control * @{ */ #ifndef _SC_EAC_H @@ -80,50 +80,6 @@ enum eac_tr_version { }; #endif -/** @brief NPA capabilities (TR-03119): PACE */ -#define NPA_BITMAP_PACE 0x40 -/** @brief NPA capabilities (TR-03119): EPA: eID */ -#define NPA_BITMAP_EID 0x20 -/** @brief NPA capabilities (TR-03119): EPA: eSign */ -#define NPA_BITMAP_ESIGN 0x10 - -/** @brief NPA result (TR-03119): Kein Fehler */ -#define NPA_SUCCESS 0x00000000 -/** @brief NPA result (TR-03119): Längen im Input sind inkonsistent */ -#define NPA_ERROR_LENGTH_INCONSISTENT 0xD0000001 -/** @brief NPA result (TR-03119): Unerwartete Daten im Input */ -#define NPA_ERROR_UNEXPECTED_DATA 0xD0000002 -/** @brief NPA result (TR-03119): Unerwartete Kombination von Daten im Input */ -#define NPA_ERROR_UNEXPECTED_DATA_COMBINATION 0xD0000003 -/** @brief NPA result (TR-03119): Die Karte unterstützt das PACE – Verfahren nicht. (Unerwartete Struktur in Antwortdaten der Karte) */ -#define NPA_ERROR_CARD_NOT_SUPPORTED 0xE0000001 -/** @brief NPA result (TR-03119): Der Kartenleser unterstützt den angeforderten bzw. den ermittelten Algorithmus nicht. */ -#define NPA_ERROR_ALGORITH_NOT_SUPPORTED 0xE0000002 -/** @brief NPA result (TR-03119): Der Kartenleser kennt die PIN – ID nicht. */ -#define NPA_ERROR_PINID_NOT_SUPPORTED 0xE0000003 -/** @brief NPA result (TR-03119): Negative Antwort der Karte auf Select EF_CardAccess (needs to be OR-ed with SW1|SW2) */ -#define NPA_ERROR_SELECT_EF_CARDACCESS 0xF0000000 -/** @brief NPA result (TR-03119): Negative Antwort der Karte auf Read Binary (needs to be OR-ed with SW1|SW2) */ -#define NPA_ERROR_READ_BINARY 0xF0010000 -/** @brief NPA result (TR-03119): Negative Antwort der Karte auf MSE: Set AT (needs to be OR-ed with SW1|SW2) */ -#define NPA_ERROR_MSE_SET_AT 0xF0020000 -/** @brief NPA result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 1 (needs to be OR-ed with SW1|SW2) */ -#define NPA_ERROR_GENERAL_AUTHENTICATE_1 0xF0030000 -/** @brief NPA result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 2 (needs to be OR-ed with SW1|SW2) */ -#define NPA_ERROR_GENERAL_AUTHENTICATE_2 0xF0040000 -/** @brief NPA result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 3 (needs to be OR-ed with SW1|SW2) */ -#define NPA_ERROR_GENERAL_AUTHENTICATE_3 0xF0050000 -/** @brief NPA result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 4 (needs to be OR-ed with SW1|SW2) */ -#define NPA_ERROR_GENERAL_AUTHENTICATE_4 0xF0060000 -/** @brief NPA result (TR-03119): Kommunikationsabbruch mit Karte. */ -#define NPA_ERROR_COMMUNICATION 0xF0100001 -/** @brief NPA result (TR-03119): Keine Karte im Feld. */ -#define NPA_ERROR_NO_CARD 0xF0100002 -/** @brief NPA result (TR-03119): Benutzerabbruch. */ -#define NPA_ERROR_ABORTED 0xF0200001 -/** @brief NPA result (TR-03119): Benutzer – Timeout */ -#define NPA_ERROR_TIMEOUT 0xF0200002 - /** @brief File identifier of EF.CardAccess */ #define FID_EF_CARDACCESS 0x011C /** @brief Short file identifier of EF.CardAccess */ @@ -134,17 +90,17 @@ enum eac_tr_version { #define SFID_EF_CARDSECURITY 0x1D /** @brief Maximum length of PIN */ -#define MAX_PIN_LEN 6 +#define EAC_MAX_PIN_LEN 6 /** @brief Minimum length of PIN */ -#define MIN_PIN_LEN 6 +#define EAC_MIN_PIN_LEN 6 /** @brief Length of CAN */ -#define CAN_LEN 6 +#define EAC_CAN_LEN 6 /** @brief Minimum length of MRZ */ -#define MAX_MRZ_LEN 128 +#define EAC_MAX_MRZ_LEN 128 /** @brief Number of retries for PIN */ -#define MAX_PIN_TRIES 3 +#define EAC_MAX_PIN_TRIES 3 /** @brief Usage counter of PIN in suspended state */ -#define UC_PIN_SUSPENDED 1 +#define EAC_UC_PIN_SUSPENDED 1 /** @@ -154,18 +110,7 @@ enum eac_tr_version { * * @return Printable string containing the name */ -const char *npa_secret_name(enum s_type pin_id); - - -/** - * @brief Get the PACE capabilities - * - * @param[in,out] bitmap where to store capabilities bitmap - * @note Since this code offers no support for terminal certificate, the bitmap is always \c PACE_BITMAP_PACE|PACE_BITMAP_EID - * - * @return \c SC_SUCCESS or error code if an error occurred - */ -int get_pace_capabilities(u8 *bitmap); +const char *eac_secret_name(enum s_type pin_id); /** * @brief Establish secure messaging using PACE @@ -233,26 +178,6 @@ int perform_chip_authentication(sc_card_t *card, int perform_chip_authentication_ex(sc_card_t *card, void *eacsmctx, unsigned char *picc_pubkey, size_t picc_pubkey_len); -/** - * @brief Sends a reset retry counter APDU - * - * According to TR-03110 the reset retry counter APDU is used to set a new PIN - * or to reset the retry counter of the PIN. The standard requires this - * operation to be authorized either by an established PACE channel or by the - * effective authorization of the terminal's certificate. - * - * @param[in] card - * @param[in] pin_id Type of secret (usually PIN or CAN). You may use enum s_type from \c . - * @param[in] ask_for_secret whether to ask the user for the secret (\c 1) or not (\c 0) - * @param[in] new (optional) new secret - * @param[in] new_len (optional) length of \a new - * - * @return \c SC_SUCCESS or error code if an error occurred - */ -int npa_reset_retry_counter(sc_card_t *card, - enum s_type pin_id, int ask_for_secret, - const char *new, size_t new_len); - /** * @brief Sends an MSE:Set AT to determine the number of remaining tries * @@ -262,34 +187,16 @@ int npa_reset_retry_counter(sc_card_t *card, * * @return \c SC_SUCCESS or error code if an error occurred */ -int npa_pace_get_tries_left(sc_card_t *card, +int eac_pace_get_tries_left(sc_card_t *card, enum s_type pin_id, int *tries_left); -/** - * @brief Send APDU to unblock the PIN - * - * @param[in] card - */ -#define npa_unblock_pin(card) \ - npa_reset_retry_counter(card, PACE_PIN, 0, NULL, 0) -/** - * @brief Send APDU to set a new PIN - * - * @param[in] card - * @param[in] newp (optional) new PIN - * @param[in] newplen (optional) length of \a new - */ -#define npa_change_pin(card, newp, newplen) \ - npa_reset_retry_counter(card, PACE_PIN, 1, newp, newplen) -/** @brief Disable all sanity checks done by libnpa */ -#define NPA_FLAG_DISABLE_CHECK_ALL 1 /** @brief Disable checking validity period of CV certificates */ -#define NPA_FLAG_DISABLE_CHECK_TA 2 +#define EAC_FLAG_DISABLE_CHECK_TA 2 /** @brief Disable checking passive authentication during CA */ -#define NPA_FLAG_DISABLE_CHECK_CA 4 +#define EAC_FLAG_DISABLE_CHECK_CA 4 -/** @brief Use \c npa_default_flags to disable checks for EAC/SM */ -extern char npa_default_flags; +/** @brief Use \c eac_default_flags to disable checks for EAC/SM */ +extern char eac_default_flags; #ifdef __cplusplus } diff --git a/src/sm/sm-iso-internal.h b/src/sm/sm-iso-internal.h index 9d8e9d09e8..8f3d7e22df 100644 --- a/src/sm/sm-iso-internal.h +++ b/src/sm/sm-iso-internal.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file @@ -72,7 +72,7 @@ int iso_get_sm_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu * int iso_free_sm_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu); /** - * @brief Cleans up allocated ressources of the ISO SM driver + * @brief Cleans up allocated resources of the ISO SM driver * * \c iso_sm_close() is designed as SM card operation. However, have in mind * that this card operation is not called automatically for \c diff --git a/src/sm/sm-iso.c b/src/sm/sm-iso.c index 2f419cbfb9..49f0cedbe7 100644 --- a/src/sm/sm-iso.c +++ b/src/sm/sm-iso.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -55,23 +55,22 @@ static const struct sc_asn1_entry c_sm_rapdu[] = { }; static int -add_iso_pad(const u8 *data, size_t datalen, int block_size, u8 **padded) +add_iso_pad(const u8 *data, size_t datalen, size_t block_size, u8 **padded) { u8 *p; - size_t p_len; + int p_len; if (!padded) return SC_ERROR_INVALID_ARGUMENTS; /* calculate length of padded message */ - p_len = (datalen / block_size) * block_size + block_size; + p_len = (int)((datalen / block_size) * block_size + block_size); p = realloc(*padded, p_len); if (!p) return SC_ERROR_OUT_OF_MEMORY; if (*padded != data) - /* Flawfinder: ignore */ memcpy(p, data, datalen); *padded = p; @@ -92,14 +91,17 @@ add_padding(const struct iso_sm_ctx *ctx, const u8 *data, size_t datalen, switch (ctx->padding_indicator) { case SM_NO_PADDING: if (*padded != data) { - p = realloc(*padded, datalen); - if (!p) - return SC_ERROR_OUT_OF_MEMORY; - *padded = p; - /* Flawfinder: ignore */ - memcpy(*padded, data, datalen); + if (datalen != 0) { + p = realloc(*padded, datalen); + if (!p) + return SC_ERROR_OUT_OF_MEMORY; + *padded = p; + memcpy(*padded, data, datalen); + } else { + *padded = NULL; + } } - return datalen; + return (int)datalen; case SM_ISO_PADDING: return add_iso_pad(data, datalen, ctx->block_length, padded); default: @@ -138,7 +140,7 @@ rm_padding(u8 padding_indicator, const u8 *data, size_t datalen) return SC_ERROR_NOT_SUPPORTED; } - return len; + return (int)len; } static int format_le(size_t le, struct sc_asn1_entry *le_entry, @@ -146,7 +148,7 @@ static int format_le(size_t le, struct sc_asn1_entry *le_entry, { u8 *p; - if (!lebuf || !le_len) + if (!lebuf || !le_len || !*le_len) return SC_ERROR_INVALID_ARGUMENTS; p = realloc(*lebuf, *le_len); @@ -156,15 +158,15 @@ static int format_le(size_t le, struct sc_asn1_entry *le_entry, switch (*le_len) { case 1: - p[0] = le; + p[0] = le & 0xff; break; case 2: - p[0] = le >> 8; + p[0] = (le >> 8) & 0xff; p[1] = le & 0xff; break; case 3: p[0] = 0x00; - p[1] = le >> 8; + p[1] = (le >> 8) & 0xff; p[2] = le & 0xff; break; default: @@ -178,23 +180,23 @@ static int format_le(size_t le, struct sc_asn1_entry *le_entry, static int prefix_buf(u8 prefix, u8 *buf, size_t buflen, u8 **cat) { - u8 *p; + u8 *p = NULL; + int ptr_same = *cat == buf; p = realloc(*cat, buflen + 1); if (!p) return SC_ERROR_OUT_OF_MEMORY; - if (*cat == buf) { + if (ptr_same) { memmove(p + 1, p, buflen); } else { - /* Flawfinder: ignore */ memcpy(p + 1, buf, buflen); } p[0] = prefix; *cat = p; - return buflen + 1; + return (int)buflen + 1; } static int format_data(sc_card_t *card, const struct iso_sm_ctx *ctx, @@ -219,13 +221,13 @@ static int format_data(sc_card_t *card, const struct iso_sm_ctx *ctx, } pad_data_len = r; - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Data to encrypt", pad_data, pad_data_len); + sc_log_hex(card->ctx, "Data to encrypt", pad_data, pad_data_len); r = ctx->encrypt(card, ctx, pad_data, pad_data_len, formatted_data); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not encrypt the data"); goto err; } - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Cryptogram", *formatted_data, r); + sc_log_hex(card->ctx, "Cryptogram", *formatted_data, r); if (prepend_padding_indicator) { r = prefix_buf(ctx->padding_indicator, *formatted_data, r, formatted_data); @@ -279,7 +281,7 @@ static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, u8 *p, *le = NULL, *sm_data = NULL, *fdata = NULL, *mac_data = NULL, *asn1 = NULL, *mac = NULL, *resp_data = NULL; size_t sm_data_len, fdata_len, mac_data_len, asn1_len, mac_len, le_len; - int r, cse; + int r; sc_apdu_t *sm_apdu = NULL; if (!apdu || !ctx || !card || !card->reader || !psm_apdu) { @@ -313,17 +315,7 @@ static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, } mac_data_len = r; - /* get le and data depending on the case of the insecure command */ - cse = apdu->cse; - if ((apdu->le/ctx->block_length + 1)*ctx->block_length + 18 > 0xff+1) - /* for encrypted APDUs we usually get authenticated status bytes (4B), - * a MAC (11B) and a cryptogram with padding indicator (3B without - * data). The cryptogram is always padded to the block size. */ - /*cse |= SC_APDU_EXT;*/ - sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, - "Response data may be truncated, because it doesn't fit into a short length APDU."); - - switch (cse) { + switch (apdu->cse) { case SC_APDU_CASE_1: break; case SC_APDU_CASE_2_SHORT: @@ -333,7 +325,7 @@ static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format Le of SM apdu"); goto err; } - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Protected Le (plain)", le, le_len); + sc_log_hex(card->ctx, "Protected Le (plain)", le, le_len); break; case SC_APDU_CASE_2_EXT: if (card->reader->active_protocol == SC_PROTO_T0) { @@ -353,7 +345,7 @@ static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, goto err; } } - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Protected Le (plain)", le, le_len); + sc_log_hex(card->ctx, "Protected Le (plain)", le, le_len); break; case SC_APDU_CASE_3_SHORT: case SC_APDU_CASE_3_EXT: @@ -368,7 +360,7 @@ static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format data of SM apdu"); goto err; } - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Padding-content indicator followed by cryptogram (plain)", + sc_log_hex(card->ctx, "Padding-content indicator followed by cryptogram (plain)", fdata, fdata_len); break; case SC_APDU_CASE_4_SHORT: @@ -380,7 +372,7 @@ static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format Le of SM apdu"); goto err; } - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Protected Le (plain)", le, le_len); + sc_log_hex(card->ctx, "Protected Le (plain)", le, le_len); } if (apdu->ins & 1) { @@ -394,7 +386,7 @@ static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format data of SM apdu"); goto err; } - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Padding-content indicator followed by cryptogram (plain)", + sc_log_hex(card->ctx, "Padding-content indicator followed by cryptogram (plain)", fdata, fdata_len); break; case SC_APDU_CASE_4_EXT: @@ -411,7 +403,7 @@ static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format Le of SM apdu"); goto err; } - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Protected Le (plain)", le, le_len); + sc_log_hex(card->ctx, "Protected Le (plain)", le, le_len); } if (apdu->ins & 1) { @@ -425,7 +417,7 @@ static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format data of SM apdu"); goto err; } - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Padding-content indicator followed by cryptogram (plain)", + sc_log_hex(card->ctx, "Padding-content indicator followed by cryptogram (plain)", fdata, fdata_len); break; default: @@ -446,7 +438,6 @@ static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, goto err; } mac_data = p; - /* Flawfinder: ignore */ memcpy(mac_data + mac_data_len, asn1, asn1_len); mac_data_len += asn1_len; r = add_padding(ctx, mac_data, mac_data_len, &mac_data); @@ -455,7 +446,7 @@ static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, } mac_data_len = r; } - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Data to authenticate", mac_data, mac_data_len); + sc_log_hex(card->ctx, "Data to authenticate", mac_data, mac_data_len); r = ctx->authenticate(card, ctx, mac_data, mac_data_len, &mac); @@ -464,7 +455,7 @@ static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, goto err; } mac_len = r; - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Cryptographic Checksum (plain)", mac, mac_len); + sc_log_hex(card->ctx, "Cryptographic Checksum (plain)", mac, mac_len); /* format SM apdu */ @@ -476,28 +467,28 @@ static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, sm_apdu->datalen = sm_data_len; sm_apdu->lc = sm_data_len; sm_apdu->le = 0; - if (cse & SC_APDU_EXT) { + /* for encrypted APDUs we usually get authenticated status bytes (4B), a + * MAC (2B without data) and a cryptogram with padding indicator (2B tag + * and indicator, max. 2B/3B ASN.1 length, without data). The cryptogram is + * always padded to the block size. */ + if (apdu->cse & SC_APDU_EXT) { sm_apdu->cse = SC_APDU_CASE_4_EXT; -#if OPENSC_NOT_BOGUS_ANYMORE - sm_apdu->resplen = 0xffff+1; -#else - sm_apdu->resplen = SC_MAX_EXT_APDU_BUFFER_SIZE; -#endif + sm_apdu->resplen = 4 + 2 + mac_len + 2 + 3 + ((apdu->resplen+1)/ctx->block_length+1)*ctx->block_length; + if (sm_apdu->resplen > SC_MAX_EXT_APDU_RESP_SIZE) + sm_apdu->resplen = SC_MAX_EXT_APDU_RESP_SIZE; } else { sm_apdu->cse = SC_APDU_CASE_4_SHORT; -#if OPENSC_NOT_BOGUS_ANYMORE - sm_apdu->resplen = 0xff+1; -#else - sm_apdu->resplen = SC_MAX_APDU_BUFFER_SIZE; -#endif + sm_apdu->resplen = 4 + 2 + mac_len + 2 + 2 + ((apdu->resplen+1)/ctx->block_length+1)*ctx->block_length; + if (sm_apdu->resplen > SC_MAX_APDU_RESP_SIZE) + sm_apdu->resplen = SC_MAX_APDU_RESP_SIZE; } - resp_data = malloc(sm_apdu->resplen); + resp_data = calloc(1, sm_apdu->resplen); if (!resp_data) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } sm_apdu->resp = resp_data; - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "ASN.1 encoded encrypted APDU data", sm_apdu->data, sm_apdu->datalen); + sc_log_hex(card->ctx, "ASN.1 encoded encrypted APDU data", sm_apdu->data, sm_apdu->datalen); *psm_apdu = sm_apdu; @@ -556,7 +547,7 @@ static int sm_decrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, if (r < 0) { goto err; } - + r = ctx->verify_authentication(card, ctx, mac, mac_len, mac_data, r); if (r < 0) @@ -597,7 +588,6 @@ static int sm_decrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, r = SC_ERROR_OUT_OF_MEMORY; goto err; } - /* Flawfinder: ignore */ memcpy(apdu->resp, data, r); apdu->resplen = r; } else { @@ -618,9 +608,9 @@ static int sm_decrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, goto err; } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Decrypted APDU sw1=%02x sw2=%02x", + sc_log(card->ctx, "Decrypted APDU sw1=%02x sw2=%02x", apdu->sw1, apdu->sw2); - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Decrypted APDU response data", + sc_log_hex(card->ctx, "Decrypted APDU response data", apdu->resp, apdu->resplen); r = SC_SUCCESS; @@ -644,15 +634,15 @@ static int iso_add_sm(struct iso_sm_ctx *sctx, sc_card_t *card, if ((apdu->cla & 0x0C) == 0x0C) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Given APDU is already protected with some secure messaging. Closing own SM context."); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_sm_stop(card), + LOG_TEST_RET(card->ctx, sc_sm_stop(card), "Could not close ISO SM session"); return SC_ERROR_SM_NOT_APPLIED; } if (sctx->pre_transmit) - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sctx->pre_transmit(card, sctx, apdu), + LOG_TEST_RET(card->ctx, sctx->pre_transmit(card, sctx, apdu), "Could not complete SM specific pre transmit routine"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sm_encrypt(sctx, card, apdu, sm_apdu), + LOG_TEST_RET(card->ctx, sm_encrypt(sctx, card, apdu, sm_apdu), "Could not encrypt APDU"); return SC_SUCCESS; @@ -661,13 +651,17 @@ static int iso_add_sm(struct iso_sm_ctx *sctx, sc_card_t *card, static int iso_rm_sm(struct iso_sm_ctx *sctx, sc_card_t *card, sc_apdu_t *sm_apdu, sc_apdu_t *apdu) { + if (!sctx) + LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, + "Invalid SM context. No SM processing performed."); + if (sctx->post_transmit) - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sctx->post_transmit(card, sctx, sm_apdu), + LOG_TEST_RET(card->ctx, sctx->post_transmit(card, sctx, sm_apdu), "Could not complete SM specific post transmit routine"); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sm_decrypt(sctx, card, sm_apdu, apdu), + LOG_TEST_RET(card->ctx, sm_decrypt(sctx, card, sm_apdu, apdu), "Could not decrypt APDU"); if (sctx->finish) - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sctx->finish(card, sctx, apdu), + LOG_TEST_RET(card->ctx, sctx->finish(card, sctx, apdu), "Could not complete SM specific post transmit routine"); return SC_SUCCESS; diff --git a/src/sm/sm-iso.h b/src/sm/sm-iso.h index 12c6535d47..88a2778696 100644 --- a/src/sm/sm-iso.h +++ b/src/sm/sm-iso.h @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file @@ -50,7 +50,112 @@ extern "C" { /** @brief Padding indicator: use no padding */ #define SM_NO_PADDING 0x02 -/** @brief Secure messaging context */ +/** @brief Secure messaging context + * + * This module provides *encoding and decoding* of secure messaging APDUs. The + * actual cryptographic operations need to be specified via the call backs of + * `struct iso_sm_ctx`. + * + * + * Initialization of ISO 7816 Secure Messaging: + * 1. Create the secure messaging context with iso_sm_ctx_create() + * 2. Customize `struct iso_sm_ctx` with the needed cryptographic callbacks + * and data + * 3. Run `iso_sm_start()`, which enables `SM_MODE_TRANSMIT`, so that all + * subsequent calls to sc_transmit_apdu() will be encrypted transparently. + * Memory ownership of `struct iso_sm_ctx` is transferred to the internal + * secure messaging context. + * + * + * Deinitialization of ISO 7816 Secure Messaging: + * 1. Run `sc_sm_stop()` + * 2. `clear_free()` hook is called + * 3. `struct iso_sm_ctx` is `free()`d + * + * + * Sending and receiving ISO 7816 Secure Messaging data: + * 1. Call `sc_transmit_apdu()` with an unencrypted APDU + * 2. `pre_transmit()` hook is called + * 3. Command APDU is encrypted (see workflow below) + * 4. Encrypted APDU is sent to the card + * 5. `post_transmit()` hook is called + * 6. Encrypted response is decrypted (see workflow below) + * 7. `finish()` hook is called + * + * + * Workflow for encrypting a command APDU: + * + * ┌───────────────┬────┬──────┬────┠+ * │ Header │ │ │ │ + * â–¶ Unencrypted command APDU │ CLA,INS,P1,P2 │ Lc │ Data │ Le │ + * └───────────────┴────┴──────┴────┘ + * ╱ ╱ │ ╲ + * 1. Add padding to `block_size` according to `padding_indicator` + * ╱ ╱ │ ╲ + * ╱ ╱ â–¼ â—¢ + * ╱ ╱ ┌───────────┠+ * ╱ ╱ │Padded Data│ + * ╱ ╱ └───────────┘ + * ╱ ╱ │ ╲ + * 2. Data encryption ╱ ╱ │ `encrypt()` ╲ + * ╱ ╱ â–¼ â—¢ + * ╱ ┌────┬──────┬─────────────────┬───────────────┬────┬──────┬──┠+ * ╱ │0x87│Length│Padding Indicator│Encrypted Data │0x97│Length│Le│ + * ╱ └────┴──────┴─────────────────┴───────────────┴────┴──────┴──┘ + * ╱ │ ╱ ╲ + * ╱ │ ╱ ╲ + * ╱ │ ╱ ╲ + * 3. Add padding to header and formatted encrypted data according to `padding_indicator` + * ╱ │ ╱ ╲ + * ╱ │ ╱ ╲ + * ╱ │ ╱ ╲ + * ╱ │ ╱ ╲ + * â—£ â–¼ â—£ â—¢ + * ┌─────────────┬────┬──────┬─────────────────┬───────────────┬────┬──────┬──┬─────────┠+ * │Padded Header│0x87│Length│Padding Indicator│Encrypted Data │0x97│Length│Le│ Padding │ + * └─────────────┴────┴──────┴─────────────────┴───────────────┴────┴──────┴──┴─────────┘ + * ╲ │ + * ──────────────────────────────────────────────────────────────── │ + * 4. MAC calculation `authenticate()` │ + * ╲ │ + * â—¢ â–¼ + * ┌────────┬────┬────┬──────┬─────────────────┬───────────────┬────┬──────┬──┬────┬──────┬───┬──────┠+ * │ Header │ Lc │0x87│Length│Padding Indicator│Encrypted Data │0x97│Length│Le│0x8E│Length│MAC│ 0x00 │ + * └────────┴────┴────┴──────┴─────────────────┴───────────────┴────┴──────┴──┴────┴──────┴───┴──────┘ + * â–¶ Encrypted command APDU + * + * + * Workflow for decrypting a response APDU + * + * â–¶ Encrypted response APDU + * ┌────┬──────┬─────────────────┬──────────────┬────┬────┬───────┬────┬──────┬───┬─────────┠+ * │0x87│Length│Padding Indicator│Encrypted Data│0x99│0x02│SW1/SW2│0x8E│Length│MAC│ SW1/SW2 │ + * └────┴──────┴─────────────────┴──────────────┴────┴────┴───────┴────┴──────┴───┴─────────┘ + * ╲ │ │ ╱ ╱ ╲ + * â—¢ │ │ â—£ â—£ â—¢ + * ┌────────────────────────────────────────────────────────┠┌─────────┠+ * │ `mac_data` │ │ `mac` │ + * └────────────────────────────────────────────────────────┘ └─────────┘ + * │ │ ╲ ╱ + * │ │ â—¢ â—£ + * 1. MAC verification │ │ `verify_authenticate()` + * â–¼ â–¼ + * ┌──────────────┠+ * │Encrypted Data│ + * └──────────────┘ + * 2. Decrypt data │ `decrypt()` ╱ + * â–¼ â—£ + * ┌───────────┠+ * │Padded Data│ + * └───────────┘ + * │ ╱ + * 3. Remove padding from data according to `padding_indicator` + * │ ╱ + * â–¼ â—£ + * ┌──────┬─────────┠+ * â–¶ Unencrypted response APDU │ Data │ SW1/SW2 │ + * └──────┴─────────┘ + **/ struct iso_sm_ctx { /** @brief data of the specific crypto implementation */ void *priv_data; @@ -60,10 +165,10 @@ struct iso_sm_ctx { /** @brief Pad to this block length */ size_t block_length; - /** @brief Call back function for authentication of data */ + /** @brief Call back function for authentication of data, i.e. MAC creation */ int (*authenticate)(sc_card_t *card, const struct iso_sm_ctx *ctx, const u8 *data, size_t datalen, u8 **outdata); - /** @brief Call back function for verifying authentication data */ + /** @brief Call back function for verifying authentication data, i.e. MAC verification */ int (*verify_authentication)(sc_card_t *card, const struct iso_sm_ctx *ctx, const u8 *mac, size_t maclen, const u8 *macdata, size_t macdatalen); @@ -75,10 +180,12 @@ struct iso_sm_ctx { int (*decrypt)(sc_card_t *card, const struct iso_sm_ctx *ctx, const u8 *enc, size_t enclen, u8 **data); - /** @brief Call back function for actions before encoding and encryption of \a apdu */ + /** @brief Call back function for actions before encoding and encryption of \a apdu, + * e.g. for incrementing a send sequence counter */ int (*pre_transmit)(sc_card_t *card, const struct iso_sm_ctx *ctx, sc_apdu_t *apdu); - /** @brief Call back function for actions before decryption and decoding of \a sm_apdu */ + /** @brief Call back function for actions before decryption and decoding of \a sm_apdu, + * e.g. for incrementing a send sequence counter */ int (*post_transmit)(sc_card_t *card, const struct iso_sm_ctx *ctx, sc_apdu_t *sm_apdu); /** @brief Call back function for actions after decrypting SM protected APDU */ @@ -119,6 +226,8 @@ struct iso_sm_ctx *iso_sm_ctx_create(void); */ int iso_sm_start(struct sc_card *card, struct iso_sm_ctx *sctx); +int iso_sm_close(struct sc_card *card); + #ifdef __cplusplus } #endif diff --git a/src/smm/Makefile.am b/src/smm/Makefile.am index 50d8cd89d7..873335fc65 100644 --- a/src/smm/Makefile.am +++ b/src/smm/Makefile.am @@ -11,14 +11,16 @@ LIBS = $(top_builddir)/src/sm/libsm.la \ $(top_builddir)/src/common/libcompat.la if ENABLE_OPENSSL +if ENABLE_SHARED lib_LTLIBRARIES = libsmm-local.la endif +endif libsmm_local_la_SOURCES = smm-local.c sm-module.h \ sm-global-platform.c sm-cwa14890.c \ sm-card-authentic.c sm-card-iasecc.c \ smm-local.exports -libsmm_local_la_LIBADD = $(OPTIONAL_OPENSSL_LIBS) ../libopensc/libopensc.la +libsmm_local_la_LIBADD = $(OPTIONAL_OPENSSL_LIBS) libsmm_local_la_LDFLAGS = -module -shared -no-undefined -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ # noinst_HEADERS = sm.h diff --git a/src/smm/Makefile.mak b/src/smm/Makefile.mak index a50ccbc3b9..ca06ecc47f 100644 --- a/src/smm/Makefile.mak +++ b/src/smm/Makefile.mak @@ -3,19 +3,25 @@ TOPDIR = ..\.. TARGET = smm-local.dll OBJECTS = smm-local.obj sm-global-platform.obj sm-cwa14890.obj sm-card-iasecc.obj sm-card-authentic.obj -LIBS = $(TOPDIR)\src\sm\libsm.lib $(TOPDIR)\src\libopensc\opensc_a.lib $(TOPDIR)\src\common\libscdl.lib +LIBS = $(TOPDIR)\src\sm\libsm.lib \ + $(TOPDIR)\src\libopensc\opensc_a.lib \ + $(TOPDIR)\src\pkcs15init\pkcs15init.lib \ + $(TOPDIR)\src\scconf\scconf.lib \ + $(TOPDIR)\src\common\common.lib \ + $(TOPDIR)\src\common\libscdl.lib \ + $(TOPDIR)\src\ui\strings.lib \ + $(TOPDIR)\src\ui\notify.lib \ + $(TOPDIR)\src\sm\libsmiso.lib \ + $(TOPDIR)\src\sm\libsmeac.lib \ + $(TOPDIR)\src\pkcs15init\pkcs15init.lib all: $(TARGET) !INCLUDE $(TOPDIR)\win32\Make.rules.mak !IF "$(OPENSSL_DEF)" == "/DENABLE_OPENSSL" -$(TARGET): $(OBJECTS) $(LIBS) - echo LIBRARY $* > $*.def - echo EXPORTS >> $*.def - type $*.exports >> $*.def - link /dll $(LINKFLAGS) /def:$*.def /out:$(TARGET) $(OBJECTS) $(LIBS) $(ZLIB_LIB) $(OPENPACE_LIB) $(OPENSSL_LIB) ws2_32.lib gdi32.lib advapi32.lib Crypt32.lib User32.lib - if EXIST $(TARGET).manifest mt -manifest $(TARGET).manifest -outputresource:$(TARGET);2 +$(TARGET): $(OBJECTS) $(LIBS) $*.def + link /dll $(LINKFLAGS) /out:$@ /def:$*.def $(OBJECTS) $(LIBS) $(ZLIB_LIB) $(OPENPACE_LIB) $(OPENSSL_LIB) ws2_32.lib gdi32.lib advapi32.lib Crypt32.lib User32.lib Shell32.lib Comctl32.lib shlwapi.lib !ELSE $(TARGET): diff --git a/src/smm/sm-card-authentic.c b/src/smm/sm-card-authentic.c index 13cde7ebbe..e458a21c48 100644 --- a/src/smm/sm-card-authentic.c +++ b/src/smm/sm-card-authentic.c @@ -17,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H @@ -59,14 +59,13 @@ sm_oberthur_diversify_keyset(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *tmp; int rv = 0, ii, tmp_len; - if (gp_keyset->kmc_len == 48) { + if (gp_keyset->kmc_len == 48) { for (ii=0; ii<3; ii++) memcpy(keys[ii], gp_keyset->kmc + 16*ii, 16); - } - else if (gp_keyset->kmc_len == 16 || gp_keyset->kmc_len == 0) { + } else if (gp_keyset->kmc_len == 16 || gp_keyset->kmc_len == 0) { if (gp_keyset->kmc_len == 16) memcpy(master_key, gp_keyset->kmc, 16); - sc_log(ctx, "KMC: %s", sc_dump_hex(master_key, sizeof(master_key))); + sc_debug(ctx, SC_LOG_DEBUG_SM, "KMC: %s", sc_dump_hex(master_key, sizeof(master_key))); for (ii=0; ii<3; ii++) { key_buff[0] = key_buff[8] = 0; key_buff[1] = key_buff[9] = 0; @@ -77,38 +76,26 @@ sm_oberthur_diversify_keyset(struct sc_context *ctx, struct sm_info *sm_info, key_buff[6] = 0xF0, key_buff[14] = 0x0F; key_buff[7] = key_buff[15] = ii+1; - sc_log(ctx, "key_buf:%s", sc_dump_hex(key_buff, 16)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "key_buf:%s", sc_dump_hex(key_buff, 16)); - rv = sm_encrypt_des_ecb3(master_key, key_buff, sizeof(key_buff), &tmp, &tmp_len); - LOG_TEST_RET(ctx, rv, "GP init session: cannot derivate key"); + rv = sm_encrypt_des_ecb3(ctx, master_key, key_buff, sizeof(key_buff), &tmp, &tmp_len); + LOG_TEST_RET(ctx, rv, "GP init session: cannot derive key"); memcpy(keys[ii], tmp, sizeof(gp_keyset->enc)); free(tmp); } - } - else { + } else { LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "GP init session: invalid KMC data"); } - if (!rv && ctx) { - char dump_buf[2048]; - - sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, - gp_session->card_challenge, sizeof(gp_session->card_challenge), dump_buf, sizeof(dump_buf)); - sc_log(ctx, "Card challenge: %s", dump_buf); - - sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, - gp_session->host_challenge, sizeof(gp_session->host_challenge), dump_buf, sizeof(dump_buf)); - sc_log(ctx, "Host challenge: %s", dump_buf); - - sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, gp_keyset->enc, sizeof(gp_keyset->enc), dump_buf, sizeof(dump_buf)); - sc_log(ctx, "ENC: %s", dump_buf); - - sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, gp_keyset->mac, sizeof(gp_keyset->mac), dump_buf, sizeof(dump_buf)); - sc_log(ctx, "MAC: %s", dump_buf); - - sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, gp_keyset->kek, sizeof(gp_keyset->kek), dump_buf, sizeof(dump_buf)); - sc_log(ctx, "KEK: %s", dump_buf); + if (!rv && ctx) { + sc_debug_hex(ctx, SC_LOG_DEBUG_SM, "Card challenge", gp_session->card_challenge, + sizeof(gp_session->card_challenge)); + sc_debug_hex(ctx, SC_LOG_DEBUG_SM, "Host challenge", gp_session->host_challenge, + sizeof(gp_session->host_challenge)); + sc_debug_hex(ctx, SC_LOG_DEBUG_SM, "ENC", gp_keyset->enc, sizeof(gp_keyset->enc)); + sc_debug_hex(ctx, SC_LOG_DEBUG_SM, "MAC", gp_keyset->mac, sizeof(gp_keyset->mac)); + sc_debug_hex(ctx, SC_LOG_DEBUG_SM, "KEK", gp_keyset->kek, sizeof(gp_keyset->kek)); } return rv; @@ -123,7 +110,7 @@ sm_authentic_encode_apdu(struct sc_context *ctx, struct sm_info *sm_info) int rv = SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "SM encode APDU: offset:"); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM encode APDU: offset:"); rv = sm_gp_securize_apdu(ctx, sm_info, NULL, apdu); LOG_TEST_RET(ctx, rv, "SM encode APDU: securize error"); @@ -143,16 +130,18 @@ sm_authentic_get_apdus(struct sc_context *ctx, struct sm_info *sm_info, if (!sm_info) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(ctx, "SM get APDUs: rdata:%p, init_len:%"SC_FORMAT_LEN_SIZE_T"u", + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get APDUs: rdata:%p, init_len:%"SC_FORMAT_LEN_SIZE_T"u", rdata, init_len); - sc_log(ctx, "SM get APDUs: serial %s", sc_dump_hex(sm_info->serialnr.value, sm_info->serialnr.len)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get APDUs: serial %s", + sc_dump_hex(sm_info->serialnr.value, sm_info->serialnr.len)); - if (init_data) { - rv = sm_gp_external_authentication(ctx, sm_info, init_data, init_len, rdata, sm_oberthur_diversify_keyset); + if (init_data) { + rv = sm_gp_external_authentication(ctx, sm_info, init_data, init_len, rdata, + sm_oberthur_diversify_keyset); LOG_TEST_RET(ctx, rv, "SM get APDUs: cannot authenticate card"); } - switch (sm_info->cmd) { + switch (sm_info->cmd) { case SM_CMD_APDU_TRANSMIT: rv = sm_authentic_encode_apdu(ctx, sm_info); LOG_TEST_RET(ctx, rv, "SM get APDUs: cannot encode APDU"); diff --git a/src/smm/sm-card-iasecc.c b/src/smm/sm-card-iasecc.c index a77849d053..dd4a9fe96a 100644 --- a/src/smm/sm-card-iasecc.c +++ b/src/smm/sm-card-iasecc.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H @@ -67,12 +67,12 @@ sm_iasecc_get_apdu_read_binary(struct sc_context *ctx, struct sm_info *sm_info, if (!rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(ctx, + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get 'READ BINARY' APDUs: offset:%"SC_FORMAT_LEN_SIZE_T"u,size:%"SC_FORMAT_LEN_SIZE_T"u", cmd_data->offs, cmd_data->count); offs = cmd_data->offs; while (cmd_data->count > data_offs) { - int sz = (cmd_data->count - data_offs) > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : (cmd_data->count - data_offs); + size_t sz = (cmd_data->count - data_offs) > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : (cmd_data->count - data_offs); struct sc_remote_apdu *rapdu = NULL; rv = rdata->alloc(rdata, &rapdu); @@ -113,12 +113,12 @@ sm_iasecc_get_apdu_update_binary(struct sc_context *ctx, struct sm_info *sm_info if (!rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(ctx, + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get 'UPDATE BINARY' APDUs: offset:%"SC_FORMAT_LEN_SIZE_T"u,size:%"SC_FORMAT_LEN_SIZE_T"u", cmd_data->offs, cmd_data->count); offs = cmd_data->offs; while (data_offs < cmd_data->count) { - int sz = (cmd_data->count - data_offs) > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : (cmd_data->count - data_offs); + size_t sz = (cmd_data->count - data_offs) > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : (cmd_data->count - data_offs); struct sc_remote_apdu *rapdu = NULL; rv = rdata->alloc(rdata, &rapdu); @@ -161,7 +161,7 @@ sm_iasecc_get_apdu_create_file(struct sc_context *ctx, struct sm_info *sm_info, if (!cmd_data || !cmd_data->data || !rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(ctx, + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get 'CREATE FILE' APDU: FCP(%"SC_FORMAT_LEN_SIZE_T"u) %s", cmd_data->size, sc_dump_hex(cmd_data->data,cmd_data->size)); @@ -197,7 +197,7 @@ sm_iasecc_get_apdu_delete_file(struct sc_context *ctx, struct sm_info *sm_info, int rv; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "SM get 'DELETE FILE' APDU: file-id %04X", file_id); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get 'DELETE FILE' APDU: file-id %04X", file_id); if (!file_id) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); @@ -237,7 +237,7 @@ sm_iasecc_get_apdu_verify_pin(struct sc_context *ctx, struct sm_info *sm_info, s if (!pin_data || !rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(ctx, "SM get 'VERIFY PIN' APDU: %u", pin_data->pin_reference); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get 'VERIFY PIN' APDU: %u", pin_data->pin_reference); rv = rdata->alloc(rdata, &rapdu); LOG_TEST_RET(ctx, rv, "SM get 'VERIFY PIN' APDUs: cannot allocate remote APDU"); @@ -248,7 +248,7 @@ sm_iasecc_get_apdu_verify_pin(struct sc_context *ctx, struct sm_info *sm_info, s rapdu->apdu.p1 = 0x00; rapdu->apdu.p2 = pin_data->pin_reference & ~IASECC_OBJECT_REF_GLOBAL; if (pin_data->pin1.len > SM_MAX_DATA_SIZE) - LOG_TEST_RET(ctx, rv, "SM get 'VERIFY PIN' APDU: invelid PIN size"); + LOG_TEST_RET(ctx, rv, "SM get 'VERIFY PIN' APDU: invalid PIN size"); memcpy((unsigned char *)rapdu->apdu.data, pin_data->pin1.data, pin_data->pin1.len); rapdu->apdu.datalen = pin_data->pin1.len; @@ -278,7 +278,7 @@ sm_iasecc_get_apdu_reset_pin(struct sc_context *ctx, struct sm_info *sm_info, st if (!pin_data || !rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(ctx, "SM get 'RESET PIN' APDU; reference %i", pin_data->pin_reference); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get 'RESET PIN' APDU; reference %i", pin_data->pin_reference); rv = rdata->alloc(rdata, &rapdu); LOG_TEST_RET(ctx, rv, "SM get 'RESET PIN' APDUs: cannot allocate remote APDU"); @@ -324,18 +324,19 @@ sm_iasecc_get_apdu_sdo_update(struct sc_context *ctx, struct sm_info *sm_info, s if (!rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(ctx, "SM get 'SDO UPDATE' APDU, SDO(class:0x%X,ref:%i)", update->sdo_class, update->sdo_ref); - for (ii=0; update->fields[ii].tag && ii < IASECC_SDO_TAGS_UPDATE_MAX; ii++) { + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get 'SDO UPDATE' APDU, SDO(class:0x%X,ref:%i)", update->sdo_class, update->sdo_ref); + for (ii = 0; ii < IASECC_SDO_TAGS_UPDATE_MAX && update->fields[ii].tag; ii++) { unsigned char *encoded = NULL; size_t encoded_len, offs; - encoded_len = iasecc_sdo_encode_update_field(ctx, update->sdo_class, update->sdo_ref, &update->fields[ii], &encoded); - LOG_TEST_RET(ctx, encoded_len, "SM get 'SDO UPDATE' APDU: encode component error"); + rv = iasecc_sdo_encode_update_field(ctx, update->sdo_class, update->sdo_ref, &update->fields[ii], &encoded); + LOG_TEST_RET(ctx, rv, "SM get 'SDO UPDATE' APDU: encode component error"); + encoded_len = rv; - sc_log(ctx, "SM IAS/ECC get APDUs: encoded component '%s'", sc_dump_hex(encoded, encoded_len)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC get APDUs: encoded component '%s'", sc_dump_hex(encoded, encoded_len)); for (offs = 0; offs < encoded_len; ) { - int len = (encoded_len - offs) > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : (encoded_len - offs); + size_t len = (encoded_len - offs) > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : (encoded_len - offs); struct sc_remote_apdu *rapdu = NULL; rv = rdata->alloc(rdata, &rapdu); @@ -383,7 +384,7 @@ sm_iasecc_get_apdu_generate_rsa(struct sc_context *ctx, struct sm_info *sm_info, int rv; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "SM get 'GENERATE RSA' APDU: SDO(class:%X,reference:%X)", sdo->sdo_class, sdo->sdo_ref); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get 'GENERATE RSA' APDU: SDO(class:%X,reference:%X)", sdo->sdo_class, sdo->sdo_ref); if (!rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); @@ -443,30 +444,31 @@ sm_iasecc_get_apdu_update_rsa(struct sc_context *ctx, struct sm_info *sm_info, s LOG_FUNC_CALLED(ctx); if (cmd_data->update_prv.sdo_class) { to_update[ii++] = &cmd_data->update_prv; - sc_log(ctx, "SM get 'UPDATE RSA' APDU: SDO(class:%X,ref:%X)", cmd_data->update_prv.sdo_class, cmd_data->update_prv.sdo_ref); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get 'UPDATE RSA' APDU: SDO(class:%X,ref:%X)", cmd_data->update_prv.sdo_class, cmd_data->update_prv.sdo_ref); } if (cmd_data->update_pub.sdo_class) { to_update[ii++] = &cmd_data->update_pub; - sc_log(ctx, "SM get 'UPDATE RSA' APDU: SDO(class:%X,ref:%X)", cmd_data->update_pub.sdo_class, cmd_data->update_pub.sdo_ref); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get 'UPDATE RSA' APDU: SDO(class:%X,ref:%X)", cmd_data->update_pub.sdo_class, cmd_data->update_pub.sdo_ref); } - for (jj=0;jj<2 && to_update[jj];jj++) { - for (ii=0; to_update[jj]->fields[ii].tag && ii < IASECC_SDO_TAGS_UPDATE_MAX; ii++) { + for (jj = 0; jj < 2 && to_update[jj]; jj++) { + for (ii = 0; ii < IASECC_SDO_TAGS_UPDATE_MAX && to_update[jj]->fields[ii].tag; ii++) { unsigned char *encoded = NULL; size_t encoded_len, offs; - sc_log(ctx, "SM IAS/ECC get APDUs: component(num %i:%i) class:%X, ref:%X", jj, ii, + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC get APDUs: component(num %i:%i) class:%X, ref:%X", jj, ii, to_update[jj]->sdo_class, to_update[jj]->sdo_ref); - encoded_len = iasecc_sdo_encode_update_field(ctx, to_update[jj]->sdo_class, to_update[jj]->sdo_ref, + rv = iasecc_sdo_encode_update_field(ctx, to_update[jj]->sdo_class, to_update[jj]->sdo_ref, &to_update[jj]->fields[ii], &encoded); - LOG_TEST_RET(ctx, encoded_len, "SM get 'UPDATE RSA' APDU: cannot encode key component"); + LOG_TEST_RET(ctx, rv, "SM get 'UPDATE RSA' APDU: cannot encode key component"); + encoded_len = rv; - sc_log(ctx, "SM IAS/ECC get APDUs: component encoded %s", sc_dump_hex(encoded, encoded_len)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC get APDUs: component encoded %s", sc_dump_hex(encoded, encoded_len)); for (offs = 0; offs < encoded_len; ) { - int len = (encoded_len - offs) > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : (encoded_len - offs); + size_t len = (encoded_len - offs) > SM_MAX_DATA_SIZE ? SM_MAX_DATA_SIZE : (encoded_len - offs); struct sc_remote_apdu *rapdu = NULL; rv = rdata->alloc(rdata, &rapdu); @@ -511,10 +513,10 @@ sm_iasecc_get_apdus(struct sc_context *ctx, struct sm_info *sm_info, if (!sm_info) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(ctx, "SM IAS/ECC get APDUs: init_len:%"SC_FORMAT_LEN_SIZE_T"u", + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC get APDUs: init_len:%"SC_FORMAT_LEN_SIZE_T"u", init_len); - sc_log(ctx, "SM IAS/ECC get APDUs: rdata:%p", rdata); - sc_log(ctx, "SM IAS/ECC get APDUs: serial %s", sc_dump_hex(sm_info->serialnr.value, sm_info->serialnr.len)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC get APDUs: rdata:%p", rdata); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC get APDUs: serial %s", sc_dump_hex(sm_info->serialnr.value, sm_info->serialnr.len)); rv = sm_cwa_decode_authentication_data(ctx, cwa_keyset, cwa_session, init_data); LOG_TEST_RET(ctx, rv, "SM IAS/ECC get APDUs: decode authentication data error"); @@ -522,9 +524,9 @@ sm_iasecc_get_apdus(struct sc_context *ctx, struct sm_info *sm_info, rv = sm_cwa_init_session_keys(ctx, cwa_session, cwa_session->params.crt_at.algo); LOG_TEST_RET(ctx, rv, "SM IAS/ECC get APDUs: cannot get session keys"); - sc_log(ctx, "SKENC %s", sc_dump_hex(cwa_session->session_enc, sizeof(cwa_session->session_enc))); - sc_log(ctx, "SKMAC %s", sc_dump_hex(cwa_session->session_mac, sizeof(cwa_session->session_mac))); - sc_log(ctx, "SSC %s", sc_dump_hex(cwa_session->ssc, sizeof(cwa_session->ssc))); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SKENC %s", sc_dump_hex(cwa_session->session_enc, sizeof(cwa_session->session_enc))); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SKMAC %s", sc_dump_hex(cwa_session->session_mac, sizeof(cwa_session->session_mac))); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SSC %s", sc_dump_hex(cwa_session->ssc, sizeof(cwa_session->ssc))); switch (sm_info->cmd) { case SM_CMD_FILE_READ: @@ -586,7 +588,7 @@ sm_iasecc_decode_card_data(struct sc_context *ctx, struct sm_info *sm_info, stru LOG_FUNC_CALLED(ctx); - sc_log(ctx, + sc_debug(ctx, SC_LOG_DEBUG_SM, "IAS/ECC decode answer() rdata length %i, out length %"SC_FORMAT_LEN_SIZE_T"u", rdata->length, out_len); for (rapdu = rdata->data; rapdu; rapdu = rapdu->next) { @@ -599,7 +601,7 @@ sm_iasecc_decode_card_data(struct sc_context *ctx, struct sm_info *sm_info, stru unsigned char ticket[8]; size_t ticket_len = sizeof(ticket); - sc_log(ctx, + sc_debug(ctx, SC_LOG_DEBUG_SM, "IAS/ECC decode response(%"SC_FORMAT_LEN_SIZE_T"u) %s", rapdu->apdu.resplen, sc_dump_hex(rapdu->apdu.resp, rapdu->apdu.resplen)); @@ -611,12 +613,12 @@ sm_iasecc_decode_card_data(struct sc_context *ctx, struct sm_info *sm_info, stru rv = sc_asn1_decode(ctx, asn1_iasecc_sm_data_object, rapdu->apdu.resp, rapdu->apdu.resplen, NULL, NULL); LOG_TEST_RET(ctx, rv, "IAS/ECC decode answer(s): ASN1 decode error"); - sc_log(ctx, "IAS/ECC decode response() SW:%02X%02X, MAC:%s", status[0], status[1], sc_dump_hex(ticket, ticket_len)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "IAS/ECC decode response() SW:%02X%02X, MAC:%s", status[0], status[1], sc_dump_hex(ticket, ticket_len)); if (status[0] != 0x90 || status[1] != 0x00) continue; if (asn1_iasecc_sm_data_object[0].flags & SC_ASN1_PRESENT) { - sc_log(ctx, "IAS/ECC decode answer() object present"); + sc_debug(ctx, SC_LOG_DEBUG_SM, "IAS/ECC decode answer() object present"); if (resp_data[0] != 0x01) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "IAS/ECC decode answer(s): invalid encrypted data format"); @@ -625,7 +627,7 @@ sm_iasecc_decode_card_data(struct sc_context *ctx, struct sm_info *sm_info, stru &decrypted, &decrypted_len); LOG_TEST_RET(ctx, rv, "IAS/ECC decode answer(s): cannot decrypt card answer data"); - sc_log(ctx, + sc_debug(ctx, SC_LOG_DEBUG_SM, "IAS/ECC decrypted data(%"SC_FORMAT_LEN_SIZE_T"u) %s", decrypted_len, sc_dump_hex(decrypted, decrypted_len)); @@ -637,12 +639,12 @@ sm_iasecc_decode_card_data(struct sc_context *ctx, struct sm_info *sm_info, stru if (out && out_len) { if (out_len < offs + decrypted_len) - LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "IAS/ECC decode answer(s): unsufficient output buffer size"); + LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "IAS/ECC decode answer(s): insufficient output buffer size"); memcpy(out + offs, decrypted, decrypted_len); offs += decrypted_len; - sc_log(ctx, + sc_debug(ctx, SC_LOG_DEBUG_SM, "IAS/ECC decode card answer(s): out_len/offs %"SC_FORMAT_LEN_SIZE_T"u/%i", out_len, offs); } diff --git a/src/smm/sm-common.exports b/src/smm/sm-common.exports index 35d4cdb090..f54389bd33 100644 --- a/src/smm/sm-common.exports +++ b/src/smm/sm-common.exports @@ -1,3 +1,2 @@ -sm_cwa_get_mac -sm_cwa_securize_apdu - +sm_cwa_get_mac +sm_cwa_securize_apdu diff --git a/src/smm/sm-cwa14890.c b/src/smm/sm-cwa14890.c index fc3ff4d6d8..de978f36f3 100644 --- a/src/smm/sm-cwa14890.c +++ b/src/smm/sm-cwa14890.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H @@ -46,22 +46,20 @@ #include "sm-module.h" int -sm_cwa_get_mac(struct sc_context *ctx, unsigned char *key, DES_cblock *icv, - unsigned char *in, int in_len, DES_cblock *out, int force_padding) +sm_cwa_get_mac(struct sc_context *ctx, unsigned char *key, sm_des_cblock *icv, + unsigned char *in, int in_len, sm_des_cblock *out, int force_padding) { - DES_cblock kk, k2; - DES_key_schedule ks,ks2; unsigned char padding[8] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; unsigned char *buf; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "sm_cwa_get_mac() data length %i", in_len); + sc_debug(ctx, SC_LOG_DEBUG_SM, "sm_cwa_get_mac() data length %i", in_len); buf = malloc(in_len + 8); if (!buf) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); - sc_log(ctx, "sm_cwa_get_mac() in_data(%i) %s", in_len, sc_dump_hex(in, in_len)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "sm_cwa_get_mac() in_data(%i) %s", in_len, sc_dump_hex(in, in_len)); memcpy(buf, in, in_len); memcpy(buf + in_len, padding, 8); @@ -70,14 +68,10 @@ sm_cwa_get_mac(struct sc_context *ctx, unsigned char *key, DES_cblock *icv, else in_len = ((in_len + 7) / 8) * 8; - sc_log(ctx, "sm_cwa_get_mac() data to MAC(%i) %s", in_len, sc_dump_hex(buf, in_len)); - sc_log(ctx, "sm_cwa_get_mac() ICV %s", sc_dump_hex((unsigned char *)icv, 8)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "sm_cwa_get_mac() data to MAC(%i) %s", in_len, sc_dump_hex(buf, in_len)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "sm_cwa_get_mac() ICV %s", sc_dump_hex((unsigned char *)icv, 8)); - memcpy(&kk, key, 8); - memcpy(&k2, key + 8, 8); - DES_set_key_unchecked(&kk,&ks); - DES_set_key_unchecked(&k2,&ks2); - DES_cbc_cksum_3des_emv96(buf, out, in_len ,&ks, &ks2, icv); + DES_cbc_cksum_3des_emv96(ctx, buf, out, in_len, key, icv); free(buf); LOG_FUNC_RETURN(ctx, SC_SUCCESS); @@ -91,8 +85,8 @@ sm_cwa_encode_external_auth_data(struct sc_context *ctx, struct sm_cwa_session * if (out_len < 16) return SC_ERROR_BUFFER_TOO_SMALL; - sc_log(ctx, "IFD.RND %s", sc_dump_hex(session_data->ifd.rnd, 8)); - sc_log(ctx, "IFD.SN %s", sc_dump_hex(session_data->ifd.sn, 8)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "IFD.RND %s", sc_dump_hex(session_data->ifd.rnd, 8)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "IFD.SN %s", sc_dump_hex(session_data->ifd.sn, 8)); memcpy(out + 0, session_data->icc.rnd, 8); memcpy(out + 8, session_data->icc.sn, 8); @@ -108,11 +102,11 @@ sm_cwa_encode_mutual_auth_data(struct sc_context *ctx, struct sm_cwa_session *se if (out_len < 64) return SC_ERROR_BUFFER_TOO_SMALL; - sc_log(ctx, "IFD.RND %s", sc_dump_hex(session_data->ifd.rnd, 8)); - sc_log(ctx, "IFD.SN %s", sc_dump_hex(session_data->ifd.sn, 8)); - sc_log(ctx, "IFD.K %s", sc_dump_hex(session_data->ifd.k, 32)); - sc_log(ctx, "ICC.RND %s", sc_dump_hex(session_data->icc.rnd, 8)); - sc_log(ctx, "ICC.SN %s", sc_dump_hex(session_data->icc.sn, 8)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "IFD.RND %s", sc_dump_hex(session_data->ifd.rnd, 8)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "IFD.SN %s", sc_dump_hex(session_data->ifd.sn, 8)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "IFD.K %s", sc_dump_hex(session_data->ifd.k, 32)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "ICC.RND %s", sc_dump_hex(session_data->icc.rnd, 8)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "ICC.SN %s", sc_dump_hex(session_data->icc.sn, 8)); memcpy(out + 0, session_data->ifd.rnd, 8); memcpy(out + 8, session_data->ifd.sn, 8); @@ -128,8 +122,8 @@ int sm_cwa_decode_authentication_data(struct sc_context *ctx, struct sm_cwa_keyset *keyset, struct sm_cwa_session *session_data, unsigned char *auth_data) { - DES_cblock icv = {0, 0, 0, 0, 0, 0, 0, 0}; - DES_cblock cblock; + sm_des_cblock icv = {0, 0, 0, 0, 0, 0, 0, 0}; + sm_des_cblock cblock; unsigned char *decrypted = NULL; size_t decrypted_len; int rv; @@ -139,7 +133,7 @@ sm_cwa_decode_authentication_data(struct sc_context *ctx, struct sm_cwa_keyset * memset(icv, 0, sizeof(icv)); rv = sm_cwa_get_mac(ctx, keyset->mac, &icv, session_data->mdata, 0x40, &cblock, 1); LOG_TEST_RET(ctx, rv, "Decode authentication data: sm_ecc_get_mac failed"); - sc_log(ctx, "MAC:%s", sc_dump_hex(cblock, sizeof(cblock))); + sc_debug(ctx, SC_LOG_DEBUG_SM, "MAC:%s", sc_dump_hex(cblock, sizeof(cblock))); if(memcmp(session_data->mdata + 0x40, cblock, 8)) LOG_FUNC_RETURN(ctx, SC_ERROR_SM_AUTHENTICATION_FAILED); @@ -147,7 +141,7 @@ sm_cwa_decode_authentication_data(struct sc_context *ctx, struct sm_cwa_keyset * rv = sm_decrypt_des_cbc3(ctx, keyset->enc, session_data->mdata, session_data->mdata_len, &decrypted, &decrypted_len); LOG_TEST_RET(ctx, rv, "sm_ecc_decode_auth_data() DES CBC3 decrypt error"); - sc_log(ctx, + sc_debug(ctx, SC_LOG_DEBUG_SM, "sm_ecc_decode_auth_data() decrypted(%"SC_FORMAT_LEN_SIZE_T"u) %s", decrypted_len, sc_dump_hex(decrypted, decrypted_len)); @@ -191,22 +185,21 @@ sm_cwa_init_session_keys(struct sc_context *ctx, struct sm_cwa_session *session_ for (ii=0; ii<32; ii++) xored[ii] = session_data->ifd.k[ii] ^ session_data->icc.k[ii]; - sc_log(ctx, "K_IFD %s", sc_dump_hex(session_data->ifd.k, 32)); - sc_log(ctx, "K_ICC %s", sc_dump_hex(session_data->icc.k, 32)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "K_IFD %s", sc_dump_hex(session_data->ifd.k, 32)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "K_ICC %s", sc_dump_hex(session_data->icc.k, 32)); if (mechanism == IASECC_ALGORITHM_SYMMETRIC_SHA1) { xored[35] = 0x01; - sc_log(ctx, "XOR for SkEnc %s", sc_dump_hex(xored, 36)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "XOR for SkEnc %s", sc_dump_hex(xored, 36)); SHA1(xored, 36, buff); memcpy(&session_data->session_enc[0], buff, sizeof(session_data->session_enc)); xored[35] = 0x02; - sc_log(ctx, "XOR for SkMac %s", sc_dump_hex(xored, 36)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "XOR for SkMac %s", sc_dump_hex(xored, 36)); SHA1(xored, 36, buff); memcpy(&session_data->session_mac[0], buff, sizeof(session_data->session_mac)); } else if (mechanism == IASECC_ALGORITHM_SYMMETRIC_SHA256) { -#if OPENSSL_VERSION_NUMBER >= 0x00908000L xored[35] = 0x01; SHA256(xored, 36, buff); memcpy(&session_data->session_enc[0], buff, sizeof(session_data->session_enc)); @@ -214,10 +207,6 @@ sm_cwa_init_session_keys(struct sc_context *ctx, struct sm_cwa_session *session_ xored[35] = 0x02; SHA256(xored, 36, buff); memcpy(&session_data->session_mac[0], buff, sizeof(session_data->session_mac)); -#else - sc_log(ctx, "No FIPS, SHA256 is not supported"); - return SC_ERROR_INVALID_ARGUMENTS; -#endif } else { return SC_ERROR_INVALID_ARGUMENTS; @@ -239,16 +228,16 @@ sm_cwa_initialize(struct sc_context *ctx, struct sm_info *sm_info, struct sc_rem size_t icc_sn_len = sizeof(cwa_session->icc.sn); struct sc_remote_apdu *new_rapdu = NULL; struct sc_apdu *apdu = NULL; - unsigned char buf[0x100], *encrypted; + unsigned char buf[0x100], *encrypted = NULL; size_t encrypted_len; - DES_cblock icv = {0, 0, 0, 0, 0, 0, 0, 0}, cblock; + sm_des_cblock icv = {0, 0, 0, 0, 0, 0, 0, 0}, cblock; int rv, offs; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "SM IAS/ECC initialize: serial %s", sc_dump_hex(sm_info->serialnr.value, sm_info->serialnr.len)); - sc_log(ctx, "SM IAS/ECC initialize: card challenge %s", sc_dump_hex(cwa_session->card_challenge, 8)); - sc_log(ctx, "SM IAS/ECC initialize: current_df_path %s", sc_print_path(&sm_info->current_path_df)); - sc_log(ctx, "SM IAS/ECC initialize: CRT_AT reference 0x%X", cwa_session->params.crt_at.refs[0]); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC initialize: serial %s", sc_dump_hex(sm_info->serialnr.value, sm_info->serialnr.len)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC initialize: card challenge %s", sc_dump_hex(cwa_session->card_challenge, 8)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC initialize: current_df_path %s", sc_print_path(&sm_info->current_path_df)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC initialize: CRT_AT reference 0x%X", cwa_session->params.crt_at.refs[0]); if (!rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); @@ -275,20 +264,20 @@ sm_cwa_initialize(struct sc_context *ctx, struct sm_info *sm_info, struct sc_rem LOG_FUNC_RETURN(ctx, offs); } - sc_log(ctx, "S(%i) %s", offs, sc_dump_hex(buf, offs)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "S(%i) %s", offs, sc_dump_hex(buf, offs)); rv = sm_encrypt_des_cbc3(ctx, cwa_keyset->enc, buf, offs, &encrypted, &encrypted_len, 1); LOG_TEST_RET(ctx, rv, "_encrypt_des_cbc3() failed"); - sc_log(ctx, "ENCed(%"SC_FORMAT_LEN_SIZE_T"u) %s", encrypted_len, + sc_debug(ctx, SC_LOG_DEBUG_SM, "ENCed(%"SC_FORMAT_LEN_SIZE_T"u) %s", encrypted_len, sc_dump_hex(encrypted, encrypted_len)); memcpy(buf, encrypted, encrypted_len); - offs = encrypted_len; + offs = (int)encrypted_len; rv = sm_cwa_get_mac(ctx, cwa_keyset->mac, &icv, buf, offs, &cblock, 1); - LOG_TEST_RET(ctx, rv, "sm_ecc_get_mac() failed"); - sc_log(ctx, "MACed(%"SC_FORMAT_LEN_SIZE_T"u) %s", sizeof(cblock), + LOG_TEST_GOTO_ERR(ctx, rv, "sm_ecc_get_mac() failed"); + sc_debug(ctx, SC_LOG_DEBUG_SM, "MACed(%"SC_FORMAT_LEN_SIZE_T"u) %s", sizeof(cblock), sc_dump_hex(cblock, sizeof(cblock))); apdu->cse = SC_APDU_CASE_4_SHORT; @@ -301,9 +290,11 @@ sm_cwa_initialize(struct sc_context *ctx, struct sm_info *sm_info, struct sc_rem apdu->datalen = encrypted_len + sizeof(cblock); memcpy(new_rapdu->sbuf, encrypted, encrypted_len); memcpy(new_rapdu->sbuf + encrypted_len, cblock, sizeof(cblock)); + rv = SC_SUCCESS; +err: free(encrypted); - LOG_FUNC_RETURN(ctx, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, rv); } @@ -313,13 +304,13 @@ sm_cwa_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, struct sc_ struct sm_cwa_session *session_data = &sm_info->session.cwa; struct sc_apdu *apdu = &rapdu->apdu; unsigned char sbuf[0x400]; - DES_cblock cblock, icv; + sm_des_cblock cblock, icv; unsigned char *encrypted = NULL, edfb_data[0x200], mac_data[0x200]; - size_t encrypted_len, edfb_len = 0, mac_len = 0, offs; - int rv; + size_t encrypted_len, edfb_len = 0, offs; + int rv, mac_len = 0; LOG_FUNC_CALLED(ctx); - sc_log(ctx, + sc_debug(ctx, SC_LOG_DEBUG_SM, "securize APDU (cla:%X,ins:%X,p1:%X,p2:%X,data(%"SC_FORMAT_LEN_SIZE_T"u):%p)", apdu->cla, apdu->ins, apdu->p1, apdu->p2, apdu->datalen, apdu->data); @@ -328,7 +319,7 @@ sm_cwa_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, struct sc_ rv = sm_encrypt_des_cbc3(ctx, session_data->session_enc, apdu->data, apdu->datalen, &encrypted, &encrypted_len, 0); LOG_TEST_RET(ctx, rv, "securize APDU: DES CBC3 encryption failed"); - sc_log(ctx, "encrypted data (len:%"SC_FORMAT_LEN_SIZE_T"u, %s)", + sc_debug(ctx, SC_LOG_DEBUG_SM, "encrypted data (len:%"SC_FORMAT_LEN_SIZE_T"u, %s)", encrypted_len, sc_dump_hex(encrypted, encrypted_len)); offs = 0; @@ -348,7 +339,7 @@ sm_cwa_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, struct sc_ memcpy(edfb_data + offs, encrypted, encrypted_len); offs += encrypted_len; edfb_len = offs; - sc_log(ctx, "securize APDU: EDFB(len:%"SC_FORMAT_LEN_SIZE_T"u,%s)", + sc_debug(ctx, SC_LOG_DEBUG_SM, "securize APDU: EDFB(len:%"SC_FORMAT_LEN_SIZE_T"u,%s)", edfb_len, sc_dump_hex(edfb_data, edfb_len)); free(encrypted); @@ -375,14 +366,14 @@ sm_cwa_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, struct sc_ mac_data[offs++] = apdu->le; /* } */ - mac_len = offs; - sc_log(ctx, "securize APDU: MAC data(len:%"SC_FORMAT_LEN_SIZE_T"u,%s)", - mac_len, sc_dump_hex(mac_data, mac_len)); + mac_len = (int)offs; + sc_debug(ctx, SC_LOG_DEBUG_SM, "securize APDU: MAC data(len:%d,%s)", + mac_len, sc_dump_hex(mac_data, offs)); memset(icv, 0, sizeof(icv)); rv = sm_cwa_get_mac(ctx, session_data->session_mac, &icv, mac_data, mac_len, &cblock, 0); LOG_TEST_RET(ctx, rv, "securize APDU: MAC calculation error"); - sc_log(ctx, "securize APDU: MAC:%s", sc_dump_hex(cblock, sizeof(cblock))); + sc_debug(ctx, SC_LOG_DEBUG_SM, "securize APDU: MAC:%s", sc_dump_hex(cblock, sizeof(cblock))); offs = 0; if (edfb_len) { @@ -400,7 +391,7 @@ sm_cwa_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, struct sc_ sbuf[offs++] = 8; memcpy(sbuf + offs, cblock, 8); offs += 8; - sc_log(ctx, "securize APDU: SM data(len:%"SC_FORMAT_LEN_SIZE_T"u,%s)", + sc_debug(ctx, SC_LOG_DEBUG_SM, "securize APDU: SM data(len:%"SC_FORMAT_LEN_SIZE_T"u,%s)", offs, sc_dump_hex(sbuf, offs)); if (offs > sizeof(rapdu->sbuf)) diff --git a/src/smm/sm-global-platform.c b/src/smm/sm-global-platform.c index e8f10507d0..5d79b95c9c 100644 --- a/src/smm/sm-global-platform.c +++ b/src/smm/sm-global-platform.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H @@ -63,9 +63,9 @@ sm_gp_initialize(struct sc_context *ctx, struct sm_info *sm_info, struct sc_rem int rv; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "SM GP initialize: serial:%s", sc_dump_hex(sn.value, sn.len)); - sc_log(ctx, "SM GP initialize: current_df_path %s", sc_print_path(&sm_info->current_path_df)); - sc_log(ctx, "SM GP initialize: KMC length %i", gp_keyset->kmc_len); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP initialize: serial:%s", sc_dump_hex(sn.value, sn.len)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP initialize: current_df_path %s", sc_print_path(&sm_info->current_path_df)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP initialize: KMC length %i", gp_keyset->kmc_len); if (!rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); @@ -97,7 +97,7 @@ sc_gp_get_session_key(struct sc_context *ctx, struct sm_gp_session *gp_session, unsigned char *key) { int out_len; - unsigned char *out; + unsigned char *out = NULL; unsigned char deriv[16]; memcpy(deriv, gp_session->card_challenge + 4, 4); @@ -105,15 +105,14 @@ sc_gp_get_session_key(struct sc_context *ctx, struct sm_gp_session *gp_session, memcpy(deriv + 8, gp_session->card_challenge, 4); memcpy(deriv + 12, gp_session->host_challenge + 4, 4); - if (sm_encrypt_des_ecb3(key, deriv, 16, &out, &out_len)) { + if (sm_encrypt_des_ecb3(ctx, key, deriv, 16, &out, &out_len)) { if (ctx) - sc_log(ctx, "SM GP get session key: des_ecb3 encryption error"); - free(out); + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "SM GP get session key: des_ecb3 encryption error"); return NULL; } else if (out==NULL || out_len!=16) { if (ctx) - sc_log(ctx, "SM GP get session key: des_ecb3 encryption error: out(%p,len:%i)", out, out_len); + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "SM GP get session key: des_ecb3 encryption error: out(%p,len:%i)", out, out_len); if (out) free(out); return NULL; @@ -124,14 +123,12 @@ sc_gp_get_session_key(struct sc_context *ctx, struct sm_gp_session *gp_session, int -sm_gp_get_cryptogram(unsigned char *session_key, +sm_gp_get_cryptogram(struct sc_context *ctx, unsigned char *session_key, unsigned char *left, unsigned char *right, unsigned char *out, int out_len) { unsigned char block[24]; - DES_cblock kk,k2; - DES_key_schedule ks,ks2; - DES_cblock cksum={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + sm_des_cblock cksum={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; if (out_len!=8) return SC_ERROR_INVALID_ARGUMENTS; @@ -140,11 +137,7 @@ sm_gp_get_cryptogram(unsigned char *session_key, memcpy(block + 8, right, 8); memcpy(block + 16, "\x80\0\0\0\0\0\0\0",8); - memcpy(&kk, session_key, 8); - memcpy(&k2, session_key + 8, 8); - DES_set_key_unchecked(&kk,&ks); - DES_set_key_unchecked(&k2,&ks2); - DES_cbc_cksum_3des(block,&cksum, sizeof(block),&ks,&ks2,&cksum); + DES_cbc_cksum_3des(ctx, block, &cksum, sizeof(block), session_key, &cksum); memcpy(out, cksum, 8); @@ -153,13 +146,11 @@ sm_gp_get_cryptogram(unsigned char *session_key, int -sm_gp_get_mac(unsigned char *key, DES_cblock *icv, - unsigned char *in, int in_len, DES_cblock *out) +sm_gp_get_mac(struct sc_context *ctx, unsigned char *key, sm_des_cblock *icv, + unsigned char *in, int in_len, sm_des_cblock *out) { int len; unsigned char *block; - DES_cblock kk, k2; - DES_key_schedule ks,ks2; block = malloc(in_len + 8); if (!block) @@ -170,12 +161,7 @@ sm_gp_get_mac(unsigned char *key, DES_cblock *icv, len = in_len + 8; len -= (len%8); - memcpy(&kk, key, 8); - memcpy(&k2, key + 8, 8); - DES_set_key_unchecked(&kk,&ks); - DES_set_key_unchecked(&k2,&ks2); - - DES_cbc_cksum_3des(block, out, len ,&ks, &ks2, icv); + DES_cbc_cksum_3des(ctx, block, out, len, key, icv); free(block); return 0; @@ -211,7 +197,7 @@ sm_gp_init_session(struct sc_context *ctx, struct sm_gp_session *gp_session, if (!adata || adata_len < 8) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(ctx, "SM GP init session: auth.data %s", sc_dump_hex(adata, 8)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP init session: auth.data %s", sc_dump_hex(adata, 8)); gp_session->session_enc = sc_gp_get_session_key(ctx, gp_session, gp_keyset->enc); gp_session->session_mac = sc_gp_get_session_key(ctx, gp_session, gp_keyset->mac); @@ -220,19 +206,19 @@ sm_gp_init_session(struct sc_context *ctx, struct sm_gp_session *gp_session, LOG_TEST_RET(ctx, SC_ERROR_SM_NO_SESSION_KEYS, "SM GP init session: get session keys error"); memcpy(gp_session->session_kek, gp_keyset->kek, 16); - sc_log(ctx, "SM GP init session: session ENC: %s", sc_dump_hex(gp_session->session_enc, 16)); - sc_log(ctx, "SM GP init session: session MAC: %s", sc_dump_hex(gp_session->session_mac, 16)); - sc_log(ctx, "SM GP init session: session KEK: %s", sc_dump_hex(gp_session->session_kek, 16)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP init session: session ENC: %s", sc_dump_hex(gp_session->session_enc, 16)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP init session: session MAC: %s", sc_dump_hex(gp_session->session_mac, 16)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP init session: session KEK: %s", sc_dump_hex(gp_session->session_kek, 16)); memset(cksum, 0, sizeof(cksum)); - rv = sm_gp_get_cryptogram(gp_session->session_enc, gp_session->host_challenge, gp_session->card_challenge, cksum, sizeof(cksum)); + rv = sm_gp_get_cryptogram(ctx, gp_session->session_enc, gp_session->host_challenge, gp_session->card_challenge, cksum, sizeof(cksum)); LOG_TEST_RET(ctx, rv, "SM GP init session: cannot get cryptogram"); - sc_log(ctx, "SM GP init session: cryptogram: %s", sc_dump_hex(cksum, 8)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP init session: cryptogram: %s", sc_dump_hex(cksum, 8)); if (memcmp(cksum, adata, adata_len)) LOG_FUNC_RETURN(ctx, SC_ERROR_SM_AUTHENTICATION_FAILED); - sc_log(ctx, "SM GP init session: card authenticated"); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP init session: card authenticated"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } @@ -257,7 +243,7 @@ sm_gp_external_authentication(struct sc_context *ctx, struct sm_info *sm_info, struct sc_apdu *apdu = NULL; unsigned char host_cryptogram[8], raw_apdu[SC_MAX_APDU_BUFFER_SIZE]; struct sm_gp_session *gp_session = &sm_info->session.gp; - DES_cblock mac; + sm_des_cblock mac; int rv, offs = 0; LOG_FUNC_CALLED(ctx); @@ -278,12 +264,12 @@ sm_gp_external_authentication(struct sc_context *ctx, struct sm_info *sm_info, rv = sm_gp_init_session(ctx, gp_session, init_data + 20, 8); LOG_TEST_RET(ctx, rv, "SM GP authentication: init session error"); - rv = sm_gp_get_cryptogram(gp_session->session_enc, + rv = sm_gp_get_cryptogram(ctx, gp_session->session_enc, gp_session->card_challenge, gp_session->host_challenge, host_cryptogram, sizeof(host_cryptogram)); LOG_TEST_RET(ctx, rv, "SM GP authentication: get host cryptogram error"); - sc_log(ctx, "SM GP authentication: host_cryptogram:%s", sc_dump_hex(host_cryptogram, 8)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP authentication: host_cryptogram:%s", sc_dump_hex(host_cryptogram, 8)); rv = rdata->alloc(rdata, &new_rapdu); LOG_TEST_RET(ctx, rv, "SM GP authentication: cannot allocate remote APDU"); @@ -300,7 +286,7 @@ sm_gp_external_authentication(struct sc_context *ctx, struct sm_info *sm_info, memcpy(raw_apdu + offs, host_cryptogram, 8); offs += 8; - rv = sm_gp_get_mac(gp_session->session_mac, &gp_session->mac_icv, raw_apdu, offs, &mac); + rv = sm_gp_get_mac(ctx, gp_session->session_mac, &gp_session->mac_icv, raw_apdu, offs, &mac); LOG_TEST_RET(ctx, rv, "SM GP authentication: get MAC error"); memcpy(new_rapdu->sbuf, host_cryptogram, 8); @@ -316,12 +302,13 @@ sm_gp_encrypt_command_data(struct sc_context *ctx, unsigned char *session_key, const unsigned char *in, size_t in_len, unsigned char **out, size_t *out_len) { unsigned char *data = NULL; - int rv, len; + int rv; + size_t len; if (!out || !out_len) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "SM GP encrypt command data error"); - sc_log(ctx, + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP encrypt command data(len:%"SC_FORMAT_LEN_SIZE_T"u,%p)", in_len, in); if (in==NULL || in_len==0) { @@ -331,7 +318,7 @@ sm_gp_encrypt_command_data(struct sc_context *ctx, unsigned char *session_key, } len = in_len + 8; - len -= (len%8); + len -= (len % 8); data = calloc(1, len); if (!data) @@ -357,7 +344,7 @@ sm_gp_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, struct sm_gp_session *gp_session = &sm_info->session.gp; unsigned gp_level = sm_info->session.gp.params.level; unsigned gp_index = sm_info->session.gp.params.index; - DES_cblock mac; + sm_des_cblock mac; unsigned char *encrypted = NULL; size_t encrypted_len = 0; int rv; @@ -365,7 +352,7 @@ sm_gp_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, LOG_FUNC_CALLED(ctx); apdu_data = (unsigned char *)apdu->data; - sc_log(ctx, + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP securize APDU(cse:%X,cla:%X,ins:%X,data(len:%"SC_FORMAT_LEN_SIZE_T"u,%p),lc:%"SC_FORMAT_LEN_SIZE_T"u,GP level:%X,GP index:%X", apdu->cse, apdu->cla, apdu->ins, apdu->datalen, apdu->data, apdu->lc, gp_level, gp_index); @@ -384,10 +371,12 @@ sm_gp_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, if (sm_gp_encrypt_command_data(ctx, gp_session->session_enc, apdu->data, apdu->datalen, &encrypted, &encrypted_len)) LOG_TEST_RET(ctx, SC_ERROR_SM_ENCRYPT_FAILED, "SM GP securize APDU: data encryption error"); - if (encrypted_len + 8 > SC_MAX_APDU_BUFFER_SIZE) - LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "SM GP securize APDU: not enough place for encrypted data"); + if (encrypted_len + 8 > SC_MAX_APDU_BUFFER_SIZE) { + rv = SC_ERROR_BUFFER_TOO_SMALL; + LOG_TEST_GOTO_ERR(ctx, rv, "SM GP securize APDU: not enough place for encrypted data"); + } - sc_log(ctx, + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP securize APDU: encrypted length %"SC_FORMAT_LEN_SIZE_T"u", encrypted_len); } @@ -403,8 +392,8 @@ sm_gp_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, memcpy(buff + 5, apdu_data, apdu->datalen); - rv = sm_gp_get_mac(gp_session->session_mac, &gp_session->mac_icv, buff, 5 + apdu->datalen, &mac); - LOG_TEST_RET(ctx, rv, "SM GP securize APDU: get MAC error"); + rv = sm_gp_get_mac(ctx, gp_session->session_mac, &gp_session->mac_icv, buff, 5 + (int)apdu->datalen, &mac); + LOG_TEST_GOTO_ERR(ctx, rv, "SM GP securize APDU: get MAC error"); if (gp_level == SM_GP_SECURITY_MAC) { memcpy(apdu_data + apdu->datalen, mac, 8); @@ -432,10 +421,13 @@ sm_gp_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, apdu->cse = SC_APDU_CASE_3_SHORT; free(encrypted); + encrypted = NULL; } memcpy(sm_info->session.gp.mac_icv, mac, 8); +err: + free(encrypted); LOG_FUNC_RETURN(ctx, rv); } diff --git a/src/smm/sm-module.h b/src/smm/sm-module.h index 0275fc1b65..64c3326338 100644 --- a/src/smm/sm-module.h +++ b/src/smm/sm-module.h @@ -17,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SM_MODULE_H @@ -34,9 +34,12 @@ extern "C" { #include "sm/sm-common.h" /* Global Platform definitions */ -int sm_gp_get_mac(unsigned char *key, DES_cblock *icv, unsigned char *in, int in_len, - DES_cblock *out); -int sm_gp_get_cryptogram(unsigned char *session_key, unsigned char *left, unsigned char *right, +int sm_gp_get_mac(struct sc_context *ctx, + unsigned char *key, sm_des_cblock *icv, + unsigned char *in, int in_len, + sm_des_cblock *out); +int sm_gp_get_cryptogram(struct sc_context *ctx, unsigned char *session_key, + unsigned char *left, unsigned char *right, unsigned char *out, int out_len); int sm_gp_external_authentication(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *init_data, size_t init_len, diff --git a/src/smm/smm-local.c b/src/smm/smm-local.c index e6537cc034..1d9878ad84 100644 --- a/src/smm/smm-local.c +++ b/src/smm/smm-local.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H @@ -65,8 +65,8 @@ sm_gp_config_get_keyset(struct sc_context *ctx, struct sm_info *sm_info) size_t hex_len = sizeof(hex); int rv, ii; - sc_log(ctx, "SM get KMC from config section '%s'", sm_info->config_section); - for (ii = 0; ctx->conf_blocks[ii]; ii++) { + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get KMC from config section '%s'", sm_info->config_section); + for (ii = 0; ctx->conf_blocks[ii]; ii++) { blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[ii], "secure_messaging", sm_info->config_section); if (blocks) { sm_conf_block = blocks[0]; @@ -83,17 +83,17 @@ sm_gp_config_get_keyset(struct sc_context *ctx, struct sm_info *sm_info) rv = sc_hex_to_bin(kmc, hex, &hex_len); if (rv) { - sc_log(ctx, "SM get KMC: hex to bin failed for '%s'; error %i", kmc, rv); + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "SM get KMC: hex to bin failed for '%s'; error %i", kmc, rv); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } - sc_log(ctx, "SM type:%X, KMC(%"SC_FORMAT_LEN_SIZE_T"u) %s", - sm_info->sm_type, hex_len, sc_dump_hex(hex, hex_len)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM type:%X, KMC(%"SC_FORMAT_LEN_SIZE_T"u) %s", + sm_info->sm_type, hex_len, sc_dump_hex(hex, hex_len)); if (hex_len != 16 && hex_len != 48 ) return SC_ERROR_INVALID_DATA; memcpy(gp_keyset->kmc, hex, hex_len); - gp_keyset->kmc_len = hex_len; + gp_keyset->kmc_len = (unsigned)hex_len; return SC_SUCCESS; } @@ -123,7 +123,7 @@ sm_cwa_config_get_keyset(struct sc_context *ctx, struct sm_info *sm_info) break; } - sc_log(ctx, "CRT(algo:%X,ref:%X)", crt_at->algo, crt_at->refs[0]); + sc_debug(ctx, SC_LOG_DEBUG_SM, "CRT(algo:%X,ref:%X)", crt_at->algo, crt_at->refs[0]); /* Keyset ENC */ if (sm_info->current_aid.len && (crt_at->refs[0] & IASECC_OBJECT_REF_LOCAL)) snprintf(name, sizeof(name), "keyset_%s_%02i_enc", @@ -132,12 +132,12 @@ sm_cwa_config_get_keyset(struct sc_context *ctx, struct sm_info *sm_info) snprintf(name, sizeof(name), "keyset_%02i_enc", ref); value = scconf_get_str(sm_conf_block, name, NULL); if (!value) { - sc_log(ctx, "No %s value in OpenSC config", name); + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "No %s value in OpenSC config", name); return SC_ERROR_SM_KEYSET_NOT_FOUND; } - sc_log(ctx, "keyset::enc(%"SC_FORMAT_LEN_SIZE_T"u) %s", strlen(value), - value); + sc_debug(ctx, SC_LOG_DEBUG_SM, "keyset::enc(%"SC_FORMAT_LEN_SIZE_T"u) %s", strlen(value), + value); if (strlen(value) == 16) { memcpy(cwa_keyset->enc, value, 16); } @@ -145,18 +145,18 @@ sm_cwa_config_get_keyset(struct sc_context *ctx, struct sm_info *sm_info) hex_len = sizeof(hex); rv = sc_hex_to_bin(value, hex, &hex_len); if (rv) { - sc_log(ctx, "SM get %s: hex to bin failed for '%s'; error %i", name, value, rv); + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "SM get %s: hex to bin failed for '%s'; error %i", name, value, rv); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } - sc_log(ctx, "ENC(%"SC_FORMAT_LEN_SIZE_T"u) %s", hex_len, - sc_dump_hex(hex, hex_len)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "ENC(%"SC_FORMAT_LEN_SIZE_T"u) %s", hex_len, + sc_dump_hex(hex, hex_len)); if (hex_len != 16) return SC_ERROR_INVALID_DATA; memcpy(cwa_keyset->enc, hex, hex_len); } - sc_log(ctx, "%s %s", name, sc_dump_hex(cwa_keyset->enc, 16)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "%s %s", name, sc_dump_hex(cwa_keyset->enc, 16)); /* Keyset MAC */ if (sm_info->current_aid.len && (crt_at->refs[0] & IASECC_OBJECT_REF_LOCAL)) @@ -166,12 +166,12 @@ sm_cwa_config_get_keyset(struct sc_context *ctx, struct sm_info *sm_info) snprintf(name, sizeof(name), "keyset_%02i_mac", ref); value = scconf_get_str(sm_conf_block, name, NULL); if (!value) { - sc_log(ctx, "No %s value in OpenSC config", name); + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "No %s value in OpenSC config", name); return SC_ERROR_SM_KEYSET_NOT_FOUND; } - sc_log(ctx, "keyset::mac(%"SC_FORMAT_LEN_SIZE_T"u) %s", strlen(value), - value); + sc_debug(ctx, SC_LOG_DEBUG_SM, "keyset::mac(%"SC_FORMAT_LEN_SIZE_T"u) %s", strlen(value), + value); if (strlen(value) == 16) { memcpy(cwa_keyset->mac, value, 16); } @@ -179,18 +179,18 @@ sm_cwa_config_get_keyset(struct sc_context *ctx, struct sm_info *sm_info) hex_len = sizeof(hex); rv = sc_hex_to_bin(value, hex, &hex_len); if (rv) { - sc_log(ctx, "SM get '%s': hex to bin failed for '%s'; error %i", name, value, rv); + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "SM get '%s': hex to bin failed for '%s'; error %i", name, value, rv); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } - sc_log(ctx, "MAC(%"SC_FORMAT_LEN_SIZE_T"u) %s", hex_len, - sc_dump_hex(hex, hex_len)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "MAC(%"SC_FORMAT_LEN_SIZE_T"u) %s", hex_len, + sc_dump_hex(hex, hex_len)); if (hex_len != 16) return SC_ERROR_INVALID_DATA; memcpy(cwa_keyset->mac, hex, hex_len); } - sc_log(ctx, "%s %s", name, sc_dump_hex(cwa_keyset->mac, 16)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "%s %s", name, sc_dump_hex(cwa_keyset->mac, 16)); cwa_keyset->sdo_reference = crt_at->refs[0]; @@ -203,33 +203,33 @@ sm_cwa_config_get_keyset(struct sc_context *ctx, struct sm_info *sm_info) hex_len = sizeof(hex); rv = sc_hex_to_bin(value, hex, &hex_len); if (rv) { - sc_log(ctx, "SM get 'ifd_serial': hex to bin failed for '%s'; error %i", value, rv); + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "SM get 'ifd_serial': hex to bin failed for '%s'; error %i", value, rv); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } if (hex_len != sizeof(cwa_session->ifd.sn)) { - sc_log(ctx, - "SM get 'ifd_serial': invalid IFD serial length: %"SC_FORMAT_LEN_SIZE_T"u", - hex_len); + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, + "SM get 'ifd_serial': invalid IFD serial length: %"SC_FORMAT_LEN_SIZE_T"u", + hex_len); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } memcpy(cwa_session->ifd.sn, hex, hex_len); - rv = RAND_bytes(cwa_session->ifd.rnd, 8); - if (!rv) { - sc_log(ctx, "Generate random error: %i", rv); + rv = RAND_bytes(cwa_session->ifd.rnd, 8); + if (!rv) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Generate random error: %i", rv); return SC_ERROR_SM_RAND_FAILED; } - rv = RAND_bytes(cwa_session->ifd.k, 32); - if (!rv) { - sc_log(ctx, "Generate random error: %i", rv); + rv = RAND_bytes(cwa_session->ifd.k, 32); + if (!rv) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Generate random error: %i", rv); return SC_ERROR_SM_RAND_FAILED; } - sc_log(ctx, "IFD.Serial: %s", sc_dump_hex(cwa_session->ifd.sn, sizeof(cwa_session->ifd.sn))); - sc_log(ctx, "IFD.Rnd: %s", sc_dump_hex(cwa_session->ifd.rnd, sizeof(cwa_session->ifd.rnd))); - sc_log(ctx, "IFD.K: %s", sc_dump_hex(cwa_session->ifd.k, sizeof(cwa_session->ifd.k))); + sc_debug(ctx, SC_LOG_DEBUG_SM, "IFD.Serial: %s", sc_dump_hex(cwa_session->ifd.sn, sizeof(cwa_session->ifd.sn))); + sc_debug(ctx, SC_LOG_DEBUG_SM, "IFD.Rnd: %s", sc_dump_hex(cwa_session->ifd.rnd, sizeof(cwa_session->ifd.rnd))); + sc_debug(ctx, SC_LOG_DEBUG_SM, "IFD.K: %s", sc_dump_hex(cwa_session->ifd.k, sizeof(cwa_session->ifd.k))); return SC_SUCCESS; } @@ -250,24 +250,24 @@ initialize(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_dat if (!sm_info) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(ctx, "Current AID: %s", sc_dump_hex(sm_info->current_aid.value, sm_info->current_aid.len)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "Current AID: %s", sc_dump_hex(sm_info->current_aid.value, sm_info->current_aid.len)); switch (sm_info->sm_type) { - case SM_TYPE_GP_SCP01: - rv = sm_gp_config_get_keyset(ctx, sm_info); - LOG_TEST_RET(ctx, rv, "SM gp configuration error"); - - rv = sm_gp_initialize(ctx, sm_info, out); - LOG_TEST_RET(ctx, rv, "SM gp initializing error"); - break; - case SM_TYPE_CWA14890: - rv = sm_cwa_config_get_keyset(ctx, sm_info); - LOG_TEST_RET(ctx, rv, "SM iasecc configuration error"); - - rv = sm_cwa_initialize(ctx, sm_info, out); - LOG_TEST_RET(ctx, rv, "SM iasecc initializing error"); - break; - default: - LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "unsupported SM type"); + case SM_TYPE_GP_SCP01: + rv = sm_gp_config_get_keyset(ctx, sm_info); + LOG_TEST_RET(ctx, rv, "SM gp configuration error"); + + rv = sm_gp_initialize(ctx, sm_info, out); + LOG_TEST_RET(ctx, rv, "SM gp initializing error"); + break; + case SM_TYPE_CWA14890: + rv = sm_cwa_config_get_keyset(ctx, sm_info); + LOG_TEST_RET(ctx, rv, "SM iasecc configuration error"); + + rv = sm_cwa_initialize(ctx, sm_info, out); + LOG_TEST_RET(ctx, rv, "SM iasecc initializing error"); + break; + default: + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "unsupported SM type"); }; LOG_FUNC_RETURN(ctx, rv); @@ -290,8 +290,8 @@ get_apdus(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *init_d if (!sm_info) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); - sc_log(ctx, "SM get APDUs: out:%p", out); - sc_log(ctx, "SM get APDUs: serial %s", sc_dump_hex(sm_info->serialnr.value, sm_info->serialnr.len)); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get APDUs: out:%p", out); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get APDUs: serial %s", sc_dump_hex(sm_info->serialnr.value, sm_info->serialnr.len)); if (sm_info->card_type == SC_CARD_TYPE_OBERTHUR_AUTHENTIC_3_2) { rv = sm_authentic_get_apdus(ctx, sm_info, init_data, init_len, out, 1); @@ -320,8 +320,8 @@ finalize(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data int rv = SC_ERROR_INTERNAL; LOG_FUNC_CALLED(ctx); - sc_log(ctx, "SM finalize: out buffer(%"SC_FORMAT_LEN_SIZE_T"u) %p", - out_len, out); + sc_debug(ctx, SC_LOG_DEBUG_SM, "SM finalize: out buffer(%"SC_FORMAT_LEN_SIZE_T"u) %p", + out_len, out); if (!sm_info || !rdata) LOG_FUNC_RETURN(ctx, SC_SUCCESS); @@ -344,7 +344,6 @@ int module_init(struct sc_context *ctx, char *data) { - sc_log(ctx, "Module init data '%s'", data); return SC_SUCCESS; } @@ -357,7 +356,6 @@ module_init(struct sc_context *ctx, char *data) int module_cleanup(struct sc_context *ctx) { - sc_log(ctx, "Module cleanup: TODO"); return SC_SUCCESS; } @@ -365,7 +363,6 @@ module_cleanup(struct sc_context *ctx) int test(struct sc_context *ctx, struct sm_info *info, char *out, size_t *out_len) { - sc_log(ctx, "Test"); return SC_SUCCESS; } diff --git a/src/smm/smm-local.exports b/src/smm/smm-local.exports index 7c131b8bf9..98d9c15fb2 100644 --- a/src/smm/smm-local.exports +++ b/src/smm/smm-local.exports @@ -1,6 +1,6 @@ -initialize -get_apdus -finalize -module_cleanup -module_init -test +initialize +get_apdus +finalize +module_cleanup +module_init +test diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 2a869d894a..cd5573068a 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -3,7 +3,7 @@ include $(top_srcdir)/win32/ltrc.inc MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak -SUBDIRS = regression +SUBDIRS = regression p11test fuzzing unittests noinst_PROGRAMS = base64 lottery p15dump pintest prngtest AM_CPPFLAGS = -I$(top_srcdir)/src diff --git a/src/tests/Makefile.mak b/src/tests/Makefile.mak index 608807e729..5c02de90a4 100644 --- a/src/tests/Makefile.mak +++ b/src/tests/Makefile.mak @@ -1,6 +1,6 @@ TOPDIR = ..\.. -TARGETS = base64.exe p15dump.exe \ +TARGETS = base64.exe p15dump.exe opensc-minidriver-test.exe \ p15dump.exe pintest.exe # prngtest.exe lottery.exe OBJECTS = print.obj sc-test.obj $(TOPDIR)\win32\versioninfo.res @@ -12,7 +12,10 @@ all: $(TARGETS) $(TARGETS): $(OBJECTS) $(LIBS) +opensc-minidriver-test.exe: + cl $(COPTS) /c $*.c + link $(LINKFLAGS) /out:$@ $*.obj bcrypt.lib ncrypt.lib crypt32.lib winscard.lib + .c.exe: cl $(COPTS) /c $< - link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj $(OBJECTS) $(LIBS) - if EXIST $@.manifest mt -manifest $@.manifest -outputresource:$@;1 + link $(LINKFLAGS) /out:$@ $*.obj $(OBJECTS) $(LIBS) diff --git a/src/tests/base64.c b/src/tests/base64.c index f868d12d8c..1bdc09deee 100644 --- a/src/tests/base64.c +++ b/src/tests/base64.c @@ -5,12 +5,23 @@ #include "libopensc/opensc.h" #include "libopensc/asn1.h" +#ifdef _MSC_VER +# ifndef _SSIZE_T_DEFINED +# undef ssize_t +# include + typedef _W64 SSIZE_T ssize_t; +# define _SSIZE_T_DEFINED +# endif /* _SSIZE_T_DEFINED */ +#endif /* _MSC_VER */ + + int main(int argc, char *argv[]) { int len, r = 1; FILE *inf = NULL; u8 buf[8192]; u8 outbuf[8192]; + ssize_t sz; if (argc != 2) { fprintf(stderr, "Usage: base64 \n"); @@ -21,18 +32,18 @@ int main(int argc, char *argv[]) perror(argv[1]); goto err; } - len = fread(buf, 1, sizeof(buf), inf); - if (len < 0) { + sz = fread(buf, 1, sizeof(buf), inf); + if (sz < 0) { perror("fread"); goto err; } - if (len == 8192) { + if (sz == 8192) { fprintf(stderr, "Too long input file.\n"); goto err; } len = sc_base64_decode((const char *) buf, outbuf, sizeof(outbuf)); if (len < 0) { - fprintf(stderr, "Base64 decoding failed: %s\n", sc_strerror(len)); + fprintf(stderr, "Base64 decoding failed: %s\n", sc_strerror(len)); goto err; } fwrite(outbuf, len, 1, stdout); diff --git a/src/tests/fuzzing/Makefile.am b/src/tests/fuzzing/Makefile.am new file mode 100644 index 0000000000..ea643f635e --- /dev/null +++ b/src/tests/fuzzing/Makefile.am @@ -0,0 +1,58 @@ +AM_CPPFLAGS = -I$(top_srcdir)/src -D'SC_PKCS15_PROFILE_DIRECTORY="$(pkgdatadir)"' \ + -D'DEFAULT_PKCS11_PROVIDER="$(DEFAULT_PKCS11_PROVIDER)"' +AM_CFLAGS = -g -O0 $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_READLINE_CFLAGS) $(PTHREAD_CFLAGS) +LIBS = $(FUZZING_LIBS)\ + $(top_builddir)/src/libopensc/libopensc.la \ + $(top_builddir)/src/common/libscdl.la \ + $(top_builddir)/src/pkcs15init/libpkcs15init.la \ + $(top_builddir)/src/common/libcompat.la + +noinst_PROGRAMS = fuzz_asn1_print fuzz_asn1_sig_value fuzz_pkcs15_decode fuzz_pkcs15_reader \ + fuzz_scconf_parse_string fuzz_pkcs15_encode fuzz_card \ + fuzz_pkcs15_tool fuzz_pkcs15_crypt fuzz_pkcs11_uri + +if ENABLE_STATIC +noinst_PROGRAMS += fuzz_pkcs15init +endif + +if ENABLE_OPENSSL +noinst_PROGRAMS += fuzz_piv_tool +endif + +if !ENABLE_SHARED +noinst_PROGRAMS += fuzz_pkcs11 +endif + +noinst_HEADERS = fuzzer_reader.h fuzzer_tool.h + +ADDITIONAL_SRC = +if !ENABLE_FUZZING +ADDITIONAL_SRC += fuzzer.c +endif + +fuzz_asn1_print_SOURCES = fuzz_asn1_print.c $(ADDITIONAL_SRC) +fuzz_asn1_sig_value_SOURCES = fuzz_asn1_sig_value.c $(ADDITIONAL_SRC) +fuzz_pkcs15_decode_SOURCES = fuzz_pkcs15_decode.c fuzzer_reader.c $(ADDITIONAL_SRC) +fuzz_pkcs15_reader_SOURCES = fuzz_pkcs15_reader.c fuzzer_reader.c $(ADDITIONAL_SRC) +fuzz_scconf_parse_string_SOURCES = fuzz_scconf_parse_string.c $(ADDITIONAL_SRC) +fuzz_pkcs15init_SOURCES = fuzz_pkcs15init.c fuzzer_reader.c $(ADDITIONAL_SRC) +fuzz_pkcs15init_LDADD = $(OPTIONAL_OPENSSL_LIBS) $(OPENPACE_LIBS) +fuzz_pkcs15init_LDFLAGS = -static +fuzz_pkcs15_encode_SOURCES = fuzz_pkcs15_encode.c fuzzer_reader.c $(ADDITIONAL_SRC) +fuzz_card_SOURCES = fuzz_card.c fuzzer_reader.c $(ADDITIONAL_SRC) +fuzz_piv_tool_SOURCES = fuzz_piv_tool.c fuzzer_reader.c fuzzer_tool.c $(ADDITIONAL_SRC) \ + ../../tools/util.c +fuzz_piv_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) +fuzz_pkcs15_tool_SOURCES = fuzz_pkcs15_tool.c fuzzer_reader.c fuzzer_tool.c $(ADDITIONAL_SRC) \ + ../../tools/util.c ../../pkcs11/pkcs11-display.c +fuzz_pkcs15_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) +fuzz_pkcs15_crypt_SOURCES = fuzz_pkcs15_crypt.c fuzzer_reader.c fuzzer_tool.c $(ADDITIONAL_SRC) \ + ../../tools/util.c +fuzz_pkcs15_crypt_LDADD = $(OPTIONAL_OPENSSL_LIBS) +fuzz_pkcs11_SOURCES = fuzz_pkcs11.c fuzzer_reader.c fuzzer_tool.c $(ADDITIONAL_SRC) +fuzz_pkcs11_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(PTHREAD_CFLAGS) +fuzz_pkcs11_LDADD = \ + $(top_builddir)/src/common/libpkcs11.la \ + $(OPTIONAL_OPENSSL_LIBS) \ + $(top_builddir)/src/pkcs11/libopensc-pkcs11.la +fuzz_pkcs11_uri_SOURCES = fuzz_pkcs11_uri.c ../../tools/pkcs11_uri.c $(ADDITIONAL_SRC) diff --git a/src/tests/fuzzing/README.md b/src/tests/fuzzing/README.md new file mode 100644 index 0000000000..1225e8176b --- /dev/null +++ b/src/tests/fuzzing/README.md @@ -0,0 +1,225 @@ +# Fuzzing in OpenSC + +OpenSC is part of the [OSS-Fuzz project](https://google.github.io/oss-fuzz/), which provides continuous fuzzing support for open-source projects. +Fuzzer [libFuzzer](https://llvm.org/docs/LibFuzzer.html) can be used for local testing. + +To the terms used, _fuzzer_ refers to a program that injects malformed inputs to the system under test; _fuzz target_ is a program that accepts data buffer, processes it and passes the data to the tested interface. + +## Building + +### Building for fuzzing +Successful build of fuzz targets requires `./configure` run with correctly set CC, CFLAGS and FUZZING_LIBS. Note that some of the fuzz targets can be built only with the `--disable-shared` option. + +Example configuration for libFuzzer: +``` +./configure --disable-optimization --disable-shared --disable-pcsc --enable-ctapi --enable-fuzzing CC=clang CFLAGS=-fsanitize=fuzzer-no-link FUZZING_LIBS=-fsanitize=fuzzer +``` + +To add some of the LLVM Sanitizers, modify `FUZZING_LIBS`: +``` +FUZZING_LIBS=-fsanitize=fuzzer,address,undefined +``` +Sanitizers can also be modified by [flags](https://github.com/google/sanitizers/wiki/SanitizerCommonFlags). + +### Building without fuzzing support +When fuzzing is not enabled explicitly by `--enable-fuzzing`, fuzz targets are built without fuzzing support. They can be used for local regression testing and accept one argument for filename with input for the testing functions. + +Example of testing without fuzzing: +``` +./fuzz_pkcs15_reader ./input_file +``` + +## Reproducing issues +Some of the issues are not reproducible when build outside of the fuzzing images. In that case, the safest +option is to reproduce them with the python/docker helpers provided by [oss-fuzz](https://github.com/google/oss-fuzz/). +You can build latest fuzzers in the oss-fuzz containers with the following steps: +``` +python3 infra/helper.py pull_images +python3 infra/helper.py build_image opensc +python3 infra/helper.py build_fuzzers opensc +``` +After that, you can download reproducer from the oss-fuzz dashboard and run it locally in the container: +``` +python3 infra/helper.py reproduce opensc fuzz_pkcs15_decode /path/to/testcase +``` +For creating a docker image for debugging the issue, run the following command: +``` +python infra/helper.py shell base-runner-debug +``` +This gives a shell inside a Docker image containing GDB. Now you can reproduce the issue and inspect the backtrace: +``` +# Shell inside Docker debug image +gdb -ex 'break __sanitizer::Die' -ex 'run' --args /out/opensc/fuzz_pkcs15_decode /out/opensc/testcase +``` + +### Expanding incomplete backtraces +Sometimes the backtrace visible in the oss-fuzz dashboard is not useful, for example showing only part of the +trace ending inside of (outdated) openssl code: +``` +Direct leak of 168 byte(s) in 1 object(s) allocated from: + #0 0x5318e6 in malloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:69:3 + #1 0x7faca8714c0d in CRYPTO_zalloc +``` +In that case, you can use the address sanitizer option `fast_unwind_on_malloc=0` in `ASAN_OPTIONS` environment +variable to expand this trace, for example: +``` +python3 infra/helper.py reproduce -eASAN_OPTIONS='fast_unwind_on_malloc=0' opensc fuzz_pkcs15_decode testcase +``` + +## Fuzzing +### libFuzzer +See libFuzzer [documentation](https://llvm.org/docs/LibFuzzer.html) for details. + +Fuzzing with a predefined corpus can be run like this: +``` +./fuzz_pkcs15_reader corpus/fuzz_pkcs15_reader +``` +Newly generated input files are stored in the corpus directory. + +By default, `stdout` is closed for fuzzing. However, some fuzz targets may output to `stderr`. You can suppress `stderr` with the `-close_fd_mask` option (see libFuzzer). + +To execute the fuzz target on one input, try: +``` +./fuzz_pkcs15_reader ./test-case +``` + +## Corpus + +### Corpus for `fuzz_pkcs15_reader` + +The corpus files for the `fuzz_pkcs15_reader` are interpreted by the virtual +reader as follows: + + * first two bytes denote the block length N as an unsigned integer. The endianness + depends on the architecture + * the following block of the length N + +The first block is always the ATR of the card, which is very frequently used +for card detection. + +All the other following blocks are used as replies from the emulated card. + +Example block: +``` +0f 00 + -- length indicator saying next block is 15 bytes long +3b f5 96 00 00 81 31 fe 45 4d 79 45 49 44 14 + -- the 15 bytes block (in this case ATR) + +29 00 + -- the second block length of 41 bytes +6f 25 81 02 7f ff 82 01 38 83 02 50 15 86 03 11 +1f ff 85 02 00 02 8a 01 01 84 0c a0 00 00 00 63 +50 4b 43 53 2d 31 35 90 00 + -- 41 bytes data block (APDU response) +``` + +### How to generate corpus files from existing cards + +Modify the `src/libopensc/reader-pcsc.c` and uncomment the following line: +``` +#define APDU_LOG_FILE "apdulog" +``` +and rebuild OpenSC. Then run any OpenSC tool talking to the card. For example +``` +./src/tools/pkcs11-tool -L --module ./src/pkcs11/.libs/opensc-pkcs11.so +``` +Any APDU returned from the card is now logged into the file `apdulog` in the +format expected by the `fuzz_pkcs15_reader` fuzz target. It is also prefixed with +the ATR of the connected card as expected by the fuzz target. This file can be used +as a starting point that gets through the card detection but does not go into +all the operations the fuzz target attempts later. + +### The pkcs15init fuzz target + +The pkcs15init fuzz target consists of two separate parts. The first one is parsing +the profile file, which is separated from the rest of the input with a NULL +byte (0x00). The rest is interpreted as in the case of the `fuzz_pkcs15_reader`. + +When creating a corpus for this fuzz target, stuff gets messier because: + + * The first part is the profile file + * The `pkcs15-init` can do only one operation at a time, so we need to skip the + card init when concatenating the APDU traces + +So at first, erase the card and move away the apdulog: +``` +./src/tools/pkcs15-init --erase-card --so-pin 12345678 +$ mv apdulog /tmp/apdu_erase +``` +Then prepare the separate files for each operation in the fuzz target: +``` +$ ./src/tools/pkcs15-init -C --pin 123456 --puk 12345678 --so-pin 12345678 --so-puk 12345678 +$ mv apdulog /tmp/apdu_create +$ ./src/tools/pkcs15-init -P -a 1 -l "Basic PIN" --pin 1234555678 --puk 12345678 +$ mv apdulog /tmp/apdu_create_pin +$ ./src/tools/pkcs15-init --store-data /path/to/any_file --label label +$ mv apdulog /tmp/apdu_store_data +$ ./src/tools/pkcs15-init --generate-key rsa:1024 --auth-id 01 --so-pin 12345678 --pin 1234555678 +$ mv apdulog /tmp/apdu_generate_rsa +$ ./src/tools/pkcs15-init --generate-key ec:prime256v1 --auth-id 01 --so-pin 12345678 --pin 123455678 +$ mv apdulog /tmp/apdu_generate_ecdsa +$ ./src/tools/pkcs15-init -F +$ mv apdulog /tmp/apdu_finalize +``` + +Now, construct the corpus file: +* insert profile and zero bytes as a delimiter +* `apdu\_create` can be used as it is +* from `apdu\_create\_pin` remove the part for connecting the card +* from `apdu\_store\_data` remove some central parts since testing data is smaller than data used in apdu +* `apdu_generate_*` and `apdu\_finalize` need to skip connecting card and `sc_pcks15_bind()` +* symmetric key generation is not supported on the card; let's fill that part with some dummy values from generating RSA keys +* `apdu\_erase` needs to skip part for connecting card + +``` +SKIP=1257 +( \ + cat file.profile; printf "\x00"; \ + cat tmp/apdu_create; \ + dd if=tmp/apdu_create_pin bs=1 skip=421; \ + dd if=tmp/apdu_store_data bs=1 skip=1257 count=1675; \ + dd if=tmp/apdu_store_data bs=1 skip=3020; \ + dd if=tmp/apdu_generate_rsa bs=1 skip=$SKIP; \ + dd if=tmp/apdu_generate_ecdsa bs=1 skip=$SKIP; \ + dd if=tmp/apdu_generate_rsa bs=1 skip=$SKIP count=5304; \ + dd if=tmp/apdu_generate_rsa bs=1 skip=$SKIP count=5304; \ + dd if=tmp/apdu_generate_rsa bs=1 skip=$SKIP count=5304; \ + dd if=tmp/apdu_finalize bs=1 skip=$SKIP; \ + dd if=tmp/apdu_erase bs=1 skip=421; \ +) > tmp/testcase +``` + +Now, let's try to feed the data into the fuzz target: +``` +OPENSC_DEBUG=9 ./src/tests/fuzzing/fuzz_pkcs15init_profile /tmp/testcase +``` +The debug log should show the card detection, which goes through and then some +pkcs15init operations. + +### The piv-tool fuzz target + +The `fuzz_piv_tool` target allows testing operations of `piv-tool`. What operation is tested depends of first byte of the fuzzing input: + +* `\x00` tests loading of the object, the input looks as\ +`| \x00 | len1 | len2 | admin key | containerID | \x00 | admin_arg | \x00 | len1 | len2 | file content | APDU part |`[^1] +* `\x01` tests loading of the certificate, the input looks as\ +`| \x01 | len1 | len2 | admin key | ref | \x00 | admin_arg | \x00 | len1 | len2 | file content | APDU part |`[^1] +* `\x02` tests loading of the compressed certificate, the input looks as by loading of certificate +* other values for first byte means that whole `argv` is taken from fuzzing input\ +`| > \x003 | arg_1 | \x00 | arg_2 | \x00 | ... | arg_n | \x00 | \x00 | APDU part |` + +### The pkcs15-tool fuzz target + +The `fuzz_pkcs15_tool` target allows testing operations of `pkcs15-tool`. The options are taken from fuzzing input, it is parsed as\ +`| arg_1 | \x00 | arg_2 | \x00 | ... | arg_n | \x00 | \x00 | APDU part |` + +[^1]: `len1` and `len2` refer to two bytes that are parsed as the length of the content of the file that is extracted from the input + +### The pkcs15-crypt fuzz target + +The `fuzz_pkcs15_crypt` target allows testing operations of `pkcs15-crypt`. What operation is tested depends of first byte of the fuzzing input: + +* the whole `argv` is taken from fuzzing input +* the `-c` and `-s` options are tested with various combinations of other command-line options\ +`| op | hash type | padding | format | aid | aid value | \x00 | id | id value | \x00 | len1 |len2 |file content | APDU part |` diff --git a/src/tests/fuzzing/corpus/fuzz_card/3676fcfa2dba95b7c439b6343228623ac0be93c4 b/src/tests/fuzzing/corpus/fuzz_card/3676fcfa2dba95b7c439b6343228623ac0be93c4 new file mode 100644 index 0000000000..60b89ef8fc Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_card/3676fcfa2dba95b7c439b6343228623ac0be93c4 differ diff --git a/src/tests/fuzzing/corpus/fuzz_card/3e651eeafa4f5ad5bb6e21787ba4ba43af7c6f76 b/src/tests/fuzzing/corpus/fuzz_card/3e651eeafa4f5ad5bb6e21787ba4ba43af7c6f76 new file mode 100644 index 0000000000..9c4954cdae Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_card/3e651eeafa4f5ad5bb6e21787ba4ba43af7c6f76 differ diff --git a/src/tests/fuzzing/corpus/fuzz_card/68446db83043eb66ce144ab42466b39b0675c42d b/src/tests/fuzzing/corpus/fuzz_card/68446db83043eb66ce144ab42466b39b0675c42d new file mode 100644 index 0000000000..ae78419100 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_card/68446db83043eb66ce144ab42466b39b0675c42d differ diff --git a/src/tests/fuzzing/corpus/fuzz_card/995545e7be2e433f450b87c2e9020ab480947bcf b/src/tests/fuzzing/corpus/fuzz_card/995545e7be2e433f450b87c2e9020ab480947bcf new file mode 100644 index 0000000000..ee7f4ad2dd Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_card/995545e7be2e433f450b87c2e9020ab480947bcf differ diff --git a/src/tests/fuzzing/corpus/fuzz_card/c62b44859dfb22dddcf8c19468b61587b02bbd5e b/src/tests/fuzzing/corpus/fuzz_card/c62b44859dfb22dddcf8c19468b61587b02bbd5e new file mode 100644 index 0000000000..a5fe068d7c Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_card/c62b44859dfb22dddcf8c19468b61587b02bbd5e differ diff --git a/src/tests/fuzzing/corpus/fuzz_card/f117a2bbb1ea0617255c7e993914ef9062303580 b/src/tests/fuzzing/corpus/fuzz_card/f117a2bbb1ea0617255c7e993914ef9062303580 new file mode 100644 index 0000000000..f871fc24df Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_card/f117a2bbb1ea0617255c7e993914ef9062303580 differ diff --git a/src/tests/fuzzing/corpus/fuzz_piv_tool/5cc15068920eb3c897b0129a6939e3b01574eb02 b/src/tests/fuzzing/corpus/fuzz_piv_tool/5cc15068920eb3c897b0129a6939e3b01574eb02 new file mode 100644 index 0000000000..6a0d7a0e6e Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_piv_tool/5cc15068920eb3c897b0129a6939e3b01574eb02 differ diff --git a/src/tests/fuzzing/corpus/fuzz_piv_tool/df81168351db4e248a9a915bb521c85dce1d17c8 b/src/tests/fuzzing/corpus/fuzz_piv_tool/df81168351db4e248a9a915bb521c85dce1d17c8 new file mode 100644 index 0000000000..85f4387068 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_piv_tool/df81168351db4e248a9a915bb521c85dce1d17c8 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11/1dca0b7f951ad6c7cbb39e0e5fa1327ada85d4a5 b/src/tests/fuzzing/corpus/fuzz_pkcs11/1dca0b7f951ad6c7cbb39e0e5fa1327ada85d4a5 new file mode 100644 index 0000000000..62849a4651 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs11/1dca0b7f951ad6c7cbb39e0e5fa1327ada85d4a5 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11/334176efba3f10cbbb96b23d04ae03240ed31e0a b/src/tests/fuzzing/corpus/fuzz_pkcs11/334176efba3f10cbbb96b23d04ae03240ed31e0a new file mode 100644 index 0000000000..ae1ef65091 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs11/334176efba3f10cbbb96b23d04ae03240ed31e0a differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11/533432db786d023c678187d79db1860ca1c44056 b/src/tests/fuzzing/corpus/fuzz_pkcs11/533432db786d023c678187d79db1860ca1c44056 new file mode 100644 index 0000000000..aab4e117cd Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs11/533432db786d023c678187d79db1860ca1c44056 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11/6ec181a01600525601900b9fe2b9eacf7d5df43d b/src/tests/fuzzing/corpus/fuzz_pkcs11/6ec181a01600525601900b9fe2b9eacf7d5df43d new file mode 100644 index 0000000000..99034943df Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs11/6ec181a01600525601900b9fe2b9eacf7d5df43d differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11/85bdf4bb93d2f4604fa3e21096d7da552cae8b97 b/src/tests/fuzzing/corpus/fuzz_pkcs11/85bdf4bb93d2f4604fa3e21096d7da552cae8b97 new file mode 100644 index 0000000000..96da74ea45 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs11/85bdf4bb93d2f4604fa3e21096d7da552cae8b97 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11/96ff9ea1b05bc5d0443305fae8ace07732c85359 b/src/tests/fuzzing/corpus/fuzz_pkcs11/96ff9ea1b05bc5d0443305fae8ace07732c85359 new file mode 100644 index 0000000000..7fa6b5c893 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs11/96ff9ea1b05bc5d0443305fae8ace07732c85359 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11/dff1ba4f2e96e390e03144ef40b06981c1a0cf8d b/src/tests/fuzzing/corpus/fuzz_pkcs11/dff1ba4f2e96e390e03144ef40b06981c1a0cf8d new file mode 100644 index 0000000000..052754a7e1 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs11/dff1ba4f2e96e390e03144ef40b06981c1a0cf8d differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11/dffd29e0ce2df7e99122dc3e5c7de81d77ffbbb9 b/src/tests/fuzzing/corpus/fuzz_pkcs11/dffd29e0ce2df7e99122dc3e5c7de81d77ffbbb9 new file mode 100644 index 0000000000..d80c8c1545 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs11/dffd29e0ce2df7e99122dc3e5c7de81d77ffbbb9 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11/e9907948b6c2a8a0f82b160ebbafd7bb47325792 b/src/tests/fuzzing/corpus/fuzz_pkcs11/e9907948b6c2a8a0f82b160ebbafd7bb47325792 new file mode 100644 index 0000000000..a9690b9847 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs11/e9907948b6c2a8a0f82b160ebbafd7bb47325792 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/28ebe0a968fab20ddb61808decaa1b18369b635e b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/28ebe0a968fab20ddb61808decaa1b18369b635e new file mode 100644 index 0000000000..708d8372b8 --- /dev/null +++ b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/28ebe0a968fab20ddb61808decaa1b18369b635e @@ -0,0 +1 @@ +pkcs11:object=my-sign-key;type=private?module-path=/mnt/libmypkcs11.so.1 \ No newline at end of file diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/2b24f3f7b062c3e845aad88b1cc76ace2fe36048 b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/2b24f3f7b062c3e845aad88b1cc76ace2fe36048 new file mode 100644 index 0000000000..89fe39486a --- /dev/null +++ b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/2b24f3f7b062c3e845aad88b1cc76ace2fe36048 @@ -0,0 +1 @@ +pkcs11:object=my-pubkey;type=public \ No newline at end of file diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/3dc11f14c5e5b62d0f579fe0b462c2f2f4735c1b b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/3dc11f14c5e5b62d0f579fe0b462c2f2f4735c1b new file mode 100644 index 0000000000..146a701cd5 --- /dev/null +++ b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/3dc11f14c5e5b62d0f579fe0b462c2f2f4735c1b @@ -0,0 +1 @@ +pkcs11: \ No newline at end of file diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/4b38e89701235f53db2a39715e9bd1c3189abe3c b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/4b38e89701235f53db2a39715e9bd1c3189abe3c new file mode 100644 index 0000000000..0dd050f29d --- /dev/null +++ b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/4b38e89701235f53db2a39715e9bd1c3189abe3c @@ -0,0 +1 @@ +pkcs11:token=The%20Software%20PKCS%2311%20Softtoken;manufacturer=Snake%20Oil,%20Inc.;model=1.0;object=my-certificate;type=cert;id=%69%95%3E%5C%F4%BD%EC%91;serial=?pin-source=file:/etc/token_pin \ No newline at end of file diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/5c56fda36553f0b28e31c735564ed2e26c88a958 b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/5c56fda36553f0b28e31c735564ed2e26c88a958 new file mode 100644 index 0000000000..33d92b1f38 --- /dev/null +++ b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/5c56fda36553f0b28e31c735564ed2e26c88a958 @@ -0,0 +1 @@ +pkcs11:object=my-key;type=private?pin-source=file:/etc/token \ No newline at end of file diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/7e0a8c68797ba32db195022be9811cb0072e9353 b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/7e0a8c68797ba32db195022be9811cb0072e9353 new file mode 100644 index 0000000000..8cfd422076 --- /dev/null +++ b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/7e0a8c68797ba32db195022be9811cb0072e9353 @@ -0,0 +1 @@ +pkcs11:slot-description=Sun%20Metaslot \ No newline at end of file diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/a81eb2485c1d1d4eb7a7dc048c9bce3f66889785 b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/a81eb2485c1d1d4eb7a7dc048c9bce3f66889785 new file mode 100644 index 0000000000..a63d98ab82 --- /dev/null +++ b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/a81eb2485c1d1d4eb7a7dc048c9bce3f66889785 @@ -0,0 +1 @@ +pkcs11:library-manufacturer=Snake%20Oil,%20Inc.;library-description=Soft%20Token%20Library;library-version=1.23 \ No newline at end of file diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/bd87cedd745977935d20c7622974ce5b455c29b3 b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/bd87cedd745977935d20c7622974ce5b455c29b3 new file mode 100644 index 0000000000..f884558f7a --- /dev/null +++ b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/bd87cedd745977935d20c7622974ce5b455c29b3 @@ -0,0 +1 @@ +pkcs11:object=my-sign-key;type=private?module-name=mypkcs11 \ No newline at end of file diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/d876e02864ba5f2287838ab6b14c9238f2f97450 b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/d876e02864ba5f2287838ab6b14c9238f2f97450 new file mode 100644 index 0000000000..0ebe8a984d --- /dev/null +++ b/src/tests/fuzzing/corpus/fuzz_pkcs11_uri/d876e02864ba5f2287838ab6b14c9238f2f97450 @@ -0,0 +1 @@ +pkcs11:token=Software%20PKCS%2311%20softtoken;manufacturer=Snake%20Oil,%20Inc.?pin-value=the-pin \ No newline at end of file diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/46aca342dc91118357b5cfd40aec995d2b53bd02 b/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/46aca342dc91118357b5cfd40aec995d2b53bd02 new file mode 100644 index 0000000000..51ce43b706 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/46aca342dc91118357b5cfd40aec995d2b53bd02 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/5f52a1ae6be6d75d0be546604b47759c6621f46f b/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/5f52a1ae6be6d75d0be546604b47759c6621f46f new file mode 100644 index 0000000000..cac951cc2f Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/5f52a1ae6be6d75d0be546604b47759c6621f46f differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/6d06b28c9e3743122056f32e09f4c6d77763f4ba b/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/6d06b28c9e3743122056f32e09f4c6d77763f4ba new file mode 100644 index 0000000000..d8ebe5be91 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/6d06b28c9e3743122056f32e09f4c6d77763f4ba differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/92f669ec651bb54b819db380603520c18b78297a b/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/92f669ec651bb54b819db380603520c18b78297a new file mode 100644 index 0000000000..63e5bb6993 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/92f669ec651bb54b819db380603520c18b78297a differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/bfb749d844f7c304e004c52a46ce84eb3da7a7f3 b/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/bfb749d844f7c304e004c52a46ce84eb3da7a7f3 new file mode 100644 index 0000000000..f0964d3db8 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/bfb749d844f7c304e004c52a46ce84eb3da7a7f3 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/e52786b16a4202c5315c834788133b173bc141a6 b/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/e52786b16a4202c5315c834788133b173bc141a6 new file mode 100644 index 0000000000..f6e331c9df Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/e52786b16a4202c5315c834788133b173bc141a6 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/e9016daf00fb6713ea6f7fc18c55e85a4a33ea3a b/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/e9016daf00fb6713ea6f7fc18c55e85a4a33ea3a new file mode 100644 index 0000000000..fa67e6ecb4 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/e9016daf00fb6713ea6f7fc18c55e85a4a33ea3a differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/0204f84aede3986d1add8909124e021cac32bec8 b/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/0204f84aede3986d1add8909124e021cac32bec8 new file mode 100644 index 0000000000..c57cad0189 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/0204f84aede3986d1add8909124e021cac32bec8 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/0e9c8b959346f4894ea97d7e3f393c2442ee1e3d b/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/0e9c8b959346f4894ea97d7e3f393c2442ee1e3d new file mode 100644 index 0000000000..e3183ae0c6 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/0e9c8b959346f4894ea97d7e3f393c2442ee1e3d differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/6e580d278c33a530284dfef5dd9ffd617597bb68 b/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/6e580d278c33a530284dfef5dd9ffd617597bb68 new file mode 100644 index 0000000000..81d9710d78 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/6e580d278c33a530284dfef5dd9ffd617597bb68 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/8989be8baa0b0269c8128729062b31f91b131ba4 b/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/8989be8baa0b0269c8128729062b31f91b131ba4 new file mode 100644 index 0000000000..df2f927a82 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/8989be8baa0b0269c8128729062b31f91b131ba4 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/b011d577451c835fd8f6052f0659337994273f3f b/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/b011d577451c835fd8f6052f0659337994273f3f new file mode 100644 index 0000000000..9cc26e0e17 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/b011d577451c835fd8f6052f0659337994273f3f differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/636b70af193cecc86033282774e8b8bc3aa39f4c b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/636b70af193cecc86033282774e8b8bc3aa39f4c new file mode 100644 index 0000000000..5e2c2a9c87 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/636b70af193cecc86033282774e8b8bc3aa39f4c differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/741a0aae7b5b08c0ad2822ede5b3364302b28b31 b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/741a0aae7b5b08c0ad2822ede5b3364302b28b31 new file mode 100644 index 0000000000..201f37c792 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/741a0aae7b5b08c0ad2822ede5b3364302b28b31 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/7cf8e9b31dcee040ee438441aca2aecb523ed5e9 b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/7cf8e9b31dcee040ee438441aca2aecb523ed5e9 new file mode 100644 index 0000000000..9b131eb877 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/7cf8e9b31dcee040ee438441aca2aecb523ed5e9 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/830e1bf4c7f0c539e9686bc1517d6f87907d4bf8 b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/830e1bf4c7f0c539e9686bc1517d6f87907d4bf8 new file mode 100644 index 0000000000..68688cbfa8 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/830e1bf4c7f0c539e9686bc1517d6f87907d4bf8 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/9ad3fc3cb11967be927bad9263d326783c450e37 b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/9ad3fc3cb11967be927bad9263d326783c450e37 new file mode 100644 index 0000000000..f9c6c87f64 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/9ad3fc3cb11967be927bad9263d326783c450e37 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/b2b75c07a2c427c15ecd40ce47a9814279745b7d b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/b2b75c07a2c427c15ecd40ce47a9814279745b7d new file mode 100644 index 0000000000..8c848037cb Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/b2b75c07a2c427c15ecd40ce47a9814279745b7d differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/cb50689bf49ccb45a2af690848517305dcf1e429 b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/cb50689bf49ccb45a2af690848517305dcf1e429 new file mode 100644 index 0000000000..4d4119d023 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/cb50689bf49ccb45a2af690848517305dcf1e429 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/de913ba454f894cfc38a16dd122ad673d32ac480 b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/de913ba454f894cfc38a16dd122ad673d32ac480 new file mode 100644 index 0000000000..e5f0b5b1ab Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/de913ba454f894cfc38a16dd122ad673d32ac480 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/741a0aae7b5b08c0ad2822ede5b3364302b28b31 b/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/741a0aae7b5b08c0ad2822ede5b3364302b28b31 new file mode 100644 index 0000000000..201f37c792 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/741a0aae7b5b08c0ad2822ede5b3364302b28b31 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/7cf8e9b31dcee040ee438441aca2aecb523ed5e9 b/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/7cf8e9b31dcee040ee438441aca2aecb523ed5e9 new file mode 100644 index 0000000000..9b131eb877 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/7cf8e9b31dcee040ee438441aca2aecb523ed5e9 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/830e1bf4c7f0c539e9686bc1517d6f87907d4bf8 b/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/830e1bf4c7f0c539e9686bc1517d6f87907d4bf8 new file mode 100644 index 0000000000..68688cbfa8 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/830e1bf4c7f0c539e9686bc1517d6f87907d4bf8 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/9ad3fc3cb11967be927bad9263d326783c450e37 b/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/9ad3fc3cb11967be927bad9263d326783c450e37 new file mode 100644 index 0000000000..f9c6c87f64 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/9ad3fc3cb11967be927bad9263d326783c450e37 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/b2b75c07a2c427c15ecd40ce47a9814279745b7d b/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/b2b75c07a2c427c15ecd40ce47a9814279745b7d new file mode 100644 index 0000000000..8c848037cb Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/b2b75c07a2c427c15ecd40ce47a9814279745b7d differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/cb50689bf49ccb45a2af690848517305dcf1e429 b/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/cb50689bf49ccb45a2af690848517305dcf1e429 new file mode 100644 index 0000000000..4d4119d023 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/cb50689bf49ccb45a2af690848517305dcf1e429 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/de913ba454f894cfc38a16dd122ad673d32ac480 b/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/de913ba454f894cfc38a16dd122ad673d32ac480 new file mode 100644 index 0000000000..e5f0b5b1ab Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/de913ba454f894cfc38a16dd122ad673d32ac480 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/580ffba4a6c4d24100dd3dc11ab0014be3de7a6b b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/580ffba4a6c4d24100dd3dc11ab0014be3de7a6b new file mode 100644 index 0000000000..2a502ef6cd Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/580ffba4a6c4d24100dd3dc11ab0014be3de7a6b differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/5ccb89aff2634fc168e0758cb2005d6dcf0398bc b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/5ccb89aff2634fc168e0758cb2005d6dcf0398bc new file mode 100644 index 0000000000..17af38c0e2 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/5ccb89aff2634fc168e0758cb2005d6dcf0398bc differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/69456a048f311376c4093ae2c613ac1f261c6207 b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/69456a048f311376c4093ae2c613ac1f261c6207 new file mode 100644 index 0000000000..74ca130a94 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/69456a048f311376c4093ae2c613ac1f261c6207 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/a1e04ba21b4b54a1fdc048611c4f890405c9885d b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/a1e04ba21b4b54a1fdc048611c4f890405c9885d new file mode 100644 index 0000000000..bd72e228bf Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/a1e04ba21b4b54a1fdc048611c4f890405c9885d differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/a3289103f478310fe6013369adf1b1e0e44b14c2 b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/a3289103f478310fe6013369adf1b1e0e44b14c2 new file mode 100644 index 0000000000..21515ccb6f Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/a3289103f478310fe6013369adf1b1e0e44b14c2 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/ade754cf6e6f55b8873e6d6a12080e7f8b4366ee b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/ade754cf6e6f55b8873e6d6a12080e7f8b4366ee new file mode 100644 index 0000000000..f17a4ace5e Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/ade754cf6e6f55b8873e6d6a12080e7f8b4366ee differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/c2e3f533efccdfbba6de4c3d2e4aea831f508034 b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/c2e3f533efccdfbba6de4c3d2e4aea831f508034 new file mode 100644 index 0000000000..11c3cb69ae Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/c2e3f533efccdfbba6de4c3d2e4aea831f508034 differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/f1cac4a34dcb285f87df7a4568fe8eb00f9a4cad b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/f1cac4a34dcb285f87df7a4568fe8eb00f9a4cad new file mode 100644 index 0000000000..7b6d420813 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/f1cac4a34dcb285f87df7a4568fe8eb00f9a4cad differ diff --git a/src/tests/fuzzing/corpus/fuzz_pkcs15init/6ce966ee0f311e1a63f2bb693caeba1b0fd1160e b/src/tests/fuzzing/corpus/fuzz_pkcs15init/6ce966ee0f311e1a63f2bb693caeba1b0fd1160e new file mode 100644 index 0000000000..69b559cb19 Binary files /dev/null and b/src/tests/fuzzing/corpus/fuzz_pkcs15init/6ce966ee0f311e1a63f2bb693caeba1b0fd1160e differ diff --git a/src/tests/fuzzing/corpus/fuzz_scconf_parse_string/497025125e0dfab0b9e16155ce16d6e25ec8ec6d b/src/tests/fuzzing/corpus/fuzz_scconf_parse_string/497025125e0dfab0b9e16155ce16d6e25ec8ec6d new file mode 100644 index 0000000000..a8f57485e1 --- /dev/null +++ b/src/tests/fuzzing/corpus/fuzz_scconf_parse_string/497025125e0dfab0b9e16155ce16d6e25ec8ec6d @@ -0,0 +1,175 @@ +app default { + debug = 3; + # Disable pop-ups of built-in GUI + disable_popups = true; + enable_default_driver = true; + + ignored_readers = "CardMan 1021", "SPR 532"; + + # The following section shows definitions for PC/SC readers. + reader_driver pcsc { + max_send_size = 65535; + max_recv_size = 65536; + connect_exclusive = true; + disconnect_action = reset; + transaction_end_action = reset; + reconnect_action = reset; + enable_pinpad = false; + fixed_pinlength = 6; + enable_escape = true; + provider_library = @DEFAULT_PCSC_PROVIDER@ + } + + reader_driver openct { + readers = 5; + + max_send_size = 255; + max_recv_size = 256; + } + + reader_driver cryptotokenkit { + max_send_size = 65535; + max_recv_size = 65536; + } + + card_drivers = old, internal; + card_driver customcos { + module = @LIBDIR@@LIB_PRE@card_customcos@DYN_LIB_EXT@; + } + + card_driver npa { + can = 222222; + st_dv_certificate = ZZSTDVCA00001.cvcert; + st_certificate = ZZSTTERM00001.cvcert; + st_key = ZZSTTERM00001.pkcs8; + } + card_atr 3b:8c:80:01:59:75:62:69:6b:65:79:4e:45:4f:72:33:58 { + atrmask = "FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:00:00"; + name = "Yubikey Neo"; + driver = "PIV-II"; + flags = "keep_alive"; + } + + card_atr 3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:00:70:0A:90:00:8B { + type = 11100; + driver = "authentic"; + name = "AuthentIC v3.1"; + secure_messaging = local_authentic; + } + card_atr 3B:7F:96:00:00:00:31:B9:64:40:70:14:10:73:94:01:80:82:90:00 { + type = 25001; + driver = "iasecc"; + name = "Gemalto MultiApp IAS/ECC v1.0.1"; + secure_messaging = local_gemalto_iam; + secure_messaging = local_adele; + read_only = false; + md_supports_X509_enrollment = true; + } + card_atr 3B:7F:96:00:00:00:31:B8:64:40:70:14:10:73:94:01:80:82:90:00 { + type = 25001; + driver = "iasecc"; + name = "Gemalto MultiApp IAS/ECC v1.0.1"; + secure_messaging = local_gemalto_iam; + read_only = false; + md_supports_X509_enrollment = true; + } + card_atr 3B:DF:18:FF:81:91:FE:1F:C3:00:31:B8:64:0C:01:EC:C1:73:94:01:80:82:90:00:B3 { + type = 25004; + driver = "iasecc"; + name = "Amos IAS/ECC v1.0.1"; + read_only = false; + md_supports_X509_enrollment = true; + secure_messaging = local_amos; + } + # SmartCard-HSM with fingerprint sensor and PIN pad + card_atr 3B:80:80:01:01 { + force_protocol = "t1"; + read_only = true; + md_supports_X509_enrollment = true; + md_supports_container_key_gen = true; + md_guid_as_label = true; + md_pinpad_dlg_main = "Fingerabdruck oder PIN eingeben"; + md_pinpad_dlg_content_user = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN auf der Karte."; + md_pinpad_dlg_content_user_sign = "Bitte verifizieren Sie Ihren Fingarabdruck oder Ihre PIN für die digitale Signatur auf der Karte."; + md_pinpad_dlg_content_admin = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + md_pinpad_dlg_expanded = "Dieses Fenster wird automatisch geschlossen, wenn die PIN oder der Fingerabdruck verifiziert wurde (Timeout nach 30 Sekunden). Nutzen Sie das PIN-Pad, um die Eingabe abzubrechen."; + md_pinpad_dlg_timeout = 30; + notify_card_inserted = "GoID erkannt"; + notify_card_inserted_text = ""; + notify_card_removed = "GoID entfernt"; + notify_pin_good = "Fingerabdruck bzw. PIN verifiziert"; + notify_pin_good_text = "GoID ist entsperrt"; + notify_pin_bad = "Fingerabdruck bzw. PIN nicht verifiziert"; + notify_pin_bad_text = "GoID ist gesperrt"; + } + + secure_messaging local_authentic { + module_path = @DEFAULT_SM_MODULE_PATH@; + mode = transmit; + flags = 0x78; + kmc = "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"; + } + + secure_messaging local_gemalto_iam { + module_name = @DEFAULT_SM_MODULE@; + type = acl; # transmit, acl + + ifd_serial = "11:22:33:44:55:66:77:88"; + keyset_02_enc = "RW_PRIV_ENC_TEST"; + keyset_02_mac = "RW_PRIV_MAC_TEST"; + keyset_E828BD080FD2504543432D654944_01_enc = "RO_ENC_TEST_KEY_"; + keyset_E828BD080FD2504543432D654944_01_mac = "RO_MAC_TEST_KEY_"; + keyset_E828BD080FD2504543432D654944_03_enc = "RW_PUBL_ENC_TEST"; + keyset_E828BD080FD2504543432D654944_03_mac = "RW_PUBL_MAC_TEST"; + } + + framework pkcs15 { + use_file_caching = true; + use_pin_caching = false; + pin_cache_counter = 3; + pin_cache_ignore_user_consent = true; + private_certificate = declassify; + enable_pkcs15_emulation = no; + try_emulation_first = yes; + enable_builtin_emulation = no; + builtin_emulators = old, internal; + emulate custom { + module = @LIBDIR@@LIB_PRE@p15emu_custom@DYN_LIB_EXT@; + } + application E828BD080FD25047656E65726963 { + type = generic; + model = "ECC Generic PKI"; + } + + application E828BD080FD2500000040301 { + type = generic; + model = "Adèle Générique"; + } + } +} + +app opensc-pkcs11 { + pkcs11 { + max_virtual_slots = 32; + slots_per_card = 2; + lock_login = true; + atomic = true; + init_sloppy = false; + user_pin_unblock_style = set_pin_in_unlogged_session; + create_puk_slot = true; + create_slots_for_pins = "user,sign"; + create_slots_for_pins = "sign"; + create_slots_for_pins = "user" + } +} + +app onepin-opensc-pkcs11 { + pkcs11 { + slots_per_card = 1; + } +} + +# Used by OpenSC minidriver on Windows only +app cardmod { +} + diff --git a/src/tests/fuzzing/corpus/fuzz_scconf_parse_string/fa7e8cb717af33932718d96a3c785268311d9c6f b/src/tests/fuzzing/corpus/fuzz_scconf_parse_string/fa7e8cb717af33932718d96a3c785268311d9c6f new file mode 100644 index 0000000000..1f2d3bf3bc --- /dev/null +++ b/src/tests/fuzzing/corpus/fuzz_scconf_parse_string/fa7e8cb717af33932718d96a3c785268311d9c6f @@ -0,0 +1,10 @@ +app default { + debug = 0; + debug_file = stdout; + framework pkcs15 { + enable_builtin_emulation = yes; + builtin_emulators = old, jpki, dnie, gids, PIV-II; + + } +} + diff --git a/src/sm/sslutil.h b/src/tests/fuzzing/fuzz_asn1_print.c similarity index 55% rename from src/sm/sslutil.h rename to src/tests/fuzzing/fuzz_asn1_print.c index 7f5b072fcf..dce3e11695 100644 --- a/src/sm/sslutil.h +++ b/src/tests/fuzzing/fuzz_asn1_print.c @@ -1,7 +1,5 @@ /* - * Copyright (C) 2011-2015 Frank Morgner - * - * This file is part of OpenSC. + * Copyright (C) 2019 Frank Morgner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -15,25 +13,19 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _SC_SSLUTIL_H -#define _SC_SSLUTIL_H - -#include -#include - -#ifdef ENABLE_OPENSSL -#include -#define ssl_error(ctx) { \ - unsigned long _r; \ - ERR_load_crypto_strings(); \ - for (_r = ERR_get_error(); _r; _r = ERR_get_error()) { \ - sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "%s", ERR_error_string(_r, NULL)); \ - } \ - ERR_free_strings(); \ -} +#ifdef HAVE_CONFIG_H +#include "config.h" #endif +#include "libopensc/asn1.h" + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { +#ifdef FUZZING_ENABLED + fclose(stdout); #endif + sc_asn1_print_tags(Data, Size); + return 0; +} diff --git a/src/tests/fuzzing/fuzz_asn1_sig_value.c b/src/tests/fuzzing/fuzz_asn1_sig_value.c new file mode 100644 index 0000000000..0a289c0493 --- /dev/null +++ b/src/tests/fuzzing/fuzz_asn1_sig_value.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2019 Frank Morgner + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "libopensc/asn1.h" +#include +#include + +static unsigned char *in = NULL, *out = NULL; +static size_t inlen = 0, outlen = 0; +static struct sc_context *ctx = NULL; + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) +{ + if (!ctx) + sc_establish_context(&ctx, "fuzz"); + + if (outlen < Size*2) { + unsigned char *p = realloc(out, Size*2); + if (p) { + out = p; + outlen = Size*2; + } + } + + if (inlen < Size) { + unsigned char *p = realloc(in, Size); + if (p) { + in = p; + } + } + memcpy(in, Data, Size); + + sc_asn1_sig_value_sequence_to_rs(ctx, + Data, Size, + out, outlen); + + unsigned char *p = NULL; + size_t plen = 0; + sc_asn1_sig_value_rs_to_sequence(ctx, + in, Size, + &p, &plen); + free(p); + + return 0; +} diff --git a/src/tests/fuzzing/fuzz_card.c b/src/tests/fuzzing/fuzz_card.c new file mode 100644 index 0000000000..1f4dcc7f0b --- /dev/null +++ b/src/tests/fuzzing/fuzz_card.c @@ -0,0 +1,103 @@ +/* + * fuzz_card.c: Fuzzer for sc_* functions + * + * Copyright (C) 2022 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fuzzer_reader.h" + + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + sc_context_t *ctx = NULL; + sc_card_t *card = NULL; + struct sc_reader *reader = NULL; + unsigned long flag = 0; + const uint8_t *ptr = NULL; + uint16_t ptr_size = 0; + u8 files[SC_MAX_EXT_APDU_BUFFER_SIZE]; + uint8_t len = 0; + u8 *rnd = NULL, *wrap_buf = NULL, *unwrap_buf = NULL; + size_t wrap_buf_len = 0, unwrap_buf_len = 0; + int r = 0; + +#ifdef FUZZING_ENABLED + fclose(stdout); +#endif + + if (size <= sizeof(unsigned long) + 1) + return 0; + + flag = *((unsigned long *) data); + len = *(data + sizeof(unsigned long)); + data += (sizeof(unsigned long) + sizeof(uint8_t)); + size -= (sizeof(unsigned long) + sizeof(uint8_t)); + + /* Establish context for fuzz app*/ + sc_establish_context(&ctx, "fuzz"); + if (!ctx) + return 0; + + if (fuzz_connect_card(ctx, &card, &reader, data, size) != SC_SUCCESS) + goto err; + + /* Wrap & Unwrap*/ + if (!(wrap_buf = malloc(SC_MAX_APDU_BUFFER_SIZE))) + goto err; + wrap_buf_len = SC_MAX_APDU_BUFFER_SIZE; + sc_wrap(card, NULL, 0, wrap_buf, wrap_buf_len); + + fuzz_get_chunk(reader, &ptr, &ptr_size); + if (!(unwrap_buf = malloc(ptr_size))) + goto err; + memcpy(unwrap_buf, ptr, ptr_size); + unwrap_buf_len = ptr_size; + sc_unwrap(card, unwrap_buf, unwrap_buf_len, NULL, 0); + + /* Write binary */ + sc_write_binary(card, 0, ptr, ptr_size, flag); + + /* Put data */ + fuzz_get_chunk(reader, &ptr, &ptr_size); + sc_put_data(card, (unsigned int)flag, ptr, ptr_size); + + /* List files */ + sc_list_files(card, files, sizeof(files)); + + /* Get challenge */ + rnd = malloc(len); + if (rnd == NULL) + goto err; + if ((r = sc_get_challenge(card, rnd, len)) != SC_SUCCESS) + sc_log(ctx, "sc_get_challenge failed with rc = %d", r); + + /* Append record */ + sc_append_record(card, ptr, ptr_size, flag); + +err: + free(rnd); + free(wrap_buf); + free(unwrap_buf); + sc_disconnect_card(card); + sc_release_context(ctx); + return 0; +} diff --git a/src/tests/fuzzing/fuzz_piv_tool.c b/src/tests/fuzzing/fuzz_piv_tool.c new file mode 100644 index 0000000000..9d5d7ca778 --- /dev/null +++ b/src/tests/fuzzing/fuzz_piv_tool.c @@ -0,0 +1,174 @@ +/* + * fuzz_piv_tool.c: Fuzz target for piv-tool + * + * Copyright (C) 2022 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "libopensc/internal.h" +#include +#include +#include +#include "fuzzer_reader.h" +#include "fuzzer_tool.h" +#undef stderr +#define stderr stdout + +/* Rename main for calling in fuzz target */ +#define main _main +/* Connect to virtual reader instead of real card*/ +#define util_connect_card(ctx, card, id, do_wait) fuzz_util_connect_card(ctx, card) +# include "tools/piv-tool.c" +#undef main + +static const uint8_t *reader_data = NULL; +static size_t reader_data_size = 0; + +/* Use instead of util_connect_card() */ +int fuzz_util_connect_card(struct sc_context *ctx, struct sc_card **card) +{ + return fuzz_connect_card(ctx, card, NULL, reader_data, reader_data_size); +} + +void initilize_global() +{ + /* Global variables need to be reser between runs, + fuzz target is called repetitively in one execution */ + reader_data = NULL; + reader_data_size = 0; + ctx = NULL; + card = NULL; + bp = NULL; + opt_reader = NULL; + opt_apdus = NULL; + opt_apdu_count = 0; + + optind = 0; + opterr = 0; /* do not print out error messages */ + optopt = 0; +} + +void test_load(char *op, const uint8_t *data, size_t size) +{ + char *filename = NULL; + char *argv[] = {"./fuzz_piv", op, NULL /*ref*/, "-i", NULL /*filename*/, "-A", NULL /*admin*/, NULL}; + int argc = 7; + char *opt_ref = NULL, *opt_admin = NULL; + + if (!(opt_ref = extract_word(&data, &size))) + return; + argv[2] = opt_ref; + + if (!(opt_admin = extract_word(&data, &size))) { + free(opt_ref); + return; + } + argv[6] = opt_admin; + + if (create_input_file(&filename, &data, &size) != 0) { + free(opt_ref); + free(opt_admin); + remove_file(filename); + return; + } + argv[4] = filename; + + reader_data = data; + reader_data_size = size; + _main(argc, argv); + + free(opt_ref); + free(opt_admin); + remove_file(filename); +} + +/* Skip argv with option for output file */ +int present_outfile(int argc, char *argv[]) +{ + const struct option _options[] = { + { "out",1, NULL,'o' }, + { NULL, 0, NULL, 0 } + }; + int c; + while ((c = getopt_long(argc, argv, "o:", _options, (int *) 0)) != -1) { + switch (c) { + case 'o': + return 1; + default: + continue; + } + } + optind = 0; + optopt = 0; + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + uint8_t operation = 0; + char *filename = NULL; + char **argv = NULL; + int argc = 0; + char auth_path[50] = {0}; + +#ifdef FUZZING_ENABLED + fclose(stdout); +#endif + if (size < 10) + return 0; + + initilize_global(); + operation = data[0]; + data++; + size--; + + /* extract admin argument and set file with admin key */ + if (create_input_file(&filename, &data, &size) != 0 || size < 3) + goto err; + sprintf(auth_path, "PIV_EXT_AUTH_KEY=%s", filename); + putenv(auth_path); + + switch (operation) { + case 0: + test_load("-O", data, size); + break; + case 1: + test_load("-C", data, size); + break; + case 2: + test_load("-Z", data, size); + break; + default: + if (get_fuzzed_argv("./fuzz_piv", data, size, &argv, &argc, &reader_data, &reader_data_size) != 0) + goto err; + if (present_outfile(argc, argv)) { + free_arguments(argc, argv); + goto err; + } + _main(argc, argv); + free_arguments(argc, argv); + } +err: + reader_data = NULL; + reader_data_size = 0; + remove_file(filename); + return 0; +} diff --git a/src/tests/fuzzing/fuzz_pkcs11.c b/src/tests/fuzzing/fuzz_pkcs11.c new file mode 100644 index 0000000000..2fd1a5febd --- /dev/null +++ b/src/tests/fuzzing/fuzz_pkcs11.c @@ -0,0 +1,1087 @@ +/* + * fuzz_pkcs11.c: Fuzz target for PKCS #11 API + * + * Copyright (C) 2022 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pkcs11/pkcs11.h" +#include "pkcs11/pkcs11-opensc.h" +#include "pkcs11/sc-pkcs11.h" +#include "fuzzer_reader.h" +#include "fuzzer_tool.h" + +#define SIG_LEN 512 + +/* If disabled, card is connected only via C_Initialize */ +#define FUZZING 1 + +extern CK_FUNCTION_LIST_3_0 pkcs11_function_list_3_0; +static CK_FUNCTION_LIST_3_0_PTR p11 = NULL; + +/* Values used for key template*/ +static CK_BBOOL _true = TRUE; +static CK_BBOOL _false = FALSE; + +/* Global parameters for key template */ +CK_ULONG key_type = 0; +unsigned char ecparams[256]; +unsigned char *opt_object_label[256]; +CK_BYTE opt_object_id[100]; +CK_MECHANISM_TYPE opt_allowed_mechanisms[20]; + +#if FUZZING +static int fuzz_card_connect(const uint8_t *data, size_t size, sc_pkcs11_slot_t **slot_out) +{ + /* Works in the same manner as card_detect() for only one slot and card with virtual reader */ + struct sc_pkcs11_card *p11card = NULL; + struct sc_reader *reader = NULL; + struct sc_app_info *app_generic = NULL; + sc_pkcs11_slot_t *slot = NULL; + CK_RV rv = CKR_OK; + int free_p11card = 0; + + /* Erase possible virtual slots*/ + list_clear(&virtual_slots); + + /* Erase possible readers from context */ + while (list_size(&context->readers)) { + sc_reader_t *rdr = (sc_reader_t *) list_get_at(&context->readers, 0); + _sc_delete_reader(context, rdr); + } + if (context->reader_driver->ops->finish != NULL) + context->reader_driver->ops->finish(context); + + /* Create virtual reader */ + context->reader_driver = sc_get_fuzz_driver(); + fuzz_add_reader(context, data, size); + reader = sc_ctx_get_reader(context, 0); + + /* Add slot for reader */ + if (create_slot(reader) != CKR_OK) { + goto fail; + } + + /* Locate a slot related to the reader */ + for (size_t i = 0; i < list_size(&virtual_slots); i++) { + slot = (sc_pkcs11_slot_t *)list_get_at(&virtual_slots, (int)i); + if (slot->reader == reader) { + p11card = slot->p11card; + break; + } + } + + /* Create p11card */ + p11card = (struct sc_pkcs11_card *)calloc(1, sizeof(struct sc_pkcs11_card)); + p11card->reader = reader; + free_p11card = 1; + + /* Connect card to reader */ + if ((rv = sc_connect_card(reader, &p11card->card)) != SC_SUCCESS) { + goto fail; + } + init_slot_info(&slot->slot_info, reader); + + /* Instead of detecting framework*/ + p11card->framework = &framework_pkcs15; + + /* Bind 'generic' application or (emulated?) card without applications */ + app_generic = sc_pkcs15_get_application_by_type(p11card->card, "generic"); + if (app_generic || !p11card->card->app_count) { + scconf_block *conf_block = NULL; + + conf_block = sc_match_atr_block(p11card->card->ctx, NULL, &p11card->reader->atr); + if (!conf_block) /* check default block */ + conf_block = sc_get_conf_block(context, "framework", "pkcs15", 1); + + rv = p11card->framework->bind(p11card, app_generic); + if (rv != CKR_TOKEN_NOT_RECOGNIZED && rv != CKR_OK) + goto fail; + + rv = p11card->framework->create_tokens(p11card, app_generic); + if (rv != CKR_OK) + goto fail; + free_p11card = 0; + } + + /* Bind rest of application*/ + for (int j = 0; j < p11card->card->app_count; j++) { + struct sc_app_info *app_info = p11card->card->app[j]; + + if (app_generic && app_generic == p11card->card->app[j]) + continue; + + if (p11card->framework->bind(p11card, app_info) != CKR_OK) { + continue; + } + rv = p11card->framework->create_tokens(p11card, app_info); + if (rv != CKR_OK) { + goto fail; + } + free_p11card = 0; + } + if (slot_out) + *slot_out = slot; +fail: + if (free_p11card) { + sc_pkcs11_card_free(p11card); + } + return (int)rv; +} +#endif + +static int fuzz_pkcs11_initialize(const uint8_t *data, size_t size, sc_pkcs11_slot_t **slot_out, CK_SESSION_HANDLE *session) +{ + p11 = &pkcs11_function_list_3_0; + + context = NULL; + memset(&sc_pkcs11_conf, 0, sizeof(struct sc_pkcs11_config)); + + p11->C_Initialize(NULL); + + #if FUZZING + /* fuzz target can connect to real card via C_Initialize */ + if (fuzz_card_connect(data, size, slot_out) != CKR_OK) { + p11->C_Finalize(NULL); + return CKR_GENERAL_ERROR; + } + #endif + + if (p11->C_OpenSession(0, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, session) != CKR_OK) { + p11->C_Finalize(NULL); + return CKR_GENERAL_ERROR; + } + return CKR_OK; +} + +static int set_mechanism(const uint8_t **data, size_t *size, CK_MECHANISM *mech) +{ + if (*size < sizeof(unsigned long int)) + return 1; + + memset(mech, 0, sizeof(*mech)); + (*mech).mechanism = *((unsigned long int *)*data); + *data += sizeof(unsigned long int); + *size -= sizeof(unsigned long int); + return 0; +} + +static void test_change_pin(const unsigned char *data, size_t size) +{ + CK_SESSION_HANDLE session; + CK_TOKEN_INFO info; + char *pin = NULL; + char *new_pin = NULL; + int login_type = data[0]; + data++; size--; + + if (!(pin = extract_word(&data, &size))) + goto end; + if (!(new_pin = extract_word(&data, &size))) + goto end; + + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + goto end; + p11->C_GetTokenInfo(0, &info); + p11->C_Login(session, login_type, (CK_UTF8CHAR *) pin, pin == NULL ? 0 : strlen(pin)); + p11->C_SetPIN(session, + (CK_UTF8CHAR *) pin, pin == NULL ? 0 : strlen(pin), + (CK_UTF8CHAR *) new_pin, new_pin == NULL ? 0 : strlen(new_pin)); + + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +end: + free(new_pin); + free(pin); +} + +static void test_init_pin(const unsigned char *data, size_t size) +{ + CK_SESSION_HANDLE session; + CK_TOKEN_INFO info; + char *pin = NULL; + char *so_pin = NULL; + int login_type = data[0]; + data++; size--; + + if (!(pin = extract_word(&data, &size))) + goto end; + if (!(so_pin = extract_word(&data, &size))) + goto end; + + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + goto end; + p11->C_GetTokenInfo(0, &info); + p11->C_Login(session, login_type, (CK_UTF8CHAR *) so_pin, so_pin == NULL ? 0 : strlen(so_pin)); + p11->C_InitPIN(session, (CK_UTF8CHAR *) pin, pin == NULL ? 0 : strlen(pin)); + + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +end: + free(pin); + free(so_pin); +} + +static void test_init_token(const unsigned char *data, size_t size) +{ + CK_SESSION_HANDLE session; + char *pin = NULL; + unsigned char *label = NULL; + size_t label_len = 0; + unsigned char token_label[33]; + sc_pkcs11_slot_t *slot = NULL; + /* token label must be padded with blank characters, and which must not be null-terminated*/ + memset(token_label, ' ', sizeof(token_label)); + + if (!(pin = extract_word(&data, &size))) + goto end; + if (!(label = (unsigned char *) extract_word(&data, &size))) + goto end; + label_len = strlen((char *) label); + memcpy(token_label, label, label_len < 33 ? label_len : 32); + + if (fuzz_pkcs11_initialize(data, size, &slot, &session) != CKR_OK) + goto end; + p11->C_InitToken(slot->id, (CK_UTF8CHAR *) pin, pin == NULL ? 0 : strlen(pin), token_label); + + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +end: + free(pin); + free(label); +} + +static void test_random(const unsigned char *data, size_t size) +{ + CK_SESSION_HANDLE session; + size_t random_len = data[0]; + CK_BYTE buf[256]; + data++; size--; + + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + return; + + p11->C_GenerateRandom(session, buf, random_len); + + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +} + +static void test_digest_update(const uint8_t *data, size_t size) +{ + CK_SESSION_HANDLE session; + const uint8_t *dig_data = NULL; + size_t dig_size = 0; + CK_MECHANISM mech = {0, NULL_PTR, 0}; + unsigned char buffer[64] = {0}; + CK_ULONG hash_len = sizeof(buffer); + size_t to_process = 0; + CK_RV rv = 0; + + if (set_mechanism(&data, &size, &mech)) + return; + + /* Copy data for hashing*/ + dig_data = data; + if ((dig_size = get_buffer(&dig_data, size, &data, &size, 6000)) == 0) + return; + + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + return; + + if (p11->C_DigestInit(session, &mech) != CKR_OK) + goto end; + + while (dig_size > 0) { + to_process = dig_size > sizeof(buffer) ? sizeof(buffer) : dig_size; + dig_size -= to_process; + memcpy(buffer, dig_data, to_process); + dig_data += to_process; + + rv = p11->C_DigestUpdate(session, buffer, to_process); + if (rv != CKR_OK) + goto end; + } + hash_len = sizeof(buffer); + p11->C_DigestFinal(session, buffer, &hash_len); + +end: + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +} + +void test_digest(const uint8_t *data, size_t size) +{ + CK_SESSION_HANDLE session; + const uint8_t *ptr = NULL; + unsigned char *dig_data = NULL; + size_t dig_size = 0; + CK_MECHANISM mech = {0, NULL_PTR, 0}; + unsigned char buffer[64] = {0}; + CK_ULONG hash_len = sizeof(buffer); + + if (set_mechanism(&data, &size, &mech)) + return; + + /* Copy data for hashing*/ + ptr = data; + if ((dig_size = get_buffer(&ptr, size, &data, &size, 6000)) == 0) + return; + if (!(dig_data = malloc(dig_size))) + return; + memcpy(dig_data, ptr, dig_size); + + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + goto end; + + if (p11->C_DigestInit(session, &mech) == CKR_OK) + p11->C_Digest(session, dig_data, dig_size, buffer, &hash_len); + + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +end: + free(dig_data); +} + +static int fuzz_find_object(CK_SESSION_HANDLE sess, CK_OBJECT_CLASS cls, + CK_OBJECT_HANDLE_PTR ret, const unsigned char *id, size_t id_len) +{ + /* taken from tools/pkcs11-tool.c */ + CK_ATTRIBUTE attrs[2]; + unsigned int nattrs = 0; + CK_ULONG count = 0; + + attrs[0].type = CKA_CLASS; + attrs[0].pValue = &cls; + attrs[0].ulValueLen = sizeof(cls); + nattrs++; + if (id) { + attrs[nattrs].type = CKA_ID; + attrs[nattrs].pValue = (void *) id; + attrs[nattrs].ulValueLen = id_len; + nattrs++; + } + + if (p11->C_FindObjectsInit(sess, attrs, nattrs) != CKR_OK) + return -1; + + if (p11->C_FindObjects(sess, ret, 1, &count) != CKR_OK) + return -1; + + if (count == 0) + *ret = CK_INVALID_HANDLE; + p11->C_FindObjectsFinal(sess); + return (int)count; +} + +static void test_sign(const uint8_t *data, size_t size) +{ + CK_SESSION_HANDLE session; + uint8_t login_type = CKU_USER; + char *pin = NULL; + const unsigned char *opt_id; + size_t opt_id_len = 0; + const uint8_t *sign_data = NULL; + size_t sign_data_size = 0; + CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; + unsigned char in_buffer[1025], sig_buffer[512]; + CK_MECHANISM mech = {0, NULL_PTR, 0}; + CK_ULONG sig_len = sizeof(sig_buffer); + size_t to_process = 0; + CK_TOKEN_INFO info; + + /* Process options*/ + if (set_mechanism(&data, &size, &mech) || size < 3) + return; + login_type = data[0]; + data++; size--; + if (!(pin = extract_word(&data, &size))) + return; + opt_id = data; + opt_id_len = get_buffer(&opt_id, size, &data, &size, 256); + + /* Prepare buffer for signing */ + sign_data = data; + if ((sign_data_size = get_buffer(&sign_data, size, &data, &size, 6000)) == 0) + goto end; + + /* Initialize */ + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + goto end; + p11->C_GetTokenInfo(0, &info); + p11->C_Login(session, login_type, (CK_UTF8CHAR *) pin, strlen(pin)); + fuzz_find_object(session, CKO_PRIVATE_KEY, &key, opt_id_len ? opt_id : NULL, opt_id_len); + + if (p11->C_SignInit(session, &mech, key) != CKR_OK) + goto fin; + p11->C_Login(session, CKU_CONTEXT_SPECIFIC, (CK_UTF8CHAR *) pin, strlen(pin)); + + if (sign_data_size <= sizeof(in_buffer)) { + memcpy(in_buffer, sign_data, sign_data_size); + p11->C_Sign(session, in_buffer, sign_data_size, sig_buffer, &sig_len); + } else { + while (sign_data_size > 0) { + to_process = sign_data_size < sizeof(in_buffer) ? sign_data_size : sizeof(in_buffer); + sign_data_size -= to_process; + memcpy(in_buffer, sign_data, to_process); + sign_data += to_process; + + if (p11->C_SignUpdate(session, in_buffer, to_process) != CKR_OK) + goto fin; + } + + sig_len = sizeof(sig_buffer); + p11->C_SignFinal(session, sig_buffer, &sig_len); + } + +fin: + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +end: + free(pin); +} + +static void test_verify(const uint8_t *data, size_t size) +{ + CK_SESSION_HANDLE session; + CK_MECHANISM mech = {0, NULL_PTR, 0}; + uint8_t login_type = CKU_USER; + char *pin = NULL; + const unsigned char *opt_id = NULL; + size_t opt_id_len = 0; + const uint8_t *verify_data = NULL, *sig_data = NULL; + size_t verify_data_size = 0; + CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; + unsigned char in_buffer[1025], sig_buffer[512]; + CK_ULONG sig_len = sizeof(sig_buffer); + size_t to_process = 0; + + /* Process options*/ + if (set_mechanism(&data, &size, &mech) || size < 3) + return; + login_type = data[0]; + data++; size--; + if (!(pin = extract_word(&data, &size))) + return; + opt_id = data; + opt_id_len = get_buffer(&opt_id, size, &data, &size, 256); + + /* Prepare buffer with data */ + verify_data = data; + if ((verify_data_size = get_buffer(&verify_data, size, &data, &size, 6000)) == 0) + goto end; + /* Get buffer with signature */ + sig_data = data; + if ((sig_len = get_buffer(&sig_data, size, &data, &size, 512)) == 0) + goto end; + memcpy(sig_buffer, sig_data, sig_len); + + /* Initialize */ + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + goto end; + + p11->C_Login(session, login_type, (CK_UTF8CHAR *) pin, strlen(pin)); + + if (!fuzz_find_object(session, CKO_PUBLIC_KEY, &key, opt_id_len ? opt_id : NULL, opt_id_len) + && !fuzz_find_object(session, CKO_CERTIFICATE, &key, opt_id_len ? opt_id : NULL, opt_id_len)) + goto fin; + + if (p11->C_VerifyInit(session, &mech, key) != CKR_OK) + goto fin; + + if (verify_data_size <= sizeof(in_buffer)) { + memcpy(in_buffer, verify_data, verify_data_size); + p11->C_Verify(session, in_buffer, verify_data_size, sig_buffer, sig_len); + } else { + while (verify_data_size > 0) { + to_process = verify_data_size < sizeof(in_buffer) ? verify_data_size : sizeof(in_buffer); + verify_data_size -= to_process; + memcpy(in_buffer, verify_data, to_process); + verify_data += to_process; + + if (p11->C_VerifyUpdate(session, in_buffer, to_process) != CKR_OK) + goto fin; + } + + p11->C_VerifyFinal(session, sig_buffer, sig_len); + } +fin: + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +end: + free(pin); +} + +static void test_decrypt(const uint8_t *data, size_t size) +{ + CK_SESSION_HANDLE session; + uint8_t login_type = CKU_USER; + char *pin = NULL; + const unsigned char *opt_id; + size_t opt_id_len = 0; + const uint8_t *dec_data = NULL; + size_t dec_data_size = 0; + CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; + unsigned char in_buffer[1024], out_buffer[1024]; + CK_MECHANISM mech = {0, NULL_PTR, 0}; + size_t out_len = 0; + + /* Process options*/ + if (set_mechanism(&data, &size, &mech) || size < 3) + return; + login_type = data[0]; + data++; size--; + if (!(pin = extract_word(&data, &size))) + return; + opt_id = data; + opt_id_len = get_buffer(&opt_id, size, &data, &size, 256); + + /* Prepare buffer for signing */ + dec_data = data; + if ((dec_data_size = get_buffer(&dec_data, size, &data, &size, 1024)) == 0) + goto end; + + /* Initialize */ + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + goto end; + + p11->C_Login(session, login_type, (CK_UTF8CHAR *) pin, strlen(pin)); + if (!fuzz_find_object(session, CKO_PRIVATE_KEY, &key, opt_id_len ? opt_id : NULL, opt_id_len) + && !fuzz_find_object(session, CKO_SECRET_KEY, &key, opt_id_len ? opt_id : NULL, opt_id_len)) + goto fin; + + if (p11->C_DecryptInit(session, &mech, key) != CKR_OK) + goto fin; + + p11->C_Login(session, CKU_CONTEXT_SPECIFIC, (CK_UTF8CHAR *) pin, strlen(pin)); + out_len = sizeof(out_buffer); + + memcpy(in_buffer, dec_data, dec_data_size); + p11->C_Decrypt(session, in_buffer, dec_data_size, out_buffer, &out_len); +fin: + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +end: + free(pin); +} + +static void test_wrap(const uint8_t *data, size_t size) +{ + CK_SESSION_HANDLE session; + uint8_t login_type = CKU_USER; + char *pin = NULL; + CK_BYTE pWrappedKey[4096]; + CK_ULONG pulWrappedKeyLen = sizeof(pWrappedKey); + CK_MECHANISM mech = {0, NULL_PTR, 0}; + CK_OBJECT_HANDLE hWrappingKey; + CK_OBJECT_HANDLE hkey; + const unsigned char *hkey_id; + const unsigned char *opt_id; + size_t opt_id_len = 0, hkey_id_len = 0; + + /* Set options */ + if (set_mechanism(&data, &size, &mech) || size < 3) + return; + login_type = data[0]; + data++; size--; + if (!(pin = extract_word(&data, &size))) + return; + opt_id = data; + opt_id_len = get_buffer(&opt_id, size, &data, &size, 256); + hkey_id = data; + hkey_id_len = get_buffer(&hkey_id, size, &data, &size, 256); + + /* Initialize */ + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + goto end; + + p11->C_Login(session, login_type, (CK_UTF8CHAR *) pin, strlen(pin)); + if (!fuzz_find_object(session, CKO_SECRET_KEY, &hkey, hkey_id_len ? hkey_id : NULL, hkey_id_len)) + goto fin; + if (!fuzz_find_object(session, CKO_PUBLIC_KEY, &hWrappingKey, opt_id_len ? opt_id : NULL, opt_id_len)) + if (!fuzz_find_object(session, CKO_SECRET_KEY, &hWrappingKey, opt_id_len ? opt_id : NULL, opt_id_len)) + goto fin; + p11->C_WrapKey(session, &mech, hWrappingKey, hkey, pWrappedKey, &pulWrappedKeyLen); + +fin: + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +end: + free(pin); +} + +#define FILL_ATTR(attr, typ, val, len) do { \ + (attr).type=(typ); \ + (attr).pValue=(val); \ + (attr).ulValueLen=len; \ +} while(0) + +void fill_bool_attr(CK_ATTRIBUTE **keyTemplate, int *n_attr, int type, int value) +{ + if (value) { + FILL_ATTR((*keyTemplate)[*n_attr], type, &_true, sizeof(_true)); + } + else { + FILL_ATTR((*keyTemplate)[*n_attr], type, &_false, sizeof(_false)); + } + + ++(*n_attr); +} + +int fill_key_template(CK_ATTRIBUTE **keyTemplate, int *n_attr, const uint8_t **data, size_t *size, CK_OBJECT_CLASS *class, int token) +{ + const unsigned char *ptr = NULL; + size_t ecparams_size = 0; + size_t opt_object_label_size = 0; + size_t opt_object_id_len = 0; + size_t opt_allowed_mechanisms_len = 0; + int bool_types[] = {CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_VERIFY, CKA_SENSITIVE, + CKA_SIGN, CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, + CKA_DERIVE, CKA_PRIVATE, CKA_ALWAYS_AUTHENTICATE, CKA_EXTRACTABLE}; + + if (!(*keyTemplate = malloc(20 * sizeof(CK_ATTRIBUTE)))) + return 1; + memset(*keyTemplate, 0, 20 * sizeof(CK_ATTRIBUTE)); + FILL_ATTR((*keyTemplate)[0], CKA_CLASS, class, sizeof(CKA_CLASS)); + *n_attr = 1; + fill_bool_attr(keyTemplate, n_attr, CKA_TOKEN, token); + + for (int i = 0; i < 13; i++) { + /* ... | present -> 0/1 | value | ...*/ + if (*size < 3) + return 1; + if ((*data)[0] % 2) { + fill_bool_attr(keyTemplate, n_attr, bool_types[i], (*data)[1] % 2); + (*data)++; (*size)--; + } + (*data)++; (*size)--; + } + + if (*size > 2 && (*data)[0] % 2 && *n_attr < 20){ + /* ... | present -> 0/1 | value | ...*/ + key_type = (CK_ULONG) (*data)[1]; + FILL_ATTR((*keyTemplate)[*n_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); + ++(*n_attr); + (*data) += 2; + (*size) -= 2; + } + + if (*size > 3 && (*data)[0] % 2 && *n_attr < 20){ + /* ... | present -> 0/1 | len | len | data | ... */ + (*data)++; (*size)--; + ptr = *data; + if ((ecparams_size = get_buffer(&ptr, *size, data, size, 256)) == 0) + return 1; + memcpy(ecparams, ptr, ecparams_size); + FILL_ATTR((*keyTemplate)[*n_attr], CKA_EC_PARAMS, ecparams, ecparams_size); + ++(*n_attr); + } + + if (*size > 3 && (*data)[0] % 2 && *n_attr < 20){ + /* ... | present -> 0/1 | len | len | data | ... */ + (*data)++; (*size)--; + ptr = *data; + if ((opt_object_label_size = get_buffer(&ptr, *size, data, size, 128)) == 0) + return 1; + memcpy(opt_object_label, ptr, opt_object_label_size); + FILL_ATTR((*keyTemplate)[*n_attr], CKA_LABEL, opt_object_label, opt_object_label_size); + ++(*n_attr); + } + + if (*size > 3 && (*data)[0] % 2 && *n_attr < 20){ + /* ... | present -> 0/1 | len | len | data | ... */ + (*data)++; (*size)--; + ptr = *data; + if ((opt_object_id_len = get_buffer(&ptr, *size, data, size, 100)) == 0) + return 1; + memcpy(opt_object_id, ptr, opt_object_id_len); + FILL_ATTR((*keyTemplate)[*n_attr], CKA_ID, opt_object_id, opt_object_id_len); + ++(*n_attr); + } + if (*size > 4 && (*data)[0] % 2 && *n_attr < 20){ + /* ... | present -> 0/1 | len | mech1 | mech2 | ... | mechn | ... */ + opt_allowed_mechanisms_len = (*data)[1] > 20 ? 20 : (*data)[1]; + (*data) += 2; + (*size) -= 2; + for (size_t i = 0; i < opt_allowed_mechanisms_len; i++) { + if (*size <= sizeof(unsigned int)) + return 1; + opt_allowed_mechanisms[i] = *((unsigned int *)data); + (*data) += sizeof(unsigned int); + (*size) -= sizeof(unsigned int); + } + FILL_ATTR((*keyTemplate)[*n_attr], CKA_ALLOWED_MECHANISMS, opt_allowed_mechanisms, opt_allowed_mechanisms_len); + ++(*n_attr); + } + if (*size == 0) + return 1; + return 0; +} + +static void test_unwrap(const uint8_t *data, size_t size) +{ + CK_SESSION_HANDLE session; + CK_MECHANISM mech = {0, NULL_PTR, 0}; + uint8_t login_type = CKU_USER; + char *pin = NULL; + const unsigned char *opt_id, *wrapped_key; + size_t opt_id_len; + CK_OBJECT_HANDLE hUnwrappingKey; + CK_ULONG wrapped_key_length; + CK_BYTE_PTR pWrappedKey; + unsigned char in_buffer[1024]; + CK_OBJECT_CLASS secret_key_class = CKO_SECRET_KEY; + CK_ATTRIBUTE *keyTemplate = NULL; + int n_attr = 2; + CK_OBJECT_HANDLE hSecretKey; + + /* Set options */ + if (set_mechanism(&data, &size, &mech) || size < 3) + goto end; + login_type = data[0]; + data++; size--; + if (!(pin = extract_word(&data, &size))) + goto end; + opt_id = data; + opt_id_len = get_buffer(&opt_id, size, &data, &size, 256); + wrapped_key = data; + if ((wrapped_key_length = get_buffer(&wrapped_key, size, &data, &size, 1024)) == 0) + goto end; + memcpy(in_buffer, wrapped_key, wrapped_key_length); + pWrappedKey = in_buffer; + + if (fill_key_template((CK_ATTRIBUTE **) &keyTemplate, &n_attr, &data, &size, &secret_key_class, true)) + goto end; + + /* Initialize */ + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + goto end; + + p11->C_Login(session, login_type, (CK_UTF8CHAR *) pin, strlen(pin)); + /* Find keys*/ + if (!fuzz_find_object(session, CKO_PRIVATE_KEY, &hUnwrappingKey, opt_id_len ? opt_id : NULL, opt_id_len)) + if (!fuzz_find_object(session, CKO_SECRET_KEY, &hUnwrappingKey, opt_id_len ? opt_id : NULL, opt_id_len)) + goto fin; + p11->C_UnwrapKey(session, &mech, hUnwrappingKey, pWrappedKey, wrapped_key_length, keyTemplate, n_attr, &hSecretKey); + +fin: + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +end: + free(pin); + free(keyTemplate); +} + +static void test_derive(const uint8_t *data, size_t size) +{ + CK_SESSION_HANDLE session; + CK_OBJECT_HANDLE key; + CK_MECHANISM mech = {0, NULL_PTR, 0}; + uint8_t login_type = CKU_USER; + char *pin = NULL; + const unsigned char *opt_id = NULL; + size_t opt_id_len; + CK_OBJECT_HANDLE newkey = 0; + CK_OBJECT_CLASS newkey_class = CKO_SECRET_KEY; + CK_ATTRIBUTE *keyTemplate = NULL; + int n_attrs = 2; + + /* Set options */ + if (set_mechanism(&data, &size, &mech) || size < 3) + goto end; + login_type = data[0]; + data++; size--; + if (!(pin = extract_word(&data, &size))) + goto end; + opt_id = data; + opt_id_len = get_buffer(&opt_id, size, &data, &size, 256); + if (fill_key_template((CK_ATTRIBUTE **) &keyTemplate, &n_attrs, &data, &size, &newkey_class, false)) + goto end; + + /* Initialize */ + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + goto end; + + p11->C_Login(session, login_type, (CK_UTF8CHAR *) pin, strlen(pin)); + if (fuzz_find_object(session, CKO_PRIVATE_KEY, &key, opt_id_len ? opt_id : NULL, opt_id_len)) + p11->C_DeriveKey(session, &mech, key, keyTemplate, n_attrs, &newkey); + + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +end: + free(pin); + free(keyTemplate); +} + +static void test_genkeypair(const uint8_t *data, size_t size) +{ + CK_SESSION_HANDLE session; + CK_OBJECT_HANDLE hPublicKey; + CK_OBJECT_HANDLE hPrivateKey; + CK_MECHANISM mech = {0, NULL_PTR, 0}; + uint8_t login_type = CKU_USER; + char *pin = NULL; + CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; + CK_OBJECT_CLASS privkey_class = CKO_PRIVATE_KEY; + int n_pubkey_attr = 2; + int n_privkey_attr = 2; + CK_ATTRIBUTE *publicKeyTemplate = NULL; + CK_ATTRIBUTE *privateKeyTemplate = NULL; + + /* Process options*/ + if (set_mechanism(&data, &size, &mech) || size < 3) + goto end; + login_type = data[0]; + data++; size--; + if (!(pin = extract_word(&data, &size))) + goto end; + + if (fill_key_template(&publicKeyTemplate, &n_pubkey_attr, &data, &size, &pubkey_class, true) != 0 + || fill_key_template(&privateKeyTemplate, &n_privkey_attr, &data, &size, &privkey_class, true) != 0) + goto end; + + /* Initialize */ + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + goto end; + + p11->C_Login(session, login_type, (CK_UTF8CHAR *) pin, strlen(pin)); + p11->C_GenerateKeyPair(session, &mech, publicKeyTemplate, n_pubkey_attr, + privateKeyTemplate, n_privkey_attr, + &hPublicKey, &hPrivateKey); + p11->C_CloseSession(session); + p11->C_Finalize(NULL); + +end: + free(pin); + free(privateKeyTemplate); + free(publicKeyTemplate); +} + +static void test_store_data(const uint8_t *data, size_t size) +{ + CK_SESSION_HANDLE session; + CK_OBJECT_HANDLE data_obj; + CK_OBJECT_CLASS class = CKO_DATA; + uint8_t login_type = CKU_USER; + unsigned char contents[5001]; + int contents_len = 0; + const uint8_t *ptr = NULL; + CK_ATTRIBUTE *data_templ = NULL; + int n_data_attr = 0; + char *pin = NULL; + unsigned char app_id[256]; + int app_id_len = 0; + + /* Create data template */ + if (!(data_templ = malloc(20 * sizeof(CK_ATTRIBUTE)))) + return; + memset(data_templ, 0, 20 * sizeof(CK_ATTRIBUTE)); + + /* Get PIN */ + if (!(pin = extract_word(&data, &size))) + goto end; + + /* Extract content from fuzzing input*/ + memset(contents, 0, sizeof(contents)); + ptr = data; + if ((contents_len = get_buffer(&ptr, size, &data, &size, 5000)) == 0) + goto end; + memcpy(contents, ptr, contents_len); + contents[contents_len] = '\0'; + + /* Fill attributes to data template */ + if (size < 4) + goto end; + FILL_ATTR(data_templ[n_data_attr], CKA_CLASS, &class, sizeof(class)); + n_data_attr++; + FILL_ATTR(data_templ[n_data_attr], CKA_VALUE, &contents, contents_len); + n_data_attr++; + fill_bool_attr(&data_templ, &n_data_attr, CKA_TOKEN, *data % 2); + data++; size--; + fill_bool_attr(&data_templ, &n_data_attr, CKA_PRIVATE, *data % 2); + data++; size--; + + /* Get application id*/ + if (data[0] % 2){ + data++; size--; + ptr = data; + if ((app_id_len = get_buffer(&ptr, size, &data, &size, 256)) == 0) + goto end; + memcpy(app_id, ptr, app_id_len); + FILL_ATTR(data_templ[n_data_attr], CKA_OBJECT_ID, app_id, app_id_len); + n_data_attr++; + } + + /* Initialize */ + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + goto end; + + p11->C_Login(session, login_type, (CK_UTF8CHAR *) pin, strlen(pin)); + p11->C_CreateObject(session, data_templ, n_data_attr, &data_obj); + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +end: + free(data_templ); + free(pin); +} + +static void test_store_cert(const uint8_t *data, size_t size) +{ + CK_SESSION_HANDLE session; + CK_OBJECT_CLASS class = CKO_CERTIFICATE; + uint8_t login_type = CKU_USER; + unsigned char contents[5000]; + int contents_len = 0; + const uint8_t *ptr = NULL; + CK_ATTRIBUTE *cert_templ = NULL; + int n_cert_attr = 0; + char *pin = NULL; + CK_OBJECT_HANDLE cert_obj; + CK_CERTIFICATE_TYPE cert_type = CKC_X_509; + + /* Create certificate template */ + if (!(cert_templ = malloc(20 * sizeof(CK_ATTRIBUTE)))) + return; + memset(cert_templ, 0, 20 * sizeof(CK_ATTRIBUTE)); + + /* Get PIN */ + if (!(pin = extract_word(&data, &size))) + goto end; + + /* Extract content from fuzzing input */ + memset(contents, 0, sizeof(contents)); + ptr = data; + if ((contents_len = get_buffer(&ptr, size, &data, &size, 5000)) == 0) + goto end; + memcpy(contents, ptr, contents_len); + contents[contents_len] = '\0'; + + /* Fill attributes to certificate template */ + if (size < 4) + goto end; + FILL_ATTR(cert_templ[n_cert_attr], CKA_CLASS, &class, sizeof(class)); + n_cert_attr++; + FILL_ATTR(cert_templ[n_cert_attr], CKA_VALUE, contents, contents_len); + n_cert_attr++; + FILL_ATTR(cert_templ[n_cert_attr], CKA_CERTIFICATE_TYPE, &cert_type, sizeof(cert_type)); + n_cert_attr++; + fill_bool_attr(&cert_templ, &n_cert_attr, CKA_TOKEN, *data % 2); + data++; size--; + fill_bool_attr(&cert_templ, &n_cert_attr, CKA_PRIVATE, *data % 2); + data++; size--; + + /* Initialize */ + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + goto end; + + p11->C_Login(session, login_type, (CK_UTF8CHAR *) pin, strlen(pin)); + p11->C_CreateObject(session, cert_templ, n_cert_attr, &cert_obj); + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +end: + free(pin); + free(cert_templ); +} + +static void test_store_key(const uint8_t *data, size_t size) +{ + CK_SESSION_HANDLE session; + CK_OBJECT_CLASS class = CKO_SECRET_KEY; + uint8_t login_type = CKU_USER; + unsigned char contents[5000]; + int contents_len = 0; + const uint8_t *ptr = NULL; + CK_ATTRIBUTE *key_template = NULL; + int n_key_attr = 0; + char *pin = NULL; + CK_OBJECT_HANDLE key_obj; + + memset(contents, 0, sizeof(contents)); + if (size < 3) + return; + class = *data; + data++; size--; + + /* Get PIN */ + if (!(pin = extract_word(&data, &size))) + goto end; + + if (fill_key_template(&key_template, &n_key_attr, &data, &size, &class, true) != 0) + goto end; + + if (size < 3) + goto end; + if (data[0] && n_key_attr < 20) { + data++; size--; + ptr = data; + if ((contents_len = get_buffer(&ptr, size, &data, &size, 5000)) == 0) + goto end; + memcpy(contents, ptr, contents_len); + FILL_ATTR(key_template[n_key_attr], CKA_VALUE, contents, contents_len); + n_key_attr++; + } + + /* Initialize */ + if (fuzz_pkcs11_initialize(data, size, NULL, &session) != CKR_OK) + goto end; + + p11->C_Login(session, login_type, (CK_UTF8CHAR *) pin, strlen(pin)); + p11->C_CreateObject(session, key_template, n_key_attr, &key_obj); + p11->C_CloseSession(session); + p11->C_Finalize(NULL); +end: + free(pin); + free(key_template); +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + uint8_t operation = 0; + void (*func_ptr[])(const uint8_t*, size_t) = { + test_change_pin, + test_init_pin, + test_init_token, + test_random, + test_digest_update, + test_digest, + test_sign, + test_verify, + test_decrypt, + test_wrap, + test_unwrap, + test_derive, + test_genkeypair, + test_store_data, + test_store_cert, + test_store_key + }; + + if (size < 10) + return 0; + + operation = *data % 16; + data++; + size--; + + func_ptr[operation](data, size); + + return 0; +} diff --git a/src/tests/fuzzing/fuzz_pkcs11_uri.c b/src/tests/fuzzing/fuzz_pkcs11_uri.c new file mode 100644 index 0000000000..2ceed7b9ac --- /dev/null +++ b/src/tests/fuzzing/fuzz_pkcs11_uri.c @@ -0,0 +1,56 @@ +/* + * fuzz_pkcs11_uri.c: Fuzz target for PKCS #11 URI parser + * + * Copyright (C) 2025 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#undef stderr +#define stderr stdout + +int +LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) +{ + char *input_string = NULL; + struct pkcs11_uri *uri = NULL; + if (Size == 0) + return 0; + if ((input_string = malloc(Size + 1)) == NULL) + return 0; + memcpy(input_string, Data, Size); + input_string[Size] = 0; + + if ((uri = pkcs11_uri_new()) == NULL) { + free(input_string); + return 0; + } + parse_pkcs11_uri(input_string, uri); + + pkcs11_uri_free(uri); + free(input_string); + return 0; +} diff --git a/src/tests/fuzzing/fuzz_pkcs15_crypt.c b/src/tests/fuzzing/fuzz_pkcs15_crypt.c new file mode 100644 index 0000000000..730878e3ae --- /dev/null +++ b/src/tests/fuzzing/fuzz_pkcs15_crypt.c @@ -0,0 +1,170 @@ +/* + * fuzz_pkcs15_crypt.c: Fuzz target for pkcs15-crypt + * + * Copyright (C) 2022 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "libopensc/internal.h" +#include +#include +#include +#include "fuzzer_reader.h" +#include "fuzzer_tool.h" +#undef stderr +#define stderr stdout + +/* Rename main to call it in fuzz target */ +#define main _main +#define util_connect_card_ex(ctx, card, id, do_wait, do_lock) fuzz_util_connect_card(ctx, card) +# include "tools/pkcs15-crypt.c" +#undef main + +static const uint8_t *reader_data = NULL; +static size_t reader_data_size = 0; + +/* Use instead of util_connect_card() */ +int fuzz_util_connect_card(sc_context_t *ctx, sc_card_t **card) +{ + opt_output = NULL; /* Do not create new outputfile */ + return fuzz_connect_card(ctx, card, NULL, reader_data, reader_data_size); +} + +void initialize_global() +{ + /* Global variables need to be reser between runs, + fuzz target is called repetitively in one execution */ + verbose = 0, opt_wait = 0, opt_raw = 0; + opt_reader = NULL; + opt_pincode = NULL, opt_key_id = NULL; + opt_input = NULL, opt_output = NULL; + opt_bind_to_aid = NULL; + opt_sig_format = NULL; + opt_crypt_flags = 0; + ctx = NULL; + card = NULL; + p15card = NULL; + + optind = 0; + opterr = 0; + optopt = 0; +} + +void test_operation(char *op, char *pin, const uint8_t *data, size_t size, char *filename, + char *hash, char *format, char *aid, char *id, uint8_t pad) +{ + char *argv[] = {"./fuzz_pkcs15_crypt", op, "-p", pin, "-i", filename, + hash, "-f", format, NULL, NULL, NULL, NULL, NULL, NULL}; + int argc = 9; + + if (aid) { + argv[argc++] = "--aid"; + argv[argc++] = aid; + } + if (id) { + argv[argc++] = "-k"; + argv[argc++] = id; + } + if (pad) + argv[argc++] = "--pkcs1"; + + reader_data = data; + reader_data_size = size; + _main(argc, argv); +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + uint8_t operation = 0; + uint8_t hash = 0; + char *hash_options[] = {"--md5", "--sha-1", "--sha-224", "--sha-256", "--sha-384", "--sha-512"}; + uint8_t pad = 0; + uint8_t format = 0; + char *formats[] = {"rs", "sequence", "openssl"}; + uint8_t aid = 0; + char *opt_aid = NULL; + uint8_t id = 0; + char *opt_id = NULL; + char *pin = NULL; + char *filename = NULL; + + if (size < 15) + return 0; + +#ifdef FUZZING_ENABLED + fclose(stdout); +#endif + + initialize_global(); + + operation = data[0] % 3; + data++; size--; + + if (!(pin = extract_word(&data, &size))) + return 0; + + if (operation == 0) { /* test random arguments */ + char **argv = NULL; + int argc = 0; + + /* setup pin and input file otherwise fuzz target waits for input from stdin */ + opt_pincode = pin; + opt_input = "invalid_filename"; + + if (get_fuzzed_argv("./fuzz_pkcs15_crypt", data, size, &argv, &argc, &reader_data, &reader_data_size) != 0) + goto err; + _main(argc, argv); + free_arguments(argc, argv); + } else { + /* Set options */ + if (size < 5) + goto err; + hash = data[0] % 6; data++; size--; + pad = data[0] % 2; data++; size--; + format = data[0] % 3; data++; size--; + + aid = data[0] % 2; data++; size--; + if (aid) { + if (!(opt_aid = extract_word(&data, &size))) + goto err; + } + if (size < 3) + goto err; + + id = data[0] % 2; data++; size--; + if (id) { + if (!(opt_id = extract_word(&data, &size))) + goto err; + } + + if (create_input_file(&filename, &data, &size) != 0) + goto err; + test_operation(operation == 1 ? "-c" : "-s", pin, data, size, filename, hash_options[hash], formats[format], opt_aid, opt_id, pad); + + remove_file(filename); + } + +err: + free(pin); + free(opt_aid); + free(opt_id); + return 0; +} diff --git a/src/tests/fuzzing/fuzz_pkcs15_decode.c b/src/tests/fuzzing/fuzz_pkcs15_decode.c new file mode 100644 index 0000000000..58747fe469 --- /dev/null +++ b/src/tests/fuzzing/fuzz_pkcs15_decode.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2019 Frank Morgner + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fuzzer_reader.h" +#include "libopensc/pkcs15.h" +#include "libopensc/internal.h" + +uint16_t fuzz_get_buffer(const uint8_t **buf, size_t buf_len, const uint8_t **out, size_t *out_len) +{ + uint16_t len = 0; + + if (!buf || !(*buf) || buf_len < 2) + return 0; + + /* Get length of the result buffer*/ + len = *((uint16_t *) *buf); + if (buf_len - 2 <= len) + return 0; + (*buf) += 2; + buf_len -= 2; + + /* Set out buffer to new reader data*/ + *out = *buf + len; + *out_len = buf_len - len; + return len; +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) +{ + size_t i = 0; + struct sc_reader *reader = NULL; + const uint8_t *buf = Data, *reader_data = NULL; + uint16_t buf_len = 0; + size_t reader_data_len = 0; + struct sc_context *ctx = NULL; + struct sc_pkcs15_card *p15card = NULL; + sc_card_t *card = NULL; + struct sc_pkcs15_tokeninfo *tokeninfo = NULL; + int (* decode_entries[])(struct sc_pkcs15_card *, struct sc_pkcs15_object *, + const u8 **nbuf, size_t *nbufsize) = { + sc_pkcs15_decode_prkdf_entry, sc_pkcs15_decode_pukdf_entry, + sc_pkcs15_decode_skdf_entry, sc_pkcs15_decode_cdf_entry, + sc_pkcs15_decode_dodf_entry, sc_pkcs15_decode_aodf_entry + }; + int algorithms[] = { SC_ALGORITHM_RSA, SC_ALGORITHM_EC, SC_ALGORITHM_GOSTR3410, SC_ALGORITHM_EDDSA }; + + /* Split data into testing buffer and APDU for connecting */ + if ((buf_len = fuzz_get_buffer(&buf, Size, &reader_data, &reader_data_len)) == 0) + return 0; + + /* Establish context for fuzz app*/ + sc_establish_context(&ctx, "fuzz"); + if (!ctx) + return 0; + + if (fuzz_connect_card(ctx, &card, &reader, reader_data, reader_data_len) != SC_SUCCESS) + goto err; + + sc_pkcs15_bind(card, NULL, &p15card); + if (!p15card) + goto err; + + for (i = 0; i < sizeof decode_entries/sizeof *decode_entries; i++) { + struct sc_pkcs15_object *obj; + const u8 *p = buf; + size_t len = (size_t) buf_len; + if (!(obj = calloc(1, sizeof *obj))) + goto err; + while (SC_SUCCESS == decode_entries[i](p15card, obj, &p, &len)) { + sc_pkcs15_free_object(obj); + if (!(obj = calloc(1, sizeof *obj))) + goto err; + } + sc_pkcs15_free_object(obj); + } + + for (i = 0; i < 4; i++) { + struct sc_pkcs15_pubkey *pubkey = calloc(1, sizeof *pubkey); + if (!pubkey) + goto err; + pubkey->algorithm = algorithms[i]; + sc_pkcs15_decode_pubkey(ctx, pubkey, buf, buf_len); + sc_pkcs15_free_pubkey(pubkey); + } + + tokeninfo = sc_pkcs15_tokeninfo_new(); + sc_pkcs15_parse_tokeninfo(ctx, tokeninfo, buf, buf_len); + sc_pkcs15_free_tokeninfo(tokeninfo); + + sc_pkcs15_parse_unusedspace(buf, buf_len, p15card); + +err: + sc_pkcs15_card_free(p15card); + sc_disconnect_card(card); + sc_release_context(ctx); + return 0; +} diff --git a/src/tests/fuzzing/fuzz_pkcs15_encode.c b/src/tests/fuzzing/fuzz_pkcs15_encode.c new file mode 100644 index 0000000000..104bcf5c7c --- /dev/null +++ b/src/tests/fuzzing/fuzz_pkcs15_encode.c @@ -0,0 +1,89 @@ +/* + * fuzz_pkcs15_encode.c: Fuzzer for encoding functions + * + * Copyright (C) 2022 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fuzzer_reader.h" +#include "libopensc/pkcs15.h" +#include "libopensc/internal.h" + + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) +{ + struct sc_context *ctx = NULL; + struct sc_card *card = NULL; + struct sc_pkcs15_card *p15card = NULL; + struct sc_pkcs15_object *obj; + unsigned char *unused_space = NULL; + size_t unused_space_len = 0; + + sc_establish_context(&ctx, "fuzz"); + if (!ctx) + return 0; + + if (fuzz_connect_card(ctx, &card, NULL, Data, Size) != SC_SUCCESS) + goto err; + + if (sc_pkcs15_bind(card, NULL, &p15card) != 0) + goto err; + + for (obj = p15card->obj_list; obj != NULL; obj = obj->next) { + u8 *buf = NULL; + size_t buf_len = 0; + struct sc_pkcs15_object *key_object = NULL; + sc_pkcs15_pubkey_t *pkey = NULL; + switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) { + case SC_PKCS15_TYPE_PUBKEY: + sc_pkcs15_encode_pukdf_entry(ctx, obj, &buf, &buf_len); + sc_pkcs15_read_pubkey(p15card, obj, &pkey); + sc_pkcs15_free_pubkey(pkey); + break; + case SC_PKCS15_TYPE_PRKEY: + sc_pkcs15_encode_prkdf_entry(ctx, obj, &buf, &buf_len); + break; + case SC_PKCS15_TYPE_DATA_OBJECT: + sc_pkcs15_encode_dodf_entry(ctx, obj, &buf, &buf_len); + break; + case SC_PKCS15_TYPE_SKEY: + sc_pkcs15_encode_skdf_entry(ctx, obj, &buf, &buf_len); + break; + case SC_PKCS15_TYPE_AUTH: + sc_pkcs15_encode_aodf_entry(ctx, obj, &buf, &buf_len); + break; + case SC_PKCS15_TYPE_CERT: + sc_pkcs15_encode_cdf_entry(ctx, obj, &buf, &buf_len); + sc_pkcs15_prkey_attrs_from_cert(p15card, obj, &key_object); + break; + } + free(buf); + } + sc_pkcs15_encode_unusedspace(ctx, p15card, &unused_space, &unused_space_len); + free(unused_space); + +err: + sc_pkcs15_card_free(p15card); + sc_disconnect_card(card); + sc_release_context(ctx); + + return 0; +} diff --git a/src/tests/fuzzing/fuzz_pkcs15_reader.c b/src/tests/fuzzing/fuzz_pkcs15_reader.c new file mode 100644 index 0000000000..87c2b5e056 --- /dev/null +++ b/src/tests/fuzzing/fuzz_pkcs15_reader.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2019 Frank Morgner + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fuzzer_reader.h" +#include "libopensc/pkcs15.h" + +const char *__asan_default_options() { + return "verbosity=0:mallocator_may_return_null=1"; +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) +{ + struct sc_context *ctx = NULL; + struct sc_card *card = NULL; + struct sc_pkcs15_card *p15card = NULL; + struct sc_reader *reader = NULL; + struct sc_pkcs15_object *obj; + + sc_establish_context(&ctx, "fuzz"); + if (!ctx) + return 0; + + if (fuzz_connect_card(ctx, &card, &reader, Data, Size) != SC_SUCCESS) + goto err; + + if (SC_SUCCESS == sc_pkcs15_bind(card, NULL, &p15card) + && p15card) { + const uint8_t *in, *param; + uint16_t in_len, param_len; + fuzz_get_chunk(reader, &in, &in_len); + fuzz_get_chunk(reader, ¶m, ¶m_len); + for (obj = p15card->obj_list; obj != NULL; obj = obj->next) { + u8 buf[0xFFFF]; + size_t i; + + int decipher_flags[] = {SC_ALGORITHM_RSA_RAW, + SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02, SC_ALGORITHM_RSA_PAD_ANSI, + SC_ALGORITHM_RSA_PAD_ISO9796}; + for (i = 0; i < sizeof decipher_flags/sizeof *decipher_flags; i++) { + sc_pkcs15_decipher(p15card, obj, decipher_flags[i], + in, in_len, buf, sizeof buf, NULL); + } + + i = sizeof buf; + sc_pkcs15_derive(p15card, obj, 0, + in, in_len, buf, &i); + + int wrap_flags[] = {0, SC_ALGORITHM_AES_ECB, SC_ALGORITHM_AES_CBC_PAD, + SC_ALGORITHM_AES_CBC}; + for (i = 0; i < sizeof wrap_flags/sizeof *wrap_flags; i++) { + /* see `pkcs15_create_secret_key` in + * `src/pkcs11/framework-pkc15.c` for creating a temporary + * secret key for wrapping/unwrapping */ + size_t l = sizeof buf; + struct sc_pkcs15_object target_key; + struct sc_pkcs15_skey_info skey_info; + uint16_t len; + memset(&target_key, 0, sizeof target_key); + memset(&skey_info, 0, sizeof skey_info); + target_key.type = SC_PKCS15_TYPE_SKEY; + target_key.flags = 2; /* TODO not sure what these mean */ + target_key.session_object = 1; + target_key.data = &skey_info; + skey_info.usage = SC_PKCS15_PRKEY_USAGE_UNWRAP | SC_PKCS15_PRKEY_USAGE_WRAP + | SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT; + skey_info.native = 0; /* card can not use this */ + skey_info.access_flags = 0; /* looks like not needed */ + skey_info.key_type = 0x1fUL; /* CKK_AES */ + skey_info.value_len = 128; + fuzz_get_chunk(reader, (const u8 **) &skey_info.data.value, &len); + skey_info.data.len = len; + + sc_pkcs15_unwrap(p15card, obj, &target_key, wrap_flags[i], + in, in_len, param, param_len); + sc_pkcs15_wrap(p15card, obj, &target_key, wrap_flags[i], + buf, &l, in, in_len); + } + + int signature_flags[] = {SC_ALGORITHM_RSA_RAW, + SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01, SC_ALGORITHM_RSA_PAD_ANSI, + SC_ALGORITHM_RSA_PAD_ISO9796, + SC_ALGORITHM_RSA_PAD_PSS|SC_ALGORITHM_MGF1_SHA1, + SC_ALGORITHM_RSA_PAD_PSS|SC_ALGORITHM_MGF1_SHA256, + SC_ALGORITHM_RSA_PAD_PSS|SC_ALGORITHM_MGF1_SHA384, + SC_ALGORITHM_RSA_PAD_PSS|SC_ALGORITHM_MGF1_SHA512, + SC_ALGORITHM_RSA_PAD_PSS|SC_ALGORITHM_MGF1_SHA224, + SC_ALGORITHM_ECDSA_RAW, SC_ALGORITHM_ECDSA_HASH_SHA1, + SC_ALGORITHM_ECDSA_HASH_SHA224, SC_ALGORITHM_ECDSA_HASH_SHA256, + SC_ALGORITHM_ECDSA_HASH_SHA384, SC_ALGORITHM_ECDSA_HASH_SHA512, + SC_ALGORITHM_GOSTR3410_RAW, SC_ALGORITHM_GOSTR3410_HASH_GOSTR3411, + SC_ALGORITHM_GOSTR3410_HASHES, + }; + for (i = 0; i < sizeof signature_flags/sizeof *signature_flags; i++) { + sc_pkcs15_compute_signature(p15card, obj, signature_flags[i], + in, in_len, buf, sizeof buf, NULL); + } + + if (obj->type == SC_PKCS15_TYPE_AUTH_PIN) { + sc_pkcs15_verify_pin(p15card, obj, in, in_len); + sc_pkcs15_change_pin(p15card, obj, in, in_len, param, param_len); + sc_pkcs15_unblock_pin(p15card, obj, in, in_len, param, param_len); + sc_pkcs15_get_pin_info(p15card, obj); + } + } + sc_pkcs15_card_free(p15card); + } + +err: + sc_disconnect_card(card); + sc_release_context(ctx); + + return 0; +} diff --git a/src/tests/fuzzing/fuzz_pkcs15_tool.c b/src/tests/fuzzing/fuzz_pkcs15_tool.c new file mode 100644 index 0000000000..93c7abb270 --- /dev/null +++ b/src/tests/fuzzing/fuzz_pkcs15_tool.c @@ -0,0 +1,98 @@ +/* + * fuzz_pkcs15_tool.c: Fuzz target for pkcs15-tool + * + * Copyright (C) 2022 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __APPLE__ +#define _XOPEN_SOURCE 600 +#else +#define _XOPEN_SOURCE 500 +#endif + +#include +#include +#include +#include "libopensc/internal.h" +#include "fuzzer_reader.h" +#include "fuzzer_tool.h" +#undef stderr +#define stderr stdout + +/* Rename main to call it in fuzz target */ +#define main _main +#define util_connect_card_ex(ctx, card, id, do_wait, do_lock) fuzz_util_connect_card(ctx, card) +# include "tools/pkcs15-tool.c" +#undef main + +static const uint8_t *reader_data = NULL; +static size_t reader_data_size = 0; + +/* Use instead of util_connect_card() */ +int fuzz_util_connect_card(sc_context_t *ctx, sc_card_t **card) +{ + return fuzz_connect_card(ctx, card, NULL, reader_data, reader_data_size); +} + +void initialize_global() +{ + /* Global variables need to be reser between runs, + fuzz target is called repetitively in one execution */ + ctx = NULL; + card = NULL; + p15card = NULL; + opt_auth_id = NULL; + opt_reader = NULL; + opt_cert = NULL; + opt_data = NULL; + opt_pubkey = NULL; + opt_outfile = NULL; + opt_bind_to_aid = NULL; + opt_newpin = NULL; + opt_pin = NULL; + opt_puk = NULL; + + optind = 0; + opterr = 0; /* do not print out error messages */ + optopt = 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + char **argv = NULL; + int argc = 0; + + if (size < 10) + return 0; + +#ifdef FUZZING_ENABLED + fclose(stdout); +#endif + initialize_global(); + + if (get_fuzzed_argv("./fuzz_pkcs15", data, size, &argv, &argc, &reader_data, &reader_data_size) != 0) + return 0; + _main(argc, argv); + free_arguments(argc, argv); + + return 0; +} diff --git a/src/tests/fuzzing/fuzz_pkcs15init.c b/src/tests/fuzzing/fuzz_pkcs15init.c new file mode 100644 index 0000000000..ea9c84fa38 --- /dev/null +++ b/src/tests/fuzzing/fuzz_pkcs15init.c @@ -0,0 +1,369 @@ +/* + * fuzz_pkcs15init.c: Fuzzer for functions processing pkcs15 init + * + * Copyright (C) 2022 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fuzzer_reader.h" +#include "pkcs15init/pkcs15-lib.c" +#include "scconf/scconf.h" +#include "pkcs15init/pkcs15-init.h" +#include "pkcs15init/profile.c" +#include "pkcs15init/profile.h" + +int fuzz_profile_load(struct sc_profile *profile, const uint8_t *data, size_t size) +{ + int rv = 0; + scconf_context *conf = NULL; + conf = scconf_new(NULL); + if (!conf) + return 0; + + if ((rv = scconf_parse_string(conf, (char *)data)) < 0) { + scconf_free(conf); + return rv; + } + + rv = process_conf(profile, conf); + scconf_free(conf); + return rv; +} + +void fuzz_pkcs15init_bind(struct sc_card *card, struct sc_profile **result, + const uint8_t *data, size_t size) +{ + struct sc_profile *profile = NULL; + const char *driver; + struct sc_pkcs15init_operations * (* func)(void) = NULL; + int r = 0; + + if (!card || !card->driver || !result) + return; + + *result = NULL; + + r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN); + if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) { + return; + } + + profile = sc_profile_new(); + if (!profile) + return; + profile->card = card; + driver = card->driver->short_name; + + for (int i = 0; profile_operations[i].name; i++) { + if (!strcasecmp(driver, profile_operations[i].name)) { + func = (struct sc_pkcs15init_operations *(*)(void)) profile_operations[i].func; + break; + } + } + if (func) { + profile->ops = func(); + } else { + sc_profile_free(profile); + return; + } + profile->name = strdup("Fuzz profile"); + + r = sc_pkcs15init_read_info(card, profile); + if (r < 0) { + sc_profile_free(profile); + return; + } + + if (fuzz_profile_load(profile, data, size) < 0) { + sc_profile_free(profile); + return; + } + + if (sc_profile_finish(profile, NULL) < 0) { + sc_profile_free(profile); + return; + } + *result = profile; +} + +int fuzz_get_reader_data(const uint8_t *from, size_t from_size, const uint8_t **to, size_t *to_size) +{ + size_t i = 0; + while(i < from_size - 1 && from[i] != '\0') + i++; + + if (from[i] != '\0') + return 0; + + *to_size = from_size - (i + 1); + *to = from + (i + 1); + return 1; +} + +void do_init_app(struct sc_profile *profile, struct sc_pkcs15_card *p15card, sc_card_t *card, + unsigned char *so_pin, unsigned char *so_puk) +{ + struct sc_pkcs15init_initargs init_args; + sc_pkcs15_auth_info_t info; + int so_puk_disabled = 0; + + memset(&init_args, 0, sizeof(init_args)); + memset(&info, 0, sizeof(info)); + sc_pkcs15init_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &info); + if ((info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED) && + (info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)) + so_puk_disabled = 1; + + sc_pkcs15init_get_pin_info(profile, SC_PKCS15INIT_SO_PUK, &info); + + init_args.so_pin = so_pin; + init_args.so_pin_len = 8; + + if (!so_puk_disabled) { + init_args.so_puk = so_puk; + init_args.so_puk_len = 8; + } + + sc_pkcs15init_add_app(card, profile, &init_args); +} + +void do_store_pin(struct sc_profile *profile, struct sc_pkcs15_card *p15card, sc_card_t *card, + unsigned char *pin, unsigned char *so_pin) +{ + struct sc_pkcs15init_pinargs pin_args; + char pin_id[SC_PKCS15_MAX_ID_SIZE] = "1\0"; + sc_pkcs15init_set_p15card(profile, p15card); + + memcpy(pin, "1234555678\0", 11); /* Set new pin */ + memset(&pin_args, 0, sizeof(pin_args)); + + sc_pkcs15_format_id(pin_id, &pin_args.auth_id); + pin_args.pin = pin; + pin_args.pin_len = 6; + pin_args.label = "Basic PIN"; + + pin_args.puk = so_pin; + pin_args.puk_len = 8; + + sc_pkcs15init_store_pin(p15card, profile, &pin_args); +} + +void do_store_data_object(struct sc_profile *profile, struct sc_pkcs15_card *p15card, sc_card_t *card, + uint8_t *buf, size_t len) +{ + struct sc_pkcs15init_dataargs args; + char value[SC_MAX_OBJECT_ID_OCTETS]; + + memcpy(value, buf, SC_MAX_OBJECT_ID_OCTETS); + value[len < SC_MAX_OBJECT_ID_OCTETS ? len : SC_MAX_OBJECT_ID_OCTETS - 1] = '\0'; + + memset(&args, 0, sizeof(args)); + sc_init_oid(&args.app_oid); + args.label = "label"; + args.app_label = "pkcs15-init"; + + sc_format_oid(&args.app_oid, value); + + args.der_encoded.value = buf; + args.der_encoded.len = len; + sc_pkcs15init_store_data_object(p15card, profile, &args, NULL); +} + +void do_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, sc_card_t *card) +{ + struct sc_pkcs15init_keygen_args keygen_args; + int algorithms[] = { SC_ALGORITHM_RSA, SC_ALGORITHM_EC }; + unsigned int keybits[] = { 1024, 0 }; + + memset(&keygen_args, 0, sizeof(keygen_args)); + sc_pkcs15_format_id("01", &(keygen_args.prkey_args.auth_id)); + keygen_args.prkey_args.access_flags |= + SC_PKCS15_PRKEY_ACCESS_SENSITIVE + | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE + | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE + | SC_PKCS15_PRKEY_ACCESS_LOCAL; + + for (int i = 0; i < 2; i++) { + keygen_args.prkey_args.key.algorithm = algorithms[i]; + if (algorithms[i] == SC_ALGORITHM_EC) /* strdup called also in parse_alg_spec() */ + keygen_args.prkey_args.key.u.ec.params.named_curve = strdup("prime256v1"); + sc_pkcs15init_generate_key(p15card, profile, &keygen_args, keybits[i], NULL); + /* clear the keygen prkey by algorithms which includes the pubkey and ec_params */ + sc_pkcs15_erase_prkey(&keygen_args.prkey_args.key); + } +} + +void do_generate_skey(struct sc_profile *profile, struct sc_pkcs15_card *p15card, sc_card_t *card) +{ + struct sc_pkcs15init_skeyargs skey_args; + int algorithms[] = { SC_ALGORITHM_DES, SC_ALGORITHM_3DES, SC_ALGORITHM_AES }; + unsigned int keybits[] = { 64, 192, 128 }; + + /* init keygen_args*/ + memset(&skey_args, 0, sizeof(skey_args)); + skey_args.label = "label"; + skey_args.usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT; + skey_args.user_consent = 0; + + for (int i = 0; i < 3; i++) { + skey_args.algorithm = algorithms[i]; + skey_args.value_len = keybits[i]; + sc_pkcs15init_generate_secret_key(p15card, profile, &skey_args, NULL); + } +} + +void do_store_secret_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, + sc_card_t *card, uint8_t *buf) +{ + struct sc_pkcs15init_skeyargs args; + int algorithms[] = { SC_ALGORITHM_AES, SC_ALGORITHM_DES, SC_ALGORITHM_3DES }; + unsigned int keybits[] = { 128, 64, 192 }; + + memset(&args, 0, sizeof(args)); + args.access_flags |= SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE | SC_PKCS15_PRKEY_ACCESS_SENSITIVE; + args.usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT; + sc_pkcs15_format_id("02", &(args.auth_id)); + + for (int i = 0; i < 3; i++) { + size_t keybytes = BYTES4BITS(keybits[i]); + args.key.data = malloc(keybytes); + memcpy(args.key.data, buf, keybytes); + args.key.data_len = keybytes; + args.algorithm = algorithms[i]; + args.value_len = keybits[i]; + + sc_pkcs15init_store_secret_key(p15card, profile, &args, NULL); + if (args.key.data) + free(args.key.data); + } +} + +void do_erase(struct sc_profile *profile, sc_card_t *card) +{ + struct sc_pkcs15_card *p15card; + + p15card = sc_pkcs15_card_new(); + p15card->card = card; + + sc_pkcs15init_erase_card(p15card, profile, NULL); + sc_pkcs15_card_free(p15card); +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + sc_context_t *ctx = NULL; + sc_card_t *card = NULL; + struct sc_pkcs15_card *p15card = NULL; + struct sc_profile *profile = NULL; + struct sc_reader *reader = NULL; + const uint8_t *reader_data = NULL; + size_t reader_data_size = 0; + uint8_t *buf = NULL; + uint16_t len = size < 256 ? size : 256; + unsigned char *pin = NULL; + unsigned char *so_pin = NULL; + unsigned char *puk = NULL; + unsigned char *so_puk = NULL; + struct sc_pkcs15_card *tmp_p15_data = NULL; + +#ifdef FUZZING_ENABLED + fclose(stdout); +#endif + + if (size == 0) + return 0; + + if (!fuzz_get_reader_data(data, size, &reader_data, &reader_data_size)) { + return 0; + } + + /* Establish context for fuzz app*/ + sc_establish_context(&ctx, "fuzz"); + if (!ctx) + return 0; + + if (fuzz_connect_card(ctx, &card, &reader, reader_data, reader_data_size) != SC_SUCCESS) + goto end; + + /* Load profile and bind with card */ + fuzz_pkcs15init_bind(card, &profile, data, size - reader_data_size); + + if(!profile) + goto end; + + pin = malloc(11); + so_pin = malloc(9); + puk = malloc(9); + so_puk = malloc(9); + buf = malloc(len * sizeof(char)); + if (!pin || !so_pin || !puk || !so_puk || !buf) + goto end_release; + + memcpy(pin, "123456\0", 7); + memcpy(so_pin, "12345678\0", 9); + memcpy(puk, "12345678\0", 9); + memcpy(so_puk, "12345678\0", 9); + memcpy(buf, data, len); + + /* test pkcs15-init functionality*/ + do_init_app(profile, p15card, card, so_pin, so_puk); + + if (!sc_pkcs15_bind(card, NULL, &p15card)) { /* First and only sc_pkcs15_bind calling, is omitted in next cases*/ + do_store_pin(profile, p15card, card, pin, so_pin); + } + + /* sc_pkcs15_bind failed, no point in testing next cases */ + if (!p15card) + goto end_release; + + do_store_data_object(profile, p15card, card, buf, len); + do_generate_key(profile, p15card, card); + do_generate_skey(profile, p15card, card); + do_store_secret_key(profile, p15card, card, buf); + + sc_pkcs15init_finalize_card(card, profile); + sc_pkcs15init_sanity_check(p15card, profile); + + do_erase(profile, card); + +end_release: + free(pin); + free(puk); + free(so_pin); + free(so_puk); + free(buf); + +end: + if (profile) { + tmp_p15_data = profile->p15_data; + sc_pkcs15init_unbind(profile); + if (tmp_p15_data != p15card) + sc_pkcs15_unbind(tmp_p15_data); + } + if (p15card) { + sc_pkcs15_unbind(p15card); + } + if (card) + sc_disconnect_card(card); + sc_release_context(ctx); + + return 0; +} diff --git a/src/tests/fuzzing/fuzz_scconf_parse_string.c b/src/tests/fuzzing/fuzz_scconf_parse_string.c new file mode 100644 index 0000000000..3a5b46d8bf --- /dev/null +++ b/src/tests/fuzzing/fuzz_scconf_parse_string.c @@ -0,0 +1,55 @@ +/* + * fuzz_scconf_parse_string.c: Fuzz target for scconf_parse_string + * + * Copyright (C) 2021 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "scconf/scconf.h" +#include "libopensc/internal.h" +#include +#include + +#define MAX_SIZE 16000 + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + scconf_context *ctx = NULL; + char *buf = NULL; + + if (size > MAX_SIZE) + return 0; + + if (!(buf = malloc(size + 1))) + return 0; + if (!(ctx = scconf_new(NULL))) { + free(buf); + return 0; + } + + memcpy(buf, data, size); + buf[size] = '\0'; + + scconf_parse_string(ctx, buf); + + scconf_free(ctx); + free(buf); + return 0; +} diff --git a/src/tests/fuzzing/fuzzer.c b/src/tests/fuzzing/fuzzer.c new file mode 100644 index 0000000000..a186ebb57d --- /dev/null +++ b/src/tests/fuzzing/fuzzer.c @@ -0,0 +1,67 @@ +/* + * fuzzer.c: Standalone main for fuzz target + * + * Copyright (C) 2021 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include +#include + +int LLVMFuzzerTestOneInput (const unsigned char *data, size_t size); + +int main (int argc, char **argv) +{ + printf("Testing one input:\n"); + FILE *fd = NULL; + long int len = 0; + unsigned char *buffer = NULL; + int r = 1; + + if (argc < 2) { + fprintf(stderr, "No arguments, passing NULL\n"); + len = 0; + } else { + if ((fd = fopen(argv[1], "r")) == NULL + || fseek(fd, 0, SEEK_END) != 0 + || (len = ftell(fd)) < 0) { + fprintf(stderr, "fopen/fseek failed\n"); + goto err; + } + rewind(fd); + if ((buffer = (unsigned char*) malloc(len)) == NULL) { + fprintf(stderr, "malloc failed\n"); + goto err; + } + + if (fread(buffer, 1, len, fd) != (size_t)len) { + fprintf(stderr, "fread failed\n"); + goto err; + } + } + + LLVMFuzzerTestOneInput(buffer, len); + r = 0; + +err: + if (fd) + fclose(fd); + if (buffer) + free(buffer); + printf("Done!\n"); + return r; +} diff --git a/src/tests/fuzzing/fuzzer_reader.c b/src/tests/fuzzing/fuzzer_reader.c new file mode 100644 index 0000000000..8fbd6ef647 --- /dev/null +++ b/src/tests/fuzzing/fuzzer_reader.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2019 Frank Morgner + * + * 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 + */ + +#include "fuzzer_reader.h" + +/* private data structures */ +struct driver_data { + const uint8_t *Data; + size_t Size; +}; + +static struct sc_reader_operations fuzz_ops = {0}; +static struct sc_reader_driver fuzz_drv = { + "Fuzzing reader", + "fuzz", + &fuzz_ops, + NULL +}; + +void fuzz_get_chunk(sc_reader_t *reader, const uint8_t **chunk, uint16_t *chunk_size) +{ + struct driver_data *data; + uint16_t c_size; + const uint8_t *c; + + if (chunk) + *chunk = NULL; + if (chunk_size) + *chunk_size = 0; + + if (!chunk || !chunk_size || !reader) { + if (reader) + sc_debug(reader->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Invalid Arguments"); + return; + } + data = reader->drv_data; + if (!data || !data->Data || data->Size < sizeof c_size) { + sc_debug(reader->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Invalid Arguments"); + return; + } + /* parse the length of the returned data on two bytes */ + c_size = *((uint16_t *) data->Data); + /* consume two bytes from the fuzzing data */ + data->Size -= sizeof c_size; + data->Data += sizeof c_size; + + if (data->Size < c_size) { + c_size = data->Size; + } + + /* consume the bytes from the fuzzing data */ + c = data->Data; + data->Size -= c_size; + data->Data += c_size; + + sc_debug_hex(reader->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Returning fuzzing chunk", c, c_size); + + *chunk = c; + *chunk_size = c_size; +} + +static int fuzz_reader_release(sc_reader_t *reader) +{ + if (reader) { + free(reader->drv_data); + reader->drv_data = NULL; + } + + return SC_SUCCESS; +} + +static int fuzz_reader_connect(sc_reader_t *reader) +{ + uint16_t chunk_size; + const uint8_t *chunk; + + fuzz_get_chunk(reader, &chunk, &chunk_size); + + if (chunk_size > SC_MAX_ATR_SIZE) + chunk_size = SC_MAX_ATR_SIZE; + else + reader->atr.len = chunk_size; + + if (chunk_size > 0) + memcpy(reader->atr.value, chunk, chunk_size); + + return SC_SUCCESS; +} + +static int fuzz_reader_disconnect(sc_reader_t *reader) +{ + return SC_SUCCESS; +} + +static int fuzz_reader_transmit(sc_reader_t *reader, sc_apdu_t *apdu) +{ + const uint8_t *chunk; + uint16_t chunk_size; + + fuzz_get_chunk(reader, &chunk, &chunk_size); + + if (chunk_size >= 2) { + /* set the SW1 and SW2 status bytes (the last two bytes of + * the response */ + apdu->sw1 = (unsigned int)chunk[chunk_size - 2]; + apdu->sw2 = (unsigned int)chunk[chunk_size - 1]; + chunk_size -= 2; + /* set output length and copy the returned data if necessary */ + if (chunk_size <= apdu->resplen) + apdu->resplen = chunk_size; + + if (apdu->resplen != 0) + memcpy(apdu->resp, chunk, apdu->resplen); + } else { + apdu->sw1 = 0x6D; + apdu->sw2 = 0x00; + apdu->resplen = 0; + } + + return SC_SUCCESS; +} + +static int fuzz_reader_lock(sc_reader_t *reader) +{ + return 0; +} + +static int fuzz_reader_unlock(sc_reader_t *reader) +{ + return 0; +} + +struct sc_reader_driver *sc_get_fuzz_driver(void) +{ + fuzz_ops.release = fuzz_reader_release; + fuzz_ops.connect = fuzz_reader_connect; + fuzz_ops.disconnect = fuzz_reader_disconnect; + fuzz_ops.transmit = fuzz_reader_transmit; + fuzz_ops.lock = fuzz_reader_lock; + fuzz_ops.unlock = fuzz_reader_unlock; + return &fuzz_drv; +} + +void fuzz_add_reader(struct sc_context *ctx, const uint8_t *Data, size_t Size) +{ + sc_reader_t *reader; + struct driver_data *data; + char name[64] = {0}; + + if (!(reader = calloc(1, sizeof(*reader))) + || !(data = (calloc(1, sizeof(*data))))) { + free(reader); + return; + } + + data->Data = Data; + data->Size = Size; + + reader->driver = &fuzz_drv; + reader->ops = &fuzz_ops; + reader->drv_data = data; + snprintf(name, sizeof name - 1, "%zu random byte%s reader (%p)", + Size, Size == 1 ? "" : "s", Data); + reader->name = strdup(name); + + reader->ctx = ctx; + list_append(&ctx->readers, reader); +} + +int fuzz_connect_card(sc_context_t *ctx, sc_card_t **card, sc_reader_t **reader_out, + const uint8_t *data, size_t size) +{ + struct sc_reader *reader = NULL; + + /* Erase possible readers from ctx */ + while (list_size(&ctx->readers)) { + sc_reader_t *rdr = (sc_reader_t *) list_get_at(&ctx->readers, 0); + _sc_delete_reader(ctx, rdr); + } + if (ctx->reader_driver->ops->finish != NULL) + ctx->reader_driver->ops->finish(ctx); + + /* Create virtual reader */ + ctx->reader_driver = sc_get_fuzz_driver(); + fuzz_add_reader(ctx, data, size); + reader = sc_ctx_get_reader(ctx, 0); + + /* Connect card */ + if (sc_connect_card(reader, card)) + return SC_ERROR_INTERNAL; + + if (reader_out) + *reader_out = reader; + + return SC_SUCCESS; +} diff --git a/src/tests/fuzzing/fuzzer_reader.h b/src/tests/fuzzing/fuzzer_reader.h new file mode 100644 index 0000000000..89e40db7bf --- /dev/null +++ b/src/tests/fuzzing/fuzzer_reader.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 Frank Morgner + * + * 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 + */ + +#ifndef FUZZER_READER_H +#define FUZZER_READER_H + +#include +#include +#include +#include "libopensc/internal.h" + +void fuzz_get_chunk(sc_reader_t *reader, const uint8_t **chunk, uint16_t *chunk_size); +struct sc_reader_driver *sc_get_fuzz_driver(void); +void fuzz_add_reader(struct sc_context *ctx, const uint8_t *Data, size_t Size); +int fuzz_connect_card(sc_context_t *ctx, sc_card_t **card, sc_reader_t **reader_out, + const uint8_t *data, size_t size); + +#endif /* FUZZER_TOOL_H */ diff --git a/src/tests/fuzzing/fuzzer_tool.c b/src/tests/fuzzing/fuzzer_tool.c new file mode 100644 index 0000000000..a395e5efe6 --- /dev/null +++ b/src/tests/fuzzing/fuzzer_tool.c @@ -0,0 +1,171 @@ +/* + * fuzzer_tool.c: Implementation of general tool-fuzzing functions + * + * Copyright (C) 2022 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "fuzzer_tool.h" +#define MAX_ARGC 10000 + +const uint8_t *get_word(const uint8_t *data, size_t size) +{ + /* Words are separated by one zero byte, + return pointer to the next word if there is one */ + const uint8_t *ptr = data; + if (!data || size == 0 || *data == 0) + return NULL; + + ptr = memchr(data, 0, size - 1); + return ptr ? ++ptr : NULL; +} + +char *extract_word(const uint8_t **data, size_t *size) +{ + /* Find word and return its copy (needs to be freed) */ + char *result = NULL; + const uint8_t *ptr = NULL; + + if (*size < 2) + return NULL; + + ptr = get_word(*data, *size); + if (!ptr) + return NULL; + result = strdup((const char *)*data); + *size -=(ptr - *data); + *data = ptr; + + return result; +} + +int get_fuzzed_argv(const char *app_name, const uint8_t *data, size_t size, + char ***argv_out, int *argc_out, const uint8_t **reader_data, size_t *reader_data_size) +{ + const uint8_t *ptr = data, *help_ptr = data; + size_t ptr_size = size; + char **argv = NULL; + int argc = 1; + + /* Count arguments until double zero bytes occurs*/ + while(*ptr != 0) { + ptr = get_word(help_ptr, ptr_size); + if (!ptr) + return -1; + argc++; + ptr_size -= (ptr - help_ptr); + help_ptr = ptr; + } + + if (argc > MAX_ARGC) + return -1; + + argv = malloc((argc + 1) * sizeof(char*)); + if (!argv) + return -1; + + /* Copy arguments into argv */ + ptr = data; + ptr_size = size; + argv[0] = strdup(app_name); + for (int i = 1; i < argc; i++) { + argv[i] = extract_word(&ptr, &ptr_size); + } + argv[argc] = NULL; + + *argc_out = argc; + *argv_out = argv; + *reader_data = ptr + 1; /* there are two zero bytes at the end of argv */ + *reader_data_size = ptr_size - 1; + return 0; +} + +uint16_t get_buffer(const uint8_t **buf, size_t buf_len, const uint8_t **out, size_t *out_len, size_t max_size) +{ + /* Split buf into two parts according to length stored in first two bytes */ + uint16_t len = 0; + + if (!buf || !(*buf) || buf_len < sizeof(uint16_t)) + return 0; + + /* Get length of the result buffer*/ + len = *((uint16_t *) *buf) % max_size; + (*buf) += 2; + buf_len -= 2; + if (buf_len <= len) { + *out = *buf; + *out_len = buf_len; + return 0; + } + + /* Set out buffer to new reader data*/ + *out = *buf + len; + *out_len = buf_len - len; + return len; +} + +int create_input_file(char **filename_out, const uint8_t **data, size_t *size) +{ + const uint8_t *ptr = *data; + size_t file_size = 0, backup_size = *size; + int fd = 0; + size_t r = 0; + char *filename = NULL; + + /* Split data into file content and rest*/ + file_size = get_buffer(&ptr, *size, data, size, 6000); + if (file_size == 0) + return 1; + + filename = strdup("/tmp/input.XXXXXX"); + fd = mkstemp(filename); + if (fd < 0) { + *data = ptr - 2; + *size = backup_size; + free(filename); + return 1; + } + + r = write(fd, ptr, file_size); + close(fd); + + if (r != file_size) { + *data = ptr - 2; + *size = backup_size; + remove_file(filename); + return 1; + } + + *filename_out = filename; + return 0; +} + +void remove_file(char *filename) +{ + if (filename) { + unlink(filename); + free(filename); + } +} + +void free_arguments(int argc, char **argv) +{ + for (int i = 0; i < argc; i++) { + free(argv[i]); + } + free(argv); +} diff --git a/src/tests/fuzzing/fuzzer_tool.h b/src/tests/fuzzing/fuzzer_tool.h new file mode 100644 index 0000000000..85b4f28d2a --- /dev/null +++ b/src/tests/fuzzing/fuzzer_tool.h @@ -0,0 +1,44 @@ +/* + * fuzzer_tool.c: Implementation of general tool-fuzzing functions + * + * Copyright (C) 2022 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifndef FUZZER_TOOL_H +#define FUZZER_TOOL_H + +#include +#include +#include +#include +#include +#include +#include + +#define HALF_BYTE ((sizeof(uint8_t) * 8 * 8) / 2) + +const uint8_t *get_word(const uint8_t *, size_t); +char *extract_word(const uint8_t **, size_t *); +int get_fuzzed_argv(const char *, const uint8_t *, size_t , + char***, int *, const uint8_t **, size_t *); +uint16_t get_buffer(const uint8_t **, size_t, const uint8_t **, size_t *, size_t); +int create_input_file(char **, const uint8_t **, size_t *); +void remove_file(char *); +void free_arguments(int, char **); + +#endif /* FUZZER_TOOL_H */ diff --git a/src/tests/lottery.c b/src/tests/lottery.c index c64f093c8e..ff556deca8 100644 --- a/src/tests/lottery.c +++ b/src/tests/lottery.c @@ -24,7 +24,7 @@ int main(int argc, char *argv[]) struct timeval tv1, tv2; u8 buf[14]; - i = sc_test_init(&argc, argv); + sc_test_init(&argc, argv); for (i = 0; i < 39; i++) freq[i] = 0; c = 0; @@ -37,6 +37,7 @@ int main(int argc, char *argv[]) if (c == 0) { if (0 != gettimeofday(&tv1, NULL)) { fprintf(stderr, "gettimeofday() failed: %s\n", strerror(errno)); + sc_test_cleanup(); return 1; } } diff --git a/src/tests/opensc-minidriver-test.c b/src/tests/opensc-minidriver-test.c new file mode 100644 index 0000000000..5abad1b6b6 --- /dev/null +++ b/src/tests/opensc-minidriver-test.c @@ -0,0 +1,861 @@ +/** + * SmartCard-HSM PKCS#11 Module + * + * Copyright (c) 2017, CardContact Systems GmbH, Minden, Germany + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CardContact Systems GmbH nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CardContact Systems GmbH BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file opensc-minidriver-test.c + * @author Andreas Schwier + * @brief Test framework for the CSP minidriver implementation + */ + +#include +#include +#include +#include +#include + +static int testscompleted = 0; +static int testsfailed = 0; +static char *reader = NULL; + + +#include +#include +#include + + + +char *SystemErrorMsg(DWORD rc) +{ + char *msg = "UNKNOWN"; + + if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, 0, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0)) { + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, GetModuleHandle("crypt32.dll"), rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + } + return msg; +} + + + +char *NTSTATUSErrorMsg(NTSTATUS nts) +{ + char *msg = "UNKNOWN"; + + HMODULE hmod = GetModuleHandle("ntdll.dll"); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, hmod, nts, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + return msg; +} + + + +char *SECURITY_STATUSErrorMsg(SECURITY_STATUS secstat) +{ + char *msg = "UNKNOWN"; + + HMODULE hmod = GetModuleHandle("crypt32.dll"); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, hmod, secstat, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + return msg; +} + + + +/* +char *ErrorMsg(DWORD rc) +{ + char *msg = "UNKNOWN"; + HMODULE hmod; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, 0, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + hmod = GetModuleHandle("bcrypt.dll"); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, hmod, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + hmod = GetModuleHandle("ncrypt.dll"); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, hmod, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + hmod = GetModuleHandle("crypt32.dll"); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, hmod, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + hmod = GetModuleHandle("ntdll.dll"); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, hmod, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + hmod = GetModuleHandle("kernel32.dll"); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, hmod, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + hmod = GetModuleHandle("KernelBase.dll"); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, hmod, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + hmod = GetModuleHandle("msvcrt.dll"); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, hmod, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + hmod = GetModuleHandle("cryptbase.dll"); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, hmod, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + + return msg; +} +*/ + + +static char *verdict(int condition) { + testscompleted++; + + if (condition) { + return "Passed"; + } else { + testsfailed++; + return "Failed"; + } +} + + + +LPVOID WINAPI CSP_ALLOC(__in SIZE_T Size) { + return calloc(1, Size); +} + + + +LPVOID WINAPI CSP_REALLOC(__in LPVOID Address, __in SIZE_T Size) { + return realloc(Address, Size); +} + + + +void WINAPI CSP_FREE(__in LPVOID Address) { + free(Address); +} + + + +int testSignRSA(NCRYPT_KEY_HANDLE hKey, DWORD padding, LPCWSTR hashAlg ) +{ + BCRYPT_KEY_HANDLE hPubKey; + SECURITY_STATUS secstat; + BCRYPT_PKCS1_PADDING_INFO p1padinfo; + BCRYPT_PSS_PADDING_INFO psspadinfo; + BCRYPT_ALG_HANDLE hSignAlg; + void *paddingInfo; + PCCERT_CONTEXT certctx; + NTSTATUS ntstat; + unsigned char cert[4096],hash[64],signature[256],pubkeyblob[1024]; + DWORD dwrc,dwlen,hashlen; + + printf(" RSA signing with %S and %s padding", hashAlg, (padding == BCRYPT_PAD_PKCS1 ? "V1.5" : "PSS")); + + memset(hash, 0xA5, sizeof(hash)); + hashlen = sizeof(hash); + + if (!wcscmp(hashAlg, BCRYPT_SHA1_ALGORITHM)) { + hashlen = 20; + } else if (!wcscmp(hashAlg, BCRYPT_SHA256_ALGORITHM)) { + hashlen = 32; + } else if (!wcscmp(hashAlg, BCRYPT_SHA384_ALGORITHM)) { + hashlen = 48; + } else if (!wcscmp(hashAlg, BCRYPT_SHA512_ALGORITHM)) { + hashlen = 64; + } else if (!wcscmp(hashAlg, BCRYPT_MD5_ALGORITHM)) { + hashlen = 16; + } + + if (padding == BCRYPT_PAD_PKCS1) { + memset(&p1padinfo, 0, sizeof(p1padinfo)); + p1padinfo.pszAlgId = hashAlg; + paddingInfo = &p1padinfo; + } else { + memset(&psspadinfo, 0, sizeof(psspadinfo)); + psspadinfo.pszAlgId = hashAlg; + psspadinfo.cbSalt = hashlen; + paddingInfo = &psspadinfo; + } + + // Export public key from smart card + secstat = NCryptExportKey(hKey, 0, BCRYPT_RSAPUBLIC_BLOB, 0, pubkeyblob, sizeof(pubkeyblob), &dwlen, 0); + + if (secstat != ERROR_SUCCESS) { + printf("\nNCryptExportKey failed: %08lx %s\n", secstat, SECURITY_STATUSErrorMsg(secstat)); + return -1; + } + + ntstat = BCryptOpenAlgorithmProvider(&hSignAlg, BCRYPT_RSA_ALGORITHM, NULL, 0); + + if (ntstat != ERROR_SUCCESS) { + printf("\nBCryptOpenAlgorithmProvider failed: %08lx %s", ntstat, NTSTATUSErrorMsg(ntstat)); + return -1; + } + + ntstat = BCryptImportKeyPair(hSignAlg, 0, BCRYPT_RSAPUBLIC_BLOB, &hPubKey, pubkeyblob, dwlen, 0); + + if (ntstat != ERROR_SUCCESS) { + printf("\nBCryptImportKeyPair failed: %08lx %s", ntstat, NTSTATUSErrorMsg(ntstat)); + return -1; + } + + secstat = NCryptSignHash(hKey, paddingInfo, hash, hashlen, signature, sizeof(signature), &dwlen, padding); + + if (secstat != ERROR_SUCCESS) { + printf("\nNCryptSignHash failed: %08lx %s\n", secstat, SECURITY_STATUSErrorMsg(secstat)); + return -1; + } + + ntstat = BCryptVerifySignature(hPubKey, paddingInfo, hash, hashlen, signature, dwlen, padding); + + if (ntstat != ERROR_SUCCESS) { + printf("\nBCryptVerifySignature failed: %08lx %s", ntstat, NTSTATUSErrorMsg(ntstat)); + return -1; + } + + BCryptDestroyKey(hPubKey); + + // Verify with certificate + // Get certificate for key + secstat = NCryptGetProperty(hKey, NCRYPT_CERTIFICATE_PROPERTY, cert, sizeof(cert), &dwlen, 0); + + if (secstat != ERROR_SUCCESS) { + printf("\nNCryptGetProperty failed: %08lx %s\n", secstat, SECURITY_STATUSErrorMsg(secstat)); + return -1; + } + + certctx = CertCreateCertificateContext(X509_ASN_ENCODING, cert, dwlen); + + if (certctx == NULL) { + dwrc = GetLastError(); + printf("\nCertCreateCertificateContext failed: %04x %s\n", dwrc, SystemErrorMsg(dwrc)); + return -1; + } + + if (!CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, &certctx->pCertInfo->SubjectPublicKeyInfo, 0, NULL, &hPubKey)) { + dwrc = GetLastError(); + printf("\nCryptImportPublicKeyInfoEx2 failed: %04x %s\n", dwrc, SystemErrorMsg(dwrc)); + return -1; + } + + secstat = NCryptSignHash(hKey, paddingInfo, hash, hashlen, signature, sizeof(signature), &dwlen, padding); + + if (secstat != ERROR_SUCCESS) { + printf("\nNCryptSignHash failed: %08lx %s\n", secstat, SECURITY_STATUSErrorMsg(secstat)); + return -1; + } + + ntstat = BCryptVerifySignature(hPubKey, paddingInfo, hash, hashlen, signature, dwlen, padding); + + if (ntstat != ERROR_SUCCESS) { + printf("\nBCryptVerifySignature failed: %08lx %s", ntstat, NTSTATUSErrorMsg(ntstat)); + return -1; + } + + BCryptDestroyKey(hPubKey); + CertFreeCertificateContext(certctx); + + return 0; +} + + + +int testSignECDSA(NCRYPT_KEY_HANDLE hKey, LPCWSTR hashAlg ) +{ + BCRYPT_KEY_HANDLE hPubKey; + SECURITY_STATUS secstat; + PCCERT_CONTEXT certctx; +// BCRYPT_ALG_HANDLE hSignAlg; + NTSTATUS ntstat; + unsigned char cert[4096],hash[64],signature[256]; // ,pubkeyblob[1024]; + DWORD dwrc,dwlen,hashlen; + + printf(" ECDSA with %S", hashAlg); + + memset(hash, 0xA5, sizeof(hash)); + hashlen = sizeof(hash); + + if (!wcscmp(hashAlg, BCRYPT_SHA1_ALGORITHM)) { + hashlen = 20; + } else if (!wcscmp(hashAlg, BCRYPT_SHA256_ALGORITHM)) { + hashlen = 32; + } else if (!wcscmp(hashAlg, BCRYPT_SHA384_ALGORITHM)) { + hashlen = 48; + } else if (!wcscmp(hashAlg, BCRYPT_SHA512_ALGORITHM)) { + hashlen = 64; + } else if (!wcscmp(hashAlg, BCRYPT_MD5_ALGORITHM)) { + hashlen = 16; + } + +#if 0 + // Export public key from smart card + secstat = NCryptExportKey(hKey, 0, BCRYPT_ECCPUBLIC_BLOB, 0, pubkeyblob, sizeof(pubkeyblob), &dwlen, 0); + + if (secstat != ERROR_SUCCESS) { + printf("\nNCryptExportKey failed: %08lx %s\n", ntstat, SECURITY_STATUSErrorMsg(secstat)); + return -1; + } + + ntstat = BCryptOpenAlgorithmProvider(&hSignAlg, BCRYPT_ECDSA_P256_ALGORITHM, NULL, 0); + if (ntstat != ERROR_SUCCESS) { + printf("\nBCryptOpenAlgorithmProvider failed: %ld\n", ntstat); + return -1; + } + + ntstat = BCryptImportKeyPair(hSignAlg, 0, BCRYPT_ECCPUBLIC_BLOB, &hPubKey, pubkeyblob, dwlen, 0); + if (ntstat != ERROR_SUCCESS) { + printf("\nBCryptImportKeyPair failed: %ld\n", ntstat); + return -1; + } + + secstat = NCryptSignHash(hKey, NULL, hash, hashlen, signature, dwlen, &dwlen, 0); + + if (secstat != ERROR_SUCCESS) { + printf("\nNCryptSignHash failed: %08lx %s\n", ntstat, SECURITY_STATUSErrorMsg(secstat)); + return -1; + } + + ntstat = BCryptVerifySignature(hPubKey, NULL, hash, hashlen, signature, dwlen, 0); + + if (ntstat != ERROR_SUCCESS) { + printf("\nBCryptVerifySignature failed: %ld\n", ntstat); + return -1; + } + + BCryptDestroyKey(hPubKey); +#endif + + // Get certificate for key + secstat = NCryptGetProperty(hKey, NCRYPT_CERTIFICATE_PROPERTY, cert, sizeof(cert), &dwlen, 0); + + if (secstat != ERROR_SUCCESS) { + printf("\nNCryptGetProperty failed: %08lx %s\n", secstat, SECURITY_STATUSErrorMsg(secstat)); + return -1; + } + + certctx = CertCreateCertificateContext(X509_ASN_ENCODING, cert, dwlen); + + if (certctx == NULL) { + dwrc = GetLastError(); + printf("\nCertCreateCertificateContext failed: %04x %s\n", dwrc, SystemErrorMsg(dwrc)); + return -1; + } + + if (!CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, &certctx->pCertInfo->SubjectPublicKeyInfo, 0, NULL, &hPubKey)) { + dwrc = GetLastError(); + printf("\nCryptImportPublicKeyInfoEx2 failed: %04x %s\n", dwrc, SystemErrorMsg(dwrc)); + return -1; + } + + secstat = NCryptSignHash(hKey, NULL, hash, hashlen, signature, dwlen, &dwlen, 0); + + if (secstat != ERROR_SUCCESS) { + printf("\nNCryptSignHash failed: %08lx %s\n", secstat, SECURITY_STATUSErrorMsg(secstat)); + return -1; + } + + ntstat = BCryptVerifySignature(hPubKey, NULL, hash, hashlen, signature, dwlen, 0); + + if (ntstat != ERROR_SUCCESS) { + printf("\nBCryptVerifySignature failed: %08lx %s", ntstat, NTSTATUSErrorMsg(ntstat)); + return -1; + } + + BCryptDestroyKey(hPubKey); + CertFreeCertificateContext(certctx); + + return 0; +} + + + +int testDecryptRSA(NCRYPT_KEY_HANDLE hKey, DWORD padding ) +{ + BCRYPT_KEY_HANDLE hPubKey; + SECURITY_STATUS secstat; + BCRYPT_OAEP_PADDING_INFO oaeppadinfo; + BCRYPT_ALG_HANDLE hSignAlg; + void *paddingInfo; + NTSTATUS ntstat; + unsigned char secret[48],plain[256],cryptogram[256],pubkeyblob[1024]; + DWORD dwlen,secretlen; + + printf(" RSA decryption with %s padding", (padding == BCRYPT_PAD_PKCS1 ? "V1.5" : "OAEP")); + + memset(secret, 0xA5, sizeof(secret)); + secret[0] = 0x5A; + secretlen = sizeof(secret); + + if (padding == BCRYPT_PAD_PKCS1) { + paddingInfo = NULL; + } else { + memset(&oaeppadinfo, 0, sizeof(oaeppadinfo)); + oaeppadinfo.pszAlgId = BCRYPT_SHA256_ALGORITHM; + paddingInfo = &oaeppadinfo; + } + + // Export public key from smart card + secstat = NCryptExportKey(hKey, 0, BCRYPT_RSAPUBLIC_BLOB, 0, pubkeyblob, sizeof(pubkeyblob), &dwlen, 0); + + if (secstat != ERROR_SUCCESS) { + printf("\nNCryptExportKey failed: %08lx %s\n", secstat, SECURITY_STATUSErrorMsg(secstat)); + return -1; + } + + ntstat = BCryptOpenAlgorithmProvider(&hSignAlg, BCRYPT_RSA_ALGORITHM, NULL, 0); + + if (ntstat != ERROR_SUCCESS) { + printf("\nBCryptOpenAlgorithmProvider failed: %08lx %s", ntstat, NTSTATUSErrorMsg(ntstat)); + return -1; + } + + ntstat = BCryptImportKeyPair(hSignAlg, 0, BCRYPT_RSAPUBLIC_BLOB, &hPubKey, pubkeyblob, dwlen, 0); + + if (ntstat != ERROR_SUCCESS) { + printf("\nBCryptImportKeyPair failed: %08lx %s", ntstat, NTSTATUSErrorMsg(ntstat)); + return -1; + } + + ntstat = BCryptEncrypt(hPubKey, secret, secretlen, paddingInfo, NULL, 0, cryptogram, sizeof(cryptogram), &dwlen, padding); + + if (ntstat != ERROR_SUCCESS) { + printf("\nBCryptEncrypt failed: %08lx %s", ntstat, NTSTATUSErrorMsg(ntstat)); + return -1; + } + + secstat = NCryptDecrypt(hKey, cryptogram, dwlen, paddingInfo, plain, sizeof(plain), &dwlen, padding); + + if (secstat != ERROR_SUCCESS) { + printf("\nNCryptExportKey failed: %08lx %s\n", secstat, SECURITY_STATUSErrorMsg(secstat)); + return -1; + } + + BCryptDestroyKey(hPubKey); + + if ((secretlen != dwlen) || memcmp(plain, secret, secretlen)) { + printf("\nDecrypted data does not match plain data\n"); + return -1; + } + return 0; +} + + + +int cryptoTests() + +{ + NCRYPT_PROV_HANDLE hProvider; + NCRYPT_KEY_HANDLE hKey; + NCryptKeyName *keyName; + PVOID enumState = NULL; + SECURITY_STATUS secstat; + NCryptAlgorithmName *algos; + DWORD dwlen, dwi; + int rc; + + secstat = NCryptOpenStorageProvider(&hProvider, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0); + + if (secstat != ERROR_SUCCESS) { + printf("NCryptOpenStorageProvider failed: %08lx %s\n", secstat, SECURITY_STATUSErrorMsg(secstat)); + return -1; + } + + secstat = NCryptEnumAlgorithms(hProvider, NCRYPT_CIPHER_OPERATION|NCRYPT_HASH_OPERATION|NCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION|NCRYPT_SECRET_AGREEMENT_OPERATION, &dwlen, &algos, 0); + + if (secstat != ERROR_SUCCESS) { + printf("NCryptEnumAlgorithms failed: %08lx %s\n", secstat, SECURITY_STATUSErrorMsg(secstat)); + return -1; + } + + for (dwi = 0; dwi < dwlen; dwi++) { + printf("%S %lx %lx %lx\n", (algos + dwi)->pszName, (algos + dwi)->dwClass, (algos + dwi)->dwAlgOperations, (algos + dwi)->dwFlags); + } + + NCryptFreeBuffer(algos); + + while (TRUE) { + secstat = NCryptEnumKeys(hProvider, NULL, &keyName, &enumState, 0); + + if (secstat != ERROR_SUCCESS) { + break; + } + + printf("%S (%S)\n", keyName->pszName, keyName->pszAlgid); + + secstat = NCryptOpenKey(hProvider, &hKey, keyName->pszName, 0, 0); + + if (secstat != ERROR_SUCCESS) { + printf("NCryptOpenKey failed: %08lx %s\n", secstat, SECURITY_STATUSErrorMsg(secstat)); + return -1; + } + + if ((keyName->dwLegacyKeySpec == AT_KEYEXCHANGE) || (keyName->dwLegacyKeySpec == AT_SIGNATURE)) { + rc = testSignRSA(hKey, BCRYPT_PAD_PKCS1, BCRYPT_SHA1_ALGORITHM ); + printf(" - %s\n", verdict(rc == 0)); + + rc = testSignRSA(hKey, BCRYPT_PAD_PKCS1, BCRYPT_SHA256_ALGORITHM ); + printf(" - %s\n", verdict(rc == 0)); + + rc = testSignRSA(hKey, BCRYPT_PAD_PKCS1, BCRYPT_SHA384_ALGORITHM ); + printf(" - %s\n", verdict(rc == 0)); + + rc = testSignRSA(hKey, BCRYPT_PAD_PKCS1, BCRYPT_SHA512_ALGORITHM ); + printf(" - %s\n", verdict(rc == 0)); + + rc = testSignRSA(hKey, BCRYPT_PAD_PKCS1, BCRYPT_MD5_ALGORITHM ); + printf(" - %s\n", verdict(rc == 0)); + + rc = testSignRSA(hKey, BCRYPT_PAD_PSS, BCRYPT_SHA1_ALGORITHM ); + printf(" - %s\n", verdict(rc == 0)); + + rc = testSignRSA(hKey, BCRYPT_PAD_PSS, BCRYPT_SHA256_ALGORITHM ); + printf(" - %s\n", verdict(rc == 0)); + + rc = testDecryptRSA(hKey, BCRYPT_PAD_PKCS1 ); + printf(" - %s\n", verdict(rc == 0)); + + rc = testDecryptRSA(hKey, BCRYPT_PAD_OAEP ); + printf(" - %s\n", verdict(rc == 0)); + } else { + rc = testSignECDSA(hKey, BCRYPT_SHA1_ALGORITHM ); + printf(" - %s\n", verdict(rc == 0)); + + rc = testSignECDSA(hKey, BCRYPT_SHA256_ALGORITHM ); + printf(" - %s\n", verdict(rc == 0)); + + rc = testSignECDSA(hKey, BCRYPT_SHA384_ALGORITHM ); + printf(" - %s\n", verdict(rc == 0)); + + rc = testSignECDSA(hKey, BCRYPT_SHA512_ALGORITHM ); + printf(" - %s\n", verdict(rc == 0)); + + rc = testSignECDSA(hKey, BCRYPT_MD5_ALGORITHM ); + printf(" - %s\n", verdict(rc == 0)); + } + + NCryptFreeObject(hKey); + } + + NCryptFreeObject(hProvider); + + return 0; +} + + + +int listReaders() +{ + SCARDCONTEXT hSCardCtx; + DWORD cch = 0; + LPTSTR readers = NULL; + + if (SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hSCardCtx) != SCARD_S_SUCCESS) { + printf("SCardEstablishContext() failed\n"); + exit(1); + } + + if (SCardListReaders(hSCardCtx, NULL, NULL, &cch) != SCARD_S_SUCCESS) { + printf("SCardListReaders() failed\n"); + exit(1); + } + + readers = malloc(cch); + reader = readers; + + if (SCardListReaders(hSCardCtx, NULL, readers, &cch) != SCARD_S_SUCCESS) { + printf("SCardListReaders() failed\n"); + exit(1); + } + + while(*readers) { + printf("%s\n", readers); + readers += strlen(readers) + 1; + } + + SCardReleaseContext(hSCardCtx); + + return 0; +} + + + +int apiTests(char *reader) +{ + HMODULE dlhandle; + PFN_CARD_ACQUIRE_CONTEXT pcac; + CARD_FREE_SPACE_INFO cardFreeSpaceInfo; + CARD_CAPABILITIES cardCapabilities; + CARD_DATA cardData; + CARD_KEY_SIZES keySizes; + CARD_FILE_INFO fileInfo; + CONTAINER_INFO containerInfo; + PIN_INFO pinInfo; + LPSTR filenames; + PBYTE pb; + DWORD readernamelen, state, protocol, atrlen; + unsigned char atr[36], cardid[16]; + DWORD dwrc,dwlen,dwparam; + BOOL flag; + char *pinEnv = getenv("MINIDRIVER_PIN"); + + if (pinEnv) + printf("Running tests using PIN=%s/len=%zd\n", pinEnv, strlen(pinEnv)); + else + printf("Running tests without any PIN\n"); + memset(&cardData, 0, sizeof(cardData)); + cardData.dwVersion = 7; + cardData.pwszCardName = L"TestCard"; + + cardData.pfnCspAlloc = CSP_ALLOC; + cardData.pfnCspReAlloc = CSP_REALLOC; + cardData.pfnCspFree = CSP_FREE; + + if (SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &cardData.hSCardCtx) != SCARD_S_SUCCESS) { + printf("SCardEstablishContext() failed\n"); + exit(1); + } + + dlhandle = LoadLibrary("opensc-minidriver.dll"); + + if (!dlhandle) { + dwrc = GetLastError(); + printf("LoadLibrary failed %04x %s\n", dwrc, SystemErrorMsg(dwrc)); + exit(1); + } + + pcac = (PFN_CARD_ACQUIRE_CONTEXT)GetProcAddress(dlhandle, "CardAcquireContext"); + + readernamelen = 0; + atrlen = sizeof(atr); + + if (SCardConnect(cardData.hSCardCtx, reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1, &cardData.hScard, &protocol) != SCARD_S_SUCCESS) { + printf("SCardStatus(T1) failed, retry with T0\n"); + if (SCardConnect(cardData.hSCardCtx, reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, &cardData.hScard, &protocol) != SCARD_S_SUCCESS) { + printf("SCardStatus() failed\n"); + exit(1); + } + } + + if (SCardStatus(cardData.hScard, NULL, &readernamelen, &state, &protocol, atr, &atrlen) != SCARD_S_SUCCESS) { + printf("SCardStatus() failed\n"); + exit(1); + } + + cardData.pbAtr = atr; + cardData.cbAtr = atrlen; + + printf("Calling CardAcquireContext()"); + dwrc = (*pcac)(&cardData, 0); + printf(" - %x : %s\n", dwrc, verdict(dwrc == SCARD_S_SUCCESS)); + + printf("Calling CardQueryFreeSpace()"); + cardFreeSpaceInfo.dwVersion = CARD_FREE_SPACE_INFO_CURRENT_VERSION; + dwrc = (*cardData.pfnCardQueryFreeSpace)(&cardData, 0, &cardFreeSpaceInfo); + printf(" - %x : %s\n", dwrc, verdict(dwrc == SCARD_S_SUCCESS)); + + printf("Calling CardGetProperty(CP_CARD_FREE_SPACE)"); + cardFreeSpaceInfo.dwVersion = CARD_FREE_SPACE_INFO_CURRENT_VERSION; + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_FREE_SPACE, (PBYTE)&cardFreeSpaceInfo, sizeof(cardFreeSpaceInfo), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict(dwrc == SCARD_S_SUCCESS)); + + printf("Calling CardQueryCapabilities()"); + cardCapabilities.dwVersion = CARD_CAPABILITIES_CURRENT_VERSION; + dwrc = (*cardData.pfnCardQueryCapabilities)(&cardData, &cardCapabilities); + printf(" - %x : %s\n", dwrc, verdict(dwrc == SCARD_S_SUCCESS)); + + printf("Calling CardGetProperty(CP_CARD_CAPABILITIES)"); + cardCapabilities.dwVersion = CARD_CAPABILITIES_CURRENT_VERSION; + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_CAPABILITIES, (PBYTE)&cardCapabilities, sizeof(cardCapabilities), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict(dwrc == SCARD_S_SUCCESS)); + + printf("Calling CardQueryKeySizes()"); + keySizes.dwVersion = CARD_KEY_SIZES_CURRENT_VERSION; + dwrc = (*cardData.pfnCardQueryKeySizes)(&cardData, AT_SIGNATURE, 0, &keySizes); + printf(" - %x : %s\n", dwrc, verdict(dwrc == SCARD_S_SUCCESS)); + + printf("Calling CardGetProperty(CP_CARD_KEYSIZES)"); + keySizes.dwVersion = CARD_KEY_SIZES_CURRENT_VERSION; + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_KEYSIZES, (PBYTE)&keySizes, sizeof(keySizes), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict(dwrc == SCARD_S_SUCCESS)); + + printf("Calling CardGetProperty(CP_CARD_READ_ONLY)"); + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_READ_ONLY, (PBYTE)&flag, sizeof(flag), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && flag)); + + printf("Calling CardGetProperty(CP_CARD_CACHE_MODE)"); + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_CACHE_MODE, (PBYTE)&dwparam, sizeof(dwparam), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwparam == CP_CACHE_MODE_NO_CACHE))); + + printf("Calling CardGetProperty(CP_SUPPORTS_WIN_X509_ENROLLMENT)"); + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_SUPPORTS_WIN_X509_ENROLLMENT, (PBYTE)&flag, sizeof(flag), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && !flag)); + + printf("Calling CardGetProperty(CP_CARD_GUID)"); + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_GUID, (PBYTE)&cardid, sizeof(cardid), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict(dwrc == SCARD_S_SUCCESS)); + + printf("Calling CardGetProperty(CP_CARD_SERIAL_NO)"); + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_SERIAL_NO, (PBYTE)&cardid, sizeof(cardid), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict(dwrc == SCARD_S_SUCCESS)); + + printf("Calling CardGetProperty(CP_CARD_PIN_INFO)"); + pinInfo.dwVersion = PIN_INFO_CURRENT_VERSION; + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_PIN_INFO, (PBYTE)&pinInfo, sizeof(pinInfo), &dwlen, ROLE_USER); + printf(" - %x : %s\n", dwrc, verdict(dwrc == SCARD_S_SUCCESS)); + + printf("Calling CardGetProperty(CP_CARD_LIST_PINS)"); + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_LIST_PINS, (PBYTE)&dwparam, sizeof(dwparam), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (IS_PIN_SET(dwparam, ROLE_USER)))); + /* let's continue the tests only for the ROLE_USER */ + dwparam = 0; + SET_PIN(dwparam, ROLE_USER); + + printf("Calling CardGetProperty(CP_CARD_AUTHENTICATED_STATE)"); + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_AUTHENTICATED_STATE, (PBYTE)&dwparam, sizeof(dwparam), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwparam == 0))); + + printf("Calling CardGetProperty(CP_CARD_PIN_STRENGTH_VERIFY)"); + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_PIN_STRENGTH_VERIFY, (PBYTE)&dwparam, sizeof(dwparam), &dwlen, ROLE_USER); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwparam == CARD_PIN_STRENGTH_PLAINTEXT))); + + printf("Calling CardGetProperty(CP_KEY_IMPORT_SUPPORT)"); + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_KEY_IMPORT_SUPPORT, (PBYTE)&dwparam, sizeof(dwparam), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwparam == 0))); + + printf("Calling CardReadFile(cardid)"); + dwrc = (*cardData.pfnCardReadFile)(&cardData, NULL, szCARD_IDENTIFIER_FILE, 0, &pb, &dwlen); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwlen == 16))); + + printf("Calling CardReadFile(cardcf)"); + dwrc = (*cardData.pfnCardReadFile)(&cardData, NULL, szCACHE_FILE, 0, &pb, &dwlen); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwlen == 6))); + + printf("Calling CardReadFile(cardapps)"); + dwrc = (*cardData.pfnCardReadFile)(&cardData, NULL, "cardapps", 0, &pb, &dwlen); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwlen == 8))); + + printf("Calling CardReadFile(mscp/cmapfile)"); + dwrc = (*cardData.pfnCardReadFile)(&cardData, szBASE_CSP_DIR, szCONTAINER_MAP_FILE, 0, &pb, &dwlen); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwlen > 0))); + + printf("Calling CardReadFile(mscp/msroots)"); + dwrc = (*cardData.pfnCardReadFile)(&cardData, szBASE_CSP_DIR, szROOT_STORE_FILE, 0, &pb, &dwlen); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwlen > 0))); + + printf("Calling CardGetFileInfo(mscp/cmapfile)"); + fileInfo.dwVersion = CARD_FILE_INFO_CURRENT_VERSION; + dwrc = (*cardData.pfnCardGetFileInfo)(&cardData, szBASE_CSP_DIR, szCONTAINER_MAP_FILE, &fileInfo); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwlen > 0))); + + printf("Calling CardReadFile(mscp/kxc00)"); + dwrc = (*cardData.pfnCardReadFile)(&cardData, szBASE_CSP_DIR, szUSER_KEYEXCHANGE_CERT_PREFIX "00", 0, &pb, &dwlen); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwlen > 0))); + + printf("Calling CardEnumFiles(root)"); + dwrc = (*cardData.pfnCardEnumFiles)(&cardData, NULL, &filenames, &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwlen > 0))); + + printf("Calling CardGetContainerInfo(0)"); + containerInfo.dwVersion = CONTAINER_INFO_CURRENT_VERSION; + dwrc = (*cardData.pfnCardGetContainerInfo)(&cardData, 0, 0, &containerInfo); + printf(" - %x : %s\n", dwrc, verdict(dwrc == SCARD_S_SUCCESS)); + + printf("Calling CardAuthenticatePin(wszCARD_USER_USER)"); + if (pinEnv) { + dwrc = (*cardData.pfnCardAuthenticatePin)(&cardData, wszCARD_USER_USER, pinEnv, (DWORD)strlen(pinEnv), &dwparam); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwparam == -1))); + + printf("Calling CardGetProperty(CP_CARD_AUTHENTICATED_STATE)"); + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_AUTHENTICATED_STATE, (PBYTE)&dwparam, sizeof(dwparam), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwparam == 2))); + + printf("Calling CardAuthenticatePin(wszCARD_USER_USER) - Wrong PIN"); + dwrc = (*cardData.pfnCardAuthenticatePin)(&cardData, wszCARD_USER_USER, "3456", 4, &dwparam); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_W_WRONG_CHV) && (dwparam == 2))); + + printf("Calling CardGetProperty(CP_CARD_AUTHENTICATED_STATE)"); + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_AUTHENTICATED_STATE, (PBYTE)&dwparam, sizeof(dwparam), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwparam == 0))); + + printf("Calling CardAuthenticatePin(wszCARD_USER_USER)"); + dwrc = (*cardData.pfnCardAuthenticatePin)(&cardData, wszCARD_USER_USER, pinEnv, (DWORD)strlen(pinEnv), &dwparam); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwparam == -1))); + + printf("Calling CardGetProperty(CP_CARD_AUTHENTICATED_STATE)"); + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_AUTHENTICATED_STATE, (PBYTE)&dwparam, sizeof(dwparam), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwparam == 2))); + + printf("Calling CardDeAuthenticate(wszCARD_USER_USER)"); + dwrc = (*cardData.pfnCardDeauthenticate)(&cardData, wszCARD_USER_USER, 0); + printf(" - %x : %s\n", dwrc, verdict(dwrc == SCARD_S_SUCCESS)); + + printf("Calling CardGetProperty(CP_CARD_AUTHENTICATED_STATE)"); + dwrc = (*cardData.pfnCardGetProperty)(&cardData, CP_CARD_AUTHENTICATED_STATE, (PBYTE)&dwparam, sizeof(dwparam), &dwlen, 0); + printf(" - %x : %s\n", dwrc, verdict((dwrc == SCARD_S_SUCCESS) && (dwparam == 0))); + } else { + printf(" - skip: missing set MINIDRIVER_PIN=abcd\n"); + } + + printf("Calling CardDeleteContext()"); + dwrc = (*cardData.pfnCardDeleteContext)(&cardData); + printf(" - %x : %s\n", dwrc, verdict(dwrc == SCARD_S_SUCCESS)); + + SCardReleaseContext(cardData.hSCardCtx); + + return 0; +} + + + +int main(int argc, char *argv[]) + +{ + if (argc == 1) { + printf("Usage: opensc-minidriver-test [-l] [-r ] [-a] [-c]\n"); + printf(" -l list readers\n"); + printf(" -r define readers\n"); + printf(" -a run API tests\n"); + printf(" -c run crypto tests\n"); + exit(1); + } + + argc--; + argv++; + + while (argc--) { + if (!strcmp(*argv, "-l")) { + listReaders(); + } else if (!strcmp(*argv, "-r")) { + if (argc == 0) { + printf("Reader name missing in -r parameter\n"); + exit(1); + } + argv++; + argc--; + reader = *argv; + } else if (!strcmp(*argv, "-a")) { + if (reader == NULL) { + printf("Need a reader name set with -r or use -l to select first reader\n"); + exit(1); + } + apiTests(reader); + } else if (!strcmp(*argv, "-c")) { + cryptoTests(); + } else { + printf("Unknown parameter %s\n", *argv); + } + argv++; + } + + printf("Unit test finished.\n"); + printf("%d tests performed.\n", testscompleted); + printf("%d tests failed.\n", testsfailed); + + exit(testsfailed ? 1 : 0); +} diff --git a/src/tests/p11test/Makefile.am b/src/tests/p11test/Makefile.am new file mode 100644 index 0000000000..f5300b22df --- /dev/null +++ b/src/tests/p11test/Makefile.am @@ -0,0 +1,42 @@ +include $(top_srcdir)/win32/ltrc.inc + +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +EXTRA_DIST = Makefile.mak + +if ENABLE_OPENSSL +if ENABLE_CMOCKA +noinst_PROGRAMS = p11test +endif +endif + +noinst_HEADERS = p11test_loader.h p11test_case_common.h \ + p11test_case_readonly.h p11test_case_multipart.h \ + p11test_case_mechs.h p11test_case_ec_sign.h \ + p11test_case_usage.h p11test_case_wait.h \ + p11test_case_pss_oaep.h p11test_helpers.h \ + p11test_case_ec_derive.h p11test_case_interface.h \ + p11test_case_wrap.h p11test_case_secret.h \ + p11test_common.h + +AM_CPPFLAGS = -I$(top_srcdir)/src + +p11test_SOURCES = p11test.c p11test_loader.c \ + p11test_case_common.c \ + p11test_case_readonly.c \ + p11test_case_multipart.c \ + p11test_case_mechs.c \ + p11test_case_ec_sign.c \ + p11test_case_ec_derive.c \ + p11test_case_usage.c \ + p11test_case_wait.c \ + p11test_case_pss_oaep.c \ + p11test_case_interface.c \ + p11test_case_wrap.c \ + p11test_case_secret.c \ + p11test_helpers.c +p11test_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(CMOCKA_CFLAGS) +p11test_LDADD = $(OPTIONAL_OPENSSL_LIBS) $(CMOCKA_LIBS) $(LDL_LIBS) + +if WIN32 +p11test_SOURCES += $(top_builddir)/win32/versioninfo.rc +endif diff --git a/src/tests/p11test/Makefile.mak b/src/tests/p11test/Makefile.mak new file mode 100644 index 0000000000..daa0de3782 --- /dev/null +++ b/src/tests/p11test/Makefile.mak @@ -0,0 +1,25 @@ +TOPDIR = ..\..\.. + +TARGETS = p11test.exe + +OBJECTS = p11test_loader.obj \ + p11test_case_common.obj \ + p11test_case_readonly.obj \ + p11test_case_multipart.obj \ + p11test_case_mechs.obj \ + p11test_case_ec_sign.obj \ + p11test_case_usage.obj \ + p11test_case_wait.obj \ + p11test_case_pss_oaep.obj \ + p11test_helpers.obj \ + $(TOPDIR)\win32\versioninfo.res + +all: $(TARGETS) + +!INCLUDE $(TOPDIR)\win32\Make.rules.mak + +$(TARGETS): $(OBJECTS) $(LIBS) + +.c.exe: + cl $(COPTS) /c $< + link $(LINKFLAGS) /out:$@ $*.obj $(OBJECTS) $(LIBS) diff --git a/src/tests/p11test/README.md b/src/tests/p11test/README.md new file mode 100644 index 0000000000..9c44fe190a --- /dev/null +++ b/src/tests/p11test/README.md @@ -0,0 +1,56 @@ +# Non-destructive PKCS#11 test suite (not only for readonly cards) + +## What are the dependencies? + +In addition to the dependencies needed by OpenSC, the test suite is +using [`cmocka`](https://cmocka.org/) unit testing framework +(`libcmocka-devel` package in Fedora/EPEL). + +## How to use? + +Build OpenSC from source: + + git clone git@github.com:OpenSC/OpenSC.git + cd OpenSC + ./bootstrap + ./configure + make -j4 + +Plug in the card/reader, change to test directory and run the test: + + cd src/tests/p11test + ./p11test -p 123456 + +It will run all tests on the first card found in PKCS#11 API +with pin `123456` and using just built OpenSC shared library from master. + +### I have more slots with different cards. + +Slot can be selected using `-s` switch on command-line. + + ./p11test -s 4 + +Slot numbers can be obtained using from `pkcs11-tool -L` (note that different +libraries might have different numbers for the slots). + +### I want to test different pkcs11 library + +You can specify different library or build from different branch +on command-line: + + ./p11test -m /usr/lib64/pkcs11/libcoolkeypk11.so + +or to debug PKCS#11 calls using `/usr/lib64/pkcs11-spy.so`: + + export PKCS11SPY="../pkcs11/.libs/opensc-pkcs11.so" + ./p11test -m ../pkcs11/.libs/pkcs11-spy.so + +You can run the test suite also on the soft tokens. The testbench for +`softhsm` and `opencryptoki` is available in the script `runtest.sh`. + +TODO: + + * Test `CKM_ECDSA_DERIVE` mechanism(s) + * Read pin from environment variable? + * Keygen write tests (optional) + * Reflect cmocka dependency in the configure diff --git a/src/tests/p11test/cert.cfg b/src/tests/p11test/cert.cfg new file mode 100644 index 0000000000..409aa93012 --- /dev/null +++ b/src/tests/p11test/cert.cfg @@ -0,0 +1,6 @@ +organization = "OpenSC" +expiration_days = 365 +email = "none@example.org" +signing_key +encryption_key + diff --git a/src/tests/p11test/epass2003_ref.json b/src/tests/p11test/epass2003_ref.json new file mode 100644 index 0000000000..356db03d81 --- /dev/null +++ b/src/tests/p11test/epass2003_ref.json @@ -0,0 +1,1745 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "MD5", + "0", + "0", + "CKF_DIGEST" + ], + [ + "RIPEMD160", + "0", + "0", + "CKF_DIGEST" + ], + [ + "GOSTR3411", + "0", + "0", + "CKF_DIGEST" + ], + [ + "ECDSA", + "256", + "256", + "CKF_HW,CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA224", + "256", + "256", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA384", + "256", + "256", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA512", + "256", + "256", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA1", + "256", + "256", + "CKF_HW,CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA256", + "256", + "256", + "CKF_HW,CKF_SIGN,CKF_VERIFY" + ], + [ + "EC_KEY_PAIR_GEN", + "256", + "256", + "CKF_HW,CKF_GENERATE_KEY_PAIR" + ], + [ + "RSA_X_509", + "512", + "2048", + "CKF_HW,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS", + "512", + "2048", + "CKF_HW,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "MD5_RSA_PKCS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RIPEMD160_RSA_PKCS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_PSS", + "512", + "2048", + "CKF_HW,CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS_PSS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS_PSS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS_PSS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS_PSS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS_PSS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_OAEP", + "512", + "2048", + "CKF_HW,CKF_DECRYPT" + ], + [ + "RSA_PKCS_KEY_PAIR_GEN", + "512", + "2048", + "CKF_HW,CKF_GENERATE_KEY_PAIR" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_X_509", + "YES", + "" + ], + [ + "01", + "RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "RIPEMD160_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "RSA_X_509", + "", + "YES" + ], + [ + "02", + "RSA_PKCS", + "", + "YES" + ], + [ + "03", + "ECDSA", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA1", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA256", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "multipart_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "MULTIPART SIGN&VERIFY WORKS" + ], + [ + "01", + "RSA_X_509", + "YES" + ], + [ + "01", + "RSA_PKCS", + "YES" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES" + ], + [ + "01", + "RIPEMD160_RSA_PKCS", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "ec_sign_size_test", + "fail_reason": "Some signatures were not verified successfully. Please review the log", + "result": "fail" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ], + [ + "01", + "RSA2k key", + "RSA", + "2048", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "02", + "RSA2k encryption key", + "RSA", + "2048", + "", + "", + "", + "YES", + "YES", + "YES", + "YES", + "", + "", + "" + ], + [ + "03", + "Private Key", + "EC", + "256", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "data": [ + [ + "KEY ID", + "MECHANISM", + "HASH", + "MGF", + "SALT", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA512", + "0", + "", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/src/tests/p11test/isoapplet_ref_v0.json b/src/tests/p11test/isoapplet_ref_v0.json new file mode 100644 index 0000000000..784cafbe0a --- /dev/null +++ b/src/tests/p11test/isoapplet_ref_v0.json @@ -0,0 +1,370 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "MD5", + "0", + "0", + "CKF_DIGEST" + ], + [ + "RIPEMD160", + "0", + "0", + "CKF_DIGEST" + ], + [ + "GOSTR3411", + "0", + "0", + "CKF_DIGEST" + ], + [ + "ECDSA_SHA1", + "192", + "384", + "CKF_HW,CKF_SIGN,CKF_VERIFY,CKF_EC_F_P,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "EC_KEY_PAIR_GEN", + "192", + "384", + "CKF_HW,CKF_GENERATE_KEY_PAIR,CKF_EC_F_P,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "RSA_PKCS", + "2048", + "2048", + "CKF_HW,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "MD5_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RIPEMD160_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_KEY_PAIR_GEN", + "2048", + "2048", + "CKF_HW,CKF_GENERATE_KEY_PAIR" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "RIPEMD160_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "RSA_PKCS", + "", + "YES" + ], + [ + "03", + "ECDSA_SHA1", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "multipart_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "MULTIPART SIGN&VERIFY WORKS" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "01", + "RIPEMD160_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "ec_sign_size_test", + "result": "pass" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "ENCAPSULATE", + "DECAPSULATE", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ], + [ + "01", + "Private Key", + "RSA", + "2048", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "02", + "Private Key", + "RSA", + "2048", + "", + "", + "", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "03", + "Private Key", + "EC", + "256", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "result": "unknown" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/src/tests/p11test/isoapplet_ref_v1.json b/src/tests/p11test/isoapplet_ref_v1.json new file mode 100644 index 0000000000..c397ced33c --- /dev/null +++ b/src/tests/p11test/isoapplet_ref_v1.json @@ -0,0 +1,632 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "MD5", + "0", + "0", + "CKF_DIGEST" + ], + [ + "RIPEMD160", + "0", + "0", + "CKF_DIGEST" + ], + [ + "GOSTR3411", + "0", + "0", + "CKF_DIGEST" + ], + [ + "ECDSA", + "192", + "384", + "CKF_HW,CKF_SIGN,CKF_VERIFY,CKF_EC_F_P,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "ECDSA_SHA1", + "192", + "384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA224", + "192", + "384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA256", + "192", + "384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA384", + "192", + "384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA512", + "192", + "384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "EC_KEY_PAIR_GEN", + "192", + "384", + "CKF_HW,CKF_GENERATE_KEY_PAIR,CKF_EC_F_P,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "RSA_PKCS", + "2048", + "4096", + "CKF_HW,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS", + "2048", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS", + "2048", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS", + "2048", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS", + "2048", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS", + "2048", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "MD5_RSA_PKCS", + "2048", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RIPEMD160_RSA_PKCS", + "2048", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_PSS", + "2048", + "4096", + "CKF_HW,CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS_PSS", + "2048", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS_PSS", + "2048", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS_PSS", + "2048", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS_PSS", + "2048", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS_PSS", + "2048", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_KEY_PAIR_GEN", + "2048", + "4096", + "CKF_HW,CKF_GENERATE_KEY_PAIR" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "RIPEMD160_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "RSA_PKCS", + "", + "YES" + ], + [ + "03", + "ECDSA", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA1", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA256", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA384", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA512", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "multipart_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "MULTIPART SIGN&VERIFY WORKS" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "01", + "RIPEMD160_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "ec_sign_size_test", + "result": "pass" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "ENCAPSULATE", + "DECAPSULATE", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ], + [ + "01", + "Private Key", + "RSA", + "2048", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "02", + "Private Key", + "RSA", + "2048", + "", + "", + "", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "03", + "Private Key", + "EC", + "256", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "data": [ + [ + "KEY ID", + "MECHANISM", + "HASH", + "MGF", + "SALT", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/src/tests/p11test/openpgp_s0_ref.json b/src/tests/p11test/openpgp_s0_ref.json new file mode 100644 index 0000000000..3699756188 --- /dev/null +++ b/src/tests/p11test/openpgp_s0_ref.json @@ -0,0 +1,334 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "MD5", + "0", + "0", + "CKF_DIGEST" + ], + [ + "RIPEMD160", + "0", + "0", + "CKF_DIGEST" + ], + [ + "GOSTR3411", + "0", + "0", + "CKF_DIGEST" + ], + [ + "RSA_PKCS", + "2048", + "2048", + "CKF_HW,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "MD5_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RIPEMD160_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_KEY_PAIR_GEN", + "2048", + "2048", + "CKF_HW,CKF_GENERATE_KEY_PAIR" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "02", + "RSA_PKCS", + "", + "YES" + ], + [ + "03", + "RSA_PKCS", + "YES", + "" + ], + [ + "03", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "03", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "03", + "RIPEMD160_RSA_PKCS", + "YES", + "" + ], + [ + "03", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "03", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "03", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "03", + "SHA224_RSA_PKCS", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "multipart_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "MULTIPART SIGN&VERIFY WORKS" + ], + [ + "03", + "MD5_RSA_PKCS", + "YES" + ], + [ + "03", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "03", + "RIPEMD160_RSA_PKCS", + "YES" + ], + [ + "03", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "03", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "03", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "03", + "SHA224_RSA_PKCS", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "ec_sign_size_test", + "result": "skip" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "ENCAPSULATE", + "DECAPSULATE", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ], + [ + "02", + "Encryption key", + "RSA", + "2048", + "", + "", + "", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "03", + "Authentication key", + "RSA", + "2048", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "result": "unknown" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/src/tests/p11test/openpgp_s1_ref.json b/src/tests/p11test/openpgp_s1_ref.json new file mode 100644 index 0000000000..204a1ce4a5 --- /dev/null +++ b/src/tests/p11test/openpgp_s1_ref.json @@ -0,0 +1,310 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "MD5", + "0", + "0", + "CKF_DIGEST" + ], + [ + "RIPEMD160", + "0", + "0", + "CKF_DIGEST" + ], + [ + "GOSTR3411", + "0", + "0", + "CKF_DIGEST" + ], + [ + "RSA_PKCS", + "2048", + "2048", + "CKF_HW,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "MD5_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RIPEMD160_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_KEY_PAIR_GEN", + "2048", + "2048", + "CKF_HW,CKF_GENERATE_KEY_PAIR" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS", + "YES", + "" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "RIPEMD160_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "multipart_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "MULTIPART SIGN&VERIFY WORKS" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "01", + "RIPEMD160_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "ec_sign_size_test", + "result": "skip" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "ENCAPSULATE", + "DECAPSULATE", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ], + [ + "01", + "Signature key", + "RSA", + "2048", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "result": "unknown" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/src/tests/p11test/oseid_ref.json b/src/tests/p11test/oseid_ref.json new file mode 100644 index 0000000000..d3c473068e --- /dev/null +++ b/src/tests/p11test/oseid_ref.json @@ -0,0 +1,1919 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "MD5", + "0", + "0", + "CKF_DIGEST" + ], + [ + "RIPEMD160", + "0", + "0", + "CKF_DIGEST" + ], + [ + "GOSTR3411", + "0", + "0", + "CKF_DIGEST" + ], + [ + "ECDSA", + "192", + "521", + "CKF_HW,CKF_SIGN,CKF_VERIFY,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "ECDSA_SHA1", + "192", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA224", + "192", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA256", + "192", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA384", + "192", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA512", + "192", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDH1_COFACTOR_DERIVE", + "192", + "521", + "CKF_HW,CKF_DERIVE,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "ECDH1_DERIVE", + "192", + "521", + "CKF_HW,CKF_DERIVE,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "EC_KEY_PAIR_GEN", + "192", + "521", + "CKF_HW,CKF_GENERATE_KEY_PAIR,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "RSA_X_509", + "512", + "2048", + "CKF_HW,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY,CKF_WRAP,CKF_UNWRAP" + ], + [ + "RSA_PKCS", + "512", + "2048", + "CKF_HW,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY,CKF_WRAP,CKF_UNWRAP" + ], + [ + "SHA1_RSA_PKCS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "MD5_RSA_PKCS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RIPEMD160_RSA_PKCS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_PSS", + "512", + "2048", + "CKF_HW,CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS_PSS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS_PSS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS_PSS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS_PSS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS_PSS", + "512", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_OAEP", + "512", + "2048", + "CKF_HW,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "RSA_PKCS_KEY_PAIR_GEN", + "512", + "2048", + "CKF_HW,CKF_GENERATE_KEY_PAIR" + ], + [ + "AES_ECB", + "128", + "256", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_CBC", + "128", + "256", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_CBC_PAD", + "128", + "256", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS", + "YES", + "" + ], + [ + "01", + "RSA_X_509", + "YES", + "" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "RIPEMD160_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "RSA_PKCS", + "", + "YES" + ], + [ + "02", + "RSA_X_509", + "", + "YES" + ], + [ + "03", + "ECDSA", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA1", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA256", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA384", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA512", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "multipart_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "MULTIPART SIGN&VERIFY WORKS" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "01", + "RIPEMD160_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "ec_sign_size_test", + "result": "pass" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "ENCAPSULATE", + "DECAPSULATE", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ], + [ + "01", + "RSA2k key", + "RSA", + "2048", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "02", + "RSA2k encryption key", + "RSA", + "2048", + "", + "", + "", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "03", + "Private Key", + "EC", + "256", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "data": [ + [ + "KEY ID", + "MECHANISM", + "HASH", + "MGF", + "SALT", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA512", + "0", + "", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/src/tests/p11test/p11test.c b/src/tests/p11test/p11test.c new file mode 100644 index 0000000000..777f6b6c3a --- /dev/null +++ b/src/tests/p11test/p11test.c @@ -0,0 +1,157 @@ +/* + * p11test.c: Test suite for PKCS#11 API + * + * Copyright (C) 2016 Martin Strhársky + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include +#include "p11test_helpers.h" + +#include "p11test_case_readonly.h" +#include "p11test_case_multipart.h" +#include "p11test_case_ec_sign.h" +#include "p11test_case_ec_derive.h" +#include "p11test_case_usage.h" +#include "p11test_case_mechs.h" +#include "p11test_case_wait.h" +#include "p11test_case_pss_oaep.h" +#include "p11test_case_interface.h" +#include "p11test_case_wrap.h" +#include "p11test_case_secret.h" + +#define DEFAULT_P11LIB "../../pkcs11/.libs/opensc-pkcs11.so" + +/* Global variable keeping information about token we are using */ +token_info_t token; +int debug_flag = 0; + +void display_usage() { + fprintf(stdout, + " Usage:\n" + " ./p11test [-m module_path] [-s slot_id] [-p pin]\n" + " -m module_path Path to tested module (e.g. /usr/lib64/opensc-pkcs11.so)\n" + " Default is "DEFAULT_P11LIB"\n" + " -p pin Application PIN\n" + " -s slot_id Slot ID with the card\n" + " -i Wait for the card before running the test (interactive)\n" + " -o File to write a log in JSON\n" + " -v Verbose log output\n" + " -h This help\n" + "\n"); +} + +int main(int argc, char** argv) { + signed char command; + const struct CMUnitTest readonly_tests_without_initialization[] = { + /* Test card events on slot */ + cmocka_unit_test_setup_teardown(wait_test, + token_initialize, token_cleanup), + + /* Check all the mechanisms provided by the token */ + cmocka_unit_test_setup_teardown(supported_mechanisms_test, + token_setup, token_cleanup), + + /* Check the PKCS #11 3.0 Interface to access new functions */ + cmocka_unit_test(interface_test), + + /* Complex readonly test of all objects on the card */ + cmocka_unit_test_setup_teardown(readonly_tests, + user_login_setup, after_test_cleanup), + + /* Multipart signatures and encryption */ + cmocka_unit_test_setup_teardown(multipart_tests, + user_login_setup, after_test_cleanup), + + /* Regression test Sign&Verify with various data lengths */ + cmocka_unit_test_setup_teardown(ec_sign_size_test, + user_login_setup, after_test_cleanup), + + /* Verify that the Usage flags on the objects are sane */ + cmocka_unit_test_setup_teardown(usage_test, + user_login_setup, after_test_cleanup), + + /* Verify that RSA-PSS and RSA-OAEP functions if supported */ + cmocka_unit_test_setup_teardown(pss_oaep_test, + user_login_setup, after_test_cleanup), + + /* Verify that ECDH key derivation works */ + cmocka_unit_test_setup_teardown(derive_tests, + user_login_setup, after_test_cleanup), + + /* Verify that basic operations with secret keys work */ + cmocka_unit_test_setup_teardown(secret_tests, + user_login_setup, after_test_cleanup), + + /* Verify that key wrapping and unwrapping works */ + cmocka_unit_test_setup_teardown(wrap_tests, + user_login_setup, after_test_cleanup), + }; + + /* Make sure it is initialized to sensible values */ + memset(&token, 0, sizeof(token_info_t)); + token.slot_id = (unsigned long) -1; + token.verify_support = 1; + token.encrypt_support = 1; + + while ((command = getopt(argc, argv, "?hm:s:p:io:v")) != -1) { + switch (command) { + case 'o': + token.log.outfile = strdup(optarg); + break; + case 'm': + token.library_path = strdup(optarg); + break; + case 's': + token.slot_id = atol(optarg); + break; + case 'p': + token.pin = (CK_UTF8CHAR*) strdup(optarg); + token.pin_length = strlen(optarg); + break; + case 'i': + token.interactive = 1; + break; + case 'h': + case '?': + display_usage(); + return 0; + case 'v': + debug_flag = 1; + break; + default: + break; + } + } + + if (token.library_path == NULL) { + debug_print("Falling back to the default library " DEFAULT_P11LIB); + token.library_path = strdup(DEFAULT_P11LIB); + } + + if (token.pin == NULL || token.pin_length == 0) { + printf("No PIN specified. Please, specify it on command-line using -p switch\n"); + return -1; + } + + debug_print("Card info:\n\tPIN %s\n\tPIN LENGTH %zu\n\t", + token.pin, token.pin_length); + + return cmocka_run_group_tests(readonly_tests_without_initialization, + group_setup, group_teardown); +} diff --git a/src/tests/p11test/p11test.supp b/src/tests/p11test/p11test.supp new file mode 100644 index 0000000000..2d4473c30a --- /dev/null +++ b/src/tests/p11test/p11test.supp @@ -0,0 +1,30 @@ +{ + Suppress pcsc_detect_readers() + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + obj:* + obj:* + fun:pcsc_detect_readers + fun:sc_ctx_detect_readers + fun:sc_context_create + fun:C_Initialize +} +{ + Suppress MessageSend() errors + Memcheck:Param + socketcall.sendto(msg) + fun:send + fun:MessageSend + fun:MessageSendWithHeader + fun:SCardConnect + fun:pcsc_detect_readers + fun:sc_ctx_detect_readers + fun:sc_context_create + fun:C_Initialize + fun:load_pkcs11_module + fun:group_setup + obj:/usr/lib64/libcmocka.so.0.3.1 + fun:_cmocka_run_group_tests +} + diff --git a/src/tests/p11test/p11test_case_common.c b/src/tests/p11test/p11test_case_common.c new file mode 100644 index 0000000000..f571cd1598 --- /dev/null +++ b/src/tests/p11test/p11test_case_common.c @@ -0,0 +1,1823 @@ +/* + * p11test_case_common.c: Functions shared between test cases. + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_common.h" +#include "../../libopensc/sc-ossl-compat.h" + +/* Unsigned long can be up to 16 B long. We print also leading "0x" and we need trailing NULL byte */ +#define FLAG_BUFFER_LEN 19 +char name_buffer[FLAG_BUFFER_LEN]; +char flag_buffer[FLAG_BUFFER_LEN]; + +void test_certs_init(test_certs_t *objects) +{ + objects->alloc_count = 0; + objects->count = 0; + objects->data = NULL; +} + +/** + * If the object enforces re-authentication, do it now. + */ +void always_authenticate(test_cert_t *o, token_info_t *info) +{ + CK_RV rv; + if (!o->always_auth) { + return; + } + + rv = info->function_pointer->C_Login(info->session_handle, + CKU_CONTEXT_SPECIFIC, info->pin, info->pin_length); + if (rv != CKR_OK) { + fail_msg(" [ SKIP %s ] Re-authentication failed", o->id_str); + exit(1); + } +} + +/** + * Allocate new place for next certificate to store in the list + * and return pointer to this object + */ +test_cert_t * +add_object(test_certs_t *objects, CK_ATTRIBUTE key_id, CK_ATTRIBUTE label) +{ + test_cert_t *o = NULL; + unsigned int i; + + if (objects->count + 1 > objects->alloc_count) { + objects->alloc_count += 8; + objects->data = realloc(objects->data, objects->alloc_count * sizeof(test_cert_t)); + if (objects->data == NULL) + return NULL; + } + + /* SoftHSM is stupid returning objects in random order. Sort here by key ID + * to provide deterministic JSON output */ + for (i = 0; i < objects->count; i++) { + size_t len = MIN(objects->data[i].key_id_size, key_id.ulValueLen); + if (memcmp(key_id.pValue, objects->data[i].key_id, len) <= 0) { + break; + } + } + if (i < objects->count) { + memmove(&objects->data[i + 1], &objects->data[i], (objects->count - i) * sizeof(test_cert_t)); + } + objects->count = objects->count + 1; + + o = &(objects->data[i]); + o->private_handle = CK_INVALID_HANDLE; + o->public_handle = CK_INVALID_HANDLE; + o->always_auth = 0; + o->extractable = 0; + o->bits = 0; + o->verify_public = 0; + o->num_mechs = 0; + o->type = -1; + o->sign = 0; + o->verify = 0; + o->decrypt = 0; + o->encrypt = 0; + o->encapsulate = 0; + o->decapsulate = 0; + o->wrap = 0; + o->unwrap = 0; + o->derive_priv = 0; + o->derive_pub = 0; + o->key_type = -1; + o->x509 = NULL; /* The "reuse" capability of d2i_X509() is strongly discouraged */ + o->key = NULL; + o->value = NULL; + + /* Store the passed CKA_ID and CKA_LABEL */ + o->key_id = malloc(key_id.ulValueLen); + memcpy(o->key_id, key_id.pValue, key_id.ulValueLen); + o->key_id_size = key_id.ulValueLen; + o->id_str = convert_byte_string(o->key_id, o->key_id_size); + o->label = malloc(label.ulValueLen + 1); + strncpy(o->label, label.pValue, label.ulValueLen); + o->label[label.ulValueLen] = '\0'; + + return o; +} + +/* + * Search for certificate in the list by ID and return pointer to it + */ +test_cert_t * search_certificate(test_certs_t *objects, CK_ATTRIBUTE *id) +{ + unsigned int i = 0; + + while (i < objects->count && (objects->data[i].key_id_size != id->ulValueLen || + memcmp(objects->data[i].key_id, id->pValue, id->ulValueLen) != 0)) + i++; + + if (i == objects->count) + return NULL; + + return &(objects->data[i]); +} + +static void +add_supported_mechs(test_cert_t *o) +{ + size_t i; + + switch (o->type) { + case EVP_PKEY_RSA: + if (token.num_rsa_mechs > 0) { + /* Get supported mechanisms by token */ + o->num_mechs = 0; + for (i = 0; i < token.num_rsa_mechs; i++) { + if (FIPS_mode()) { + /* Skip algorithms that are not supported in FIPS mode */ + if (token.rsa_mechs[i].mech == CKM_RSA_PKCS || + token.rsa_mechs[i].mech == CKM_RSA_X_509 || + token.rsa_mechs[i].mech == CKM_MD5_RSA_PKCS) + continue; + } + o->mechs[o->num_mechs].mech = token.rsa_mechs[i].mech; + o->mechs[o->num_mechs].params = token.rsa_mechs[i].params; + o->mechs[o->num_mechs].params_len = token.rsa_mechs[i].params_len; + o->mechs[o->num_mechs].result_flags = 0; + o->mechs[o->num_mechs].usage_flags = token.rsa_mechs[i].usage_flags; + o->num_mechs++; + } + } else if (!FIPS_mode()) { + /* Use the default list */ + o->num_mechs = 1; + o->mechs[0].mech = CKM_RSA_PKCS; + o->mechs[0].params = NULL; + o->mechs[0].params_len = 0; + o->mechs[0].result_flags = 0; + o->mechs[0].usage_flags = CKF_SIGN | CKF_VERIFY + | CKF_ENCRYPT | CKF_DECRYPT; + } + break; + case EVP_PKEY_EC: + if (token.num_ec_mechs > 0) { + o->num_mechs = token.num_ec_mechs; + for (i = 0; i < token.num_ec_mechs; i++) { + o->mechs[i].mech = token.ec_mechs[i].mech; + o->mechs[i].params = token.ec_mechs[i].params; + o->mechs[i].params_len = token.ec_mechs[i].params_len; + o->mechs[i].result_flags = 0; + o->mechs[i].usage_flags = + token.ec_mechs[i].usage_flags; + } + } else { + /* Use the default list */ + o->num_mechs = 1; + o->mechs[0].mech = CKM_ECDSA; + o->mechs[0].params = NULL; + o->mechs[0].params_len = 0; + o->mechs[0].result_flags = 0; + o->mechs[0].usage_flags = CKF_SIGN | CKF_VERIFY; + } + break; +#ifdef EVP_PKEY_ED25519 + case EVP_PKEY_ED25519: +#endif +#ifdef EVP_PKEY_ED448 + case EVP_PKEY_ED448: +#endif +#if defined(EVP_PKEY_ED25519) || defined(EVP_PKEY_ED448) + if (token.num_ed_mechs > 0) { + o->num_mechs = token.num_ed_mechs; + for (i = 0; i < token.num_ed_mechs; i++) { + o->mechs[i].mech = token.ed_mechs[i].mech; + o->mechs[i].params = token.ed_mechs[i].params; + o->mechs[i].params_len = token.ed_mechs[i].params_len; + o->mechs[i].result_flags = 0; + o->mechs[i].usage_flags = + token.ed_mechs[i].usage_flags; + } + } else { + /* Use the default list */ + o->num_mechs = 1; + o->mechs[0].mech = CKM_EDDSA; + o->mechs[0].params = NULL; + o->mechs[0].params_len = 0; + o->mechs[0].result_flags = 0; + o->mechs[0].usage_flags = CKF_SIGN | CKF_VERIFY; + } + break; +#endif /* defined(EVP_PKEY_ED25519) || defined(EVP_PKEY_ED448) */ +#ifdef EVP_PKEY_X25519 + case EVP_PKEY_X25519: +#endif +#ifdef EVP_PKEY_X448 + case EVP_PKEY_X448: +#endif +#if defined(EVP_PKEY_X25519) || defined(EVP_PKEY_X448) + if (token.num_montgomery_mechs > 0) { + o->num_mechs = token.num_montgomery_mechs; + for (i = 0; i < token.num_montgomery_mechs; i++) { + o->mechs[i].mech = token.montgomery_mechs[i].mech; + o->mechs[i].params = token.montgomery_mechs[i].params; + o->mechs[i].params_len = token.montgomery_mechs[i].params_len; + o->mechs[i].result_flags = 0; + o->mechs[i].usage_flags = + token.montgomery_mechs[i].usage_flags; + } + } else { + /* Use the default list */ + o->num_mechs = 1; + o->mechs[0].mech = CKM_ECDH1_DERIVE; + o->mechs[0].params = NULL; + o->mechs[0].params_len = 0; + o->mechs[0].result_flags = 0; + o->mechs[0].usage_flags = CKF_DERIVE; + } + break; +#endif /* defined(EVP_PKEY_X25519) || defined(EVP_PKEY_X448) */ +#ifdef EVP_PKEY_ML_DSA_44 + case EVP_PKEY_ML_DSA_44: + case EVP_PKEY_ML_DSA_65: + case EVP_PKEY_ML_DSA_87: + if (token.num_ml_dsa_mechs > 0) { + o->num_mechs = token.num_ml_dsa_mechs; + for (i = 0; i < token.num_ml_dsa_mechs; i++) { + o->mechs[i].mech = token.ml_dsa_mechs[i].mech; + o->mechs[i].params = token.ml_dsa_mechs[i].params; + o->mechs[i].params_len = token.ml_dsa_mechs[i].params_len; + o->mechs[i].result_flags = 0; + o->mechs[i].usage_flags = token.ml_dsa_mechs[i].usage_flags; + } + } else { + /* Use the default list */ + o->num_mechs = 1; + o->mechs[0].mech = CKM_ML_DSA; + o->mechs[0].params = NULL; + o->mechs[0].params_len = 0; + o->mechs[0].result_flags = 0; + o->mechs[0].usage_flags = CKF_SIGN | CKF_VERIFY; + } + break; +#endif /* EVP_PKEY_ML_DSA_44 */ +#ifdef EVP_PKEY_ML_KEM_512 + case EVP_PKEY_ML_KEM_512: + case EVP_PKEY_ML_KEM_768: + case EVP_PKEY_ML_KEM_1024: + if (token.num_ml_kem_mechs > 0) { + o->num_mechs = token.num_ml_kem_mechs; + for (i = 0; i < token.num_ml_kem_mechs; i++) { + o->mechs[i].mech = token.ml_kem_mechs[i].mech; + o->mechs[i].params = token.ml_kem_mechs[i].params; + o->mechs[i].params_len = token.ml_kem_mechs[i].params_len; + o->mechs[i].result_flags = 0; + o->mechs[i].usage_flags = token.ml_kem_mechs[i].usage_flags; + } + } else { + /* Use the default list */ + o->num_mechs = 1; + o->mechs[0].mech = CKM_ML_KEM; + o->mechs[0].params = NULL; + o->mechs[0].params_len = 0; + o->mechs[0].result_flags = 0; + o->mechs[0].usage_flags = CKF_ENCAPSULATE | CKF_DECAPSULATE; + } + break; +#endif /* EVP_PKEY_ML_KEM_512 */ +#ifdef EVP_PKEY_SLH_DSA_SHA2_128S + case EVP_PKEY_SLH_DSA_SHA2_128S: + case EVP_PKEY_SLH_DSA_SHAKE_128S: + case EVP_PKEY_SLH_DSA_SHA2_128F: + case EVP_PKEY_SLH_DSA_SHAKE_128F: + case EVP_PKEY_SLH_DSA_SHA2_192S: + case EVP_PKEY_SLH_DSA_SHAKE_192S: + case EVP_PKEY_SLH_DSA_SHA2_192F: + case EVP_PKEY_SLH_DSA_SHAKE_192F: + case EVP_PKEY_SLH_DSA_SHA2_256S: + case EVP_PKEY_SLH_DSA_SHAKE_256S: + case EVP_PKEY_SLH_DSA_SHA2_256F: + case EVP_PKEY_SLH_DSA_SHAKE_256F: + if (token.num_slh_dsa_mechs > 0) { + o->num_mechs = token.num_slh_dsa_mechs; + for (i = 0; i < token.num_slh_dsa_mechs; i++) { + o->mechs[i].mech = token.slh_dsa_mechs[i].mech; + o->mechs[i].params = token.slh_dsa_mechs[i].params; + o->mechs[i].params_len = token.slh_dsa_mechs[i].params_len; + o->mechs[i].result_flags = 0; + o->mechs[i].usage_flags = token.slh_dsa_mechs[i].usage_flags; + } + } else { + /* Use the default list */ + o->num_mechs = 1; + o->mechs[0].mech = CKM_SLH_DSA; + o->mechs[0].params = NULL; + o->mechs[0].params_len = 0; + o->mechs[0].result_flags = 0; + o->mechs[0].usage_flags = CKF_SIGN | CKF_VERIFY; + } + break; +#endif /* EVP_PKEY_SLH_DSA_SHA2_128S */ + /* Nothing in the above enum can be used for secret keys */ + case CKK_AES: + if (token.num_aes_mechs > 0 ) { + o->num_mechs = token.num_aes_mechs; + for (i = 0; i < token.num_aes_mechs; i++) { + o->mechs[i].mech = token.aes_mechs[i].mech; + o->mechs[i].params = token.aes_mechs[i].params; + o->mechs[i].params_len = token.aes_mechs[i].params_len; + o->mechs[i].result_flags = 0; + o->mechs[i].usage_flags = token.aes_mechs[i].usage_flags; + } + } else { + /* Use the default list */ + o->num_mechs = 1; + o->mechs[0].mech = CKM_AES_CBC; + o->mechs[0].params = NULL; + o->mechs[0].params_len = 0; + o->mechs[0].result_flags = 0; + o->mechs[0].usage_flags = CKF_ENCRYPT|CKF_DECRYPT|CKF_WRAP|CKF_UNWRAP; + } + break; + } +} + +/** + * Allocate place in the structure for every certificate found + * and store related information + */ +int callback_certificates(test_certs_t *objects, + CK_ATTRIBUTE template[], unsigned long template_size, CK_OBJECT_HANDLE object_handle) +{ + EVP_PKEY *evp = NULL; + const u_char *cp = NULL; + test_cert_t *o = NULL; + int base_id; + + if (*(CK_CERTIFICATE_TYPE *)template[3].pValue != CKC_X_509) + return 0; + + /* Ignore objects with empty ID -- we don't know what to do with them */ + if (template[0].ulValueLen == 0) { + return 0; + } + + if ((o = add_object(objects, template[0], template[2])) == NULL) + return -1; + + /* Extract public key from the certificate */ + cp = template[1].pValue; + if (d2i_X509(&(o->x509), &cp, template[1].ulValueLen) == NULL) { + fail_msg("d2i_X509"); + return -1; + } else if ((evp = X509_get_pubkey(o->x509)) == NULL) { + fail_msg("X509_get_pubkey failed."); + return -1; + } + + base_id = EVP_PKEY_base_id(evp); + switch (base_id) { + case EVP_PKEY_RSA: + case EVP_PKEY_EC: + case EVP_PKEY_ED25519: + case EVP_PKEY_X25519: +#ifdef EVP_PKEY_ED448 + case EVP_PKEY_ED448: +#endif +#ifdef EVP_PKEY_X448 + case EVP_PKEY_X448: +#endif +#ifdef EVP_PKEY_ML_DSA_44 + case EVP_PKEY_ML_DSA_44: + case EVP_PKEY_ML_DSA_65: + case EVP_PKEY_ML_DSA_87: +#endif +#ifdef EVP_PKEY_SLH_DSA_SHA2_128S + case EVP_PKEY_SLH_DSA_SHA2_128S: + case EVP_PKEY_SLH_DSA_SHAKE_128S: + case EVP_PKEY_SLH_DSA_SHA2_128F: + case EVP_PKEY_SLH_DSA_SHAKE_128F: + case EVP_PKEY_SLH_DSA_SHA2_192S: + case EVP_PKEY_SLH_DSA_SHAKE_192S: + case EVP_PKEY_SLH_DSA_SHA2_192F: + case EVP_PKEY_SLH_DSA_SHAKE_192F: + case EVP_PKEY_SLH_DSA_SHA2_256S: + case EVP_PKEY_SLH_DSA_SHAKE_256S: + case EVP_PKEY_SLH_DSA_SHA2_256F: + case EVP_PKEY_SLH_DSA_SHAKE_256F: +#endif + o->key = evp; + o->type = base_id; + o->bits = EVP_PKEY_bits(evp); + break; + default: + EVP_PKEY_free(evp); + fprintf(stderr, "[WARN %s ]evp->type = 0x%.4X (not supported)\n", + o->id_str, EVP_PKEY_id(evp)); + } + + debug_print(" [ OK %s ] Certificate with label %s loaded successfully", + o->id_str, o->label); + return 0; +} + +/** + * Pair found private keys on the card with existing certificates + */ +int callback_private_keys(test_certs_t *objects, + CK_ATTRIBUTE template[], unsigned long template_size, CK_OBJECT_HANDLE object_handle) +{ + test_cert_t *o = NULL; + char *key_id; + + /* Ignore objects with empty ID -- we don't know what to do with them */ + if (template[3].ulValueLen == 0) { + return 0; + } + + /* Search for already stored certificate with same ID */ + if ((o = search_certificate(objects, &(template[3]))) == NULL) { + key_id = convert_byte_string(template[3].pValue, + template[3].ulValueLen); + fprintf(stderr, "Can't find certificate for private key with ID %s\n", key_id); + free(key_id); + + fprintf(stderr, "Let's create a bogus structure without certificate data\n"); + if ((o = add_object(objects, template[3], template[7])) == NULL) + return -1; + } + + if (o->private_handle != CK_INVALID_HANDLE) { + key_id = convert_byte_string(template[3].pValue, template[3].ulValueLen); + fprintf(stderr, "Object already filled? ID %s\n", key_id); + free(key_id); + return -1; + } + + /* Store attributes, flags and handles */ + o->private_handle = object_handle; + o->sign = (template[0].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[0].pValue) : CK_FALSE; + o->decrypt = (template[1].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[1].pValue) : CK_FALSE; + o->key_type = (template[2].ulValueLen == sizeof(CK_KEY_TYPE)) + ? *((CK_KEY_TYPE *) template[2].pValue) : (CK_KEY_TYPE) -1; + o->always_auth = (template[4].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[4].pValue) : CK_FALSE; + o->unwrap = (template[5].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[5].pValue) : CK_FALSE; + o->derive_priv = (template[6].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[6].pValue) : CK_FALSE; + o->extractable = (template[8].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[8].pValue) : CK_FALSE; + o->decapsulate = (template[9].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[9].pValue) : CK_FALSE; + o->parameter_set = (template[10].ulValueLen == sizeof(CK_ULONG)) + ? *((CK_ULONG *) template[10].pValue) : CK_UNAVAILABLE_INFORMATION; + + debug_print(" [ OK %s ] Private key loaded successfully S:%d D:%d T:%02lX", + o->id_str, o->sign, o->decrypt, o->key_type); + return 0; +} + +/** + * Pair found public keys on the card with existing certificates + */ +int callback_public_keys(test_certs_t *objects, + CK_ATTRIBUTE template[], unsigned long template_size, CK_OBJECT_HANDLE object_handle) +{ + test_cert_t *o = NULL; + char *key_id; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_PKEY_CTX *ctx = NULL; + OSSL_PARAM *params = NULL; + OSSL_PARAM_BLD *bld = NULL; +#endif + + /* Search for already stored certificate with same ID */ + if ((o = search_certificate(objects, &(template[3]))) == NULL) { + key_id = convert_byte_string(template[3].pValue, template[3].ulValueLen); + fprintf(stderr, "Can't find certificate for public key with ID %s\n", key_id); + free(key_id); + return -1; + } + + if (o->verify_public != 0) { + key_id = convert_byte_string(template[3].pValue, template[3].ulValueLen); + fprintf(stderr, "Object already filled? ID %s\n", key_id); + free(key_id); + return -1; + } + + o->public_handle = object_handle; + o->verify = (template[0].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[0].pValue) : CK_FALSE; + o->encrypt = (template[1].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[1].pValue) : CK_FALSE; + /* store key type in case there is no corresponding private key */ + o->key_type = (template[2].ulValueLen == sizeof(CK_KEY_TYPE)) + ? *((CK_KEY_TYPE *) template[2].pValue) : (CK_KEY_TYPE) -1; + o->wrap = (template[8].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[8].pValue) : CK_FALSE; + o->derive_pub = (template[9].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[9].pValue) : CK_FALSE; + o->encapsulate = (template[10].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[10].pValue) : CK_FALSE; + + /* check if we get the same public key as from the certificate */ + if (o->key_type == CKK_RSA) { + BIGNUM *n = NULL, *e = NULL; + n = BN_bin2bn(template[4].pValue, (int)template[4].ulValueLen, NULL); + e = BN_bin2bn(template[5].pValue, (int)template[5].ulValueLen, NULL); + if (o->key != NULL) { + int rv; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const BIGNUM *cert_n = NULL, *cert_e = NULL; + RSA *rsa = EVP_PKEY_get0_RSA(o->key); + RSA_get0_key(rsa, &cert_n, &cert_e, NULL); +#else + BIGNUM *cert_n = NULL, *cert_e = NULL; + if ((EVP_PKEY_get_bn_param(o->key, OSSL_PKEY_PARAM_RSA_N, &cert_n) != 1) || + (EVP_PKEY_get_bn_param(o->key, OSSL_PKEY_PARAM_RSA_E, &cert_e) != 1)) { + fprintf(stderr, "Failed to extract RSA key parameters"); + BN_free(cert_n); + BN_free(n); + BN_free(e); + return -1; + } +#endif + rv = BN_cmp(cert_n, n) == 0 && BN_cmp(cert_e, e) == 0; + if (rv == 1) { + o->verify_public = 1; + } else { + debug_print(" [WARN %s ] Got different public key then from the certificate", + o->id_str); + } +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + BN_free(cert_n); + BN_free(cert_e); +#endif + BN_free(n); + BN_free(e); + } else { /* store the public key for future use */ + o->type = EVP_PKEY_RSA; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA *rsa = RSA_new(); + if (rsa == NULL) { + fail_msg("Unable to allocate RSA key"); + return -1; + } + o->key = EVP_PKEY_new(); + if (o->key == NULL) { + fail_msg("Unable to allocate EVP_PKEY"); + RSA_free(rsa); + return -1; + } + if (RSA_set0_key(rsa, n, e, NULL) != 1) { + fail_msg("Unable set RSA key params"); + EVP_PKEY_free(o->key); + RSA_free(rsa); + return -1; + } + if (EVP_PKEY_assign_RSA(o->key, rsa) != 1) { + EVP_PKEY_free(o->key); + RSA_free(rsa); + fail_msg("Unable to assign RSA to EVP_PKEY"); + return -1; + } +#else + if (!(ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL)) || + !(bld = OSSL_PARAM_BLD_new()) || + OSSL_PARAM_BLD_push_BN(bld, "n", n) != 1 || + OSSL_PARAM_BLD_push_BN(bld, "e", e) != 1 || + !(params = OSSL_PARAM_BLD_to_param(bld))) { + EVP_PKEY_CTX_free(ctx); + BN_free(n); + BN_free(e); + OSSL_PARAM_BLD_free(bld); + fail_msg("Unable to set key params"); + return -1; + } + OSSL_PARAM_BLD_free(bld); + if (EVP_PKEY_fromdata_init(ctx) != 1 || + EVP_PKEY_fromdata(ctx, &o->key, EVP_PKEY_PUBLIC_KEY, params) != 1) { + EVP_PKEY_CTX_free(ctx); + BN_free(n); + BN_free(e); + OSSL_PARAM_free(params); + fail_msg("Unable to store key"); + return -1; + } + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + BN_free(n); + BN_free(e); +#endif + o->bits = EVP_PKEY_bits(o->key); + } + } else if (o->key_type == CKK_EC) { + int ec_error = 1; + ASN1_OBJECT *oid = NULL; + ASN1_OCTET_STRING *pub_asn1 = NULL; + const unsigned char *pub, *p; + EC_POINT *ecpoint = NULL; + EC_GROUP *ecgroup = NULL; + int nid, pub_len; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EC_GROUP *cert_group = NULL; + EC_POINT *cert_point = NULL; +#endif + + /* Parse the nid out of the EC_PARAMS */ + p = template[6].pValue; + oid = d2i_ASN1_OBJECT(NULL, &p, template[6].ulValueLen); + if (oid == NULL) { + debug_print(" [WARN %s ] Failed to convert EC_PARAMS" + " to OpenSSL format", o->id_str); + goto ec_out; + } + nid = OBJ_obj2nid(oid); + ASN1_OBJECT_free(oid); + if (nid == NID_undef) { + debug_print(" [WARN %s ] Failed to convert EC_PARAMS" + " to NID", o->id_str); + goto ec_out; + } + ecgroup = EC_GROUP_new_by_curve_name(nid); + if (ecgroup == NULL) { + debug_print(" [WARN %s ] Failed to create new EC_GROUP" + " from NID", o->id_str); + goto ec_out; + } + EC_GROUP_set_asn1_flag(ecgroup, OPENSSL_EC_NAMED_CURVE); + + p = template[7].pValue; + pub_asn1 = d2i_ASN1_OCTET_STRING(NULL, &p, template[7].ulValueLen); + pub = ASN1_STRING_get0_data(pub_asn1); + pub_len = ASN1_STRING_length(pub_asn1); + + if (!(ecpoint = EC_POINT_new(ecgroup))) { + debug_print(" [WARN %s ] Cannot allocate EC_POINT", o->id_str); + goto ec_out; + } + + if (EC_POINT_oct2point(ecgroup, ecpoint, pub, pub_len, NULL) != 1) { + debug_print(" [WARN %s ] Cannot parse EC_POINT", o->id_str); + goto ec_out; + } + + if (o->key != NULL) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L + EC_KEY *ec = EVP_PKEY_get0_EC_KEY(o->key); + const EC_GROUP *cert_group = EC_KEY_get0_group(ec); + const EC_POINT *cert_point = EC_KEY_get0_public_key(ec); + int cert_nid = EC_GROUP_get_curve_name(cert_group); +#else + char curve_name[80]; + size_t curve_name_len = 0; + unsigned char pubkey[256]; + size_t pubkey_len = 0; + int cert_nid = 0; + if (EVP_PKEY_get_group_name(o->key, curve_name, sizeof(curve_name), &curve_name_len) != 1 || + (cert_nid = OBJ_txt2nid(curve_name)) == NID_undef || + (cert_group = EC_GROUP_new_by_curve_name(cert_nid)) == NULL) { + debug_print(" [WARN %s ] Cannot get EC_GROUP from EVP_PKEY", o->id_str); + goto ec_out; + } + cert_point = EC_POINT_new(cert_group); + if (!cert_point || + EVP_PKEY_get_octet_string_param(o->key, OSSL_PKEY_PARAM_PUB_KEY, pubkey, sizeof(pubkey), &pubkey_len) != 1 || + EC_POINT_oct2point(cert_group, cert_point, pubkey, pubkey_len, NULL) != 1) { + debug_print(" [WARN %s ] Cannot get EC_POINT from EVP_PKEY", o->id_str); + goto ec_out; + } +#endif + if (cert_nid != nid || + EC_GROUP_cmp(cert_group, ecgroup, NULL) != 0 || + EC_POINT_cmp(ecgroup, cert_point, ecpoint, NULL) != 0) { + debug_print(" [WARN %s ] Got different public" + "key then from the certificate", + o->id_str); + goto ec_out; + } + o->verify_public = 1; + } else { /* store the public key for future use */ + o->type = EVP_PKEY_EC; + o->key = EVP_PKEY_new(); + o->bits = EC_GROUP_get_degree(ecgroup); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + EC_KEY *ec = EC_KEY_new_by_curve_name(nid); + EC_KEY_set_public_key(ec, ecpoint); + EC_KEY_set_group(ec, ecgroup); + EVP_PKEY_set1_EC_KEY(o->key, ec); + EC_KEY_free(ec); +#else + ctx = EVP_PKEY_CTX_new_from_name(0, "EC", 0); + + const char *curve_name = OBJ_nid2sn(nid); + if (!(bld = OSSL_PARAM_BLD_new()) || + OSSL_PARAM_BLD_push_utf8_string(bld, "group", curve_name, strlen(curve_name)) != 1 || + OSSL_PARAM_BLD_push_octet_string(bld, "pub", pub, pub_len) != 1 || + !(params = OSSL_PARAM_BLD_to_param(bld))) { + debug_print(" [WARN %s ] Cannot set params from EVP_PKEY", o->id_str); + goto ec_out; + } + + if (ctx == NULL || params == NULL || + EVP_PKEY_fromdata_init(ctx) != 1 || + EVP_PKEY_fromdata(ctx, &o->key, EVP_PKEY_PUBLIC_KEY, params) != 1) { + debug_print(" [WARN %s ] Cannot set params for EVP_PKEY", o->id_str); + goto ec_out; + } +#endif + } + + ec_error = 0; + + ec_out: + ASN1_STRING_free(pub_asn1); + EC_GROUP_free(ecgroup); + EC_POINT_free(ecpoint); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_BLD_free(bld); + OSSL_PARAM_free(params); + EC_GROUP_free(cert_group); + EC_POINT_free(cert_point); +#endif + + if (ec_error) { + debug_print(" [WARN %s ] Failed to check EC public key", o->id_str); + return -1; + } + + } else if (o->key_type == CKK_EC_EDWARDS + || o->key_type == CKK_EC_MONTGOMERY) { + EVP_PKEY *key = NULL; + ASN1_PRINTABLESTRING *curve = NULL; + ASN1_OBJECT *obj = NULL; + const unsigned char *a; + ASN1_OCTET_STRING *os; + int evp_type = 0, exp_length = 0; + + a = template[6].pValue; + if (d2i_ASN1_PRINTABLESTRING(&curve, &a, (long)template[6].ulValueLen) != NULL) { + switch (o->key_type) { +#ifdef EVP_PKEY_ED25519 + case CKK_EC_EDWARDS: + if (strcmp((char *)ASN1_STRING_get0_data(curve), "edwards25519") == 0) { + evp_type = EVP_PKEY_ED25519; + break; +#ifdef EVP_PKEY_ED448 + } else if (strcmp((char *)ASN1_STRING_get0_data(curve), "edwards448") == 0) { + evp_type = EVP_PKEY_ED448; + break; +#endif /* EVP_PKEY_ED448 */ + } + debug_print(" [WARN %s ] Unknown curve name. " + " expected edwards25519 or edwards448, got %s", + o->id_str, ASN1_STRING_get0_data(curve)); + break; +#endif +#ifdef EVP_PKEY_X25519 + case CKK_EC_MONTGOMERY: + if (strcmp((char *)ASN1_STRING_get0_data(curve), "curve25519") == 0) { + evp_type = EVP_PKEY_X25519; + break; +#ifdef EVP_PKEY_X448 + } else if (strcmp((char *)ASN1_STRING_get0_data(curve), "curve448") == 0) { + evp_type = EVP_PKEY_X448; + break; +#endif /* EVP_PKEY_X448 */ + } + debug_print(" [WARN %s ] Unknown curve name. " + " expected curve25519 or curve448, got %s", + o->id_str, ASN1_STRING_get0_data(curve)); + break; +#endif + default: + debug_print(" [WARN %s ] Unknown key type %lu", o->id_str, o->key_type); + return -1; + } + ASN1_PRINTABLESTRING_free(curve); + } else if (d2i_ASN1_OBJECT(&obj, &a, (long)template[6].ulValueLen) != NULL) { +#if defined(EVP_PKEY_ED25519) || defined (EVP_PKEY_X25519) + int nid = OBJ_obj2nid(obj); +#endif + ASN1_OBJECT_free(obj); + + switch (o->key_type) { +#ifdef EVP_PKEY_ED25519 + case CKK_EC_EDWARDS: + if (nid == NID_ED25519) { + evp_type = EVP_PKEY_ED25519; + break; +#ifdef EVP_PKEY_ED448 + } else if (nid == NID_ED448) { + evp_type = EVP_PKEY_ED448; + break; +#endif /* EVP_PKEY_ED448 */ + } + debug_print(" [WARN %s ] Unknown OID. " + " expected NID_ED25519 or NID_ED448, got %d", + o->id_str, nid); + break; +#endif +#ifdef EVP_PKEY_X25519 + case CKK_EC_MONTGOMERY: + if (nid == NID_X25519) { + evp_type = EVP_PKEY_X25519; + break; +#ifdef EVP_PKEY_X448 + } else if (nid == NID_X448) { + evp_type = EVP_PKEY_X448; + break; +#endif /* EVP_PKEY_X448 */ + } + debug_print(" [WARN %s ] Unknown OID. " + " expected NID_X25519 or NID_X448, got %d", + o->id_str, nid); + break; +#endif + default: + debug_print(" [WARN %s ] Unknown key type %lu", o->id_str, o->key_type); + return -1; + } + } else { + debug_print(" [WARN %s ] Failed to convert EC_PARAMS" + " to curve name or object id", o->id_str); + return -1; + } + + /* PKCS#11-compliant modules should return ASN1_OCTET_STRING or ASN1_BIT_STRING */ + a = template[7].pValue; + os = d2i_ASN1_OCTET_STRING(NULL, &a, (long)template[7].ulValueLen); + if (!os) { + os = d2i_ASN1_BIT_STRING(NULL, &a, (long)template[7].ulValueLen); + } + /* or raw bytes as a last resort? */ + if (!os) { + if ((os = ASN1_OCTET_STRING_new()) == NULL) { + debug_print(" [WARN %s ] Out of memory", o->id_str); + return -1; + } + if (ASN1_OCTET_STRING_set(os, template[7].pValue, (int)template[7].ulValueLen) == 0) { + debug_print(" [WARN %s ] Cannot decode EC_POINT", o->id_str); + return -1; + } + } + switch (evp_type) { + case EVP_PKEY_ED25519: + case EVP_PKEY_X25519: + exp_length = 32; + break; +#ifdef EVP_PKEY_ED448 + case EVP_PKEY_ED448: + exp_length = 57; + break; +#endif +#ifdef EVP_PKEY_X448 + case EVP_PKEY_X448: + exp_length = 56; + break; +#endif + } + if (ASN1_STRING_length(os) != exp_length) { + debug_print(" [WARN %s ] Invalid length of EC_POINT value. Got %d, expected %d", + o->id_str, ASN1_STRING_length(os), exp_length); + return -1; + } + key = EVP_PKEY_new_raw_public_key(evp_type, NULL, + (const uint8_t *)ASN1_STRING_get0_data(os), + ASN1_STRING_length(os)); + if (key == NULL) { + debug_print(" [WARN %s ] Out of memory", o->id_str); + ASN1_STRING_free(os); + return -1; + } + if (o->key != NULL) { + unsigned char *pub = NULL; + size_t publen = 0; + + /* TODO check EVP_PKEY type */ + + if (EVP_PKEY_get_raw_public_key(o->key, NULL, &publen) != 1) { + debug_print(" [WARN %s ] Cannot get size of the key", o->id_str); + ASN1_STRING_free(os); + return -1; + } + pub = malloc(publen); + if (pub == NULL) { + debug_print(" [WARN %s ] Out of memory", o->id_str); + ASN1_STRING_free(os); + return -1; + } + + if (EVP_PKEY_get_raw_public_key(o->key, pub, &publen) != 1 || + publen != (size_t)ASN1_STRING_length(os) || + memcmp(pub, ASN1_STRING_get0_data(os), publen) != 0) { + debug_print(" [WARN %s ] Got different public" + "key then from the certificate", + o->id_str); + free(pub); + ASN1_STRING_free(os); + return -1; + } + free(pub); + EVP_PKEY_free(key); + o->verify_public = 1; + } else { /* store the public key for future use */ + o->type = evp_type; + o->key = key; + o->bits = exp_length * 8; + } + ASN1_STRING_free(os); +#if defined(EVP_PKEY_ML_DSA_44) || defined(EVP_PKEY_ML_KEM_512) || defined(EVP_PKEY_SLH_DSA_SHA2_128S) + } else if (o->key_type == CKK_ML_DSA || + o->key_type == CKK_ML_KEM || + o->key_type == CKK_SLH_DSA) { + int base_id = 0; + + o->parameter_set = (template[11].ulValueLen == sizeof(CK_ULONG)) + ? *((CK_ULONG *)template[11].pValue) : CK_UNAVAILABLE_INFORMATION; + switch (o->key_type) { + case CKK_ML_DSA: + switch (o->parameter_set) { + case CKP_ML_DSA_44: + base_id = EVP_PKEY_ML_DSA_44; + break; + case CKP_ML_DSA_65: + base_id = EVP_PKEY_ML_DSA_65; + break; + case CKP_ML_DSA_87: + base_id = EVP_PKEY_ML_DSA_87; + break; + default: + debug_print(" [WARN %s ] Unknown parameter set (%lu)", + o->id_str, o->parameter_set); + return -1; + } + break; +#ifdef EVP_PKEY_ML_KEM_512 + case CKK_ML_KEM: + switch (o->parameter_set) { + case CKP_ML_KEM_512: + base_id = EVP_PKEY_ML_KEM_512; + break; + case CKP_ML_KEM_768: + base_id = EVP_PKEY_ML_KEM_768; + break; + case CKP_ML_KEM_1024: + base_id = EVP_PKEY_ML_KEM_1024; + break; + default: + debug_print(" [WARN %s ] Unknown parameter set (%lu)", + o->id_str, o->parameter_set); + return -1; + } + break; +#endif + case CKK_SLH_DSA: + switch (o->parameter_set) { + case CKP_SLH_DSA_SHA2_128S: + base_id = EVP_PKEY_SLH_DSA_SHA2_128S; + break; + case CKP_SLH_DSA_SHAKE_128S: + base_id = EVP_PKEY_SLH_DSA_SHAKE_128S; + break; + case CKP_SLH_DSA_SHA2_128F: + base_id = EVP_PKEY_SLH_DSA_SHA2_128F; + break; + case CKP_SLH_DSA_SHAKE_128F: + base_id = EVP_PKEY_SLH_DSA_SHAKE_128F; + break; + case CKP_SLH_DSA_SHA2_192S: + base_id = EVP_PKEY_SLH_DSA_SHA2_192S; + break; + case CKP_SLH_DSA_SHAKE_192S: + base_id = EVP_PKEY_SLH_DSA_SHAKE_192S; + break; + case CKP_SLH_DSA_SHA2_192F: + base_id = EVP_PKEY_SLH_DSA_SHA2_192F; + break; + case CKP_SLH_DSA_SHAKE_192F: + base_id = EVP_PKEY_SLH_DSA_SHAKE_192F; + break; + case CKP_SLH_DSA_SHA2_256S: + base_id = EVP_PKEY_SLH_DSA_SHA2_256S; + break; + case CKP_SLH_DSA_SHAKE_256S: + base_id = EVP_PKEY_SLH_DSA_SHAKE_256S; + break; + case CKP_SLH_DSA_SHA2_256F: + base_id = EVP_PKEY_SLH_DSA_SHA2_256F; + break; + case CKP_SLH_DSA_SHAKE_256F: + base_id = EVP_PKEY_SLH_DSA_SHAKE_256F; + break; + default: + debug_print(" [WARN %s ] Unknown parameter set (%lu)", + o->id_str, o->parameter_set); + return -1; + } + break; + default: + debug_print(" [WARN %s ] Unknown key type (%lu)", o->id_str, o->key_type); + return -1; + } + + if (o->key != NULL) { + // compare with the certificate + unsigned char *cert_pubkey = NULL; + size_t cert_pubkey_len = 0; + + if (base_id != o->type) { + debug_print(" [WARN %s ] Got different key type than certificate " + "(got %d, expected %d)", + o->id_str, base_id, o->type); + } + + if ((EVP_PKEY_get_octet_string_param(o->key, "pub", NULL, 0, &cert_pubkey_len) != 1)) { + fprintf(stderr, "Failed to extract public key length"); + return -1; + } + if ((cert_pubkey = malloc(cert_pubkey_len)) == NULL) { + fprintf(stderr, "Out of memory"); + return -1; + } + if ((EVP_PKEY_get_octet_string_param(o->key, "pub", cert_pubkey, cert_pubkey_len, &cert_pubkey_len) != 1)) { + fprintf(stderr, "Failed to extract public key"); + free(cert_pubkey); + return -1; + } + if (template[12].ulValueLen == cert_pubkey_len || + memcmp(template[12].pValue, cert_pubkey, cert_pubkey_len) == 0) { + o->verify_public = 1; + } else { + debug_print(" [WARN %s ] Got different public key then from the certificate", + o->id_str); + } + free(cert_pubkey); + } else { + // construct the OpenSSL key + o->type = base_id; + if (!(ctx = EVP_PKEY_CTX_new_id(o->type, NULL)) || + !(bld = OSSL_PARAM_BLD_new()) || + OSSL_PARAM_BLD_push_octet_string(bld, "pub", template[12].pValue, + template[12].ulValueLen) != 1 || + !(params = OSSL_PARAM_BLD_to_param(bld))) { + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_BLD_free(bld); + fail_msg("Unable to set key params"); + return -1; + } + OSSL_PARAM_BLD_free(bld); + if (EVP_PKEY_fromdata_init(ctx) != 1 || + EVP_PKEY_fromdata(ctx, &o->key, EVP_PKEY_PUBLIC_KEY, params) != 1) { + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + fail_msg("Unable to build key"); + return -1; + } + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + o->bits = EVP_PKEY_bits(o->key); + } +#endif /* EVP_PKEY_ML_DSA_44 */ + } else { + debug_print(" [WARN %s ] unknown key. Key type: %02lX", o->id_str, o->key_type); + return -1; + } + + add_supported_mechs(o); + + debug_print(" [ OK %s ] Public key loaded successfully V:%d E:%d T:%02lX", + o->id_str, o->verify, o->encrypt, o->key_type); + return 0; +} + +/** + * Store any secret keys + */ +int callback_secret_keys(test_certs_t *objects, + CK_ATTRIBUTE template[], unsigned long template_size, CK_OBJECT_HANDLE object_handle) +{ + test_cert_t *o = NULL; + + /* Ignore objects with empty ID and label that are left in SoftHSM after deriving key even after + * destroying them */ + if (template[13].pValue == NULL || template[1].pValue == NULL) { + return 0; + } + + if ((o = add_object(objects, template[1], template[13])) == NULL) + return -1; + + /* TODO generic secret + * there is also no respective EVP_* for AES keys in OpenSSL ... + o->type = ??; */ + + /* Store attributes, flags and handles */ + o->private_handle = object_handle; + /* For verification/encryption, we use the same key */ + o->public_handle = object_handle; + o->key_type = (template[0].ulValueLen == sizeof(CK_KEY_TYPE)) + ? *((CK_KEY_TYPE *) template[0].pValue) : (CK_KEY_TYPE) -1; + o->sign = (template[3].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[3].pValue) : CK_FALSE; + o->verify = (template[4].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[4].pValue) : CK_FALSE; + o->encrypt = (template[5].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[5].pValue) : CK_FALSE; + o->decrypt = (template[6].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[6].pValue) : CK_FALSE; + o->derive_priv = (template[7].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[7].pValue) : CK_FALSE; + o->wrap = (template[8].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[8].pValue) : CK_FALSE; + o->unwrap = (template[9].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[9].pValue) : CK_FALSE; + o->extractable = (template[12].ulValueLen == sizeof(CK_BBOOL)) + ? *((CK_BBOOL *) template[12].pValue) : CK_FALSE; + + if (template[10].ulValueLen > 0) { + /* pass the pointer to our structure */ + o->value = template[10].pValue; + template[10].pValue = NULL; + /* if there is CKA_VALUE_LEN it will be rewritten later */ + o->bits = template[10].ulValueLen * 8; + } + + if (template[11].pValue != NULL && template[11].ulValueLen > 0) { + o->bits = *((CK_ULONG *)template[11].pValue) * 8; + } + + add_supported_mechs(o); + + debug_print(" [ OK %s ] Secret key loaded successfully T:%02lX", o->id_str, o->key_type); + return 0; +} + + +int search_objects(test_certs_t *objects, token_info_t *info, + CK_ATTRIBUTE filter[], long filter_size, CK_ATTRIBUTE template[], long template_size, + int (*callback)(test_certs_t *, CK_ATTRIBUTE[], unsigned long, CK_OBJECT_HANDLE)) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_ULONG object_count; + CK_OBJECT_HANDLE object_handle = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE_PTR object_handles = NULL; + unsigned long i = 0, objects_length = 0; + int j, ret = -1; + + /* FindObjects first + * https://wiki.oasis-open.org/pkcs11/CommonBugs + */ + rv = fp->C_FindObjectsInit(info->session_handle, filter, filter_size); + if (rv != CKR_OK) { + fprintf(stderr, "C_FindObjectsInit: rv = 0x%.8lX\n", rv); + return -1; + } + + while(1) { + rv = fp->C_FindObjects(info->session_handle, &object_handle, 1, &object_count); + if (object_count == 0) + break; + if (rv != CKR_OK) { + fprintf(stderr, "C_FindObjects: rv = 0x%.8lX\n", rv); + goto out; + } + /* store handle */ + if (i >= objects_length) { + CK_OBJECT_HANDLE_PTR new_object_handles = NULL; + objects_length += 4; // do not realloc after each row + new_object_handles = realloc(object_handles, objects_length * sizeof(CK_OBJECT_HANDLE)); + if (new_object_handles == NULL) { + fail_msg("Realloc failed. Need to store object handles.\n"); + goto out; + } + object_handles = new_object_handles; + } + object_handles[i++] = object_handle; + } + objects_length = i; //terminate list of handles + + rv = fp->C_FindObjectsFinal(info->session_handle); + if (rv != CKR_OK) { + fprintf(stderr, "C_FindObjectsFinal: rv = 0x%.8lX\n", rv); + fail_msg("Could not find certificate.\n"); + goto out; + } + + for (i = 0; i < objects_length; i++) { + /* Find attributes one after another to handle errors + * https://wiki.oasis-open.org/pkcs11/CommonBugs + */ + for (j = 0; j < template_size; j++) { + template[j].pValue = NULL; + template[j].ulValueLen = 0; + + rv = fp->C_GetAttributeValue(info->session_handle, object_handles[i], + &(template[j]), 1); + if (rv == CKR_ATTRIBUTE_TYPE_INVALID || + rv == CKR_ATTRIBUTE_SENSITIVE || + rv == CKR_DEVICE_ERROR) { + continue; + } else if (rv != CKR_OK) { + fail_msg("C_GetAttributeValue: rv = 0x%.8lX\n", rv); + goto out; + } + + /* Allocate memory to hold the data we want */ + if (template[j].ulValueLen == 0) { + continue; + } else { + template[j].pValue = malloc(template[j].ulValueLen); + if (template[j].pValue == NULL) { + fail_msg("malloc failed"); + goto out; + } + } + /* Call again to get actual attribute */ + rv = fp->C_GetAttributeValue(info->session_handle, object_handles[i], + &(template[j]), 1); + if (rv != CKR_OK) { + fail_msg("C_GetAttributeValue: rv = 0x%.8lX\n", rv); + goto out; + } + } + + callback(objects, template, template_size, object_handles[i]); + // XXX check results + for (j = 0; j < template_size; j++) + free(template[j].pValue); + } + ret = 0; +out: + free(object_handles); + return ret; +} + +void search_for_all_objects(test_certs_t *objects, token_info_t *info) +{ + CK_OBJECT_CLASS keyClass = CKO_CERTIFICATE; + CK_OBJECT_CLASS privateClass = CKO_PRIVATE_KEY; + CK_OBJECT_CLASS publicClass = CKO_PUBLIC_KEY; + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_ATTRIBUTE filter[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + }; + CK_ULONG filter_size = 1; + CK_ATTRIBUTE attrs[] = { + { CKA_ID, NULL_PTR, 0}, + { CKA_VALUE, NULL_PTR, 0}, + { CKA_LABEL, NULL_PTR, 0}, + { CKA_CERTIFICATE_TYPE, NULL_PTR, 0}, + }; + CK_ULONG attrs_size = sizeof (attrs) / sizeof (CK_ATTRIBUTE); + CK_ATTRIBUTE private_attrs[] = { + { CKA_SIGN, NULL, 0}, // CK_BBOOL + { CKA_DECRYPT, NULL, 0}, // CK_BBOOL + { CKA_KEY_TYPE, NULL, 0}, // CKK_ + { CKA_ID, NULL, 0}, + { CKA_ALWAYS_AUTHENTICATE, NULL, 0}, // CK_BBOOL + { CKA_UNWRAP, NULL, 0}, // CK_BBOOL + { CKA_DERIVE, NULL, 0}, // CK_BBOOL + { CKA_LABEL, NULL_PTR, 0}, + { CKA_EXTRACTABLE, NULL, 0}, // CK_BBOOL + { CKA_DECAPSULATE, NULL, 0}, // CK_BBOOL + { CKA_PARAMETER_SET, NULL, 0}, // CK_ULONG + }; + CK_ULONG private_attrs_size = sizeof (private_attrs) / sizeof (CK_ATTRIBUTE); + CK_ATTRIBUTE public_attrs[] = { + { CKA_VERIFY, NULL, 0}, // CK_BBOOL + { CKA_ENCRYPT, NULL, 0}, // CK_BBOOL + { CKA_KEY_TYPE, NULL, 0}, + { CKA_ID, NULL, 0}, + { CKA_MODULUS, NULL, 0}, + { CKA_PUBLIC_EXPONENT, NULL, 0}, + { CKA_EC_PARAMS, NULL, 0}, + { CKA_EC_POINT, NULL, 0}, + { CKA_WRAP, NULL, 0}, // CK_BBOOL + { CKA_DERIVE, NULL, 0}, // CK_BBOOL + { CKA_ENCAPSULATE, NULL, 0}, // CK_BBOOL + { CKA_PARAMETER_SET, NULL, 0}, // CK_ULONG + { CKA_VALUE, NULL, 0}, + }; + CK_ULONG public_attrs_size = sizeof (public_attrs) / sizeof (CK_ATTRIBUTE); + CK_ATTRIBUTE secret_attrs[] = { + { CKA_KEY_TYPE, NULL, 0}, + { CKA_ID, NULL, 0}, + { CKA_TOKEN, NULL, 0}, // CK_BBOOL + { CKA_SIGN, NULL, 0}, // CK_BBOOL + { CKA_VERIFY, NULL, 0}, // CK_BBOOL + { CKA_ENCRYPT, NULL, 0}, // CK_BBOOL + { CKA_DECRYPT, NULL, 0}, // CK_BBOOL + { CKA_DERIVE, NULL, 0}, // CK_BBOOL + { CKA_WRAP, NULL, 0}, // CK_BBOOL + { CKA_UNWRAP, NULL, 0}, // CK_BBOOL + { CKA_VALUE, NULL, 0}, + { CKA_VALUE_LEN, NULL, 0}, + { CKA_EXTRACTABLE, NULL, 0}, // CK_BBOOL + { CKA_LABEL, NULL_PTR, 0}, + }; + CK_ULONG secret_attrs_size = sizeof (secret_attrs) / sizeof (CK_ATTRIBUTE); + + debug_print("\nSearch for all certificates on the card"); + search_objects(objects, info, filter, filter_size, + attrs, attrs_size, callback_certificates); + + + /* do the same thing with private keys (collect handles based on the collected IDs) */ + debug_print("\nSearch for all private keys respective to the certificates"); + filter[0].pValue = &privateClass; + // search for all and pair on the fly + search_objects(objects, info, filter, filter_size, + private_attrs, private_attrs_size, callback_private_keys); + + debug_print("\nSearch for all public keys respective to the certificates"); + filter[0].pValue = &publicClass; + search_objects(objects, info, filter, filter_size, + public_attrs, public_attrs_size, callback_public_keys); + + debug_print("\nSearch for all secret keys"); + filter[0].pValue = &secretClass; + search_objects(objects, info, filter, filter_size, secret_attrs, secret_attrs_size, + callback_secret_keys); +} + +void clean_all_objects(test_certs_t *objects) { + unsigned int i; + for (i = 0; i < objects->count; i++) { + free(objects->data[i].key_id); + free(objects->data[i].id_str); + free(objects->data[i].label); + free(objects->data[i].value); + X509_free(objects->data[i].x509); + EVP_PKEY_free(objects->data[i].key); + } + free(objects->data); +} + +const char *get_mechanism_name(unsigned long mech_id) +{ + switch (mech_id) { + case CKM_RSA_PKCS: + return "RSA_PKCS"; + case CKM_SHA1_RSA_PKCS: + return "SHA1_RSA_PKCS"; + case CKM_SHA224_RSA_PKCS: + return "SHA224_RSA_PKCS"; + case CKM_SHA256_RSA_PKCS: + return "SHA256_RSA_PKCS"; + case CKM_SHA384_RSA_PKCS: + return "SHA384_RSA_PKCS"; + case CKM_SHA512_RSA_PKCS: + return "SHA512_RSA_PKCS"; + case CKM_SHA3_224_RSA_PKCS: + return "SHA3_224_RSA_PKCS"; + case CKM_SHA3_256_RSA_PKCS: + return "SHA3_256_RSA_PKCS"; + case CKM_SHA3_384_RSA_PKCS: + return "SHA3_384_RSA_PKCS"; + case CKM_SHA3_512_RSA_PKCS: + return "SHA3_512_RSA_PKCS"; + case CKM_RSA_X_509: + return "RSA_X_509"; + case CKM_ECDSA: + return "ECDSA"; + case CKM_ECDSA_SHA1: + return "ECDSA_SHA1"; + case CKM_ECDSA_SHA224: + return "ECDSA_SHA224"; + case CKM_ECDSA_SHA256: + return "ECDSA_SHA256"; + case CKM_ECDSA_SHA384: + return "ECDSA_SHA384"; + case CKM_ECDSA_SHA512: + return "ECDSA_SHA512"; + case CKM_ECDSA_SHA3_224: + return "ECDSA_SHA3_224"; + case CKM_ECDSA_SHA3_256: + return "ECDSA_SHA3_256"; + case CKM_ECDSA_SHA3_384: + return "ECDSA_SHA3_384"; + case CKM_ECDSA_SHA3_512: + return "ECDSA_SHA3_512"; + case CKM_EDDSA: + return "EDDSA"; + case CKM_XEDDSA: + return "XEDDSA"; + case CKM_ECDH1_DERIVE: + return "ECDH1_DERIVE"; + case CKM_ECDH1_COFACTOR_DERIVE: + return "ECDH1_COFACTOR_DERIVE"; + case CKM_EC_KEY_PAIR_GEN: + return "EC_KEY_PAIR_GEN"; + case CKM_EC_EDWARDS_KEY_PAIR_GEN: + return "EC_EDWARDS_KEY_PAIR_GEN"; + case CKM_RSA_PKCS_KEY_PAIR_GEN: + return "RSA_PKCS_KEY_PAIR_GEN"; + case CKM_GENERIC_SECRET_KEY_GEN: + return "GENERIC_SECRET_KEY_GEN"; + case CKM_MD5_RSA_PKCS: + return "MD5_RSA_PKCS"; + case CKM_RIPEMD160_RSA_PKCS: + return "RIPEMD160_RSA_PKCS"; + case CKM_RSA_PKCS_PSS: + return "RSA_PKCS_PSS"; + case CKM_SHA1_RSA_PKCS_PSS: + return "SHA1_RSA_PKCS_PSS"; + case CKM_SHA224_RSA_PKCS_PSS: + return "SHA224_RSA_PKCS_PSS"; + case CKM_SHA256_RSA_PKCS_PSS: + return "SHA256_RSA_PKCS_PSS"; + case CKM_SHA384_RSA_PKCS_PSS: + return "SHA384_RSA_PKCS_PSS"; + case CKM_SHA512_RSA_PKCS_PSS: + return "SHA512_RSA_PKCS_PSS"; + case CKM_SHA3_224_RSA_PKCS_PSS: + return "SHA3_224_RSA_PKCS_PSS"; + case CKM_SHA3_256_RSA_PKCS_PSS: + return "SHA3_256_RSA_PKCS_PSS"; + case CKM_SHA3_384_RSA_PKCS_PSS: + return "SHA3_384_RSA_PKCS_PSS"; + case CKM_SHA3_512_RSA_PKCS_PSS: + return "SHA3_512_RSA_PKCS_PSS"; + case CKM_MD5_HMAC: + return "MD5_HMAC"; + case CKM_SHA_1_HMAC: + return "SHA_1_HMAC"; + case CKM_SHA_1_HMAC_GENERAL: + return "SHA_1_HMAC_GENERAL"; + case CKM_SHA224_HMAC: + return "SHA224_HMAC"; + case CKM_SHA224_HMAC_GENERAL: + return "SHA224_HMAC_GENERAL"; + case CKM_SHA256_HMAC: + return "SHA256_HMAC"; + case CKM_SHA256_HMAC_GENERAL: + return "SHA256_HMAC_GENERAL"; + case CKM_SHA384_HMAC: + return "SHA384_HMAC"; + case CKM_SHA384_HMAC_GENERAL: + return "SHA384_HMAC_GENERAL"; + case CKM_SHA512_HMAC: + return "SHA512_HMAC"; + case CKM_SHA512_HMAC_GENERAL: + return "SHA512_HMAC_GENERAL"; + case CKM_RSA_PKCS_OAEP: + return "RSA_PKCS_OAEP"; + case CKM_RIPEMD160: + return "RIPEMD160"; + case CKM_GOSTR3411: + return "GOSTR3411"; + case CKM_MD5: + return "MD5"; + case CKM_SHA_1: + return "SHA_1"; + case CKM_SHA224: + return "SHA224"; + case CKM_SHA256: + return "SHA256"; + case CKM_SHA384: + return "SHA384"; + case CKM_SHA512: + return "SHA512"; + case CKM_SHA3_256: + return "SHA3_256"; + case CKM_SHA3_224: + return "SHA3_224"; + case CKM_SHA3_384: + return "SHA3_384"; + case CKM_SHA3_512: + return "SHA3_512"; + case CKM_AES_ECB: + return "AES_ECB"; + case CKM_AES_ECB_ENCRYPT_DATA: + return "AES_ECB_ENCRYPT_DATA"; + case CKM_AES_KEY_GEN: + return "AES_KEY_GEN"; + case CKM_AES_CBC: + return "AES_CBC"; + case CKM_AES_CBC_ENCRYPT_DATA: + return "AES_CBC_ENCRYPT_DATA"; + case CKM_AES_CBC_PAD: + return "AES_CBC_PAD"; + case CKM_AES_MAC: + return "AES_MAC"; + case CKM_AES_MAC_GENERAL: + return "AES_MAC_GENERAL"; + case CKM_AES_CFB64: + return "AES_CFB64"; + case CKM_AES_CFB8: + return "AES_CFB8"; + case CKM_AES_CFB128: + return "AES_CFB128"; + case CKM_AES_OFB: + return "AES_OFB"; + case CKM_AES_CTR: + return "AES_CTR"; + case CKM_AES_GCM: + return "AES_GCM"; + case CKM_AES_CCM: + return "AES_CCM"; + case CKM_AES_CTS: + return "AES_CTS"; + case CKM_AES_CMAC: + return "AES_CMAC"; + case CKM_AES_CMAC_GENERAL: + return "AES_CMAC_GENERAL"; + case CKM_DES3_CMAC: + return "DES3_CMAC"; + case CKM_DES3_CMAC_GENERAL: + return "DES3_CMAC_GENERAL"; + case CKM_DES3_ECB: + return "DES3_ECB"; + case CKM_DES3_CBC: + return "DES3_CBC"; + case CKM_DES3_CBC_PAD: + return "DES3_CBC_PAD"; + case CKM_DES3_CBC_ENCRYPT_DATA: + return "DES3_CBC_ENCRYPT_DATA"; + case CKM_AES_XCBC_MAC: + return "AES_XCBC_MAC"; + case CKM_AES_XCBC_MAC_96: + return "AES_XCBC_MAC_96"; + case CKM_AES_KEY_WRAP: + return "AES_KEY_WRAP"; + case CKM_AES_KEY_WRAP_PAD: + return "AES_KEY_WRAP_PAD"; + case CKM_ML_DSA_KEY_PAIR_GEN: + return "ML_DSA_KEY_PAIR_GEN"; + case CKM_ML_DSA: + return "ML_DSA"; + case CKM_HASH_ML_DSA: + return "HASH_ML_DSA"; + case CKM_HASH_ML_DSA_SHA224: + return "HASH_ML_DSA_SHA224"; + case CKM_HASH_ML_DSA_SHA256: + return "HASH_ML_DSA_SHA256"; + case CKM_HASH_ML_DSA_SHA384: + return "HASH_ML_DSA_SHA384"; + case CKM_HASH_ML_DSA_SHA512: + return "HASH_ML_DSA_SHA3_512"; + case CKM_HASH_ML_DSA_SHA3_224: + return "HASH_ML_DSA_SHA3_224"; + case CKM_HASH_ML_DSA_SHA3_256: + return "HASH_ML_DSA_SHA3_256"; + case CKM_HASH_ML_DSA_SHA3_384: + return "HASH_ML_DSA_SHA3_384"; + case CKM_HASH_ML_DSA_SHA3_512: + return "HASH_ML_DSA_SHA3_512"; + case CKM_HASH_ML_DSA_SHAKE128: + return "HASH_ML_DSA_SHAKE128"; + case CKM_HASH_ML_DSA_SHAKE256: + return "HASH_ML_DSA_SHAKE256"; + case CKM_ML_KEM_KEY_PAIR_GEN: + return "ML_KEM_KEY_PAIR_GEN"; + case CKM_ML_KEM: + return "ML_KEM"; + case CKM_SLH_DSA_KEY_PAIR_GEN: + return "SLH_DSA_KEY_PAIR_GEN"; + case CKM_SLH_DSA: + return "SLH_DSA"; + case CKM_HASH_SLH_DSA: + return "HASH_SLH_DSA"; + case CKM_HASH_SLH_DSA_SHA224: + return "HASH_SLH_DSA_SHA224"; + case CKM_HASH_SLH_DSA_SHA256: + return "HASH_SLH_DSA_SHA256"; + case CKM_HASH_SLH_DSA_SHA384: + return "HASH_SLH_DSA_SHA384"; + case CKM_HASH_SLH_DSA_SHA512: + return "HASH_SLH_DSA_SHA3_512"; + case CKM_HASH_SLH_DSA_SHA3_224: + return "HASH_SLH_DSA_SHA3_224"; + case CKM_HASH_SLH_DSA_SHA3_256: + return "HASH_SLH_DSA_SHA3_256"; + case CKM_HASH_SLH_DSA_SHA3_384: + return "HASH_SLH_DSA_SHA3_384"; + case CKM_HASH_SLH_DSA_SHA3_512: + return "HASH_SLH_DSA_SHA3_512"; + case CKM_HASH_SLH_DSA_SHAKE128: + return "HASH_SLH_DSA_SHAKE128"; + case CKM_HASH_SLH_DSA_SHAKE256: + return "HASH_SLH_DSA_SHAKE256"; + default: + sprintf(name_buffer, "0x%.8lX", mech_id); + return name_buffer; + } +} + +const char *get_mgf_name(unsigned long mgf_id) +{ + switch (mgf_id) { + case CKG_MGF1_SHA1: + return "MGF1_SHA_1"; + case CKG_MGF1_SHA224: + return "MGF1_SHA224"; + case CKG_MGF1_SHA256: + return "MGF1_SHA256"; + case CKG_MGF1_SHA384: + return "MGF1_SHA384"; + case CKG_MGF1_SHA512: + return "MGF1_SHA512"; + case CKG_MGF1_SHA3_224: + return "MGF1_SHA3_224"; + case CKG_MGF1_SHA3_256: + return "MGF1_SHA3_256"; + case CKG_MGF1_SHA3_384: + return "MGF1_SHA3_384"; + case CKG_MGF1_SHA3_512: + return "MGF1_SHA3_512"; + default: + sprintf(name_buffer, "0x%.8lX", mgf_id); + return name_buffer; + } +} + +const char * +get_key_type(test_cert_t * key) +{ + switch (key->key_type) { + case CKK_RSA: + return "RSA"; + case CKK_EC: + return "EC"; + case CKK_EC_EDWARDS: + return "EC_EDWARDS"; + case CKK_EC_MONTGOMERY: + return "EC_MONTGOMERY"; + case CKK_AES: + return "AES"; + case CKK_ML_DSA: + return "ML-DSA"; + case CKK_ML_KEM: + return "ML-KEM"; + case CKK_SLH_DSA: + return "SLH-DSA"; + default: + sprintf(name_buffer, "0x%.8lX", key->key_type); + return name_buffer; + } +} + +const char *get_mechanism_flag_name(unsigned long mech_id) +{ + switch (mech_id) { + case CKF_HW: + return "CKF_HW"; + case CKF_MESSAGE_ENCRYPT: + return "CKF_MESSAGE_ENCRYPT"; + case CKF_MESSAGE_DECRYPT: + return "CKF_MESSAGE_DECRYPT"; + case CKF_MESSAGE_SIGN: + return "CKF_MESSAGE_SIGN"; + case CKF_MESSAGE_VERIFY: + return "CKF_MESSAGE_VERIFY"; + case CKF_MULTI_MESSAGE: + return "CKF_MULTI_MESSAGE"; + case CKF_ENCRYPT: + return "CKF_ENCRYPT"; + case CKF_DECRYPT: + return "CKF_DECRYPT"; + case CKF_DIGEST: + return "CKF_DIGEST"; + case CKF_SIGN: + return "CKF_SIGN"; + case CKF_SIGN_RECOVER: + return "CKF_SIGN_RECOVER"; + case CKF_VERIFY: + return "CKF_VERIFY"; + case CKF_VERIFY_RECOVER: + return "CKF_VERIFY_RECOVER"; + case CKF_GENERATE: + return "CKF_GENERATE"; + case CKF_GENERATE_KEY_PAIR: + return "CKF_GENERATE_KEY_PAIR"; + case CKF_WRAP: + return "CKF_WRAP"; + case CKF_UNWRAP: + return "CKF_UNWRAP"; + case CKF_DERIVE: + return "CKF_DERIVE"; + case CKF_EC_F_P: + return "CKF_EC_F_P"; + case CKF_EC_F_2M: + return "CKF_EC_F_2M"; + case CKF_EC_NAMEDCURVE: + return "CKF_EC_NAMEDCURVE"; + case CKF_EC_UNCOMPRESS: + return "CKF_EC_UNCOMPRESS"; + case CKF_EC_COMPRESS: + return "CKF_EC_COMPRESS"; + case CKF_EC_ECPARAMETERS: + return "CKF_EC_ECPARAMETERS"; + case CKF_ENCAPSULATE: + return "CKF_ENCAPSULATE"; + case CKF_DECAPSULATE: + return "CKF_DECAPSULATE"; + default: + sprintf(flag_buffer, "0x%.8lX", mech_id); + return flag_buffer; + } +} + +const char * +get_mechanism_all_flag_name(unsigned long mech_id) +{ + CK_FLAGS j; + static char f_buffer[80]; + + f_buffer[0] = '\0'; + for (j = 1; j <= CKF_DECAPSULATE; j = j << 1) + /* append the name of the mechanism (only for known mechanisms) */ + if ((mech_id & j) != 0 && strncmp("0x", get_mechanism_flag_name(j), 2)) { + snprintf(f_buffer + strlen(f_buffer), + sizeof(f_buffer) - strlen(f_buffer), "%s,", get_mechanism_flag_name(j)); + } + /* remove comma at end of string */ + if ((strlen(f_buffer) > 0) && f_buffer[strlen(f_buffer) - 1] == ',') + f_buffer[strlen(f_buffer) - 1] = '\0'; + return f_buffer; +} + +char *convert_byte_string(unsigned char *id, unsigned long length) +{ + unsigned int i; + char *data; + if (length == 0) { + return NULL; + } + + data = malloc(3 * length * sizeof(char) + 1); + if (data == NULL) { + return NULL; + } + + for (i = 0; i < length; i++) { + sprintf(&data[i * 3], "%02X:", id[i]); + } + + data[length * 3 - 1] = '\0'; + return data; +} + +void write_data_row(token_info_t *info, int cols, ...) +{ + va_list ap; + int i, intval, type; + char *data; + + cols = cols*2; /* shut GCC up */ + va_start(ap, cols); + fprintf(info->log.fd, "\n\t["); + for (i = 1; i <= cols; i+=2) { + if (i > 1) + fprintf(info->log.fd, ","); + type = va_arg(ap, int); + if (type == 'd') { + intval = va_arg(ap, int); + fprintf(info->log.fd, "\n\t\t\"%d\"", intval); + } else if (type == 's') { + data = va_arg(ap, char*); + fprintf(info->log.fd, "\n\t\t\"%s\"", data); + } + } + fprintf(info->log.fd, "\n\t]"); + va_end(ap); +} + +int is_pss_mechanism(CK_MECHANISM_TYPE mech) +{ + return (mech == CKM_RSA_PKCS_PSS || + mech == CKM_SHA1_RSA_PKCS_PSS || + mech == CKM_SHA256_RSA_PKCS_PSS || + mech == CKM_SHA384_RSA_PKCS_PSS || + mech == CKM_SHA512_RSA_PKCS_PSS || + mech == CKM_SHA224_RSA_PKCS_PSS || + mech == CKM_SHA3_256_RSA_PKCS_PSS || + mech == CKM_SHA3_384_RSA_PKCS_PSS || + mech == CKM_SHA3_512_RSA_PKCS_PSS || + mech == CKM_SHA3_224_RSA_PKCS_PSS); +} + +CK_RV +destroy_tmp_object(token_info_t *info, CK_OBJECT_HANDLE h) +{ + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + return fp->C_DestroyObject(info->session_handle, h); +} diff --git a/src/tests/p11test/p11test_case_common.h b/src/tests/p11test/p11test_case_common.h new file mode 100644 index 0000000000..5681742abd --- /dev/null +++ b/src/tests/p11test/p11test_case_common.h @@ -0,0 +1,149 @@ +/* + * p11test_case_common.h: Functions shared between test cases. + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifndef P11TEST_CASE_COMMON_H +#define P11TEST_CASE_COMMON_H + +#include +#include +#include +#include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# include +# include +#endif +#if OPENSSL_VERSION_NUMBER >= 0x30500000L +# include +#endif +#include "p11test_common.h" + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +typedef struct { + unsigned char *key_id; + CK_ULONG key_id_size; + char *id_str; + X509 *x509; + int type; + EVP_PKEY *key; + CK_OBJECT_HANDLE private_handle; + CK_OBJECT_HANDLE public_handle; + CK_BBOOL sign; + CK_BBOOL decrypt; + CK_BBOOL verify; + CK_BBOOL encrypt; + CK_BBOOL wrap; + CK_BBOOL unwrap; + CK_BBOOL derive_priv; + CK_BBOOL derive_pub; + CK_KEY_TYPE key_type; + CK_BBOOL always_auth; + CK_BBOOL extractable; + CK_BBOOL decapsulate; + CK_BBOOL encapsulate; + char *label; + CK_ULONG bits; + char *value; + CK_ULONG parameter_set; + int verify_public; + test_mech_t mechs[MAX_PSS_MECHS]; + size_t num_mechs; +} test_cert_t; + +typedef struct { + unsigned int alloc_count; + unsigned int count; + test_cert_t *data; +} test_certs_t; + +void test_certs_init(test_certs_t *objects); + +void always_authenticate(test_cert_t *o, token_info_t *info); + +int search_objects(test_certs_t *objects, token_info_t *info, + CK_ATTRIBUTE filter[], long filter_size, CK_ATTRIBUTE template[], long template_size, + int (*callback)(test_certs_t *, CK_ATTRIBUTE[], unsigned long, CK_OBJECT_HANDLE)); +void search_for_all_objects(test_certs_t *objects, token_info_t *info); +void clean_all_objects(test_certs_t *objects); + +const char *get_mechanism_name(unsigned long mech_id); +const char *get_mgf_name(unsigned long mech_id); +const char *get_mechanism_flag_name(unsigned long flag_id); +const char *get_mechanism_all_flag_name(unsigned long flag_id); +const char *get_key_type(test_cert_t *key); +char *convert_byte_string(unsigned char *id, unsigned long length); + +int is_pss_mechanism(CK_MECHANISM_TYPE mech); + +CK_RV destroy_tmp_object(token_info_t *info, CK_OBJECT_HANDLE o); + +// TODO sanitize inputs + +#define P11TEST_START(info) if (info->log.fd) { \ + if (info->log.in_test) \ + fprintf(info->log.fd, ",\n\t\"result\": \"unknown\"\n}"); \ + fprintf(info->log.fd, "%s\n{\n\t\"test_id\": \"%s\"", \ + info->log.first ? "" : ",", __func__); \ + info->log.in_test = 1; \ + info->log.first = 0; \ + info->log.in_data = 0; \ + } else {} + +#define _P11TEST_FINALIZE(info, result) if (info->log.fd) {\ + if (info->log.in_data) {\ + fprintf(info->log.fd, "]"); \ + } \ + if (info->log.in_test) { \ + fprintf(info->log.fd, ",\n\t\"result\": \"" result "\"\n}"); \ + info->log.in_test = 0; \ + } \ + } else {} + +#define P11TEST_SKIP(info) do { _P11TEST_FINALIZE(info, "skip"); skip(); return; } while(0); + +#define P11TEST_PASS(info) do { _P11TEST_FINALIZE(info, "pass"); } while(0); + +#define P11TEST_FAIL(info, msg, ...) do { \ + if (info->log.fd && info->log.in_test) { \ + fprintf(info->log.fd, ",\n\t\"fail_reason\": \"" msg "\"", ##__VA_ARGS__); \ + } \ + _P11TEST_FINALIZE(info, "fail") \ + fail_msg(msg, ##__VA_ARGS__); \ + exit(1); \ + } while (0); + +#define P11TEST_DATA_ROW(info, cols, ...) if (info->log.fd) { \ + if (info->log.in_test == 0) {\ + fail_msg("Can't add data outside of the test");\ + exit(1); \ + } \ + if (info->log.in_data == 0) {\ + fprintf(info->log.fd, ",\n\t\"data\": [");\ + info->log.in_data = 1;\ + } else { \ + fprintf(info->log.fd, ",");\ + } \ + write_data_row(info, cols, ##__VA_ARGS__); \ + } else {} + +void write_data_row(token_info_t *info, int cols, ...); + +#endif /* P11TEST_CASE_COMMON_H */ diff --git a/src/tests/p11test/p11test_case_ec_derive.c b/src/tests/p11test/p11test_case_ec_derive.c new file mode 100644 index 0000000000..6d0e0dbcd3 --- /dev/null +++ b/src/tests/p11test/p11test_case_ec_derive.c @@ -0,0 +1,483 @@ +/* + * p11test_case_ec_derive.c: Check the functionality of derive mechanisms + * + * Copyright (C) 2019 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ +#include "p11test_case_ec_derive.h" +#include "libopensc/internal.h" + +size_t +pkcs11_derive(test_cert_t *o, token_info_t * info, + unsigned char *pub, size_t pub_len, test_mech_t *mech, unsigned char **secret) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_ECDH1_DERIVE_PARAMS params = {CKD_NULL, 0, NULL_PTR, 0, NULL_PTR}; + CK_MECHANISM mechanism = { mech->mech, NULL_PTR, 0 }; + CK_OBJECT_HANDLE newkey; + CK_OBJECT_CLASS newkey_class = CKO_SECRET_KEY; + CK_KEY_TYPE newkey_type = CKK_GENERIC_SECRET; + CK_ULONG newkey_len = BYTES4BITS(o->bits); + CK_BYTE newkey_id[] = {0x00, 0xff, 0x31}; + CK_BYTE newkey_label[] = {"Derived key"}; + CK_BBOOL _true = TRUE; + CK_BBOOL _false = FALSE; + CK_ATTRIBUTE template[] = { + {CKA_TOKEN, &_false, sizeof(_false)}, /* session only object */ + {CKA_CLASS, &newkey_class, sizeof(newkey_class)}, + {CKA_ID, &newkey_id, sizeof(newkey_id)}, + {CKA_LABEL, &newkey_label, sizeof(newkey_label)}, + {CKA_KEY_TYPE, &newkey_type, sizeof(newkey_type)}, + {CKA_VALUE_LEN, &newkey_len, sizeof(newkey_len)}, + {CKA_SENSITIVE, &_false, sizeof(_false)}, + {CKA_EXTRACTABLE, &_true, sizeof(_true)}, + {CKA_ENCRYPT, &_true, sizeof(_true)}, + {CKA_DECRYPT, &_true, sizeof(_true)}, + {CKA_WRAP, &_true, sizeof(_true)}, + {CKA_UNWRAP, &_true, sizeof(_true)}, + }; + CK_ATTRIBUTE get_value = {CKA_VALUE, NULL_PTR, 0}; + CK_ULONG template_len = 10; + + params.pSharedData = NULL; + params.ulSharedDataLen = 0; + params.pPublicData = pub; + params.ulPublicDataLen = pub_len; + + mechanism.pParameter = ¶ms; + mechanism.ulParameterLen = sizeof(CK_ECDH1_DERIVE_PARAMS); + + rv = fp->C_DeriveKey(info->session_handle, &mechanism, o->private_handle, + template, template_len, &newkey); + if (rv != CKR_OK) { + debug_print(" C_DeriveKey: rv = 0x%.8lX\n", rv); + return 0; + } + + /* Lets read the derived data now */ + rv = fp->C_GetAttributeValue(info->session_handle, newkey, + &get_value, 1); + if (rv != CKR_OK) { + fail_msg("C_GetAttributeValue: rv = 0x%.8lX\n", rv); + destroy_tmp_object(info, newkey); + return 0; + } + + get_value.pValue = malloc(get_value.ulValueLen); + if (get_value.pValue == NULL) { + fail_msg("malloc failed"); + destroy_tmp_object(info, newkey); + return 0; + } + + rv = fp->C_GetAttributeValue(info->session_handle, newkey, + &get_value, 1); + destroy_tmp_object(info, newkey); + if (rv != CKR_OK) { + fail_msg("C_GetAttributeValue: rv = 0x%.8lX\n", rv); + return 0; + } + + *secret = get_value.pValue; + return get_value.ulValueLen; +} + +int +test_derive_montgomery(test_cert_t *o, token_info_t *info, test_mech_t *mech) +{ +#ifdef EVP_PKEY_X25519 + unsigned char *secret = NULL, *pkcs11_secret = NULL; + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *pkey = NULL; /* This is peer key */ + unsigned char *pub = NULL; + size_t pub_len = 0, secret_len = o->bits / 8, pkcs11_secret_len = 0; + int rc; + + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", o->id_str); + return 1; + } + + switch (o->type) { + case EVP_PKEY_X25519: +#ifdef EVP_PKEY_X448 + case EVP_PKEY_X448: +#endif + /* OK */ + break; + default: + debug_print(" [ KEY %s ] Skip non-montgomery key for derive", o->id_str); + return 1; + } + + /* First, we need to generate our key */ + pctx = EVP_PKEY_CTX_new_id(o->type, NULL); + if (pctx == NULL) { + debug_print(" [ KEY %s ] EVP_PKEY_CTX_new_id failed", o->id_str); + return 1; + } + + rc = EVP_PKEY_keygen_init(pctx); + if (rc != 1) { + debug_print(" [ KEY %s ] EVP_PKEY_keygen_init failed", o->id_str); + EVP_PKEY_CTX_free(pctx); + return 1; + } + + rc = EVP_PKEY_keygen(pctx, &pkey); + EVP_PKEY_CTX_free(pctx); + if (rc != 1) { + debug_print(" [ KEY %s ] EVP_PKEY_keygen failed", o->id_str); + return 1; + } + + /* Just here we start with key derivation in OpenSSL */ + pctx = EVP_PKEY_CTX_new(pkey, NULL); + if (pctx == NULL) { + debug_print(" [ KEY %s ] EVP_PKEY_CTX_new failed", o->id_str); + EVP_PKEY_free(pkey); + return 1; + } + + rc = EVP_PKEY_derive_init(pctx); + if (rc != 1) { + debug_print(" [ KEY %s ] EVP_PKEY_derive_init failed", o->id_str); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + return 1; + } + + rc = EVP_PKEY_derive_set_peer(pctx, o->key); + if (rc != 1) { + debug_print(" [ KEY %s ] EVP_PKEY_derive_set_peer failed", o->id_str); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + return 1; + } + + /* Allocate the memory for the shared secret */ + if ((secret = malloc(secret_len)) == NULL) { + debug_print(" [ KEY %s ] Failed to allocate memory for secret", o->id_str); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + return 1; + } + + rc = EVP_PKEY_derive(pctx, secret, &secret_len); + EVP_PKEY_CTX_free(pctx); + if (rc != 1) { + debug_print(" [ KEY %s ] EVP_PKEY_derive failed", o->id_str); + EVP_PKEY_free(pkey); + free(secret); + return 1; + } + + /* Try to do the same with the card key */ + rc = EVP_PKEY_get_raw_public_key(pkey, pub, &pub_len); + if (rc != 1) { + debug_print(" [ KEY %s ] EVP_PKEY_get_raw_public_key failed", o->id_str); + EVP_PKEY_free(pkey); + free(secret); + return 1; + } + pub = malloc(pub_len); + if (pub == NULL) { + debug_print(" [ KEY %s ] failed to allocate memory", o->id_str); + EVP_PKEY_free(pkey); + free(secret); + return 1; + } + rc = EVP_PKEY_get_raw_public_key(pkey, pub, &pub_len); + if (rc != 1) { + debug_print(" [ KEY %s ] EVP_PKEY_get_raw_public_key failed", o->id_str); + free(pub); + EVP_PKEY_free(pkey); + free(secret); + return 1; + } + + pkcs11_secret_len = pkcs11_derive(o, info, pub, pub_len, mech, &pkcs11_secret); + + if (secret_len == pkcs11_secret_len && memcmp(secret, pkcs11_secret, secret_len) == 0) { + mech->result_flags |= FLAGS_DERIVE; + debug_print(" [ OK %s ] Derived secrets match", o->id_str); + free(pub); + EVP_PKEY_free(pkey); + free(secret); + free(pkcs11_secret); + return 0; + } + + debug_print(" [ KEY %s ] Derived secret does not match", o->id_str); + free(pub); + EVP_PKEY_free(pkey); + free(secret); + free(pkcs11_secret); + return 1; +#else + return 0; +#endif +} + +int test_derive(test_cert_t *o, token_info_t *info, test_mech_t *mech) +{ + unsigned char *secret = NULL, *pkcs11_secret = NULL; + unsigned char *pub = NULL; + size_t pub_len = 0, secret_len = 0, pkcs11_secret_len = 0; + int rv = 1; + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + int nid = 0; + EC_GROUP *group = NULL; + const EC_POINT *publickey = NULL; + EC_KEY *key = NULL; +#endif + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *evp_pkey = NULL; + + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", o->id_str); + return 1; + } + + if (o->type != EVP_PKEY_EC) { + debug_print(" [ KEY %s ] Skip non-EC key for derive", o->id_str); + return 1; + } + + debug_print(" [ KEY %s ] Trying EC derive using CKM_%s and %lu-bit key", + o->id_str, get_mechanism_name(mech->mech), o->bits); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if (o->bits == 256) + nid = NID_X9_62_prime256v1; + else if (o->bits == 384) + nid = NID_secp384r1; + else if (o->bits == 521) + nid = NID_secp521r1; + else { + debug_print(" [ KEY %s ] Skip key of unknown size", o->id_str); + return 1; + } +#endif + + /* Generate the peer private key */ + if ((pctx = EVP_PKEY_CTX_new(o->key, NULL)) == NULL) { + debug_print(" [ KEY %s ] EVP_PKEY_CTX_new_id failed", o->id_str); + return 1; + } + + if (EVP_PKEY_keygen_init(pctx) != 1) { + debug_print(" [ KEY %s ] EVP_PKEY_keygen_init failed", o->id_str); + EVP_PKEY_CTX_free(pctx); + return 1; + } + + if (EVP_PKEY_keygen(pctx, &evp_pkey) != 1) { + debug_print(" [ KEY %s ] EVP_PKEY_keygen failed", o->id_str); + EVP_PKEY_CTX_free(pctx); + return 1; + } + EVP_PKEY_CTX_free(pctx); + + /* Start with key derivation in OpenSSL*/ + pctx = EVP_PKEY_CTX_new(evp_pkey, NULL); + if (pctx == NULL || + EVP_PKEY_derive_init(pctx) != 1 || + EVP_PKEY_derive_set_peer(pctx, o->key) != 1) { + debug_print(" [ KEY %s ] Cannot derive key", o->id_str); + EVP_PKEY_free(evp_pkey); + return 1; + } + + /* Get buffer length */ + if (EVP_PKEY_derive(pctx, NULL, &secret_len) != 1) { + debug_print(" [ KEY %s ] EVP_PKEY_derive failed", o->id_str); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(evp_pkey); + return 1; + } + /* Allocate the memory for the shared secret */ + if ((secret = malloc(secret_len)) == NULL) { + debug_print(" [ KEY %s ] Failed to allocate memory for secret", o->id_str); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(evp_pkey); + return 1; + } + + if (EVP_PKEY_derive(pctx, secret, &secret_len) != 1) { + debug_print(" [ KEY %s ] EVP_PKEY_derive failed", o->id_str); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(evp_pkey); + free(secret); + return 1; + } + EVP_PKEY_CTX_free(pctx); + + /* Try to do the same with the card key */ + + /* Get length of pub */ +#if OPENSSL_VERSION_NUMBER < 0x30000000L + group = EC_GROUP_new_by_curve_name(nid); + key = EVP_PKEY_get0_EC_KEY(evp_pkey); + publickey = EC_KEY_get0_public_key(key); + + pub_len = EC_POINT_point2oct(group, publickey, + POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); +#else + EVP_PKEY_get_octet_string_param(evp_pkey, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0, &pub_len); +#endif + /* Allocate memory for public key*/ + if (pub_len == 0) { + debug_print(" [ KEY %s ] Failed to allocate memory for secret", o->id_str); + free(secret); + EVP_PKEY_free(evp_pkey); + return 1; + } + + pub = malloc(pub_len); + if (pub == NULL) { + debug_print(" [ OK %s ] Failed to allocate memory", o->id_str); + free(secret); + EVP_PKEY_free(evp_pkey); + return 1; + } +#if OPENSSL_VERSION_NUMBER < 0x30000000L + pub_len = EC_POINT_point2oct(group, publickey, + POINT_CONVERSION_UNCOMPRESSED, pub, pub_len, NULL); + EC_GROUP_free(group); + if (pub_len == 0) { +#else + if (EVP_PKEY_get_octet_string_param(evp_pkey, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, pub, pub_len, NULL) != 1) { +#endif + debug_print(" [ KEY %s ] Cannot get public key", o->id_str); + EVP_PKEY_free(evp_pkey); + free(secret); + free(pub); + return 1; + } + EVP_PKEY_free(evp_pkey); + + pkcs11_secret_len = pkcs11_derive(o, info, pub, pub_len, mech, &pkcs11_secret); + + if (secret_len == pkcs11_secret_len && memcmp(secret, pkcs11_secret, secret_len) == 0) { + mech->result_flags |= FLAGS_DERIVE; + debug_print(" [ OK %s ] Derived secrets match", o->id_str); + rv = 0; + } else { + debug_print(" [ KEY %s ] Derived secret does not match", o->id_str); + } + + free(pub); + free(secret); + free(pkcs11_secret); + return rv; +} + + +void derive_tests(void **state) { + unsigned int i; + size_t j; + int errors = 0; + token_info_t *info = (token_info_t *) *state; + test_certs_t objects; + + test_certs_init(&objects); + + P11TEST_START(info); + search_for_all_objects(&objects, info); + + debug_print("Check if the key derivation works.\n"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + /* Ignore if there is missing private key */ + if (o->private_handle == CK_INVALID_HANDLE) { + continue; + } + + for (j = 0; j < o->num_mechs; j++) { + if ((o->mechs[j].usage_flags & CKF_DERIVE) == 0 || !o->derive_priv) { + continue; + } + + switch (o->key_type) { + case CKK_EC: + errors += test_derive(o, info, &(o->mechs[j])); + break; + case CKK_EC_MONTGOMERY: + errors += test_derive_montgomery(o, info, &(o->mechs[j])); + break; + default: + /* Other keys do not support derivation */ + break; + } + } + } + + /* print summary */ + printf("[KEY ID] [LABEL]\n"); + printf("[ TYPE ] [ SIZE ] [ PUBLIC ] [ DERIVE ]\n"); + P11TEST_DATA_ROW(info, 3, + 's', "KEY ID", + 's', "MECHANISM", + 's', "DERIVE WORKS"); + for (i = 0; i < objects.count; i++) { + if (objects.data[i].key_type != CKK_EC && + objects.data[i].key_type != CKK_EC_MONTGOMERY) + continue; + + test_cert_t *o = &objects.data[i]; + printf("\n[%-6s] [%s]\n", o->id_str, o->label); + printf("[%s] [%6lu] [ %s ] [ %s%s ]\n", + (o->key_type == CKK_EC ? "ECDSA " : + o->key_type == CKK_EC_MONTGOMERY ? "XEDDSA" : " ?? "), + o->bits, + o->verify_public == 1 ? " ./ " : " ", + o->derive_pub ? "[./]" : "[ ]", + o->derive_priv ? "[./]" : "[ ]"); + if (!o->derive_pub && !o->derive_priv) { + printf(" no usable attributes found ... ignored\n"); + continue; + } + if (objects.data[i].private_handle == CK_INVALID_HANDLE) { + continue; + } + for (j = 0; j < o->num_mechs; j++) { + test_mech_t *mech = &o->mechs[j]; + if ((mech->usage_flags & CKF_DERIVE) == 0) { + /* not applicable mechanisms are skipped */ + continue; + } + printf(" [ %-22s ] [ %s ]\n", + get_mechanism_name(mech->mech), + mech->result_flags & FLAGS_DERIVE ? "[./]" : " "); + if ((mech->result_flags & FLAGS_DERIVE) == 0) + continue; /* skip empty rows for export */ + P11TEST_DATA_ROW(info, 3, + 's', o->id_str, + 's', get_mechanism_name(mech->mech), + 's', mech->result_flags & FLAGS_DERIVE ? "YES" : ""); + } + } + printf(" Public == Cert -----^ ^\n"); + printf(" ECDH Derive functionality -------'\n"); + + clean_all_objects(&objects); + if (errors > 0) + P11TEST_FAIL(info, "Not all the derive mechanisms worked."); + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_ec_derive.h b/src/tests/p11test/p11test_case_ec_derive.h new file mode 100644 index 0000000000..ea21cee969 --- /dev/null +++ b/src/tests/p11test/p11test_case_ec_derive.h @@ -0,0 +1,26 @@ +/* + * p11test_case_ec_derive.h: Check the functionality of derive mechanisms + * + * Copyright (C) 2019 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_common.h" +#include "p11test_case_readonly.h" + +void derive_tests(void **state); + diff --git a/src/tests/p11test/p11test_case_ec_sign.c b/src/tests/p11test/p11test_case_ec_sign.c new file mode 100644 index 0000000000..3d0139ffab --- /dev/null +++ b/src/tests/p11test/p11test_case_ec_sign.c @@ -0,0 +1,90 @@ +/* + * p11test_case_ec_sign.c: Test different data lengths for EC signatures + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ +#include "p11test_case_ec_sign.h" +#include "libopensc/internal.h" + +void ec_sign_size_test(void **state) { + unsigned int i; + unsigned long min, max, l; + int inc, errors = 0, rv; + size_t j; + token_info_t *info = (token_info_t *) *state; + test_certs_t objects; + + test_certs_init(&objects); + + P11TEST_START(info); + if (token.num_ec_mechs == 0 && token.num_ed_mechs == 0) { + fprintf(stderr, "Token does not support any ECC signature mechanisms. Skipping.\n"); + P11TEST_SKIP(info); + } + + search_for_all_objects(&objects, info); + + debug_print("\nCheck functionality of Sign&Verify on different data lengths"); + for (i = 0; i < objects.count; i++) { + unsigned long curve_len = 0; + switch (objects.data[i].key_type) { + case CKK_EC: + /* This tests just couple of sizes around the curve length + * to verify they are properly truncated on input */ + curve_len = BYTES4BITS(objects.data[i].bits); + min = curve_len - 2; + max = curve_len + 2; + inc = 1; + break; + case CKK_EC_EDWARDS: + /* Tests larger inputs for EdDSA. Previously, we had hardcoded limit of 512 + * https://github.com/OpenSC/OpenSC/issues/2300 */ + min = 128; + max = 1024; + inc = 128; + break; + default: + continue; + } + + // sanity: Test all mechanisms + if (objects.data[i].sign && objects.data[i].verify) { + for (j = 0; j < objects.data[i].num_mechs; j++) { + test_mech_t *m = &(objects.data[i].mechs[j]); + if ((m->usage_flags & CKF_SIGN) == 0) { + /* Skip non-signature mechanisms (for example derive ones) */ + continue; + } + for (l = min; l < max; l += inc) { + /* Skip inputs not matching digest sizes for raw ECDSA as the card + * will likely reject them as not valid hash outputs */ + if (m->mech == CKM_ECDSA && (l != 20 && l != 28 && l != 32 && l != 48 && l != 64)) + continue; + rv = sign_verify_test(&(objects.data[i]), info, m, l, 0); + if (rv == -1) + errors++; + } + } + } + } + clean_all_objects(&objects); + + if (errors > 0) + P11TEST_FAIL(info, "Some signatures were not verified successfully. Please review the log"); + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_ec_sign.h b/src/tests/p11test/p11test_case_ec_sign.h new file mode 100644 index 0000000000..299a92f25d --- /dev/null +++ b/src/tests/p11test/p11test_case_ec_sign.h @@ -0,0 +1,25 @@ +/* + * p11test_case_ec_sign.h: Test different data lengths for EC signatures + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_common.h" +#include "p11test_case_readonly.h" + +void ec_sign_size_test(void **state); diff --git a/src/tests/p11test/p11test_case_interface.c b/src/tests/p11test/p11test_case_interface.c new file mode 100644 index 0000000000..97a969182e --- /dev/null +++ b/src/tests/p11test/p11test_case_interface.c @@ -0,0 +1,139 @@ +/* + * p11test_case_interface.c: Test new PKCS #11 3.0 interface + * + * Copyright (C) 2020 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_interface.h" +#include + +extern void *pkcs11_so; + +void interface_test(void **state) +{ + token_info_t *info = (token_info_t *) *state; + CK_RV (*C_GetInterfaceList)(CK_INTERFACE_PTR, CK_ULONG_PTR) = NULL; + CK_RV (*C_GetInterface)(CK_UTF8CHAR_PTR, CK_VERSION_PTR, CK_INTERFACE_PTR_PTR, CK_FLAGS) = NULL; + CK_RV rv; + CK_ULONG count = 0; + CK_INTERFACE *interfaces = NULL; + CK_INTERFACE_PTR interface; + CK_VERSION version; + unsigned int i; + + P11TEST_START(info); + + C_GetInterfaceList = (CK_RV (*)(CK_INTERFACE_PTR, CK_ULONG_PTR)) dlsym(pkcs11_so, "C_GetInterfaceList"); + if (C_GetInterfaceList == NULL) { + /* If the library does not have this function, it is probably not PKCS #11 3.0 */ + P11TEST_SKIP(info); + } + /* If we have C_GetInterfaceList, we should have also C_GetInterface */ + C_GetInterface = (CK_RV (*)(CK_UTF8CHAR_PTR, CK_VERSION_PTR, CK_INTERFACE_PTR_PTR, CK_FLAGS)) + dlsym(pkcs11_so, "C_GetInterface"); + assert_non_null(C_GetInterface); + + /* Invalid arguments */ + rv = C_GetInterfaceList(NULL, NULL); + assert_int_equal(rv, CKR_ARGUMENTS_BAD); + + /* Get the count of interfaces */ + rv = C_GetInterfaceList(NULL, &count); + assert_int_equal(rv, CKR_OK); + /* Assume at least one interface here. Generally, OpenSC will provide two, 3.0 and 2.x */ + assert_true(count > 0); + + interfaces = malloc(count * sizeof(CK_INTERFACE)); + assert_non_null(interfaces); + + /* Now get the actual interfaces */ + rv = C_GetInterfaceList(interfaces, &count); + assert_int_equal(rv, CKR_OK); + for (i = 0; i < count; i++) { + printf("interface '%s' version %d.%d funcs %p flags 0x%lu\n", + interfaces[i].pInterfaceName, + ((CK_VERSION *)interfaces[i].pFunctionList)->major, + ((CK_VERSION *)interfaces[i].pFunctionList)->minor, + interfaces[i].pFunctionList, + interfaces[i].flags); + } + /* run the rest only if we have 2 interfaces (assume OpenSC) */ + if (count == 2) { + CK_VERSION version2; + assert_string_equal((char *)interfaces[0].pInterfaceName, "PKCS 11"); + assert_int_equal(((CK_VERSION *)interfaces[0].pFunctionList)->major, 3); + assert_int_equal(((CK_VERSION *)interfaces[0].pFunctionList)->minor, 0); + assert_int_equal(interfaces[0].flags, 0); + assert_string_equal((char *)interfaces[1].pInterfaceName, "PKCS 11"); + assert_int_equal(((CK_VERSION *)interfaces[1].pFunctionList)->major, 2); + // assert_int_equal(((CK_VERSION *)interfaces[1].pFunctionList)->minor, 20); + assert_int_equal(interfaces[1].flags, 0); + version2 = *(CK_VERSION *)interfaces[1].pFunctionList; + + /* GetInterface with NULL name should give us default PKCS 11 one */ + rv = C_GetInterface(NULL, NULL, &interface, 0); + assert_int_equal(rv, CKR_OK); + assert_string_equal((char *)interface->pInterfaceName, "PKCS 11"); + assert_int_equal(((CK_VERSION *)interface->pFunctionList)->major, 3); + assert_int_equal(((CK_VERSION *)interface->pFunctionList)->minor, 0); + assert_int_equal(interface->flags, 0); + /* The function list should be the same */ + assert_ptr_equal(interfaces[0].pFunctionList, interface->pFunctionList); + + /* GetInterface with explicit 3.0 version */ + version.major = 3; + version.minor = 0; + rv = C_GetInterface((CK_UTF8CHAR_PTR) "PKCS 11", &version, &interface, 0); + assert_int_equal(rv, CKR_OK); + assert_string_equal((char *)interface->pInterfaceName, "PKCS 11"); + assert_int_equal(((CK_VERSION *)interface->pFunctionList)->major, 3); + assert_int_equal(((CK_VERSION *)interface->pFunctionList)->minor, 0); + assert_int_equal(interface->flags, 0); + /* The function list should be the same */ + assert_ptr_equal(interfaces[0].pFunctionList, interface->pFunctionList); + + /* GetInterface the other interface (with explicit 2.x version) */ + version.major = 2; /* assumed 2 */ + version.minor = version2.minor; + rv = C_GetInterface((CK_UTF8CHAR_PTR) "PKCS 11", &version, &interface, 0); + assert_int_equal(rv, CKR_OK); + assert_string_equal((char *)interface->pInterfaceName, "PKCS 11"); + assert_int_equal(((CK_VERSION *)interface->pFunctionList)->major, 2); + // assert_int_equal(((CK_VERSION *)interface->pFunctionList)->minor, 20); + assert_int_equal(interface->flags, 0); + /* The function list should be the same here too */ + assert_ptr_equal(interfaces[1].pFunctionList, interface->pFunctionList); + } + + /* GetInterface with unknown interface */ + rv = C_GetInterface((CK_UTF8CHAR_PTR) "PKCS 11 other", NULL, &interface, 0); + assert_int_equal(rv, CKR_ARGUMENTS_BAD); + + /* GetInterface with wrong version */ + version.major = 4; + version.minor = 50; + rv = C_GetInterface((CK_UTF8CHAR_PTR) "PKCS 11", &version, &interface, 0); + assert_int_equal(rv, CKR_ARGUMENTS_BAD); + + /* GetInterface with unknown flags */ + rv = C_GetInterface((CK_UTF8CHAR_PTR) "PKCS 11", NULL, &interface, 2); + assert_int_equal(rv, CKR_ARGUMENTS_BAD); + free(interfaces); + + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_interface.h b/src/tests/p11test/p11test_case_interface.h new file mode 100644 index 0000000000..59ebe1f461 --- /dev/null +++ b/src/tests/p11test/p11test_case_interface.h @@ -0,0 +1,25 @@ +/* + * p11test_case_interface.h: Test new PKCS #11 3.0 interface + * + * Copyright (C) 2020 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_common.h" + +void interface_test(void **state); + diff --git a/src/tests/p11test/p11test_case_mechs.c b/src/tests/p11test/p11test_case_mechs.c new file mode 100644 index 0000000000..b0d084daef --- /dev/null +++ b/src/tests/p11test/p11test_case_mechs.c @@ -0,0 +1,270 @@ +/* + * p11test_case_mechs.c: Check mechanisms supported by token + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_mechs.h" + +/* + * insert mechanism name with flags to the list of test mechanisms + * again, sort them by value to keep JSON format same as different softhsm + * versions return the mechanisms in different order */ +void +insert_mechanism(test_mech_t *mech_list, size_t *mech_list_len, CK_MECHANISM_TYPE mechanism, CK_MECHANISM_INFO info) +{ + size_t i; + + for (i = 0; i < *mech_list_len; i++) { + if (mechanism <= mech_list[i].mech) { + break; + } + } + if (i < *mech_list_len) { + memmove(&mech_list[i + 1], &mech_list[i], (*mech_list_len - i) * sizeof(test_mech_t)); + } + *mech_list_len = *mech_list_len + 1; + + mech_list[i].mech = mechanism; + mech_list[i].usage_flags = info.flags; +} + +void +supported_mechanisms_test(void **state) +{ + token_info_t *info = (token_info_t *) *state; + CK_FUNCTION_LIST_PTR function_pointer = info->function_pointer; + + CK_RV rv; + CK_ULONG mechanism_count, i; + CK_MECHANISM_TYPE_PTR mechanism_list; + CK_MECHANISM_INFO_PTR mechanism_info; + + P11TEST_START(info); + rv = function_pointer->C_GetMechanismList(info->slot_id, NULL_PTR, + &mechanism_count); + if ((rv == CKR_OK) && (mechanism_count > 0)) { + mechanism_list = (CK_MECHANISM_TYPE_PTR) + malloc(mechanism_count * sizeof(CK_MECHANISM_TYPE)); + rv = function_pointer->C_GetMechanismList(info->slot_id, + mechanism_list, &mechanism_count); + if (rv != CKR_OK) { + free(mechanism_list); + function_pointer->C_Finalize(NULL_PTR); + P11TEST_FAIL(info, "Could not get mechanism list!"); + } + + mechanism_info = (CK_MECHANISM_INFO_PTR) + malloc(mechanism_count * sizeof(CK_MECHANISM_INFO)); + if (mechanism_info == NULL) + P11TEST_FAIL(info, "Couldn't malloc()"); + + for (i = 0; i < mechanism_count; i++) { + CK_MECHANISM_TYPE mechanism_type = mechanism_list[i]; + rv = function_pointer->C_GetMechanismInfo(info->slot_id, + mechanism_type, &mechanism_info[i]); + if (rv != CKR_OK) + continue; + + /* store mechanisms list for later tests */ + + /* List all known RSA mechanisms */ + if (mechanism_list[i] == CKM_RSA_X_509 || + mechanism_list[i] == CKM_RSA_PKCS || + mechanism_list[i] == CKM_MD5_RSA_PKCS || + mechanism_list[i] == CKM_RIPEMD160_RSA_PKCS || + mechanism_list[i] == CKM_SHA1_RSA_PKCS || + mechanism_list[i] == CKM_SHA224_RSA_PKCS || + mechanism_list[i] == CKM_SHA256_RSA_PKCS || + mechanism_list[i] == CKM_SHA384_RSA_PKCS || + mechanism_list[i] == CKM_SHA512_RSA_PKCS || + mechanism_list[i] == CKM_SHA3_224_RSA_PKCS || + mechanism_list[i] == CKM_SHA3_256_RSA_PKCS || + mechanism_list[i] == CKM_SHA3_384_RSA_PKCS || + mechanism_list[i] == CKM_SHA3_512_RSA_PKCS || + mechanism_list[i] == CKM_RSA_PKCS_PSS || + mechanism_list[i] == CKM_SHA1_RSA_PKCS_PSS || + mechanism_list[i] == CKM_SHA256_RSA_PKCS_PSS || + mechanism_list[i] == CKM_SHA384_RSA_PKCS_PSS || + mechanism_list[i] == CKM_SHA512_RSA_PKCS_PSS || + mechanism_list[i] == CKM_SHA224_RSA_PKCS_PSS || + mechanism_list[i] == CKM_SHA3_224_RSA_PKCS_PSS || + mechanism_list[i] == CKM_SHA3_256_RSA_PKCS_PSS || + mechanism_list[i] == CKM_SHA3_384_RSA_PKCS_PSS || + mechanism_list[i] == CKM_SHA3_512_RSA_PKCS_PSS || + mechanism_list[i] == CKM_RSA_PKCS_OAEP) { + if (token.num_rsa_mechs < MAX_MECHS) { + insert_mechanism(token.rsa_mechs, &token.num_rsa_mechs, + mechanism_list[i], mechanism_info[i]); + } else + P11TEST_FAIL(info, "Too many RSA mechanisms (%d)", MAX_MECHS); + } + + /* We list all known EC mechanisms */ + if (mechanism_list[i] == CKM_ECDSA || + mechanism_list[i] == CKM_ECDSA_SHA1 || + mechanism_list[i] == CKM_ECDSA_SHA256 || + mechanism_list[i] == CKM_ECDSA_SHA384 || + mechanism_list[i] == CKM_ECDSA_SHA512 || + mechanism_list[i] == CKM_ECDSA_SHA3_224 || + mechanism_list[i] == CKM_ECDSA_SHA3_256 || + mechanism_list[i] == CKM_ECDSA_SHA3_384 || + mechanism_list[i] == CKM_ECDSA_SHA3_512 || + /* Including derive mechanisms */ + mechanism_list[i] == CKM_ECDH1_DERIVE || + mechanism_list[i] == CKM_ECDH1_COFACTOR_DERIVE || + mechanism_list[i] == CKM_ECMQV_DERIVE) { + if (token.num_ec_mechs < MAX_MECHS) { + insert_mechanism(token.ec_mechs, &token.num_ec_mechs, + mechanism_list[i], mechanism_info[i]); + } else + P11TEST_FAIL(info, "Too many EC mechanisms (%d)", MAX_MECHS); + } + + /* We list all known edwards EC curve mechanisms */ + if (mechanism_list[i] == CKM_EDDSA) { + if (token.num_ed_mechs < MAX_MECHS) { + insert_mechanism(token.ed_mechs, &token.num_ed_mechs, + mechanism_list[i], mechanism_info[i]); + } else + P11TEST_FAIL(info, "Too many edwards EC mechanisms (%d)", MAX_MECHS); + } + + /* We list all known montgomery EC curve mechanisms */ + if (mechanism_list[i] == CKM_XEDDSA + || mechanism_list[i] == CKM_ECDH1_DERIVE) { + if (token.num_montgomery_mechs < MAX_MECHS) { + insert_mechanism(token.montgomery_mechs, &token.num_montgomery_mechs, + mechanism_list[i], mechanism_info[i]); + } else + P11TEST_FAIL(info, "Too many montgomery EC mechanisms (%d)", MAX_MECHS); + } + + /* We list all known ML-DSA mechanisms */ + if (mechanism_list[i] == CKM_ML_DSA || + mechanism_list[i] == CKM_HASH_ML_DSA || + mechanism_list[i] == CKM_HASH_ML_DSA_SHA224 || + mechanism_list[i] == CKM_HASH_ML_DSA_SHA256 || + mechanism_list[i] == CKM_HASH_ML_DSA_SHA384 || + mechanism_list[i] == CKM_HASH_ML_DSA_SHA512 || + mechanism_list[i] == CKM_HASH_ML_DSA_SHA3_224 || + mechanism_list[i] == CKM_HASH_ML_DSA_SHA3_256 || + mechanism_list[i] == CKM_HASH_ML_DSA_SHA3_384 || + mechanism_list[i] == CKM_HASH_ML_DSA_SHA3_512 || + mechanism_list[i] == CKM_HASH_ML_DSA_SHAKE128 || + mechanism_list[i] == CKM_HASH_ML_DSA_SHAKE256) { + if (token.num_ml_dsa_mechs < MAX_MECHS) { + insert_mechanism(token.ml_dsa_mechs, &token.num_ml_dsa_mechs, + mechanism_list[i], mechanism_info[i]); + } else + P11TEST_FAIL(info, "Too many ML-DSA mechanisms (%d)", MAX_MECHS); + } + + /* We list all known ML-KEM mechanisms */ + if (mechanism_list[i] == CKM_ML_KEM) { + if (token.num_ml_kem_mechs < MAX_MECHS) { + insert_mechanism(token.ml_kem_mechs, &token.num_ml_kem_mechs, + mechanism_list[i], mechanism_info[i]); + } else + P11TEST_FAIL(info, "Too many ML-KEM mechanisms (%d)", MAX_MECHS); + } + + /* We list all known SLH-DSA mechanisms */ + if (mechanism_list[i] == CKM_SLH_DSA || + mechanism_list[i] == CKM_HASH_SLH_DSA || + mechanism_list[i] == CKM_HASH_SLH_DSA_SHA224 || + mechanism_list[i] == CKM_HASH_SLH_DSA_SHA256 || + mechanism_list[i] == CKM_HASH_SLH_DSA_SHA384 || + mechanism_list[i] == CKM_HASH_SLH_DSA_SHA512 || + mechanism_list[i] == CKM_HASH_SLH_DSA_SHA3_224 || + mechanism_list[i] == CKM_HASH_SLH_DSA_SHA3_256 || + mechanism_list[i] == CKM_HASH_SLH_DSA_SHA3_384 || + mechanism_list[i] == CKM_HASH_SLH_DSA_SHA3_512 || + mechanism_list[i] == CKM_HASH_SLH_DSA_SHAKE128 || + mechanism_list[i] == CKM_HASH_SLH_DSA_SHAKE256) { + if (token.num_slh_dsa_mechs < MAX_MECHS) { + insert_mechanism(token.slh_dsa_mechs, &token.num_slh_dsa_mechs, + mechanism_list[i], mechanism_info[i]); + } else + P11TEST_FAIL(info, "Too many SLH-DSA mechanisms (%d)", MAX_MECHS); + } + + /* We list all known secret key mechanisms */ + if (mechanism_list[i] == CKM_AES_ECB || + mechanism_list[i] == CKM_AES_ECB_ENCRYPT_DATA || + mechanism_list[i] == CKM_AES_CBC || + mechanism_list[i] == CKM_AES_CBC_ENCRYPT_DATA || + mechanism_list[i] == CKM_AES_CBC_PAD || + mechanism_list[i] == CKM_AES_MAC || + mechanism_list[i] == CKM_AES_MAC_GENERAL || + mechanism_list[i] == CKM_AES_CFB64 || + mechanism_list[i] == CKM_AES_CFB8 || + mechanism_list[i] == CKM_AES_CFB128 || + mechanism_list[i] == CKM_AES_OFB || + mechanism_list[i] == CKM_AES_CTR || + mechanism_list[i] == CKM_AES_GCM || + mechanism_list[i] == CKM_AES_CCM || + mechanism_list[i] == CKM_AES_CTS || + mechanism_list[i] == CKM_AES_KEY_WRAP || + mechanism_list[i] == CKM_AES_KEY_WRAP_PAD || + mechanism_list[i] == CKM_AES_CMAC || + mechanism_list[i] == CKM_AES_CMAC_GENERAL || + mechanism_list[i] == CKM_AES_XCBC_MAC || + mechanism_list[i] == CKM_AES_XCBC_MAC_96) { + if (token.num_aes_mechs < MAX_MECHS) { + insert_mechanism(token.aes_mechs, &token.num_aes_mechs, + mechanism_list[i], mechanism_info[i]); + } else + P11TEST_FAIL(info, "Too many AES mechanisms (%d)", MAX_MECHS); + } + + if ((mechanism_info[i].flags & CKF_GENERATE_KEY_PAIR) != 0) { + if (token.num_keygen_mechs < MAX_MECHS) { + insert_mechanism(token.keygen_mechs, &token.num_keygen_mechs, + mechanism_list[i], mechanism_info[i]); + } else + P11TEST_FAIL(info, "Too many KEYGEN mechanisms (%d)", MAX_MECHS); + } + } + + printf("[ MECHANISM ] [ KEY SIZE ] [ FLAGS ]\n"); + printf("[ CKM_* ] [ MIN][ MAX ] [ ]\n"); + P11TEST_DATA_ROW(info, 4, + 's', "MECHANISM", + 's', "MIN KEY", + 's', "MAX KEY", + 's', "FLAGS"); + for (i = 0; i < mechanism_count; i++) { + printf("[%-21s] [%4lu][%5lu] [0x%.8lX]", + get_mechanism_name(mechanism_list[i]), + mechanism_info[i].ulMinKeySize, + mechanism_info[i].ulMaxKeySize, + mechanism_info[i].flags); + P11TEST_DATA_ROW(info, 4, + 's', get_mechanism_name(mechanism_list[i]), + 'd', mechanism_info[i].ulMinKeySize, + 'd', mechanism_info[i].ulMaxKeySize, + 's', get_mechanism_all_flag_name(mechanism_info[i].flags)); + printf(" %s\n", get_mechanism_all_flag_name(mechanism_info[i].flags)); + } + free(mechanism_list); + free(mechanism_info); + } + P11TEST_PASS(info); +} + diff --git a/src/tests/p11test/p11test_case_mechs.h b/src/tests/p11test/p11test_case_mechs.h new file mode 100644 index 0000000000..ff3439cd4a --- /dev/null +++ b/src/tests/p11test/p11test_case_mechs.h @@ -0,0 +1,24 @@ +/* + * p11test_case_mechs.h: Check mechanisms supported by token + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_common.h" + +void supported_mechanisms_test(void **state); diff --git a/src/tests/p11test/p11test_case_multipart.c b/src/tests/p11test/p11test_case_multipart.c new file mode 100644 index 0000000000..fdc8019a93 --- /dev/null +++ b/src/tests/p11test/p11test_case_multipart.c @@ -0,0 +1,133 @@ +/* + * p11test_case_multipart.c: Multipart Sign & Verify tests (RSA only) + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_multipart.h" + +void multipart_tests(void **state) { + + token_info_t *info = (token_info_t *) *state; + unsigned int i; + int used; + size_t j; + test_certs_t objects; + + test_certs_init(&objects); + + P11TEST_START(info); + search_for_all_objects(&objects, info); + + debug_print("\nCheck functionality of Multipart Sign&Verify"); + for (i = 0; i < objects.count; i++) { + if (objects.data[i].private_handle == CK_INVALID_HANDLE) { + debug_print(" [ SKIP %s ] Skip missing private key", + objects.data[i].id_str); + continue; + } + if (objects.data[i].type == EVP_PKEY_EC) { + debug_print(" [ SKIP %s ] EC keys do not support multi-part operations", + objects.data[i].id_str); + continue; + } + used = 0; + /* do the Sign&Verify */ + /* XXX some keys do not have appropriate flags, but we can use them + * or vice versa */ + //if (objects.data[i].sign && objects.data[i].verify) + for (j = 0; j < objects.data[i].num_mechs; j++) { + switch (objects.data[i].mechs[j].mech) { + case CKM_RSA_X_509: + case CKM_RSA_PKCS: + case CKM_RSA_PKCS_PSS: + /* these should not support multi-part operations */ + continue; + } + used |= sign_verify_test(&(objects.data[i]), info, + &(objects.data[i].mechs[j]), 32, 1); + } + + if (!used) { + debug_print(" [ WARN %s ] Private key with unknown purpose T:%02lX", + objects.data[i].id_str, objects.data[i].key_type); + } + } + + if (objects.count == 0) { + printf(" [WARN] No objects to display\n"); + return; + } + + /* print summary */ + printf("[KEY ID] [TYPE] [ SIZE ] [PUBLIC] [SIGN&VERIFY] [LABEL]\n"); + P11TEST_DATA_ROW(info, 3, + 's', "KEY ID", + 's', "MECHANISM", + 's', "MULTIPART SIGN&VERIFY WORKS"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + + if (o->key_type != CKK_RSA) + continue; + + printf("[%-6s] [%s] [%6lu] [ %s ] [%s%s] [%s]\n", + o->id_str, + "RSA ", + o->bits, + o->verify_public == 1 ? " ./ " : " ", + o->sign ? "[./] " : "[ ] ", + o->verify ? " [./] " : " [ ] ", + o->label); + if (o->private_handle == CK_INVALID_HANDLE) { + continue; + } + for (j = 0; j < o->num_mechs; j++) { + test_mech_t *mech = &o->mechs[j]; + + switch (mech->mech) { + case CKM_RSA_X_509: + case CKM_RSA_PKCS: + case CKM_RSA_PKCS_PSS: + /* these do not support multi-part operations */ + continue; + } + if ((mech->usage_flags & CKF_SIGN) == 0) { + /* not applicable mechanisms are skipped */ + continue; + } + printf(" [ %-20s ] [ %s ]\n", + get_mechanism_name(mech->mech), + mech->result_flags & FLAGS_SIGN_ANY ? "[./]" : " "); + if ((mech->result_flags & FLAGS_SIGN_ANY) == 0) + continue; /* do not export unknown and non-working algorithms */ + P11TEST_DATA_ROW(info, 3, + 's', o->id_str, + 's', get_mechanism_name(mech->mech), + 's', mech->result_flags & FLAGS_SIGN_ANY ? "YES" : ""); + } + printf("\n"); + } + printf(" Public == Cert ------------^ ^ ^ ^\n"); + printf(" Sign Attribute --------------------' | |\n"); + printf(" Sign&Verify functionality ------------' |\n"); + printf(" Verify Attribute ------------------------'\n"); + + clean_all_objects(&objects); + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_multipart.h b/src/tests/p11test/p11test_case_multipart.h new file mode 100644 index 0000000000..3ca6f97dd6 --- /dev/null +++ b/src/tests/p11test/p11test_case_multipart.h @@ -0,0 +1,26 @@ +/* + * p11test_case_multipart.h: Multipart Sign & Verify tests (RSA only) + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_common.h" +#include "p11test_case_readonly.h" + +void multipart_tests(void **state); + diff --git a/src/tests/p11test/p11test_case_pss_oaep.c b/src/tests/p11test/p11test_case_pss_oaep.c new file mode 100644 index 0000000000..2b91b9280a --- /dev/null +++ b/src/tests/p11test/p11test_case_pss_oaep.c @@ -0,0 +1,973 @@ +/* + * p11test_case_pss_oaep.c: RSA-PSS and RSA-OAEP tests + * + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_pss_oaep.h" +#include "libopensc/internal.h" + +#include +#include + +#define SHORT_MESSAGE_TO_SIGN "Simple message for signing & verifying. It needs to be little bit longer to fit also longer keys and allow the truncation.\n" +#define BUFFER_SIZE 4096 + +const unsigned char *global_message = (unsigned char *) SHORT_MESSAGE_TO_SIGN; +size_t global_message_length = sizeof(SHORT_MESSAGE_TO_SIGN); + +const CK_MECHANISM_TYPE * +get_oaep_mechanism_hashes(CK_MECHANISM_TYPE mech) +{ + static CK_MECHANISM_TYPE h[10]; + + switch (mech) { + case CKM_RSA_PKCS_OAEP: + h[0] = CKM_SHA_1; + h[1] = CKM_SHA224; + h[2] = CKM_SHA256; + h[3] = CKM_SHA384; + h[4] = CKM_SHA512; + h[5] = CKM_SHA3_224; + h[6] = CKM_SHA3_256; + h[7] = CKM_SHA3_384; + h[8] = CKM_SHA3_512; + h[9] = -1; + break; + + default: + h[0] = -1; + break; + } + + return h; +} +const CK_MECHANISM_TYPE * +get_pss_mechanism_hashes(CK_MECHANISM_TYPE mech) +{ + static CK_MECHANISM_TYPE h[10]; + + switch (mech) { + case CKM_RSA_PKCS_PSS: + h[0] = CKM_SHA_1; + h[1] = CKM_SHA224; + h[2] = CKM_SHA256; + h[3] = CKM_SHA384; + h[4] = CKM_SHA512; + h[5] = CKM_SHA3_224; + h[6] = CKM_SHA3_256; + h[7] = CKM_SHA3_384; + h[8] = CKM_SHA3_512; + h[9] = -1; + break; + + case CKM_SHA1_RSA_PKCS_PSS: + h[0] = CKM_SHA_1; + h[1] = -1; + break; + + case CKM_SHA224_RSA_PKCS_PSS: + h[0] = CKM_SHA224; + h[1] = -1; + break; + + case CKM_SHA256_RSA_PKCS_PSS: + h[0] = CKM_SHA256; + h[1] = -1; + break; + + case CKM_SHA384_RSA_PKCS_PSS: + h[0] = CKM_SHA384; + h[1] = -1; + break; + + case CKM_SHA512_RSA_PKCS_PSS: + h[0] = CKM_SHA512; + h[1] = -1; + break; + + case CKM_SHA3_224_RSA_PKCS_PSS: + h[0] = CKM_SHA3_224; + h[1] = -1; + break; + + case CKM_SHA3_256_RSA_PKCS_PSS: + h[0] = CKM_SHA3_256; + h[1] = -1; + break; + + case CKM_SHA3_384_RSA_PKCS_PSS: + h[0] = CKM_SHA3_384; + h[1] = -1; + break; + + case CKM_SHA3_512_RSA_PKCS_PSS: + h[0] = CKM_SHA3_512; + h[1] = -1; + break; + + default: + h[0] = -1; + break; + } + + return h; +} + +const CK_MECHANISM_TYPE * +get_mechanism_hashes(CK_MECHANISM_TYPE mech) +{ + if (mech == CKM_RSA_PKCS_OAEP) + return get_oaep_mechanism_hashes(mech); + else + return get_pss_mechanism_hashes(mech); +} + +const CK_RSA_PKCS_MGF_TYPE * +get_mgfs(void) +{ + static CK_RSA_PKCS_MGF_TYPE h[10]; + + h[0] = CKG_MGF1_SHA1; + h[1] = CKG_MGF1_SHA224; + h[2] = CKG_MGF1_SHA256; + h[3] = CKG_MGF1_SHA384; + h[4] = CKG_MGF1_SHA512; + h[5] = CKG_MGF1_SHA3_224; + h[6] = CKG_MGF1_SHA3_256; + h[7] = CKG_MGF1_SHA3_384; + h[8] = CKG_MGF1_SHA3_512; + h[9] = -1; + + return h; +} + +const EVP_MD *mgf_cryptoki_to_ossl(CK_RSA_PKCS_MGF_TYPE mgf) +{ + switch (mgf) { + case CKG_MGF1_SHA224: + return EVP_sha224(); + + case CKG_MGF1_SHA256: + return EVP_sha256(); + + case CKG_MGF1_SHA384: + return EVP_sha384(); + + case CKG_MGF1_SHA512: + return EVP_sha512(); + + case CKG_MGF1_SHA3_224: + return EVP_sha3_224(); + + case CKG_MGF1_SHA3_256: + return EVP_sha3_256(); + + case CKG_MGF1_SHA3_384: + return EVP_sha3_384(); + + case CKG_MGF1_SHA3_512: + return EVP_sha3_512(); + + case CKG_MGF1_SHA1: + default: + return EVP_sha1(); + + } +} + +const EVP_MD *md_cryptoki_to_ossl(CK_MECHANISM_TYPE hash) +{ + /* Digest mechanisms */ + switch (hash) { + case CKM_SHA224: + return EVP_sha224(); + + case CKM_SHA256: + return EVP_sha256(); + + case CKM_SHA384: + return EVP_sha384(); + + case CKM_SHA512: + return EVP_sha512(); + + case CKM_SHA3_224: + return EVP_sha3_224(); + + case CKM_SHA3_256: + return EVP_sha3_256(); + + case CKM_SHA3_384: + return EVP_sha3_384(); + + case CKM_SHA3_512: + return EVP_sha3_512(); + + case CKM_SHA_1: + default: + return EVP_sha1(); + + } +} + +size_t get_hash_length(CK_MECHANISM_TYPE mech) +{ + switch (mech) { + case CKM_SHA224: + return SHA224_DIGEST_LENGTH; + case CKM_SHA256: + return SHA256_DIGEST_LENGTH; + case CKM_SHA384: + return SHA384_DIGEST_LENGTH; + case CKM_SHA512: + return SHA512_DIGEST_LENGTH; + case CKM_SHA3_224: + return 224 / 8; + case CKM_SHA3_256: + return 256 / 8; + case CKM_SHA3_384: + return 384 / 8; + case CKM_SHA3_512: + return 512 / 8; + default: + case CKM_SHA_1: + return SHA_DIGEST_LENGTH; + } +} + +CK_BYTE *hash_message(const CK_BYTE *message, size_t message_length, + CK_MECHANISM_TYPE hash) +{ + CK_BYTE *out = NULL; + const EVP_MD *md = NULL; + size_t digest_len = 0; + + switch (hash) { + case CKM_SHA224: + digest_len = SHA224_DIGEST_LENGTH; + md = EVP_sha224(); + break; + + case CKM_SHA256: + digest_len = SHA256_DIGEST_LENGTH; + md = EVP_sha256(); + break; + + case CKM_SHA384: + digest_len = SHA384_DIGEST_LENGTH; + md = EVP_sha384(); + break; + + case CKM_SHA512: + digest_len = SHA512_DIGEST_LENGTH; + md = EVP_sha512(); + break; + + case CKM_SHA3_224: + digest_len = 224 / 8; + md = EVP_sha3_224(); + break; + + case CKM_SHA3_256: + digest_len = 256 / 8; + md = EVP_sha3_256(); + break; + + case CKM_SHA3_384: + digest_len = 384 / 8; + md = EVP_sha3_384(); + break; + + case CKM_SHA3_512: + digest_len = 512 / 8; + md = EVP_sha3_512(); + break; + + case CKM_SHA_1: + default: + digest_len = SHA_DIGEST_LENGTH; + md = EVP_sha1(); + break; + } + + out = malloc(digest_len); + if (!out || + EVP_Digest(message, message_length, out, NULL, md, NULL) != 1) { + free(out); + return NULL; + } + return out; +} + +int oaep_encrypt_message_openssl(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char **enc_message) +{ + size_t enc_length = 0; + CK_RV rv = -1; + EVP_PKEY_CTX *pctx = NULL; + const EVP_MD *md = EVP_md_null(); + const EVP_MD *mgf1_md = EVP_md_null(); + + md = md_cryptoki_to_ossl(mech->hash); + mgf1_md = mgf_cryptoki_to_ossl(mech->mgf); + + if ((pctx = EVP_PKEY_CTX_new(o->key, NULL)) == NULL + || EVP_PKEY_encrypt_init(pctx) != 1 + || EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_OAEP_PADDING) != 1 + || EVP_PKEY_CTX_set_rsa_oaep_md(pctx, md) != 1 + || EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1_md) != 1) { + fprintf(stderr, " [ ERROR %s ] Failed to initialize EVP_PKEY_CTX. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + goto out; + } + + if (EVP_PKEY_encrypt(pctx, NULL, &enc_length, message, message_length) <= 0) { + fprintf(stderr, " [ ERROR %s ] Failed get signature length. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + goto out; + } + + *enc_message = OPENSSL_malloc(enc_length); + + rv = EVP_PKEY_encrypt(pctx, *enc_message, &enc_length, message, message_length); + if (rv <= 0) { + fprintf(stderr, " [ ERROR %s ] Signature is not valid. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + } +out: + EVP_PKEY_CTX_free(pctx); + return (int)enc_length; +} + +void fill_oaep_params(CK_RSA_PKCS_OAEP_PARAMS *oaep_params, + test_mech_t *mech) +{ + oaep_params->hashAlg = mech->hash; + oaep_params->mgf = mech->mgf; + oaep_params->source = CKZ_DATA_SPECIFIED; + oaep_params->pSourceData = NULL; + oaep_params->ulSourceDataLen = 0; + +} + +int oaep_encrypt_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char **enc_message) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM enc_mechanism = { mech->mech, NULL_PTR, 0 }; + CK_RSA_PKCS_OAEP_PARAMS oaep_params; + CK_ULONG enc_message_length; + static int encrypt_support = 1; + + fill_oaep_params(&oaep_params, mech); + enc_mechanism.pParameter = &oaep_params; + enc_mechanism.ulParameterLen = sizeof(oaep_params); + + if (!encrypt_support) + goto openssl_encrypt; + + rv = fp->C_EncryptInit(info->session_handle, &enc_mechanism, + o->public_handle); + if (rv != CKR_OK) { + debug_print(" C_EncryptInit: rv = 0x%.8lX", rv); + encrypt_support = 0; /* avoid trying over and over again */ + goto openssl_encrypt; + } + + /* get the expected length */ + rv = fp->C_Encrypt(info->session_handle, message, message_length, + NULL, &enc_message_length); + if (rv != CKR_OK) { + debug_print(" C_Encrypt: rv = 0x%.8lX", rv); + goto openssl_encrypt; + } + *enc_message = malloc(enc_message_length); + if (*enc_message == NULL) { + debug_print("malloc returned null"); + return -1; + } + + /* Do the actual encryption with allocated buffer */ + rv = fp->C_Encrypt(info->session_handle, message, message_length, + *enc_message, &enc_message_length); + if (rv == CKR_OK) { + mech->result_flags |= FLAGS_DECRYPT_OPENSSL; + return (int)enc_message_length; + } + debug_print(" C_Encrypt: rv = 0x%.8lX", rv); + +openssl_encrypt: + debug_print(" [ KEY %s ] Falling back to openssl encryption", o->id_str); + return oaep_encrypt_message_openssl(o, info, message, message_length, mech, + enc_message); +} + +int oaep_decrypt_message(test_cert_t *o, token_info_t *info, CK_BYTE *enc_message, + CK_ULONG enc_message_length, test_mech_t *mech, unsigned char **dec_message) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM dec_mechanism = { mech->mech, NULL_PTR, 0 }; + CK_RSA_PKCS_OAEP_PARAMS oaep_params; + CK_ULONG dec_message_length = BUFFER_SIZE; + + fill_oaep_params(&oaep_params, mech); + dec_mechanism.pParameter = &oaep_params; + dec_mechanism.ulParameterLen = sizeof(oaep_params); + + rv = fp->C_DecryptInit(info->session_handle, &dec_mechanism, + o->private_handle); + if (rv == CKR_KEY_TYPE_INCONSISTENT) { + debug_print(" [SKIP %s ] Not allowed to decrypt with this key?", o->id_str); + return 0; + } else if (rv != CKR_OK) { + debug_print(" C_DecryptInit: rv = 0x%.8lX\n", rv); + return -1; + } + + *dec_message = malloc(dec_message_length); + + always_authenticate(o, info); + + rv = fp->C_Decrypt(info->session_handle, enc_message, + enc_message_length, *dec_message, &dec_message_length); + if (rv != CKR_OK) { + free(*dec_message); + debug_print(" C_Decrypt: rv = 0x%.8lX\n", rv); + return -1; + } + return (int) dec_message_length; +} + +/* Perform encryption and decryption of a message using private key referenced + * in the o object with mechanism defined by mech. + * + * NONE of the reasonable mechanisms support encryption/decryption + * + * Returns + * * 1 for successful Encrypt&Decrypt sequence + * * 0 for skipped test (unsupported mechanism, key, ...) + * * -1 otherwise. + * Serious errors terminate the execution. + */ +int oaep_encrypt_decrypt_test(test_cert_t *o, token_info_t *info, test_mech_t *mech) +{ + CK_BYTE *message = (CK_BYTE *) SHORT_MESSAGE_TO_SIGN; + CK_BYTE *dec_message = NULL; + int dec_message_length = 0; + int message_length = 16; + unsigned char *enc_message = NULL; + int enc_message_length, rv; + + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", o->id_str); + return 0; + } + + if (o->type != EVP_PKEY_RSA) { + debug_print(" [ KEY %s ] Skip non-RSA key for encryption", o->id_str); + return 0; + } + + if (mech->mech != CKM_RSA_PKCS_OAEP) { + mech->usage_flags &= ~CKF_DECRYPT; + debug_print(" [SKIP %s ] non RSA-OAEP mechanism", o->id_str); + return 0; + } + + message_length = MIN((int)global_message_length, + (int)(BYTES4BITS(o->bits) - 2 * get_hash_length(mech->hash) - 2)); + + /* will not work for 1024b RSA key and SHA512 hash: It has max size -2 */ + if (message_length < 0) { + mech->usage_flags &= ~CKF_DECRYPT; + debug_print(" [SKIP %s ] Too small modulus (%ld bits)" + " or too large hash %s (%zu B) for OAEP", o->id_str, + o->bits, get_mechanism_name(mech->hash), + get_hash_length(mech->hash)); + return 0; + } + + debug_print(" [ KEY %s ] Encrypt message of length %d using CKM_%s, " + "hash CKM_%s, mgf=CKG_%s", o->id_str, (unsigned) message_length, + get_mechanism_name(mech->mech), get_mechanism_name(mech->hash), + get_mgf_name(mech->mgf)); + enc_message_length = oaep_encrypt_message(o, info, message, + (unsigned) message_length, mech, &enc_message); + if (enc_message_length <= 0) { + return -1; + } + + debug_print(" [ KEY %s ] Decrypt message", o->id_str); + dec_message_length = oaep_decrypt_message(o, info, enc_message, + enc_message_length, mech, &dec_message); + free(enc_message); + if (dec_message_length <= 0) { + return -1; + } + + if (memcmp(dec_message, message, dec_message_length) == 0 + && dec_message_length == message_length) { + debug_print(" [ OK %s ] Text decrypted successfully.", o->id_str); + mech->result_flags |= FLAGS_DECRYPT; + rv = 1; + } else { + dec_message[dec_message_length] = '\0'; + debug_print(" [ ERROR %s ] Text decryption failed. Recovered text: %s", + o->id_str, dec_message); + rv = 0; + } + free(dec_message); + return rv; +} + +static unsigned long +get_max_salt_len(unsigned long bits, CK_MECHANISM_TYPE hash) +{ + return BYTES4BITS(bits) - get_hash_length(hash) - 2; +} + +int fill_pss_params(CK_RSA_PKCS_PSS_PARAMS *pss_params, + test_mech_t *mech, test_cert_t *o) +{ + pss_params->hashAlg = mech->hash; + pss_params->mgf = mech->mgf; + switch (mech->salt){ + case -2: + /* max possible ( modlen - hashlen -2 ) */ + pss_params->sLen = get_max_salt_len(o->bits, mech->hash); + break; + case -1: + /* digest length */ + /* will not work with SHA512 and 1024b keys (max is 62b!) */ + if (get_hash_length(mech->hash) > get_max_salt_len(o->bits, mech->hash)) { + return -1; + } + pss_params->sLen = get_hash_length(mech->hash); + break; + case 0: + default: + pss_params->sLen = 0; + break; + } + return 1; +} + +int pss_sign_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char **sign) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM sign_mechanism = { mech->mech, NULL_PTR, 0 }; + CK_ULONG sign_length = 0; + CK_RSA_PKCS_PSS_PARAMS pss_params; + + if (fill_pss_params(&pss_params, mech, o) != 1) { + debug_print(" [SKIP %s ] Impossible to use requested salt length", o->id_str); + return 0; + } + sign_mechanism.pParameter = &pss_params; + sign_mechanism.ulParameterLen = sizeof(pss_params); + + rv = fp->C_SignInit(info->session_handle, &sign_mechanism, + o->private_handle); + if (rv == CKR_KEY_TYPE_INCONSISTENT) { + debug_print(" [SKIP %s ] Not allowed to sign with this key?", o->id_str); + return 0; + } else if (rv == CKR_MECHANISM_INVALID) { + debug_print(" [SKIP %s ] Bad mechanism. Not supported?", o->id_str); + return 0; + } else if (rv != CKR_OK) { + debug_print(" C_SignInit: rv = 0x%.8lX\n", rv); + return -1; + } + + always_authenticate(o, info); + + /* Call C_Sign with NULL argument to find out the real size of signature */ + rv = fp->C_Sign(info->session_handle, + message, message_length, *sign, &sign_length); + if (rv != CKR_OK) { + fprintf(stderr, " C_Sign: rv = 0x%.8lX\n", rv); + return -1; + } + + *sign = malloc(sign_length); + if (*sign == NULL) { + fprintf(stderr, "%s: malloc failed", __func__); + return -1; + } + + /* Call C_Sign with allocated buffer to the actual signature */ + rv = fp->C_Sign(info->session_handle, + message, message_length, *sign, &sign_length); + + if (rv != CKR_OK) { + free(*sign); + *sign = NULL; + fprintf(stderr, " C_Sign: rv = 0x%.8lX\n", rv); + return -1; + } + return (int)sign_length; +} + +int pss_verify_message_openssl(test_cert_t *o, token_info_t *info, + CK_BYTE *message, CK_ULONG message_length, test_mech_t *mech, + unsigned char *sign, CK_ULONG sign_length) +{ + int rv = -1; + EVP_PKEY_CTX *pctx = NULL; + const CK_BYTE *my_message; + CK_BYTE *free_message = NULL; + CK_ULONG my_message_length; + const EVP_MD *mgf_md = EVP_md_null(); + const EVP_MD *md = EVP_md_null(); + + md = md_cryptoki_to_ossl(mech->hash); + mgf_md = mgf_cryptoki_to_ossl(mech->mgf); + + if (mech->mech != CKM_RSA_PKCS_PSS) { + my_message = free_message = hash_message(message, message_length, mech->hash); + my_message_length = get_hash_length(mech->hash); + } else { + my_message = message; + my_message_length = message_length; + } + + if ((pctx = EVP_PKEY_CTX_new(o->key, NULL)) == NULL + || EVP_PKEY_verify_init(pctx) != 1 + || EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) != 1 + || EVP_PKEY_CTX_set_signature_md(pctx, md) != 1 + || EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, mech->salt) != 1 + || EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf_md) != 1) { + fprintf(stderr, " [ ERROR %s ] Failed to initialize EVP_PKEY_CTX. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + goto out; + } + + rv = EVP_PKEY_verify(pctx, sign, sign_length, my_message, my_message_length); + if (rv == 1) { + debug_print(" [ OK %s ] Signature is valid.", o->id_str); + mech->result_flags |= FLAGS_SIGN_OPENSSL; + } else { + fprintf(stderr, " [ ERROR %s ] Signature is not valid. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + goto out; + } +out: + free(free_message); + EVP_PKEY_CTX_free(pctx); + return rv; +} + +int pss_verify_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char *sign, + CK_ULONG sign_length) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM sign_mechanism = { mech->mech, NULL_PTR, 0 }; + CK_RSA_PKCS_PSS_PARAMS pss_params; + static int verify_support = 1; + + if (!verify_support) + goto openssl_verify; + + fill_pss_params(&pss_params, mech, o); + sign_mechanism.pParameter = &pss_params; + sign_mechanism.ulParameterLen = sizeof(pss_params); + + /* try C_Verify() if it is supported */ + rv = fp->C_VerifyInit(info->session_handle, &sign_mechanism, + o->public_handle); + if (rv != CKR_OK) { + debug_print(" C_VerifyInit: rv = 0x%.8lX", rv); + verify_support = 0; /* avoid trying over and over again */ + goto openssl_verify; + } + + rv = fp->C_Verify(info->session_handle, + message, message_length, sign, sign_length); + + if (rv == CKR_OK) { + mech->result_flags |= FLAGS_SIGN; + debug_print(" [ OK %s ] Verification successful", o->id_str); + return 1; + } + debug_print(" C_Verify: rv = 0x%.8lX", rv); + verify_support = 0; /* avoid trying over and over again */ + +openssl_verify: + debug_print(" [ KEY %s ] Falling back to openssl verification", o->id_str); + return pss_verify_message_openssl(o, info, message, message_length, mech, + sign, sign_length); +} + +/* Perform signature and verification of a message using private key referenced + * in the o object with mechanism defined by mech. + * + * Returns + * * 1 for successful Sign&Verify sequence + * * 0 for skipped test (unsupported mechanism, key, ...) + * * -1 otherwise. + * Serious errors terminate the execution. + */ +int pss_sign_verify_test(test_cert_t *o, token_info_t *info, test_mech_t *mech) +{ + CK_BYTE *message = NULL; + size_t message_length = global_message_length; + CK_BYTE *sign = NULL; + CK_ULONG sign_length = 0; + int rv = 0; + + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", o->id_str); + return 0; + } + + if (o->type != EVP_PKEY_RSA) { + debug_print(" [SKIP %s ] Skip non-RSA key", o->id_str); + return 0; + } + + if (!is_pss_mechanism(mech->mech)) { + mech->usage_flags &= ~CKF_SIGN; + debug_print(" [SKIP %s ] non RSA-PSS mechanism %s", o->id_str, + get_mechanism_name(mech->mech)); + return 0; + } + + if (mech->mech == CKM_RSA_PKCS_PSS) { + message = hash_message(global_message, global_message_length, + mech->hash); + message_length = get_hash_length(mech->hash); + } else { + message = (unsigned char *) SHORT_MESSAGE_TO_SIGN; + } + + debug_print(" [ KEY %s ] Signing message using CKM_%s, CKM_%s," + " CKG_%s, salt_len=%d", o->id_str, + get_mechanism_name(mech->mech), get_mechanism_name(mech->hash), + get_mgf_name(mech->mgf), mech->salt); + rv = pss_sign_message(o, info, message, message_length, mech, &sign); + if (rv <= 0) { + goto out; + } + sign_length = (unsigned long) rv; + + debug_print(" [ KEY %s ] Verify message signature", o->id_str); + rv = pss_verify_message(o, info, message, message_length, mech, + sign, sign_length); +out: + if (mech->mech == CKM_RSA_PKCS_PSS) { + free(message); + } + free(sign); + return rv; +} + +/* ignore the prefilled mechanisms and list all combinations of mechanisms + * found, all reasonable hash functions, MGFs and salt lengths + */ +void fill_object_pss_mechanisms(token_info_t *info, test_cert_t *o) +{ + const CK_MECHANISM_TYPE *h; + const CK_RSA_PKCS_MGF_TYPE *mgf; + int n = 0, s; + unsigned int j; + + for (j = 0; j < token.num_rsa_mechs; j++) { + test_mech_t *source_mech = &token.rsa_mechs[j]; + + /* skip non-RSA-PSS mechs early */ + if (!is_pss_mechanism(source_mech->mech) && + source_mech->mech != CKM_RSA_PKCS_OAEP) { + continue; + } + + h = get_mechanism_hashes(source_mech->mech); + for (; *h != (CK_MECHANISM_TYPE) -1; h++) { + mgf = get_mgfs(); + for (; *mgf != (CK_RSA_PKCS_MGF_TYPE) -1; mgf++) { + /* OAEP does not have salt */ + if (source_mech->mech == CKM_RSA_PKCS_OAEP) + s = 0; + else + s = -2; + + for (; s <= 0; s++) { + test_mech_t *mech = &o->mechs[n++]; + mech->mech = source_mech->mech; + mech->hash = *h; + mech->mgf = *mgf; + mech->salt = s; + mech->usage_flags = + source_mech->usage_flags; + mech->result_flags = 0; + if (n >= MAX_PSS_MECHS) + P11TEST_FAIL(info, "Too many mechanisms (%d)", MAX_PSS_MECHS); + } + } + } + + } + o->num_mechs = n; +} + +int have_pss_oaep_mechanisms() +{ + unsigned have = 0, i; + for (i = 0; i <= token.num_rsa_mechs; i++) { + if (is_pss_mechanism(token.rsa_mechs[i].mech) || + token.rsa_mechs[i].mech == CKM_RSA_PKCS_OAEP) { + have++; + } + } + return have; +} + +void pss_oaep_test(void **state) { + + token_info_t *info = (token_info_t *) *state; + unsigned int i; + int used; + size_t j; + test_certs_t objects; + + test_certs_init(&objects); + + P11TEST_START(info); + + if (have_pss_oaep_mechanisms() == 0) { + fprintf(stderr, "Token does not support any RSA-PSS or OAEP mechanisms. Skipping.\n"); + skip(); + } + + search_for_all_objects(&objects, info); + + debug_print("\nCheck functionality of Sign&Verify and/or Encrypt&Decrypt with RSA/OAEP mechanisms"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + /* do the Sign&Verify and/or Encrypt&Decrypt */ + used = 0; + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", + o->id_str); + continue; + } + /* Do not list non-RSA keys here */ + if (o->type != EVP_PKEY_RSA) + continue; + + fill_object_pss_mechanisms(info, o); + for (j = 0; j < o->num_mechs; j++) + if (o->mechs[j].mech != CKM_RSA_PKCS_OAEP) + used |= pss_sign_verify_test(o, info, + &(o->mechs[j])); + + for (j = 0; j < o->num_mechs; j++) + if (o->mechs[j].mech == CKM_RSA_PKCS_OAEP) + used |= oaep_encrypt_decrypt_test(o, info, + &(o->mechs[j])); + + if (!used) { + debug_print(" [ WARN %s ] Private key with unknown purpose T:%02lX", + o->id_str, o->key_type); + } + } + + if (objects.count == 0) { + printf(" [WARN] No objects to display\n"); + return; + } + + /* print summary */ + printf("[KEY ID] [LABEL]\n"); + printf("[ TYPE ] [ SIZE ] [PUBLIC] [SIGN&VERIFY] [ENC&DECRYPT]\n"); + printf("[ MECHANISM ] [ HASH ] [ MGF ] [SALT] [ WORKS ] [ WORKS ]\n"); + P11TEST_DATA_ROW(info, 7, + 's', "KEY ID", + 's', "MECHANISM", + 's', "HASH", + 's', "MGF", + 's', "SALT", + 's', "SIGN&VERIFY WORKS", + 's', "ENCRYPT&DECRYPT WORKS"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + + /* Do not go through incomplete pairs */ + if (o->private_handle == CK_INVALID_HANDLE) + continue; + + /* Do not list non-RSA keys here */ + if (o->type != EVP_PKEY_RSA) + continue; + + printf("\n[%-6s] [%s]\n", + o->id_str, + o->label); + printf("[ %s ] [%6lu] [ %s ] [%s%s] [%s%s]\n", + o->key_type == CKK_RSA ? "RSA " : " ?? ", + o->bits, + o->verify_public == 1 ? " ./ " : " ", + o->sign ? "[./] " : "[ ] ", + o->verify ? " [./] " : " [ ] ", + o->encrypt ? "[./] " : "[ ] ", + o->decrypt ? " [./] " : " [ ] "); + if (!o->sign && !o->verify && !o->encrypt && !o->decrypt) { + printf(" no usable attributes found ... ignored\n"); + continue; + } + for (j = 0; j < o->num_mechs; j++) { + test_mech_t *mech = &o->mechs[j]; + printf(" [ %-20s ] [%-8s] [%-13s] [%4d] [ %s ] [ %s ]\n", + get_mechanism_name(mech->mech), + get_mechanism_name(mech->hash), + get_mgf_name(mech->mgf), + mech->salt, + mech->result_flags & FLAGS_SIGN_ANY ? "[./]" : " ", + mech->result_flags & FLAGS_DECRYPT_ANY ? "[./]" : " "); + if ((mech->result_flags & FLAGS_SIGN_ANY) == 0 && + (mech->result_flags & FLAGS_DECRYPT_ANY) == 0) + continue; /* skip empty rows for export */ + P11TEST_DATA_ROW(info, 7, + 's', o->id_str, + 's', get_mechanism_name(mech->mech), + 's', get_mechanism_name(mech->hash), + 's', get_mgf_name(mech->mgf), + 'd', mech->salt, + 's', mech->result_flags & FLAGS_SIGN_ANY + ? "YES" : "", + 's', mech->result_flags & FLAGS_DECRYPT_ANY + ? "YES" : ""); + } + } + printf(" Public == Cert ----------^ ^ ^ ^ ^ ^ ^\n"); + printf(" Sign Attribute -----------------------------------------------' | | | | |\n"); + printf(" Sign&Verify functionality ---------------------------------------' | | | |\n"); + printf(" Verify Attribute ---------------------------------------------------' | | |\n"); + printf(" Encrypt Attribute ----------------------------------------------------------' | |\n"); + printf(" Encrypt & Decrypt functionality -----------------------------------------------' |\n"); + printf(" Decrypt Attribute ----------------------------------------------------------------'\n"); + + clean_all_objects(&objects); + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_pss_oaep.h b/src/tests/p11test/p11test_case_pss_oaep.h new file mode 100644 index 0000000000..ce2185352c --- /dev/null +++ b/src/tests/p11test/p11test_case_pss_oaep.h @@ -0,0 +1,24 @@ +/* + * p11test_case_pss_oaep.h: RSA-PSS and RSA-OAEP tests + * + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_common.h" + +void pss_oaep_test(void **state); diff --git a/src/tests/p11test/p11test_case_readonly.c b/src/tests/p11test/p11test_case_readonly.c new file mode 100644 index 0000000000..a11bcb5849 --- /dev/null +++ b/src/tests/p11test/p11test_case_readonly.c @@ -0,0 +1,926 @@ +/* + * p11test_case_readonly.c: Sign & Verify tests + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_readonly.h" + +#include +#include +#include +#include +#include +#include "libopensc/internal.h" +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# include +#endif + +#define MESSAGE_TO_SIGN "Simple message for signing & verifying. " \ + "It needs to be little bit longer to fit also longer keys and allow the truncation.\n" \ + "Simple message for signing & verifying. " \ + "It needs to be little bit longer to fit also longer keys and allow the truncation.\n" \ + "Simple message for signing & verifying. " \ + "It needs to be little bit longer to fit also longer keys and allow the truncation.\n" \ + "Simple message for signing & verifying. " \ + "It needs to be little bit longer to fit also longer keys and allow the truncation.\n" \ + "Simple message for signing & verifying. " \ + "It needs to be little bit longer to fit also longer keys and allow the truncation.\n" \ + "Simple message for signing & verifying. " \ + "It needs to be little bit longer to fit also longer keys and allow the truncation.\n" \ + "Simple message for signing & verifying. " \ + "It needs to be little bit longer to fit also longer keys and allow the truncation.\n" \ + "Simple message for signing & verifying. " \ + "It needs to be little bit longer to fit also longer keys and allow the truncation.\n" \ + "Simple message for signing & verifying. " \ + "It needs to be little bit longer to fit also longer keys and allow the truncation.\n" +#define MESSAGE_DIGEST "\x30\x21\x30\x09\x06\x05\x2b\x0e" \ + "\x03\x02\x1a\x05\x00\x04\x14\xd9" \ + "\xdd\xa3\x76\x44\x2f\x50\xe1\xec" \ + "\xd3\x8b\xcd\x6f\xc6\xce\x4e\xfd" \ + "\xd3\x1a\x3f" +#define BUFFER_SIZE 4096 + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +OSSL_PROVIDER *legacy_provider = NULL; +#endif + +const unsigned char *const_message = (unsigned char *) MESSAGE_TO_SIGN; + +unsigned char * +rsa_x_509_pad_message(const unsigned char *message, unsigned long *message_length, test_cert_t *o, int encrypt) +{ + unsigned long pad_message_length = BYTES4BITS(o->bits); + unsigned char *pad_message = NULL; + size_t padding_len = pad_message_length - (*message_length) - 3; + + if (pad_message_length - (*message_length) <= 11) { + debug_print("Cannot pad message - buffer too small"); + return NULL; + } + if ((pad_message = malloc(pad_message_length)) == NULL) { + fprintf(stderr, "System error: unable to allocate memory\n"); + return NULL; + } + + pad_message[0] = 0x00; + pad_message[pad_message_length - 1] = 0x00; + if (!encrypt) { + pad_message[1] = 0x01; + memset(pad_message + 2, 0xff, padding_len); + } else { + pad_message[1] = 0x02; + if (RAND_bytes(pad_message + 2, (int)padding_len) != 1) { + debug_print("Cannot generate random bytes."); + } + } + memcpy(pad_message + 2 + padding_len, message, (*message_length) * sizeof(unsigned char)); + + *message_length = pad_message_length; + return pad_message; +} + +int encrypt_message_openssl(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char **enc_message) +{ + int rv = -1, padding; + size_t outlen = 0; + EVP_PKEY_CTX *ctx = NULL; + + /* allocate the buffer if none provided */ + if (*enc_message == NULL) { + outlen = EVP_PKEY_size(o->key); + *enc_message = malloc(outlen); + if (*enc_message == NULL) { + debug_print("malloc returned null"); + return -1; + } + } + + /* Prepare padding for RSA_X_509 */ + padding = ((mech->mech == CKM_RSA_X_509) ? RSA_NO_PADDING : RSA_PKCS1_PADDING); + + ctx = EVP_PKEY_CTX_new(o->key, NULL); + if (!ctx || (rv = EVP_PKEY_encrypt_init(ctx)) <= 0 || + (rv = EVP_PKEY_CTX_set_rsa_padding(ctx, padding)) <= 0 || + (rv = EVP_PKEY_encrypt(ctx, *enc_message, &outlen, message, message_length)) <= 0) { + free(*enc_message); + *enc_message = NULL; + EVP_PKEY_CTX_free(ctx); + fprintf(stderr, " [ ERROR %s ] OpenSSL encrypt failed: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + return -1; + } + EVP_PKEY_CTX_free(ctx); + return (int)outlen; +} + +int encrypt_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char **enc_message) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM enc_mechanism = { mech->mech, mech->params, mech->params_len }; + CK_ULONG enc_message_length; + + if (!info->encrypt_support) + goto openssl_encrypt; + + rv = fp->C_EncryptInit(info->session_handle, &enc_mechanism, + o->public_handle); + if (rv != CKR_OK) { + debug_print(" C_EncryptInit: rv = 0x%.8lX", rv); + info->encrypt_support = 0; /* avoid trying over and over again */ + goto openssl_encrypt; + } + + /* get the expected length */ + rv = fp->C_Encrypt(info->session_handle, message, message_length, + NULL, &enc_message_length); + if (rv != CKR_OK) { + debug_print(" C_Encrypt: rv = 0x%.8lX", rv); + goto openssl_encrypt; + } + *enc_message = malloc(enc_message_length); + if (*enc_message == NULL) { + debug_print("malloc returned null"); + return -1; + } + + /* Do the actual encryption with allocated buffer */ + rv = fp->C_Encrypt(info->session_handle, message, message_length, + *enc_message, &enc_message_length); + if (rv == CKR_OK) { + return (int)enc_message_length; + } + debug_print(" C_Encrypt: rv = 0x%.8lX", rv); + +openssl_encrypt: + debug_print(" [ KEY %s ] Falling back to openssl encryption", o->id_str); + return encrypt_message_openssl(o, info, message, message_length, mech, + enc_message); +} + +int decrypt_message(test_cert_t *o, token_info_t *info, CK_BYTE *enc_message, + CK_ULONG enc_message_length, test_mech_t *mech, unsigned char **dec_message) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM dec_mechanism = { mech->mech, mech->params, mech->params_len }; + CK_ULONG dec_message_length = BUFFER_SIZE; + + rv = fp->C_DecryptInit(info->session_handle, &dec_mechanism, + o->private_handle); + if (rv == CKR_KEY_TYPE_INCONSISTENT) { + debug_print(" [SKIP %s ] Not allowed to decrypt with this key?", o->id_str); + return 0; + } else if (rv != CKR_OK) { + debug_print("C_DecryptInit: rv = 0x%.8lX\n", rv); + return -1; + } + + *dec_message = malloc(dec_message_length); + + always_authenticate(o, info); + + rv = fp->C_Decrypt(info->session_handle, enc_message, + enc_message_length, *dec_message, &dec_message_length); + if (rv != CKR_OK) { + free(*dec_message); + *dec_message = NULL; + debug_print(" C_Decrypt: rv = 0x%.8lX\n", rv); + return -1; + } + return (int) dec_message_length; +} + +/* Perform encryption and decryption of a message using private key referenced + * in the o object with mechanism defined by mech. + * + * NONE of the reasonable mechanisms support multipart encryption/decryption + * + * Returns + * * 1 for successful Encrypt&Decrypt sequence + * * 0 for skipped test (unsupported mechanism, key, ...) + * * -1 otherwise. + * Serious errors terminate the execution. + */ +int encrypt_decrypt_test(test_cert_t *o, token_info_t *info, test_mech_t *mech, + CK_ULONG message_length, int multipart) +{ + CK_BYTE *message = NULL; + CK_BYTE *dec_message = NULL; + int dec_message_length = 0; + unsigned char *enc_message = NULL; + int enc_message_length, rv; + + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", o->id_str); + return 0; + } + + if (o->type != EVP_PKEY_RSA) { + debug_print(" [SKIP %s ] Skip non-RSA key for encryption", o->id_str); + return 0; + } + + if (mech->mech == CKM_RSA_PKCS_OAEP) { + mech->usage_flags &= ~CKF_DECRYPT; + debug_print(" [SKIP %s ] RSA-OAEP tested separately", o->id_str); + return 0; + } + + if (mech->mech != CKM_RSA_X_509 && mech->mech != CKM_RSA_PKCS) { + debug_print(" [ KEY %s ] Skip encryption for non-supported mechanism %s", + o->id_str, get_mechanism_name(mech->mech)); + return 0; + } + + if (mech->mech == CKM_RSA_X_509) { + if ((message = rsa_x_509_pad_message(const_message, + &message_length, o, 1)) == NULL) { + debug_print(" [SKIP %s ] Could not pad message", o->id_str); + return -1; + } + } else { + message = (CK_BYTE *) strdup(MESSAGE_TO_SIGN); + } + + + debug_print(" [ KEY %s ] Encrypt message using CKM_%s", + o->id_str, get_mechanism_name(mech->mech)); + enc_message_length = encrypt_message(o, info, message, message_length, + mech, &enc_message); + if (enc_message_length <= 0) { + free(enc_message); + free(message); + return -1; + } + + debug_print(" [ KEY %s ] Decrypt message", o->id_str); + dec_message_length = decrypt_message(o, info, enc_message, + enc_message_length, mech, &dec_message); + free(enc_message); + if (dec_message_length <= 0) { + free(message); + return -1; + } + + if ((unsigned int) dec_message_length == message_length && + memcmp(dec_message, message, dec_message_length) == 0) { + debug_print(" [ OK %s ] Text decrypted successfully.", o->id_str); + mech->result_flags |= FLAGS_DECRYPT; + rv = 1; + } else { + dec_message[dec_message_length] = '\0'; + debug_print(" [ ERROR %s ] Text decryption failed. Recovered text: %s", + o->id_str, dec_message); + rv = 0; + } + free(dec_message); + free(message); + return rv; +} + +int sign_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char **sign, + int multipart) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM sign_mechanism = { mech->mech, mech->params, mech->params_len }; + CK_ULONG sign_length = 0; + char *name; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (!legacy_provider) { + if (!(legacy_provider = OSSL_PROVIDER_try_load(NULL, "legacy", 1))) { + debug_print(" [SKIP %s ] Failed to load legacy provider", o->id_str); + return 0; + } + } +#endif + rv = fp->C_SignInit(info->session_handle, &sign_mechanism, + o->private_handle); + if (rv == CKR_KEY_TYPE_INCONSISTENT) { + debug_print(" [SKIP %s ] Not allowed to sign with this key?", o->id_str); + return 0; + } else if (rv == CKR_MECHANISM_INVALID) { + debug_print(" [SKIP %s ] Bad mechanism. Not supported?", o->id_str); + return 0; + } else if (rv != CKR_OK) { + debug_print(" [SKIP %s ] Not allowed to sign with this key?", o->id_str); + return 0; + } + + always_authenticate(o, info); + + if (multipart) { + unsigned long part = message_length / 3; + rv = fp->C_SignUpdate(info->session_handle, message, part); + if (rv == CKR_MECHANISM_INVALID) { + fprintf(stderr, " Multipart Signature not supported with CKM_%s\n", + get_mechanism_name(mech->mech)); + return -1; + } else if (rv != CKR_OK) { + fprintf(stderr, " C_SignUpdate: rv = 0x%.8lX\n", rv); + return -1; + } + rv = fp->C_SignUpdate(info->session_handle, message + part, message_length - part); + if (rv != CKR_OK) { + fprintf(stderr, " C_SignUpdate: rv = 0x%.8lX\n", rv); + return -1; + } + /* Call C_SignFinal with NULL argument to find out the real size of signature */ + rv = fp->C_SignFinal(info->session_handle, *sign, &sign_length); + if (rv != CKR_OK) { + fprintf(stderr, " C_SignFinal: rv = 0x%.8lX\n", rv); + return -1; + } + + *sign = malloc(sign_length); + if (*sign == NULL) { + fprintf(stderr, "%s: malloc failed", __func__); + return -1; + } + + /* Call C_SignFinal with allocated buffer to the actual signature */ + rv = fp->C_SignFinal(info->session_handle, *sign, &sign_length); + name = "C_SignFinal"; + } else { + /* Call C_Sign with NULL argument to find out the real size of signature */ + rv = fp->C_Sign(info->session_handle, + message, message_length, *sign, &sign_length); + if (rv != CKR_OK) { + fprintf(stderr, " C_Sign: rv = 0x%.8lX\n", rv); + return -1; + } + + *sign = malloc(sign_length); + if (*sign == NULL) { + fprintf(stderr, "%s: malloc failed", __func__); + return -1; + } + + /* Call C_Sign with allocated buffer to the actual signature */ + rv = fp->C_Sign(info->session_handle, + message, message_length, *sign, &sign_length); + name = "C_Sign"; + } + if (rv != CKR_OK) { + free(*sign); + fprintf(stderr, " %s: rv = 0x%.8lX\n", name, rv); + return -1; + } + return (int)sign_length; +} + +int verify_message_openssl(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char *sign, + CK_ULONG sign_length) +{ + CK_RV rv; + CK_BYTE *cmp_message = NULL; + unsigned int cmp_message_length = 0; + + switch (o->type) { + case EVP_PKEY_RSA: { + const EVP_MD *md = NULL; + EVP_MD_CTX *mdctx = NULL; + EVP_PKEY_CTX *ctx = NULL; + int padding = RSA_PKCS1_PADDING; + + /* Digest mechanisms */ + switch (mech->mech) { + case CKM_RSA_X_509: + padding = RSA_NO_PADDING; + /* fall through */ + case CKM_RSA_PKCS: + if ((ctx = EVP_PKEY_CTX_new(o->key, NULL)) == NULL || + (rv = EVP_PKEY_verify_init(ctx)) <= 0 || + (rv = EVP_PKEY_CTX_set_rsa_padding(ctx, padding)) <= 0 || + (rv = EVP_PKEY_verify(ctx, sign, sign_length, message, message_length)) != 1) { + fprintf(stderr, " [ ERROR %s ] Signature is not valid. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + EVP_PKEY_CTX_free(ctx); + return -1; + } + mech->result_flags |= FLAGS_SIGN_OPENSSL; + debug_print(" [ OK %s ] Signature is valid.", o->id_str); + EVP_PKEY_CTX_free(ctx); + return 1; + break; + case CKM_SHA1_RSA_PKCS: + md = EVP_sha1(); + break; + case CKM_SHA224_RSA_PKCS: + md = EVP_sha224(); + break; + case CKM_SHA256_RSA_PKCS: + md = EVP_sha256(); + break; + case CKM_SHA384_RSA_PKCS: + md = EVP_sha384(); + break; + case CKM_SHA512_RSA_PKCS: + md = EVP_sha512(); + break; + case CKM_SHA3_224_RSA_PKCS: + md = EVP_sha3_224(); + break; + case CKM_SHA3_256_RSA_PKCS: + md = EVP_sha3_256(); + break; + case CKM_SHA3_384_RSA_PKCS: + md = EVP_sha3_384(); + break; + case CKM_SHA3_512_RSA_PKCS: + md = EVP_sha3_512(); + break; + case CKM_MD5_RSA_PKCS: + md = EVP_md5(); + break; + case CKM_RIPEMD160_RSA_PKCS: +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (!legacy_provider) { + if (!(legacy_provider = OSSL_PROVIDER_try_load(NULL, "legacy", 1))) { + debug_print(" [SKIP %s ] Failed to load legacy provider", o->id_str); + return 0; + } + } +#endif + md = EVP_ripemd160(); + break; + default: + debug_print(" [SKIP %s ] Skip verify of unknown mechanism", o->id_str); + return 0; + } + + if ((mdctx = EVP_MD_CTX_new()) == NULL || + (rv = EVP_DigestVerifyInit(mdctx, NULL, md, NULL, o->key) <= 0) || + (rv = EVP_DigestVerify(mdctx, sign, sign_length, message, message_length)) != 1) { + fprintf(stderr, " [ ERROR %s ] Signature is not valid. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + EVP_MD_CTX_free(mdctx); + return -1; + } + mech->result_flags |= FLAGS_SIGN_OPENSSL; + debug_print(" [ OK %s ] Signature is valid.", o->id_str); + EVP_MD_CTX_free(mdctx); + return 1; + } + case EVP_PKEY_EC: { + int nlen; + const EVP_MD *md = NULL; + ECDSA_SIG *sig = ECDSA_SIG_new(); + BIGNUM *r = NULL, *s = NULL; + EVP_PKEY_CTX *ctx = NULL; + ctx = EVP_PKEY_CTX_new(o->key, NULL); + + if (!sig || !ctx) { + fprintf(stderr, "Verification failed"); + EVP_PKEY_CTX_free(ctx); + ECDSA_SIG_free(sig); + return -1; + } + nlen = (int)sign_length / 2; + r = BN_bin2bn(&sign[0], nlen, NULL); + s = BN_bin2bn(&sign[nlen], nlen, NULL); + ECDSA_SIG_set0(sig, r, s); + + switch (mech->mech) { + case CKM_ECDSA_SHA512: + md = EVP_sha512(); + break; + case CKM_ECDSA_SHA384: + md = EVP_sha384(); + break; + case CKM_ECDSA_SHA256: + md = EVP_sha256(); + break; + case CKM_ECDSA_SHA1: + md = EVP_sha1(); + break; + case CKM_ECDSA: + cmp_message = message; + cmp_message_length = (unsigned)message_length; + break; + case CKM_ECDSA_SHA3_224: + md = EVP_sha3_224(); + break; + case CKM_ECDSA_SHA3_256: + md = EVP_sha3_256(); + break; + case CKM_ECDSA_SHA3_384: + md = EVP_sha3_384(); + break; + case CKM_ECDSA_SHA3_512: + md = EVP_sha3_512(); + break; + default: + debug_print(" [SKIP %s ] Skip verify of unknown mechanism", o->id_str); + EVP_PKEY_CTX_free(ctx); + ECDSA_SIG_free(sig); + return 0; + } + int sig_asn1_len = 0; + unsigned char *sig_asn1 = NULL; + sig_asn1_len = i2d_ECDSA_SIG(sig, &sig_asn1); + ECDSA_SIG_free(sig); + + if (md != NULL) { + cmp_message = malloc(EVP_MAX_MD_SIZE); + if (cmp_message == NULL) { + fprintf(stderr, "malloc failed\n"); + return -1; + } + rv = EVP_Digest(message, message_length, cmp_message, &cmp_message_length, md, NULL); + if (rv != 1) { + fprintf(stderr, "EVP_Digest failed\n"); + free(cmp_message); + return -1; + } + } + + if (EVP_PKEY_verify_init(ctx) != 1) { + fprintf(stderr, "EVP_PKEY_verify_init\n"); + free(cmp_message); + return -1; + } + + rv = EVP_PKEY_verify(ctx, sig_asn1, sig_asn1_len, cmp_message, cmp_message_length); + OPENSSL_free(sig_asn1); + if (md != NULL) { + free(cmp_message); + } + EVP_PKEY_CTX_free(ctx); + if (rv == 1) { + debug_print(" [ OK %s ] EC Signature of length %lu is valid.", + o->id_str, message_length); + mech->result_flags |= FLAGS_SIGN_OPENSSL; + return 1; + } else { + fprintf(stderr, " [FAIL %s ] EVP_PKEY_verify: rv = %lu: %s\n", o->id_str, + rv, ERR_error_string(ERR_peek_last_error(), NULL)); + return -1; + } + break; + } +#ifdef EVP_PKEY_ED25519 + case EVP_PKEY_ED25519: +#endif +#ifdef EVP_PKEY_ED448 + case EVP_PKEY_ED448: +#endif +#if defined(EVP_PKEY_ED25519) || defined(EVP_PKEY_ED448) + { + /* need to be created even though we do not do any MD */ + EVP_MD_CTX *ctx = EVP_MD_CTX_create(); + + rv = EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, o->key); + if (rv != 1) { + fprintf(stderr, " [FAIL %s ] EVP_DigestVerifyInit: rv = %lu: %s\n", o->id_str, + rv, ERR_error_string(ERR_peek_last_error(), NULL)); + EVP_MD_CTX_free(ctx); + return -1; + } + + rv = EVP_DigestVerify(ctx, sign, sign_length, message, message_length); + if (rv == 1) { + debug_print(" [ OK %s ] EdDSA Signature of length %lu is valid.", + o->id_str, message_length); + mech->result_flags |= FLAGS_SIGN_OPENSSL; + EVP_MD_CTX_free(ctx); + return 1; + } else { + fprintf(stderr, " [FAIL %s ] EVP_DigestVerifyInit: rv = %lu: %s\n", o->id_str, + rv, ERR_error_string(ERR_peek_last_error(), NULL)); + EVP_MD_CTX_free(ctx); + return -1; + } + break; + } +#endif /* defined(EVP_PKEY_ED25519) || defined(EVP_PKEY_ED448) */ + default: + fprintf(stderr, " [ KEY %s ] Unknown type. Not verifying\n", o->id_str); + } + return 0; +} + +int verify_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char *sign, + CK_ULONG sign_length, int multipart) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM sign_mechanism = {mech->mech, mech->params, mech->params_len}; + static int verify_support = 1; + char *name; + + if (!verify_support) + goto openssl_verify; + + /* try C_Verify() if it is supported */ + rv = fp->C_VerifyInit(info->session_handle, &sign_mechanism, + o->public_handle); + if (rv != CKR_OK) { + debug_print(" C_VerifyInit: rv = 0x%.8lX", rv); + verify_support = 0; /* avoid trying over and over again */ + goto openssl_verify; + } + if (multipart) { + unsigned long part = message_length / 3; + /* First part */ + rv = fp->C_VerifyUpdate(info->session_handle, message, part); + if (rv != CKR_OK) { + debug_print(" C_VerifyUpdate: rv = 0x%.8lX", rv); + goto openssl_verify; + } + /* Second part */ + rv = fp->C_VerifyUpdate(info->session_handle, message + part, + message_length - part); + if (rv != CKR_OK) { + debug_print(" C_VerifyUpdate: rv = 0x%.8lX", rv); + goto openssl_verify; + } + /* Final */ + rv = fp->C_VerifyFinal(info->session_handle, + sign, sign_length); + name = "C_VerifyFinal"; + } else { + rv = fp->C_Verify(info->session_handle, + message, message_length, sign, sign_length); + name = "C_Verify"; + } + if (rv == CKR_OK) { + mech->result_flags |= FLAGS_SIGN; + debug_print(" [ OK %s ] [PKCS11] Verification successful", o->id_str); + return 1; + } + debug_print(" %s: rv = 0x%.8lX", name, rv); + verify_support = 0; /* avoid trying over and over again */ + +openssl_verify: + debug_print(" [ KEY %s ] Falling back to openssl verification", o->id_str); + return verify_message_openssl(o, info, message, message_length, mech, + sign, sign_length); +} + +/* Perform signature and verification of a message using private key referenced + * in the o object with mechanism defined by mech. Message length can be + * specified using argument message_length. + * + * Returns + * * 1 for successful Sign&Verify sequence + * * 0 for skipped test (unsupported mechanism, key, ...) + * * -1 otherwise. + * Serious errors terminate the execution. + */ +int sign_verify_test(test_cert_t *o, token_info_t *info, test_mech_t *mech, + CK_ULONG message_length, int multipart) +{ + CK_BYTE *message = NULL; + CK_BYTE *sign = NULL; + CK_ULONG sign_length = 0; +#ifdef EVP_PKEY_ED448 + CK_EDDSA_PARAMS eddsa_params = { + .phFlag = CK_FALSE, + .ulContextDataLen = 0, + .pContextData = NULL, + }; +#endif + CK_HASH_SIGN_ADDITIONAL_CONTEXT pqc_params = { + .hedgeVariant = CKH_HEDGE_PREFERRED, + .pContext = NULL, + .ulContextLen = 0, + .hash = CKM_SHA256, + }; + int rv = 0; + + if (message_length > strlen(MESSAGE_TO_SIGN)) { + fail_msg("Truncate is longer than the actual message"); + return -1; + } + + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", o->id_str); + return 0; + } + + switch (o->type) { +#ifdef EVP_PKEY_ED448 + case EVP_PKEY_ED448: + /* The Ed448 requires parameter */ + mech->params = &eddsa_params; + mech->params_len = sizeof(CK_EDDSA_PARAMS); + /* fall through */ +#endif /* EVP_PKEY_ED448 */ +#ifdef EVP_PKEY_ED25519 + case EVP_PKEY_ED25519: +#endif +#ifdef EVP_PKEY_ML_DSA_44 + case EVP_PKEY_ML_DSA_44: + case EVP_PKEY_ML_DSA_65: + case EVP_PKEY_ML_DSA_87: +#endif +#ifdef EVP_PKEY_SLH_DSA_SHA2_128S + case EVP_PKEY_SLH_DSA_SHA2_128S: + case EVP_PKEY_SLH_DSA_SHAKE_128S: + case EVP_PKEY_SLH_DSA_SHA2_128F: + case EVP_PKEY_SLH_DSA_SHAKE_128F: + case EVP_PKEY_SLH_DSA_SHA2_192S: + case EVP_PKEY_SLH_DSA_SHAKE_192S: + case EVP_PKEY_SLH_DSA_SHA2_192F: + case EVP_PKEY_SLH_DSA_SHAKE_192F: + case EVP_PKEY_SLH_DSA_SHA2_256S: + case EVP_PKEY_SLH_DSA_SHAKE_256S: + case EVP_PKEY_SLH_DSA_SHA2_256F: + case EVP_PKEY_SLH_DSA_SHAKE_256F: +#endif + case EVP_PKEY_EC: + case EVP_PKEY_RSA: + /* OK */ + break; + default: + debug_print(" [SKIP %s ] Skip non-RSA and non-EC key", o->id_str); + return 0; + } + + if (is_pss_mechanism(mech->mech)) { + mech->usage_flags &= ~CKF_SIGN; + debug_print(" [SKIP %s ] RSA-PSS tested separately", o->id_str); + return 0; + } + + if (mech->mech == CKM_RSA_X_509) { /* manually add padding */ + message = rsa_x_509_pad_message(const_message, + &message_length, o, 0); + } else if (mech->mech == CKM_RSA_PKCS) { + /* DigestInfo + SHA1(message) */ + message_length = 35; + message = malloc(message_length * sizeof(unsigned char)); + memcpy(message, MESSAGE_DIGEST, message_length); + } else if (mech->mech == CKM_HASH_ML_DSA || mech->mech == CKM_HASH_SLH_DSA) { + /* Hash of the message, */ + message_length = 32; + message = malloc(message_length * sizeof(unsigned char)); + memcpy(message, MESSAGE_DIGEST, message_length); + /* and requires parameter */ + mech->params = &pqc_params; + mech->params_len = sizeof(CK_HASH_SIGN_ADDITIONAL_CONTEXT); + } else + message = (CK_BYTE *) strdup(MESSAGE_TO_SIGN); + + debug_print(" [ KEY %s ] Signing message of length %lu using CKM_%s", + o->id_str, message_length, get_mechanism_name(mech->mech)); + rv = sign_message(o, info, message, message_length, mech, &sign, multipart); + if (rv <= 0) { + free(message); + return rv; + } + sign_length = (unsigned long) rv; + + debug_print(" [ KEY %s ] Verify message signature", o->id_str); + rv = verify_message(o, info, message, message_length, mech, + sign, sign_length, multipart); + free(sign); + free(message); + return rv; +} + +void readonly_tests(void **state) { + + token_info_t *info = (token_info_t *) *state; + unsigned int i; + int used; + size_t j; + test_certs_t objects; + + test_certs_init(&objects); + + search_for_all_objects(&objects, info); + + P11TEST_START(info); + debug_print("\nCheck functionality of Sign&Verify and/or Encrypt&Decrypt"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + /* do the Sign&Verify and/or Encrypt&Decrypt */ + used = 0; + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", o->id_str); + continue; + } + /* XXX some keys do not have appropriate flags, but we can use them + * or vice versa */ + // if (o->sign && o->verify) + for (j = 0; j < o->num_mechs; j++) { + test_mech_t *m = &(objects.data[i].mechs[j]); + if ((m->usage_flags & CKF_SIGN) == 0) { + /* Skip non-signature mechanisms (for example derive ones) */ + continue; + } + used |= sign_verify_test(o, info, m, 32, 0); + } + + // if (o->encrypt && o->decrypt) + for (j = 0; j < o->num_mechs; j++) { + test_mech_t *m = &(objects.data[i].mechs[j]); + if ((m->usage_flags & CKF_DECRYPT) == 0) { + /* Skip non-decrypt mechanisms (for example derive ones) */ + continue; + } + used |= encrypt_decrypt_test(o, info, m, 32, 0); + } + + if (!used) { + debug_print(" [WARN %s ] Private key with unknown purpose T:%02lX", o->id_str, + o->key_type); + } + } + + if (objects.count == 0) { + printf(" [WARN] No objects to display\n"); + return; + } + + /* print summary */ + printf("[KEY ID] [LABEL]\n"); + printf("[ TYPE ] [ SIZE ] [PUBLIC] [SIGN&VERIFY] [ENC&DECRYPT]\n"); + P11TEST_DATA_ROW(info, 4, + 's', "KEY ID", + 's', "MECHANISM", + 's', "SIGN&VERIFY WORKS", + 's', "ENCRYPT&DECRYPT WORKS"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + + if (o->key_type != CKK_RSA && + o->key_type != CKK_EC && + o->key_type != CKK_EC_EDWARDS && + o->key_type != CKK_ML_DSA && + o->key_type != CKK_SLH_DSA) + continue; + + printf("\n[%-6s] [%s]\n", o->id_str, o->label); + printf("[%s] [%6lu] [ %s ] [%s%s] [%s%s]\n", + (o->key_type == CKK_RSA ? " RSA " : + o->key_type == CKK_EC ? " EC " : + o->key_type == CKK_EC_EDWARDS ? " EDDSA " : + o->key_type == CKK_ML_DSA ? "ML-DSA " : + o->key_type == CKK_SLH_DSA ? "SLH-DSA" : " ?? "), + o->bits, + o->verify_public == 1 ? " ./ " : " ", + o->sign ? "[./] " : "[ ] ", + o->verify ? " [./] " : " [ ] ", + o->encrypt ? "[./] " : "[ ] ", + o->decrypt ? " [./] " : " [ ] "); + if (!o->sign && !o->verify && !o->encrypt && !o->decrypt && + !o->encapsulate && !o->decapsulate) { + printf(" no usable attributes found ... ignored\n"); + continue; + } + if (o->private_handle == CK_INVALID_HANDLE) { + continue; + } + for (j = 0; j < o->num_mechs; j++) { + test_mech_t *mech = &o->mechs[j]; + if ((mech->usage_flags & (CKF_SIGN|CKF_DECRYPT)) == 0) { + /* not applicable mechanisms are skipped */ + continue; + } + printf(" [ %-21s ] [ %s ] [ %s ]\n", + get_mechanism_name(mech->mech), + mech->result_flags & FLAGS_SIGN_ANY ? "[./]" : " ", + mech->result_flags & FLAGS_DECRYPT_ANY ? "[./]" : " "); + if ((mech->result_flags & FLAGS_SIGN_ANY) == 0 && + (mech->result_flags & FLAGS_DECRYPT_ANY) == 0) + continue; /* skip empty rows for export */ + P11TEST_DATA_ROW(info, 4, + 's', o->id_str, + 's', get_mechanism_name(mech->mech), + 's', mech->result_flags & FLAGS_SIGN_ANY ? "YES" : "", + 's', mech->result_flags & FLAGS_DECRYPT_ANY ? "YES" : ""); + } + } + printf(" Public == Cert ------^ ^ ^ ^ ^ ^ ^\n"); + printf(" Sign Attribute --------------' | | | | '---- Decrypt Attribute\n"); + printf(" Sign&Verify functionality- -----' | | '------- Enc&Dec functionality\n"); + printf(" Verify Attribute ------------------' '---------- Encrypt Attribute\n"); + + clean_all_objects(&objects); + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_readonly.h b/src/tests/p11test/p11test_case_readonly.h new file mode 100644 index 0000000000..cdb63a21d3 --- /dev/null +++ b/src/tests/p11test/p11test_case_readonly.h @@ -0,0 +1,42 @@ +/* + * p11test_case_readonly.h: Sign & Verify tests + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_common.h" + +void readonly_tests(void **state); +int encrypt_decrypt_test(test_cert_t *o, token_info_t *info, test_mech_t *mech, + CK_ULONG message_length, int multipart); +int sign_verify_test(test_cert_t *o, token_info_t *info, test_mech_t *mech, + CK_ULONG message_length, int multipart); + +int verify_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char *sign, + CK_ULONG sign_length, int multipart); +int sign_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char **sign, + int multipart); +int encrypt_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char **enc_message); +int decrypt_message(test_cert_t *o, token_info_t *info, CK_BYTE *enc_message, + CK_ULONG enc_message_length, test_mech_t *mech, unsigned char **dec_message); + +unsigned char *rsa_x_509_pad_message(const unsigned char *message, unsigned long *message_length, + test_cert_t *o, int encrypt); diff --git a/src/tests/p11test/p11test_case_secret.c b/src/tests/p11test/p11test_case_secret.c new file mode 100644 index 0000000000..c80a53cba5 --- /dev/null +++ b/src/tests/p11test/p11test_case_secret.c @@ -0,0 +1,338 @@ +/* + * p11test_case_secret.c: Check the functionality of operations with secret keys + * + * Copyright (C) 2021 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ +#include "p11test_case_secret.h" +#include "p11test_case_readonly.h" + +#define MESSAGE_TO_SIGN "Simple message for signing & verifying. " \ + "It needs to be little bit longer to fit also longer keys and allow the truncation.\n" + +const unsigned char *short_message = (unsigned char *) MESSAGE_TO_SIGN; + +static unsigned char * +pkcs7_pad_message(const unsigned char *message, unsigned long message_length, + unsigned long block_len, unsigned long *out_len) +{ + unsigned long pad_length = block_len - (message_length % block_len); + unsigned char *pad_message = malloc(message_length + pad_length); + if (pad_message == NULL) { + return NULL; + } + memcpy(pad_message, message, message_length); + memset(pad_message + message_length, (int)pad_length, pad_length); + *out_len = message_length + pad_length; + return pad_message; +} + +/* Perform encryption and decryption of a message using secret key referenced + * in the o object with mechanism defined by mech. + * + * NONE of the reasonable mechanisms support multipart encryption/decryption + * + * Returns + * * 0 for successful Sign&Verify sequence or skipped test (unsupported mechanism, key, ...) + * * 1 for failure + * Serious errors terminate the execution. + */ +int test_secret_encrypt_decrypt(test_cert_t *o, token_info_t *info, test_mech_t *mech, + CK_ULONG message_length, int multipart) +{ + CK_BYTE *message = NULL; + CK_BYTE *dec_message = NULL; + CK_BYTE iv[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + CK_AES_CTR_PARAMS ctr_params = { 64, {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CK_BYTE aad[] = {0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; + CK_GCM_PARAMS gcm_params = { + .pIv = (void *)iv, + .ulIvLen = 16, + .ulIvBits = 64, + .pAAD = aad, /* TODO: SoftHSM crashes without AAD */ + .ulAADLen = sizeof(aad), + .ulTagBits = 128, + }; + CK_CCM_PARAMS ccm_params = { + .ulDataLen = message_length, + .pNonce = (void *)iv, + .ulNonceLen = 13, + .pAAD = aad, + .ulAADLen = sizeof(aad), + .ulMACLen = 16, + }; + int dec_message_length = 0; + unsigned char *enc_message = NULL; + int enc_message_length, rv; + + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing secret key", o->id_str); + return 1; + } + + if (o->key_type != CKK_AES) { + debug_print(" [ KEY %s ] Skip non-AES key for encryption", o->id_str); + return 0; + } + + switch (mech->mech) { + case CKM_AES_CBC: + case CKM_AES_CBC_PAD: + case CKM_AES_CTS: + case CKM_AES_OFB: + case CKM_AES_CFB8: + case CKM_AES_CFB128: + mech->params = &iv; + mech->params_len = sizeof(iv); + break; + case CKM_AES_CTR: + mech->params = &ctr_params; + mech->params_len = sizeof(ctr_params); + break; + case CKM_AES_GCM: + mech->params = &gcm_params; + mech->params_len = sizeof(gcm_params); + break; + case CKM_AES_CCM: + mech->params = &ccm_params; + mech->params_len = sizeof(ccm_params); + break; + case CKM_AES_ECB: + /* No parameters needed */ + break; + default: + debug_print(" [SKIP %s ] Unknown mechanism %s", o->id_str, get_mechanism_name(mech->mech)); + return 0; + } + if (mech->mech == CKM_AES_CBC || mech->mech == CKM_AES_ECB) { + /* This mechanism requires the blocks to be aligned to block size */ + message = pkcs7_pad_message(short_message, message_length, 16, &message_length); + } else { + message = (CK_BYTE *)strndup(MESSAGE_TO_SIGN, message_length); + } + + debug_print(" [ KEY %s ] Encrypt message using CKM_%s", + o->id_str, get_mechanism_name(mech->mech)); + enc_message_length = encrypt_message(o, info, message, message_length, + mech, &enc_message); + if (enc_message_length <= 0) { + mech->params = NULL; + mech->params_len = 0; + free(enc_message); + free(message); + return 1; + } + + debug_print(" [ KEY %s ] Decrypt message", o->id_str); + dec_message_length = decrypt_message(o, info, enc_message, + enc_message_length, mech, &dec_message); + free(enc_message); + if (dec_message_length <= 0) { + mech->params = NULL; + mech->params_len = 0; + free(message); + return 1; + } + + if (memcmp(dec_message, message, dec_message_length) == 0 + && (unsigned int) dec_message_length == message_length) { + debug_print(" [ OK %s ] Text decrypted successfully.", o->id_str); + mech->result_flags |= FLAGS_DECRYPT; + rv = 0; + } else { + dec_message[dec_message_length] = '\0'; + debug_print(" [ ERROR %s ] Text decryption failed. Recovered text: %s", + o->id_str, dec_message); + rv = 1; + } + mech->params = NULL; + mech->params_len = 0; + free(dec_message); + free(message); + return rv; +} + +/* Perform signature and verification of a message using secret key referenced + * in the o object with mechanism defined by mech. Message length can be + * specified using argument message_length. + * + * Returns + * * 0 for successful Sign&Verify sequence or skipped test (unsupported mechanism, key, ...) + * * 1 for failure + * Serious errors terminate the execution. + */ +int test_secret_sign_verify(test_cert_t *o, token_info_t *info, test_mech_t *mech, + CK_ULONG message_length, int multipart) +{ + CK_BYTE *message = NULL; + CK_ULONG sig_len = 42; + CK_BYTE *sign = NULL; + CK_ULONG sign_length = 0; + int rv = 0; + + if (message_length > strlen(MESSAGE_TO_SIGN)) { + fail_msg("Truncate (%lu) is longer than the actual message (%lu)", + message_length, strlen(MESSAGE_TO_SIGN)); + return 0; + } + + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing secret key handle", o->id_str); + return 1; + } + + if (o->key_type != CKK_AES) { + debug_print(" [SKIP %s ] Skip non-AES key", o->id_str); + return 0; + } + + if (mech->mech == CKM_AES_CMAC) { + message = (CK_BYTE *) strndup(MESSAGE_TO_SIGN, message_length); + } else if (mech->mech == CKM_AES_CMAC_GENERAL) { + message = (CK_BYTE *) strndup(MESSAGE_TO_SIGN, message_length); + /* This mechanism requires parameter denoting the requested output length */ + mech->params = &sig_len; + mech->params_len = sizeof(sig_len); + } else { + debug_print(" [SKIP %s ] Unknown mechanism", o->id_str); + return 0; + } + + debug_print(" [ KEY %s ] Signing message of length %lu using CKM_%s", + o->id_str, message_length, get_mechanism_name(mech->mech)); + rv = sign_message(o, info, message, message_length, mech, &sign, multipart); + if (rv <= 0) { + mech->params = NULL; + mech->params_len = 0; + free(message); + return 1; + } + sign_length = (unsigned long) rv; + + debug_print(" [ KEY %s ] Verify message signature", o->id_str); + rv = verify_message(o, info, message, message_length, mech, + sign, sign_length, multipart); + mech->params = NULL; + mech->params_len = 0; + free(sign); + free(message); + /* the semantics is different in the verify function */ + return rv == 1 ? 0 : 1; +} + +void secret_tests(void **state) +{ + unsigned int i; + size_t j; + int errors = 0; + token_info_t *info = (token_info_t *) *state; + test_certs_t objects; + + test_certs_init(&objects); + + P11TEST_START(info); + search_for_all_objects(&objects, info); + + /* Make sure to try the pkcs11 functions */ + info->verify_support = 1; + info->encrypt_support = 1; + + debug_print("Check operations on secret keys.\n"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + if (o->key_type != CKK_AES) + continue; + /* Ignore if there is missing private key */ + if (o->private_handle == CK_INVALID_HANDLE) + continue; + + for (j = 0; j < o->num_mechs; j++) { + if (o->key_type == CKK_AES) { + if (o->mechs[j].usage_flags & CKF_SIGN) { + errors += test_secret_sign_verify(&(objects.data[i]), + info, &(o->mechs[j]), 42, 0); + } + if (o->mechs[j].usage_flags & CKF_DECRYPT) { + errors += test_secret_encrypt_decrypt(&(objects.data[i]), + info, &(o->mechs[j]), 42, 0); + } + } + } + } + + /* print summary */ + printf("[KEY ID] [LABEL]\n"); + printf("[ TYPE ] [ SIZE ] [SIGN&VERIFY] [ENC&DECRYPT]\n"); + P11TEST_DATA_ROW(info, 4, + 's', "KEY ID", + 's', "MECHANISM", + 's', "SIGN&VERIFY WORKS", + 's', "ENCRYPT&DECRYPT WORKS"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + + if (o->key_type != CKK_AES) + continue; + + printf("\n[%-6s] [%s]\n", + o->id_str, + o->label); + printf("[ %s ] [%6lu] [%s%s] [%s%s]\n", + "AES ", + o->bits, + o->sign ? "[./] " : "[ ] ", + o->verify ? " [./] " : " [ ] ", + o->encrypt ? "[./] " : "[ ] ", + o->decrypt ? " [./] " : " [ ] "); + if (!o->sign && !o->verify && !o->encrypt && !o->decrypt) { + printf(" no usable attributes found ... ignored\n"); + continue; + } + if (o->private_handle == CK_INVALID_HANDLE) { + continue; + } + for (j = 0; j < o->num_mechs; j++) { + test_mech_t *mech = &o->mechs[j]; + if ((mech->usage_flags & (CKF_SIGN|CKF_DECRYPT)) == 0) { + /* not applicable mechanisms are skipped */ + continue; + } + printf(" [ %-11s ] [ %s ] [ %s ]\n", + get_mechanism_name(mech->mech), + mech->result_flags & FLAGS_SIGN_ANY ? "[./]" : " ", + mech->result_flags & FLAGS_DECRYPT_ANY ? "[./]" : " "); + if ((mech->result_flags & FLAGS_SIGN_ANY) == 0 && + (mech->result_flags & FLAGS_DECRYPT_ANY) == 0) + continue; /* skip empty rows for export */ + P11TEST_DATA_ROW(info, 4, + 's', o->id_str, + 's', get_mechanism_name(mech->mech), + 's', mech->result_flags & FLAGS_SIGN_ANY ? "YES" : "", + 's', mech->result_flags & FLAGS_DECRYPT_ANY ? "YES" : ""); + } + } + printf(" Sign Attribute ----^ ^ ^ ^ ^ ^---- Decrypt Attribute\n"); + printf(" Sign&Verify works ----' | | '------- Enc&Dec works\n"); + printf(" Verify Attribute --------' '---------- Encrypt Attribute\n"); + + clean_all_objects(&objects); + if (errors > 0) + P11TEST_FAIL(info, "Not all the secret key operation worked."); + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_secret.h b/src/tests/p11test/p11test_case_secret.h new file mode 100644 index 0000000000..753c644f4f --- /dev/null +++ b/src/tests/p11test/p11test_case_secret.h @@ -0,0 +1,26 @@ +/* + * p11test_case_secret.h: Check the functionality of operations with secret keys + * + * Copyright (C) 2021 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_common.h" +#include "p11test_case_readonly.h" + +void secret_tests(void **state); + diff --git a/src/tests/p11test/p11test_case_usage.c b/src/tests/p11test/p11test_case_usage.c new file mode 100644 index 0000000000..cba1369034 --- /dev/null +++ b/src/tests/p11test/p11test_case_usage.c @@ -0,0 +1,169 @@ +/* + * p11test_case_usage.c: Check if the usage flags are sane + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ +#include "p11test_case_usage.h" + +void usage_test(void **state) { + unsigned int i; + int errors = 0; + token_info_t *info = (token_info_t *) *state; + test_certs_t objects; + + test_certs_init(&objects); + + P11TEST_START(info); + search_for_all_objects(&objects, info); + + debug_print("Check if the usage flags are sane.\n"); + for (i = 0; i < objects.count; i++) { + /* Ignore if there is missing private key */ + if (objects.data[i].private_handle == CK_INVALID_HANDLE) + continue; + + /* The usage flags are paired */ + if (objects.data[i].sign && !objects.data[i].verify) { + errors++; + fprintf(stderr, " [ ERROR %s ] If Sign is set, Verify should be set too.\n", + objects.data[i].id_str); + } + if (objects.data[i].decrypt && !objects.data[i].encrypt) { + errors++; + fprintf(stderr, " [ ERROR %s ] If Decrypt is set, Encrypt should be set too.\n", + objects.data[i].id_str); + } + if (objects.data[i].unwrap && !objects.data[i].wrap) { + errors++; + fprintf(stderr, " [ ERROR %s ] If Unwrap is set, Wrap should be set too.\n", + objects.data[i].id_str); + } + if (objects.data[i].encapsulate && !objects.data[i].decapsulate) { + errors++; + fprintf(stderr, " [ ERROR %s ] If Encapsulate is set, Decapsulate should be set too.\n", + objects.data[i].id_str); + } + if (objects.data[i].derive_pub != objects.data[i].derive_priv) { + errors++; + fprintf(stderr, " [ ERROR %s ] Derive should be set on both private and public part.\n", + objects.data[i].id_str); + } + + /* We have at least one usage flag for every key group */ + if (!objects.data[i].sign && !objects.data[i].verify && + !objects.data[i].encrypt && !objects.data[i].decrypt && + !objects.data[i].wrap && !objects.data[i].unwrap && + !objects.data[i].encapsulate && !objects.data[i].decapsulate && + !objects.data[i].derive_pub && !objects.data[i].derive_priv) { + errors++; + fprintf(stderr, " [ ERROR %s ] Key group should have at least one usage flag.\n", + objects.data[i].id_str); + } + } + + /* print summary */ + printf("[KEY ID] [LABEL]\n"); + printf("[ TYPE ] [ SIZE ] [PUBLIC] [SIGN&VERIFY] [ENC&DECRYPT] [WRAP&UNWR] [ENC&DECAPS] [ DERIVE ] [ALWAYS_AUTH]\n"); + P11TEST_DATA_ROW(info, 16, + 's', "KEY ID", + 's', "LABEL", + 's', "TYPE", + 's', "BITS", + 's', "VERIFY PUBKEY", + 's', "SIGN", + 's', "VERIFY", + 's', "ENCRYPT", + 's', "DECRYPT", + 's', "WRAP", + 's', "UNWRAP", + 's', "ENCAPSULATE", + 's', "DECAPSULATE", + 's', "DERIVE PUBLIC", + 's', "DERIVE PRIVATE", + 's', "ALWAYS AUTH"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + + printf("\n[%-6s] [%s]\n", o->id_str, o->label); + + /* Ignore if there is missing private key */ + if (objects.data[i].private_handle == CK_INVALID_HANDLE) + continue; + + printf("[%s] [%6lu] [ %s ] [%s%s] [%s%s] [%s %s] [%s %s] [%s%s] [ %s ]\n", + (o->key_type == CKK_RSA ? " RSA " : o->key_type == CKK_EC ? " EC " + : o->key_type == CKK_EC_EDWARDS ? "EDDSA " + : o->key_type == CKK_EC_MONTGOMERY ? "XEDDSA" + : o->key_type == CKK_AES ? " AES " + : o->key_type == CKK_GENERIC_SECRET ? "GENER " + : o->key_type == CKK_ML_DSA ? "ML-DSA" + : o->key_type == CKK_ML_KEM ? "ML-KEM" + : o->key_type == CKK_SLH_DSA ? "SLHDSA" + : " ?? "), + o->bits, + o->verify_public == 1 ? " ./ " : " ", + o->sign ? "[./] " : "[ ] ", + o->verify ? " [./] " : " [ ] ", + o->encrypt ? "[./] " : "[ ] ", + o->decrypt ? " [./] " : " [ ] ", + o->wrap ? "[./]" : "[ ]", + o->unwrap ? "[./]" : "[ ]", + o->encapsulate ? "[./]" : "[ ]", + o->decapsulate ? "[./]" : "[ ]", + o->derive_pub ? "[./]" : "[ ]", + o->derive_priv ? "[./]" : "[ ]", + o->always_auth ? "[./]" : "[ ]"); + P11TEST_DATA_ROW(info, 16, + 's', o->id_str, + 's', o->label, + 's', (o->key_type == CKK_RSA ? "RSA" : o->key_type == CKK_EC ? "EC" + : o->key_type == CKK_EC_EDWARDS ? "EDDSA" + : o->key_type == CKK_EC_MONTGOMERY ? "XEDDSA" + : o->key_type == CKK_AES ? "AES" + : o->key_type == CKK_GENERIC_SECRET ? "GEN" + : o->key_type == CKK_ML_DSA ? "ML-DSA" + : o->key_type == CKK_ML_KEM ? "ML-KEM" + : o->key_type == CKK_SLH_DSA ? "SLH-DSA" + : " ?? "), + 'd', o->bits, + 's', o->verify_public == 1 ? "YES" : "", + 's', o->sign ? "YES" : "", + 's', o->verify ? "YES" : "", + 's', o->encrypt ? "YES" : "", + 's', o->decrypt ? "YES" : "", + 's', o->wrap ? "YES" : "", + 's', o->unwrap ? "YES" : "", + 's', o->encapsulate ? "YES" : "", + 's', o->decapsulate ? "YES" : "", + 's', o->derive_pub ? "YES" : "", + 's', o->derive_priv ? "YES" : "", + 's', o->always_auth ? "YES" : ""); + } + printf(" Public == Cert -----^ ^-----^ ^-----^ ^----^ ^-----^ ^---^ ^\n"); + printf(" Sign & Verify Attributes ------' | | | | |\n"); + printf(" Encrypt & Decrypt Attributes ----------------' | | | |\n"); + printf(" Wrap & Unwrap Attributes ---------------------------------' | | |\n"); + printf(" Encapsulate & Decapsulate Attributes ----------------------------------' | |\n"); + printf(" Public and Private key Derive Attributes ------------------------------------------' |\n"); + printf(" Always Authenticate Attribute ------------------------------------------------------------------'\n"); + + clean_all_objects(&objects); + if (errors > 0) + P11TEST_FAIL(info, "Not all the usage flags were successfully verified. See the verbose log."); + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_usage.h b/src/tests/p11test/p11test_case_usage.h new file mode 100644 index 0000000000..a728890d89 --- /dev/null +++ b/src/tests/p11test/p11test_case_usage.h @@ -0,0 +1,25 @@ +/* + * p11test_case_usage.h: Check if the usage flags are sane + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_common.h" +#include "p11test_case_readonly.h" + +void usage_test(void **state); diff --git a/src/tests/p11test/p11test_case_wait.c b/src/tests/p11test/p11test_case_wait.c new file mode 100644 index 0000000000..2d88c3b02e --- /dev/null +++ b/src/tests/p11test/p11test_case_wait.c @@ -0,0 +1,59 @@ +/* + * p11test_case_wait.c: Test slot events (insert / remove) + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ +#include "p11test_case_wait.h" + +void wait_test(void **state) { + + token_info_t *info = (token_info_t *) *state; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_RV rv; + CK_SLOT_ID slot_id; + CK_SLOT_INFO slot_info; + int token_present = 0; + + P11TEST_START(info); + if (!info->interactive) { + fprintf(stderr, "To test wait, run in interactive mode (-i switch).\n"); + P11TEST_SKIP(info); + } + + do { + printf(" [ Waiting for slot event ... ]\n"); + + rv = fp->C_WaitForSlotEvent(0, &slot_id, NULL_PTR); + if (rv == CKR_FUNCTION_NOT_SUPPORTED) { + fprintf(stderr, "Function does not support call with blocking wait. Skipping.\n"); + skip(); + } else if (rv != CKR_OK) + P11TEST_FAIL(info, "C_WaitForSlotEvent: rv = 0x%.8lX\n", rv); + + rv = fp->C_GetSlotInfo(slot_id, &slot_info); + if (rv != CKR_OK) + P11TEST_FAIL(info, "C_GetSlotInfo: rv = 0x%.8lX\n", rv); + + token_present = ((slot_info.flags & CKF_TOKEN_PRESENT) != 0); + + printf(" [ Slot %lu ] %s\n", slot_id, slot_info.slotDescription); + printf(" Status: %s\n", + token_present ? "Token present": "No token"); + } while (!token_present); + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_wait.h b/src/tests/p11test/p11test_case_wait.h new file mode 100644 index 0000000000..36f75e2a0d --- /dev/null +++ b/src/tests/p11test/p11test_case_wait.h @@ -0,0 +1,24 @@ +/* + * p11test_case_wait.h: Test slot events (insert / remove) + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ +#include "p11test_case_common.h" + +void wait_test(void **state); + diff --git a/src/tests/p11test/p11test_case_wrap.c b/src/tests/p11test/p11test_case_wrap.c new file mode 100644 index 0000000000..e9c503d7e8 --- /dev/null +++ b/src/tests/p11test/p11test_case_wrap.c @@ -0,0 +1,670 @@ +/* + * p11test_case_wrap.c: Check the functionality of wrap mechanisms + * + * Copyright (C) 2021 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ +#include "p11test_case_wrap.h" +#include "p11test_case_readonly.h" + +#include +#include + +/* returns the new length of message after stripping the pkcs7 padding */ +static int +strip_pkcs7_padding(const unsigned char *message, unsigned long message_length, + unsigned long block_len) +{ + unsigned char pad_length = message[message_length - 1]; + + if (pad_length > block_len) { + return 0; + } + + return (int)message_length - pad_length; +} + +/* Perform encryption and decryption of a secret key using a PKCS#11 key referenced + * in the pkcs11_key object. The encryption is done in openssl and decryption in token. + + * + * Returns + * * 0 for successful Encrypt&Decrypt sequence + * * -1 for failure + * * 1 for skipped test (unsupported key type) + */ +static int +check_encrypt_decrypt_secret(CK_BYTE *plain_key, CK_ULONG plain_key_len, test_cert_t *pkcs11_key, + token_info_t *info) +{ + CK_BYTE iv[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *cipher = NULL; + unsigned char plaintext[42] = { + 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, + 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, + 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x6b, + }; + int plaintext_len = sizeof(plaintext); + unsigned char ciphertext[100]; + int ciphertext_len = sizeof(ciphertext); + test_mech_t aes_mech = {.mech = CKM_AES_CBC, .params = &iv, .params_len = sizeof(iv)}; + unsigned char *check = NULL; + int check_len = 0; + int rv, len; + + if (pkcs11_key->key_type != CKK_AES) { + fprintf(stderr, " AES supported only\n"); + return 1; + } + + /* First, do the encryption dance with OpenSSL */ + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) { + EVP_CIPHER_CTX_free(ctx); + fprintf(stderr, " EVP_CIPHER_CTX_new failed\n"); + return -1; + } + + if (plain_key_len == 32) { + cipher = EVP_aes_256_cbc(); + } else if (plain_key_len == 16) { + cipher = EVP_aes_128_cbc(); + } else { + EVP_CIPHER_CTX_free(ctx); + fprintf(stderr, " Invalid key length %lu", plain_key_len); + return -1; + } + rv = EVP_EncryptInit_ex(ctx, cipher, NULL, plain_key, iv); + if (rv != 1) { + EVP_CIPHER_CTX_free(ctx); + fprintf(stderr, " EVP_EncryptInit_ex failed\n"); + return -1; + } + rv = EVP_EncryptUpdate(ctx, ciphertext, &ciphertext_len, plaintext, plaintext_len); + if (rv != 1) { + EVP_CIPHER_CTX_free(ctx); + fprintf(stderr, " EVP_EncryptUpdate failed\n"); + return -1; + } + rv = EVP_EncryptFinal_ex(ctx, ciphertext + ciphertext_len, &len); + EVP_CIPHER_CTX_free(ctx); + if (rv != 1) { + fprintf(stderr, " EVP_EncryptFinal_ex failed\n"); + return -1; + } + ciphertext_len += len; + /* Now, decrypt with the PKCS#11 */ + check_len = decrypt_message(pkcs11_key, info, ciphertext, ciphertext_len, &aes_mech, &check); + if (check_len < 0) { + fprintf(stderr, " Cannot decrypt message\n"); + return -1; + } + + check_len = strip_pkcs7_padding(check, check_len, 16); + if (check_len <= 0) { + free(check); + fprintf(stderr, " Failed to strip PKCS#7 padding\n"); + return -1; + } + if (check_len == plaintext_len && memcmp(plaintext, check, plaintext_len) == 0) { + free(check); + return 0; + } + /* else error */ + fprintf(stderr, " Decrypted message does not match (%d, %d)\n", check_len, plaintext_len); + fprintf(stderr, "\nplaintext:\n"); + for (int i = 0; i < plaintext_len; i++) { + fprintf(stderr, ":%x", plaintext[i]); + } + fprintf(stderr, "\ncheck:\n"); + for (int i = 0; i < check_len; i++) { + fprintf(stderr, ":%x", check[i]); + } + fprintf(stderr, "\n"); + free(check); + return -1; +} + +/* Perform key wrapping of a secret or private key on token key using a public key referenced + * in the o object. The wrapped key is then decrypted to verify the operation was successful, if possible. + * + * Returns + * * 0 for successful Wrapping or skipped test + * * 1 for failure + */ +static int +test_wrap(test_cert_t *o, token_info_t *info, test_cert_t *key, test_mech_t *mech) +{ + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM mechanism = { mech->mech, NULL_PTR, 0 }; + CK_MECHANISM tmp_mechanism = {mech->mech, NULL_PTR, 0}; + /* SoftHSM supports only SHA1 with OAEP encryption */ + CK_RSA_PKCS_OAEP_PARAMS oaep_params = {CKM_SHA_1, CKG_MGF1_SHA1, CKZ_DATA_SPECIFIED, NULL, 0}; + CK_BYTE iv[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + CK_AES_CTR_PARAMS ctr_params = { 64, {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CK_BYTE aad[] = {0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; + CK_GCM_PARAMS gcm_params = { + .pIv = (void *)iv, + .ulIvLen = 16, + .ulIvBits = 64, + .pAAD = aad, /* TODO: SoftHSM crashes without AAD */ + .ulAADLen = sizeof(aad), + .ulTagBits = 128, + }; + /* It is very unclear from the PKCS#11 specs what + * value we should provide here to DataLen for + * wrapping and unwrapping operation. */ + CK_CCM_PARAMS ccm_params = { + .ulDataLen = key->bits, + .pNonce = (void *)iv, + .ulNonceLen = 13, + .pAAD = aad, + .ulAADLen = sizeof(aad), + .ulMACLen = 16, + }; + //unsigned char key[16]; + CK_BYTE *wrapped = NULL; + CK_ULONG wrapped_len = 0; + CK_BYTE *plain = NULL; + CK_ULONG plain_len = 0; + CK_RV rv; + + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", o->id_str); + return 0; + } + + if (o->key_type != CKK_RSA && o->key_type != CKK_AES) { + debug_print(" [ SKIP %s ] Skip non-RSA and non-AES key for wrapping", o->id_str); + return 0; + } + + debug_print(" [ KEY %s ] Wrap a key [%s] (%s) using CKM_%s", o->id_str, key->id_str, + get_key_type(key), get_mechanism_name(mech->mech)); + /* RSA mechanisms */ + switch (mech->mech) { + case CKM_RSA_X_509: + if (o->bits < key->bits) { + debug_print(" [SKIP %s ] The wrapping key too small", o->id_str); + return 0; + } + break; + case CKM_RSA_PKCS: + if (o->bits - 11 < key->bits) { + debug_print(" [SKIP %s ] The wrapping key too small", o->id_str); + return 0; + } + break; + case CKM_RSA_PKCS_OAEP: + if (o->bits - 2 - 2*SHA_DIGEST_LENGTH < key->bits) { + debug_print(" [SKIP %s ] The wrapping key too small", o->id_str); + return 0; + } + mech->params = &oaep_params; + mech->params_len = sizeof(oaep_params); + break; + /* AES mechanisms */ + case CKM_AES_CBC: + case CKM_AES_CBC_PAD: + case CKM_AES_CTS: + case CKM_AES_OFB: + case CKM_AES_CFB8: + case CKM_AES_CFB128: + mech->params = &iv; + mech->params_len = sizeof(iv); + break; + case CKM_AES_CTR: + mech->params = &ctr_params; + mech->params_len = sizeof(ctr_params); + break; + case CKM_AES_GCM: + mech->params = &gcm_params; + mech->params_len = sizeof(gcm_params); + break; + case CKM_AES_CCM: + /* The CCM parameters need to match with the input data length + * for encryption but we do not know the size for asymmetric + * keys so try to figure out by querying size in different mode */ + tmp_mechanism.mechanism = CKM_AES_CTR; + tmp_mechanism.pParameter = &ctr_params; + tmp_mechanism.ulParameterLen = sizeof(ctr_params); + rv = fp->C_WrapKey(info->session_handle, &tmp_mechanism, o->public_handle, + key->private_handle, wrapped, &wrapped_len); + if (rv != CKR_OK) { + mech->params = NULL; + mech->params_len = 0; + debug_print(" [ KEY %s ] Failed to find CCM param dataLen", o->id_str); + return 1; + } + ccm_params.ulDataLen = wrapped_len; + mech->params = &ccm_params; + mech->params_len = sizeof(ccm_params); + break; + case CKM_AES_ECB: + case CKM_AES_KEY_WRAP: + case CKM_AES_KEY_WRAP_PAD: + /* Nothing special ... */ + break; + default: + debug_print(" [ KEY %s ] Unknown wrapping mechanism %s", + o->id_str, get_mechanism_name(mech->mech)); + return 1; + } + + /* Get the wrapped size */ + mechanism.pParameter = mech->params; + mechanism.ulParameterLen = mech->params_len; + rv = fp->C_WrapKey(info->session_handle, &mechanism, o->public_handle, key->private_handle, + wrapped, &wrapped_len); + if (rv != CKR_OK) { + mech->params = NULL; + mech->params_len = 0; + fprintf(stderr, " C_WrapKey: rv = 0x%.8lX\n", rv); + return 1; + } + wrapped = malloc(wrapped_len); + if (wrapped == NULL) { + mech->params = NULL; + mech->params_len = 0; + fprintf(stderr, "%s: malloc failed", __func__); + return 1; + } + /* Wrap the key using public key through PKCS#11 */ + rv = fp->C_WrapKey(info->session_handle, &mechanism, o->public_handle, key->private_handle, + wrapped, &wrapped_len); + if (rv == CKR_KEY_NOT_WRAPPABLE) { + /* nothing we can do about this: skip */ + mech->params = NULL; + mech->params_len = 0; + fprintf(stderr, " [SKIP %s ] CKR_KEY_NOT_WRAPPABLE\n", o->id_str); + free(wrapped); + return 0; + } + if (rv != CKR_OK) { + mech->params = NULL; + mech->params_len = 0; + fprintf(stderr, " C_WrapKey: rv = 0x%.8lX\n", rv); + free(wrapped); + return 1; + } + + if (mech->mech == CKM_AES_KEY_WRAP || mech->mech == CKM_AES_KEY_WRAP_PAD) { + /* good enough for now -- I dont know how to check these */ + free(wrapped); + goto out; + } + /* OK, we have wrapped key. Now, check it is really the key on the card. + * We need to decipher the wrapped key with the wrapping key, which + * should be generally the reverse operation to the wrapping for the + * simple wrapping mechanisms and which should give us a plain key. + */ + rv = decrypt_message(o, info, wrapped, wrapped_len, mech, &plain); + free(wrapped); + mech->params = NULL; + mech->params_len = 0; + if (rv <= 0) { + debug_print(" [ KEY %s ] Unable to decrypt the wrapped key", o->id_str); + return 1; + } + plain_len = rv; + /* + * Then we need need to check it against something to make sure we have + * the right key. There are two ways: + * 1) The key is publicly readable through CKA_VALUE (not the case most of the time) + * 2) We encrypt something with a assumed key and decrypt it with the card key + */ + if (key->value) { + if (plain_len == key->bits/8 && memcmp(plain, key->value, plain_len) == 0) { + debug_print(" [ OK %s ] Wrapped key recovered correctly", o->id_str); + } else { + fprintf(stderr, " [ ERROR %s ] Wrapped key does not match\n", o->id_str); + fprintf(stderr, "\nplaintext:\n"); + if (plain != NULL) { + for (unsigned long i = 0; i < plain_len; i++) { + fprintf(stderr, ":%x", plain[i]); + } + } else { + fprintf(stderr, "NULL"); + } + fprintf(stderr, "\nkey->value:\n"); + for (unsigned long i = 0; i < key->bits / 8; i++) { + fprintf(stderr, ":%x", key->value[i]); + } + fprintf(stderr, "\n"); + return 1; + } + free(plain); + } else if (key->key_type == CKK_AES) { + rv = check_encrypt_decrypt_secret(plain, plain_len, key, info); + free(plain); + if (rv == 0) { + debug_print(" [ OK %s ] Decrypted message matches", o->id_str); + } else { + fprintf(stderr, " [ ERROR %s ] Decrypted message does not match\n", o->id_str); + return 1; + } + } + +out: + debug_print(" [ OK %s ] Key wrapping works.", o->id_str); + if (key->key_type == CKK_AES) { + mech->result_flags |= FLAGS_WRAP_SYM; + } else { + mech->result_flags |= FLAGS_WRAP; + } + return 0; +} + +static int +test_unwrap_aes(test_cert_t *o, token_info_t *info, test_mech_t *mech) +{ + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM mechanism = {mech->mech, NULL_PTR, 0}; + /* SoftHSM supports only SHA1 with OAEP encryption */ + CK_RSA_PKCS_OAEP_PARAMS oaep_params = {CKM_SHA_1, CKG_MGF1_SHA1, CKZ_DATA_SPECIFIED, NULL, 0}; + CK_BYTE key[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + CK_BYTE *key_padded = key; + CK_ULONG key_len = sizeof(key); + CK_ULONG key_padded_len = sizeof(key); + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_AES; + CK_BBOOL _true = CK_TRUE; + CK_BYTE new_id[] = {0x00, 0xff, 0x42}; + CK_BYTE new_label[] = "Unwrapped key"; + CK_ATTRIBUTE template[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + {CKA_KEY_TYPE, &keyType, sizeof(keyType)}, + {CKA_ENCRYPT, &_true, sizeof(_true)}, + {CKA_DECRYPT, &_true, sizeof(_true)}, + {CKA_TOKEN, &_true, sizeof(_true)}, + {CKA_ID, &new_id, sizeof(new_id)}, + {CKA_LABEL, &new_label, sizeof(new_label)}, + {CKA_VALUE_LEN, &key_len, sizeof(key_len)}, /* keep this one last! */ + }; + CK_ULONG template_len = sizeof(template) / sizeof(template[0]); + size_t wrapped_len; + CK_BYTE *wrapped = NULL; + test_cert_t tmp_key = {0}; + CK_RV rv; + + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", o->id_str); + return 1; + } + + if (o->key_type != CKK_RSA && o->key_type != CKK_AES) { + debug_print(" [ KEY %s ] Skip non-RSA and non-AES key for wrapping", o->id_str); + return 1; + } + + debug_print(" [ KEY %s ] Unwrap a AES key using CKM_%s", o->id_str, get_mechanism_name(mech->mech)); + + /* Wrap/encrypt the key and set up the parameters */ + switch (mech->mech) { + case CKM_RSA_PKCS_OAEP: + mech->params = &oaep_params; + mech->params_len = sizeof(oaep_params); + /* fall through */ + case CKM_RSA_X_509: + if (mech->mech == CKM_RSA_X_509 && (key_padded = rsa_x_509_pad_message(key, &key_padded_len, o, 1)) == NULL) { + debug_print(" [ERROR %s ] Could not pad message", o->id_str); + return 1; + } + /* fall through */ + case CKM_RSA_PKCS: + wrapped_len = encrypt_message(o, info, key_padded, key_padded_len, mech, &wrapped); + if (wrapped_len <= 0) { + if (key != key_padded) { + free(key_padded); + } + debug_print(" [ERROR %s ] Failed to encrypt message with public key to unwrap", o->id_str); + return 1; + } + break; + /* AES mechanisms: TODO + case CKM_AES_CBC: + case CKM_AES_CBC_PAD: + case CKM_AES_ECB: + mech->params = &iv; + mech->params_len = sizeof(iv); + break; + case CKM_AES_CTR: + mech->params = &ctr_params; + mech->params_len = sizeof(ctr_params); + break; + case CKM_AES_GCM: + mech->params = &gcm_params; + mech->params_len = sizeof(gcm_params); + break; + case CKM_AES_KEY_WRAP: + case:CKM_AES_KEY_WRAP_PAD: + // Nothing special ... + break; */ + default: + debug_print(" [ KEY %s ] Unknown wrapping mechanism %s", o->id_str, + get_mechanism_name(mech->mech)); + return 1; + } + + mechanism.pParameter = mech->params; + mechanism.ulParameterLen = mech->params_len; + rv = fp->C_UnwrapKey(info->session_handle, &mechanism, o->private_handle, wrapped, wrapped_len, + template, template_len, &tmp_key.private_handle); + if (rv == CKR_ATTRIBUTE_READ_ONLY) { + /* The SoftHSM chokes on CKA_VALUE_LEN but MyEID requires it so first try with the attribute and retry + * without to make softhsm happy */ + template_len--; + rv = fp->C_UnwrapKey(info->session_handle, &mechanism, o->private_handle, wrapped, + wrapped_len, template, template_len, &tmp_key.private_handle); + } + free(wrapped); + if (rv != CKR_OK) { + if (key != key_padded) { + free(key_padded); + } + mech->params = NULL; + mech->params_len = 0; + fprintf(stderr, " C_UnwrapKey: rv = 0x%.8lX\n", rv); + return -1; + } + + /* now, check the key */ + /* Simple test might be the attempt to encrypt some data with the key and check it can be decrypted with + * the plaintext secret */ + tmp_key.public_handle = tmp_key.private_handle; + tmp_key.key_type = CKK_AES; + tmp_key.sign = CK_TRUE; + tmp_key.verify = CK_TRUE; + tmp_key.encrypt = CK_TRUE; + tmp_key.decrypt = CK_TRUE; + tmp_key.wrap = CK_TRUE; + tmp_key.unwrap = CK_TRUE; + tmp_key.extractable = CK_TRUE; + tmp_key.bits = CK_TRUE; + rv = check_encrypt_decrypt_secret(key, sizeof(key), &tmp_key, info); + if (key != key_padded) { + free(key_padded); + } + destroy_tmp_object(info, tmp_key.private_handle); + if (rv != 0) { + fprintf(stderr, " [ ERROR %s ] Decrypted message does not match\n", o->id_str); + return -1; + } + debug_print(" [ OK %s ] Decrypted message matches", o->id_str); + mech->result_flags |= FLAGS_UNWRAP_SYM; + return 0; +} + +void +wrap_tests(void **state) +{ + unsigned int i; + size_t j; + int errors = 0; + token_info_t *info = (token_info_t *) *state; + test_certs_t objects; + test_cert_t *aes_key = NULL, *aes2_key = NULL; + test_cert_t *rsa_key = NULL, *rsa2_key = NULL; + test_cert_t *ec_key = NULL; + + test_certs_init(&objects); + + P11TEST_START(info); + search_for_all_objects(&objects, info); + + /* Find keys to wrap */ + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + if (aes_key == NULL && o->key_type == CKK_AES && o->extractable) { + aes_key = o; + } else if (aes2_key == NULL && o->key_type == CKK_AES && o->extractable) { + aes2_key = o; + } else if (rsa_key == NULL && o->key_type == CKK_RSA && o->extractable) { + rsa_key = o; + } else if (rsa2_key == NULL && o->key_type == CKK_RSA && o->extractable) { + rsa2_key = o; + } else if (ec_key == NULL && o->key_type == CKK_EC && o->extractable) { + ec_key = o; + } + } + + debug_print("Check if the wrap operation works.\n"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + /* Ignore if there is missing private key */ + if (o->private_handle == CK_INVALID_HANDLE) + continue; + + for (j = 0; j < o->num_mechs; j++) { + /* + if ((o->mechs[j].usage_flags & CKF_WRAP) == 0 || !o->wrap) + continue; + if ((o->mechs[j].usage_flags & CKF_UNWRAP) == 0 || !o->unwrap) + continue; + */ + if ((o->mechs[j].usage_flags & (CKF_WRAP|CKF_UNWRAP)) == 0) + continue; + + switch (o->key_type) { + case CKK_RSA: + /* We probably can not wrap one key with itself */ + if (rsa_key && o != rsa_key) { + errors += test_wrap(o, info, rsa_key, &(o->mechs[j])); + } else if (rsa2_key && o != rsa2_key) { + errors += test_wrap(o, info, rsa2_key, &(o->mechs[j])); + } + if (aes_key) { + errors += test_wrap(o, info, aes_key, &(o->mechs[j])); + } + if (ec_key) { + errors += test_wrap(o, info, ec_key, &(o->mechs[j])); + } + errors += test_unwrap_aes(o, info, &(o->mechs[j])); + break; + case CKK_AES: + /* We probably can not wrap one key with itself */ + if (aes_key && o != aes_key) { + errors += test_wrap(o, info, aes_key, &(o->mechs[j])); + } else if (aes2_key && o != aes2_key) { + errors += test_wrap(o, info, aes2_key, &(o->mechs[j])); + } + if (rsa_key) { + errors += test_wrap(o, info, rsa_key, &(o->mechs[j])); + } + /* TODO differentiate the RSA and EC key */ + if (ec_key) { + errors += test_wrap(o, info, ec_key, &(o->mechs[j])); + } + // errors += test_unwrap_aes(o, info, &(o->mechs[j])); + break; + default: + /* Other keys do not support wrapping */ + break; + } + } + } + + /* print summary */ + printf("[KEY ID] [EXTRACTABLE] [LABEL]\n"); + printf("[ TYPE ] [ SIZE ] [ WRAP ] [UNWRAP]\n"); + P11TEST_DATA_ROW(info, 4, + 's', "KEY ID", + 's', "MECHANISM", + 's', "WRAP WORKS", + 's', "UNWRAP WORKS"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + if (o->key_type != CKK_RSA && o->key_type != CKK_AES) + continue; + + printf("\n[%-6s] [ %s ] [%s]\n", + o->id_str, + o->extractable ? "./" : " ", + o->label); + printf("[ %s ] [%6lu] [ [%s] ] [ [%s] ]\n", + (o->key_type == CKK_RSA ? "RSA " : + o->key_type == CKK_AES ? "AES " : " ?? "), + o->bits, + o->wrap ? "./" : " ", + o->unwrap ? "./" : " "); + /* the attributes are sometimes confusing + if (!o->wrap && !o->unwrap) { + printf(" no usable attributes found ... ignored\n"); + continue; + } */ + if (o->private_handle == CK_INVALID_HANDLE) { + continue; + } + for (j = 0; j < o->num_mechs; j++) { + test_mech_t *mech = &o->mechs[j]; + if ((mech->usage_flags & (CKF_WRAP | CKF_UNWRAP)) == 0) { + /* not applicable mechanisms are skipped */ + continue; + } + printf(" [ %-24s ] [%s][%s] [%s][%s]\n", + get_mechanism_name(mech->mech), + mech->result_flags & FLAGS_WRAP_SYM ? "./" : " ", + mech->result_flags & FLAGS_WRAP ? "./" : " ", + mech->result_flags & FLAGS_UNWRAP_SYM ? "./" : " ", + mech->result_flags & FLAGS_UNWRAP ? "./" : " "); + if ((mech->result_flags & (FLAGS_WRAP | FLAGS_UNWRAP)) == 0) + continue; /* skip empty rows for export */ + P11TEST_DATA_ROW(info, 6, + 's', o->id_str, + 's', get_mechanism_name(mech->mech), + 's', mech->result_flags & FLAGS_WRAP_SYM ? "YES" : "", + 's', mech->result_flags & FLAGS_WRAP ? "YES" : "", + 's', mech->result_flags & FLAGS_UNWRAP_SYM ? "YES" : "", + 's', mech->result_flags & FLAGS_UNWRAP ? "YES" : ""); + } + } + printf(" Wrapping symmetric key works --^ ^ ^ ^- Unwrapping asymmetric key works\n"); + printf(" Wrapping asymmetric key works -----' '------- Unwrapping symmetric key works\n"); + + clean_all_objects(&objects); + if (errors > 0) + P11TEST_FAIL(info, "Not all the wrap/unwrap mechanisms worked."); + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_wrap.h b/src/tests/p11test/p11test_case_wrap.h new file mode 100644 index 0000000000..2e861a2b7a --- /dev/null +++ b/src/tests/p11test/p11test_case_wrap.h @@ -0,0 +1,27 @@ +/* + * p11test_case_wrap.h: Check the functionality of wrap mechanisms + * + * Copyright (C) 2021 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_case_common.h" +#include "p11test_case_readonly.h" + +void wrap_tests(void **state); + + diff --git a/src/tests/p11test/p11test_common.h b/src/tests/p11test/p11test_common.h new file mode 100644 index 0000000000..4a2594fc6a --- /dev/null +++ b/src/tests/p11test/p11test_common.h @@ -0,0 +1,115 @@ +/* + * p11test_common.h: Test suite shared declarations for PKCS#11 API + * + * Copyright (C) 2016 Martin Strhársky + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifndef P11TEST_COMMON_H +#define P11TEST_COMMON_H +#include "config.h" +#include +#include +#include +#include +#include +#include "pkcs11/pkcs11.h" +#include "libopensc/sc-ossl-compat.h" + +#define MAX_MECHS 256 +#define MAX_PSS_MECHS 1024 + +#define debug_print(fmt, ...) \ + do { \ + if (debug_flag) { \ + fprintf(stderr, fmt "\n", ##__VA_ARGS__); \ + } \ + } while (0) + +#define FLAGS_SIGN 0x001 +#define FLAGS_SIGN_OPENSSL 0x002 +#define FLAGS_SIGN_ANY ( FLAGS_SIGN | FLAGS_SIGN_OPENSSL ) +#define FLAGS_DECRYPT 0x004 +#define FLAGS_DECRYPT_OPENSSL 0x008 +#define FLAGS_DECRYPT_ANY ( FLAGS_DECRYPT | FLAGS_DECRYPT_OPENSSL ) +#define FLAGS_DERIVE 0x010 +#define FLAGS_WRAP 0x020 +#define FLAGS_WRAP_SYM 0x040 +#define FLAGS_UNWRAP 0x080 +#define FLAGS_UNWRAP_SYM 0x100 + +typedef struct { + char *outfile; + FILE *fd; + int in_test; + int first; + int in_data; + int first_data; +} log_context_t; + +typedef struct { + CK_MECHANISM_TYPE mech; + /* RSA-PSS parameters */ + CK_MECHANISM_TYPE hash; + CK_RSA_PKCS_MGF_TYPE mgf; + int salt; + /* generic parameters used for example for secret keys */ + void *params; + unsigned long params_len; + unsigned long usage_flags; + int result_flags; +} test_mech_t; + +typedef struct { + CK_FUNCTION_LIST_PTR function_pointer; + CK_SLOT_ID slot_id; + CK_SESSION_HANDLE session_handle; + CK_UTF8CHAR* pin; + size_t pin_length; + char *library_path; + unsigned int interactive; + log_context_t log; + + int verify_support; + int encrypt_support; + + test_mech_t rsa_mechs[MAX_MECHS]; + size_t num_rsa_mechs; + test_mech_t ec_mechs[MAX_MECHS]; + size_t num_ec_mechs; + test_mech_t ed_mechs[MAX_MECHS]; + size_t num_ed_mechs; + test_mech_t montgomery_mechs[MAX_MECHS]; + size_t num_montgomery_mechs; + test_mech_t ml_dsa_mechs[MAX_MECHS]; + size_t num_ml_dsa_mechs; + test_mech_t ml_kem_mechs[MAX_MECHS]; + size_t num_ml_kem_mechs; + test_mech_t slh_dsa_mechs[MAX_MECHS]; + size_t num_slh_dsa_mechs; + test_mech_t aes_mechs[MAX_MECHS]; + size_t num_aes_mechs; + test_mech_t keygen_mechs[MAX_MECHS]; + size_t num_keygen_mechs; +} token_info_t; + +extern token_info_t token; +extern int debug_flag; + +#endif /* P11TEST_COMMON_H */ + diff --git a/src/tests/p11test/p11test_helpers.c b/src/tests/p11test/p11test_helpers.c new file mode 100644 index 0000000000..69df47c1be --- /dev/null +++ b/src/tests/p11test/p11test_helpers.c @@ -0,0 +1,229 @@ +/* + * p11test_helpers.c: Test suite for PKCS#11 API: Supporting functions + * + * Copyright (C) 2016 Martin Strhársky + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_helpers.h" +#include "p11test_loader.h" + +int +open_session(token_info_t *info) +{ + CK_FUNCTION_LIST_PTR function_pointer = info->function_pointer; + CK_RV rv; + + rv = function_pointer->C_OpenSession(info->slot_id, + CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, + &info->session_handle); + + if (rv != CKR_OK) { + return 1; + } + + debug_print("Session was successfully created"); + return 0; +} + +int +initialize_cryptoki(token_info_t *info) +{ + CK_FUNCTION_LIST_PTR function_pointer = info->function_pointer; + CK_RV rv; + + rv = function_pointer->C_Initialize(NULL_PTR); + if (rv != CKR_OK) { + fprintf(stderr, "Could not initialize CRYPTOKI!\n"); + return 1; + } + + if (get_slot_with_card(info)) { + function_pointer->C_Finalize(NULL_PTR); + fprintf(stderr, "There is no card present in reader.\n"); + return 1; + } + + return 0; +} + +int token_initialize(void **state) +{ + token_info_t *info = (token_info_t *) *state; + if (initialize_cryptoki(info)) { + debug_print("CRYPTOKI couldn't be initialized"); + return 1; + } + return 0; +} + +void logfile_init(token_info_t *info) +{ + if (token.log.outfile == NULL) { + return; + } + + if ((info->log.fd = fopen(token.log.outfile, "w")) == NULL) { + fail_msg("Couldn't open file for test results."); + exit(1); + } + fprintf(info->log.fd, "{\n\"time\": 0,\n\"results\": ["); + info->log.in_test = 0; + info->log.first = 1; +} + +void logfile_finalize(token_info_t *info) +{ + if (info == NULL || info->log.fd == NULL) { + return; + } + + /* Make sure the JSON object for test is closed */ + if (info->log.in_test) { + fprintf(info->log.fd, ",\n\t\"result\": \"unknown\"\n},"); + info->log.in_test = 0; + } + + fprintf(info->log.fd, "]\n}\n"); + fclose(info->log.fd); +} + +int group_setup(void **state) +{ + token_info_t * info = calloc(1, sizeof(token_info_t)); + + assert_non_null(info); + + info->pin = token.pin; + info->pin_length = token.pin_length; + info->interactive = token.interactive; + info->slot_id = token.slot_id; + + if (load_pkcs11_module(info, token.library_path)) { + free(info); + fail_msg("Could not load module!\n"); + exit(1); + } + + logfile_init(info); + + *state = info; + return 0; +} + +int group_teardown(void **state) +{ + token_info_t *info = (token_info_t *) *state; + debug_print("Clearing state after group tests!"); + // XXX do not finalize already Finalized + //if(info && info->function_pointer) + // info->function_pointer->C_Finalize(NULL_PTR); + + free(token.library_path); + free(token.pin); + + logfile_finalize(info); + free(info); + + close_pkcs11_module(); + + return 0; +} + +int prepare_token(token_info_t *info) +{ + if (initialize_cryptoki(info)) { + debug_print("CRYPTOKI couldn't be initialized"); + return 1; + } + + if (open_session(info)) { + debug_print("Could not open session to token!"); + return 1; + } + + return 0; +} + +int finalize_token(token_info_t *info) +{ + CK_FUNCTION_LIST_PTR function_pointer = info->function_pointer; + + info->session_handle = 0; + debug_print("Closing all sessions"); + function_pointer->C_CloseAllSessions(info->slot_id); + debug_print("Finalize CRYPTOKI"); + function_pointer->C_Finalize(NULL_PTR); + return 0; +} + +int user_login_setup(void **state) +{ + token_info_t *info = (token_info_t *) *state; + CK_FUNCTION_LIST_PTR function_pointer = info->function_pointer; + CK_RV rv; + + if (prepare_token(info)) { + fail_msg("Could not prepare token.\n"); + exit(1); + } + + debug_print("Logging in to the token!"); + rv = function_pointer->C_Login(info->session_handle, CKU_USER, + token.pin, token.pin_length); + + if (rv != CKR_OK) { + fail_msg("Could not login to token with user PIN '%s'\n", token.pin); + exit(1); + } + + return 0; +} + +int after_test_cleanup(void **state) +{ + token_info_t *info = (token_info_t *) *state; + CK_FUNCTION_LIST_PTR function_pointer = info->function_pointer; + + debug_print("Logging out from token"); + function_pointer->C_Logout(info->session_handle); + + finalize_token(info); + return 0; +} + +int token_setup(void **state) +{ + token_info_t *info = (token_info_t *) *state; + + if (prepare_token(info)) { + fail_msg("Could not prepare token.\n"); + exit(1); + } + + return 0; +} + +int token_cleanup(void **state) +{ + token_info_t *info = (token_info_t *) *state; + + finalize_token(info); + return 0; +} + diff --git a/src/tests/p11test/p11test_helpers.h b/src/tests/p11test/p11test_helpers.h new file mode 100644 index 0000000000..fac3850d06 --- /dev/null +++ b/src/tests/p11test/p11test_helpers.h @@ -0,0 +1,37 @@ +/* + * p11test_helpers.h: Test suite for PKCS#11 API: Supporting functions + * + * Copyright (C) 2016 Martin Strhársky + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifndef P11TEST_HELPERS_H +#define P11TEST_HELPERS_H +#include "p11test_common.h" + +int group_setup(void **state); +int group_teardown(void **state); + +int user_login_setup(void **state); +int after_test_cleanup(void **state); + +int token_setup(void **state); +int token_cleanup(void **state); + +int token_initialize(void **state); +#endif //P11TEST_HELPERS_H diff --git a/src/tests/p11test/p11test_loader.c b/src/tests/p11test/p11test_loader.c new file mode 100644 index 0000000000..107266a5d9 --- /dev/null +++ b/src/tests/p11test/p11test_loader.c @@ -0,0 +1,147 @@ +/* + * p11test_loader.c: Library loader for PKCS#11 test suite + * + * Copyright (C) 2016 Martin Strhársky + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "p11test_loader.h" + +void *pkcs11_so; + +int get_slot_with_card(token_info_t * info) +{ + CK_SLOT_ID_PTR slot_list; + CK_SLOT_ID slot_id; + CK_ULONG slot_count = 0; + CK_RV rv; + int error = 0; + unsigned int i; + + CK_FUNCTION_LIST_PTR function_pointer = info->function_pointer; + + /* Get slot list for memory allocation */ + rv = function_pointer->C_GetSlotList(0, NULL_PTR, &slot_count); + + if ((rv == CKR_OK) && (slot_count > 0)) { + slot_list = malloc(slot_count * sizeof (CK_SLOT_ID)); + + if (slot_list == NULL) { + fprintf(stderr, "System error: unable to allocate memory\n"); + return 1; + } + + /* Get the slot list for processing */ + rv = function_pointer->C_GetSlotList(0, slot_list, &slot_count); + if (rv != CKR_OK) { + fprintf(stderr, "GetSlotList failed: unable to get slot count.\n"); + error = 1; + goto cleanup; + } + } else { + fprintf(stderr, "GetSlotList failed: unable to get slot list.\n"); + return 1; + } + + /* Find a slot capable of specified mechanism */ + for (i = 0; i < slot_count; i++) { + CK_SLOT_INFO slot_info; + slot_id = slot_list[i]; + + rv = function_pointer->C_GetSlotInfo(slot_id, &slot_info); + if (rv != CKR_OK) + continue; + + if (info->slot_id == slot_id) { + if (info->slot_id == slot_list[i]) { /* explicitly specified slot */ + debug_print("Manually selected slot %lu (%s a token)\n", info->slot_id, + ((slot_info.flags & CKF_TOKEN_PRESENT) ? "with" : "without")); + goto cleanup; + } + } + + if (slot_info.flags & CKF_TOKEN_PRESENT) { + /* first found slot if not specified */ + if (info->slot_id == (unsigned long) -1) { + info->slot_id = slot_id; + goto cleanup; + } + } + } + error = 1; + fprintf(stderr, "No slot with card inserted or the selected slot does not exist\n"); + + cleanup: + if (slot_list) { + free(slot_list); + } + + return error; +} + +int load_pkcs11_module(token_info_t * info, const char* path_to_pkcs11_library) { + CK_RV rv; + CK_RV (*C_GetFunctionList)(CK_FUNCTION_LIST_PTR_PTR) = 0; + + if(strlen(path_to_pkcs11_library) == 0) { + fprintf(stderr, "You have to specify path to PKCS#11 library."); + return 1; + } + + pkcs11_so = dlopen(path_to_pkcs11_library, RTLD_NOW); + + if (!pkcs11_so) { + fprintf(stderr, "Error loading pkcs#11 so: %s\n", dlerror()); + return 1; + } + + C_GetFunctionList = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR)) dlsym(pkcs11_so, "C_GetFunctionList"); + + if (!C_GetFunctionList) { + fprintf(stderr, "Could not get function list: %s\n", dlerror()); + return 1; + } + + rv = C_GetFunctionList(&info->function_pointer); + if (CKR_OK != rv) { + fprintf(stderr, "C_GetFunctionList call failed: 0x%.8lX", rv); + return 1; + } + + rv = info->function_pointer->C_Initialize(NULL_PTR); + + if (rv != CKR_OK) { + fprintf(stderr, "C_Initialize: Error = 0x%.8lX\n", rv); + return 1; + } + + if (get_slot_with_card(info)) { + fprintf(stderr, "There is no card present in reader.\n"); + info->function_pointer->C_Finalize(NULL_PTR); + return 1; + } + + info->function_pointer->C_Finalize(NULL_PTR); + return 0; +} + +void close_pkcs11_module() { + if(pkcs11_so) + dlclose(pkcs11_so); +} + diff --git a/src/tests/p11test/p11test_loader.h b/src/tests/p11test/p11test_loader.h new file mode 100644 index 0000000000..5b0c62a3a6 --- /dev/null +++ b/src/tests/p11test/p11test_loader.h @@ -0,0 +1,34 @@ +/* + * p11test_loader.h: Library loader for PKCS#11 test suite + * + * Copyright (C) 2016 Martin Strhársky + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifndef P11TEST_LOADER_H +#define P11TEST_LOADER_H + +#include +#include "p11test_helpers.h" + +int load_pkcs11_module(token_info_t * info, const char* path_to_pkcs11_library); +int get_slot_with_card(token_info_t * info); +void close_pkcs11_module(); + + +#endif //P11TEST_LOADER_H diff --git a/src/tests/p11test/p11test_opensc.conf b/src/tests/p11test/p11test_opensc.conf new file mode 100644 index 0000000000..4edf4baf1b --- /dev/null +++ b/src/tests/p11test/p11test_opensc.conf @@ -0,0 +1,12 @@ +app default { + framework pkcs15 { + use_file_caching = no; + } + reader_driver pcsc { + # The pinpad is disabled by default, + # because of many broken readers out there + enable_pinpad = false; + } + # for pkcs15-init to work in the build directory, we need to point to the directory with the profiles + profile_dir = ../../pkcs15init/ +} diff --git a/src/tests/p11test/pico-hsm_ref.json b/src/tests/p11test/pico-hsm_ref.json new file mode 100644 index 0000000000..e1472734db --- /dev/null +++ b/src/tests/p11test/pico-hsm_ref.json @@ -0,0 +1,274 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "MD5", + "0", + "0", + "CKF_DIGEST" + ], + [ + "RIPEMD160", + "0", + "0", + "CKF_DIGEST" + ], + [ + "GOSTR3411", + "0", + "0", + "CKF_DIGEST" + ], + [ + "ECDSA", + "192", + "521", + "CKF_HW,CKF_SIGN,CKF_VERIFY,CKF_EC_F_P,CKF_EC_ECPARAMETERS,CKF_EC_NAMEDCURVE,CKF" + ], + [ + "ECDSA_SHA1", + "192", + "521", + "CKF_HW,CKF_SIGN,CKF_VERIFY,CKF_EC_F_P,CKF_EC_ECPARAMETERS,CKF_EC_NAMEDCURVE,CKF" + ], + [ + "ECDSA_SHA224", + "192", + "521", + "CKF_HW,CKF_SIGN,CKF_VERIFY,CKF_EC_F_P,CKF_EC_ECPARAMETERS,CKF_EC_NAMEDCURVE,CKF" + ], + [ + "ECDSA_SHA256", + "192", + "521", + "CKF_HW,CKF_SIGN,CKF_VERIFY,CKF_EC_F_P,CKF_EC_ECPARAMETERS,CKF_EC_NAMEDCURVE,CKF" + ], + [ + "ECDSA_SHA384", + "192", + "521", + "CKF_HW,CKF_SIGN,CKF_VERIFY,CKF_EC_F_P,CKF_EC_ECPARAMETERS,CKF_EC_NAMEDCURVE,CKF" + ], + [ + "ECDSA_SHA512", + "192", + "521", + "CKF_HW,CKF_SIGN,CKF_VERIFY,CKF_EC_F_P,CKF_EC_ECPARAMETERS,CKF_EC_NAMEDCURVE,CKF" + ], + [ + "ECDH1_COFACTOR_DERIVE", + "192", + "521", + "CKF_HW,CKF_DERIVE,CKF_EC_F_P,CKF_EC_ECPARAMETERS,CKF_EC_NAMEDCURVE,CKF_EC_UNCOM" + ], + [ + "ECDH1_DERIVE", + "192", + "521", + "CKF_HW,CKF_DERIVE,CKF_EC_F_P,CKF_EC_ECPARAMETERS,CKF_EC_NAMEDCURVE,CKF_EC_UNCOM" + ], + [ + "EC_KEY_PAIR_GEN", + "192", + "521", + "CKF_HW,CKF_GENERATE_KEY_PAIR,CKF_EC_F_P,CKF_EC_ECPARAMETERS,CKF_EC_NAMEDCURVE,C" + ], + [ + "RSA_X_509", + "1024", + "4096", + "CKF_HW,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS", + "1024", + "4096", + "CKF_HW,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS", + "1024", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS", + "1024", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS", + "1024", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS", + "1024", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_PSS", + "1024", + "4096", + "CKF_HW,CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS_PSS", + "1024", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS_PSS", + "1024", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS_PSS", + "1024", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS_PSS", + "1024", + "4096", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_OAEP", + "1024", + "4096", + "CKF_HW,CKF_DECRYPT" + ], + [ + "RSA_PKCS_KEY_PAIR_GEN", + "1024", + "4096", + "CKF_HW,CKF_GENERATE_KEY_PAIR" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "result": "unknown" +}, +{ + "test_id": "multipart_tests", + "result": "unknown" +}, +{ + "test_id": "ec_sign_size_test", + "result": "pass" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "ENCAPSULATE", + "DECAPSULATE", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "result": "unknown" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/src/tests/p11test/piv_ref.json b/src/tests/p11test/piv_ref.json new file mode 100644 index 0000000000..8c57a893a2 --- /dev/null +++ b/src/tests/p11test/piv_ref.json @@ -0,0 +1,3344 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "MD5", + "0", + "0", + "CKF_DIGEST" + ], + [ + "RIPEMD160", + "0", + "0", + "CKF_DIGEST" + ], + [ + "GOSTR3411", + "0", + "0", + "CKF_DIGEST" + ], + [ + "ECDSA", + "256", + "384", + "CKF_HW,CKF_SIGN,CKF_VERIFY,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "ECDSA_SHA1", + "256", + "384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA224", + "256", + "384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA256", + "256", + "384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA384", + "256", + "384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA512", + "256", + "384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDH1_COFACTOR_DERIVE", + "256", + "384", + "CKF_HW,CKF_DERIVE,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "ECDH1_DERIVE", + "256", + "384", + "CKF_HW,CKF_DERIVE,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "RSA_X_509", + "2048", + "2048", + "CKF_HW,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS", + "2048", + "2048", + "CKF_HW,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "MD5_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RIPEMD160_RSA_PKCS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_PSS", + "2048", + "2048", + "CKF_HW,CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS_PSS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS_PSS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS_PSS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS_PSS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS_PSS", + "2048", + "2048", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_OAEP", + "2048", + "2048", + "CKF_HW,CKF_DECRYPT" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "01", + "RSA_X_509", + "YES", + "YES" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "RIPEMD160_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "ECDSA", + "YES", + "" + ], + [ + "02", + "ECDSA_SHA1", + "YES", + "" + ], + [ + "02", + "ECDSA_SHA256", + "YES", + "" + ], + [ + "02", + "ECDSA_SHA384", + "YES", + "" + ], + [ + "02", + "ECDSA_SHA512", + "YES", + "" + ], + [ + "04", + "RSA_PKCS", + "YES", + "" + ], + [ + "04", + "RSA_X_509", + "YES", + "" + ], + [ + "04", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "04", + "RIPEMD160_RSA_PKCS", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "multipart_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "MULTIPART SIGN&VERIFY WORKS" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "01", + "RIPEMD160_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES" + ], + [ + "04", + "MD5_RSA_PKCS", + "YES" + ], + [ + "04", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "04", + "RIPEMD160_RSA_PKCS", + "YES" + ], + [ + "04", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "04", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "04", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "04", + "SHA224_RSA_PKCS", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "ec_sign_size_test", + "result": "pass" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "ENCAPSULATE", + "DECAPSULATE", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ], + [ + "01", + "Certificate for PIV Authentication", + "RSA", + "2048", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "02", + "Certificate for Digital Signature", + "EC", + "256", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "", + "", + "YES" + ], + [ + "03", + "Certificate for Key Management", + "EC", + "256", + "YES", + "", + "", + "", + "", + "", + "", + "", + "", + "YES", + "YES", + "" + ], + [ + "04", + "Certificate for Card Authentication", + "RSA", + "2048", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "data": [ + [ + "KEY ID", + "MECHANISM", + "HASH", + "MGF", + "SALT", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "04", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "04", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "04", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "04", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "04", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "04", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ], + [ + "03", + "ECDH1_DERIVE", + "YES" + ], + [ + "03", + "ECDH1_COFACTOR_DERIVE", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/src/tests/p11test/runtest.sh b/src/tests/p11test/runtest.sh new file mode 100755 index 0000000000..12b2d703fc --- /dev/null +++ b/src/tests/p11test/runtest.sh @@ -0,0 +1,237 @@ +#!/bin/bash +# runtest.sh: Run test on existing card with possible initialization +# +# Copyright (C) 2016, 2017 Red Hat, Inc. +# +# Author: Jakub Jelen +# +# 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 General Public License +# along with this program. If not, see . + +#set -x +SOPIN="12345678" +PIN="123456" +GENERATE_KEYS=1 +PKCS11_TOOL="../../tools/pkcs11-tool"; +PKCS15_INIT="env OPENSC_CONF=p11test_opensc.conf ../../tools/pkcs15-init" +SC_HSM_TOOL="../../tools/sc-hsm-tool"; + +function generate_sym() { + TYPE="$1" + ID="$2" + LABEL="$3" + + # Generate key + $PKCS11_TOOL --keygen --key-type="$TYPE" --login --pin=$PIN \ + --extractable --usage-wrap --usage-decrypt \ + --module="$P11LIB" --label="$LABEL" --id=$ID + + if [[ "$?" -ne "0" ]]; then + echo "Couldn't generate $TYPE key pair" + return 1 + fi + + p11tool --login --provider="$P11LIB" --list-all +} + +function generate_cert() { + TYPE="$1" + ID="$2" + LABEL="$3" + CERT="$4" # whether to generate certificate too + + # Generate key pair + $PKCS11_TOOL --keypairgen --key-type="$TYPE" --login --pin=$PIN \ + --extractable --usage-wrap --usage-sign --usage-decrypt --usage-derive \ + --module="$P11LIB" --label="$LABEL" --id=$ID + + if [[ "$?" -ne "0" ]]; then + echo "Couldn't generate $TYPE key pair" + return 1 + fi + + # We can not do this with EdDSA keys as they are not supported in certtool + # We can not do this with curve25519 keys as they do not need to support signatures at all + if [[ "$CERT" -ne 0 ]]; then + # check type value for the PKCS#11 URI (RHEL7 is using old "object-type") + TYPE_KEY="type" + export GNUTLS_PIN=$PIN + p11tool --list-all --provider="$P11LIB" --login | grep "object-type" && \ + TYPE_KEY="object-type" + + # Generate certificate + certtool --generate-self-signed --outfile="$TYPE.cert" --template=cert.cfg \ + --provider="$P11LIB" --load-privkey "pkcs11:object=$LABEL;$TYPE_KEY=private" \ + --load-pubkey "pkcs11:object=$LABEL;$TYPE_KEY=public" + # convert to DER: + openssl x509 -inform PEM -outform DER -in "$TYPE.cert" -out "$TYPE.cert.der" + # Write certificate + #p11tool --login --write --load-certificate="$TYPE.cert" --label="$LABEL" \ + # --provider="$P11LIB" + $PKCS11_TOOL --write-object "$TYPE.cert.der" --type=cert --id=$ID \ + --label="$LABEL" --module="$P11LIB" + + rm "$TYPE.cert" "$TYPE.cert.der" + fi + + p11tool --login --provider="$P11LIB" --list-all +} + +function card_setup() { + ECC_KEYS=1 + EDDSA=1 + SECRET=1 + case $1 in + "softhsm") + P11LIB="/usr/lib64/pkcs11/libsofthsm2.so" + echo "directories.tokendir = .tokens/" > .softhsm2.conf + mkdir ".tokens" + export SOFTHSM2_CONF=".softhsm2.conf" + # Init token + softhsm2-util --init-token --slot 0 --label "SC test" --so-pin="$SOPIN" --pin="$PIN" + ;; + "opencryptoki") + # Supports only RSA mechanisms + ECC_KEYS=0 + EDDSA=0 + SECRET=0 + P11LIB="/usr/lib64/pkcs11/libopencryptoki.so" + SO_PIN=87654321 + SLOT_ID=3 # swtok slot + systemctl is-active pkcsslotd > /dev/null + if [[ "$?" -ne "0" ]]; then + echo "Opencryptoki needs pkcsslotd running" + exit 1 + fi + groups | grep pkcs11 > /dev/null + if [[ "$?" -ne "0" ]]; then + echo "Opencryptoki requires the user to be in pkcs11 group" + exit 1 + fi + echo "test_swtok" | /usr/sbin/pkcsconf -I -c $SLOT_ID -S $SO_PIN + /usr/sbin/pkcsconf -u -c $SLOT_ID -S $SO_PIN -n $PIN + ;; + "kryoptic") + PIN="$SOPIN" + P11LIB="/home/jjelen/devel/kryoptic/target/debug/libkryoptic_pkcs11.so" + KRYOPTIC_DB="kryoptic.sql" + export KRYOPTIC_CONF="$KRYOPTIC_DB:1" + # Init token + $PKCS11_TOOL --init-token --so-pin="$SOPIN" --label="Kryoptic token" --module="$P11LIB" + $PKCS11_TOOL --init-pin --pin="$PIN" --so-pin="$SOPIN" --label="Kryoptic token" --module="$P11LIB" + ;; + "readonly") + GENERATE_KEYS=0 + if [[ ! -z "$2" && -f "$2" ]]; then + P11LIB="$2" + else + P11LIB="/usr/lib64/pkcs11/opensc-pkcs11.so" + P11LIB="../pkcs11/.libs/opensc-pkcs11.so" + fi + ;; + "myeid") + GENERATE_KEYS=0 # we generate them directly here + P11LIB="../../pkcs11/.libs/opensc-pkcs11.so" + $PKCS15_INIT --erase-card + $PKCS15_INIT -C --pin $PIN --puk $SOPIN --so-pin $SOPIN --so-puk $SOPIN + $PKCS15_INIT -P -a 1 -l "Basic PIN" --pin $PIN --puk $PIN + INIT="$PKCS15_INIT --auth-id 01 --so-pin $SOPIN --pin $PIN" + $INIT --generate-key ec:prime256v1 --id 01 --label="EC key" --key-usage=sign,keyAgreement + $INIT --generate-key rsa:2048 --id 02 --label="RSA key" --key-usage=sign,decrypt + $INIT --store-secret-key /dev/urandom --secret-key-algorithm aes:256 --extractable --id 03 --label="AES256 key" --key-usage=sign,decrypt + $INIT --store-secret-key /dev/urandom --secret-key-algorithm aes:128 --extractable --id 04 --label="AES128 key" --key-usage=sign,decrypt + $PKCS15_INIT -F + ;; + "sc-hsm") + GENERATE_KEYS=0 # we generate them directly here + SOPIN="3537363231383830" + PIN="648219" + P11LIB="../../pkcs11/.libs/opensc-pkcs11.so" + $SC_HSM_TOOL --initialize --so-pin $SOPIN --pin $PIN + $PKCS11_TOOL --module $P11LIB -l --pin $PIN --keypairgen --key-type rsa:2048 --id 10 --label="RSA key" + $PKCS11_TOOL --module $P11LIB -l --pin $PIN --keypairgen --key-type EC:prime256v1 --label "EC key" + ;; + "epass2003") + GENERATE_KEYS=0 # we generate them directly here + P11LIB="../../pkcs11/.libs/opensc-pkcs11.so" + PIN="987654" + SOPIN="1234567890" + $PKCS15_INIT --erase-card -T + $PKCS15_INIT --create-pkcs15 -T -p pkcs15+onepin --pin $PIN --puk 1234567890 + INIT="$PKCS15_INIT --auth-id 01 --so-pin $SOPIN --pin $PIN" + $INIT --generate-key ec:prime256v1 --id 01 --label="EC key" --key-usage=sign,keyAgreement + $INIT --generate-key rsa:2048 --id 02 --label="RSA key" --key-usage=sign,decrypt + $PKCS15_INIT -F + ;; + *) + echo "Error: Missing argument." + echo " Usage:" + echo " runtest.sh [softhsm|opencryptoki|myeid|sc-hsm|kryoptic|readonly [pkcs-library.so]]" + exit 1; + ;; + esac + + if [[ $GENERATE_KEYS -eq 1 ]]; then + # Generate 1024b RSA Key pair + generate_cert "RSA:1024" "01" "RSA_auth" 1 + # Generate 2048b RSA Key pair + generate_cert "RSA:2048" "02" "RSA2048" 1 + # Generate 3082b RSA Key pair + generate_cert "RSA:3072" "09" "RSA3072" 1 + # Generate 4096 RSA Key pair + generate_cert "RSA:4096" "10" "RSA4096" 1 + if [[ $ECC_KEYS -eq 1 ]]; then + # Generate 256b ECC Key pair + generate_cert "EC:secp256r1" "03" "ECC_auth" 1 + # Generate 521b ECC Key pair + generate_cert "EC:secp521r1" "04" "ECC521" 1 + fi + if [[ $EDDSA -eq 1 ]]; then + # Generate Ed25519 + generate_cert "EC:edwards25519" "05" "EDDSA" 0 + # Generate curve25519 + #generate_cert "EC:curve25519" "06" "Curve25519" 0 + # not supported by softhsm either + fi + if [[ $SECRET -eq 1 ]]; then + # Generate AES 128 key + generate_sym "aes:16" "07" "AES128 key" + # Generate AES 256 key + generate_sym "aes:32" "08" "AES256 key" + fi + fi +} + +function card_cleanup() { + case $1 in + "softhsm") + rm .softhsm2.conf + rm -rf ".tokens" + ;; + "kryoptic") + rm kryoptic.sql + ;; + esac +} + +card_setup "$@" +make p11test || exit +if [[ "$PKCS11SPY" != "" ]]; then + export PKCS11SPY="$P11LIB" + $VALGRIND ./p11test -v -m ../../pkcs11/.libs/pkcs11-spy.so -p $PIN &> /tmp/spy.log + echo "Output stored in /tmp/spy.log" +else + $VALGRIND ./p11test -v -m "$P11LIB" -o test.json -p $PIN +fi + +card_cleanup "$@" diff --git a/src/tests/p11test/sc-hsm_ref.json b/src/tests/p11test/sc-hsm_ref.json new file mode 100644 index 0000000000..807b5db390 --- /dev/null +++ b/src/tests/p11test/sc-hsm_ref.json @@ -0,0 +1,879 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "MD5", + "0", + "0", + "CKF_DIGEST" + ], + [ + "RIPEMD160", + "0", + "0", + "CKF_DIGEST" + ], + [ + "GOSTR3411", + "0", + "0", + "CKF_DIGEST" + ], + [ + "ECDSA", + "192", + "521", + "0x01D02801" + ], + [ + "ECDSA_SHA384", + "192", + "521", + "0x00002800" + ], + [ + "ECDSA_SHA512", + "192", + "521", + "0x00002800" + ], + [ + "ECDSA_SHA1", + "192", + "521", + "0x01D02801" + ], + [ + "ECDSA_SHA224", + "192", + "521", + "0x01D02801" + ], + [ + "ECDSA_SHA256", + "192", + "521", + "0x01D02801" + ], + [ + "ECDH1_COFACTOR_DERIVE", + "192", + "521", + "0x01D80001" + ], + [ + "ECDH1_DERIVE", + "192", + "521", + "0x01D80001" + ], + [ + "EC_KEY_PAIR_GEN", + "192", + "521", + "0x01D10001" + ], + [ + "RSA_X_509", + "1024", + "4096", + "0x00002A01" + ], + [ + "RSA_PKCS", + "1024", + "4096", + "0x00002A01" + ], + [ + "SHA256_RSA_PKCS", + "1024", + "4096", + "0x00002800" + ], + [ + "SHA384_RSA_PKCS", + "1024", + "4096", + "0x00002800" + ], + [ + "SHA512_RSA_PKCS", + "1024", + "4096", + "0x00002800" + ], + [ + "RSA_PKCS_PSS", + "1024", + "4096", + "0x00002801" + ], + [ + "SHA256_RSA_PKCS_PSS", + "1024", + "4096", + "0x00002800" + ], + [ + "SHA384_RSA_PKCS_PSS", + "1024", + "4096", + "0x00002800" + ], + [ + "SHA512_RSA_PKCS_PSS", + "1024", + "4096", + "0x00002800" + ], + [ + "RSA_PKCS_OAEP", + "1024", + "4096", + "0x00000201" + ], + [ + "RSA_PKCS_KEY_PAIR_GEN", + "1024", + "4096", + "CKF_GENERATE_KEY_PAIR" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "10", + "RSA_X_509", + "YES", + "YES" + ], + [ + "10", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "10", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "10", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "10", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "40", + "RSA_X_509", + "YES", + "YES" + ], + [ + "40", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "40", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "40", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "40", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "20", + "ECDSA", + "YES", + "" + ], + [ + "20", + "ECDSA_SHA384", + "YES", + "" + ], + [ + "20", + "ECDSA_SHA512", + "YES", + "" + ], + [ + "20", + "ECDSA_SHA1", + "YES", + "" + ], + [ + "20", + "ECDSA_SHA256", + "YES", + "" + ], + [ + "30", + "ECDSA", + "YES", + "" + ], + [ + "30", + "ECDSA_SHA384", + "YES", + "" + ], + [ + "30", + "ECDSA_SHA512", + "YES", + "" + ], + [ + "30", + "ECDSA_SHA1", + "YES", + "" + ], + [ + "30", + "ECDSA_SHA256", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "multipart_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "MULTIPART SIGN&VERIFY WORKS" + ], + [ + "10", + "RSA_X_509", + "YES" + ], + [ + "10", + "RSA_PKCS", + "YES" + ], + [ + "10", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "10", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "10", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "40", + "RSA_X_509", + "YES" + ], + [ + "40", + "RSA_PKCS", + "YES" + ], + [ + "40", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "40", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "40", + "SHA512_RSA_PKCS", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "ec_sign_size_test", + "result": "pass" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ], + [ + "10", + "Private Key", + "RSA", + "2048", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "" + ], + [ + "40", + "Private Key", + "RSA", + "4096", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "" + ], + [ + "20", + "ECC Key", + "EC", + "256", + "", + "YES", + "YES", + "", + "", + "", + "", + "YES", + "YES", + "" + ], + [ + "30", + "ECC2 Key", + "EC", + "521", + "", + "YES", + "YES", + "", + "", + "", + "", + "YES", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "data": [ + [ + "KEY ID", + "MECHANISM", + "HASH", + "MGF", + "SALT", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "10", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "10", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "10", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "10", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "10", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "10", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "10", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "10", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "10", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "10", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "10", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "10", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "10", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "10", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "10", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "10", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "10", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "10", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "10", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "40", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "40", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "40", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "40", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "40", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "40", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "40", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "40", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "40", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "40", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "40", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "40", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "40", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "40", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "40", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "40", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "40", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "40", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "40", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA512", + "0", + "", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ], + [ + "20", + "ECDH1_COFACTOR_DERIVE", + "YES" + ], + [ + "20", + "ECDH1_DERIVE", + "YES" + ], + [ + "30", + "ECDH1_COFACTOR_DERIVE", + "YES" + ], + [ + "30", + "ECDH1_DERIVE", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/src/tests/p11test/virt_cacard_ref.json b/src/tests/p11test/virt_cacard_ref.json new file mode 100644 index 0000000000..6cb70abcf9 --- /dev/null +++ b/src/tests/p11test/virt_cacard_ref.json @@ -0,0 +1,4767 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "MD5", + "0", + "0", + "CKF_DIGEST" + ], + [ + "RIPEMD160", + "0", + "0", + "CKF_DIGEST" + ], + [ + "GOSTR3411", + "0", + "0", + "CKF_DIGEST" + ], + [ + "RSA_X_509", + "1024", + "3072", + "CKF_HW,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS", + "1024", + "3072", + "CKF_HW,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS", + "1024", + "3072", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS", + "1024", + "3072", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS", + "1024", + "3072", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS", + "1024", + "3072", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS", + "1024", + "3072", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "MD5_RSA_PKCS", + "1024", + "3072", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RIPEMD160_RSA_PKCS", + "1024", + "3072", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_PSS", + "1024", + "3072", + "CKF_HW,CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS_PSS", + "1024", + "3072", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS_PSS", + "1024", + "3072", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS_PSS", + "1024", + "3072", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS_PSS", + "1024", + "3072", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS_PSS", + "1024", + "3072", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_OAEP", + "1024", + "3072", + "CKF_HW,CKF_DECRYPT" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "00:01", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "00:01", + "RSA_X_509", + "YES", + "YES" + ], + [ + "00:01", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "00:01", + "RIPEMD160_RSA_PKCS", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "00:02", + "RSA_X_509", + "YES", + "YES" + ], + [ + "00:02", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "00:02", + "RIPEMD160_RSA_PKCS", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "00:03", + "RSA_X_509", + "YES", + "YES" + ], + [ + "00:03", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "00:03", + "RIPEMD160_RSA_PKCS", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "multipart_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "MULTIPART SIGN&VERIFY WORKS" + ], + [ + "00:01", + "MD5_RSA_PKCS", + "YES" + ], + [ + "00:01", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "00:01", + "RIPEMD160_RSA_PKCS", + "YES" + ], + [ + "00:01", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "00:01", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "00:01", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "00:01", + "SHA224_RSA_PKCS", + "YES" + ], + [ + "00:02", + "MD5_RSA_PKCS", + "YES" + ], + [ + "00:02", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "00:02", + "RIPEMD160_RSA_PKCS", + "YES" + ], + [ + "00:02", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "00:02", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "00:02", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "00:02", + "SHA224_RSA_PKCS", + "YES" + ], + [ + "00:03", + "MD5_RSA_PKCS", + "YES" + ], + [ + "00:03", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "00:03", + "RIPEMD160_RSA_PKCS", + "YES" + ], + [ + "00:03", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "00:03", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "00:03", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "00:03", + "SHA224_RSA_PKCS", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "ec_sign_size_test", + "result": "skip" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "ENCAPSULATE", + "DECAPSULATE", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ], + [ + "00:01", + "CAC ID Certificate", + "RSA", + "2048", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "00:02", + "CAC Email Signature Certificate", + "RSA", + "2048", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "00:03", + "CAC Email Encryption Certificate", + "RSA", + "2048", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "data": [ + [ + "KEY ID", + "MECHANISM", + "HASH", + "MGF", + "SALT", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "00:01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "00:01", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "00:01", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "00:01", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "00:01", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "00:02", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "00:02", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "00:02", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "00:02", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "00:03", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "00:03", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "00:03", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "00:03", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:03", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "00:03", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/src/tests/p15dump.c b/src/tests/p15dump.c index da8b4a3e50..f44c90f539 100644 --- a/src/tests/p15dump.c +++ b/src/tests/p15dump.c @@ -74,7 +74,7 @@ static int dump_unusedspace(void) } path.count = -1; - r = sc_pkcs15_read_file(p15card, &path, &buf, &buf_len); + r = sc_pkcs15_read_file(p15card, &path, &buf, &buf_len, 0); if (r < 0) { if (r == SC_ERROR_FILE_NOT_FOUND) { printf("\nNo EF(UnusedSpace) file\n"); @@ -123,6 +123,7 @@ int main(int argc, char *argv[]) /* Keep card locked to prevent useless calls to sc_logout */ if (i) { fprintf(stderr, "failed: %s\n", sc_strerror(i)); + sc_test_cleanup(); return 1; } printf("found.\n"); diff --git a/src/tests/pintest.c b/src/tests/pintest.c index 42607f3b95..1003dbc246 100644 --- a/src/tests/pintest.c +++ b/src/tests/pintest.c @@ -13,6 +13,7 @@ #include #endif +#include "libopensc/internal.h" #include "libopensc/opensc.h" #include "libopensc/pkcs15.h" #include "common/compat_getpass.h" @@ -56,7 +57,7 @@ static int ask_and_verify_pin(struct sc_pkcs15_object *pin_obj) { struct sc_pkcs15_auth_info *pin_info = (struct sc_pkcs15_auth_info *) pin_obj->data; int i = 0; - char prompt[80]; + char prompt[(sizeof pin_obj->label) + 30]; u8 *pass; if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) { @@ -64,7 +65,8 @@ static int ask_and_verify_pin(struct sc_pkcs15_object *pin_obj) return 0; } - sprintf(prompt, "Please enter PIN code [%.*s]: ", (int) sizeof pin_obj->label, pin_obj->label); + snprintf(prompt, sizeof(prompt), "Please enter PIN code [%.*s]: ", + (int) sizeof pin_obj->label, pin_obj->label); pass = (u8 *) getpass(prompt); if (SC_SUCCESS != sc_lock(card)) diff --git a/src/tests/print.c b/src/tests/print.c index 7f0cb0148c..163c686297 100644 --- a/src/tests/print.c +++ b/src/tests/print.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002 Juha Yrjölä +/* Copyright (C) 2001, 2002 Juha Yrjölä * All rights reserved. * * PKCS#15 PIN code test @@ -24,7 +24,8 @@ void sc_test_print_card(const sc_pkcs15_card_t *mycard) }; int i, count = 0; - assert(mycard != NULL); + if (mycard == NULL) + return; printf("PKCS#15 Card [%s]:\n", mycard->tokeninfo->label); printf("\tVersion : %d\n", mycard->tokeninfo->version); printf("\tSerial number : %s\n", mycard->tokeninfo->serial_number); @@ -128,7 +129,7 @@ static void print_prkey(const struct sc_pkcs15_object *obj) } printf("\n"); if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA) - printf("\tModLength : %lu\n", + printf("\tModLength : %lu\n", (unsigned long) prkey->modulus_length); printf("\tKey ref : %d\n", prkey->key_reference); printf("\tNative : %s\n", prkey->native ? "yes" : "no"); @@ -236,14 +237,6 @@ void sc_test_print_object(const struct sc_pkcs15_object *obj) printer = print_pubkey; kind = "Public RSA key"; break; - case SC_PKCS15_TYPE_PRKEY_DSA: - printer = print_prkey; - kind = "Private DSA key"; - break; - case SC_PKCS15_TYPE_PUBKEY_DSA: - printer = print_pubkey; - kind = "Public DSA key"; - break; case SC_PKCS15_TYPE_CERT_X509: printer = print_cert_x509; kind = "X.509 Certificate"; diff --git a/src/tests/sc-test.c b/src/tests/sc-test.c index 42bc1f6fd6..2d9d3dda7f 100644 --- a/src/tests/sc-test.c +++ b/src/tests/sc-test.c @@ -10,7 +10,7 @@ #include #include -#include "common/compat_getopt.h" +#include #include "libopensc/opensc.h" #include "sc-test.h" @@ -83,18 +83,22 @@ int sc_test_init(int *argc, char *argv[]) if (rc < 0) return rc; } else { - for (i = rc = 0; rc != 1 && i < (int) sc_ctx_get_reader_count(ctx); i++) + for (i = rc = 0; !(rc & SC_READER_CARD_PRESENT) && + i < (int) sc_ctx_get_reader_count(ctx); i++) rc = sc_detect_card_presence(sc_ctx_get_reader(ctx, i)); - if (rc == 1) + if (rc < 0) + return rc; + if (rc & SC_READER_CARD_PRESENT) { opt_reader = i - 1; + } else { + rc = 0; + } } if (rc > 0) { printf("Card detected in reader '%s'\n",sc_ctx_get_reader(ctx, opt_reader)->name); break; } - if (rc < 0) - return rc; printf("Please insert a smart card. Press return to continue"); fflush(stdout); diff --git a/src/tests/unittests/Makefile.am b/src/tests/unittests/Makefile.am new file mode 100644 index 0000000000..96ace28569 --- /dev/null +++ b/src/tests/unittests/Makefile.am @@ -0,0 +1,67 @@ +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +EXTRA_DIST = Makefile.mak + +if ENABLE_CMOCKA +include $(top_srcdir)/aminclude_static.am +clean-local: code-coverage-clean +distclean-local: code-coverage-dist-clean + +@VALGRIND_CHECK_RULES@ +if VALGRIND_ENABLED +#VALGRIND_SUPPRESSIONS_FILES = $(top_srcdir)/tests/opensc.supp +VALGRIND_FLAGS = --num-callers=30 -q --keep-debuginfo=yes --gen-suppressions=all +# to avoid false positive leaks from pcsclite +TESTS_ENVIRONMENT = LD_PRELOAD='$(PCSCLITE_PATH)' +endif + +noinst_PROGRAMS = asn1 simpletlv cachedir pkcs15filter openpgp-tool hextobin \ + decode_ecdsa_signature check_macro_reference_loop strip_pkcs1_2_padding \ + base64 parse_pkcs11_uri +TESTS = asn1 simpletlv cachedir pkcs15filter openpgp-tool hextobin \ + decode_ecdsa_signature check_macro_reference_loop strip_pkcs1_2_padding \ + base64 parse_pkcs11_uri + +noinst_HEADERS = torture.h + +AM_CFLAGS = -I$(top_srcdir)/src/ \ + $(CODE_COVERAGE_CFLAGS) \ + $(OPTIONAL_OPENSSL_CFLAGS) \ + $(CMOCKA_CFLAGS) +AM_CPPFLAGS =$(CODE_COVERAGE_CPPFLAGS) +LDADD = $(top_builddir)/src/libopensc/libopensc.la \ + $(top_builddir)/src/common/libscdl.la \ + $(top_builddir)/src/common/libcompat.la \ + $(CODE_COVERAGE_LIBS) \ + $(OPTIONAL_OPENSSL_LIBS) \ + $(CMOCKA_LIBS) + +asn1_SOURCES = asn1.c +simpletlv_SOURCES = simpletlv.c +cachedir_SOURCES = cachedir.c +pkcs15filter_SOURCES = pkcs15-emulator-filter.c +openpgp_tool_SOURCES = openpgp-tool.c $(top_builddir)/src/tools/openpgp-tool-helpers.c +hextobin_SOURCES = hextobin.c +decode_ecdsa_signature_SOURCES = decode_ecdsa_signature.c +check_macro_reference_loop_SOURCES = check_macro_reference_loop.c +strip_pkcs1_2_padding_SOURCES = strip_pkcs1_2_padding.c +base64_SOURCES = base64.c +parse_pkcs11_uri_SOURCES = parse_pkcs11_uri.c + +if ENABLE_ZLIB +noinst_PROGRAMS += compression +TESTS += compression + +compression_SOURCES = compression.c +compression_LDADD = $(LDADD) $(OPTIONAL_ZLIB_LIBS) +endif + +if ENABLE_OPENSSL +noinst_PROGRAMS += sm +TESTS += sm + +sm_SOURCES = sm.c +sm_LDADD = $(top_builddir)/src/sm/libsm.la $(LDADD) +endif + + +endif diff --git a/src/tests/unittests/Makefile.mak b/src/tests/unittests/Makefile.mak new file mode 100644 index 0000000000..998a55bc33 --- /dev/null +++ b/src/tests/unittests/Makefile.mak @@ -0,0 +1,22 @@ +TOPDIR = ..\..\.. + +TARGETS = asn1 compression pkcs15filter check_macro_reference_loop \ + strip_pkcs1_2_padding base64 parse_pkcs11_uri + +OBJECTS = asn1.obj \ + compression.obj \ + pkcs15-emulator-filter.obj \ + check_macro_reference_loop.obj \ + strip_pkcs1_2_padding.obj \ + parse_pkcs11_uri.obj \ + $(TOPDIR)\win32\versioninfo.res + +all: $(TARGETS) + +!INCLUDE $(TOPDIR)\win32\Make.rules.mak + +$(TARGETS): $(OBJECTS) $(LIBS) + +.c.exe: + cl $(COPTS) /c $< + link $(LINKFLAGS) /out:$@ $*.obj $(OBJECTS) $(LIBS) diff --git a/src/tests/unittests/asn1.c b/src/tests/unittests/asn1.c new file mode 100644 index 0000000000..dc8dccd4e0 --- /dev/null +++ b/src/tests/unittests/asn1.c @@ -0,0 +1,878 @@ +/* + * asn1.c: Unit tests for ASN1 parsers + * + * Copyright (C) 2019 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "torture.h" +#include "libopensc/log.c" +#include "libopensc/asn1.c" + +/* The last argument is an OID value */ +#define TORTURE_OID(name, asn1_data, ...) \ + static void torture_asn1_oid_## name (void **state) \ + { \ + u8 data[] = asn1_data; \ + size_t datalen = sizeof(data) - 1; \ + struct sc_object_id ref_oid = {{__VA_ARGS__}}; \ + struct sc_object_id oid; \ + int rv; \ + u8 *buf = NULL; \ + size_t buflen = 0; \ + \ + rv = sc_asn1_decode_object_id(data, datalen, &oid); \ + assert_int_equal(rv, SC_SUCCESS); \ + assert_int_equal(sc_compare_oid(&ref_oid, &oid), 1); /* XXX */ \ + rv = sc_asn1_encode_object_id(&buf, &buflen, &oid); \ + assert_int_equal(rv, SC_SUCCESS); \ + assert_int_equal(buflen, datalen); \ + assert_memory_equal(buf, data, buflen); \ + free(buf); \ + } +#define TORTURE_OID_ERROR(name, asn1_data, error) \ + static void torture_asn1_oid_## name (void **state) \ + { \ + u8 data[] = asn1_data; \ + size_t datalen = sizeof(data) - 1; \ + struct sc_object_id oid; \ + int rv; \ + \ + rv = sc_asn1_decode_object_id(data, datalen, &oid); \ + assert_int_equal(rv, error); \ + } + +/* Without the tag (0x06) and length */ +/* Small OID values */ +TORTURE_OID(small, "\x01\x02\x03\x04\x05\x06", 0, 1, 2, 3, 4, 5, 6, -1) +/* Limit what we can fit into the first byte */ +TORTURE_OID(limit, "\x7F", 2, 47, -1) +/* The second octet already overflows to the second byte */ +TORTURE_OID(two_byte, "\x81\x00", 2, 48, -1) +/* Existing OID ec publickey */ +TORTURE_OID(ecpubkey, "\x2A\x86\x48\xCE\x3D\x02\x01", 1, 2, 840, 10045, 2, 1, -1) + +/* Negative tests */ +/* Missing second byte, even though indicated with the first bit */ +TORTURE_OID_ERROR(missing, "\x81", SC_ERROR_INVALID_ASN1_OBJECT) +/* Missing second byte in later identifiers */ +TORTURE_OID_ERROR(missing_second, "\x2A\x48\x81", SC_ERROR_INVALID_ASN1_OBJECT) +/* Non-minimal encoding of first part */ +TORTURE_OID_ERROR(non_minimal_second, "\x2A\x80\x01", SC_ERROR_INVALID_ASN1_OBJECT) +/* Non-minimal encoding of first part */ +TORTURE_OID_ERROR(non_minimal, "\x80\x01", SC_ERROR_INVALID_ASN1_OBJECT) + +/* + * Test undefined behavior of too large parts of OID encoding + * + * The specification does not place any limits to these values, but they + * are internally in opensc stored as ints so it makes sense to reject + * the too-large onese for now, rather than causing undefined overflow. + * + * https://oss-fuzz.com/testcase-detail/5673497895895040 + */ +#if INT_MAX == 2147483647 +/* 2.5.4.2147483647 (The last part is largest 32 bit integer) */ +TORTURE_OID(last_int_max, "\x55\x04\x87\xFF\xFF\xFF\x7F", 2, 5, 4, 2147483647, -1) +/* 2.2147483647.4.3 (The second part is largest 32 bit integer) */ +TORTURE_OID(first_int_max, "\x88\x80\x80\x80\x4F\x04\x03", 2, 2147483647, 4, 3, -1) +#else +/* 2.5.4.2147483647 (The last part is largest 32 bit integer) */ +TORTURE_OID_ERROR(last_int_max, "\x55\x04\x87\xFF\xFF\xFF\x7F", SC_ERROR_NOT_SUPPORTED) +/* 2.2147483647.4.3 (The second part is largest 32 bit integer) */ +TORTURE_OID_ERROR(first_int_max, "\x88\x80\x80\x80\x4F\x04\x03", SC_ERROR_NOT_SUPPORTED) +#endif + +/* 2.5.4.2147483648 (The last part is 32 bit integer overflow) */ +TORTURE_OID_ERROR(last_32b_overflow, "\x55\x04\x88\x80\x80\x80\x00", SC_ERROR_NOT_SUPPORTED) +/* 2.2147483648.4.3 (The second part is 32 bit integer overflow) */ +TORTURE_OID_ERROR(first_32b_overflow, "\x88\x80\x80\x80\x50\x04\x03", SC_ERROR_NOT_SUPPORTED) +/* TODO SC_MAX_OBJECT_ID_OCTETS */ + +#define TORTURE_INTEGER(name, asn1_data, int_value) \ + static void torture_asn1_integer_## name (void **state) \ + { \ + u8 data[] = asn1_data; \ + size_t datalen = sizeof(data) - 1; \ + int value = 0; \ + int rv; \ + u8 *buf = NULL; \ + size_t buflen = 0; \ + \ + rv = sc_asn1_decode_integer(data, datalen, &value, 1); \ + assert_int_equal(rv, SC_SUCCESS); \ + assert_int_equal(value, int_value); \ + rv = asn1_encode_integer(value, &buf, &buflen); \ + assert_int_equal(rv, SC_SUCCESS); \ + assert_int_equal(buflen, datalen); \ + assert_memory_equal(buf, data, buflen); \ + free(buf); \ + } +#define TORTURE_INTEGER_ERROR(name, asn1_data, error) \ + static void torture_asn1_integer_## name (void **state) \ + { \ + u8 data[] = asn1_data; \ + size_t datalen = sizeof(data) - 1; \ + int value = 0; \ + int rv; \ + \ + rv = sc_asn1_decode_integer(data, datalen, &value, 1); \ + assert_int_equal(rv, error); \ + } +#define TORTURE_INTEGER_NONSTRICT(name, asn1_data, error, int_value) \ + static void torture_asn1_integer_## name (void **state) \ + { \ + u8 data[] = asn1_data; \ + size_t datalen = sizeof(data) - 1; \ + int value = 0; \ + int rv; \ + \ + rv = sc_asn1_decode_integer(data, datalen, &value, 1); \ + assert_int_equal(rv, error); \ + /* but we can parse them without the strict checking */ \ + rv = sc_asn1_decode_integer(data, datalen, &value, 0); \ + assert_int_equal(rv, SC_SUCCESS); \ + assert_int_equal(value, int_value); \ + } + +/* Data are without the Tag (0x02) and Length */ +/* Positive test cases, mostly corner cases */ +TORTURE_INTEGER(zero, "\x00", 0) +TORTURE_INTEGER(one, "\x01", 1) +TORTURE_INTEGER(minus_one, "\xFF", -1) +TORTURE_INTEGER(padded_128, "\x00\x80", 128) +TORTURE_INTEGER(max2, "\x7F\xFF", 32767) +TORTURE_INTEGER(min2, "\x80\x00", -32768) + +#if INT_MAX == 2147483647 +TORTURE_INTEGER(max4, "\x7F\xFF\xFF\xFF", 2147483647) +TORTURE_INTEGER(min4, "\x80\x00\x00\x00", -2147483648) +#else +TORTURE_INTEGER_ERROR(max4, "\x7F\xFF\xFF\xFF", SC_ERROR_NOT_SUPPORTED) +TORTURE_INTEGER_ERROR(min4, "\x80\x00\x00\x00", SC_ERROR_NOT_SUPPORTED) +#endif + +/* Negative test cases */ +TORTURE_INTEGER_ERROR(null, "", SC_ERROR_INVALID_ASN1_OBJECT) +TORTURE_INTEGER_ERROR(over, "\x7F\xFF\xFF\xFF\xFF", SC_ERROR_NOT_SUPPORTED) + +/* Tests fail in strict mode, but work otherwise */ +TORTURE_INTEGER_NONSTRICT(padded_zero, "\x00\x00", SC_ERROR_INVALID_ASN1_OBJECT, 0) +TORTURE_INTEGER_NONSTRICT(padded_one, "\x00\x01", SC_ERROR_INVALID_ASN1_OBJECT, 1) +TORTURE_INTEGER_NONSTRICT(padded_minus_one, "\xFF\xFF", SC_ERROR_INVALID_ASN1_OBJECT, -1) +TORTURE_INTEGER_NONSTRICT(padded_127, "\x00\x7F", SC_ERROR_INVALID_ASN1_OBJECT, 127) + +/* + * Test undefined behavior of negative INTEGERS handling. + * https://oss-fuzz.com/testcase-detail/5125815506829312 + * + * The issue was not actually the size of the integers, but that first + * negative value wrote ones to the whole integer and it was not possible + * to shift values afterward. + */ +TORTURE_INTEGER(negative, "\xff\x20", -224) + +#define TORTURE_BIT_FIELD(name, asn1_data, int_value) \ + static void torture_asn1_bit_field_## name (void **state) \ + { \ + u8 data[] = asn1_data; \ + size_t datalen = sizeof(data) - 1; \ + unsigned int value = 0; \ + size_t value_len = sizeof(value); \ + int rv; \ + \ + rv = decode_bit_field(data, datalen, &value, value_len, 1); \ + assert_int_equal(rv, SC_SUCCESS); \ + assert_int_equal(value, int_value); \ + } +#define TORTURE_BIT_FIELD_ERROR(name, asn1_data, error) \ + static void torture_asn1_bit_field_## name (void **state) \ + { \ + u8 data[] = asn1_data; \ + size_t datalen = sizeof(data) - 1; \ + unsigned int value = 0; \ + size_t value_len = sizeof(value); \ + int rv; \ + \ + rv = decode_bit_field(data, datalen, &value, value_len, 1); \ + assert_int_equal(rv, error); \ + } +/* Without the Tag (0x03) and Length */ +/* Simple value 0 */ +TORTURE_BIT_FIELD(zero, "\x07\x00", 0) +/* Simple value 1 */ +TORTURE_BIT_FIELD(one, "\x07\x80", 1) +/* This is the last value that can be represented in the unsigned int */ +TORTURE_BIT_FIELD(uint_max, "\x00\xff\xff\xff\xff", UINT_MAX) +/* Valid padding */ +TORTURE_BIT_FIELD(padding, "\x01\xfe", 127) +/* Empty bit field needs zero padding */ +TORTURE_BIT_FIELD(zero_only, "\x00", 0) + +/* Negative test cases */ +/* Too large unused bits field */ +TORTURE_BIT_FIELD_ERROR(large_unused_bits, "\x20\xff\xff\xff\xff", SC_ERROR_INVALID_ASN1_OBJECT) +/* Too large to represent in the unsigned int type */ +TORTURE_BIT_FIELD_ERROR(too_large, "\x00\xff\xff\xff\xff\xff", SC_ERROR_BUFFER_TOO_SMALL) +/* Invalid (non-zero bits) padding */ +TORTURE_BIT_FIELD_ERROR(invalid_padding, "\x01\xff", SC_ERROR_INVALID_ASN1_OBJECT) +/* Empty bit field with non-zero zero-bits */ +TORTURE_BIT_FIELD_ERROR(zero_invalid, "\x07", SC_ERROR_INVALID_ASN1_OBJECT) +/* Empty BIT FIELD is not valid */ +TORTURE_BIT_FIELD_ERROR(empty, "", SC_ERROR_INVALID_ASN1_OBJECT) + +/* Setup context */ +static int setup_sc_context(void **state) +{ + sc_context_t *ctx = NULL; + int rv; + + rv = sc_establish_context(&ctx, "asn1"); + assert_non_null(ctx); + assert_int_equal(rv, SC_SUCCESS); + + *state = ctx; + + return 0; +} + +/* Cleanup context */ +static int teardown_sc_context(void **state) +{ + sc_context_t *ctx = *state; + int rv; + + rv = sc_release_context(ctx); + assert_int_equal(rv, SC_SUCCESS); + + return 0; +} + +#define DEPTH 1 +static void torture_asn1_decode_entry_octet_string_empty(void **state) +{ + sc_context_t *ctx = *state; + /* Skipped the Tag and Length (0x04, 0x00) */ + const u8 octet_string[0] = {}; + struct sc_asn1_entry asn1_struct[2] = { + { "direct", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + u8 *result = NULL; + size_t resultlen = 0; + int rv; + + /* set the pointers to the expected results */ + sc_format_asn1_entry(asn1_struct, &result, &resultlen, 0); + rv = asn1_decode_entry(ctx, asn1_struct, octet_string, 0, DEPTH); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(resultlen, 0); + assert_null(result); +} + +static void torture_asn1_decode_entry_octet_string_short(void **state) +{ + sc_context_t *ctx = *state; + /* Skipped the Tag and Length (0x04, 0x01) */ + const u8 octet_string[] = {0xbc}; + struct sc_asn1_entry asn1_struct[2] = { + { "direct", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | SC_ASN1_CONS, + SC_ASN1_ALLOC, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + u8 *result = NULL; + size_t resultlen = 0; + int rv; + + /* set the pointers to the expected results */ + sc_format_asn1_entry(asn1_struct, &result, &resultlen, 0); + rv = asn1_decode_entry(ctx, asn1_struct, octet_string, sizeof(octet_string), DEPTH); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(resultlen, sizeof(octet_string)); + assert_memory_equal(result, octet_string, resultlen); + free(result); +} + +/* In case of we expect UNSIGNED value from this, the parser already takes + * care of removing initial zero byte, which is used to avoid mismatches with + * negative integers */ +static void torture_asn1_decode_entry_octet_string_unsigned(void **state) +{ + sc_context_t *ctx = *state; + /* Skipped the Tag and Length (0x04, 0x02) */ + const u8 octet_string[] = {0x00, 0xff}; + struct sc_asn1_entry asn1_struct[2] = { + { "direct", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | SC_ASN1_CONS, + SC_ASN1_ALLOC | SC_ASN1_UNSIGNED, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + u8 *result = NULL; + size_t resultlen = 0; + int rv; + + /* set the pointers to the expected results */ + sc_format_asn1_entry(asn1_struct, &result, &resultlen, 0); + rv = asn1_decode_entry(ctx, asn1_struct, octet_string, sizeof(octet_string), DEPTH); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(resultlen, sizeof(octet_string) -1); + assert_memory_equal(result, octet_string + 1, resultlen); + free(result); +} + +static void torture_asn1_decode_entry_octet_string_pre_allocated(void **state) +{ + sc_context_t *ctx = *state; + /* Skipped the Tag and Length (0x04, 0x02) */ + const u8 octet_string[] = {0x01, 0x02, 0x03, 0x04}; + struct sc_asn1_entry asn1_struct[2] = { + { "direct", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | SC_ASN1_CONS, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + u8 result[8]; + size_t resultlen = sizeof(result); + int rv; + + /* set the pointers to the expected results */ + sc_format_asn1_entry(asn1_struct, &result, &resultlen, 0); + rv = asn1_decode_entry(ctx, asn1_struct, octet_string, sizeof(octet_string), DEPTH); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(resultlen, sizeof(octet_string)); + assert_memory_equal(result, octet_string, resultlen); +} + +static void torture_asn1_decode_entry_octet_string_pre_allocated_truncate(void **state) +{ + sc_context_t *ctx = *state; + /* Skipped the Tag and Length (0x04, 0x02) */ + const u8 octet_string[] = {0x01, 0x02, 0x03, 0x04}; + struct sc_asn1_entry asn1_struct[2] = { + { "direct", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | SC_ASN1_CONS, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + u8 result[2]; + size_t resultlen = sizeof(result); + int rv; + + /* set the pointers to the expected results */ + sc_format_asn1_entry(asn1_struct, &result, &resultlen, 0); + rv = asn1_decode_entry(ctx, asn1_struct, octet_string, sizeof(octet_string), DEPTH); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(resultlen, sizeof(result)); + assert_memory_equal(result, octet_string, resultlen); +} + +static void torture_asn1_decode_entry_bit_string_empty(void **state) +{ + sc_context_t *ctx = *state; + /* Skipped the Tag and Length (0x04, 0x00) */ + const u8 bit_string[] = {0x00}; + struct sc_asn1_entry asn1_struct[2] = { + { "signatureValue", SC_ASN1_BIT_STRING, SC_ASN1_TAG_BIT_STRING, SC_ASN1_ALLOC, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + u8 *result = NULL; + size_t resultlen = 0; + int rv; + + /* set the pointers to the expected results */ + sc_format_asn1_entry(asn1_struct, &result, &resultlen, 0); + rv = asn1_decode_entry(ctx, asn1_struct, bit_string, sizeof(bit_string), DEPTH); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(resultlen, 0); + assert_null(result); +} + +static void torture_asn1_decode_entry_bit_string_short(void **state) +{ + sc_context_t *ctx = *state; + /* Skipped the Tag and Length (0x04, 0x00) */ + const u8 bit_string[] = {0x00, 0xFE}; + /* By default, the bit string has MSB on the right. Yay */ + const u8 exp_result[] = {0x7F}; + struct sc_asn1_entry asn1_struct[2] = { + { "signatureValue", SC_ASN1_BIT_STRING, SC_ASN1_TAG_BIT_STRING, SC_ASN1_ALLOC, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + u8 *result = NULL; + size_t resultlen = 0; + int rv; + + /* set the pointers to the expected results */ + sc_format_asn1_entry(asn1_struct, &result, &resultlen, 0); + rv = asn1_decode_entry(ctx, asn1_struct, bit_string, sizeof(bit_string), DEPTH); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(resultlen, 8); + assert_memory_equal(exp_result, result, resultlen/8); + free(result); +} + +/* This modification does not invert the bit order */ +static void torture_asn1_decode_entry_bit_string_ni(void **state) +{ + sc_context_t *ctx = *state; + /* Skipped the Tag and Length (0x04, 0x00) */ + const u8 bit_string[] = {0x00, 0xFE}; + struct sc_asn1_entry asn1_struct[2] = { + { "signatureValue", SC_ASN1_BIT_STRING_NI, SC_ASN1_TAG_BIT_STRING, SC_ASN1_ALLOC, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + u8 *result = NULL; + size_t resultlen = 0; + int rv; + + /* set the pointers to the expected results */ + sc_format_asn1_entry(asn1_struct, &result, &resultlen, 0); + rv = asn1_decode_entry(ctx, asn1_struct, bit_string, sizeof(bit_string), DEPTH); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(resultlen, 8); + assert_memory_equal(bit_string + 1, result, resultlen/8); + free(result); +} + +static void torture_asn1_put_tag_short(void **state) +{ + unsigned int tag = 0xAC; + const u8 expected[] = {0xAC, 0x01, 0x02}; + const u8 data[] = {0x02}; + size_t data_len = 1; + u8 out[10]; + size_t out_len = sizeof(out); + u8 *p = out; + int rv; + + /* Without the out and out_len we are getting expected length */ + rv = sc_asn1_put_tag(tag, data, data_len, NULL, 0, &p); + assert_int_equal(rv, sizeof(expected)); + assert_ptr_equal(p, out); + + /* Now we do the actual encoding */ + rv = sc_asn1_put_tag(tag, data, data_len, out, out_len, &p); + assert_int_equal(rv, SC_SUCCESS); + assert_memory_equal(out, expected, sizeof(expected)); + assert_ptr_equal(p, out + sizeof(expected)); + + /* Short buffer */ + rv = sc_asn1_put_tag(tag, data, data_len, out, 2, &p); + assert_int_equal(rv, SC_ERROR_BUFFER_TOO_SMALL); +} + +static void torture_asn1_put_tag_long_tag(void **state) +{ + /* Max supported value already encoded as ASN1 tag */ + unsigned int tag = 0xFFFFFF7F; + const u8 expected[] = {0xFF, 0xFF, 0xFF, 0x7F, 0x01, 0x02}; + const u8 data[] = {0x02}; + size_t data_len = 1; + u8 out[10]; + size_t out_len = sizeof(out); + u8 *p = out; + int rv; + + /* Without the out and out_len we are getting expected length */ + rv = sc_asn1_put_tag(tag, data, data_len, NULL, 0, &p); + assert_int_equal(rv, sizeof(expected)); + assert_ptr_equal(p, out); + + /* Now we do the actual encoding */ + rv = sc_asn1_put_tag(tag, data, data_len, out, out_len, &p); + assert_int_equal(rv, SC_SUCCESS); + assert_memory_equal(out, expected, sizeof(expected)); + assert_ptr_equal(p, out + sizeof(expected)); + + /* The buffer is too small */ + rv = sc_asn1_put_tag(tag, data, data_len, out, 5, &p); + assert_int_equal(rv, SC_ERROR_BUFFER_TOO_SMALL); + + /* the MSB of last byte needs to be 0 */ + tag = 0xFFFFFF8F; + rv = sc_asn1_put_tag(tag, data, data_len, NULL, 0, NULL); + assert_int_equal(rv, SC_ERROR_INVALID_DATA); + + /* the MSB of all byts needs to be 1 */ + tag = 0xFFFF7F7F; + rv = sc_asn1_put_tag(tag, data, data_len, NULL, 0, NULL); + assert_int_equal(rv, SC_ERROR_INVALID_DATA); + + /* First byte has bits 5-1 set to 1 */ + tag = 0xE0FFFF7F; + rv = sc_asn1_put_tag(tag, data, data_len, NULL, 0, NULL); + assert_int_equal(rv, SC_ERROR_INVALID_DATA); +} + +static void torture_asn1_put_tag_long_data(void **state) +{ + unsigned int tag = 0xAC; + const u8 expected[131] = {0xAC, 0x81, 0x80, 0x00, /* the rest is zero */}; + const u8 data[128] = {0}; + size_t data_len = sizeof(data); + u8 out[200]; + size_t out_len = sizeof(out); + u8 *p = out; + int rv; + + /* Without the out and out_len we are getting expected length */ + rv = sc_asn1_put_tag(tag, data, data_len, NULL, 0, &p); + assert_int_equal(rv, sizeof(expected)); + assert_ptr_equal(p, out); + + /* Now we do the actual encoding */ + rv = sc_asn1_put_tag(tag, data, data_len, out, out_len, &p); + assert_int_equal(rv, SC_SUCCESS); + assert_memory_equal(out, expected, sizeof(expected)); + assert_ptr_equal(p, out + sizeof(expected)); + + /* The buffer is too small */ + rv = sc_asn1_put_tag(tag, data, data_len, out, 130, &p); + assert_int_equal(rv, SC_ERROR_BUFFER_TOO_SMALL); +} + +static void torture_asn1_put_tag_without_data(void **state) +{ + unsigned int tag = 0xAC; + const u8 expected[] = {0xAC, 0x01}; + size_t data_len = 1; + u8 out[10]; + size_t out_len = sizeof(out); + u8 *p = out; + int rv; + + /* Without the out and out_len we are getting expected length */ + rv = sc_asn1_put_tag(tag, NULL, data_len, NULL, 0, &p); + assert_int_equal(rv, sizeof(expected) + data_len); + assert_ptr_equal(p, out); + + /* Now we do the actual encoding, but data field is not filled */ + rv = sc_asn1_put_tag(tag, NULL, data_len, out, out_len, &p); + assert_int_equal(rv, SC_SUCCESS); + assert_memory_equal(out, expected, sizeof(expected)); + assert_ptr_equal(p, out + sizeof(expected)); +} + +static void torture_asn1_encode_simple(void **state) +{ + sc_context_t *ctx = *state; + struct sc_asn1_entry asn1[] = { + { "OctetString", SC_ASN1_OCTET_STRING, 0x05, SC_ASN1_PRESENT, NULL, NULL }, + { NULL , 0 , 0 , 0 , NULL , NULL } + }; + u8 expected[] = {0x05, 0x09, 't', 'e', 's', 't', ' ', 'd', 'a', 't', 'a'}; + char *data = "test data"; + size_t datalen = strlen(data); + u8 *outptr = NULL; + size_t outlen = 0; + int rv; + + /* NULL arguments should not crash */ + rv = sc_asn1_encode(NULL, NULL, NULL, NULL); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); + + /* NULL asn1 entry should not crash */ + rv = sc_asn1_encode(ctx, NULL, NULL, NULL); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); + + /* Real example of encoding an octet string */ + asn1[0].parm = data; + asn1[0].arg = &datalen; + rv = sc_asn1_encode(ctx, asn1, &outptr, &outlen); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(outlen, sizeof(expected)); + assert_memory_equal(expected, outptr, sizeof(expected)); + free(outptr); + + /* Context is not needed */ + rv = sc_asn1_encode(NULL, asn1, &outptr, &outlen); + assert_int_equal(rv, SC_SUCCESS); + free(outptr); +} + +/* + * Tests for SC_ASN1_OPTIONAL on SC_ASN1_CHOICE entries. + * + * Schema used in the first four tests (outer has three entries): + * before INTEGER mandatory + * choice CHOICE(NULL|OID) optional in tests 1,2,4; mandatory in test 3 + * after INTEGER mandatory + * + * The end-of-stream test (test 5) omits the "after" field. + */ +static void +torture_asn1_decode_optional_choice_absent(void **state) +{ + sc_context_t *ctx = *state; + // clang-format off + struct sc_asn1_entry choice_alts[3] = { + { "null_alt", SC_ASN1_NULL, SC_ASN1_TAG_NULL, 0, NULL, NULL }, + { "object_alt", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + struct sc_asn1_entry outer[4] = { + { "before", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "choice", SC_ASN1_CHOICE, 0, SC_ASN1_OPTIONAL, NULL, NULL }, + { "after", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + // clang-format on + /* INTEGER(1), no CHOICE tag, INTEGER(2) */ + const u8 data[] = {0x02, 0x01, 0x01, 0x02, 0x01, 0x02}; + int before = 0, after = 0; + int rv; + + sc_format_asn1_entry(&outer[0], &before, NULL, 0); + sc_format_asn1_entry(&outer[1], choice_alts, NULL, 0); + sc_format_asn1_entry(&outer[2], &after, NULL, 0); + + rv = sc_asn1_decode(ctx, outer, data, sizeof(data), NULL, NULL); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(before, 1); + assert_int_equal(after, 2); + assert_false(choice_alts[0].flags & SC_ASN1_PRESENT); + assert_false(choice_alts[1].flags & SC_ASN1_PRESENT); +} + +static void +torture_asn1_decode_optional_choice_present(void **state) +{ + sc_context_t *ctx = *state; + struct sc_object_id oid_val; + // clang-format off + struct sc_asn1_entry choice_alts[3] = { + { "null_alt", SC_ASN1_NULL, SC_ASN1_TAG_NULL, 0, NULL, NULL }, + { "object_alt", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + struct sc_asn1_entry outer[4] = { + { "before", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "choice", SC_ASN1_CHOICE, 0, SC_ASN1_OPTIONAL, NULL, NULL }, + { "after", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + // clang-format on + /* INTEGER(1), OID {1.2} (0x06 0x01 0x2a — matches second CHOICE alternative), INTEGER(2) */ + const u8 data[] = {0x02, 0x01, 0x01, 0x06, 0x01, 0x2a, 0x02, 0x01, 0x02}; + int before = 0, after = 0; + int rv; + + sc_format_asn1_entry(&choice_alts[1], &oid_val, NULL, 0); + sc_format_asn1_entry(&outer[0], &before, NULL, 0); + sc_format_asn1_entry(&outer[1], choice_alts, NULL, 0); + sc_format_asn1_entry(&outer[2], &after, NULL, 0); + + rv = sc_asn1_decode(ctx, outer, data, sizeof(data), NULL, NULL); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(before, 1); + assert_int_equal(after, 2); + assert_false(choice_alts[0].flags & SC_ASN1_PRESENT); + assert_true(choice_alts[1].flags & SC_ASN1_PRESENT); + assert_int_equal(oid_val.value[0], 1); + assert_int_equal(oid_val.value[1], 2); +} + +static void +torture_asn1_decode_mandatory_choice_absent(void **state) +{ + sc_context_t *ctx = *state; + // clang-format off + struct sc_asn1_entry choice_alts[3] = { + { "null_alt", SC_ASN1_NULL, SC_ASN1_TAG_NULL, 0, NULL, NULL }, + { "object_alt", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + struct sc_asn1_entry outer[4] = { + { "before", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "choice", SC_ASN1_CHOICE, 0, 0, NULL, NULL }, /* NOT optional */ + { "after", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + // clang-format on + /* INTEGER(1), no CHOICE tag, INTEGER(2) — CHOICE is mandatory so this must fail */ + const u8 data[] = {0x02, 0x01, 0x01, 0x02, 0x01, 0x02}; + int before = 0, after = 0; + int rv; + + sc_format_asn1_entry(&outer[0], &before, NULL, 0); + sc_format_asn1_entry(&outer[1], choice_alts, NULL, 0); + sc_format_asn1_entry(&outer[2], &after, NULL, 0); + + rv = sc_asn1_decode(ctx, outer, data, sizeof(data), NULL, NULL); + assert_int_equal(rv, SC_ERROR_ASN1_OBJECT_NOT_FOUND); + assert_int_equal(before, 1); + assert_int_equal(after, 0); + assert_false(choice_alts[0].flags & SC_ASN1_PRESENT); + assert_false(choice_alts[1].flags & SC_ASN1_PRESENT); +} + +static void +torture_asn1_decode_optional_choice_malformed(void **state) +{ + sc_context_t *ctx = *state; + struct sc_object_id oid_val; + // clang-format off + struct sc_asn1_entry choice_alts[3] = { + { "null_alt", SC_ASN1_NULL, SC_ASN1_TAG_NULL, 0, NULL, NULL }, + { "object_alt", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + struct sc_asn1_entry outer[4] = { + { "before", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "choice", SC_ASN1_CHOICE, 0, SC_ASN1_OPTIONAL, NULL, NULL }, + { "after", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + // clang-format on + /* INTEGER(1), OID tag with malformed content (0x81 has the multi-byte continuation + * bit set but no following byte), INTEGER(2). A real storage pointer is required on + * the OID alternative: with parm=NULL asn1_decode_entry skips content validation and + * the TLV is silently consumed. With parm set, sc_asn1_decode_object_id returns an + * error; the inner decoder exits before writing back p/left, so the optional CHOICE + * swallows the error and the position is unchanged. "after" then sees 0x06 (OID tag) + * where INTEGER (0x02) was expected → SC_ERROR_ASN1_OBJECT_NOT_FOUND. */ + const u8 data[] = {0x02, 0x01, 0x01, 0x06, 0x01, 0x81, 0x02, 0x01, 0x02}; + int before = 0, after = 0; + int rv; + + sc_format_asn1_entry(&choice_alts[1], &oid_val, NULL, 0); + sc_format_asn1_entry(&outer[0], &before, NULL, 0); + sc_format_asn1_entry(&outer[1], choice_alts, NULL, 0); + sc_format_asn1_entry(&outer[2], &after, NULL, 0); + + rv = sc_asn1_decode(ctx, outer, data, sizeof(data), NULL, NULL); + assert_int_equal(rv, SC_ERROR_ASN1_OBJECT_NOT_FOUND); + assert_int_equal(before, 1); + assert_int_equal(after, 0); + assert_false(choice_alts[0].flags & SC_ASN1_PRESENT); + assert_false(choice_alts[1].flags & SC_ASN1_PRESENT); +} + +static void +torture_asn1_decode_optional_choice_end_of_stream(void **state) +{ + sc_context_t *ctx = *state; + // clang-format off + struct sc_asn1_entry choice_alts[3] = { + { "null_alt", SC_ASN1_NULL, SC_ASN1_TAG_NULL, 0, NULL, NULL }, + { "object_alt", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + struct sc_asn1_entry outer[3] = { + { "before", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "choice", SC_ASN1_CHOICE, 0, SC_ASN1_OPTIONAL, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + // clang-format on + /* INTEGER(1) only — stream ends before the optional CHOICE */ + const u8 data[] = {0x02, 0x01, 0x01}; + int before = 0; + int rv; + + sc_format_asn1_entry(&outer[0], &before, NULL, 0); + sc_format_asn1_entry(&outer[1], choice_alts, NULL, 0); + + rv = sc_asn1_decode(ctx, outer, data, sizeof(data), NULL, NULL); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(before, 1); + assert_false(choice_alts[0].flags & SC_ASN1_PRESENT); + assert_false(choice_alts[1].flags & SC_ASN1_PRESENT); +} + +int main(void) +{ + int rc; + // clang-format off + struct CMUnitTest tests[] = { + /* INTEGER */ + cmocka_unit_test(torture_asn1_integer_zero), + cmocka_unit_test(torture_asn1_integer_one), + cmocka_unit_test(torture_asn1_integer_minus_one), + cmocka_unit_test(torture_asn1_integer_padded_128), + cmocka_unit_test(torture_asn1_integer_max2), + cmocka_unit_test(torture_asn1_integer_min2), + cmocka_unit_test(torture_asn1_integer_max4), + cmocka_unit_test(torture_asn1_integer_min4), + cmocka_unit_test(torture_asn1_integer_null), + cmocka_unit_test(torture_asn1_integer_over), + cmocka_unit_test(torture_asn1_integer_padded_zero), + cmocka_unit_test(torture_asn1_integer_padded_one), + cmocka_unit_test(torture_asn1_integer_padded_minus_one), + cmocka_unit_test(torture_asn1_integer_padded_127), + cmocka_unit_test(torture_asn1_integer_negative), + /* OBJECT ID */ + cmocka_unit_test(torture_asn1_oid_small), + cmocka_unit_test(torture_asn1_oid_limit), + cmocka_unit_test(torture_asn1_oid_two_byte), + cmocka_unit_test(torture_asn1_oid_ecpubkey), + cmocka_unit_test(torture_asn1_oid_missing), + cmocka_unit_test(torture_asn1_oid_missing_second), + cmocka_unit_test(torture_asn1_oid_last_int_max), + cmocka_unit_test(torture_asn1_oid_first_int_max), + cmocka_unit_test(torture_asn1_oid_last_32b_overflow), + cmocka_unit_test(torture_asn1_oid_first_32b_overflow), + cmocka_unit_test(torture_asn1_oid_non_minimal), + cmocka_unit_test(torture_asn1_oid_non_minimal_second), + /* BIT FIELD */ + cmocka_unit_test(torture_asn1_bit_field_zero), + cmocka_unit_test(torture_asn1_bit_field_one), + cmocka_unit_test(torture_asn1_bit_field_uint_max), + cmocka_unit_test(torture_asn1_bit_field_padding), + cmocka_unit_test(torture_asn1_bit_field_zero_only), + cmocka_unit_test(torture_asn1_bit_field_large_unused_bits), + cmocka_unit_test(torture_asn1_bit_field_too_large), + cmocka_unit_test(torture_asn1_bit_field_invalid_padding), + cmocka_unit_test(torture_asn1_bit_field_zero_invalid), + cmocka_unit_test(torture_asn1_bit_field_empty), + /* decode_entry(): OCTET STRING */ + cmocka_unit_test_setup_teardown(torture_asn1_decode_entry_octet_string_empty, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_asn1_decode_entry_octet_string_short, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_asn1_decode_entry_octet_string_unsigned, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_asn1_decode_entry_octet_string_pre_allocated, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_asn1_decode_entry_octet_string_pre_allocated_truncate, + setup_sc_context, teardown_sc_context), + /* decode_entry(): BIT STRING */ + cmocka_unit_test_setup_teardown(torture_asn1_decode_entry_bit_string_empty, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_asn1_decode_entry_bit_string_short, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_asn1_decode_entry_bit_string_ni, + setup_sc_context, teardown_sc_context), + /* put_tag() */ + cmocka_unit_test(torture_asn1_put_tag_short), + cmocka_unit_test(torture_asn1_put_tag_without_data), + cmocka_unit_test(torture_asn1_put_tag_long_tag), + cmocka_unit_test(torture_asn1_put_tag_long_data), + /* encode() */ + cmocka_unit_test_setup_teardown(torture_asn1_encode_simple, + setup_sc_context, teardown_sc_context), + /* decode(): optional CHOICE */ + cmocka_unit_test_setup_teardown(torture_asn1_decode_optional_choice_absent, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_asn1_decode_optional_choice_present, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_asn1_decode_mandatory_choice_absent, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_asn1_decode_optional_choice_malformed, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_asn1_decode_optional_choice_end_of_stream, + setup_sc_context, teardown_sc_context), + }; + // clang-format on + + rc = cmocka_run_group_tests(tests, NULL, NULL); + return rc; +} diff --git a/src/tests/unittests/base64.c b/src/tests/unittests/base64.c new file mode 100644 index 0000000000..a63bcc6e1f --- /dev/null +++ b/src/tests/unittests/base64.c @@ -0,0 +1,176 @@ +/* + * base64.c: Unit tests for Base64 encoding and decoding + * + * Copyright (C) 2024 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "common/compat_strlcpy.c" +#include "libopensc/log.c" +#include "libopensc/padding.c" +#include "torture.h" +#include + +static void +torture_encode_short_length(void **state) +{ + u8 data[] = "ew"; + size_t data_len = 2; + u8 buf[6] = {0}; + size_t buf_len = 6; + u8 expected[] = "ZXc=\n"; + size_t expected_size = 6; + int r = sc_base64_encode(data, data_len, buf, buf_len, 64); + assert_int_equal(r, SC_SUCCESS); + assert_memory_equal(buf, expected, expected_size); +} + +static void +torture_encode_data(void **state) +{ + u8 data[] = "Hello World"; + size_t data_len = 11; + u8 buf[18] = {0}; + size_t buf_len = 18; + u8 expected[] = "SGVsbG8gV29ybGQ=\n"; + size_t expected_size = 18; + int r = sc_base64_encode(data, data_len, buf, buf_len, 64); + assert_int_equal(r, SC_SUCCESS); + assert_memory_equal(buf, expected, expected_size); +} + +static void +torture_encode_more_lines(void **state) +{ + u8 data[] = "Hello World"; + size_t data_len = 11; + u8 buf[21] = {0}; + size_t buf_len = 21; + u8 expected[] = "SGVs\nbG8g\nV29y\nbGQ=\n"; + size_t expected_size = 21; + int r = sc_base64_encode(data, data_len, buf, buf_len, 4); + assert_int_equal(r, SC_SUCCESS); + assert_memory_equal(buf, expected, expected_size); +} + +static void +torture_encode_small_out_length_for_lines(void **state) +{ + u8 data[] = "Hello World"; + size_t data_len = 11; + u8 buf[18] = {0}; + size_t buf_len = 18; + int r = sc_base64_encode(data, data_len, buf, buf_len, 4); + assert_int_equal(r, SC_ERROR_BUFFER_TOO_SMALL); +} + +static void +torture_encode_small_out_length_for_last_foursome(void **state) +{ + u8 data[] = "Hello World"; + size_t data_len = 11; + u8 buf[15] = {0}; + size_t buf_len = 15; + int r = sc_base64_encode(data, data_len, buf, buf_len, 64); + assert_int_equal(r, SC_ERROR_BUFFER_TOO_SMALL); +} + +static void +torture_encode_small_out_length_for_last_newline(void **state) +{ + u8 data[] = "Hello World"; + size_t data_len = 11; + u8 buf[16] = {0}; + size_t buf_len = 16; + int r = sc_base64_encode(data, data_len, buf, buf_len, 64); + assert_int_equal(r, SC_ERROR_BUFFER_TOO_SMALL); +} + +static void +torture_encode_small_out_length_for_last_0(void **state) +{ + u8 data[] = "Hello World"; + size_t data_len = 11; + u8 buf[17] = {0}; + size_t buf_len = 17; + int r = sc_base64_encode(data, data_len, buf, buf_len, 64); + assert_int_equal(r, SC_ERROR_BUFFER_TOO_SMALL); +} + +static void +torture_decode_short_data(void **state) +{ + char data[] = "SGVsbG8gV29ybGQ="; + u8 buf[11] = {0}; + size_t buf_len = 11; + u8 expected[] = "Hello World"; + size_t expected_size = 11; + int actual_size = sc_base64_decode(data, buf, buf_len); + assert_int_equal(actual_size, expected_size); + assert_memory_equal(buf, expected, expected_size); +} + +static void +torture_decode_skip_newline_inside(void **state) +{ + char data[] = "SG\nVsbG8gV29ybGQ="; + u8 buf[11] = {0}; + size_t buf_len = 11; + u8 expected[] = "Hello World"; + size_t expected_size = 11; + int actual_size = sc_base64_decode(data, buf, buf_len); + assert_int_equal(actual_size, expected_size); + assert_memory_equal(buf, expected, expected_size); +} + +static void +torture_decode_zero_byte_early_finish(void **state) +{ + char data[] = "\0GVsbG8gV29ybGQ="; + u8 buf[11] = {0}; + size_t buf_len = 11; + int error = sc_base64_decode(data, buf, buf_len); + assert_int_equal(error, 0); +} + +static void +torture_decode_non_ascii_character(void **state) +{ + char data[] = "SG\x11sbG8gV29ybGQ="; + u8 buf[11] = {0}; + size_t buf_len = 11; + int error = sc_base64_decode(data, buf, buf_len); + assert_int_equal(error, SC_ERROR_INVALID_ARGUMENTS); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(torture_encode_short_length), + cmocka_unit_test(torture_encode_data), + cmocka_unit_test(torture_encode_more_lines), + cmocka_unit_test(torture_encode_small_out_length_for_lines), + cmocka_unit_test(torture_encode_small_out_length_for_last_foursome), + cmocka_unit_test(torture_encode_small_out_length_for_last_newline), + cmocka_unit_test(torture_encode_small_out_length_for_last_0), + cmocka_unit_test(torture_decode_short_data), + cmocka_unit_test(torture_decode_skip_newline_inside), + cmocka_unit_test(torture_decode_zero_byte_early_finish), + cmocka_unit_test(torture_decode_non_ascii_character)}; + return cmocka_run_group_tests(tests, NULL, NULL); +} \ No newline at end of file diff --git a/src/tests/unittests/cachedir.c b/src/tests/unittests/cachedir.c new file mode 100644 index 0000000000..0cfd309399 --- /dev/null +++ b/src/tests/unittests/cachedir.c @@ -0,0 +1,107 @@ +/* + * cachedir.c: Test various options how cache dir is evaluated + * + * Copyright (C) 2020 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include + +#include "torture.h" +#include "libopensc/opensc.h" + +static void torture_cachedir_default_empty_home(void **state) +{ + sc_context_t *ctx = NULL; + char buf[PATH_MAX] = {0}; + size_t buflen = sizeof(buf); + int rv; + + rv = sc_establish_context(&ctx, "cachedir"); + assert_int_equal(rv, SC_SUCCESS); + assert_non_null(ctx); + + /* Keep configuration empty */ + setenv("OPENSC_CONF", "/nonexistent", 1); + setenv("XDG_CACHE_HOME", "", 1); + setenv("HOME", "", 1); + + rv = sc_get_cache_dir(ctx, buf, buflen); + assert_int_equal(rv, SC_ERROR_INTERNAL); + + sc_release_context(ctx); +} + +static void torture_cachedir_default_empty(void **state) +{ + sc_context_t *ctx = NULL; + char buf[PATH_MAX] = {0}; + size_t buflen = sizeof(buf); + int rv; + + rv = sc_establish_context(&ctx, "cachedir"); + assert_int_equal(rv, SC_SUCCESS); + assert_non_null(ctx); + + /* Keep configuration empty */ + setenv("OPENSC_CONF", "/nonexistent", 1); + setenv("XDG_CACHE_HOME", "", 1); + setenv("HOME", "/home/test", 1); + + rv = sc_get_cache_dir(ctx, buf, buflen); + assert_int_equal(rv, SC_SUCCESS); + assert_string_equal(buf, "/home/test/.cache/opensc"); + + sc_release_context(ctx); +} + +static void torture_cachedir_default_cache_home(void **state) +{ + sc_context_t *ctx = NULL; + char buf[PATH_MAX] = {0}; + size_t buflen = sizeof(buf); + int rv; + + rv = sc_establish_context(&ctx, "cachedir"); + assert_int_equal(rv, SC_SUCCESS); + assert_non_null(ctx); + + /* Keep configuration empty */ + setenv("OPENSC_CONF", "/nonexistent", 1); + setenv("XDG_CACHE_HOME", "/home/test2/.cache", 1); + setenv("HOME", "/home/test", 1); + + rv = sc_get_cache_dir(ctx, buf, buflen); + assert_int_equal(rv, SC_SUCCESS); + assert_string_equal(buf, "/home/test2/.cache/opensc"); + + sc_release_context(ctx); +} + + +int main(void) +{ + int rc; + struct CMUnitTest tests[] = { + cmocka_unit_test(torture_cachedir_default_empty_home), + cmocka_unit_test(torture_cachedir_default_empty), + cmocka_unit_test(torture_cachedir_default_cache_home), + }; + + rc = cmocka_run_group_tests(tests, NULL, NULL); + return rc; +} diff --git a/src/tests/unittests/check_macro_reference_loop.c b/src/tests/unittests/check_macro_reference_loop.c new file mode 100644 index 0000000000..c8a20a71af --- /dev/null +++ b/src/tests/unittests/check_macro_reference_loop.c @@ -0,0 +1,186 @@ +/* + * check_macro_reference_loop.c: Unit tests checking macro reference loop + * + * Copyright (C) 2023 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#define SC_PKCS15_PROFILE_DIRECTORY "" + +#include "torture.h" +#include "libopensc/log.c" +#include "pkcs15init/profile.c" +#include "common/compat_strlcpy.c" +#include + +static void +torture_no_loop(void **state) +{ + scconf_list value = {.data = "value"}; + sc_macro_t macro = {.name = "name", .value = &value}; + sc_profile_t profile = {.macro_list = ¯o}; + + int r = check_macro_reference_loop("name", ¯o, &profile, 10); + assert_int_equal(r, 0); +} + +static void +torture_one_macro_no_loop(void **state) +{ + scconf_list value = {.data = "value"}; + sc_macro_t macro = {.name = "name", .value = &value}; + sc_profile_t profile = {.macro_list = ¯o}; + + int r = check_macro_reference_loop("name", ¯o, &profile, 10); + assert_int_equal(r, 0); +} + +static void +torture_one_macro_loop(void **state) +{ + scconf_list value = {.data = "$name"}; + sc_macro_t macro = {.name = "name", .value = &value}; + sc_profile_t profile = {.macro_list = ¯o}; + + int r = check_macro_reference_loop("name", ¯o, &profile, 10); + assert_int_equal(r, 1); +} + +static void +torture_long_macro_loop(void **state) +{ + scconf_list value1 = {.data = "$second"}; + scconf_list value2 = {.data = "$third"}; + scconf_list value3 = {.data = "$first"}; + sc_macro_t macro3 = {.name = "third", .value = &value3}; + sc_macro_t macro2 = {.name = "second", .value = &value2, .next = ¯o3}; + sc_macro_t macro1 = {.name = "first", .value = &value1, .next = ¯o2}; + sc_profile_t profile = {.macro_list = ¯o1}; + + int r = check_macro_reference_loop("first", ¯o1, &profile, 10); + assert_int_equal(r, 1); +} + +static void +torture_long_macro_loop_too_deep(void **state) +{ + scconf_list value1 = {.data = "$second"}; + scconf_list value2 = {.data = "$third"}; + scconf_list value3 = {.data = "value"}; + sc_macro_t macro3 = {.name = "third", .value = &value3}; + sc_macro_t macro2 = {.name = "second", .value = &value2, .next = ¯o3}; + sc_macro_t macro1 = {.name = "first", .value = &value1, .next = ¯o2}; + sc_profile_t profile = {.macro_list = ¯o1}; + + int r = check_macro_reference_loop("first", ¯o1, &profile, 14); + assert_int_equal(r, 1); +} + +static void +torture_macro_loop_inner_string(void **state) +{ + scconf_list value1 = {.data = "xx$second"}; + scconf_list value2 = {.data = "$third"}; + scconf_list value3 = {.data = "$first\0"}; + sc_macro_t macro3 = {.name = "third", .value = &value3}; + sc_macro_t macro2 = {.name = "second", .value = &value2, .next = ¯o3}; + sc_macro_t macro1 = {.name = "first", .value = &value1, .next = ¯o2}; + sc_profile_t profile = {.macro_list = ¯o1}; + + int r = check_macro_reference_loop("first", ¯o1, &profile, 10); + assert_int_equal(r, 1); +} + +static void +torture_macro_loop_indirect(void **state) +{ + scconf_list value1 = {.data = "$x"}; + scconf_list value2 = {.data = "-$x"}; + sc_macro_t macro2 = {.name = "o", .value = &value2}; + sc_macro_t macro1 = {.name = "x", .value = &value1, .next = ¯o2}; + sc_profile_t profile = {.macro_list = ¯o1}; + + int r = check_macro_reference_loop("o", ¯o2, &profile, 10); + assert_int_equal(r, 1); +} + +static void +torture_macro_loop_indirect_multivalue(void **state) +{ + scconf_list value3 = {.data = "-$x"}; + scconf_list value2 = {.data = "1", .next = &value3}; + scconf_list value1 = {.data = "$x"}; + sc_macro_t macro2 = {.name = "o", .value = &value2}; + sc_macro_t macro1 = {.name = "x", .value = &value1, .next = ¯o2}; + sc_profile_t profile = {.macro_list = ¯o1}; + + int r = check_macro_reference_loop("o", ¯o2, &profile, 10); + assert_int_equal(r, 1); +} + +#if 0 +/* A reproducer for https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=64549 + * This can no longer happen as the non-printable macro names are now ignored while they are defined + */ +static void torture_macro_loop_indirect_nonprintable(void **state) +{ + scconf_list value3 = {.data = "$\270\270x\001"}; + scconf_list value2 = {.data = "$e"}; + scconf_list value1 = {.data = "$e"}; + sc_macro_t macro3 = {.name = "e", .value = &value3}; + sc_macro_t macro2 = {.name = "osi", .value = &value2, .next = ¯o3}; + sc_macro_t macro1 = {.name = "\270\270x\001", .value = &value1, .next = ¯o2}; + sc_profile_t profile = {.macro_list = ¯o1}; + + int r = check_macro_reference_loop("osi", ¯o2, &profile, 10); + assert_int_equal(r, 1); +} +#endif /* 0 */ + +/* + *A reproducer for https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=68061 + */ +static void +torture_macro_loop_long_name(void **state) +{ + scconf_list value1 = {.data = "$second"}; + scconf_list value2 = {.data = "$dtBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCBBBBe"}; + scconf_list value3 = {.data = "$second"}; + sc_macro_t macro3 = {.name = "dtBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCBBBBe", .value = &value3}; + sc_macro_t macro2 = {.name = "second", .value = &value2, .next = ¯o3}; + sc_macro_t macro1 = {.name = "first", .value = &value1, .next = ¯o2}; + sc_profile_t profile = {.macro_list = ¯o1}; + + int r = check_macro_reference_loop("first", ¯o1, &profile, 10); + assert_int_equal(r, 1); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(torture_no_loop), + cmocka_unit_test(torture_one_macro_no_loop), + cmocka_unit_test(torture_one_macro_loop), + cmocka_unit_test(torture_long_macro_loop), + cmocka_unit_test(torture_long_macro_loop_too_deep), + cmocka_unit_test(torture_macro_loop_inner_string), + cmocka_unit_test(torture_macro_loop_indirect), + cmocka_unit_test(torture_macro_loop_indirect_multivalue), + cmocka_unit_test(torture_macro_loop_long_name), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/unittests/compression.c b/src/tests/unittests/compression.c new file mode 100644 index 0000000000..ddd34a6d41 --- /dev/null +++ b/src/tests/unittests/compression.c @@ -0,0 +1,355 @@ +/* + * compression.c: Unit tests for compression API + * + * Copyright (C) 2019 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "torture.h" +#include "libopensc/log.c" +#include "libopensc/compression.c" + +/* The data from fuzzer has valid header (0x1f, 0x8b), but anything + * after that is just garbage. The first call to inflate() + * returns Z_STREAM_END, calculated number of processed bytes 0, while + * keeping the allocated buffers. + */ +u8 invalid_data[] = { + 0x1f, 0x8b, 0x08, 0x10, 0x08, 0x78, 0x10, 0x1f, + 0x8b, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, + 0x10, 0x08, 0x78, 0x10, 0x1f, 0x8b, 0x08, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x10, 0x08, 0x78, + 0x10, 0x1f, 0x8b, 0x08, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x08, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x10, 0x08, 0x78, + 0x10, 0x1f, 0x8b}; + +/* Generated using + * $ echo "test" > /tmp/test + * $ gzip -c /tmp/test > /tmp/test.gz + * $ hexdump -C /tmp/test.gz + */ +u8 valid_data[] = { + 0x1f, 0x8b, 0x08, 0x08, 0x5d, 0xd8, 0xcb, 0x5d, + 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x00, 0x2b, + 0x49, 0x2d, 0x2e, 0xe1, 0x02, 0x00, 0xc6, 0x35, + 0xb9, 0x3b, 0x05, 0x00, 0x00, 0x00}; + +/* Generated as in the previous test case with some added mess on the end + */ +u8 invalid_suffix_data[] = { + 0x1f, 0x8b, 0x08, 0x08, 0x5d, 0xd8, 0xcb, 0x5d, + 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x00, 0x2b, + 0x49, 0x2d, 0x2e, 0xe1, 0x02, 0x00, 0xc6, 0x35, + 0xb9, 0x3b, 0x05, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff}; + +/* https://github.com/madler/zlib/blob/master/test/infcover.c + */ +u8 zlib_good[] = {0x78, 0x9c, 0x63, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1}; + +/* Generated using + * $ echo "test" > /tmp/test + * $ pigz --zlib /tmp/test > /tmp/test.zz + * $ hexdump -C /tmp/test.zz + */ +u8 valid_zlib_data[] = {0x78, 0x5e, 0x2b, 0x49, 0x2d, 0x2e, 0xe1, 0x02, 0x00, 0x06, 0x28, 0x01, 0xcb}; + +/* Generated as in the previous test case with some added mess on the end + */ +u8 invalid_zlib_suffix_data[] = {0x78, 0x5e, 0x2b, 0x49, 0x2d, 0x2e, 0xe1, 0x02, 0x00, 0x06, 0x28, 0x01, 0xcb, + 0xff, 0xff, 0xff, 0xff}; + +static void torture_compression_decompress_alloc_empty(void **state) +{ + u8 *buf = NULL; + u8 *data = NULL; + size_t buflen = 0; + size_t datalen = 0; + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); + assert_null(buf); +} + +static void torture_compression_decompress_alloc_gzip_empty(void **state) +{ + u8 *buf = NULL; + u8 *data = NULL; + size_t buflen = 0; + size_t datalen = 0; + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, data, datalen, COMPRESSION_GZIP); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); + assert_null(buf); +} + +static void torture_compression_decompress_alloc_zlib_empty(void **state) +{ + u8 *buf = NULL; + u8 *data = NULL; + size_t buflen = 0; + size_t datalen = 0; + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, data, datalen, COMPRESSION_ZLIB); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); + assert_null(buf); +} + +static void torture_compression_decompress_alloc_header(void **state) +{ + u8 *buf = NULL; + u8 data[] = {0x1f, 0x8b}; + size_t buflen = 0; + size_t datalen = sizeof(data); + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); + assert_null(buf); +} + +static void torture_compression_decompress_alloc_header_invalid(void **state) +{ + u8 *buf = NULL; + u8 data[] = {0x1e, 0x8a}; + size_t buflen = 0; + size_t datalen = sizeof(data); + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); + assert_null(buf); +} + +static void torture_compression_decompress_alloc_invalid(void **state) +{ + u8 *buf = NULL; + size_t buflen = 0; + size_t datalen = sizeof(invalid_data); + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, invalid_data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); + assert_null(buf); +} + +static void torture_compression_decompress_alloc_valid(void **state) +{ + u8 *buf = NULL; + size_t buflen = 0; + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, valid_data, sizeof(valid_data), COMPRESSION_AUTO); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(buflen, 5); + assert_memory_equal(buf, "test\x0a", 5); + + rv = sc_decompress_alloc(&buf, &buflen, valid_zlib_data, sizeof(valid_zlib_data), COMPRESSION_AUTO); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(buflen, 5); + assert_memory_equal(buf, "test\x0a", 5); + free(buf); +} + +static void torture_compression_decompress_alloc_invalid_suffix(void **state) +{ + u8 *buf = NULL; + size_t buflen = 0; + int rv; + + rv = sc_decompress_alloc(&buf, &buflen, invalid_suffix_data, sizeof(invalid_suffix_data), COMPRESSION_AUTO); + assert_int_equal(rv, SC_SUCCESS); /* TODO Is this fine? */ + assert_int_equal(buflen, 5); + assert_memory_equal(buf, "test\x0a", 5); + + rv = sc_decompress_alloc(&buf, &buflen, invalid_zlib_suffix_data, sizeof(invalid_zlib_suffix_data), COMPRESSION_AUTO); + assert_int_equal(rv, SC_SUCCESS); /* TODO Is this fine? */ + assert_int_equal(buflen, 5); + assert_memory_equal(buf, "test\x0a", 5); + free(buf); +} + + + +/* Decompress without allocation */ +static void torture_compression_decompress_empty(void **state) +{ + u8 buf[1024]; + u8 *data = NULL; + size_t buflen = sizeof(buf); + size_t datalen = 0; + int rv; + + rv = sc_decompress(buf, &buflen, data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, sizeof(buf)); /* not touched */ +} + +static void torture_compression_decompress_gzip_empty(void **state) +{ + u8 buf[1024]; + u8 *data = NULL; + size_t buflen = sizeof(buf); + size_t datalen = 0; + int rv; + + rv = sc_decompress(buf, &buflen, data, datalen, COMPRESSION_GZIP); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, sizeof(buf)); /* not touched */ +} + +static void torture_compression_decompress_zlib_empty(void **state) +{ + u8 buf[1024]; + u8 *data = NULL; + size_t buflen = sizeof(buf); + size_t datalen = 0; + int rv; + + rv = sc_decompress(buf, &buflen, data, datalen, COMPRESSION_ZLIB); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, sizeof(buf)); /* not touched */ +} + +static void torture_compression_decompress_header(void **state) +{ + u8 buf[1024]; + u8 data[] = {0x1f, 0x8b}; + size_t buflen = sizeof(buf); + size_t datalen = sizeof(data); + int rv; + + rv = sc_decompress(buf, &buflen, data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); +} + +static void torture_compression_decompress_header_invalid(void **state) +{ + u8 buf[1024]; + u8 data[] = {0x1e, 0x8a}; + size_t buflen = sizeof(buf); + size_t datalen = sizeof(data); + int rv; + + rv = sc_decompress(buf, &buflen, data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); +} + +static void torture_compression_decompress_invalid(void **state) +{ + u8 buf[1024]; + size_t buflen = sizeof(buf); + size_t datalen = sizeof(invalid_data); + int rv; + + rv = sc_decompress(buf, &buflen, invalid_data, datalen, COMPRESSION_AUTO); + assert_int_equal(rv, SC_ERROR_UNKNOWN_DATA_RECEIVED); + assert_int_equal(buflen, 0); +} + +static void torture_compression_decompress_valid(void **state) +{ + u8 buf[1024]; + size_t buflen = sizeof(buf); + int rv; + + rv = sc_decompress(buf, &buflen, valid_data, sizeof(valid_data), COMPRESSION_AUTO); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(buflen, 5); + assert_memory_equal(buf, "test\x0a", 5); + + rv = sc_decompress(buf, &buflen, valid_zlib_data, sizeof(valid_zlib_data), COMPRESSION_AUTO); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(buflen, 5); + assert_memory_equal(buf, "test\x0a", 5); +} + +static void torture_compression_decompress_invalid_suffix(void **state) +{ + u8 buf[1024]; + size_t buflen = sizeof(buf); + int rv; + + rv = sc_decompress(buf, &buflen, invalid_suffix_data, sizeof(invalid_suffix_data), COMPRESSION_AUTO); + assert_int_equal(rv, SC_SUCCESS); /* TODO Is this fine? */ + assert_int_equal(buflen, 5); + assert_memory_equal(buf, "test\x0a", 5); + + rv = sc_decompress(buf, &buflen, invalid_zlib_suffix_data, sizeof(invalid_zlib_suffix_data), COMPRESSION_AUTO); + assert_int_equal(rv, SC_SUCCESS); /* TODO Is this fine? */ + assert_int_equal(buflen, 5); + assert_memory_equal(buf, "test\x0a", 5); +} + +static void torture_compression_decompress_zlib_good(void **state) +{ + u8 buf[1024]; + size_t buflen; + int rv; + + buflen = sizeof(buf); + rv = sc_decompress(buf, &buflen, zlib_good, sizeof zlib_good, COMPRESSION_AUTO); + assert_int_equal(rv, SC_SUCCESS); +} + + + +int main(void) +{ + int rc; + struct CMUnitTest tests[] = { + /* Decompress alloc */ + cmocka_unit_test(torture_compression_decompress_alloc_empty), + cmocka_unit_test(torture_compression_decompress_alloc_gzip_empty), + cmocka_unit_test(torture_compression_decompress_alloc_zlib_empty), + cmocka_unit_test(torture_compression_decompress_alloc_header), + cmocka_unit_test(torture_compression_decompress_alloc_header_invalid), + cmocka_unit_test(torture_compression_decompress_alloc_invalid), + cmocka_unit_test(torture_compression_decompress_alloc_invalid_suffix), + cmocka_unit_test(torture_compression_decompress_alloc_valid), + /* Decompress */ + cmocka_unit_test(torture_compression_decompress_empty), + cmocka_unit_test(torture_compression_decompress_gzip_empty), + cmocka_unit_test(torture_compression_decompress_zlib_empty), + cmocka_unit_test(torture_compression_decompress_header), + cmocka_unit_test(torture_compression_decompress_header_invalid), + cmocka_unit_test(torture_compression_decompress_invalid), + cmocka_unit_test(torture_compression_decompress_invalid_suffix), + cmocka_unit_test(torture_compression_decompress_valid), + cmocka_unit_test(torture_compression_decompress_zlib_good), + }; + + rc = cmocka_run_group_tests(tests, NULL, NULL); + return rc; +} diff --git a/src/tests/unittests/decode_ecdsa_signature.c b/src/tests/unittests/decode_ecdsa_signature.c new file mode 100644 index 0000000000..f20ef44e9d --- /dev/null +++ b/src/tests/unittests/decode_ecdsa_signature.c @@ -0,0 +1,234 @@ +/* + * decode_ecdsa_signature.c: Unit tests for decode ASN.1 ECDSA signature + * + * Copyright (C) 2022 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "torture.h" +#include "libopensc/log.c" +#include "libopensc/asn1.c" +#include + +static int setup(void **state) +{ + struct sc_context *ctx = NULL; + + sc_establish_context(&ctx, "test"); + *state = ctx; + return 0; +} + +static int teardown(void **state) +{ + struct sc_context *ctx = *state; + + sc_release_context(ctx); + + return 0; +} + +static void torture_empty_rs(void **state) +{ + int r = 0; + size_t fieldsize = 24; + struct sc_context *ctx = *state; + u8 *out = malloc(2); + char data[] = { 0x30, 0x04, 0x02, 0x00, 0x02, 0x00}; + + r = sc_asn1_decode_ecdsa_signature(ctx, (u8 *) data, 6, fieldsize, (u8 ** ) &out, 2); + free(out); + assert_int_equal(r, SC_ERROR_INVALID_DATA); +} + +static void torture_valid_format(void **state) +{ + int r = 0; + size_t fieldsize = 1; + struct sc_context *ctx = *state; + u8 *out = malloc(2); + u8 result[2] = { 0x03, 0x04}; + char data[] = { 0x30, 0x06, 0x02, 0x01, 0x03, 0x02, 0x01, 0x04}; + + if (!out) + return; + + r = sc_asn1_decode_ecdsa_signature(ctx, (u8 *) data, 8, fieldsize, (u8 **) &out, 2); + + assert_int_equal(r, 2 * fieldsize); + assert_memory_equal(result, out, 2); + free(out); +} + +static void torture_valid_format_leading00(void **state) +{ + int r = 0; + size_t fieldsize = 1; + struct sc_context *ctx = *state; + u8 *out = malloc(2); + u8 result[2] = { 0x03, 0x04}; + char data[] = { 0x30, 0x07, 0x02, 0x02, 0x00, 0x03, 0x02, 0x01, 0x04}; + + if (!out) + return; + + r = sc_asn1_decode_ecdsa_signature(ctx, (u8 *) data, 9, fieldsize, (u8 **) &out, 2); + + assert_int_equal(r, 2 * fieldsize); + assert_memory_equal(result, out, 2); + free(out); +} + +static void torture_valid_format_long_fieldsize(void **state) +{ + int r = 0; + size_t fieldsize = 3; + struct sc_context *ctx = *state; + u8 *out = malloc(6); + u8 result[6] = { 0x00, 0x00, 0x03, 0x00, 0x00, 0x04}; + char data[] = { 0x30, 0x06, 0x02, 0x01, 0x03, 0x02, 0x01, 0x04}; + + if (!out) + return; + + r = sc_asn1_decode_ecdsa_signature(ctx, (u8 *) data, 9, fieldsize, (u8 **) &out, 6); + + assert_int_equal(r, 2 * fieldsize); + assert_memory_equal(result, out, 6); + free(out); +} + +static void torture_wrong_tag_len(void **state) +{ + int r = 0; + size_t fieldsize = 1; + struct sc_context *ctx = *state; + u8 *out = malloc(2); + char data[] = { 0x30, 0x05, 0x02, 0x01, 0x03, 0x02, 0x01, 0x04}; + + if (!out) + return; + + r = sc_asn1_decode_ecdsa_signature(ctx, (u8 *) data, 8, fieldsize, (u8 **) &out, 2); + + assert_int_equal(r, SC_ERROR_INVALID_DATA); + free(out); +} + +static void torture_wrong_integer_tag_len(void **state) +{ + int r = 0; + size_t fieldsize = 1; + struct sc_context *ctx = *state; + u8 *out = malloc(2); + char data[] = { 0x30, 0x06, 0x02, 0x01, 0x03, 0x02, 0x02, 0x04}; + + if (!out) + return; + + r = sc_asn1_decode_ecdsa_signature(ctx, (u8 *) data, 8, fieldsize, (u8 **) &out, 2); + + assert_int_equal(r, SC_ERROR_INVALID_DATA); + free(out); +} + +static void torture_small_fieldsize(void **state) +{ + int r = 0; + size_t fieldsize = 1; + struct sc_context *ctx = *state; + u8 *out = malloc(3); + char data[] = { 0x30, 0x07, 0x02, 0x01, 0x03, 0x02, 0x02, 0x04, 0x05}; + + if (!out) + return; + + r = sc_asn1_decode_ecdsa_signature(ctx, (u8 *) data, 9, fieldsize, (u8 **) &out, 3); + + assert_int_equal(r, SC_ERROR_INVALID_DATA); + free(out); +} + +static void torture_long_leading00(void **state) +{ + int r = 0; + size_t fieldsize = 1; + struct sc_context *ctx = *state; + u8 *out = malloc(3); + char data[] = { 0x30, 0x07, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02, 0x01, 0x04}; + + if (!out) + return; + + r = sc_asn1_decode_ecdsa_signature(ctx, (u8 *) data, 10, fieldsize, (u8 **) &out, 3); + + assert_int_equal(r, SC_ERROR_INVALID_DATA); + free(out); +} + +static void torture_missing_tag(void **state) +{ + int r = 0; + size_t fieldsize = 1; + struct sc_context *ctx = *state; + u8 *out = malloc(2); + char data[] = { 0x20, 0x07, 0x02, 0x01, 0x03, 0x02, 0x02, 0x04, 0x05}; + + if (!out) + return; + + r = sc_asn1_decode_ecdsa_signature(ctx, (u8 *) data, 9, fieldsize, (u8 **) &out, 2); + + assert_int_equal(r, SC_ERROR_INVALID_DATA); + free(out); +} + + +static void torture_missing_integer_tag(void **state) +{ + int r = 0; + size_t fieldsize = 1; + struct sc_context *ctx = *state; + u8 *out = malloc(2); + char data[] = { 0x30, 0x07, 0x01, 0x01, 0x03, 0x02, 0x02, 0x04, 0x05}; + + if (!out) + return; + + r = sc_asn1_decode_ecdsa_signature(ctx, (u8 *) data, 9, fieldsize, (u8 **) &out, 2); + + assert_int_equal(r, SC_ERROR_INVALID_DATA); + free(out); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(torture_empty_rs, setup, teardown), + cmocka_unit_test_setup_teardown(torture_valid_format, setup, teardown), + cmocka_unit_test_setup_teardown(torture_valid_format_leading00, setup, teardown), + cmocka_unit_test_setup_teardown(torture_valid_format_long_fieldsize, setup, teardown), + cmocka_unit_test_setup_teardown(torture_wrong_tag_len, setup, teardown), + cmocka_unit_test_setup_teardown(torture_wrong_integer_tag_len, setup, teardown), + cmocka_unit_test_setup_teardown(torture_small_fieldsize, setup, teardown), + cmocka_unit_test_setup_teardown(torture_long_leading00, setup, teardown), + cmocka_unit_test_setup_teardown(torture_missing_tag, setup, teardown), + cmocka_unit_test_setup_teardown(torture_missing_integer_tag, setup, teardown), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} + diff --git a/src/tests/unittests/hextobin.c b/src/tests/unittests/hextobin.c new file mode 100644 index 0000000000..bd913e5c19 --- /dev/null +++ b/src/tests/unittests/hextobin.c @@ -0,0 +1,125 @@ +/* + * hextobin.c: Test suite for sc_hex_to_bin() + * + * Copyright (C) 2022 Peter Popovec + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include "libopensc/opensc.h" + +#define LEN 30 + +#define C_END -1 +#define C_ERROR -3 + +struct tst { + int result_len; + const char *input; + const char *output; +}; +int main() +{ + struct tst *t; + struct tst test[] = { + {1, "0", "\x00"}, + {1, " 0", "\x00"}, + {1, "00", "\x00"}, + {1, ":00", "\x00"}, + {1, ":0", "\x00"}, + {1, "d", "\x0d"}, + {1, ":a", "\x0a"}, + {1, "01", "\x01"}, + {1, " 09", "\x09"}, + {1, ":0a", "\x0a"}, + {1, " :0b :", "\x0b"}, + {1, "10", "\x10"}, + {1, " 90", "\x90"}, + {1, ":a0", "\xa0"}, + {1, " :B0 :", "\xb0"}, + {1, " 11:", "\x11"}, + {1, " :11:", "\x11"}, + {1, ":a1", "\xa1"}, + {2, "01:10", "\x01\x10"}, + {2, "10:10", "\x10\x10"}, + {2, " 12ab", "\x12\xab"}, + {3, "10:20:30", "\x10\x20\x30"}, + {3, "1020:30", "\x10\x20\x30"}, + {3, "1020: :30", "\x10\x20\x30"}, + {3, "102030", "\x10\x20\x30"}, + {3, ":102030", "\x10\x20\x30"}, + {3, ":102030:", "\x10\x20\x30"}, + {3, ":102030::", "\x10\x20\x30"}, + {3, "b2:11 :22", "\xb2\x11\x22"}, + {3, "b2 11 22", "\xb2\x11\x22"}, + {9, "10:203040:5060708090", "\x10\x20\x30\x40\x50\x60\x70\x80\x90"}, + {0, "::::", ""}, + {0, ":", ""}, + {0, " ", ""}, + {0, "", ""}, + {C_ERROR, " :0 :", ""}, + {C_ERROR, " :b :", ""}, + {C_ERROR, " :c ", ""}, + {C_ERROR, "1:10", ""}, + {C_ERROR, " :b:2 :", ""}, + {C_ERROR, " ::1:2:a:b", ""}, + {C_ERROR, "1:1", ""}, + {C_ERROR, " :1 1:", ""}, + {C_ERROR, "0:0 :", ""}, + {C_ERROR, "1:234:56", ""}, /* odd number of characters between delimiters (234) */ + {C_ERROR, " :b:211 :", ""}, + {C_ERROR, "02030", ""}, /* one char missing (to have full byte) */ + {C_ERROR, "111", ""}, + {C_ERROR, "b:211 :2", ""}, + {C_ERROR, "G", ""}, + {C_ERROR, " z", ""}, + {C_ERROR, ":a1:1", ""}, + {C_END, "", ""} + }; + uint8_t res[LEN]; + size_t len; + int rv, r; + + for (t = test; t->result_len != C_END; t++) { + r = t->result_len; + len = LEN; + rv = sc_hex_to_bin(t->input, res, &len); + if (rv) { + if (r != C_ERROR) { + fprintf(stderr, "fail at string %s (return code %d, %d\n", t->input, + rv, r); + return 1; + } + } else { + if (r == C_ERROR) { + fprintf(stderr, "fail at string %s (return code %d, %d)\n", + t->input, rv, r); + return 2; + } + if ((int)len != r) { + fprintf(stderr, "fail at string %s (length %zu %d)\n", t->input, + len, r); + return 3; + } + if (memcmp(t->output, res, len)) { + fprintf(stderr, "fail at string %s (return value)\n", t->input); + return 4; + } + } + } + return 0; +} diff --git a/src/tests/unittests/openpgp-tool.c b/src/tests/unittests/openpgp-tool.c new file mode 100644 index 0000000000..9b944f7eee --- /dev/null +++ b/src/tests/unittests/openpgp-tool.c @@ -0,0 +1,168 @@ +/* + * openpgp-tool.c: Test various functions of openpgp-tool + * + * Copyright (C) 2021 Vincent Pelletier + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "torture.h" + +#include "tools/openpgp-tool-helpers.h" + +struct expectation { + const char *data; + size_t length; + const char *output; +}; + +static void torture_prettify(void **state, const struct expectation *cur, char *(prettify_func)(const u8 *data, size_t length)) +{ + char *output; + + while (cur->data != NULL) { + output = prettify_func((u8 *) cur->data, cur->length); + if (cur->output == NULL) + assert_null(output); + else { + assert_non_null(output); + assert_string_equal(output, cur->output); + } + cur++; + } +} + +const struct expectation expectations_algorithm[] = { + { "", 0, NULL }, + { "\x12\x2b\x06\x01\x04\x01\x97\x55\x01\x05\x01", 11, "ECDH" }, + { "\x01\x08\x00\x00\x20\x00", 6, "RSA2048" }, + { NULL, 0, NULL } +}; + +static void torture_prettify_algorithm(void **state) +{ + torture_prettify(state, expectations_algorithm, prettify_algorithm); +} + +const struct expectation expectations_date[] = { + { "\x01\x02\x03", 3, NULL }, + { "\x12\x34\x56\x78", 4, "1979-09-05 22:51:36" }, + { "\x7f\xff\xff\xff", 4, "2038-01-19 03:14:07" }, + /* XXX: probably not a feature */ + { "\x80\x00\x00\x00", 4, "1901-12-13 20:45:52" }, + { NULL, 0, NULL } +}; + +static void torture_prettify_date(void **state) +{ + torture_prettify(state, expectations_date, prettify_date); +} + +const struct expectation expectations_version[] = { + { "\x01", 1, NULL }, + { "\x03\x41", 2, "3.41" }, + { NULL, 0, NULL } +}; + +static void torture_prettify_version(void **state) +{ + torture_prettify(state, expectations_version, prettify_version); +} + +const struct expectation expectations_manufacturer[] = { + { "\x01", 1, NULL }, + { "\xf5\x17", 2, "FSIJ" }, + { "\xff\x00", 2, "unmanaged S/N range" }, + { "\xff\x7f", 2, "unmanaged S/N range" }, + { "\xff\xfe", 2, "unmanaged S/N range" }, + { "\xff\xff", 2, "test card" }, + /* Number picked by a fair dice roll among unregistered numbers */ + { "\x81\x88", 2, "unknown" }, + { NULL, 0, NULL } +}; + +static void torture_prettify_manufacturer(void **state) +{ + torture_prettify(state, expectations_manufacturer, prettify_manufacturer); +} + +const struct expectation expectations_serialnumber[] = { + { "\x00\x00\x00", 3, NULL }, + { "\x12\x34\x56\x78", 4, "12345678" }, + { "\x80\x00\x00\x00", 4, "80000000" }, + { NULL, 0, NULL } +}; + +static void torture_prettify_serialnumber(void **state) +{ + torture_prettify(state, expectations_serialnumber, prettify_serialnumber); +} + +const struct expectation expectations_name[] = { + { "", 0, NULL }, + { "John Doe", 8, "John Doe" }, + { "John + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "tools/pkcs11_uri.c" +#include "torture.h" +#include + +static void +torture_id_non_percent(void **state) +{ + char *uri = "pkcs11:id=123456;"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 0); + assert_string_equal("123456", result->id); + assert_int_equal(strlen("123456"), result->id_len); + pkcs11_uri_free(result); +} + +static void +torture_id_percent(void **state) +{ + char *uri = "pkcs11:id=%30%31%32"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 0); + assert_string_equal("012", result->id); + assert_int_equal(3, result->id_len); + pkcs11_uri_free(result); +} + +static void +torture_id_percent_incorrect(void **state) +{ + char *uri = "pkcs11:id=%30%31%3"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 1); + assert_ptr_equal(NULL, result->id); + assert_int_equal(0, result->id_len); + pkcs11_uri_free(result); +} + +static void +torture_manufacturer(void **state) +{ + char *uri = "pkcs11:manufacturer=Snake%20Oil,%20Inc."; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 0); + assert_string_equal("Snake Oil, Inc.", result->token_manufacturer); + pkcs11_uri_free(result); +} + +static void +torture_object(void **state) +{ + char *uri = "pkcs11:object=my-certificate;"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 0); + assert_string_equal("my-certificate", result->object); + pkcs11_uri_free(result); +} + +static void +torture_empty_serial(void **state) +{ + char *uri = "pkcs11:serial=;"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 0); + assert_string_equal("", result->serial); + pkcs11_uri_free(result); +} + +static void +torture_serial(void **state) +{ + char *uri = "pkcs11:serial=123456;"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 0); + assert_string_equal("123456", result->serial); + pkcs11_uri_free(result); +} + +static void +torture_invalid_token(void **state) +{ + char *uri = "pkcs11:invalid=123456"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 1); + pkcs11_uri_free(result); +} + +static void +torture_half_invalid_token(void **state) +{ + char *uri = "pkcs11:serial-number=123456;"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 1); + pkcs11_uri_free(result); +} + +static void +torture_spec_only_scheme(void **state) +{ + char *uri = "pkcs11:"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 0); + pkcs11_uri_free(result); +} + +static void +torture_spec_object_label(void **state) +{ + char *uri = "pkcs11:object=my-pubkey;type=public"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 0); + assert_string_equal("my-pubkey", result->object); + assert_string_equal("public", result->type); + pkcs11_uri_free(result); +} + +static void +torture_spec_type_pin(void **state) +{ + char *uri = "pkcs11:object=my-key;type=private?pin-source=file:/etc/token"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 0); + assert_string_equal("my-key", result->object); + assert_string_equal("private", result->type); + assert_string_equal("file:/etc/token", result->pin_source); + pkcs11_uri_free(result); +} + +static void +torture_spec_token_object_serial_pin(void **state) +{ + char *uri = "pkcs11:token=The%20Software%20PKCS%2311%20Softtoken;" + "manufacturer=Snake%20Oil,%20Inc.;" + "model=1.0;" + "object=my-certificate;" + "type=cert;" + "id=%69%95%3E%5C%F4%BD%EC%91;" + "serial=" + "?pin-source=file:/etc/token_pin"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 0); + assert_string_equal("The Software PKCS#11 Softtoken", result->token_label); + assert_string_equal("Snake Oil, Inc.", result->token_manufacturer); + assert_string_equal("my-certificate", result->object); + assert_string_equal("cert", result->type); + assert_string_equal("\x69\x95\x3E\x5C\xF4\xBD\xEC\x91", result->id); + assert_int_equal(8, result->id_len); + assert_string_equal("", result->serial); + assert_string_equal("file:/etc/token_pin", result->pin_source); + pkcs11_uri_free(result); +} + +static void +torture_spec_unsupported_module_name(void **state) +{ + char *uri = "pkcs11:object=my-sign-key;" + "type=private" + "?module-name=mypkcs11"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 1); + pkcs11_uri_free(result); +} + +static void +torture_module_path(void **state) +{ + char *uri = "pkcs11:object=my-sign-key;" + "type=private" + "?module-path=/mnt/libmypkcs11.so.1"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 0); + assert_string_equal("my-sign-key", result->object); + assert_string_equal("private", result->type); + assert_string_equal("/mnt/libmypkcs11.so.1", result->module_path); + pkcs11_uri_free(result); +} + +static void +torture_pin_value(void **state) +{ + char *uri = "pkcs11:token=Software%20PKCS%2311%20softtoken;" + "manufacturer=Snake%20Oil,%20Inc." + "?pin-value=the-pin"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 0); + assert_string_equal("Software PKCS#11 softtoken", result->token_label); + assert_string_equal("Snake Oil, Inc.", result->token_manufacturer); + assert_string_equal("the-pin", result->pin_value); + pkcs11_uri_free(result); +} + +static void +torture_encoded_semicolon(void **state) +{ + char *uri = "pkcs11:token=A%20name%20with%20a%20substring%20%25%3B;" + "object=my-certificate;" + "type=cert"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 0); + assert_string_equal("A name with a substring %;", result->token_label); + assert_string_equal("my-certificate", result->object); + assert_string_equal("cert", result->type); + pkcs11_uri_free(result); +} + +static void +torture_pin_pin_source(void **state) +{ + char *uri = "pkcs11:?pin-value=the-pin;pin-source=file:/path/to/pin"; + struct pkcs11_uri *result = pkcs11_uri_new(); + int rv = parse_pkcs11_uri(uri, result); + assert_int_equal(rv, 1); + pkcs11_uri_free(result); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(torture_id_non_percent), + cmocka_unit_test(torture_id_percent), + cmocka_unit_test(torture_id_percent_incorrect), + cmocka_unit_test(torture_manufacturer), + cmocka_unit_test(torture_invalid_token), + cmocka_unit_test(torture_half_invalid_token), + cmocka_unit_test(torture_object), + cmocka_unit_test(torture_empty_serial), + cmocka_unit_test(torture_serial), + cmocka_unit_test(torture_spec_only_scheme), + cmocka_unit_test(torture_spec_object_label), + cmocka_unit_test(torture_spec_type_pin), + cmocka_unit_test(torture_spec_token_object_serial_pin), + cmocka_unit_test(torture_spec_unsupported_module_name), + cmocka_unit_test(torture_module_path), + cmocka_unit_test(torture_pin_value), + cmocka_unit_test(torture_encoded_semicolon), + cmocka_unit_test(torture_pin_pin_source)}; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/unittests/pkcs15-emulator-filter.c b/src/tests/unittests/pkcs15-emulator-filter.c new file mode 100644 index 0000000000..9960fc5815 --- /dev/null +++ b/src/tests/unittests/pkcs15-emulator-filter.c @@ -0,0 +1,494 @@ +/* + * pkcs15-emulator-filter.c: Unit tests for PKCS15 emulator filter + * + * Copyright (C) 2021 Red Hat, Inc. + * + * Author: Veronika Hanulikova + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "torture.h" +#include "libopensc/pkcs15-emulator-filter.c" + +int func(sc_pkcs15_card_t *card, struct sc_aid *aid) { + (void) card; + (void) aid; + return SC_SUCCESS; +} + +// clang-format off +struct sc_pkcs15_emulator_handler builtin[] = { + { "openpgp", &func }, + { "starcert", &func }, + { NULL, NULL } +}; +struct sc_pkcs15_emulator_handler old[] = { + { "cardos", &func }, + { "jcop", &func }, + { NULL, NULL } +}; +// clang-format on + +/* add_emul */ +static void torture_null_add_emul(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators = { { &builtin[0] }, 1 }; + int rv; + + rv = add_emul(NULL, &builtin[0]); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); + rv = add_emul(&filtered_emulators, NULL); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); +} + +static void torture_name_add_emul(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int rv; + filtered_emulators.ccount = 0; + + rv = add_emul(&filtered_emulators, &builtin[0]); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(filtered_emulators.ccount, 1); +} + +static void torture_name_already_in_add_emul(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators = { { &builtin[0] }, 1 }; + int rv; + + rv = add_emul(&filtered_emulators, &builtin[0]); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(filtered_emulators.ccount, 1); +} + +static void torture_full_add_emul(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + filtered_emulators.ccount = SC_MAX_PKCS15_EMULATORS; + for (i = 0; i < SC_MAX_PKCS15_EMULATORS; i++) { + filtered_emulators.list_of_handlers[i] = &builtin[0]; + } + + rv = add_emul(&filtered_emulators, &old[0]); + assert_int_equal(rv, SC_ERROR_TOO_MANY_OBJECTS); + assert_int_equal(filtered_emulators.ccount, SC_MAX_PKCS15_EMULATORS); + assert_ptr_equal(filtered_emulators.list_of_handlers[SC_MAX_PKCS15_EMULATORS - 1], &builtin[0]); +} + +static void torture_overfilled_add_emul(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + filtered_emulators.ccount = SC_MAX_PKCS15_EMULATORS + 1; + for (i = 0; i < SC_MAX_PKCS15_EMULATORS + 1; i++) { + filtered_emulators.list_of_handlers[i] = &builtin[0]; + } + + rv = add_emul(&filtered_emulators, &old[0]); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); + assert_int_equal(filtered_emulators.ccount, SC_MAX_PKCS15_EMULATORS + 1); +} + +static void torture_invalid_handler_name_add_emul(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + struct sc_pkcs15_emulator_handler handler = { NULL, &func }; + int rv; + filtered_emulators.ccount = 0; + + rv = add_emul(&filtered_emulators, &handler); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); +} + +static void torture_invalid_handler_func_add_emul(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + struct sc_pkcs15_emulator_handler handler = { "name", NULL }; + int rv; + filtered_emulators.ccount = 0; + + rv = add_emul(&filtered_emulators, &handler); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); +} + +static void torture_invalid_emulator_list_add_emul(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators = { { NULL }, 1 }; + struct sc_pkcs15_emulator_handler handler = { "name", &func }; + int rv; + + rv = add_emul(&filtered_emulators, &handler); + assert_int_equal(rv, SC_ERROR_OBJECT_NOT_VALID); +} + +/* add_emul_list */ +static void torture_null_add_emul_list(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators = { { &builtin[0] }, 1 }; + int rv; + + rv = add_emul_list(NULL, builtin); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); + rv = add_emul_list(&filtered_emulators, NULL); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); +} + +static void torture_internal_add_emul_list(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + filtered_emulators.ccount = 0; + + rv = add_emul_list(&filtered_emulators, builtin); + assert_int_equal(rv, SC_SUCCESS); + for (i = 0; builtin[i].name; i++) { + assert_ptr_equal(&builtin[i], filtered_emulators.list_of_handlers[i]); + } + assert_int_equal(filtered_emulators.ccount, i); +} + +static void torture_internal_already_name_add_emul_list(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators = { { &builtin[0] }, 1 }; + int i, rv; + + rv = add_emul_list(&filtered_emulators, builtin); + assert_int_equal(rv, SC_SUCCESS); + for (i = 0; builtin[i].name; i++) { + assert_ptr_equal(&builtin[i], filtered_emulators.list_of_handlers[i]); + } + assert_int_equal(filtered_emulators.ccount, i); +} + +static void torture_internal_already_name2_add_emul_list(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators = { { &old[0] }, 1 }; + int i, rv; + + rv = add_emul_list(&filtered_emulators, builtin); + assert_int_equal(rv, SC_SUCCESS); + for (i = 0; builtin[i].name; i++) { + assert_ptr_equal(&builtin[i], filtered_emulators.list_of_handlers[i + 1]); + } + assert_int_equal(filtered_emulators.ccount, i + 1); +} + +static void torture_full_add_emul_list(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + filtered_emulators.ccount = SC_MAX_PKCS15_EMULATORS; + for (i = 0; i < SC_MAX_PKCS15_EMULATORS; i++) { + filtered_emulators.list_of_handlers[i] = &builtin[1]; + } + + rv = add_emul_list(&filtered_emulators, builtin); + assert_int_equal(rv, SC_ERROR_TOO_MANY_OBJECTS); + assert_int_equal(filtered_emulators.ccount, SC_MAX_PKCS15_EMULATORS); +} + +static void torture_one_to_full_add_emul_list(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + filtered_emulators.ccount = SC_MAX_PKCS15_EMULATORS - 1; + for (i = 0; i < SC_MAX_PKCS15_EMULATORS - 1; i++) { + filtered_emulators.list_of_handlers[i] = &builtin[0]; + } + + rv = add_emul_list(&filtered_emulators, old); + assert_int_equal(rv, SC_ERROR_TOO_MANY_OBJECTS); + assert_ptr_equal(filtered_emulators.list_of_handlers[SC_MAX_PKCS15_EMULATORS - 1], &old[0]); + assert_int_equal(filtered_emulators.ccount, SC_MAX_PKCS15_EMULATORS); +} + +static void torture_overfilled_add_emul_list(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + filtered_emulators.ccount = SC_MAX_PKCS15_EMULATORS + 1; + for (i = 0; i < SC_MAX_PKCS15_EMULATORS + 1; i++) { + filtered_emulators.list_of_handlers[i] = &builtin[0]; + } + + rv = add_emul_list(&filtered_emulators, old); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); + assert_ptr_equal(filtered_emulators.list_of_handlers[SC_MAX_PKCS15_EMULATORS - 1], &builtin[0]); + assert_int_equal(filtered_emulators.ccount, SC_MAX_PKCS15_EMULATORS + 1); +} + +/* set_emulators */ +static void torture_non_existing(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int rv; + scconf_list list = { NULL, "non" }; + filtered_emulators.ccount = 0; + + rv = set_emulators(NULL, &filtered_emulators, &list, builtin, old); + assert_int_equal(rv, SC_SUCCESS); + assert_null(filtered_emulators.list_of_handlers[0]); +} + +static void torture_internal_only(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + scconf_list list = { NULL, "internal" }; + filtered_emulators.ccount = 0; + + rv = set_emulators(NULL, &filtered_emulators, &list, builtin, old); + assert_int_equal(rv, SC_SUCCESS); + for (i = 0; builtin[i].name; i++) { + assert_ptr_equal(&builtin[i], filtered_emulators.list_of_handlers[i]); + } + assert_int_equal(filtered_emulators.ccount, 2); + assert_null(filtered_emulators.list_of_handlers[2]); + assert_null(builtin[i].name); +} + +static void torture_old_only(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + scconf_list list = { NULL, "old" }; + filtered_emulators.ccount = 0; + + rv = set_emulators(NULL, &filtered_emulators, &list, builtin, old); + assert_int_equal(rv, SC_SUCCESS); + for (i = 0; old[i].name; i++) { + assert_ptr_equal(&old[i], filtered_emulators.list_of_handlers[i]); + } + assert_null(filtered_emulators.list_of_handlers[i]); + assert_null(old[i].name); +} + +static void torture_internal_name(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int rv; + scconf_list list = { NULL, strdup(builtin[0].name) }; + filtered_emulators.ccount = 0; + + rv = set_emulators(NULL, &filtered_emulators, &list, builtin, old); + assert_int_equal(rv, SC_SUCCESS); + assert_ptr_equal(&builtin[0], filtered_emulators.list_of_handlers[0]); + assert_null(filtered_emulators.list_of_handlers[1]); + free(list.data); +} + +static void torture_old_name(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int rv; + scconf_list list = { NULL, strdup(old[0].name) }; + filtered_emulators.ccount = 0; + + rv = set_emulators(NULL, &filtered_emulators, &list, builtin, old); + assert_int_equal(rv, SC_SUCCESS); + assert_ptr_equal(&old[0], filtered_emulators.list_of_handlers[0]); + assert_null(filtered_emulators.list_of_handlers[1]); + free(list.data); +} + +static void torture_internal_and_name(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + scconf_list list2 = { NULL, "cardos" }; + scconf_list list1 = { &list2, "internal" }; + filtered_emulators.ccount = 0; + + rv = set_emulators(NULL, &filtered_emulators, &list1, builtin, old); + assert_int_equal(rv, SC_SUCCESS); + for (i = 0; builtin[i].name; i++) { + assert_ptr_equal(&builtin[i], filtered_emulators.list_of_handlers[i]); + } + assert_ptr_equal(&old[0], filtered_emulators.list_of_handlers[i]); + assert_null(filtered_emulators.list_of_handlers[i + 1]); +} + +static void torture_name_and_internal(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int rv; + scconf_list list2 = { NULL, "internal" }; + scconf_list list1 = { &list2, "starcert" }; + filtered_emulators.ccount = 0; + + rv = set_emulators(NULL, &filtered_emulators, &list1, builtin, old); + assert_int_equal(rv, SC_SUCCESS); + assert_ptr_equal(&builtin[1], filtered_emulators.list_of_handlers[0]); + assert_ptr_equal(&builtin[0], filtered_emulators.list_of_handlers[1]); + assert_null(filtered_emulators.list_of_handlers[2]); +} + +static void torture_internal_and_nonexisting(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + scconf_list list2 = { NULL, "non" }; + scconf_list list1 = { &list2, "internal" }; + filtered_emulators.ccount = 0; + + rv = set_emulators(NULL, &filtered_emulators, &list1, builtin, old); + assert_int_equal(rv, SC_SUCCESS); + for (i = 0; builtin[i].name; i++) { + assert_ptr_equal(&builtin[i], filtered_emulators.list_of_handlers[i]); + } + assert_null(filtered_emulators.list_of_handlers[i]); + assert_null(builtin[i].name); +} + +static void torture_nonexisting_and_internal(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + scconf_list list2 = { NULL, "internal" }; + scconf_list list1 = { &list2, "non" }; + filtered_emulators.ccount = 0; + + rv = set_emulators(NULL, &filtered_emulators, &list1, builtin, old); + assert_int_equal(rv, SC_SUCCESS); + for (i = 0; builtin[i].name; i++) { + assert_ptr_equal(&builtin[i], filtered_emulators.list_of_handlers[i]); + } + assert_null(filtered_emulators.list_of_handlers[i]); + assert_null(builtin[i].name); +} + +static void torture_null_set_emul(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators = { { &builtin[0] }, 1 }; + int rv; + scconf_list list1 = { NULL, "internal" }; + + rv = set_emulators(NULL, NULL, &list1, builtin, old); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); + rv = set_emulators(NULL, &filtered_emulators, NULL, builtin, old); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); + rv = set_emulators(NULL, &filtered_emulators, &list1, NULL, old); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); + rv = set_emulators(NULL, &filtered_emulators, &list1, builtin, NULL); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); +} + +static void torture_full_set_emul(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + filtered_emulators.ccount = SC_MAX_PKCS15_EMULATORS; + scconf_list list1 = { NULL, "old" }; + for (i = 0; i < SC_MAX_PKCS15_EMULATORS; i++) { + filtered_emulators.list_of_handlers[i] = &builtin[0]; + } + + rv = set_emulators(NULL, &filtered_emulators, &list1, builtin, old); + assert_int_equal(rv, SC_ERROR_TOO_MANY_OBJECTS); + assert_non_null(filtered_emulators.list_of_handlers[SC_MAX_PKCS15_EMULATORS - 1]); + assert_null(filtered_emulators.list_of_handlers[SC_MAX_PKCS15_EMULATORS]); + assert_int_equal(filtered_emulators.ccount, SC_MAX_PKCS15_EMULATORS); +} + +static void torture_one_to_full_set_emul(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + filtered_emulators.ccount = SC_MAX_PKCS15_EMULATORS - 1; + scconf_list list1 = { NULL, "old" }; + for (i = 0; i < SC_MAX_PKCS15_EMULATORS - 1; i++) { + filtered_emulators.list_of_handlers[i] = &builtin[0]; + } + + rv = set_emulators(NULL, &filtered_emulators, &list1, builtin, old); + assert_int_equal(rv, SC_ERROR_TOO_MANY_OBJECTS); + assert_ptr_equal(filtered_emulators.list_of_handlers[SC_MAX_PKCS15_EMULATORS - 1], &old[0]); + assert_null(filtered_emulators.list_of_handlers[SC_MAX_PKCS15_EMULATORS]); + assert_int_equal(filtered_emulators.ccount, SC_MAX_PKCS15_EMULATORS); +} + +static void torture_one_to_full2_set_emul(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + filtered_emulators.ccount = SC_MAX_PKCS15_EMULATORS - 1; + scconf_list list1 = { NULL, strdup(old[1].name) }; + for (i = 0; i < SC_MAX_PKCS15_EMULATORS - 1; i++) { + filtered_emulators.list_of_handlers[i] = &builtin[0]; + } + + rv = set_emulators(NULL, &filtered_emulators, &list1, builtin, old); + assert_int_equal(rv, SC_SUCCESS); + assert_ptr_equal(filtered_emulators.list_of_handlers[SC_MAX_PKCS15_EMULATORS - 1], &old[1]); + assert_null(filtered_emulators.list_of_handlers[SC_MAX_PKCS15_EMULATORS]); + assert_int_equal(filtered_emulators.ccount, SC_MAX_PKCS15_EMULATORS); + free(list1.data); +} + +static void torture_overfilled_set_emul(void **state) +{ + struct _sc_pkcs15_emulators filtered_emulators; + int i, rv; + filtered_emulators.ccount = SC_MAX_PKCS15_EMULATORS + 1; + scconf_list list1 = { NULL, strdup(old[1].name) }; + for (i = 0; i < SC_MAX_PKCS15_EMULATORS + 1; i++) { + filtered_emulators.list_of_handlers[i] = &builtin[0]; + } + + rv = set_emulators(NULL, &filtered_emulators, &list1, builtin, old); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); + free(list1.data); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + /* add_emul */ + cmocka_unit_test(torture_null_add_emul), + cmocka_unit_test(torture_name_add_emul), + cmocka_unit_test(torture_name_already_in_add_emul), + cmocka_unit_test(torture_full_add_emul), + cmocka_unit_test(torture_overfilled_add_emul), + cmocka_unit_test(torture_invalid_handler_name_add_emul), + cmocka_unit_test(torture_invalid_handler_func_add_emul), + cmocka_unit_test(torture_invalid_emulator_list_add_emul), + /* add_emul_list */ + cmocka_unit_test(torture_null_add_emul_list), + cmocka_unit_test(torture_internal_add_emul_list), + cmocka_unit_test(torture_internal_already_name_add_emul_list), + cmocka_unit_test(torture_internal_already_name2_add_emul_list), + cmocka_unit_test(torture_full_add_emul_list), + cmocka_unit_test(torture_one_to_full_add_emul_list), + cmocka_unit_test(torture_overfilled_add_emul_list), + /* set_emulators */ + cmocka_unit_test(torture_non_existing), + cmocka_unit_test(torture_internal_only), + cmocka_unit_test(torture_old_only), + cmocka_unit_test(torture_internal_name), + cmocka_unit_test(torture_old_name), + cmocka_unit_test(torture_internal_and_name), + cmocka_unit_test(torture_name_and_internal), + cmocka_unit_test(torture_internal_and_nonexisting), + cmocka_unit_test(torture_nonexisting_and_internal), + cmocka_unit_test(torture_null_set_emul), + cmocka_unit_test(torture_full_set_emul), + cmocka_unit_test(torture_one_to_full_set_emul), + cmocka_unit_test(torture_one_to_full2_set_emul), + cmocka_unit_test(torture_overfilled_set_emul), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/unittests/simpletlv.c b/src/tests/unittests/simpletlv.c new file mode 100644 index 0000000000..127e0dfd44 --- /dev/null +++ b/src/tests/unittests/simpletlv.c @@ -0,0 +1,151 @@ +/* + * asn1.c: Unit tests for SimpleTLV parser and encoder + * + * Copyright (C) 2019 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "torture.h" +#include "libopensc/sc.c" +#include "libopensc/simpletlv.c" + +static void torture_simpletlv_read_tag_null(void **state) +{ + const u8 *data = NULL; + size_t datalen = 0; + u8 tag = 0; + size_t taglen = 0; + int rv; + + rv = sc_simpletlv_read_tag(&data, datalen, &tag, &taglen); + assert_int_equal(rv, SC_ERROR_INVALID_TLV_OBJECT); + assert_int_equal(tag, 0); + assert_int_equal(taglen, 0); +} + +#define TORTURE_READ_TAG(name, data, tag_value, len_value, error) \ + static void torture_simpletlv_read_tag_## name (void **state) \ + { \ + u8 buf[] = data; \ + const u8 *bufptr = buf; \ + size_t buflen = sizeof(buf) - 1; \ + u8 tag = 0; \ + size_t taglen = 0; \ + int rv; \ + \ + rv = sc_simpletlv_read_tag(&bufptr, buflen, &tag, &taglen); \ + assert_int_equal(rv, error); \ + assert_int_equal(tag, tag_value); \ + assert_int_equal(taglen, len_value); \ + } +#define TORTURE_READ_TAG_SUCCESS(name, data, tag_value, len_value) \ + TORTURE_READ_TAG(name, data, tag_value, len_value, SC_SUCCESS) +#define TORTURE_READ_TAG_ERROR(name, data, error) \ + TORTURE_READ_TAG(name, data, 0, 0, error) + +TORTURE_READ_TAG_ERROR(short, "\x42", SC_ERROR_INVALID_TLV_OBJECT) +TORTURE_READ_TAG_SUCCESS(minimal, + "\x42\x00", 0x42, 0) +TORTURE_READ_TAG_SUCCESS(minimal2, + "\x42\x00", 0x42, 0) +TORTURE_READ_TAG_SUCCESS(valid_short, + "\x42\x02\x01\x02", 0x42, 2) +TORTURE_READ_TAG_ERROR(incomplete_length, + "\x42\xff", SC_ERROR_INVALID_TLV_OBJECT) +TORTURE_READ_TAG_SUCCESS(long_length, + "\x42\xff\x00\x00", 0x42, 0) +TORTURE_READ_TAG(missing_data, + "\x42\xff\x02\x00", 0x42, 2, SC_ERROR_TLV_END_OF_CONTENTS) +TORTURE_READ_TAG_SUCCESS(valid_long, + "\x42\xff\x02\x00\x01\x02", 0x42, 2) + +static void torture_simpletlv_put_tag_null(void **state) +{ + u8 *data = NULL; + size_t datalen = 0; + u8 *outptr = NULL; + int rv; + + rv = sc_simpletlv_put_tag(0x42, 2, data, datalen, &outptr); + assert_int_equal(rv, SC_ERROR_INVALID_ARGUMENTS); + assert_null(outptr); +} + +#define TORTURE_PUT_TAG(name, tag, taglen, data_len, exp_data, error) \ + static void torture_simpletlv_put_tag_## name(void **state) \ + { \ + u8 data[data_len] = {}; \ + size_t datalen = sizeof(data); \ + u8 *outptr = NULL; \ + int rv; \ + \ + rv = sc_simpletlv_put_tag(tag, taglen, data, datalen, &outptr); \ + assert_int_equal(rv, error); \ + assert_memory_equal(data, exp_data, MIN(sizeof(data), sizeof(exp_data))); \ + if (rv == SC_SUCCESS) { \ + assert_non_null(outptr); \ + } else { \ + assert_null(outptr); \ + } \ + } +#define TORTURE_PUT_TAG_ERROR(name, tag, taglen, data_len, error) \ + TORTURE_PUT_TAG(name, tag, taglen, data_len, "", error) +#define TORTURE_PUT_TAG_SUCCESS(name, tag, taglen, data_len, exp_data) \ + TORTURE_PUT_TAG(name, tag, taglen, data_len, exp_data, SC_SUCCESS) + +TORTURE_PUT_TAG_ERROR(too_small, 0x42, 2, 1, SC_ERROR_INVALID_ARGUMENTS) +TORTURE_PUT_TAG_SUCCESS(valid_short, 0x42, 2, 2, "\x42\x02") +TORTURE_PUT_TAG_ERROR(invalid_tag, 0x00, 2, 2, SC_ERROR_INVALID_ARGUMENTS) +TORTURE_PUT_TAG_ERROR(invalid_tag2, 0xff, 2, 2, SC_ERROR_INVALID_ARGUMENTS) +TORTURE_PUT_TAG_SUCCESS(max_short, 0x42, 0xfe, 2, "\x42\xfe") +TORTURE_PUT_TAG_ERROR(too_long_length, 0x42, 512, 3, SC_ERROR_INVALID_ARGUMENTS) +TORTURE_PUT_TAG_SUCCESS(valid_long, 0x42, 512, 4, "\x42\xff\x00\x02") +TORTURE_PUT_TAG_SUCCESS(first_long, 0x42, 0xff, 4, "\x42\xff\xff\x00") +TORTURE_PUT_TAG_SUCCESS(last_long, 0x42, 0xffff, 4, "\x42\xff\xff\xff") +TORTURE_PUT_TAG_ERROR(too_large_length, 0x42, 0x10000, 4, SC_ERROR_WRONG_LENGTH) + +int main(void) +{ + int rc; + struct CMUnitTest tests[] = { + /* simpletlv_read_tag() */ + cmocka_unit_test(torture_simpletlv_read_tag_null), + cmocka_unit_test(torture_simpletlv_read_tag_short), + cmocka_unit_test(torture_simpletlv_read_tag_minimal), + cmocka_unit_test(torture_simpletlv_read_tag_minimal2), + cmocka_unit_test(torture_simpletlv_read_tag_valid_short), + cmocka_unit_test(torture_simpletlv_read_tag_incomplete_length), + cmocka_unit_test(torture_simpletlv_read_tag_long_length), + cmocka_unit_test(torture_simpletlv_read_tag_missing_data), + cmocka_unit_test(torture_simpletlv_read_tag_valid_long), + /* simpletlv_put_tag() */ + cmocka_unit_test(torture_simpletlv_put_tag_null), + cmocka_unit_test(torture_simpletlv_put_tag_too_small), + cmocka_unit_test(torture_simpletlv_put_tag_valid_short), + cmocka_unit_test(torture_simpletlv_put_tag_invalid_tag), + cmocka_unit_test(torture_simpletlv_put_tag_invalid_tag2), + cmocka_unit_test(torture_simpletlv_put_tag_max_short), + cmocka_unit_test(torture_simpletlv_put_tag_too_long_length), + cmocka_unit_test(torture_simpletlv_put_tag_valid_long), + cmocka_unit_test(torture_simpletlv_put_tag_first_long), + cmocka_unit_test(torture_simpletlv_put_tag_last_long), + cmocka_unit_test(torture_simpletlv_put_tag_too_large_length), + }; + + rc = cmocka_run_group_tests(tests, NULL, NULL); + return rc; +} diff --git a/src/tests/unittests/sm.c b/src/tests/unittests/sm.c new file mode 100644 index 0000000000..a21661e6b5 --- /dev/null +++ b/src/tests/unittests/sm.c @@ -0,0 +1,376 @@ +/* + * sm.c: Unit tests for Secure Messaging + * + * Copyright (C) 2021 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#include "torture.h" +#include "libopensc/log.c" +#include "sm/sm-common.h" + +/* Setup context */ +static int setup_sc_context(void **state) +{ + sc_context_t *ctx = NULL; + int rv; + + rv = sc_establish_context(&ctx, "sm"); + assert_non_null(ctx); + assert_int_equal(rv, SC_SUCCESS); + + *state = ctx; + + return 0; +} + +/* Cleanup context */ +static int teardown_sc_context(void **state) +{ + sc_context_t *ctx = *state; + int rv; + + rv = sc_release_context(ctx); + assert_int_equal(rv, SC_SUCCESS); + + return 0; +} + +static void torture_sm_incr_ssc(void **state) +{ + unsigned char in[] = {0x00, 0x00}; + + (void)state; + + /* just make sure it does not crash */ + sm_incr_ssc(NULL, 0); + + /* zero-length input should not underflow the buffer */ + sm_incr_ssc(in, 0); + + /* shortest possible input */ + in[0] = 0x42; + sm_incr_ssc(in, 1); + assert_int_equal(in[0], 0x43); + + /* overflow to the second byte */ + in[0] = 0x00; + in[1] = 0xff; + sm_incr_ssc(in, 2); + assert_int_equal(in[0], 0x01); + assert_int_equal(in[1], 0x00); + + /* overflow */ + in[0] = 0xff; + in[1] = 0xff; + sm_incr_ssc(in, 2); + assert_int_equal(in[0], 0x00); + assert_int_equal(in[1], 0x00); +} + +static void torture_sm_crypt_des_cbc3(void **state) +{ + sc_context_t *ctx = *state; + /* Test vector from + * https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-20.pdf + * 5.2.1.1 The Variable Plaintext Known Answer Test -TCBC Mode + */ + unsigned char key[] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY1 */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY2 */}; + unsigned char plain[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char ciphertext[] = {0x95, 0xF8, 0xA5, 0xE5, 0xDD, 0x31, 0xD9, 0x00}; + unsigned char *out = NULL; /* allocates */ + size_t out_len = 0; + int rv; + + rv = sm_encrypt_des_cbc3(ctx, key, plain, sizeof(plain), &out, &out_len, 1); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(out_len, sizeof(ciphertext)); + assert_memory_equal(out, ciphertext, sizeof(ciphertext)); + free(out); + out = NULL; + out_len = 0; + + rv = sm_decrypt_des_cbc3(ctx, key, ciphertext, sizeof(ciphertext), &out, &out_len); + assert_int_equal(rv, SC_SUCCESS); + assert_memory_equal(out, plain, sizeof(plain)); + free(out); +} + +static void torture_sm_crypt_des_cbc3_multiblock(void **state) +{ + /* not a test vector -- generated by openssl 1.1.1 */ + sc_context_t *ctx = *state; + unsigned char key[] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY1 */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY2 */}; + unsigned char plain[] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00}; + unsigned char ciphertext[] = { + 0x95, 0xF8, 0xA5, 0xE5, 0xDD, 0x31, 0xD9, 0x00, + 0xAF, 0xA0, 0x77, 0x1d, 0x35, 0xE1, 0xCC, 0x26}; + unsigned char *out = NULL; /* allocates */ + size_t out_len = 0; + int rv; + + rv = sm_encrypt_des_cbc3(ctx, key, plain, sizeof(plain), &out, &out_len, 1); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(out_len, sizeof(ciphertext)); + assert_memory_equal(out, ciphertext, sizeof(ciphertext)); + free(out); + out = NULL; + out_len = 0; + + rv = sm_decrypt_des_cbc3(ctx, key, ciphertext, sizeof(ciphertext), &out, &out_len); + assert_int_equal(rv, SC_SUCCESS); + assert_memory_equal(out, plain, sizeof(plain)); + free(out); +} + +static void torture_sm_crypt_des_cbc3_force_pad(void **state) +{ + /* not a test vector -- generated by openssl 1.1.1 */ + sc_context_t *ctx = *state; + unsigned char key[] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY1 */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY2 */}; + unsigned char plain[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char ciphertext[] = { + 0x95, 0xF8, 0xA5, 0xE5, 0xDD, 0x31, 0xD9, 0x00, + 0xC6, 0xD3, 0xE1, 0x4F, 0xFB, 0xDE, 0xDF, 0xF9}; + unsigned char *out = NULL; /* allocates */ + size_t out_len = 0; + int rv; + + rv = sm_encrypt_des_cbc3(ctx, key, plain, sizeof(plain), &out, &out_len, 0); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(out_len, sizeof(ciphertext)); + assert_memory_equal(out, ciphertext, sizeof(ciphertext)); + free(out); + out = NULL; + out_len = 0; + + rv = sm_decrypt_des_cbc3(ctx, key, ciphertext, sizeof(ciphertext), &out, &out_len); + assert_int_equal(rv, SC_SUCCESS); + assert_memory_equal(out, plain, sizeof(plain)); + free(out); +} + +static void torture_sm_encrypt_des_ecb3(void **state) +{ + sc_context_t *ctx = *state; + /* Test vector from + * https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-20.pdf + * 5.2.1.1 The Variable Plaintext Known Answer Test -TCBC Mode + */ + unsigned char key[] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY1 */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY2 */}; + unsigned char plain[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char ciphertext[] = {0x95, 0xF8, 0xA5, 0xE5, 0xDD, 0x31, 0xD9, 0x00}; + unsigned char *out = NULL; /* allocates */ + int out_len = 0; + int rv; + + rv = sm_encrypt_des_ecb3(ctx, key, plain, sizeof(plain), &out, &out_len); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(out_len, sizeof(ciphertext)); + assert_memory_equal(out, ciphertext, sizeof(ciphertext)); + free(out); + out = NULL; + out_len = 0; + + rv = sm_encrypt_des_ecb3(ctx, key, ciphertext, sizeof(ciphertext), &out, &out_len); + assert_int_equal(rv, SC_SUCCESS); + assert_memory_equal(out, plain, sizeof(plain)); + free(out); +} + +static void torture_sm_encrypt_des_ecb3_multiblock(void **state) +{ + sc_context_t *ctx = *state; + /* not a test vector -- generated by openssl 1.1.1 */ + unsigned char key[] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY1 */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY2 */}; + unsigned char plain[] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char ciphertext[] = { + 0x95, 0xF8, 0xA5, 0xE5, 0xDD, 0x31, 0xD9, 0x00, + 0x4B, 0xD3, 0x88, 0xFF, 0x6C, 0xD8, 0x1D, 0x4F}; + unsigned char *out = NULL; /* allocates */ + int out_len = 0; + int rv; + + rv = sm_encrypt_des_ecb3(ctx, key, plain, sizeof(plain), &out, &out_len); + assert_int_equal(rv, SC_SUCCESS); + assert_int_equal(out_len, sizeof(ciphertext)); + assert_memory_equal(out, ciphertext, sizeof(ciphertext)); + free(out); + out = NULL; + out_len = 0; + + rv = sm_encrypt_des_ecb3(ctx, key, ciphertext, sizeof(ciphertext), &out, &out_len); + assert_int_equal(rv, SC_SUCCESS); + assert_memory_equal(out, plain, sizeof(plain)); + free(out); +} + +static void torture_DES_cbc_cksum_3des(void **state) +{ + sc_context_t *ctx = *state; + /* not a test vector -- generated by openssl 1.1.1 */ + unsigned char key[] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY1 */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY2 */}; + unsigned char iv[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char plain[] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char checksum_ref[] = { + 0x95, 0xF8, 0xA5, 0xE5, 0xDD, 0x31, 0xD9, 0x00}; + unsigned long sum_ref = 0xdd31d900UL; + unsigned char checksum[8]; + unsigned long sum; + + sum = DES_cbc_cksum_3des(ctx, plain, &checksum, sizeof(plain), key, &iv); + assert_int_equal(sum, sum_ref); + assert_memory_equal(checksum, checksum_ref, sizeof(checksum_ref)); + + /* The checksum argument is not required */ + sum = DES_cbc_cksum_3des(ctx, plain, NULL, sizeof(plain), key, &iv); + assert_int_equal(sum, sum_ref); +} + +static void torture_DES_cbc_cksum_3des_multiblock(void **state) +{ + sc_context_t *ctx = *state; + /* not a test vector -- generated by openssl 1.1.1 */ + unsigned char key[] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY1 */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY2 */}; + unsigned char iv[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + /* I think this function assumes/requires full blocks */ + unsigned char plain[] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char checksum_ref[] = { + 0xC6, 0x3F, 0x6E, 0x72, 0xC7, 0xCF, 0x4E, 0x07}; + unsigned long sum_ref = 0xc7cf4e07UL; + unsigned char checksum[8]; + unsigned long sum; + + sum = DES_cbc_cksum_3des(ctx, plain, &checksum, sizeof(plain), key, &iv); + assert_memory_equal(checksum, checksum_ref, sizeof(checksum_ref)); + assert_int_equal(sum, sum_ref); + + /* The checksum argument is not required */ + sum = DES_cbc_cksum_3des(ctx, plain, NULL, sizeof(plain), key, &iv); + assert_int_equal(sum, sum_ref); +} + +static void torture_DES_cbc_cksum_3des_emv96(void **state) +{ + sc_context_t *ctx = *state; + /* not a test vector -- generated by openssl 1.1.1 */ + unsigned char key[] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY1 */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY2 */}; + unsigned char iv[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char plain[] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char checksum_ref[] = { + 0x00, 0x00, 0x00, 0x00, 0xDD, 0x31, 0xD9, 0x00}; + unsigned long sum_ref = 0xdd31d900UL; + unsigned char checksum[8]; + unsigned long sum; + + sum = DES_cbc_cksum_3des_emv96(ctx, plain, &checksum, sizeof(plain), key, &iv); + assert_int_equal(sum, sum_ref); + assert_memory_equal(checksum, checksum_ref, sizeof(checksum_ref)); + + /* The checksum argument is not required */ + sum = DES_cbc_cksum_3des_emv96(ctx, plain, NULL, sizeof(plain), key, &iv); + assert_int_equal(sum, sum_ref); +} + +static void torture_DES_cbc_cksum_3des_emv96_multiblock(void **state) +{ + sc_context_t *ctx = *state; + /* not a test vector -- generated by openssl 1.1.1 */ + unsigned char key[] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY1 */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* KEY2 */}; + unsigned char iv[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + /* I think this function assumes/requires full blocks */ + unsigned char plain[] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char checksum_ref[] = { + 0x95, 0xf8, 0xA5, 0xe5, 0xC7, 0xCF, 0x4E, 0x07}; + unsigned long sum_ref = 0xc7cf4e07UL; + unsigned char checksum[8] = {0}; + unsigned long sum; + + sum = DES_cbc_cksum_3des_emv96(ctx, plain, &checksum, sizeof(plain), key, &iv); + assert_memory_equal(checksum, checksum_ref, sizeof(checksum_ref)); + assert_int_equal(sum, sum_ref); + + /* The checksum argument is not required */ + sum = DES_cbc_cksum_3des_emv96(ctx, plain, NULL, sizeof(plain), key, &iv); + assert_int_equal(sum, sum_ref); +} + +int main(void) +{ + int rc; + struct CMUnitTest tests[] = { + /* sm_incr_ssc */ + cmocka_unit_test(torture_sm_incr_ssc), + /* sm_encrypt_des_cbc3 and sm_decrypt_des_cbc3 */ + cmocka_unit_test_setup_teardown(torture_sm_crypt_des_cbc3, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_sm_crypt_des_cbc3_multiblock, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_sm_crypt_des_cbc3_force_pad, + setup_sc_context, teardown_sc_context), + /* sm_encrypt_des_ecb3 */ + cmocka_unit_test_setup_teardown(torture_sm_encrypt_des_ecb3, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_sm_encrypt_des_ecb3_multiblock, + setup_sc_context, teardown_sc_context), + /* DES_cbc_cksum_3des */ + cmocka_unit_test_setup_teardown(torture_DES_cbc_cksum_3des, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_DES_cbc_cksum_3des_multiblock, + setup_sc_context, teardown_sc_context), + /* DES_cbc_cksum_3des_emv96 */ + cmocka_unit_test_setup_teardown(torture_DES_cbc_cksum_3des_emv96, + setup_sc_context, teardown_sc_context), + cmocka_unit_test_setup_teardown(torture_DES_cbc_cksum_3des_emv96_multiblock, + setup_sc_context, teardown_sc_context), + }; + + rc = cmocka_run_group_tests(tests, NULL, NULL); + return rc; +} diff --git a/src/tests/unittests/strip_pkcs1_2_padding.c b/src/tests/unittests/strip_pkcs1_2_padding.c new file mode 100644 index 0000000000..385841fc59 --- /dev/null +++ b/src/tests/unittests/strip_pkcs1_2_padding.c @@ -0,0 +1,215 @@ +#include "common/compat_strlcpy.c" +#include "libopensc/log.c" +#include "libopensc/padding.c" +#include "torture.h" +#include + +static void +torture_long_output_buffer(void **state) +{ + unsigned int n = 14; + unsigned int in_len = 14; + unsigned char in[] = {0x00, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00, + 'm', 's', 'g'}; + unsigned int out_len = 3; + unsigned char *out = calloc(out_len, sizeof(unsigned char)); + unsigned char result_msg[] = {'m', 's', 'g'}; + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, 3); + assert_int_equal(r, (int)out_len); + assert_memory_equal(out, result_msg, r); + free(out); +} + +static void +torture_short_output_buffer(void **state) +{ + unsigned int n = 14; + unsigned int in_len = 14; + unsigned char in[] = {0x00, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00, + 'm', 's', 'g'}; + unsigned int out_len = 1; + unsigned char *out = calloc(out_len, sizeof(unsigned char)); + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal((int)out_len, 1); + assert_int_equal(r, SC_ERROR_WRONG_PADDING); + free(out); +} + +static void +torture_short_message_correct_padding(void **state) +{ + unsigned int n = 14; + unsigned int in_len = 14; + unsigned char in[] = {0x00, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00, + 'm', 's', 'g'}; + unsigned int out_len = 3; + unsigned char *out = calloc(out_len, sizeof(unsigned char)); + unsigned char result_msg[] = {'m', 's', 'g'}; + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, 3); + assert_int_equal(r, (int)out_len); + assert_memory_equal(out, result_msg, r); + free(out); +} + +static void +torture_missing_first_zero(void **state) +{ + unsigned int n = 13; + unsigned int in_len = 13; + unsigned char in[] = {0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00, + 'm', 's', 'g'}; + unsigned int out_len = 10; + unsigned char *out = calloc(out_len, sizeof(unsigned char)); + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal((int)out_len, 10); + assert_int_equal(r, SC_ERROR_WRONG_PADDING); + free(out); +} + +static void +torture_missing_two(void **state) +{ + unsigned int n = 13; + unsigned int in_len = 13; + unsigned char in[] = {0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00, + 'm', 's', 'g'}; + unsigned int out_len = 10; + unsigned char *out = calloc(out_len, sizeof(unsigned char)); + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal((int)out_len, 10); + assert_int_equal(r, SC_ERROR_WRONG_PADDING); + free(out); +} + +static void +torture_short_padding(void **state) +{ + unsigned int n = 13; + unsigned int in_len = 13; + unsigned char in[] = {0x00, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, + 'm', 's', 'g'}; + unsigned int out_len = 10; + unsigned char *out = calloc(out_len, sizeof(unsigned char)); + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal((int)out_len, 10); + assert_int_equal(r, SC_ERROR_WRONG_PADDING); + free(out); +} + +static void +torture_missing_second_zero(void **state) +{ + unsigned int n = 13; + unsigned int in_len = 13; + unsigned char in[] = {0x00, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 'm', 's', 'g'}; + unsigned int out_len = 10; + unsigned char *out = calloc(out_len, sizeof(unsigned char)); + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal((int)out_len, 10); + assert_int_equal(r, SC_ERROR_WRONG_PADDING); + free(out); +} + +static void +torture_missing_message(void **state) +{ + unsigned int n = 20; + unsigned int in_len = 11; + unsigned char in[] = {0x00, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00}; + unsigned int out_len = 11; + unsigned char *out = calloc(out_len, sizeof(unsigned char)); + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal((int)out_len, 11); + assert_int_equal(r, SC_ERROR_WRONG_PADDING); + free(out); +} + +static void +torture_one_byte_message(void **state) +{ + unsigned int n = 12; + unsigned int in_len = 12; + unsigned char in[] = {0x00, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00, + 'm'}; + unsigned int out_len = 1; + unsigned char *out = calloc(out_len, sizeof(unsigned char)); + unsigned char result_msg[] = {'m'}; + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, 1); + assert_int_equal(r, (int)out_len); + assert_memory_equal(out, result_msg, r); + free(out); +} + +static void +torture_longer_padding(void **state) +{ + unsigned int n = 26; + unsigned int in_len = 26; + unsigned char in[] = {0x00, 0x02, + 0x0e, 0x38, 0x97, 0x18, 0x16, 0x57, 0x9e, 0x30, 0xb6, 0xa5, 0x78, 0x13, 0x20, 0xca, 0x11, + 0x00, + 0x9d, 0x98, 0x3d, 0xca, 0xa9, 0xa7, 0x11, 0x0a}; + unsigned int out_len = 8; + unsigned char *out = calloc(out_len, sizeof(unsigned char)); + unsigned char result_msg[] = {0x9d, 0x98, 0x3d, 0xca, 0xa9, 0xa7, 0x11, 0x0a}; + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal(r, 8); + assert_int_equal(r, (int)out_len); + assert_memory_equal(out, result_msg, r); + free(out); +} + +static void +torture_empty_message(void **state) +{ + unsigned int n = 18; + unsigned int in_len = 18; + unsigned char in[] = {0x00, 0x02, + 0x0e, 0x38, 0x97, 0x18, 0x16, 0x57, 0x9e, 0x30, 0xb6, 0xa5, 0x78, 0x13, 0x20, 0xca, 0x11, + 0x00}; + unsigned int out_len = 8; + unsigned char *out = calloc(out_len, sizeof(unsigned char)); + int r = sc_pkcs1_strip_02_padding_constant_time(NULL, n, in, in_len, out, &out_len); + assert_int_equal((int)out_len, 0); + assert_int_equal(r, 0); + free(out); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(torture_long_output_buffer), + cmocka_unit_test(torture_short_output_buffer), + cmocka_unit_test(torture_short_message_correct_padding), + cmocka_unit_test(torture_missing_first_zero), + cmocka_unit_test(torture_missing_two), + cmocka_unit_test(torture_short_padding), + cmocka_unit_test(torture_missing_second_zero), + cmocka_unit_test(torture_missing_message), + cmocka_unit_test(torture_one_byte_message), + cmocka_unit_test(torture_longer_padding), + cmocka_unit_test(torture_empty_message)}; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/unittests/torture.h b/src/tests/unittests/torture.h new file mode 100644 index 0000000000..ce6addd859 --- /dev/null +++ b/src/tests/unittests/torture.h @@ -0,0 +1,33 @@ +/* + * torture.h: Unit tests in OpenSC + * + * Copyright (C) 2019 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 General Public License + * along with this program. If not, see . + */ + +#ifndef P11TEST_COMMON_H +#define P11TEST_COMMON_H +#include "config.h" +#include +#include +#include +#include +#include + + + +#endif /* P11TEST_COMMON_H */ diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index 471ed5a889..49fe622ef6 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -1,57 +1,76 @@ include $(top_srcdir)/win32/ltrc.inc +if ENABLE_ZLIB +VDFORMAT=XML +else +VDFORMAT=GZIP +endif + do_subst = $(SED) \ + -e 's,[@]bindir[@],$(bindir),g' \ -e 's,[@]CVCDIR[@],$(CVCDIR),g' \ - -e 's,[@]PACKAGE[@],$(PACKAGE),g' \ -e 's,[@]PACKAGE_BUGREPORT[@],$(PACKAGE_BUGREPORT),g' \ - -e 's,[@]PACKAGE_NAME[@],$(PACKAGE_NAME),g' \ - -e 's,[@]PACKAGE_TARNAME[@],$(PACKAGE_TARNAME),g' \ - -e 's,[@]PACKAGE_URL[@],$(PACKAGE_URL),g' \ -e 's,[@]PACKAGE_SUMMARY[@],$(PACKAGE_SUMMARY),g' \ - -e 's,[@]PACKAGE_VERSION[@],"$(PACKAGE_VERSION)",g' \ + -e 's,[@]PKCS11_REGISTER_SKIP_FIREFOX[@],$(PKCS11_REGISTER_SKIP_FIREFOX),g' \ + -e 's,[@]VDFORMAT[@],$(VDFORMAT),g' \ -e 's,[@]X509DIR[@],$(X509DIR),g' -NPA_TOOL_BUILT_SOURCES = npa-tool-cmdline.h npa-tool-cmdline.c +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +EXTRA_DIST = Makefile.mak versioninfo-tools.rc versioninfo-opensc-notify.rc goid-tool.ggo.in npa-tool.ggo.in opensc-notify.ggo.in egk-tool.ggo.in opensc-asn1.ggo.in org.opensc.notify.desktop.in pkcs11-register.desktop.in org.opensc-project.mac.pkcs11-register.plist.in org.opensc-project.mac.opensc-notify.plist.in -MAINTAINERCLEANFILES = $(srcdir)/Makefile.in $(srcdir)/versioninfo-tools.rc -EXTRA_DIST = Makefile.mak versioninfo-tools.rc.in npa-tool.ggo.in npa-tool.1 - -noinst_HEADERS = util.h fread_to_eof.h -noinst_PROGRAMS = sceac-example -bin_PROGRAMS = opensc-tool opensc-explorer pkcs15-tool pkcs15-crypt \ - pkcs11-tool cardos-tool eidenv openpgp-tool iasecc-tool +noinst_HEADERS = util.h fread_to_eof.h \ + egk-tool-cmdline.h goid-tool-cmdline.h npa-tool-cmdline.h \ + opensc-asn1-cmdline.h opensc-notify-cmdline.h pkcs11-register-cmdline.h \ + openpgp-tool-helpers.h pkcs11_uri.h +bin_PROGRAMS = opensc-tool opensc-explorer opensc-asn1 \ + pkcs15-tool pkcs15-crypt pkcs11-tool pkcs11-register \ + cardos-tool eidenv openpgp-tool iasecc-tool egk-tool goid-tool \ + dtrust-tool lteid-tool if ENABLE_OPENSSL bin_PROGRAMS += cryptoflex-tool pkcs15-init netkey-tool piv-tool \ - westcos-tool sc-hsm-tool dnie-tool gids-tool npa-tool + westcos-tool sc-hsm-tool dnie-tool gids-tool +if ENABLE_OPENPACE +bin_PROGRAMS += npa-tool +endif +endif + +if ENABLE_NOTIFY +bin_PROGRAMS += opensc-notify endif -if ENABLE_MAN -dist_man1_MANS = npa-tool.1 +if ENABLE_OPENPACE +noinst_PROGRAMS = sceac-example endif # compile with $(PTHREAD_CFLAGS) to allow debugging with gdb -AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_READLINE_CFLAGS) $(PTHREAD_CFLAGS) -AM_CPPFLAGS = -I$(top_srcdir)/src +AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_READLINE_CFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/src -D'DEFAULT_PKCS11_PROVIDER="$(DEFAULT_PKCS11_PROVIDER)"' -D'DEFAULT_ONEPIN_PKCS11_PROVIDER="$(DEFAULT_ONEPIN_PKCS11_PROVIDER)"' LIBS = \ $(top_builddir)/src/libopensc/libopensc.la \ $(top_builddir)/src/common/libscdl.la \ $(top_builddir)/src/common/libcompat.la sceac_example_SOURCES = sceac-example.c -sceac_example_LDADD = $(top_builddir)/src/libopensc/libopensc.la $(OPENPACE_LIBS) -sceac_example_CFLAGS = -I$(top_srcdir)/src $(OPENPACE_CFLAGS) +sceac_example_LDADD = $(top_builddir)/src/libopensc/libopensc.la $(OPTIONAL_OPENSSL_LIBS) $(OPENPACE_LIBS) +sceac_example_CFLAGS = -I$(top_srcdir)/src $(OPTIONAL_OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) opensc_tool_SOURCES = opensc-tool.c util.c piv_tool_SOURCES = piv-tool.c util.c piv_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) opensc_explorer_SOURCES = opensc-explorer.c util.c opensc_explorer_LDADD = $(OPTIONAL_READLINE_LIBS) -pkcs15_tool_SOURCES = pkcs15-tool.c util.c +pkcs15_tool_SOURCES = pkcs15-tool.c util.c ../pkcs11/pkcs11-display.c ../pkcs11/pkcs11-display.h pkcs15_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) -pkcs11_tool_SOURCES = pkcs11-tool.c util.c +pkcs11_tool_SOURCES = pkcs11-tool.c util.c pkcs11_uri.c +pkcs11_tool_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(PTHREAD_CFLAGS) pkcs11_tool_LDADD = \ $(top_builddir)/src/common/libpkcs11.la \ $(OPTIONAL_OPENSSL_LIBS) +if ENABLE_SHARED +else +pkcs11_tool_LDADD += \ + $(top_builddir)/src/pkcs11/libopensc-pkcs11.la +endif pkcs15_crypt_SOURCES = pkcs15-crypt.c util.c pkcs15_crypt_LDADD = $(OPTIONAL_OPENSSL_LIBS) cryptoflex_tool_SOURCES = cryptoflex-tool.c util.c @@ -65,47 +84,97 @@ netkey_tool_SOURCES = netkey-tool.c netkey_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) westcos_tool_SOURCES = westcos-tool.c util.c westcos_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) -openpgp_tool_SOURCES = openpgp-tool.c util.c +openpgp_tool_SOURCES = openpgp-tool.c util.c openpgp-tool-helpers.c openpgp_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) iasecc_tool_SOURCES = iasecc-tool.c util.c iasecc_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) -sc_hsm_tool_SOURCES = sc-hsm-tool.c util.c +sc_hsm_tool_SOURCES = sc-hsm-tool.c util.c fread_to_eof.c sc_hsm_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) dnie_tool_SOURCES = dnie-tool.c util.c dnie_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) gids_tool_SOURCES = gids-tool.c util.c gids_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) -npa_tool_SOURCES = npa-tool.c fread_to_eof.c $(NPA_TOOL_BUILT_SOURCES) -npa_tool_LDADD = $(top_builddir)/src/libopensc/libopensc.la \ - $(OPENPACE_LIBS) -npa_tool_CFLAGS = -I$(top_srcdir)/src $(OPENPACE_CFLAGS) $(OPENSSL_CFLAGS) +npa_tool_SOURCES = npa-tool.c fread_to_eof.c util.c npa-tool-cmdline.c +npa_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) $(OPENPACE_LIBS) +npa_tool_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) +if HAVE_UNKNOWN_WARNING_OPTION +npa_tool_CFLAGS += -Wno-unknown-warning-option +endif +if HAVE_SHORTEN_WARNING_OPTION +npa_tool_CFLAGS += -Wno-shorten-64-to-32 +endif -npa-tool.c: $(abs_builddir)/npa-tool.ggo $(NPA_TOOL_BUILT_SOURCES) +lteid_tool_SOURCES = lteid-tool.c util.c +lteid_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) $(OPENPACE_LIBS) +lteid_tool_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) -# We only want *cmdline* to be generated when they have explicitly been removed. -$(NPA_TOOL_BUILT_SOURCES): - $(MAKE) $(abs_builddir)/npa-tool.ggo - $(GENGETOPT) --include-getopt --file-name=npa-tool-cmdline --output-dir=$(builddir) < $(abs_builddir)/npa-tool.ggo +opensc_notify_SOURCES = opensc-notify.c opensc-notify-cmdline.c +opensc_notify_CFLAGS = $(PTHREAD_CFLAGS) +if HAVE_UNKNOWN_WARNING_OPTION +opensc_notify_CFLAGS += -Wno-unknown-warning-option +endif +if HAVE_SHORTEN_WARNING_OPTION +opensc_notify_CFLAGS += -Wno-shorten-64-to-32 +endif -$(abs_builddir)/npa-tool.ggo: npa-tool.ggo.in - $(do_subst) < $(abs_srcdir)/npa-tool.ggo.in > $@ +egk_tool_SOURCES = egk-tool.c util.c egk-tool-cmdline.c +egk_tool_LDADD = $(OPTIONAL_ZLIB_LIBS) +egk_tool_CFLAGS = $(OPTIONAL_ZLIB_CFLAGS) +if HAVE_UNKNOWN_WARNING_OPTION +egk_tool_CFLAGS += -Wno-unknown-warning-option +endif +if HAVE_SHORTEN_WARNING_OPTION +egk_tool_CFLAGS += -Wno-shorten-64-to-32 +endif -# We only want npa-tool.1 to be generated when it has explicitly been removed. -npa-tool.1: - $(MAKE) npa-tool$(EXEEXT) - $(HELP2MAN) \ - --output=$@ \ - --no-info \ - --source='$(PACKAGE_STRING)' \ - $(builddir)/npa-tool$(EXEEXT) +goid_tool_SOURCES = goid-tool.c util.c fread_to_eof.c goid-tool-cmdline.c +goid_tool_LDADD = $(OPENPACE_LIBS) +goid_tool_CFLAGS = $(OPENPACE_CFLAGS) +if HAVE_UNKNOWN_WARNING_OPTION +goid_tool_CFLAGS += -Wno-unknown-warning-option +endif +if HAVE_SHORTEN_WARNING_OPTION +goid_tool_CFLAGS += -Wno-shorten-64-to-32 +endif + +dtrust_tool_SOURCES = dtrust-tool.c util.c +dtrust_tool_LDADD = $(OPENPACE_LIBS) +dtrust_tool_CFLAGS = $(OPENPACE_CFLAGS) + +opensc_asn1_SOURCES = opensc-asn1.c fread_to_eof.c opensc-asn1-cmdline.c +opensc_asn1_CFLAGS = +if HAVE_UNKNOWN_WARNING_OPTION +opensc_asn1_CFLAGS += -Wno-unknown-warning-option +endif +if HAVE_SHORTEN_WARNING_OPTION +opensc_asn1_CFLAGS += -Wno-shorten-64-to-32 +endif + +pkcs11_register_SOURCES = pkcs11-register.c fread_to_eof.c pkcs11-register-cmdline.c +pkcs11_register_LDADD = $(top_builddir)/src/common/libpkcs11.la +pkcs11_register_CFLAGS = +if HAVE_UNKNOWN_WARNING_OPTION +pkcs11_register_CFLAGS += -Wno-unknown-warning-option +endif +if HAVE_SHORTEN_WARNING_OPTION +pkcs11_register_CFLAGS += -Wno-shorten-64-to-32 +endif + +.PHONY: cmdline +cmdline: + @for f in *.ggo.in; do $(do_subst) < "$$f" > "$${f%.in}"; done + @for f in *.ggo; do $(GENGETOPT) --file-name="$${f%.ggo}-cmdline" --output-dir=$(builddir) < "$$f"; done + $(AM_V_GEN)$(GENGETOPT) --file-name=opensc-asn1-cmdline --output-dir=$(builddir) --unamed-opts < opensc-asn1.ggo if WIN32 +LIBS += -lshlwapi opensc_tool_SOURCES += versioninfo-tools.rc piv_tool_SOURCES += versioninfo-tools.rc opensc_explorer_SOURCES += versioninfo-tools.rc pkcs15_tool_SOURCES += versioninfo-tools.rc pkcs11_tool_SOURCES += versioninfo-tools.rc +pkcs11_register_SOURCES += versioninfo-tools.rc pkcs15_crypt_SOURCES += versioninfo-tools.rc cryptoflex_tool_SOURCES += versioninfo-tools.rc pkcs15_init_SOURCES += versioninfo-tools.rc @@ -117,7 +186,24 @@ openpgp_tool_SOURCES += versioninfo-tools.rc iasecc_tool_SOURCES += versioninfo-tools.rc sc_hsm_tool_SOURCES += versioninfo-tools.rc gids_tool_SOURCES += versioninfo-tools.rc +opensc_notify_SOURCES += versioninfo-opensc-notify.rc +endif + +applicationsdir = $(datadir)/applications +applications_DATA = org.opensc.notify.desktop + +if ENABLE_AUTOSTART +xdg_autostartdir = $(sysconfdir)/xdg/autostart +xdg_autostart_DATA = pkcs11-register.desktop endif +%.desktop: %.desktop.in + $(AM_V_GEN)$(do_subst) < $< > $@ + +noinst_DATA = org.opensc-project.mac.pkcs11-register.plist org.opensc-project.mac.opensc-notify.plist + +%.plist: %.plist.in + $(AM_V_GEN)$(do_subst) < $< > $@ + clean-local: - rm -f $(abs_builddir)/npa-tool.ggo + rm -f $(abs_builddir)/npa-tool.ggo $(abs_builddir)/opensc-notify.ggo $(abs_builddir)/opensc-asn1.ggo $(abs_builddir)/goid-tool.ggo $(abs_builddir)/egk-tool.ggo org.opensc.notify.desktop pkcs11-register.desktop org.opensc-project.mac.opensc-notify.plist org.opensc-project.mac.pkcs11-register.plist diff --git a/src/tools/Makefile.mak b/src/tools/Makefile.mak index d03c52c7f4..75d3727724 100644 --- a/src/tools/Makefile.mak +++ b/src/tools/Makefile.mak @@ -6,9 +6,11 @@ default: all TARGETS = opensc-tool.exe opensc-explorer.exe pkcs15-tool.exe pkcs15-crypt.exe \ pkcs11-tool.exe cardos-tool.exe eidenv.exe openpgp-tool.exe iasecc-tool.exe \ - $(PROGRAMS_OPENSSL) + opensc-notify.exe egk-tool.exe goid-tool.exe dtrust-tool.exe \ + opensc-asn1.exe pkcs11-register.exe $(PROGRAMS_OPENSSL) $(PROGRAMS_OPENPACE) + +OBJECTS = util.obj versioninfo-tools.res -OBJECTS = util.obj npa-tool-cmdline.obj fread_to_eof.obj versioninfo-tools.res LIBS = $(TOPDIR)\src\common\common.lib \ $(TOPDIR)\src\scconf\scconf.lib \ $(TOPDIR)\src\libopensc\opensc.lib \ @@ -20,7 +22,46 @@ all: $(TARGETS) $(TARGETS): $(OBJECTS) $(LIBS) +opensc-notify.exe: opensc-notify-cmdline.obj versioninfo-opensc-notify.res $(LIBS) + cl $(COPTS) /c $*.c + link $(LINKFLAGS) /out:$@ $*.obj opensc-notify-cmdline.obj versioninfo-opensc-notify.res $(LIBS) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib + +npa-tool.exe: npa-tool-cmdline.obj fread_to_eof.obj $(OBJECTS) $(LIBS) + cl $(COPTS) /c $*.c + link $(LINKFLAGS) /out:$@ $*.obj npa-tool-cmdline.obj fread_to_eof.obj $(OBJECTS) $(LIBS) $(OPENPACE_LIB) $(OPENSSL_LIB) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib + +egk-tool.exe: egk-tool-cmdline.obj $(OBJECTS) $(LIBS) + cl $(COPTS) /c $*.c + link $(LINKFLAGS) /out:$@ $*.obj egk-tool-cmdline.obj $(OBJECTS) $(LIBS) $(ZLIB_LIB) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib + +goid-tool.exe: goid-tool-cmdline.obj fread_to_eof.obj $(OBJECTS) $(LIBS) + cl $(COPTS) /c $*.c + link $(LINKFLAGS) /out:$@ $*.obj goid-tool-cmdline.obj fread_to_eof.obj $(OBJECTS) $(LIBS) $(OPENPACE_LIB) $(OPENSSL_LIB) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib + +opensc-asn1.exe: opensc-asn1-cmdline.obj fread_to_eof.obj versioninfo-tools.res $(LIBS) + cl $(COPTS) /c $*.c + link $(LINKFLAGS) /out:$@ $*.obj opensc-asn1-cmdline.obj fread_to_eof.obj versioninfo-tools.res $(LIBS) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib + +pkcs11-register.exe: pkcs11-register-cmdline.obj fread_to_eof.obj $(LIBS) + cl $(COPTS) /c $*.c + link $(LINKFLAGS) /out:$@ $*.obj pkcs11-register-cmdline.obj fread_to_eof.obj versioninfo-tools.res $(LIBS) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib + +pkcs15-tool.exe: pkcs15-tool.obj $(TOPDIR)\src\pkcs11\pkcs11-display.obj + cl $(COPTS) /c $*.c + link $(LINKFLAGS) /out:$@ $*.obj $(TOPDIR)\src\pkcs11\pkcs11-display.obj $(OBJECTS) $(LIBS) $(OPENSSL_LIB) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib + +openpgp-tool.exe: openpgp-tool-helpers.obj $(LIBS) + cl $(COPTS) /c $*.c + link $(LINKFLAGS) /out:$@ $*.obj openpgp-tool-helpers.obj $(OBJECTS) $(LIBS) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib + +sc-hsm-tool.exe: sc-hsm-tool.obj fread_to_eof.obj $(OBJECTS) $(LIBS) + cl $(COPTS) /c $*.c + link $(LINKFLAGS) /out:$@ $*.obj fread_to_eof.obj $(OBJECTS) $(LIBS) $(OPENSSL_LIB) gdi32.lib shell32.lib User32.lib ws2_32.lib + +pkcs11-tool.exe: pkcs11-tool.obj pkcs11_uri.obj $(OBJECTS) $(LIBS) + cl $(COPTS) /c $*.c + link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj pkcs11_uri.obj $(OBJECTS) $(LIBS) $(OPENSSL_LIB) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib + .c.exe: cl $(COPTS) /c $< - link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj $(OBJECTS) $(LIBS) $(OPENPACE_LIB) $(OPENSSL_LIB) gdi32.lib shell32.lib ws2_32.lib - if EXIST $@.manifest mt -manifest $@.manifest -outputresource:$@;1 + link $(LINKFLAGS) /out:$@ $*.obj $(OBJECTS) $(LIBS) $(OPENSSL_LIB) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib diff --git a/src/tools/apdus b/src/tools/apdus deleted file mode 100644 index 922c3cd410..0000000000 --- a/src/tools/apdus +++ /dev/null @@ -1,29 +0,0 @@ -00:22:81:B6:0F:83:0D:5A:5A:43:56:43:41:41:54:41:30:30:30:31 -00:2a:00:be:e4:7f:4e:81:9d:5f:29:01:00:42:0d:5a:5a:43:56:43:41:41:54:41:30:30:30:31:7f:49:4f:06:0a:04:00:7f:00:07:02:02:02:02:03:86:41:04:52:dd:32:ea:fe:1f:bb:b4:00:0c:d9:ce:75:f6:66:36:cf:cf:1e:dd:44:f7:b1:ed:ae:25:b8:41:93:da:04:a9:1c:77:ee:87:f5:c8:f9:59:ed:27:62:00:de:33:ab:57:4c:e9:80:11:35:ff:44:97:a3:71:62:b7:c8:54:8a:0c:5f:20:0e:5a:5a:44:56:43:41:41:54:41:30:30:30:30:35:7f:4c:12:06:09:04:00:7f:00:07:03:01:02:02:53:05:70:03:01:ff:b7:5f:25:06:01:00:00:06:01:01:5f:24:06:01:00:01:00:03:01:5f:37:40:6f:13:ae:9a:6f:4e:dd:b7:83:9f:f3:f0:4d:71:e0:dc:37:7b:c4:b0:8f:ad:29:5e:ed:24:1b:52:43:28:ad:07:30:eb:55:34:97:b4:fb:66:e9:bb:7a:b9:08:15:f0:42:73:f0:9e:75:1d:7f:d4:b8:61:43:9b:4e:e6:53:81:c3 -002281B610830E5A5A445643414154413030303035 -002a00be0001417f4e81fa5f290100420e5a5a4456434141544130303030357f494f060a04007f000702020202038641049bfe7415d73c4a78d60b2cc1bca11b6d5e523969acfb5b756a3be1551b22239c79ae362b838b00669983c0caf6ed0c781d401c95d2b32857de8ce1b619dac4a75f200a5a5a5349543030304f347f4c12060904007f000703010202530500000000045f25060100000902015f2406010000090206655e732d060904007f0007030103028020b02baa51a94fac0954df204d61fe22da1d408d45db4aa1d70e600dad4faf6799732d060904007f0007030103018020c72e13582f01ba068dd1aac29a2428c0c54ab9c204fd53b3f13e8290e21e50f95f374083c5b441fec5b18efd1caa4a11b8e1cede0a8b42d442f00d7f604e429f339b4e3e6c06f9e76a2daa82c1722ee137a89038b969c634561581e6c26d9f6fa75c52 -00:22:81:A4:53:80:0A:04:00:7F:00:07:02:02:02:02:03:83:0A:5A:5A:53:49:54:30:30:30:4F:34:91:20:88:E5:F2:C6:11:18:0D:0A:C1:0E:BD:E6:FC:2A:5E:62:41:79:C0:A5:77:C3:E4:88:52:DD:81:A4:CD:F7:90:51:67:17:73:15:06:09:04:00:7F:00:07:03:01:04:02:53:08:32:30:31:30:30:39:32:34 -00:84:00:00:08 -00820000400C9E7DB72CB0FAEA15B00FECAE0257546446A9395862239AF240C3C29E857F8403345817760FE13F6597F04D2F7330B59065F68DF71EF7FDEC86743CDE2869DD -00a4000c023f00 -00:A4:02:0C:02:01:1D -00:b0:00:00:80 -00B0008080 -00b0010080 -00:B0:01:80:80 -00:b0:02:00:80 -00B0028080 -00b0030080 -00:B0:03:80:80 -00:b0:04:00:80 -00B0048080 -00b0050080 -00:B0:05:80:80 -00:b0:06:00:80 -00B0068080 -00b0070080 -00:B0:07:80:80 -00:22:41:a4:0c:80:0a:04:00:7f:00:07:02:02:03:02:02 -00860000457C43804104239E3D05EEB059117D30F86AEB5AE7D12E0EBF758889C79115F2A13DC1BB570A5CAD91A384337C09D1B74BED1C0FF195A7C3EA3A2CEDF86DDEF7B95D1FD1B35D00 -0020001006010203040506 - diff --git a/src/tools/cardos-tool.c b/src/tools/cardos-tool.c index c3c6406be2..bef36efe65 100644 --- a/src/tools/cardos-tool.c +++ b/src/tools/cardos-tool.c @@ -18,7 +18,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -36,11 +36,21 @@ #ifdef ENABLE_OPENSSL #include #include +#include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#endif #endif #include "libopensc/opensc.h" +#include "libopensc/cards.h" +#include "libopensc/log.h" #include "util.h" +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + static OSSL_PROVIDER *legacy_provider = NULL; +#endif + static const char *app_name = "cardos-tool"; static int opt_wait = 0; @@ -54,7 +64,6 @@ static const struct option options[] = { {"startkey", 1, NULL, 's'}, {"change-startkey", 1, NULL, 'S'}, {"reader", 1, NULL, 'r'}, - {"card-driver", 1, NULL, 'c'}, {"wait", 0, NULL, 'w'}, {"verbose", 0, NULL, 'v'}, {NULL, 0, NULL, 0} @@ -67,9 +76,8 @@ static const char *option_help[] = { "Specify startkey for format", "Change Startkey with given APDU command", "Uses reader number [0]", - "Forces the use of driver [auto-detect]", "Wait for a card to be inserted", - "Verbose operation. Use several times to enable debug output.", + "Verbose operation, may be used several times", }; static sc_context_t *ctx = NULL; @@ -188,6 +196,11 @@ static int cardos_info(void) printf(" (that's CardOS M4.4)\n"); } else if (apdu.resp[0] == 0xc9 && apdu.resp[1] == 0x01) { printf(" (that's CardOS V5.0)\n"); + } else if (apdu.resp[0] == 0xc9 && + (apdu.resp[1] == 0x02 || apdu.resp[1] == 0x03)) { + printf(" (that's CardOS V5.3)\n"); + } else if (apdu.resp[0] == 0xc9 && apdu.resp[1] == 0x04) { + printf(" (that's CardOS V5.4)\n"); } else { printf(" (unknown Version)\n"); } @@ -391,9 +404,11 @@ static int cardos_sm4h(const unsigned char *in, size_t inlen, unsigned char int plain_lc; /* data size in orig APDU */ unsigned int mac_input_len, enc_input_len; unsigned char *mac_input, *enc_input; - DES_key_schedule ks_a, ks_b; - DES_cblock des_in,des_out; + unsigned char des_in[8], des_out[8]; unsigned int i,j; + EVP_CIPHER_CTX *cctx = NULL; + int tmplen = 0; + unsigned char key1[8], key2[8]; if (keylen != 16) { printf("key has wrong size, need 16 bytes, got %"SC_FORMAT_LEN_SIZE_T"d. aborting.\n", @@ -425,27 +440,110 @@ static int cardos_sm4h(const unsigned char *in, size_t inlen, unsigned char memcpy(&mac_input[4],&in[5],plain_lc); /* calloc already did the ansi padding: rest is 00 */ - /* prepare des key using first 8 bytes of key */ - DES_set_key((const_DES_cblock*) &key[0], &ks_a); - /* prepare des key using second 8 bytes of key */ - DES_set_key((const_DES_cblock*) &key[8], &ks_b); + /* prepare des ctx */ + memcpy(key1, key, 8); + memcpy(key2, key + 8, 8); + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (!legacy_provider) { + if (!(legacy_provider = OSSL_PROVIDER_try_load(NULL, "legacy", 1))) { + sc_log_openssl(ctx); + printf("Failed to load legacy provider, aborting\n"); + free(mac_input); + return 0; + } + } +#endif + cctx = EVP_CIPHER_CTX_new(); + if (!cctx || + !EVP_EncryptInit_ex(cctx, EVP_des_ecb(), NULL, key1, NULL) || + !EVP_CIPHER_CTX_set_padding(cctx, 0)) { + sc_log_openssl(ctx); + printf("Can not setup context, aborting\n"); + free(mac_input); + EVP_CIPHER_CTX_free(cctx); + return 0; + } + /* first block: XOR with IV and encrypt with key A IV is 8 bytes 00 */ for (i=0; i < 8; i++) des_in[i] = mac_input[i]^00; - DES_ecb_encrypt(&des_in, &des_out, &ks_a, 1); + if (!EVP_EncryptUpdate(cctx, des_out, &tmplen, des_in, 8)) { + sc_log_openssl(ctx); + printf("Can not setup context, aborting\n"); + free(mac_input); + EVP_CIPHER_CTX_free(cctx); + return 0; + } /* all other blocks: XOR with prev. result and encrypt with key A */ for (j=1; j < (mac_input_len / 8); j++) { for (i=0; i < 8; i++) des_in[i] = mac_input[i+j*8]^des_out[i]; - DES_ecb_encrypt(&des_in, &des_out, &ks_a, 1); + if (!EVP_EncryptUpdate(cctx, des_out, &tmplen, des_in, 8)) { + sc_log_openssl(ctx); + printf("Can not encrypt, aborting\n"); + free(mac_input); + EVP_CIPHER_CTX_free(cctx); + return 0; + } + } + if (!EVP_EncryptFinal_ex(cctx, des_out + tmplen, &tmplen)) { + sc_log_openssl(ctx); + printf("Can not encrypt, aborting\n"); + free(mac_input); + EVP_CIPHER_CTX_free(cctx); + return 0; } /* now decrypt with key B and encrypt with key A again */ /* (a noop if key A and B are the same, e.g. 8 bytes ff */ + if (!EVP_DecryptInit_ex(cctx, EVP_des_ecb(), NULL, key2, NULL) || + !EVP_CIPHER_CTX_set_padding(cctx, 0)) { + sc_log_openssl(ctx); + printf("Can not setup context, aborting\n"); + free(mac_input); + EVP_CIPHER_CTX_free(cctx); + return 0; + } for (i=0; i < 8; i++) des_in[i] = des_out[i]; - DES_ecb_encrypt(&des_in, &des_out, &ks_b, 0); + if (!EVP_DecryptUpdate(cctx, des_out, &tmplen, des_in, 8)) { + sc_log_openssl(ctx); + printf("Can not setup context, aborting\n"); + free(mac_input); + EVP_CIPHER_CTX_free(cctx); + return 0; + } + if (!EVP_EncryptFinal_ex(cctx, des_out + tmplen, &tmplen)) { + sc_log_openssl(ctx); + printf("Can not encrypt, aborting\n"); + free(mac_input); + EVP_CIPHER_CTX_free(cctx); + return 0; + } + + if (!EVP_EncryptInit_ex(cctx, EVP_des_ecb(), NULL, key1, NULL) || + !EVP_CIPHER_CTX_set_padding(cctx, 0)) { + sc_log_openssl(ctx); + printf("Can not setup context, aborting\n"); + free(mac_input); + EVP_CIPHER_CTX_free(cctx); + return 0; + } for (i=0; i < 8; i++) des_in[i] = des_out[i]; - DES_ecb_encrypt(&des_in, &des_out, &ks_a, 1); + if (!EVP_EncryptUpdate(cctx, des_out, &tmplen, des_in, 8)) { + sc_log_openssl(ctx); + printf("Can not encrypt, aborting\n"); + free(mac_input); + EVP_CIPHER_CTX_free(cctx); + return 0; + } + if (!EVP_EncryptFinal_ex(cctx, des_out + tmplen, &tmplen)) { + sc_log_openssl(ctx); + printf("Can not encrypt, aborting\n"); + free(mac_input); + EVP_CIPHER_CTX_free(cctx); + return 0; + } /* now we want to enc: * orig APDU data plus mac (8 bytes) plus iso padding (1-8 bytes) */ @@ -462,7 +560,7 @@ static int cardos_sm4h(const unsigned char *in, size_t inlen, unsigned char memcpy(&enc_input[0],&in[5],plain_lc); for (i=0; i < 8; i++) enc_input[i+plain_lc] = des_out[i]; enc_input[plain_lc+8] = 0x80; /* iso padding */ - /* calloc already cleard the remaining bytes to 00 */ + /* calloc already cleared the remaining bytes to 00 */ if (outlen < 5 + enc_input_len) { free(mac_input); @@ -478,27 +576,54 @@ static int cardos_sm4h(const unsigned char *in, size_t inlen, unsigned char out[4] = enc_input_len; /* lc */ /* encrypt first block */ + cctx = EVP_CIPHER_CTX_new(); + if (!cctx || + !EVP_EncryptInit_ex(cctx, EVP_des_ede_ecb(), NULL, key, NULL) || + !EVP_CIPHER_CTX_set_padding(cctx, 0)) { + sc_log_openssl(ctx); + printf("Can not setup context, aborting\n"); + free(mac_input); + free(enc_input); + EVP_CIPHER_CTX_free(cctx); + return 0; + } /* xor data and IV (8 bytes 00) to get input data */ for (i=0; i < 8; i++) des_in[i] = enc_input[i] ^ 00; - /* encrypt with des2 (tripple des, but using keys A-B-A) */ - DES_ecb2_encrypt(&des_in, &des_out, &ks_a, &ks_b, 1); + /* encrypt with des2 (triple des, but using keys A-B-A) */ + if (!EVP_EncryptUpdate(cctx, des_out, &tmplen, des_in, 8)) { + sc_log_openssl(ctx); + printf("Can not encrypt, aborting\n"); + free(mac_input); + free(enc_input); + EVP_CIPHER_CTX_free(cctx); + return 0; + } /* copy encrypted bytes into output */ for (i=0; i < 8; i++) out[5+i] = des_out[i]; - /* encrypt other blocks (usualy one) */ + /* encrypt other blocks (usually one) */ for (j=1; j < (enc_input_len / 8); j++) { /* xor data and prev. result to get input data */ for (i=0; i < 8; i++) des_in[i] = enc_input[i+j*8] ^ des_out[i]; - /* encrypt with des2 (tripple des, but using keys A-B-A) */ - DES_ecb2_encrypt(&des_in, &des_out, &ks_a, &ks_b, 1); + /* encrypt with des2 (triple des, but using keys A-B-A) */ + if (!EVP_EncryptUpdate(cctx, des_out, &tmplen, des_in, 8)) { + sc_log_openssl(ctx); + printf("Can not encrypt, aborting\n"); + free(mac_input); + free(enc_input); + EVP_CIPHER_CTX_free(cctx); + return 0; + } /* copy encrypted bytes into output */ for (i=0; i < 8; i++) out[5+8*j+i] = des_out[i]; } + + EVP_CIPHER_CTX_free(cctx); if (verbose) { printf ("Unencrypted APDU:\n"); util_hex_dump_asc(stdout, in, inlen, -1); @@ -850,6 +975,11 @@ static int cardos_change_startkey(const char *change_startkey_apdu) u8 rbuf[256]; int r; + if (!change_startkey_apdu) { + printf("Missing change StartKey, aborting\n"); + return 1; + } + if (verbose) { printf ("Change StartKey APDU:\n"); util_hex_dump_asc(stdout, (unsigned char *)change_startkey_apdu, @@ -1024,23 +1154,18 @@ static int cardos_change_startkey(const char *change_startkey_apdu) { } #endif -int main(int argc, char *const argv[]) +int main(int argc, char *argv[]) { - int err = 0, r, c, long_optind = 0; + int err = 0, r, c; int do_info = 0; int do_format = 0; int do_change_startkey = 0; int action_count = 0; - const char *opt_driver = NULL; const char *opt_startkey = NULL; const char *opt_change_startkey = NULL; sc_context_param_t ctx_param; - while (1) { - c = getopt_long(argc, argv, "hifs:r:vdc:wS:", options, - &long_optind); - if (c == -1) - break; + while ((c = getopt_long(argc, argv, "hifs:r:vdwS:", options, (int *) 0)) != -1) { switch (c) { case 'h': printf("NB! This tool is only for Siemens CardOS based cards!\n\n"); @@ -1067,19 +1192,25 @@ int main(int argc, char *const argv[]) case 'v': verbose++; break; - case 'c': - opt_driver = optarg; - break; case 'w': opt_wait = 1; break; + default: + util_print_usage_and_die(app_name, options, option_help, NULL); } } + + if (action_count == 0) + util_print_usage_and_die(app_name, options, option_help, NULL); /* create sc_context_t object */ memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.ver = 0; ctx_param.app_name = app_name; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; + r = sc_context_create(&ctx, &ctx_param); if (r) { fprintf(stderr, "Failed to establish context: %s\n", @@ -1087,25 +1218,25 @@ int main(int argc, char *const argv[]) return 1; } - if (verbose > 1) { - ctx->debug = verbose; - sc_ctx_log_to_file(ctx, "stderr"); - } - - if (opt_driver != NULL) { - err = sc_set_card_driver(ctx, opt_driver); - if (err) { - fprintf(stderr, "Driver '%s' not found!\n", - opt_driver); - err = 1; - goto end; - } + /* force CardOS card driver */ + err = sc_set_card_driver(ctx, "cardos"); + if (err) { + fprintf(stderr, "CardOS card driver not found!\n"); + err = 1; + goto end; } - err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); + err = util_connect_card(ctx, &card, opt_reader, opt_wait); if (err) goto end; + /* fail if card is not a CardOS card */ + if (card->type < SC_CARD_TYPE_CARDOS_BASE || card->type >= SC_CARD_TYPE_CARDOS_BASE+1000) { + fprintf(stderr, "Card type %X: not a CardOS card\n", card->type); + err = 1; + goto end; + } + if (do_info) { if ((err = cardos_info())) { goto end; @@ -1124,12 +1255,11 @@ int main(int argc, char *const argv[]) } action_count--; } - end: +end: if (card) { sc_unlock(card); sc_disconnect_card(card); } - if (ctx) - sc_release_context(ctx); + sc_release_context(ctx); return err; } diff --git a/src/tools/cryptoflex-tool.c b/src/tools/cryptoflex-tool.c index ed9255e050..92f5dad928 100644 --- a/src/tools/cryptoflex-tool.c +++ b/src/tools/cryptoflex-tool.c @@ -15,17 +15,25 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "libopensc/sc-ossl-compat.h" +#include "libopensc/internal.h" +#include "libopensc/log.h" #include #include #include #include #include +#include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# include +# include +# include +#endif #include "libopensc/pkcs15.h" #include "common/compat_strlcpy.h" @@ -81,7 +89,7 @@ static const char *option_help[] = { "Modulus length to use in key generation [1024]", "Uses reader ", "Wait for card insertion", - "Verbose operation. Use several times to enable debug output.", + "Verbose operation, may be used several times", }; static sc_context_t *ctx = NULL; @@ -179,7 +187,7 @@ static BIGNUM * cf2bn(const u8 *buf, size_t bufsize, BIGNUM *num) invert_buf(tmp, buf, bufsize); - return BN_bin2bn(tmp, bufsize, num); + return BN_bin2bn(tmp, (int)bufsize, num); } static int bn2cf(const BIGNUM *num, u8 *buf) @@ -195,12 +203,18 @@ static int bn2cf(const BIGNUM *num, u8 *buf) return r; } -static int parse_public_key(const u8 *key, size_t keysize, RSA *rsa) +static int parse_public_key(const u8 *key, size_t keysize, EVP_PKEY *pkey) { const u8 *p = key; BIGNUM *n, *e; - int base; - + size_t base; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA *rsa = NULL; +#else + EVP_PKEY_CTX *cctx = NULL; + OSSL_PARAM *params = NULL; + OSSL_PARAM_BLD *bld = NULL; +#endif base = (keysize - 7) / 5; if (base != 32 && base != 48 && base != 64 && base != 128) { fprintf(stderr, "Invalid public key.\n"); @@ -208,8 +222,10 @@ static int parse_public_key(const u8 *key, size_t keysize, RSA *rsa) } p += 3; n = BN_new(); - if (n == NULL) + if (n == NULL) { + sc_log_openssl(ctx); return -1; + } cf2bn(p, 2 * base, n); p += 2 * base; p += base; @@ -218,17 +234,47 @@ static int parse_public_key(const u8 *key, size_t keysize, RSA *rsa) if (e == NULL) return -1; cf2bn(p, 4, e); - if (RSA_set0_key(rsa, n, e, NULL) != 1) - return -1; + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if (!(rsa = RSA_new()) || + !(pkey = EVP_PKEY_new()) || + RSA_set0_key(rsa, n, e, NULL) != 1 || + EVP_PKEY_assign_RSA(pkey, rsa) != 1) { + sc_log_openssl(ctx); + RSA_free(rsa); + EVP_PKEY_free(pkey); + return -1; + } +#else + cctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + if (!cctx || + !(bld = OSSL_PARAM_BLD_new()) || + OSSL_PARAM_BLD_push_BN(bld, "n", n) != 1 || + OSSL_PARAM_BLD_push_BN(bld, "e", e) != 1 || + !(params = OSSL_PARAM_BLD_to_param(bld))) { + sc_log_openssl(ctx); + OSSL_PARAM_BLD_free(bld); + EVP_PKEY_CTX_free(cctx); + OSSL_PARAM_free(params); + return -1; + } + OSSL_PARAM_BLD_free(bld); + + if (EVP_PKEY_fromdata_init(cctx) != 1 || + EVP_PKEY_fromdata(cctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) != 1) { + sc_log_openssl(ctx); + return -1; + } + OSSL_PARAM_free(params); + EVP_PKEY_CTX_free(cctx); +#endif return 0; } -static int gen_d(RSA *rsa) +static int gen_d(BIGNUM **rsa_d_new, const BIGNUM *rsa_p, const BIGNUM *rsa_q, const BIGNUM *rsa_n, const BIGNUM *rsa_e) { BN_CTX *bnctx; BIGNUM *r0, *r1, *r2; - const BIGNUM *rsa_p, *rsa_q, *rsa_n, *rsa_e, *rsa_d; - BIGNUM *rsa_n_new, *rsa_e_new, *rsa_d_new; bnctx = BN_CTX_new(); if (bnctx == NULL) @@ -237,35 +283,43 @@ static int gen_d(RSA *rsa) r0 = BN_CTX_get(bnctx); r1 = BN_CTX_get(bnctx); r2 = BN_CTX_get(bnctx); - RSA_get0_key(rsa, &rsa_n, &rsa_e, &rsa_d); - RSA_get0_factors(rsa, &rsa_p, &rsa_q); BN_sub(r1, rsa_p, BN_value_one()); BN_sub(r2, rsa_q, BN_value_one()); BN_mul(r0, r1, r2, bnctx); - if ((rsa_d_new = BN_mod_inverse(NULL, rsa_e, r0, bnctx)) == NULL) { + if ((*rsa_d_new = BN_mod_inverse(NULL, rsa_e, r0, bnctx)) == NULL) { + sc_log_openssl(ctx); fprintf(stderr, "BN_mod_inverse() failed.\n"); return -1; } - /* RSA_set0_key will free previous value, and replace with new value - * Thus the need to copy the contents of rsa_n and rsa_e - */ - rsa_n_new = BN_dup(rsa_n); - rsa_e_new = BN_dup(rsa_e); - if (RSA_set0_key(rsa, rsa_n_new, rsa_e_new, rsa_d_new) != 1) - return -1; - BN_CTX_end(bnctx); BN_CTX_free(bnctx); return 0; } -static int parse_private_key(const u8 *key, size_t keysize, RSA *rsa) +static int parse_private_key(const u8 *key, size_t keysize, EVP_PKEY *pkey) { const u8 *p = key; BIGNUM *bn_p, *q, *dmp1, *dmq1, *iqmp; - int base; + size_t base; + BIGNUM *rsa_d = NULL; + int rv = 0; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const BIGNUM *rsa_n, *rsa_e; + BIGNUM *rsa_n_new, *rsa_e_new ; + RSA *rsa = NULL; + if (!(rsa = EVP_PKEY_get0_RSA(pkey))) { + sc_log_openssl(ctx); + return -1; + } +#else + OSSL_PARAM *params = NULL, *pkey_params = NULL, *new_params = NULL; + const OSSL_PARAM *e = NULL, *n = NULL; + BIGNUM *rsa_n, *rsa_e; + OSSL_PARAM_BLD *bld = NULL; + EVP_PKEY_CTX *cctx = NULL; +#endif base = (keysize - 3) / 5; if (base != 32 && base != 48 && base != 64 && base != 128) { @@ -274,47 +328,117 @@ static int parse_private_key(const u8 *key, size_t keysize, RSA *rsa) } p += 3; bn_p = BN_new(); - if (bn_p == NULL) + if (bn_p == NULL) { + sc_log_openssl(ctx); return -1; + } cf2bn(p, base, bn_p); p += base; q = BN_new(); - if (q == NULL) + if (q == NULL) { + sc_log_openssl(ctx); return -1; + } cf2bn(p, base, q); p += base; iqmp = BN_new(); - if (iqmp == NULL) + if (iqmp == NULL) { + sc_log_openssl(ctx); return -1; + } cf2bn(p, base, iqmp); p += base; dmp1 = BN_new(); - if (dmp1 == NULL) + if (dmp1 == NULL) { + sc_log_openssl(ctx); return -1; + } cf2bn(p, base, dmp1); p += base; dmq1 = BN_new(); - if (dmq1 == NULL) + if (dmq1 == NULL) { + sc_log_openssl(ctx); return -1; + } cf2bn(p, base, dmq1); - p += base; - - if (RSA_set0_factors(rsa, bn_p, q) != 1) +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL); + + if (RSA_set0_factors(rsa, bn_p, q) != 1 || + RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp) != 1 || + gen_d(&rsa_d, bn_p, q, rsa_n, rsa_e) != 0) { + sc_log_openssl(ctx); return -1; - if (RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp) != 1) + } + + /* RSA_set0_key will free previous value, and replace with new value + * Thus the need to copy the contents of rsa_n and rsa_e + */ + rsa_n_new = BN_dup(rsa_n); + rsa_e_new = BN_dup(rsa_e); + if (!rsa_n_new || !rsa_e_new || + RSA_set0_key(rsa, rsa_n_new, rsa_e_new, rsa_d) != 1) { + sc_log_openssl(ctx); return -1; - if (gen_d(rsa)) + } +#else + /* Extract parameters from pkey */ + if (EVP_PKEY_todata(pkey, EVP_PKEY_PUBLIC_KEY, &pkey_params) != 1) { + sc_log_openssl(ctx); return -1; + } + e = OSSL_PARAM_locate_const(pkey_params, "e"); + n = OSSL_PARAM_locate_const(pkey_params, "n"); + if (!e || !n) { + sc_log_openssl(ctx); + OSSL_PARAM_free(pkey_params); + return -1; + } + OSSL_PARAM_get_BN(e, &rsa_e); + OSSL_PARAM_get_BN(n, &rsa_n); + gen_d(&rsa_d, bn_p, q, rsa_n, rsa_e); + /* Merge params*/ + if (!(bld = OSSL_PARAM_BLD_new()) || + OSSL_PARAM_BLD_push_BN(bld, "d", rsa_d) != 1 || + OSSL_PARAM_BLD_push_BN(bld, "rsa-factor1", bn_p) != 1 || + OSSL_PARAM_BLD_push_BN(bld, "rsa-factor2", q) != 1 || + OSSL_PARAM_BLD_push_BN(bld, "rsa-exponent1", dmp1) != 1 || + OSSL_PARAM_BLD_push_BN(bld, "rsa-exponent2", dmq1) != 1 || + OSSL_PARAM_BLD_push_BN(bld, "rsa-coefficient1", iqmp) != 1 || + !(new_params = OSSL_PARAM_BLD_to_param(bld))) { + sc_log_openssl(ctx); + OSSL_PARAM_free(pkey_params); + OSSL_PARAM_BLD_free(bld); + return -1; + } + OSSL_PARAM_BLD_free(bld); + if (!(params = OSSL_PARAM_merge(pkey_params, new_params))) { + sc_log_openssl(ctx); + OSSL_PARAM_free(pkey_params); + OSSL_PARAM_free(new_params); + return -1; + } - return 0; + /* Create pkey from params */ + if (!(cctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL)) || + EVP_PKEY_fromdata_init(cctx) != 1 || + EVP_PKEY_fromdata(cctx, &pkey, EVP_PKEY_KEYPAIR, params) != 1) { + rv = -1; + sc_log_openssl(ctx); + } + OSSL_PARAM_free(pkey_params); + OSSL_PARAM_free(new_params); + EVP_PKEY_CTX_free(cctx); +#endif + return rv; } -static int read_public_key(RSA *rsa) +static int read_public_key(EVP_PKEY *pkey) { int r; sc_path_t path; @@ -331,7 +455,7 @@ static int read_public_key(RSA *rsa) fprintf(stderr, "Unable to select public key file: %s\n", sc_strerror(r)); return 2; } - bufsize = file->size; + bufsize = MIN(file->size, sizeof buf); sc_file_free(file); r = sc_read_binary(card, 0, buf, bufsize, 0); if (r < 0) { @@ -356,11 +480,11 @@ static int read_public_key(RSA *rsa) printf("Key number %d not found.\n", opt_key_num); return 2; } - return parse_public_key(p, keysize, rsa); + return parse_public_key(p, keysize, pkey); } -static int read_private_key(RSA *rsa) +static int read_private_key(EVP_PKEY *pkey) { int r; sc_path_t path; @@ -382,7 +506,7 @@ static int read_private_key(RSA *rsa) e = sc_file_get_acl_entry(file, SC_AC_OP_READ); if (e == NULL || e->method == SC_AC_NEVER) return 10; - bufsize = file->size; + bufsize = MIN(file->size, sizeof buf); sc_file_free(file); r = sc_read_binary(card, 0, buf, bufsize, 0); if (r < 0) { @@ -407,23 +531,23 @@ static int read_private_key(RSA *rsa) printf("Key number %d not found.\n", opt_key_num); return 2; } - return parse_private_key(p, keysize, rsa); + return parse_private_key(p, keysize, pkey); } static int read_key(void) { - RSA *rsa = RSA_new(); + EVP_PKEY *pkey = NULL; u8 buf[1024], *p = buf; u8 b64buf[2048]; int r; - if (rsa == NULL) - return -1; - r = read_public_key(rsa); + r = read_public_key(pkey); if (r) return r; - r = i2d_RSA_PUBKEY(rsa, &p); + + r = i2d_PUBKEY(pkey, &p); if (r <= 0) { + sc_log_openssl(ctx); fprintf(stderr, "Error encoding public key.\n"); return -1; } @@ -434,14 +558,15 @@ static int read_key(void) } printf("-----BEGIN PUBLIC KEY-----\n%s-----END PUBLIC KEY-----\n", b64buf); - r = read_private_key(rsa); + r = read_private_key(pkey); if (r == 10) return 0; else if (r) return r; p = buf; - r = i2d_RSAPrivateKey(rsa, &p); + r = i2d_PrivateKey(pkey, &p); if (r <= 0) { + sc_log_openssl(ctx); fprintf(stderr, "Error encoding private key.\n"); return -1; } @@ -631,41 +756,75 @@ static int create_key_files(void) return 0; } -static int read_rsa_privkey(RSA **rsa_out) +static int read_rsa_privkey(EVP_PKEY **pkey_out) { - RSA *rsa = NULL; + EVP_PKEY *pkey = NULL; BIO *in = NULL; int r; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA *rsa = NULL; + pkey = EVP_PKEY_new(); +#else + OSSL_DECODER_CTX *dctx; +#endif in = BIO_new(BIO_s_file()); if (opt_prkeyf == NULL) { + sc_log_openssl(ctx); fprintf(stderr, "Private key file must be set.\n"); return 2; } r = BIO_read_filename(in, opt_prkeyf); if (r <= 0) { + sc_log_openssl(ctx); perror(opt_prkeyf); return 2; } +#if OPENSSL_VERSION_NUMBER < 0x30000000L rsa = PEM_read_bio_RSAPrivateKey(in, NULL, NULL, NULL); if (rsa == NULL) { + sc_log_openssl(ctx); fprintf(stderr, "Unable to load private key.\n"); + BIO_free(in); + return 2; + } + if (EVP_PKEY_assign_RSA(pkey, rsa) != 1) { + sc_log_openssl(ctx); + fprintf(stderr, "Unable to set private key.\n"); + RSA_free(rsa); + BIO_free(in); + return 2; + } +#else + dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "PEM", NULL, "RSA", OSSL_KEYMGMT_SELECT_KEYPAIR, NULL, NULL); + if (!dctx || OSSL_DECODER_from_bio(dctx, in) != 1) { + sc_log_openssl(ctx); + fprintf(stderr, "Unable to initialite decoder.\n"); + OSSL_DECODER_CTX_free(dctx); + BIO_free(in); return 2; } +#endif BIO_free(in); - *rsa_out = rsa; + *pkey_out = pkey; return 0; } -static int encode_private_key(RSA *rsa, u8 *key, size_t *keysize) +static int encode_private_key(EVP_PKEY *pkey, u8 *key, size_t *keysize) { u8 buf[1024], *p = buf; u8 bnbuf[256]; int base = 0; int r; + int rv = 0; +#if OPENSSL_VERSION_NUMBER < 0x30000000L const BIGNUM *rsa_p, *rsa_q, *rsa_dmp1, *rsa_dmq1, *rsa_iqmp; + RSA *rsa = EVP_PKEY_get1_RSA(pkey); +#else + BIGNUM *rsa_p = NULL, *rsa_q = NULL, *rsa_dmp1 = NULL, *rsa_dmq1 = NULL, *rsa_iqmp = NULL; +#endif - switch (RSA_bits(rsa)) { + switch (EVP_PKEY_bits(pkey)) { case 512: base = 32; break; @@ -687,12 +846,23 @@ static int encode_private_key(RSA *rsa, u8 *key, size_t *keysize) *p++ = (5 * base + 3) & 0xFF; *p++ = opt_key_num; +#if OPENSSL_VERSION_NUMBER < 0x30000000L RSA_get0_factors(rsa, &rsa_p, &rsa_q); +#else + if (EVP_PKEY_get_bn_param(pkey, "rsa-factor1", &rsa_p) != 1 || + EVP_PKEY_get_bn_param(pkey, "rsa-factor2", &rsa_q) != 1) { + sc_log_openssl(ctx); + fprintf(stderr, "Invalid private key.\n"); + rv = 2; + goto end; + } +#endif r = bn2cf(rsa_p, bnbuf); if (r != base) { fprintf(stderr, "Invalid private key.\n"); - return 2; + rv = 2; + goto end; } memcpy(p, bnbuf, base); p += base; @@ -700,17 +870,29 @@ static int encode_private_key(RSA *rsa, u8 *key, size_t *keysize) r = bn2cf(rsa_q, bnbuf); if (r != base) { fprintf(stderr, "Invalid private key.\n"); - return 2; + rv = 2; + goto end; } memcpy(p, bnbuf, base); p += base; +#if OPENSSL_VERSION_NUMBER < 0x30000000L RSA_get0_crt_params(rsa, &rsa_dmp1, &rsa_dmq1, &rsa_iqmp); - +#else + if (EVP_PKEY_get_bn_param(pkey, "rsa-exponent1", &rsa_dmp1) != 1 || + EVP_PKEY_get_bn_param(pkey, "rsa-exponent2", &rsa_dmq1) != 1 || + EVP_PKEY_get_bn_param(pkey, "rsa-coefficient1", &rsa_iqmp) != 1) { + sc_log_openssl(ctx); + fprintf(stderr, "Invalid private key.\n"); + rv = 2; + goto end; + } +#endif r = bn2cf(rsa_iqmp, bnbuf); if (r != base) { fprintf(stderr, "Invalid private key.\n"); - return 2; + rv = 2; + goto end; } memcpy(p, bnbuf, base); p += base; @@ -718,7 +900,8 @@ static int encode_private_key(RSA *rsa, u8 *key, size_t *keysize) r = bn2cf(rsa_dmp1, bnbuf); if (r != base) { fprintf(stderr, "Invalid private key.\n"); - return 2; + rv = 2; + goto end; } memcpy(p, bnbuf, base); p += base; @@ -726,7 +909,8 @@ static int encode_private_key(RSA *rsa, u8 *key, size_t *keysize) r = bn2cf(rsa_dmq1, bnbuf); if (r != base) { fprintf(stderr, "Invalid private key.\n"); - return 2; + rv = 2; + goto end; } memcpy(p, bnbuf, base); p += base; @@ -734,18 +918,34 @@ static int encode_private_key(RSA *rsa, u8 *key, size_t *keysize) memcpy(key, buf, p - buf); *keysize = p - buf; - return 0; +end: +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA_free(rsa); +#else + BN_free(rsa_p); + BN_free(rsa_q); + BN_free(rsa_dmp1); + BN_free(rsa_dmq1); + BN_free(rsa_iqmp); +#endif + return rv; } -static int encode_public_key(RSA *rsa, u8 *key, size_t *keysize) +static int encode_public_key(EVP_PKEY *pkey, u8 *key, size_t *keysize) { u8 buf[1024], *p = buf; u8 bnbuf[256]; int base = 0; int r; + int rv = 0; +#if OPENSSL_VERSION_NUMBER < 0x30000000L const BIGNUM *rsa_n, *rsa_e; + RSA *rsa = NULL; +#else + BIGNUM *rsa_n = NULL, *rsa_e = NULL; +#endif - switch (RSA_bits(rsa)) { + switch (EVP_PKEY_bits(pkey)) { case 512: base = 32; break; @@ -767,11 +967,22 @@ static int encode_public_key(RSA *rsa, u8 *key, size_t *keysize) *p++ = (5 * base + 7) & 0xFF; *p++ = opt_key_num; +#if OPENSSL_VERSION_NUMBER < 0x30000000L RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL); +#else + if (EVP_PKEY_get_bn_param(pkey, "n", &rsa_n) != 1 || + EVP_PKEY_get_bn_param(pkey, "e", &rsa_e) != 1) { + sc_log_openssl(ctx); + fprintf(stderr, "Invalid public key.\n"); + rv = 2; + goto end; + } +#endif r = bn2cf(rsa_n, bnbuf); if (r != 2*base) { fprintf(stderr, "Invalid public key.\n"); - return 2; + rv = 2; + goto end; } memcpy(p, bnbuf, 2*base); p += 2*base; @@ -783,13 +994,24 @@ static int encode_public_key(RSA *rsa, u8 *key, size_t *keysize) memcpy(p, bnbuf, 2*base); p += 2*base; r = bn2cf(rsa_e, bnbuf); + if (r != 4) { + fprintf(stderr, "Invalid exponent value.\n"); + rv = 2; + goto end; + } memcpy(p, bnbuf, 4); p += 4; memcpy(key, buf, p - buf); *keysize = p - buf; - - return 0; +end: +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA_free(rsa); +#else + BN_free(rsa_n); + BN_free(rsa_e); +#endif + return rv; } static int update_public_key(const u8 *key, size_t keysize) @@ -806,7 +1028,7 @@ static int update_public_key(const u8 *key, size_t keysize) fprintf(stderr, "Unable to select public key file: %s\n", sc_strerror(r)); return 2; } - idx = keysize * (opt_key_num-1); + idx = (int)keysize * (opt_key_num - 1); r = sc_update_binary(card, idx, key, keysize, 0); if (r < 0) { fprintf(stderr, "Unable to write public key: %s\n", sc_strerror(r)); @@ -829,7 +1051,7 @@ static int update_private_key(const u8 *key, size_t keysize) fprintf(stderr, "Unable to select private key file: %s\n", sc_strerror(r)); return 2; } - idx = keysize * (opt_key_num-1); + idx = (int)keysize * (opt_key_num - 1); r = sc_update_binary(card, idx, key, keysize, 0); if (r < 0) { fprintf(stderr, "Unable to write private key: %s\n", sc_strerror(r)); @@ -841,17 +1063,17 @@ static int update_private_key(const u8 *key, size_t keysize) static int store_key(void) { u8 prv[1024], pub[1024]; - size_t prvsize, pubsize; + size_t prvsize = 0, pubsize = 0; int r; - RSA *rsa; + EVP_PKEY *pkey = NULL; - r = read_rsa_privkey(&rsa); + r = read_rsa_privkey(&pkey); if (r) return r; - r = encode_private_key(rsa, prv, &prvsize); + r = encode_private_key(pkey, prv, &prvsize); if (r) return r; - r = encode_public_key(rsa, pub, &pubsize); + r = encode_public_key(pkey, pub, &pubsize); if (r) return r; if (verbose) @@ -978,7 +1200,7 @@ static int create_pin(void) return create_pin_file(&path, opt_pin_num, ""); } -int main(int argc, char * const argv[]) +int main(int argc, char *argv[]) { int err = 0, r, c, long_optind = 0; int action_count = 0; @@ -1065,6 +1287,9 @@ int main(int argc, char * const argv[]) memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.ver = 0; ctx_param.app_name = app_name; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; r = sc_context_create(&ctx, &ctx_param); if (r) { @@ -1072,12 +1297,9 @@ int main(int argc, char * const argv[]) return 1; } - if (verbose > 1) { - ctx->debug = verbose; - sc_ctx_log_to_file(ctx, "stderr"); - } - - err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); + err = util_connect_card(ctx, &card, opt_reader, opt_wait); + if (err) + goto end; printf("Using card driver: %s\n", card->driver->name); if (do_create_pin_file) { @@ -1119,7 +1341,6 @@ int main(int argc, char * const argv[]) sc_unlock(card); sc_disconnect_card(card); } - if (ctx) - sc_release_context(ctx); + sc_release_context(ctx); return err; } diff --git a/src/tools/dnie-tool.c b/src/tools/dnie-tool.c index c856a7bacb..af1971668e 100644 --- a/src/tools/dnie-tool.c +++ b/src/tools/dnie-tool.c @@ -18,7 +18,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -36,6 +36,7 @@ #include "libopensc/opensc.h" #include "libopensc/errors.h" #include "libopensc/cardctl.h" +#include "libopensc/cards.h" #include "libopensc/pkcs15.h" #include "util.h" @@ -54,7 +55,6 @@ static const char *app_name = "dnie-tool"; static const struct option options[] = { {"reader", 1, NULL, 'r'}, - {"driver", 1, NULL, 'c'}, {"wait", 0, NULL, 'w'}, {"pin", 1, NULL, 'p'}, {"idesp", 0, NULL, 'i'}, @@ -68,7 +68,6 @@ static const struct option options[] = { static const char *option_help[] = { "Uses reader number [0]", - "Uses card driver [auto-detect]", "Wait for a card to be inserted", "Specify PIN", "Retrieve IDESP", @@ -76,7 +75,7 @@ static const char *option_help[] = { "Show DNIe number, Name, and SurName", "Show DNIe serial number", "Display all the information available", - "Verbose operation. Use several times to enable debug output." + "Verbose operation, may be used several times", }; /* Get DNIe device extra information */ @@ -86,7 +85,6 @@ int main(int argc, char* argv[]) int opt_wait = 0; const char *opt_pin = NULL; const char *opt_reader = NULL; - const char *opt_driver = NULL; int opt_operation = OP_NONE; int verbose = 0; @@ -94,25 +92,16 @@ int main(int argc, char* argv[]) sc_context_t *ctx = NULL; sc_context_param_t ctx_param; sc_card_t *card = NULL; - int c, long_optind, r, tries_left; + int c, r; char *data[] = { NULL, NULL, NULL, NULL, NULL }; sc_serial_number_t serial; - while (1) { - c = getopt_long(argc, argv, "r:c:wp:iVdsav", - options, &long_optind); - if (c == -1) - break; + while ((c = getopt_long(argc, argv, "r:wp:iVdsav", options, (int *) 0)) != -1) { switch (c) { - case '?': - util_print_usage_and_die(app_name, options, option_help, NULL); case 'r': opt_reader = optarg; break; - case 'c': - opt_driver = optarg; - break; case 'w': opt_wait = 1; break; @@ -137,46 +126,48 @@ int main(int argc, char* argv[]) case 'v': verbose++; break; + default: + util_print_usage_and_die(app_name, options, option_help, NULL); } } memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.app_name = app_name; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; r = sc_context_create(&ctx, &ctx_param); if (r) { fprintf(stderr, "Error: Failed to establish context: %s\n", sc_strerror(r)); + err = -1; goto dnie_tool_end; } - if (verbose > 1) { - ctx->debug = verbose; - sc_ctx_log_to_file(ctx,"stderr"); - } - - if (opt_driver != NULL) { - err = sc_set_card_driver(ctx, opt_driver); - if (err) { - fprintf(stderr, "Driver '%s' not found!\n", - opt_driver); - err = -1; - goto dnie_tool_end; - } + /* force DNIe card driver */ + err = sc_set_card_driver(ctx, "dnie"); + if (err) { + fprintf(stderr, "DNIe card driver not found!\n"); + err = -1; + goto dnie_tool_end; } - if (util_connect_card(ctx, &card, opt_reader, opt_wait, verbose) ) { + if (util_connect_card(ctx, &card, opt_reader, opt_wait) ) { fprintf(stderr, "Error: Cannot connect with card\n"); err = -1; goto dnie_tool_end; } - if ( strcmp(card->name,"dnie") ) { - fprintf(stderr, "Error: Card sems not to be a DNIe\n"); - err=-1; + /* fail if card is not a DNIe card */ + if (card->type < SC_CARD_TYPE_DNIE_BASE || card->type >= SC_CARD_TYPE_DNIE_BASE+1000) { + fprintf(stderr, "Card type %X: not a DNIe card\n", card->type); + err = -1; goto dnie_tool_end; } if ( opt_pin ) { + int tries_left; + /* verify */ r = sc_verify(card, SC_AC_CHV, 0, (u8*)opt_pin, strlen(opt_pin), &tries_left); @@ -191,44 +182,44 @@ int main(int argc, char* argv[]) } } - if (opt_operation==0) { - fprintf(stderr,"Error: No operation specified"); - err = -1; - goto dnie_tool_end; - } - if (opt_operation & 0x0f) { + if (opt_operation & (OP_GET_DATA | OP_GET_IDESP | OP_GET_VERSION | OP_GET_SERIALNR)) { r = sc_card_ctl(card, SC_CARDCTL_DNIE_GET_INFO, data); if ( r != SC_SUCCESS ) { fprintf(stderr, "Error: Get info failed: %s\n", sc_strerror(r)); err = -1; goto dnie_tool_end; } - } - if (opt_operation & OP_GET_DATA) { - printf("DNIe Number: %s\n",data[0]); - printf("SurName: %s\n",data[1]); - printf("Name: %s\n",data[2]); - } - if (opt_operation & OP_GET_IDESP) { - if (data[3]==NULL) - printf("IDESP: (No disponible)\n"); - else printf("IDESP: %s\n",data[3]); - } - if (opt_operation & OP_GET_VERSION) { - if (data[4]==NULL) - printf("DNIe Version: (No disponible)\n"); - else printf("DNIe Version: %s\n",data[4]); - } - if (opt_operation & OP_GET_SERIALNR) { - r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); - if ( r != SC_SUCCESS ) { - fprintf(stderr,"Error: Get serial failed: %s\n",sc_strerror(r)); - err = -1; - goto dnie_tool_end; + + if (opt_operation & OP_GET_DATA) { + printf("DNIe Number: %s\n",data[0]); + printf("Surname: %s\n",data[1]); + printf("Name: %s\n",data[2]); + } + if (opt_operation & OP_GET_IDESP) { + if (data[3]==NULL) + printf("IDESP: (Not available)\n"); + else printf("IDESP: %s\n",data[3]); } - printf("Serial number: "); - util_hex_dump(stdout, serial.value, serial.len, NULL); - putchar('\n'); + if (opt_operation & OP_GET_VERSION) { + if (data[4]==NULL) + printf("DNIe Version: (Not available)\n"); + else printf("DNIe Version: %s\n",data[4]); + } + if (opt_operation & OP_GET_SERIALNR) { + r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); + if ( r != SC_SUCCESS ) { + fprintf(stderr,"Error: Get serial failed: %s\n",sc_strerror(r)); + err = -1; + goto dnie_tool_end; + } + printf("Serial number: "); + util_hex_dump(stdout, serial.value, serial.len, NULL); + putchar('\n'); + } + } + else { + fprintf(stderr,"Error: No operation specified"); + err = -1; } dnie_tool_end: @@ -237,8 +228,7 @@ int main(int argc, char* argv[]) sc_unlock(card); sc_disconnect_card(card); } - if (ctx) - sc_release_context(ctx); + sc_release_context(ctx); return err; } diff --git a/src/tools/dtrust-tool.c b/src/tools/dtrust-tool.c new file mode 100644 index 0000000000..e6dee122fc --- /dev/null +++ b/src/tools/dtrust-tool.c @@ -0,0 +1,1126 @@ +/* + * dtrust-tool.c: tool for D-Trust cards + * + * Copyright (C) 2024 mario.haustein@hrz.tu-chemnitz.de + * + * 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 + */ + +#include "config.h" + +#include +#include + +#include "libopensc/opensc.h" + +#include "libopensc/card-dtrust.h" +#include "libopensc/cards.h" +#include "libopensc/errors.h" + +#include "sm/sm-eac.h" +#include "util.h" + +static const char *app_name = "dtrust-tool"; + +enum { + OPT_CAN_VERIFY = 0x100, + OPT_CAN_ENTER, + OPT_CHANGE, + OPT_VERIFY, + OPT_RESUME, + OPT_UNBLOCK, +}; + +// clang-format off +static const struct option options[] = { + {"reader", 1, NULL, 'r'}, + {"wait", 0, NULL, 'w'}, + {"verify-can", 0, NULL, OPT_CAN_VERIFY}, + {"enter-can", 0, NULL, OPT_CAN_ENTER}, + {"pin-status", 0, NULL, 's'}, + {"check-transport-protection", 0, NULL, 'c'}, + {"unlock-transport-protection", 0, NULL, 'u'}, + {"change-pin", 1, NULL, OPT_CHANGE}, + {"change-verify", 1, NULL, OPT_VERIFY}, + {"resume-pin", 1, NULL, OPT_RESUME}, + {"unblock-pin", 1, NULL, OPT_UNBLOCK}, + {"help", 0, NULL, 'h'}, + {"verbose", 0, NULL, 'v'}, + {NULL, 0, NULL, 0} +}; + +static const char *option_help[] = { + "Uses reader number [0]", + "Wait for card insertion", + "Verify Card Access Number (CAN)", + "Enter CAN explicitly", + "Show PIN status", + "Check transport protection", + "Unlock transport protection", + "Change PIN", + "Verification PIN ID for PIN change", + "Resume suspended PIN", + "Unblock blocked PIN", + "This message", + "Verbose operation, may be used several times", +}; +// clang-format on + +static const char *opt_reader = NULL; +static int opt_wait = 0, verbose = 0; +static unsigned char opt_can_verify = 0; +static unsigned char opt_can_enter = 0; +static int opt_status = 0; +static int opt_check = 0; +static int opt_unlock = 0; +static const char *opt_change = NULL; +static const char *opt_verify = NULL; +static const char *opt_resume = NULL; +static const char *opt_unblock = NULL; + +int +get_pin(char **pin, const char *label, unsigned char check) +{ + int r; + char *pin2 = NULL; + size_t len1 = 0; + size_t len2 = 0; + + r = -1; + + if (pin == NULL) + return -1; + + *pin = NULL; + + printf("Enter %s:", label); + r = util_getpass(pin, &len1, stdin); + if (r < 0 || *pin == NULL) { + fprintf(stderr, "Unable to get PIN"); + goto fail; + } + + if (!check) + return 0; + + printf("Enter %s again:", label); + r = util_getpass(&pin2, &len2, stdin); + if (r < 0 || pin2 == NULL) { + fprintf(stderr, "Unable to get PIN"); + goto fail; + } + + r = strcmp(*pin, pin2); + if (r) + fprintf(stderr, "PINs doesn't match.\n"); + + /* Free repeated PIN in any case. */ + if (pin2 != NULL) { + sc_mem_clear(pin2, len2); + free(pin2); + } + + if (r == 0) + return 0; + +fail: + /* Free PIN only in case of an error. */ + if (*pin != NULL) { + sc_mem_clear(*pin, len1); + free(*pin); + *pin = NULL; + } + + return -1; +} + +int +parse_pin(sc_card_t *card, const char *pinstr, const char *label, unsigned char *require_can) +{ + const char *valid = NULL; + + switch (card->type) { + case SC_CARD_TYPE_DTRUST_V4_1_STD: + case SC_CARD_TYPE_DTRUST_V4_1_MULTI: + case SC_CARD_TYPE_DTRUST_V4_1_M100: + case SC_CARD_TYPE_DTRUST_V4_4_STD: + case SC_CARD_TYPE_DTRUST_V4_4_MULTI: + valid = "PIN.CH, PUK.CH, PIN.T, PIN.QES"; + + if (!strcasecmp(pinstr, "PIN.CH")) + return DTRUST4_PIN_ID_PIN_CH; + if (!strcasecmp(pinstr, "PUK.CH")) + return DTRUST4_PIN_ID_PUK_CH; + if (!strcasecmp(pinstr, "PIN.T")) + return DTRUST4_PIN_ID_PIN_T; + if (!strcasecmp(pinstr, "PIN.QES")) + return DTRUST4_PIN_ID_QES; + break; + + case SC_CARD_TYPE_DTRUST_V5_1_STD: + case SC_CARD_TYPE_DTRUST_V5_1_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_M100: + valid = "PUK.CH, PIN.T, PIN.T.AUT, PIN.QES, PIN.AUT"; + + if (!strcasecmp(pinstr, "PIN.T.AUT")) { + return DTRUST5_PIN_ID_PIN_T_AUT; + } + if (!strcasecmp(pinstr, "PIN.AUT")) { + if (require_can != NULL) + *require_can = 1; + return DTRUST5_PIN_ID_AUT; + } + /* fall through */ + + case SC_CARD_TYPE_DTRUST_V5_4_STD: + case SC_CARD_TYPE_DTRUST_V5_4_MULTI: + if (valid == NULL) + valid = "PUK.CH, PIN.T, PIN.QES"; + + if (!strcasecmp(pinstr, "PUK.CH")) + return PACE_PIN_ID_PUK; + if (!strcasecmp(pinstr, "PIN.T")) + return DTRUST5_PIN_ID_PIN_T; + if (!strcasecmp(pinstr, "PIN.QES")) { + if (require_can != NULL) + *require_can = 1; + return DTRUST5_PIN_ID_QES; + } + break; + } + + fprintf(stderr, "%s PIN '%s' is invalid. Choose one from: %s\n", label, pinstr, valid); + + return -1; +} + +void +pin_status(sc_card_t *card, int ref, const char *pin_label, unsigned char transport) +{ + int r; + struct sc_pin_cmd_data data; + + memset(&data, 0, sizeof(data)); + data.cmd = SC_PIN_CMD_GET_INFO; + data.pin_type = SC_AC_CHV; + data.pin_reference = ref; + + r = sc_pin_cmd(card, &data); + + if (r == SC_SUCCESS) { + if (data.pin1.tries_left < 0) + printf("%s: usable\n", pin_label); + else + printf("%s: usable (%d tries left)\n", pin_label, data.pin1.tries_left); + } else if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) + printf("%s: not usable (transport protection still in force)\n", pin_label); + else if (r == SC_ERROR_AUTH_METHOD_BLOCKED) + printf("%s: blocked (use PUK to unblock PIN)\n", pin_label); + else if (r == SC_ERROR_REF_DATA_NOT_USABLE) { + if (transport) + printf("%s: not usable (transport protection already broken)\n", pin_label); + else + printf("%s: not usable\n", pin_label); + } else + fprintf(stderr, "%s: status query failed (%s).\n", pin_label, sc_strerror(r)); +} + +int +check_transport_protection(sc_card_t *card, u8 ref, const char *pin_label) +{ + int r; + struct sc_apdu apdu; + u8 buf[6]; + u8 prot_intact[6] = {0xE3, 0x04, 0x90, 0x02, 0x00, 0x01}; + u8 prot_broken[6] = {0xE3, 0x04, 0x90, 0x02, 0x00, 0x00}; + + r = sc_select_file(card, sc_get_mf_path(), NULL); + if (r != SC_SUCCESS) { + fprintf(stderr, "Check transport protection of %s: Unable to select master file (%s)\n", pin_label, sc_strerror(r)); + return -1; + } + + sc_format_apdu_ex(&apdu, 0x80, 0xCA, 0x00, ref, NULL, 0, buf, sizeof(buf)); + + r = sc_transmit_apdu(card, &apdu); + if (r != SC_SUCCESS) { + fprintf(stderr, "Check transport protection of %s: APDU transmit failed (%s)\n", pin_label, sc_strerror(r)); + return -1; + } + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r != SC_SUCCESS) { + /* Pin use counter may only be read out, if the retry counter + * is on its maximum value. In case of an error, the PIN has to + * be verified successfully first. */ + fprintf(stderr, "Check transport protection of %s: GET_DATA failed (%s)\n", pin_label, sc_strerror(r)); + return -1; + } + + if (apdu.resplen == sizeof(prot_intact) && !memcmp(apdu.resp, prot_intact, 6)) { + printf("Transport protection of %s is still intact.\n", pin_label); + return 0; + } else if (apdu.resplen == sizeof(prot_broken) && !memcmp(apdu.resp, prot_broken, 6)) { + printf("Transport protection of %s is broken.\n", pin_label); + return 1; + } + + fprintf(stderr, "Check transport protection of %s: illegal response: ", pin_label); + util_hex_dump(stderr, apdu.resp, apdu.resplen, " "); + fprintf(stderr, "\n"); + + return -1; +} + +void +unlock_transport_protection4(sc_card_t *card) +{ + struct sc_pin_cmd_data data; + int r; + char *tpin = NULL; + char *qespin = NULL; + + memset(&data, 0, sizeof(data)); + data.cmd = SC_PIN_CMD_CHANGE; + data.pin_type = SC_AC_CHV; + data.pin_reference = DTRUST4_PIN_ID_QES; + data.pin1.min_length = 5; + data.pin1.max_length = 5; + data.pin2.min_length = 6; + data.pin2.max_length = 12; + + if (card->reader->capabilities & SC_READER_CAP_PIN_PAD) { + printf("Please enter PINs on the reader's pin pad.\n"); + data.pin1.prompt = "Enter Transport PIN"; + data.pin2.prompt = "Enter Signature PIN"; + data.flags |= SC_PIN_CMD_USE_PINPAD; + } else { + r = get_pin(&tpin, "Transport PIN", 0); + if (r < 0) + goto fail; + + r = get_pin(&qespin, "new Signature PIN", 1); + if (r < 0) + goto fail; + + data.pin1.data = (u8 *)tpin; + data.pin1.len = strlen(tpin); + data.pin2.data = (u8 *)qespin; + data.pin2.len = strlen(qespin); + } + + r = sc_pin_cmd(card, &data); + + if (r == SC_SUCCESS) + printf("Transport protection removed. You can now use your Signature PIN.\n"); + else if (r == SC_ERROR_PIN_CODE_INCORRECT) + printf("Wrong pin. %d attempts left.\n", data.pin1.tries_left); + else + printf("Can't change pin: %s\n", sc_strerror(r)); + +fail: + if (qespin != NULL) { + sc_mem_clear(qespin, strlen(qespin)); + free(qespin); + } + + if (tpin != NULL) { + sc_mem_clear(tpin, strlen(tpin)); + free(tpin); + } +} + +void +unlock_transport_protection5(sc_card_t *card, int ref_pace, int ref_pin, const char *pathstr, const char *pin_label) +{ + int r; + sc_path_t path; + struct sc_pin_cmd_data data; + char *tpin = NULL; + char *newpin = NULL; + + printf("Unlocking %s\n", pin_label); + + /* Query all PINs at once */ + if (!(card->reader->capabilities & SC_READER_CAP_PACE_GENERIC)) { + r = get_pin(&tpin, "Transport PIN", 0); + if (r < 0) + goto fail; + } + + if (!(card->reader->capabilities & SC_READER_CAP_PIN_PAD)) { + r = get_pin(&newpin, pin_label, 1); + if (r < 0) + goto fail; + + if (strlen(newpin) != 8) { + fprintf(stderr, "Error. New PIN must be exactly 8 characters long.\n"); + goto fail; + } + } + + /* Authenticate via PACE */ + memset(&data, 0, sizeof(data)); + data.cmd = SC_PIN_CMD_VERIFY; + data.pin_type = SC_AC_CHV; + data.pin_reference = ref_pace; + + if (card->reader->capabilities & SC_READER_CAP_PACE_GENERIC) + printf("Enter Transport PIN on the readers pin pad now.\n"); + else { + data.pin1.data = (u8 *)tpin; + data.pin1.len = strlen(tpin); + } + + r = sc_pin_cmd(card, &data); + if (r) { + fprintf(stderr, "Error verifying Transport PIN: %s\n", sc_strerror(r)); + if (data.pin1.tries_left >= 0) + fprintf(stderr, "%d attempts left.\n", data.pin1.tries_left); + goto fail; + } + + /* Select application of the PIN */ + sc_format_path(pathstr, &path); + r = sc_select_file(card, &path, NULL); + if (r) + goto fail; + + /* Change PIN */ + memset(&data, 0, sizeof(data)); + data.cmd = SC_PIN_CMD_CHANGE; + data.flags = SC_PIN_CMD_IMPLICIT_CHANGE; + data.pin_type = SC_AC_CHV; + data.pin_reference = ref_pin; + data.pin2.min_length = 8; + data.pin2.max_length = 8; + + if (card->reader->capabilities & SC_READER_CAP_PIN_PAD) { + printf("Enter new %s on the readers pin pad now.\n", pin_label); + data.pin2.prompt = pin_label; + data.flags |= SC_PIN_CMD_USE_PINPAD; + } else { + data.pin2.data = (u8 *)newpin; + data.pin2.len = strlen(newpin); + } + + /* We only have one chance to set the new PIN. Once the Transport PIN + * is verified, it is not usable anymore. For pin pad readers we + * continue as long as the new PIN is set successfully or the user + * aborts the program and renders its card unusable as a consequence. */ + do { + r = sc_pin_cmd(card, &data); + if (r == SC_SUCCESS) { + printf("Transport protection removed. You can now use your %s.\n", pin_label); + break; + } + + printf("Can't change pin: %s\n", sc_strerror(r)); + } while (card->reader->capabilities & SC_READER_CAP_PIN_PAD); + +fail: + if (newpin != NULL) { + sc_mem_clear(newpin, strlen(newpin)); + free(newpin); + } + + if (tpin != NULL) { + sc_mem_clear(tpin, strlen(tpin)); + free(tpin); + } +} + +void +change_pin(sc_card_t *card, int ref_verify, int ref_change) +{ + struct sc_pin_cmd_data data_verify, data_change; + const char *pathstr = "3F00"; + const char *oldstr = "Old PIN"; + const char *newstr = "New PIN"; + unsigned char pace = 0; + char *oldpin = NULL; + char *newpin = NULL; + sc_path_t path; + int r; + + memset(&data_verify, 0, sizeof(struct sc_pin_cmd_data)); + memset(&data_change, 0, sizeof(struct sc_pin_cmd_data)); + + data_verify.cmd = SC_PIN_CMD_VERIFY; + data_verify.pin_type = SC_AC_CHV; + data_verify.pin_reference = ref_verify; + + data_change.cmd = SC_PIN_CMD_CHANGE; + data_change.flags |= SC_PIN_CMD_IMPLICIT_CHANGE; + data_change.pin_type = SC_AC_CHV; + data_change.pin_reference = ref_change; + + switch (card->type) { + case SC_CARD_TYPE_DTRUST_V4_1_STD: + case SC_CARD_TYPE_DTRUST_V4_1_MULTI: + case SC_CARD_TYPE_DTRUST_V4_1_M100: + case SC_CARD_TYPE_DTRUST_V4_4_STD: + case SC_CARD_TYPE_DTRUST_V4_4_MULTI: + data_verify.pin1.min_length = ref_verify == DTRUST4_PIN_ID_PUK_CH ? 8 : 6; + data_verify.pin1.max_length = 12; + data_change.pin2.min_length = ref_change == DTRUST4_PIN_ID_PUK_CH ? 8 : 6; + data_change.pin2.max_length = 12; + + if (ref_change == DTRUST4_PIN_ID_QES) + pathstr = "3F000101"; + + if (ref_change != DTRUST4_PIN_ID_PIN_CH && + ref_change != DTRUST4_PIN_ID_PUK_CH && + ref_change != DTRUST4_PIN_ID_QES) { + fprintf(stderr, "Invalid change PIN. Only PIN.CH, PUK.CH or PIN.QES may be changed.\n"); + return; + } + + /* Every PIN can change itself */ + if (ref_verify == ref_change) + break; + + /* PUK can change cardholder PIN */ + if (ref_verify == DTRUST4_PIN_ID_PUK_CH && ref_change == DTRUST4_PIN_ID_PIN_CH) { + oldstr = "Cardholder PIN"; + break; + } + + fprintf(stderr, "Invalid verification PIN. PINs can only changed by itself.\n"); + fprintf(stderr, "Additionally the cardholder PIN may by changed by the PUK.\n"); + return; + + case SC_CARD_TYPE_DTRUST_V5_1_STD: + case SC_CARD_TYPE_DTRUST_V5_1_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_M100: + case SC_CARD_TYPE_DTRUST_V5_4_STD: + case SC_CARD_TYPE_DTRUST_V5_4_MULTI: + data_verify.pin1.min_length = 8; + data_verify.pin1.max_length = 8; + data_change.pin2.min_length = 8; + data_change.pin2.max_length = 8; + + if (ref_change == DTRUST5_PIN_ID_QES) + pathstr = "3F000101"; + else if (ref_change == DTRUST5_PIN_ID_AUT) + pathstr = "3F000102"; + + if (ref_change != PACE_PIN_ID_PUK && + ref_change != DTRUST5_PIN_ID_QES && + ref_change != DTRUST5_PIN_ID_AUT) { + fprintf(stderr, "Invalid change PIN. Only PUK.CH, PIN.QES or PIN.AUT may be changed.\n"); + return; + } + + /* PUK has to be verified via PACE */ + if (ref_verify == PACE_PIN_ID_PUK) + pace = 1; + + /* Every PIN can change itself */ + if (ref_verify == ref_change) + break; + + fprintf(stderr, "Invalid verification PIN. PINs can only changed by itself.\n"); + return; + + default: + return; + } + + if ((card->reader->capabilities & SC_READER_CAP_PIN_PAD) && !pace) { + data_verify.flags |= SC_PIN_CMD_USE_PINPAD; + } else if (!(card->reader->capabilities & SC_READER_CAP_PACE_GENERIC) || !pace) { + r = get_pin(&oldpin, oldstr, 0); + if (r < 0) + goto fail; + + data_verify.pin1.data = (const unsigned char *)oldpin; + data_verify.pin1.len = strlen(oldpin); + } + + if (card->reader->capabilities & SC_READER_CAP_PIN_PAD) { + data_change.flags |= SC_PIN_CMD_USE_PINPAD; + } else { + r = get_pin(&newpin, newstr, 1); + if (r < 0) + goto fail; + + data_change.pin2.data = (const unsigned char *)newpin; + data_change.pin2.len = strlen(newpin); + } + + sc_format_path(pathstr, &path); + + r = sc_select_file(card, pace ? sc_get_mf_path() : &path, NULL); + if (r) { + fprintf(stderr, "Error selecting application: %s\n", sc_strerror(r)); + goto fail; + } + + r = sc_pin_cmd(card, &data_verify); + if (r) { + fprintf(stderr, "Error verifying PIN: %s\n", sc_strerror(r)); + if (data_verify.pin1.tries_left >= 0) + fprintf(stderr, "%d attempts left.\n", data_verify.pin1.tries_left); + goto fail; + } + + if (pace) { + r = sc_select_file(card, &path, NULL); + if (r) { + fprintf(stderr, "Error selecting application: %s\n", sc_strerror(r)); + goto fail; + } + } + + r = sc_pin_cmd(card, &data_change); + if (r) { + fprintf(stderr, "Error changing PIN: %s\n", sc_strerror(r)); + goto fail; + } + +fail: + free(oldpin); + free(newpin); +} + +void +resume_pin(sc_card_t *card, int ref_pin) +{ + struct sc_pin_cmd_data data; + char *pin = NULL; + int r; + + memset(&data, 0, sizeof(struct sc_pin_cmd_data)); + + data.cmd = SC_PIN_CMD_VERIFY; + data.pin_type = SC_AC_CHV; + data.pin_reference = ref_pin; + + switch (card->type) { + case SC_CARD_TYPE_DTRUST_V5_1_STD: + case SC_CARD_TYPE_DTRUST_V5_1_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_M100: + case SC_CARD_TYPE_DTRUST_V5_4_STD: + case SC_CARD_TYPE_DTRUST_V5_4_MULTI: + data.pin1.min_length = 8; + data.pin1.max_length = 8; + + if (ref_pin == PACE_PIN_ID_PUK) + break; + + /* Resuming a transport PIN leads to decreasing its use + * counter. Not performing the signature PIN changing procedure + * irectly after transport PIN verification would render the + * card useless. Thus we enforce the user to resume the PIN + * during the regular unlock procedure. */ + fprintf(stderr, "Invalid PIN to resume. Only the PUK can be resumed with this command.\n"); + fprintf(stderr, "To resume a transport PIN call this tool with --can and --unlock-transport-protection parameter.\n"); + return; + + default: + fprintf(stderr, "This card does not support PINs which can be resumed.\n"); + return; + } + + /* Suspended PIN always require a PACE authentication. */ + if (!(card->reader->capabilities & SC_READER_CAP_PACE_GENERIC)) { + r = get_pin(&pin, "PIN to resume", 0); + if (r < 0) + goto fail; + + data.pin1.data = (const unsigned char *)pin; + data.pin1.len = strlen(pin); + } + + /* CAN was already verified by the caller at this point. We can + * directly verify the resumed PIN. */ + + r = sc_select_file(card, sc_get_mf_path(), NULL); + if (r) { + fprintf(stderr, "Error selecting master application: %s\n", sc_strerror(r)); + goto fail; + } + + r = sc_pin_cmd(card, &data); + if (r) { + fprintf(stderr, "Error resuming PIN: %s\n", sc_strerror(r)); + goto fail; + } + +fail: + free(pin); +} + +void +unblock_pin(sc_card_t *card, int ref_pin) +{ + struct sc_pin_cmd_data data_verify, data_unblock; + const char *pathstr = "3F00"; + unsigned char pace = 0; + char *puk = NULL; + sc_path_t path; + int r; + + memset(&data_verify, 0, sizeof(struct sc_pin_cmd_data)); + memset(&data_unblock, 0, sizeof(struct sc_pin_cmd_data)); + + data_verify.cmd = SC_PIN_CMD_VERIFY; + data_verify.pin_type = SC_AC_CHV; + + data_unblock.cmd = SC_PIN_CMD_UNBLOCK; + data_unblock.pin_type = SC_AC_CHV; + data_unblock.pin_reference = ref_pin; + + switch (card->type) { + case SC_CARD_TYPE_DTRUST_V4_1_STD: + case SC_CARD_TYPE_DTRUST_V4_1_MULTI: + case SC_CARD_TYPE_DTRUST_V4_1_M100: + case SC_CARD_TYPE_DTRUST_V4_4_STD: + case SC_CARD_TYPE_DTRUST_V4_4_MULTI: + data_verify.pin_reference = DTRUST4_PIN_ID_PUK_CH; + data_verify.pin1.min_length = 8; + data_verify.pin1.max_length = 12; + + if (ref_pin == DTRUST4_PIN_ID_QES) + pathstr = "3F000101"; + + if (ref_pin == DTRUST4_PIN_ID_PIN_CH || + ref_pin == DTRUST4_PIN_ID_PIN_T || + ref_pin == DTRUST4_PIN_ID_QES) { + break; + } + + fprintf(stderr, "Invalid unblock PIN. Only PIN.CH, PIN.T or PIN.QES may be unblocked.\n"); + return; + + case SC_CARD_TYPE_DTRUST_V5_1_STD: + case SC_CARD_TYPE_DTRUST_V5_1_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_M100: + case SC_CARD_TYPE_DTRUST_V5_4_STD: + case SC_CARD_TYPE_DTRUST_V5_4_MULTI: + pace = 1; + data_verify.pin_reference = PACE_PIN_ID_PUK; + data_verify.pin1.min_length = 8; + data_verify.pin1.max_length = 8; + + if (ref_pin == DTRUST5_PIN_ID_QES) + pathstr = "3F000101"; + else if (ref_pin == DTRUST5_PIN_ID_AUT) + pathstr = "3F000102"; + + if (ref_pin == DTRUST5_PIN_ID_PIN_T || + ref_pin == DTRUST5_PIN_ID_PIN_T_AUT || + ref_pin == DTRUST5_PIN_ID_QES || + ref_pin == DTRUST5_PIN_ID_AUT) { + break; + } + + fprintf(stderr, "Invalid unblock PIN. Only PIN.T, PIN.T.AUT, PIN.QES or PIN.AUT may be unblocked.\n"); + return; + + default: + return; + } + + if ((card->reader->capabilities & SC_READER_CAP_PIN_PAD) && !pace) { + data_verify.flags |= SC_PIN_CMD_USE_PINPAD; + } else if (!(card->reader->capabilities & SC_READER_CAP_PACE_GENERIC) || !pace) { + r = get_pin(&puk, "PUK", 0); + if (r < 0) + goto fail; + + data_verify.pin1.data = (const unsigned char *)puk; + data_verify.pin1.len = strlen(puk); + } + + sc_format_path(pathstr, &path); + + r = sc_select_file(card, pace ? sc_get_mf_path() : &path, NULL); + if (r) { + fprintf(stderr, "Error selecting application: %s\n", sc_strerror(r)); + goto fail; + } + + r = sc_pin_cmd(card, &data_verify); + if (r) { + fprintf(stderr, "Error verifying PUK: %s\n", sc_strerror(r)); + if (data_verify.pin1.tries_left >= 0) + fprintf(stderr, "%d attempts left.\n", data_verify.pin1.tries_left); + goto fail; + } + + if (!pace) { + r = sc_select_file(card, &path, NULL); + if (r) { + fprintf(stderr, "Error selecting application: %s\n", sc_strerror(r)); + goto fail; + } + } + + r = sc_pin_cmd(card, &data_unblock); + if (r) { + fprintf(stderr, "Error unblocking PIN: %s\n", sc_strerror(r)); + goto fail; + } + +fail: + free(puk); +} + +int +main(int argc, char *argv[]) +{ + int r, c, long_optind = 0; + char *can = NULL; + sc_context_param_t ctx_param; + sc_card_t *card = NULL; + sc_context_t *ctx = NULL; + int pin_change = -1; + int pin_verify = -1; + int pin_resume = -1; + int pin_unblock = -1; + sc_path_t path; + + while (1) { + c = getopt_long(argc, argv, "r:wscuhv", options, &long_optind); + + if (c == -1) + break; + + if (c == '?' || c == 'h') + util_print_usage_and_die(app_name, options, option_help, NULL); + + switch (c) { + case 'r': + opt_reader = optarg; + break; + case 'w': + opt_wait = 1; + break; + case OPT_CAN_VERIFY: + opt_can_verify = 1; + break; + case OPT_CAN_ENTER: + opt_can_enter = 1; + break; + case 's': + opt_status = 1; + break; + case 'c': + opt_check = 1; + break; + case 'u': + opt_unlock = 1; + break; + case OPT_CHANGE: + opt_change = optarg; + break; + case OPT_VERIFY: + opt_verify = optarg; + break; + case OPT_RESUME: + opt_resume = optarg; + break; + case OPT_UNBLOCK: + opt_unblock = optarg; + break; + case 'v': + verbose++; + break; + } + } + + memset(&ctx_param, 0, sizeof(ctx_param)); + ctx_param.ver = 0; + ctx_param.app_name = argv[0]; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; + + r = sc_context_create(&ctx, &ctx_param); + if (r) { + fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); + return 1; + } + + r = sc_set_card_driver(ctx, "dtrust"); + if (r) { + fprintf(stderr, "Driver 'dtrust' not found!\n"); + goto out; + } + + r = util_connect_card(ctx, &card, opt_reader, opt_wait); + if (r) + goto out; + + if (opt_status || opt_check) + opt_can_verify = 1; + + if (opt_change != NULL) { + pin_change = parse_pin(card, opt_change, "Change", opt_verify == NULL ? &opt_can_verify : NULL); + pin_verify = pin_change; + if (pin_change < 0) + goto out; + } + + if (opt_verify != NULL) { + pin_verify = parse_pin(card, opt_verify, "Verification", &opt_can_verify); + if (pin_verify < 0) + goto out; + } + + if (opt_resume != NULL) { + opt_can_verify = 1; + pin_resume = parse_pin(card, opt_resume, "Resume", NULL); + if (pin_resume < 0) + goto out; + } + + if (opt_unblock != NULL) { + pin_unblock = parse_pin(card, opt_unblock, "Unblock", NULL); + if (pin_unblock < 0) + goto out; + } + + /* D-Trust Card 5 requires PACE authentication with CAN */ + if (opt_can_verify && + card->type >= SC_CARD_TYPE_DTRUST_V5_1_STD && + card->type <= SC_CARD_TYPE_DTRUST_V5_4_MULTI) { + struct sc_pin_cmd_data data; + + memset(&data, 0, sizeof(data)); + data.cmd = SC_PIN_CMD_VERIFY; + data.pin_type = SC_AC_CHV; + data.pin_reference = PACE_PIN_ID_CAN; + + if (opt_can_enter) { + /* To determine whether we need to query for the CAN, + * we first need to check the card type and thus + * connect to the card. The Windows driver releases + * the card lock after 5 seconds of inactivity. Thus + * we release the card on our own, wait for the user + * input, and finally lock the card again to circumvent + * this timeout. */ + sc_unlock(card); + + r = get_pin(&can, "CAN", 0); + if (r < 0) + goto out; + + r = sc_lock(card); + if (r < 0) { + fprintf(stderr, "Failed to lock card.\n"); + goto out; + } + + data.pin1.data = (const unsigned char *)can; + data.pin1.len = strlen(can); + } + + r = sc_select_file(card, sc_get_mf_path(), NULL); + if (r) + goto out; + + r = sc_pin_cmd(card, &data); + if (r) { + fprintf(stderr, "Error verifying CAN.\n"); + goto out; + } + } + + if (opt_status) { + switch (card->type) { + case SC_CARD_TYPE_DTRUST_V4_1_STD: + case SC_CARD_TYPE_DTRUST_V4_1_MULTI: + case SC_CARD_TYPE_DTRUST_V4_1_M100: + pin_status(card, DTRUST4_PIN_ID_PIN_CH, "Card Holder PIN", 0); + /* fall through */ + + case SC_CARD_TYPE_DTRUST_V4_4_STD: + case SC_CARD_TYPE_DTRUST_V4_4_MULTI: + /* We have to select the QES app to verify and change the Signature PIN. */ + sc_format_path("3F000101", &path); + r = sc_select_file(card, &path, NULL); + if (r) + goto out; + + pin_status(card, DTRUST4_PIN_ID_PUK_CH, "Card Holder PUK", 0); + pin_status(card, DTRUST4_PIN_ID_QES, "Signature PIN", 0); + + /* According to the spec, the local bit has to be set. */ + pin_status(card, 0x80 | DTRUST4_PIN_ID_PIN_T, "Transport PIN", 1); + break; + + case SC_CARD_TYPE_DTRUST_V5_1_STD: + case SC_CARD_TYPE_DTRUST_V5_1_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_M100: + r = sc_select_file(card, sc_get_mf_path(), NULL); + if (r) + goto out; + + pin_status(card, DTRUST5_PIN_ID_PIN_T_AUT, "Transport PIN (Authentication)", 1); + + /* We have to select the eSign app to verify and change the Authentication PIN. */ + sc_format_path("3F000102", &path); + r = sc_select_file(card, &path, NULL); + if (r) + goto out; + + pin_status(card, DTRUST5_PIN_ID_AUT, "Authentication PIN", 0); + /* fall through */ + + case SC_CARD_TYPE_DTRUST_V5_4_STD: + case SC_CARD_TYPE_DTRUST_V5_4_MULTI: + r = sc_select_file(card, sc_get_mf_path(), NULL); + if (r) + goto out; + + pin_status(card, PACE_PIN_ID_PUK, "Card Holder PUK", 0); + pin_status(card, DTRUST5_PIN_ID_PIN_T, "Transport PIN (Signature)", 1); + + /* We have to select the QES app to verify and change the Signature PIN. */ + sc_format_path("3F000101", &path); + r = sc_select_file(card, &path, NULL); + if (r) + goto out; + + pin_status(card, DTRUST5_PIN_ID_QES, "Signature PIN", 0); + break; + } + } + + if (opt_check) { + switch (card->type) { + case SC_CARD_TYPE_DTRUST_V4_1_STD: + case SC_CARD_TYPE_DTRUST_V4_1_MULTI: + case SC_CARD_TYPE_DTRUST_V4_1_M100: + case SC_CARD_TYPE_DTRUST_V4_4_STD: + case SC_CARD_TYPE_DTRUST_V4_4_MULTI: + check_transport_protection(card, DTRUST4_PIN_ID_PIN_T, "Signature PIN"); + break; + + case SC_CARD_TYPE_DTRUST_V5_1_STD: + case SC_CARD_TYPE_DTRUST_V5_1_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_M100: + check_transport_protection(card, DTRUST5_PIN_ID_PIN_T_AUT, "Authentication PIN"); + /* fall through */ + + case SC_CARD_TYPE_DTRUST_V5_4_STD: + case SC_CARD_TYPE_DTRUST_V5_4_MULTI: + check_transport_protection(card, DTRUST5_PIN_ID_PIN_T_AUT, "Signature PIN"); + break; + } + } + + if (opt_unlock) { + /* Warn the user he must not abort the unlocking process on + * pin pad readers, as the transport pin has already been used + * and there is no next attempt. */ + if (card->reader->capabilities & SC_READER_CAP_PIN_PAD && + card->type >= SC_CARD_TYPE_DTRUST_V5_1_STD && + card->type <= SC_CARD_TYPE_DTRUST_V5_4_MULTI) { + printf("\n"); + printf("CAUTION.\n"); + printf("\n"); + printf("You are about to remove the transport protection. After entering the transport\n"); + printf("PIN, don't abort the program! Otherwise your card becomes irrecoverably unusable.\n"); + printf("In case of an error, continue to enter your PIN as long as you reader accepts\n"); + printf("it. The new PIN must be exactly 8 characters long.\n"); + printf("\n"); + printf("If in doubt, cancel now and try to unlock your card in a card reader without a\n"); + printf("pin pad. Then all, your inputs will be validated before unlocking the card.\n"); + printf("\n"); + printf("Enter 'yes' to continue.\n"); + +#ifndef _WIN32 + ssize_t ret; + char *str = NULL; + size_t len = 0; + + ret = getline(&str, &len, stdin); + if (ret >= 0) + ret = strcmp(str, "yes\n"); + free(str); + + if (ret) + goto out; +#else + char str[8]; + char *ret; + + ret = fgets(str, 8, stdin); + if (ret == NULL || strcmp(ret, "yes\n")) + goto out; +#endif + } + + switch (card->type) { + case SC_CARD_TYPE_DTRUST_V4_1_STD: + case SC_CARD_TYPE_DTRUST_V4_1_MULTI: + case SC_CARD_TYPE_DTRUST_V4_1_M100: + case SC_CARD_TYPE_DTRUST_V4_4_STD: + case SC_CARD_TYPE_DTRUST_V4_4_MULTI: + r = check_transport_protection(card, DTRUST4_PIN_ID_PIN_T, "Signature PIN"); + if (r) + printf("Cannot remove transport protection of Signature PIN.\n"); + else + unlock_transport_protection4(card); + break; + + case SC_CARD_TYPE_DTRUST_V5_1_STD: + case SC_CARD_TYPE_DTRUST_V5_1_MULTI: + case SC_CARD_TYPE_DTRUST_V5_1_M100: + r = check_transport_protection(card, DTRUST5_PIN_ID_PIN_T_AUT, "Authentication PIN"); + if (r) + printf("Cannot remove transport protection of Authentication PIN.\n"); + else { + unlock_transport_protection5(card, DTRUST5_PIN_ID_PIN_T_AUT, DTRUST5_PIN_ID_AUT, "3F000102", "Authentication PIN"); + } + /* fall through */ + + case SC_CARD_TYPE_DTRUST_V5_4_STD: + case SC_CARD_TYPE_DTRUST_V5_4_MULTI: + r = check_transport_protection(card, DTRUST5_PIN_ID_PIN_T, "Signature PIN"); + if (r) + printf("Cannot remove transport protection of Signature PIN.\n"); + else { + unlock_transport_protection5(card, DTRUST5_PIN_ID_PIN_T, DTRUST5_PIN_ID_QES, "3F000101", "Signature PIN"); + } + break; + } + } else if (opt_change != NULL) { + change_pin(card, pin_verify, pin_change); + } else if (opt_resume != NULL) { + resume_pin(card, pin_resume); + } else if (opt_unblock != NULL) { + unblock_pin(card, pin_unblock); + } + +out: + if (can != NULL) { + sc_mem_clear(can, strlen(can)); + free(can); + } + + if (card) { + sc_unlock(card); + sc_disconnect_card(card); + } + + sc_release_context(ctx); + + return EXIT_SUCCESS; +} diff --git a/src/tools/egk-tool-cmdline.c b/src/tools/egk-tool-cmdline.c new file mode 100644 index 0000000000..305b5691d2 --- /dev/null +++ b/src/tools/egk-tool-cmdline.c @@ -0,0 +1,710 @@ +/* + File autogenerated by gengetopt version 2.23 + generated with the following command: + /opt/homebrew/bin/gengetopt --file-name=egk-tool-cmdline --output-dir=. + + The developers of gengetopt consider the fixed text that goes in all + gengetopt output files to be in the public domain: + we make no copyright claims on it. +*/ + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#ifndef FIX_UNUSED +#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ +#endif + +#include + +#include "egk-tool-cmdline.h" + +const char *gengetopt_args_info_purpose = ""; + +const char *gengetopt_args_info_usage = "Usage: egk-tool [OPTION]..."; + +const char *gengetopt_args_info_versiontext = ""; + +const char *gengetopt_args_info_description = ""; + +const char *gengetopt_args_info_help[] = { + " -h, --help Print help and exit", + " -V, --version Print version and exit", + " -r, --reader=STRING Number of the reader to use. By default, the first\n reader with a present card is used. If the argument is\n an ATR, the reader with a matching card will be\n chosen.", + " -v, --verbose Use (several times) to be more verbose", + "\n'Gesundheitsanwendung', Health Care Application (HCA):", + " --pd Show 'Persönliche Versichertendaten' (XML)\n (default=off)", + " --vd Show 'Allgemeine Versichertendaten' (XML) (default=off)", + " --gvd Show 'Geschützte Versichertendaten' (XML)\n (default=off)", + " --vsd-status Show 'Versichertenstammdaten-Status' (default=off)", + "\nReport bugs to https://github.com/OpenSC/OpenSC/issues\n\nWritten by Frank Morgner ", + 0 +}; + +typedef enum {ARG_NO + , ARG_FLAG + , ARG_STRING +} cmdline_parser_arg_type; + +static +void clear_given (struct gengetopt_args_info *args_info); +static +void clear_args (struct gengetopt_args_info *args_info); + +static int +cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params, const char *additional_error); + +static int +cmdline_parser_required2 (struct gengetopt_args_info *args_info, const char *prog_name, const char *additional_error); + +static char * +gengetopt_strdup (const char *s); + +static +void clear_given (struct gengetopt_args_info *args_info) +{ + args_info->help_given = 0 ; + args_info->version_given = 0 ; + args_info->reader_given = 0 ; + args_info->verbose_given = 0 ; + args_info->pd_given = 0 ; + args_info->vd_given = 0 ; + args_info->gvd_given = 0 ; + args_info->vsd_status_given = 0 ; +} + +static +void clear_args (struct gengetopt_args_info *args_info) +{ + FIX_UNUSED (args_info); + args_info->reader_arg = NULL; + args_info->reader_orig = NULL; + args_info->pd_flag = 0; + args_info->vd_flag = 0; + args_info->gvd_flag = 0; + args_info->vsd_status_flag = 0; + +} + +static +void init_args_info(struct gengetopt_args_info *args_info) +{ + + + args_info->help_help = gengetopt_args_info_help[0] ; + args_info->version_help = gengetopt_args_info_help[1] ; + args_info->reader_help = gengetopt_args_info_help[2] ; + args_info->verbose_help = gengetopt_args_info_help[3] ; + args_info->verbose_min = 0; + args_info->verbose_max = 0; + args_info->pd_help = gengetopt_args_info_help[5] ; + args_info->vd_help = gengetopt_args_info_help[6] ; + args_info->gvd_help = gengetopt_args_info_help[7] ; + args_info->vsd_status_help = gengetopt_args_info_help[8] ; + +} + +void +cmdline_parser_print_version (void) +{ + printf ("%s %s\n", + (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE), + CMDLINE_PARSER_VERSION); + + if (strlen(gengetopt_args_info_versiontext) > 0) + printf("\n%s\n", gengetopt_args_info_versiontext); +} + +static void print_help_common(void) +{ + size_t len_purpose = strlen(gengetopt_args_info_purpose); + size_t len_usage = strlen(gengetopt_args_info_usage); + + if (len_usage > 0) { + printf("%s\n", gengetopt_args_info_usage); + } + if (len_purpose > 0) { + printf("%s\n", gengetopt_args_info_purpose); + } + + if (len_usage || len_purpose) { + printf("\n"); + } + + if (strlen(gengetopt_args_info_description) > 0) { + printf("%s\n\n", gengetopt_args_info_description); + } +} + +void +cmdline_parser_print_help (void) +{ + int i = 0; + print_help_common(); + while (gengetopt_args_info_help[i]) + printf("%s\n", gengetopt_args_info_help[i++]); +} + +void +cmdline_parser_init (struct gengetopt_args_info *args_info) +{ + clear_given (args_info); + clear_args (args_info); + init_args_info (args_info); +} + +void +cmdline_parser_params_init(struct cmdline_parser_params *params) +{ + if (params) + { + params->override = 0; + params->initialize = 1; + params->check_required = 1; + params->check_ambiguity = 0; + params->print_errors = 1; + } +} + +struct cmdline_parser_params * +cmdline_parser_params_create(void) +{ + struct cmdline_parser_params *params = + (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params)); + cmdline_parser_params_init(params); + return params; +} + +static void +free_string_field (char **s) +{ + if (*s) + { + free (*s); + *s = 0; + } +} + + +static void +cmdline_parser_release (struct gengetopt_args_info *args_info) +{ + + free_string_field (&(args_info->reader_arg)); + free_string_field (&(args_info->reader_orig)); + + + + clear_given (args_info); +} + + +static void +write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[]) +{ + FIX_UNUSED (values); + if (arg) { + fprintf(outfile, "%s=\"%s\"\n", opt, arg); + } else { + fprintf(outfile, "%s\n", opt); + } +} + +static void +write_multiple_into_file(FILE *outfile, int len, const char *opt, char **arg, const char *values[]) +{ + int i; + + for (i = 0; i < len; ++i) + write_into_file(outfile, opt, (arg ? arg[i] : 0), values); +} + +int +cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) +{ + int i = 0; + + if (!outfile) + { + fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE); + return EXIT_FAILURE; + } + + if (args_info->help_given) + write_into_file(outfile, "help", 0, 0 ); + if (args_info->version_given) + write_into_file(outfile, "version", 0, 0 ); + if (args_info->reader_given) + write_into_file(outfile, "reader", args_info->reader_orig, 0); + write_multiple_into_file(outfile, args_info->verbose_given, "verbose", 0, 0); + if (args_info->pd_given) + write_into_file(outfile, "pd", 0, 0 ); + if (args_info->vd_given) + write_into_file(outfile, "vd", 0, 0 ); + if (args_info->gvd_given) + write_into_file(outfile, "gvd", 0, 0 ); + if (args_info->vsd_status_given) + write_into_file(outfile, "vsd-status", 0, 0 ); + + + i = EXIT_SUCCESS; + return i; +} + +int +cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) +{ + FILE *outfile; + int i = 0; + + outfile = fopen(filename, "w"); + + if (!outfile) + { + fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); + return EXIT_FAILURE; + } + + i = cmdline_parser_dump(outfile, args_info); + fclose (outfile); + + return i; +} + +void +cmdline_parser_free (struct gengetopt_args_info *args_info) +{ + cmdline_parser_release (args_info); +} + +/** @brief replacement of strdup, which is not standard */ +char * +gengetopt_strdup (const char *s) +{ + char *result = 0; + if (!s) + return result; + + result = (char*)malloc(strlen(s) + 1); + if (result == (char*)0) + return (char*)0; + strcpy(result, s); + return result; +} + +static int +check_multiple_option_occurrences(const char *prog_name, unsigned int option_given, unsigned int min, unsigned int max, const char *option_desc); + +int +check_multiple_option_occurrences(const char *prog_name, unsigned int option_given, unsigned int min, unsigned int max, const char *option_desc) +{ + int error_occurred = 0; + + if (option_given && (min > 0 || max > 0)) + { + if (min > 0 && max > 0) + { + if (min == max) + { + /* specific occurrences */ + if (option_given != (unsigned int) min) + { + fprintf (stderr, "%s: %s option occurrences must be %d\n", + prog_name, option_desc, min); + error_occurred = 1; + } + } + else if (option_given < (unsigned int) min + || option_given > (unsigned int) max) + { + /* range occurrences */ + fprintf (stderr, "%s: %s option occurrences must be between %d and %d\n", + prog_name, option_desc, min, max); + error_occurred = 1; + } + } + else if (min > 0) + { + /* at least check */ + if (option_given < min) + { + fprintf (stderr, "%s: %s option occurrences must be at least %d\n", + prog_name, option_desc, min); + error_occurred = 1; + } + } + else if (max > 0) + { + /* at most check */ + if (option_given > max) + { + fprintf (stderr, "%s: %s option occurrences must be at most %d\n", + prog_name, option_desc, max); + error_occurred = 1; + } + } + } + + return error_occurred; +} +int +cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info) +{ + return cmdline_parser2 (argc, argv, args_info, 0, 1, 1); +} + +int +cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params) +{ + int result; + result = cmdline_parser_internal (argc, argv, args_info, params, 0); + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) +{ + int result; + struct cmdline_parser_params params; + + params.override = override; + params.initialize = initialize; + params.check_required = check_required; + params.check_ambiguity = 0; + params.print_errors = 1; + + result = cmdline_parser_internal (argc, argv, args_info, ¶ms, 0); + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name) +{ + int result = EXIT_SUCCESS; + + if (cmdline_parser_required2(args_info, prog_name, 0) > 0) + result = EXIT_FAILURE; + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser_required2 (struct gengetopt_args_info *args_info, const char *prog_name, const char *additional_error) +{ + int error_occurred = 0; + FIX_UNUSED (additional_error); + + /* checks for required options */ + if (check_multiple_option_occurrences(prog_name, args_info->verbose_given, args_info->verbose_min, args_info->verbose_max, "'--verbose' ('-v')")) + error_occurred = 1; + + + /* checks for dependences among options */ + + return error_occurred; +} + + +static char *package_name = 0; + +/** + * @brief updates an option + * @param field the generic pointer to the field to update + * @param orig_field the pointer to the orig field + * @param field_given the pointer to the number of occurrence of this option + * @param prev_given the pointer to the number of occurrence already seen + * @param value the argument for this option (if null no arg was specified) + * @param possible_values the possible values for this option (if specified) + * @param default_value the default value (in case the option only accepts fixed values) + * @param arg_type the type of this option + * @param check_ambiguity @see cmdline_parser_params.check_ambiguity + * @param override @see cmdline_parser_params.override + * @param no_free whether to free a possible previous value + * @param multiple_option whether this is a multiple option + * @param long_opt the corresponding long option + * @param short_opt the corresponding short option (or '-' if none) + * @param additional_error possible further error specification + */ +static +int update_arg(void *field, char **orig_field, + unsigned int *field_given, unsigned int *prev_given, + char *value, const char *possible_values[], + const char *default_value, + cmdline_parser_arg_type arg_type, + int check_ambiguity, int override, + int no_free, int multiple_option, + const char *long_opt, char short_opt, + const char *additional_error) +{ + char *stop_char = 0; + const char *val = value; + int found; + char **string_field; + FIX_UNUSED (field); + + stop_char = 0; + found = 0; + + if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given))) + { + if (short_opt != '-') + fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", + package_name, long_opt, short_opt, + (additional_error ? additional_error : "")); + else + fprintf (stderr, "%s: `--%s' option given more than once%s\n", + package_name, long_opt, + (additional_error ? additional_error : "")); + return 1; /* failure */ + } + + FIX_UNUSED (default_value); + + if (field_given && *field_given && ! override) + return 0; + if (prev_given) + (*prev_given)++; + if (field_given) + (*field_given)++; + if (possible_values) + val = possible_values[found]; + + switch(arg_type) { + case ARG_FLAG: + *((int *)field) = !*((int *)field); + break; + case ARG_STRING: + if (val) { + string_field = (char **)field; + if (!no_free && *string_field) + free (*string_field); /* free previous string */ + *string_field = gengetopt_strdup (val); + } + break; + default: + break; + }; + + FIX_UNUSED(stop_char); + + /* store the original value */ + switch(arg_type) { + case ARG_NO: + case ARG_FLAG: + break; + default: + if (value && orig_field) { + if (no_free) { + *orig_field = value; + } else { + if (*orig_field) + free (*orig_field); /* free previous string */ + *orig_field = gengetopt_strdup (value); + } + } + }; + + return 0; /* OK */ +} + + +int +cmdline_parser_internal ( + int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params, const char *additional_error) +{ + int c; /* Character of the parsed option. */ + + int error_occurred = 0; + struct gengetopt_args_info local_args_info; + + int override; + int initialize; + int check_required; + int check_ambiguity; + + package_name = argv[0]; + + /* TODO: Why is this here? It is not used anywhere. */ + override = params->override; + FIX_UNUSED(override); + + initialize = params->initialize; + check_required = params->check_required; + + /* TODO: Why is this here? It is not used anywhere. */ + check_ambiguity = params->check_ambiguity; + FIX_UNUSED(check_ambiguity); + + if (initialize) + cmdline_parser_init (args_info); + + cmdline_parser_init (&local_args_info); + + optarg = 0; + optind = 0; + opterr = params->print_errors; + optopt = '?'; + + while (1) + { + int option_index = 0; + + static struct option long_options[] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { "reader", 1, NULL, 'r' }, + { "verbose", 0, NULL, 'v' }, + { "pd", 0, NULL, 0 }, + { "vd", 0, NULL, 0 }, + { "gvd", 0, NULL, 0 }, + { "vsd-status", 0, NULL, 0 }, + { 0, 0, 0, 0 } + }; + + c = getopt_long (argc, argv, "hVr:v", long_options, &option_index); + + if (c == -1) break; /* Exit from `while (1)' loop. */ + + switch (c) + { + case 'h': /* Print help and exit. */ + cmdline_parser_print_help (); + cmdline_parser_free (&local_args_info); + exit (EXIT_SUCCESS); + + case 'V': /* Print version and exit. */ + cmdline_parser_print_version (); + cmdline_parser_free (&local_args_info); + exit (EXIT_SUCCESS); + + case 'r': /* Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen.. */ + + + if (update_arg( (void *)&(args_info->reader_arg), + &(args_info->reader_orig), &(args_info->reader_given), + &(local_args_info.reader_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "reader", 'r', + additional_error)) + goto failure; + + break; + case 'v': /* Use (several times) to be more verbose. */ + + local_args_info.verbose_given++; + + break; + + case 0: /* Long option with no short option */ + /* Show 'Persönliche Versichertendaten' (XML). */ + if (strcmp (long_options[option_index].name, "pd") == 0) + { + + + if (update_arg((void *)&(args_info->pd_flag), 0, &(args_info->pd_given), + &(local_args_info.pd_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "pd", '-', + additional_error)) + goto failure; + + } + /* Show 'Allgemeine Versichertendaten' (XML). */ + else if (strcmp (long_options[option_index].name, "vd") == 0) + { + + + if (update_arg((void *)&(args_info->vd_flag), 0, &(args_info->vd_given), + &(local_args_info.vd_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "vd", '-', + additional_error)) + goto failure; + + } + /* Show 'Geschützte Versichertendaten' (XML). */ + else if (strcmp (long_options[option_index].name, "gvd") == 0) + { + + + if (update_arg((void *)&(args_info->gvd_flag), 0, &(args_info->gvd_given), + &(local_args_info.gvd_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "gvd", '-', + additional_error)) + goto failure; + + } + /* Show 'Versichertenstammdaten-Status'. */ + else if (strcmp (long_options[option_index].name, "vsd-status") == 0) + { + + + if (update_arg((void *)&(args_info->vsd_status_flag), 0, &(args_info->vsd_status_given), + &(local_args_info.vsd_status_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "vsd-status", '-', + additional_error)) + goto failure; + + } + + break; + case '?': /* Invalid option. */ + /* `getopt_long' already printed an error message. */ + goto failure; + + default: /* bug: option not considered. */ + fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); + abort (); + } /* switch */ + } /* while */ + + + + args_info->verbose_given += local_args_info.verbose_given; + local_args_info.verbose_given = 0; + + if (check_required) + { + error_occurred += cmdline_parser_required2 (args_info, argv[0], additional_error); + } + + cmdline_parser_release (&local_args_info); + + if ( error_occurred ) + return (EXIT_FAILURE); + + return 0; + +failure: + + cmdline_parser_release (&local_args_info); + return (EXIT_FAILURE); +} +/* vim: set ft=c noet ts=8 sts=8 sw=8 tw=80 nojs spell : */ diff --git a/src/tools/egk-tool-cmdline.h b/src/tools/egk-tool-cmdline.h new file mode 100644 index 0000000000..ffbc9c8979 --- /dev/null +++ b/src/tools/egk-tool-cmdline.h @@ -0,0 +1,193 @@ +/** @file egk-tool-cmdline.h + * @brief The header file for the command line option parser + * generated by GNU Gengetopt version 2.23 + * http://www.gnu.org/software/gengetopt. + * DO NOT modify this file, since it can be overwritten + * @author GNU Gengetopt */ + +#ifndef EGK_TOOL_CMDLINE_H +#define EGK_TOOL_CMDLINE_H + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include /* for FILE */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef CMDLINE_PARSER_PACKAGE +/** @brief the program name (used for printing errors) */ +#define CMDLINE_PARSER_PACKAGE "egk-tool" +#endif + +#ifndef CMDLINE_PARSER_PACKAGE_NAME +/** @brief the complete program name (used for help and version) */ +#define CMDLINE_PARSER_PACKAGE_NAME "egk-tool" +#endif + +#ifndef CMDLINE_PARSER_VERSION +/** @brief the program version */ +#define CMDLINE_PARSER_VERSION VERSION +#endif + +/** @brief Where the command line options are stored */ +struct gengetopt_args_info +{ + const char *help_help; /**< @brief Print help and exit help description. */ + const char *version_help; /**< @brief Print version and exit help description. */ + char * reader_arg; /**< @brief Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen.. */ + char * reader_orig; /**< @brief Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen. original value given at command line. */ + const char *reader_help; /**< @brief Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen. help description. */ + unsigned int verbose_min; /**< @brief Use (several times) to be more verbose's minimum occurreces */ + unsigned int verbose_max; /**< @brief Use (several times) to be more verbose's maximum occurreces */ + const char *verbose_help; /**< @brief Use (several times) to be more verbose help description. */ + int pd_flag; /**< @brief Show 'Persönliche Versichertendaten' (XML) (default=off). */ + const char *pd_help; /**< @brief Show 'Persönliche Versichertendaten' (XML) help description. */ + int vd_flag; /**< @brief Show 'Allgemeine Versichertendaten' (XML) (default=off). */ + const char *vd_help; /**< @brief Show 'Allgemeine Versichertendaten' (XML) help description. */ + int gvd_flag; /**< @brief Show 'Geschützte Versichertendaten' (XML) (default=off). */ + const char *gvd_help; /**< @brief Show 'Geschützte Versichertendaten' (XML) help description. */ + int vsd_status_flag; /**< @brief Show 'Versichertenstammdaten-Status' (default=off). */ + const char *vsd_status_help; /**< @brief Show 'Versichertenstammdaten-Status' help description. */ + + unsigned int help_given ; /**< @brief Whether help was given. */ + unsigned int version_given ; /**< @brief Whether version was given. */ + unsigned int reader_given ; /**< @brief Whether reader was given. */ + unsigned int verbose_given ; /**< @brief Whether verbose was given. */ + unsigned int pd_given ; /**< @brief Whether pd was given. */ + unsigned int vd_given ; /**< @brief Whether vd was given. */ + unsigned int gvd_given ; /**< @brief Whether gvd was given. */ + unsigned int vsd_status_given ; /**< @brief Whether vsd-status was given. */ + +} ; + +/** @brief The additional parameters to pass to parser functions */ +struct cmdline_parser_params +{ + int override; /**< @brief whether to override possibly already present options (default 0) */ + int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */ + int check_required; /**< @brief whether to check that all required options were provided (default 1) */ + int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */ + int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */ +} ; + +/** @brief the purpose string of the program */ +extern const char *gengetopt_args_info_purpose; +/** @brief the usage string of the program */ +extern const char *gengetopt_args_info_usage; +/** @brief the description string of the program */ +extern const char *gengetopt_args_info_description; +/** @brief all the lines making the help output */ +extern const char *gengetopt_args_info_help[]; + +/** + * The command line parser + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser (int argc, char **argv, + struct gengetopt_args_info *args_info); + +/** + * The command line parser (version with additional parameters - deprecated) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param override whether to override possibly already present options + * @param initialize whether to initialize the option structure my_args_info + * @param check_required whether to check that all required options were provided + * @return 0 if everything went fine, NON 0 if an error took place + * @deprecated use cmdline_parser_ext() instead + */ +int cmdline_parser2 (int argc, char **argv, + struct gengetopt_args_info *args_info, + int override, int initialize, int check_required); + +/** + * The command line parser (version with additional parameters) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param params additional parameters for the parser + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_ext (int argc, char **argv, + struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params); + +/** + * Save the contents of the option struct into an already open FILE stream. + * @param outfile the stream where to dump options + * @param args_info the option struct to dump + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_dump(FILE *outfile, + struct gengetopt_args_info *args_info); + +/** + * Save the contents of the option struct into a (text) file. + * This file can be read by the config file parser (if generated by gengetopt) + * @param filename the file where to save + * @param args_info the option struct to save + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_file_save(const char *filename, + struct gengetopt_args_info *args_info); + +/** + * Print the help + */ +void cmdline_parser_print_help(void); +/** + * Print the version + */ +void cmdline_parser_print_version(void); + +/** + * Initializes all the fields a cmdline_parser_params structure + * to their default values + * @param params the structure to initialize + */ +void cmdline_parser_params_init(struct cmdline_parser_params *params); + +/** + * Allocates dynamically a cmdline_parser_params structure and initializes + * all its fields to their default values + * @return the created and initialized cmdline_parser_params structure + */ +struct cmdline_parser_params *cmdline_parser_params_create(void); + +/** + * Initializes the passed gengetopt_args_info structure's fields + * (also set default values for options that have a default) + * @param args_info the structure to initialize + */ +void cmdline_parser_init (struct gengetopt_args_info *args_info); +/** + * Deallocates the string fields of the gengetopt_args_info structure + * (but does not deallocate the structure itself) + * @param args_info the structure to deallocate + */ +void cmdline_parser_free (struct gengetopt_args_info *args_info); + +/** + * Checks that all the required options were specified + * @param args_info the structure to check + * @param prog_name the name of the program that will be used to print + * possible errors + * @return + */ +int cmdline_parser_required (struct gengetopt_args_info *args_info, + const char *prog_name); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* EGK_TOOL_CMDLINE_H */ diff --git a/src/tools/egk-tool.c b/src/tools/egk-tool.c new file mode 100644 index 0000000000..099d29e8ce --- /dev/null +++ b/src/tools/egk-tool.c @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2017 Frank Morgner + * + * This file is part of OpenSC. + * + * 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 + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "egk-tool-cmdline.h" +#include "libopensc/log.h" +#include "libopensc/opensc.h" +#include "util.h" +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +/* + * References: + * + * Speicherstrukturen der eGK für Gesundheitsanwendungen version 1.6.0 from 18.03.2008 + * https://fachportal.gematik.de/fileadmin/user_upload/fachportal/files/Spezifikationen/Basis-Rollout/Elektronische_Gesundheitskarte/gematik_eGK_Speicherstrukturen_V1_6_0.pdf + * + * Spezifikation der elektronischen Gesundheitskarte eGK-Objektsystem version 4.5.0 from 02.10.2019 + * https://fachportal.gematik.de/fachportal-import/files/gemSpec_eGK_ObjSys_G2_1_V4.5.0.pdf + * + * Speicherstrukturen der eGK für die Fachanwendung VSDM version 1.2.1 from 19.02.2021 + * https://fachportal.gematik.de/fachportal-import/files/gemSpec_eGK_Fach_VSDM_V1.2.1.pdf + * + * Speicherstrukturen der eGK für die Fachanwendung AMTS version 1.2.0 from 26.10.2018 + * https://www.vesta-gematik.de/standard/formhandler/324/gemSpec_eGK_Fach_AMTS_V1_2_0.pdf + */ + +#ifdef ENABLE_ZLIB +#include + +int uncompress_gzip(void* uncompressed, size_t *uncompressed_len, + const void* compressed, size_t compressed_len) +{ + z_stream stream; + memset(&stream, 0, sizeof stream); + stream.total_in = compressed_len; + stream.avail_in = (unsigned)compressed_len; + stream.total_out = *uncompressed_len; + stream.avail_out = (unsigned)*uncompressed_len; + stream.next_in = (Bytef *) compressed; + stream.next_out = (Bytef *) uncompressed; + + /* 15 window bits, and the +32 tells zlib to to detect if using gzip or zlib */ + if (inflateInit2(&stream, (15 + 32)) != Z_OK) { + return SC_ERROR_INVALID_DATA; + } + if (inflate(&stream, Z_FINISH) != Z_STREAM_END) { + inflateEnd(&stream); + return SC_ERROR_INVALID_DATA; + } + *uncompressed_len = stream.total_out; + inflateEnd(&stream); + + return SC_SUCCESS; +} +#else +int uncompress_gzip(void* uncompressed, size_t *uncompressed_len, + const void* compressed, size_t compressed_len) +{ + return SC_ERROR_NOT_SUPPORTED; +} +#endif + +#define PRINT(c) (isprint(c) ? c : '?') + +void dump_binary(void *buf, size_t buf_len) +{ +#ifdef _WIN32 + _setmode(fileno(stdout), _O_BINARY); +#endif + fwrite(buf, 1, buf_len, stdout); +#ifdef _WIN32 + _setmode(fileno(stdout), _O_TEXT); +#endif +} + +const unsigned char aid_hca[] = {0xD2, 0x76, 0x00, 0x00, 0x01, 0x02}; +static const char *app_name = "egk-tool"; + +int read_file(struct sc_card *card, char *str_path, unsigned char **data, size_t *data_len) +{ + struct sc_path path; + struct sc_file *file; + unsigned char *p; + int ok = 0; + int r; + size_t len; + + sc_format_path(str_path, &path); + if (SC_SUCCESS != sc_select_file(card, &path, &file)) { + goto err; + } + + len = file && file->size > 0 ? file->size : 4096; + p = realloc(*data, len); + if (!p) { + goto err; + } + *data = p; + *data_len = len; + + r = sc_read_binary(card, 0, p, len, 0); + if (r < 0) + goto err; + + *data_len = r; + ok = 1; + +err: + sc_file_free(file); + + return ok; +} + +void decode_version(unsigned char *bcd, unsigned int *major, unsigned int *minor, unsigned int *fix) +{ + *major = 0; + *minor = 0; + *fix = 0; + + /* decode BCD to decimal */ + if ((bcd[0]>>4) < 10 && ((bcd[0]&0xF) < 10) && ((bcd[1]>>4) < 10)) { + *major = (bcd[0]>>4)*100 + (bcd[0]&0xF)*10 + (bcd[1]>>4); + } + if (((bcd[1]&0xF) < 10) && ((bcd[2]>>4) < 10) && ((bcd[2]&0xF) < 10)) { + *minor = (bcd[1]&0xF)*100 + (bcd[2]>>4)*10 + (bcd[2]&0xF); + } + if ((bcd[3]>>4) < 10 && ((bcd[3]&0xF) < 10) + && (bcd[4]>>4) < 10 && ((bcd[4]&0xF) < 10)) { + *fix = (bcd[3]>>4)*1000 + (bcd[3]&0xF)*100 + + (bcd[4]>>4)*10 + (bcd[4]&0xF); + } +} + +int +main (int argc, char **argv) +{ + struct gengetopt_args_info cmdline; + struct sc_path path; + struct sc_context *ctx; + struct sc_card *card = NULL; + unsigned char *data = NULL; + size_t data_len = 0; + int r; + sc_context_param_t ctx_param; + + if (cmdline_parser(argc, argv, &cmdline) != 0) + exit(1); + + memset(&ctx_param, 0, sizeof(ctx_param)); + ctx_param.ver = 0; + ctx_param.app_name = app_name; + ctx_param.debug = cmdline.verbose_given; + if (cmdline.verbose_given > 1) + ctx_param.debug_file = stderr; + + r = sc_context_create(&ctx, &ctx_param); + if (r) { + fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); + exit(1); + } + + r = util_connect_card_ex(ctx, &card, cmdline.reader_arg, 0, 0); + if (r) + goto err; + + + sc_path_set(&path, SC_PATH_TYPE_DF_NAME, aid_hca, sizeof aid_hca, 0, 0); + if (SC_SUCCESS != sc_select_file(card, &path, NULL)) + goto err; + + if (cmdline.pd_flag + && read_file(card, "iD001", &data, &data_len) + && data_len >= 2) { + size_t len_pd = (data[0] << 8) | data[1]; + + if (len_pd + 2 <= data_len) { + unsigned char uncompressed[1024]; + size_t uncompressed_len = sizeof uncompressed; + + if (uncompress_gzip(uncompressed, &uncompressed_len, + data + 2, len_pd) == SC_SUCCESS) { + dump_binary(uncompressed, uncompressed_len); + } else { + dump_binary(data + 2, len_pd); + } + } + } + + if ((cmdline.vd_flag || cmdline.gvd_flag) + && read_file(card, "iD002", &data, &data_len) + && data_len >= 8) { + size_t off_vd = (data[0] << 8) | data[1]; + size_t end_vd = (data[2] << 8) | data[3]; + size_t off_gvd = (data[4] << 8) | data[5]; + size_t end_gvd = (data[6] << 8) | data[7]; + size_t len_vd = end_vd - off_vd + 1; + size_t len_gvd = end_gvd - off_gvd + 1; + + if (off_vd <= end_vd && end_vd < data_len + && off_gvd <= end_gvd && end_gvd < data_len) { + unsigned char uncompressed[1024]; + size_t uncompressed_len = sizeof uncompressed; + + if (cmdline.vd_flag) { + if (uncompress_gzip(uncompressed, &uncompressed_len, + data + off_vd, len_vd) == SC_SUCCESS) { + dump_binary(uncompressed, uncompressed_len); + } else { + dump_binary(data + off_vd, len_vd); + } + } + + if (cmdline.gvd_flag) { + if (uncompress_gzip(uncompressed, &uncompressed_len, + data + off_gvd, len_gvd) == SC_SUCCESS) { + dump_binary(uncompressed, uncompressed_len); + } else { + dump_binary(data + off_gvd, len_gvd); + } + } + } + } + + if (cmdline.vsd_status_flag + && read_file(card, "iD00C", &data, &data_len) + && data_len >= 25) { + char *status; + unsigned int major, minor, fix; + + switch (data[0]) { + case '0': + status = "Transactions pending"; + break; + case '1': + status = "No transactions pending"; + break; + default: + status = "Unknown"; + break; + } + + decode_version(data+15, &major, &minor, &fix); + + printf( + "Status %s\n" + "Timestamp %c%c.%c%c.%c%c%c%c at %c%c:%c%c:%c%c\n" + "Version %u.%u.%u\n", + status, + PRINT(data[7]), PRINT(data[8]), + PRINT(data[5]), PRINT(data[6]), + PRINT(data[1]), PRINT(data[2]), PRINT(data[3]), PRINT(data[4]), + PRINT(data[9]), PRINT(data[10]), + PRINT(data[11]), PRINT(data[12]), + PRINT(data[13]), PRINT(data[14]), + major, minor, fix); + } + +err: + sc_disconnect_card(card); + sc_release_context(ctx); + cmdline_parser_free (&cmdline); + + return 0; +} diff --git a/src/tools/egk-tool.ggo.in b/src/tools/egk-tool.ggo.in new file mode 100644 index 0000000000..031af0b23a --- /dev/null +++ b/src/tools/egk-tool.ggo.in @@ -0,0 +1,30 @@ +package "egk-tool" +purpose "@PACKAGE_SUMMARY@" + +option "reader" r + "Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen." + string + optional +option "verbose" v + "Use (several times) to be more verbose" + multiple + optional + +section "'Gesundheitsanwendung', Health Care Application (HCA)" +option "pd" - + "Show 'Persönliche Versichertendaten' (@VDFORMAT@)" + flag off +option "vd" - + "Show 'Allgemeine Versichertendaten' (@VDFORMAT@)" + flag off +option "gvd" - + "Show 'Geschützte Versichertendaten' (@VDFORMAT@)" + flag off +option "vsd-status" - + "Show 'Versichertenstammdaten-Status'" + flag off + +text " +Report bugs to @PACKAGE_BUGREPORT@ + +Written by Frank Morgner " diff --git a/src/tools/eidenv.c b/src/tools/eidenv.c index 4d2029b355..0a75ae863f 100644 --- a/src/tools/eidenv.c +++ b/src/tools/eidenv.c @@ -1,5 +1,5 @@ /* - * eidenv.c: EstEID utility + * eidenv.c: Belpic utility * * Copyright (C) 2004 Martin Paljak * @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -23,15 +23,19 @@ #include #ifndef _WIN32 #include +#else +#include #endif #include #include -#include "common/compat_getopt.h" +#include + +#include "common/compat_strlcat.h" +#include "common/compat_strlcpy.h" #include "libopensc/opensc.h" #include "libopensc/asn1.h" #include "libopensc/cards.h" -#include "libopensc/esteid.h" #include "util.h" static char *opt_reader = NULL; @@ -54,34 +58,10 @@ static const struct option options[] = { /* Probably not used, but needed to build on Windows */ static const char *app_name = "eidenv"; -static struct { - const char *name; - const char *env_name; - int recno; -} esteid_data[] = { - {"Surname", "ESTEID_SURNAME", 1}, - {"Given names 1", "ESTEID_GIVEN_NAMES1", 2}, - {"Given names 2", "ESTEID_GIVEN_NAMES2", 3}, - {"Sex", "ESTEID_SEX", 4}, - {"Citizenship", "ESTEID_CITIZENSHIP", 5}, - {"Date of birth", "ESTEID_DATE_OF_BIRTH", 6}, - {"Personal ID code", "ESTEID_PERSONAL_ID", 7}, - {"Document number", "ESTEID_DOCUMENT_NR", 8}, - {"Expiry date", "ESTEID_EXPIRY_DATE", 9}, - {"Place of birth", "ESTEID_PLACE_OF_BIRTH", 10}, - {"Issuing date", "ESTEID_ISSUING_DATE", 11}, - {"Permit type", "ESTEID_PERMIT_TYPE", 12}, - {"Remark 1", "ESTEID_REMARK1", 13}, - {"Remark 2", "ESTEID_REMARK2", 14}, - {"Remark 3", "ESTEID_REMARK3", 15}, - {"Remark 4", "ESTEID_REMARK4", 16}, - {NULL, NULL, 0} -}; - static void show_version(void) { fprintf(stderr, - "eidenv - EstEID utility version " PACKAGE_VERSION "\n" + "eidenv - Belpic utility version " PACKAGE_VERSION "\n" "\n" "Copyright (c) 2004 Martin Paljak \n" "Licensed under LGPL v2\n"); @@ -104,7 +84,7 @@ static void decode_options(int argc, char **argv) { int c; - while ((c = getopt_long(argc, argv,"pwtr:x:hV", options, (int *) 0)) != EOF) { + while ((c = getopt_long(argc, argv,"pwtr:x:hV", options, (int *) 0)) != -1) { switch (c) { case 'r': @@ -138,73 +118,6 @@ static void decode_options(int argc, char **argv) } } -static void do_esteid(sc_card_t *card) -{ - sc_path_t path; - int r, i; - unsigned char buff[512]; - - if (stats) { - int key_used[4]; - sc_format_path("3f00eeee0013", &path); - r = sc_select_file(card, &path, NULL); - if (r) { - fprintf(stderr, "Failed to select key counters: %s\n", sc_strerror(r)); - goto out; - } - - /* print the counters */ - for (i = 1; i <= 4; i++) { - r = sc_read_record(card, i, buff, 128, SC_RECORD_BY_REC_NR); - key_used[i - 1] = 0xffffff - ((unsigned char) buff[0xc] * 65536 - + (unsigned char) buff[0xd] * 256 - + (unsigned char) buff[0xe]); - } - for (i = 0; i < 2; i++) { - printf("Key generation #%d usage:\n\tsign: %d\n\tauth: %d\n", - i, key_used[i], key_used[i + 2]); - } - exit_status = EXIT_SUCCESS; - goto out; - } - - /* Or just read the datafile */ - sc_format_path("3f00eeee5044", &path); - r = sc_select_file(card, &path, NULL); - if (r) { - fprintf(stderr, "Failed to select DF: %s\n", sc_strerror(r)); - goto out; - } - - for (i = 0; esteid_data[i].recno != 0; i++) { - r = sc_read_record(card, esteid_data[i].recno, buff, 50, SC_RECORD_BY_REC_NR); - if (r < 0) { - fprintf (stderr, "Failed to read record %d from card: %s\n", - esteid_data[i].recno, sc_strerror (r)); - goto out; - } - buff[r] = '\0'; - if (exec_program) { - unsigned char * cp; - cp = malloc(strlen(esteid_data[i].env_name) + - strlen((char *) buff) + 2); - if (cp) { - strcpy((char *) cp,esteid_data[i].env_name); - strcat((char *) cp,"="); - strcat((char *) cp,(char *) buff); - putenv((char *) cp); - } - } else { - printf("%s: %s\n", esteid_data[i].name, buff); - } - } - - exit_status = EXIT_SUCCESS; - -out: - return; -} - /* Select and read a transparent EF */ static int read_transp(sc_card_t *card, const char *pathstring, unsigned char *buf, int buflen) { @@ -226,10 +139,10 @@ static int read_transp(sc_card_t *card, const char *pathstring, unsigned char *b /* Hex-encode the buf, 2*len+1 bytes must be reserved. E.g. {'1','2'} -> {'3','1','3','2','\0'} */ static const char hextable[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'E'}; -static void bintohex(char *buf, int len) +static void bintohex(char *buf, size_t len) { - int i; - for (i = len - 1; i >= 0; i--) { + size_t i; + for (i = 0; i < len; i++) { buf[2 * i + 1] = hextable[((unsigned char) buf[i]) % 16]; buf[2 * i] = hextable[((unsigned char) buf[i]) / 16]; } @@ -238,13 +151,15 @@ static void bintohex(char *buf, int len) static void exportprint(const char *key, const char *val) { if (exec_program) { - char * cp; - cp = malloc(strlen(key) + strlen(val) + 2); + char *cp; + size_t cp_size = strlen(key) + strlen(val) + 2; + cp = malloc(cp_size); if (cp) { - strcpy(cp, key); - strcat(cp, "="); - strcat(cp, val); + strlcpy(cp, key, cp_size); + strlcat(cp, "=", cp_size); + strlcat(cp, val, cp_size); putenv(cp); + free(cp); } } else printf("%s: %s\n", key, val); @@ -271,22 +186,22 @@ static void do_belpic(sc_card_t *card) char documenttype[5 + 1]; char specialstatus[5 + 1]; } id_data; - int cardnumberlen = sizeof(id_data.cardnumber); - int chipnumberlen = sizeof(id_data.chipnumber); - int validfromlen = sizeof(id_data.validfrom); - int validtilllen = sizeof(id_data.validtill); - int deliveringmunicipalitylen = sizeof(id_data.deliveringmunicipality); - int nationalnumberlen = sizeof(id_data.nationalnumber); - int namelen = sizeof(id_data.name); - int firstnameslen = sizeof(id_data.firstnames); - int initiallen = sizeof(id_data.initial); - int nationalitylen = sizeof(id_data.nationality); - int birthlocationlen = sizeof(id_data.birthlocation); - int birthdatelen = sizeof(id_data.birthdate); - int sexlen = sizeof(id_data.sex); - int nobleconditionlen = sizeof(id_data.noblecondition); - int documenttypelen = sizeof(id_data.documenttype); - int specialstatuslen = sizeof(id_data.specialstatus); + size_t cardnumberlen = sizeof(id_data.cardnumber); + size_t chipnumberlen = sizeof(id_data.chipnumber); + size_t validfromlen = sizeof(id_data.validfrom); + size_t validtilllen = sizeof(id_data.validtill); + size_t deliveringmunicipalitylen = sizeof(id_data.deliveringmunicipality); + size_t nationalnumberlen = sizeof(id_data.nationalnumber); + size_t namelen = sizeof(id_data.name); + size_t firstnameslen = sizeof(id_data.firstnames); + size_t initiallen = sizeof(id_data.initial); + size_t nationalitylen = sizeof(id_data.nationality); + size_t birthlocationlen = sizeof(id_data.birthlocation); + size_t birthdatelen = sizeof(id_data.birthdate); + size_t sexlen = sizeof(id_data.sex); + size_t nobleconditionlen = sizeof(id_data.noblecondition); + size_t documenttypelen = sizeof(id_data.documenttype); + size_t specialstatuslen = sizeof(id_data.specialstatus); struct sc_asn1_entry id[] = { {"cardnumber", SC_ASN1_UTF8STRING, 1, 0, id_data.cardnumber, &cardnumberlen}, @@ -314,9 +229,9 @@ static void do_belpic(sc_card_t *card) char zipcode[4 + 1]; char municipality[40 + 1]; /* UTF8 */ } address_data; - int streetandnumberlen = sizeof(address_data.streetandnumber); - int zipcodelen = sizeof(address_data.zipcode); - int municipalitylen = sizeof(address_data.municipality); + size_t streetandnumberlen = sizeof(address_data.streetandnumber); + size_t zipcodelen = sizeof(address_data.zipcode); + size_t municipalitylen = sizeof(address_data.municipality); struct sc_asn1_entry address[] = { {"streetandnumber", SC_ASN1_UTF8STRING, 1, 0, address_data.streetandnumber, &streetandnumberlen}, {"zipcode", SC_ASN1_UTF8STRING, 2, 0, address_data.zipcode, &zipcodelen}, @@ -397,19 +312,18 @@ int main(int argc, char **argv) sc_strerror(r)); return 1; } - r = util_connect_card(ctx, &card, opt_reader, opt_wait, 0); + r = util_connect_card(ctx, &card, opt_reader, opt_wait); if (r) { fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(r)); + sc_release_context(ctx); return 1; } /* Check card type */ - if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V10 || card->type == SC_CARD_TYPE_MCRD_ESTEID_V11 || card->type == SC_CARD_TYPE_MCRD_ESTEID_V30) - do_esteid(card); - else if (card->type == SC_CARD_TYPE_BELPIC_EID) + if (card->type == SC_CARD_TYPE_BELPIC_EID) do_belpic(card); else { - fprintf(stderr, "Not an EstEID or Belpic card!\n"); + fprintf(stderr, "Not an Belpic card!\n"); goto out; } diff --git a/src/tools/fread_to_eof.c b/src/tools/fread_to_eof.c index a563e46ffc..12f07e8ce9 100644 --- a/src/tools/fread_to_eof.c +++ b/src/tools/fread_to_eof.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" diff --git a/src/tools/fread_to_eof.h b/src/tools/fread_to_eof.h index 14a176c8bf..cb52a20729 100644 --- a/src/tools/fread_to_eof.h +++ b/src/tools/fread_to_eof.h @@ -15,11 +15,13 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _FREAD_TO_EOF_H #define _FREAD_TO_EOF_H +#include + int fread_to_eof(const char *file, unsigned char **buf, size_t *buflen); #endif diff --git a/src/tools/gids-tool.c b/src/tools/gids-tool.c index 48084d8c3e..8c30628fd9 100644 --- a/src/tools/gids-tool.c +++ b/src/tools/gids-tool.c @@ -15,10 +15,10 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -35,13 +35,9 @@ #include #include "libopensc/sc-ossl-compat.h" -#if OPENSSL_VERSION_NUMBER >= 0x10000000L #include #include -#endif -#if OPENSSL_VERSION_NUMBER >= 0x00907000L #include -#endif #include #include @@ -88,15 +84,12 @@ static const char *option_help[] = { "Define serial number", "Unblock the user PIN after an administrator authentication", "Change the administrator key", - "Define the new adminastrator key", + "Define the new administrator key", "Uses reader number [0]", "Wait for a card to be inserted", - "Verbose operation. Use several times to enable debug output.", + "Verbose operation, may be used several times", }; -static sc_context_t *ctx = NULL; -static sc_card_t *card = NULL; - static int initialize(sc_card_t *card, const char *so_pin, const char *user_pin, const char* serial) { sc_cardctl_gids_init_param_t param; @@ -122,8 +115,7 @@ static int initialize(sc_card_t *card, const char *so_pin, const char *user_pin, return -1; } - if (len == 0) { - } else if (len != 24) { + if (len != 24) { fprintf(stderr, "The admin key must be a hexadecimal string of 48 characters\n"); return -1; } @@ -236,12 +228,12 @@ static int unblock(sc_card_t* card, const char *so_pin, const char *user_pin) { data.pin2.len = strlen(_user_pin); data.pin2.data = (unsigned char*) _user_pin; data.pin_reference = 0x80; - r = sc_pin_cmd(card, &data, NULL); + r = sc_pin_cmd(card, &data); if (r < 0) { fprintf(stderr, "reset pin failed with %s\n", sc_strerror(r)); return -1; } - printf("Unblock PIN done successfuly\n"); + printf("Unblock PIN done successfully\n"); // the card should have deauthenticated the admin, but to be sure: sc_logout(card); return 0; @@ -345,7 +337,9 @@ static int gids_get_DO(sc_card_t* card, int fileIdentifier, int dataObjectIdenti if (datasize > *responselen) { return SC_ERROR_BUFFER_TOO_SMALL; } - memcpy(response, p, datasize); + if (response) { + memcpy(response, p, datasize); + } *responselen = datasize; return SC_SUCCESS; } @@ -363,7 +357,7 @@ static int print_info(sc_card_t *card) { gids_mf_record_t *records = (gids_mf_record_t *) (masterfile+1); int recordcount; int i; - + printf("===============================\n"); printf("Dumping the content of the card\n"); printf("===============================\n"); @@ -408,40 +402,46 @@ static int print_info(sc_card_t *card) { printf("Unable to find the container file (mscp\\cmapfile)\n"); } else { PCONTAINER_MAP_RECORD cmaprecords = (PCONTAINER_MAP_RECORD) cmapfile; - int cmaprecordnum = (cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); - int keymaprecordnum = -1; + size_t cmaprecordnum = (cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); + size_t keymaprecordnum = -1; struct gids_keymap_record* keymaprecord = ((struct gids_keymap_record*)(keymap +1)); if (cmaprecordnum == 0) { printf(" no container found\n"); } else { + size_t j; r = gids_get_DO(card, KEYMAP_FI, KEYMAP_DO, keymap, &keymapsize); if (r < 0) { printf(" the keymap couldn't be found\n"); } else { keymaprecordnum = (keymapsize - 1) / sizeof(struct gids_keymap_record); } - for (i = 0; i < cmaprecordnum; i++) { - printf(" container: %d\n", i); - wprintf(L" guid: %s\n", cmaprecords[i].wszGuid); + for (j = 0; j < cmaprecordnum; j++) { +#ifdef _WIN32 + wprintf(L" guid: %ls\n", cmaprecords[j].wszGuid); +#else + /* avoid converting Windows' WCHAR to Unix' wchar_t by simply dumping the content */ + util_hex_dump(stdout, (unsigned char *)cmaprecords[j].wszGuid, + sizeof cmaprecords[j].wszGuid, ""); +#endif printf(" bFlags: "); - if (cmaprecords[i].bFlags & CONTAINER_MAP_VALID_CONTAINER) { + if (cmaprecords[j].bFlags & CONTAINER_MAP_VALID_CONTAINER) { printf("Valid container"); - if (cmaprecords[i].bFlags & CONTAINER_MAP_DEFAULT_CONTAINER) { + if (cmaprecords[j].bFlags & CONTAINER_MAP_DEFAULT_CONTAINER) { printf(",Default container"); } } else { printf("Empty container"); } printf("\n"); - printf(" wSigKeySizeBits: %d\n", cmaprecords[i].wSigKeySizeBits); - printf(" wKeyExchangeKeySizeBits: %d\n", cmaprecords[i].wKeyExchangeKeySizeBits); - if (i < keymaprecordnum) { + printf(" wSigKeySizeBits: %d\n", cmaprecords[j].wSigKeySizeBits); + printf(" wKeyExchangeKeySizeBits: %d\n", cmaprecords[j].wKeyExchangeKeySizeBits); + if (j < keymaprecordnum) { printf(" key info:\n"); - printf(" state: %d\n", keymaprecord[i].state); - printf(" algid: %d\n", keymaprecord[i].algid); - printf(" keyref: 0x%x\n", keymaprecord[i].keyref); + printf(" state: %d\n", keymaprecord[j].state); + printf(" algid: %d\n", keymaprecord[j].algid); + printf(" keyref: 0x%x\n", keymaprecord[j].keyref); printf(" key type: "); - switch(keymaprecord[i].keytype) { + switch(keymaprecord[j].keytype) { case 0: printf("none\n"); break; @@ -466,7 +466,7 @@ static int print_info(sc_card_t *card) { return SC_SUCCESS; } -int main(int argc, char * const argv[]) +int main(int argc, char * argv[]) { int err = 0, r, c, long_optind = 0; int action_count = 0; @@ -479,9 +479,8 @@ int main(int argc, char * const argv[]) const char *opt_serial_number = NULL; const char *opt_new_key = NULL; sc_context_param_t ctx_param; - - setbuf(stderr, NULL); - setbuf(stdout, NULL); + sc_context_t *ctx = NULL; + sc_card_t *card = NULL; while (1) { c = getopt_long(argc, argv, "XUCr:wv", options, &long_optind); @@ -526,26 +525,13 @@ int main(int argc, char * const argv[]) } } - - /* OpenSSL magic */ -#if (OPENSSL_VERSION_NUMBER >= 0x00907000L && OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) - OPENSSL_config(NULL); -#endif -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS - | OPENSSL_INIT_ADD_ALL_CIPHERS - | OPENSSL_INIT_ADD_ALL_DIGESTS, - NULL); -#else /* OpenSSL magic */ - OPENSSL_malloc_init(); - - ERR_load_crypto_strings(); - OpenSSL_add_all_algorithms(); -#endif memset(&ctx_param, 0, sizeof(sc_context_param_t)); ctx_param.app_name = app_name; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; r = sc_context_create(&ctx, &ctx_param); if (r != SC_SUCCESS) { @@ -553,13 +539,7 @@ int main(int argc, char * const argv[]) exit(1); } - /* Only change if not in opensc.conf */ - if (verbose > 1 && ctx->debug == 0) { - ctx->debug = verbose; - sc_ctx_log_to_file(ctx, "stderr"); - } - - r = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); + r = util_connect_card(ctx, &card, opt_reader, opt_wait); if (r != SC_SUCCESS) { if (r < 0) { fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(err)); @@ -597,8 +577,7 @@ int main(int argc, char * const argv[]) sc_unlock(card); sc_disconnect_card(card); } - if (ctx) - sc_release_context(ctx); + sc_release_context(ctx); ERR_print_errors_fp(stderr); return err; diff --git a/src/tools/goid-tool-cmdline.c b/src/tools/goid-tool-cmdline.c new file mode 100644 index 0000000000..f87b986da9 --- /dev/null +++ b/src/tools/goid-tool-cmdline.c @@ -0,0 +1,1649 @@ +/* + File autogenerated by gengetopt version 2.23 + generated with the following command: + /opt/homebrew/bin/gengetopt --file-name=goid-tool-cmdline --output-dir=. + + The developers of gengetopt consider the fixed text that goes in all + gengetopt output files to be in the public domain: + we make no copyright claims on it. +*/ + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#ifndef FIX_UNUSED +#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ +#endif + +#include + +#include "goid-tool-cmdline.h" + +const char *gengetopt_args_info_purpose = ""; + +const char *gengetopt_args_info_usage = "Usage: goid-tool [OPTION]..."; + +const char *gengetopt_args_info_versiontext = ""; + +const char *gengetopt_args_info_description = ""; + +const char *gengetopt_args_info_help[] = { + " -h, --help Print help and exit", + " -V, --version Print version and exit", + " -r, --reader=STRING Number of the reader to use. By default, the\n first reader with a present card is used. If\n the argument is an ATR, the reader with a\n matching card will be chosen.", + " -v, --verbose Use (several times) to be more verbose", + " -p, --verify-pin Verify PIN", + " -b, --verify-bio Verify finger print", + " --verify-pin-or-bio Verify PIN or finger print (user's choice)", + "\nReport bugs to https://github.com/OpenSC/OpenSC/issues\n\nWritten by Frank Morgner ", + "\n Mode: soc\n Options for SoCManager Applet", + " --new-pin Change PIN", + " --new-bio Use (several times) to change one or more\n biometric templates", + " --info Dump Information about the SoCManager's\n configuration", + "\n Mode: pxs\n Options for PAccess Applet", + " -c, --certificate=FILENAME Use (several times) to pass CV certificates", + " -k, --key=FILENAME Private key for the CV certificate", + " --print-cardid Print the card ID", + " --write-cardid=CARDID Write the specified card ID", + " --print-paccessid Print the PAccess ID", + " --write-paccessid=PACCESSID\n Write the specified PAccess ID", + " --read-dg=ID Read the specified data group; use several\n times to read out multiple files", + " --out-file=FILENAME Write output to a file instead of printing it;\n use once for each use of `--read-dg'", + " --write-dg=ID Write the specified data group; use several\n times to write multiple files", + " --in-file=FILENAME Read input from a file; use once for each use\n of `--write-dg'", + " --delete-dg=ID Delete the specified data group; use several\n times to delete multiple files", + " --create-dg=ID Create the specified data group; use several\n times to create multiple files", + " --new-size=SIZE File size of newly created DGs (default=`256')", + " --new-read-ac=STRING Access condition for reading newly created DGs\n (possible values=\"always\", \"never\",\n \"ta\", \"sm\" default=`sm')", + " --new-read-ac-chatbit=INDEX\n Required access bit in certificate's CHAT for\n reading newly created DGs", + " --new-write-ac=STRING Access condition for writing newly created DGs\n (possible values=\"always\", \"never\",\n \"ta\", \"sm\" default=`sm')", + " --new-write-ac-chatbit=INDEX\n Required access bit in certificate's CHAT for\n reading newly created DGs", + 0 +}; + +typedef enum {ARG_NO + , ARG_STRING + , ARG_SHORT +} cmdline_parser_arg_type; + +static +void clear_given (struct gengetopt_args_info *args_info); +static +void clear_args (struct gengetopt_args_info *args_info); + +static int +cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params, const char *additional_error); + +static int +cmdline_parser_required2 (struct gengetopt_args_info *args_info, const char *prog_name, const char *additional_error); + +const char *cmdline_parser_new_read_ac_values[] = {"always", "never", "ta", "sm", 0}; /*< Possible values for new-read-ac. */ +const char *cmdline_parser_new_write_ac_values[] = {"always", "never", "ta", "sm", 0}; /*< Possible values for new-write-ac. */ + +static char * +gengetopt_strdup (const char *s); + +static +void clear_given (struct gengetopt_args_info *args_info) +{ + args_info->help_given = 0 ; + args_info->version_given = 0 ; + args_info->reader_given = 0 ; + args_info->verbose_given = 0 ; + args_info->verify_pin_given = 0 ; + args_info->verify_bio_given = 0 ; + args_info->verify_pin_or_bio_given = 0 ; + args_info->new_pin_given = 0 ; + args_info->new_bio_given = 0 ; + args_info->info_given = 0 ; + args_info->certificate_given = 0 ; + args_info->key_given = 0 ; + args_info->print_cardid_given = 0 ; + args_info->write_cardid_given = 0 ; + args_info->print_paccessid_given = 0 ; + args_info->write_paccessid_given = 0 ; + args_info->read_dg_given = 0 ; + args_info->out_file_given = 0 ; + args_info->write_dg_given = 0 ; + args_info->in_file_given = 0 ; + args_info->delete_dg_given = 0 ; + args_info->create_dg_given = 0 ; + args_info->new_size_given = 0 ; + args_info->new_read_ac_given = 0 ; + args_info->new_read_ac_chatbit_given = 0 ; + args_info->new_write_ac_given = 0 ; + args_info->new_write_ac_chatbit_given = 0 ; + args_info->pxs_mode_counter = 0 ; + args_info->soc_mode_counter = 0 ; +} + +static +void clear_args (struct gengetopt_args_info *args_info) +{ + FIX_UNUSED (args_info); + args_info->reader_arg = NULL; + args_info->reader_orig = NULL; + args_info->certificate_arg = NULL; + args_info->certificate_orig = NULL; + args_info->key_arg = NULL; + args_info->key_orig = NULL; + args_info->write_cardid_arg = NULL; + args_info->write_cardid_orig = NULL; + args_info->write_paccessid_arg = NULL; + args_info->write_paccessid_orig = NULL; + args_info->read_dg_arg = NULL; + args_info->read_dg_orig = NULL; + args_info->out_file_arg = NULL; + args_info->out_file_orig = NULL; + args_info->write_dg_arg = NULL; + args_info->write_dg_orig = NULL; + args_info->in_file_arg = NULL; + args_info->in_file_orig = NULL; + args_info->delete_dg_arg = NULL; + args_info->delete_dg_orig = NULL; + args_info->create_dg_arg = NULL; + args_info->create_dg_orig = NULL; + args_info->new_size_arg = 256; + args_info->new_size_orig = NULL; + args_info->new_read_ac_arg = gengetopt_strdup ("sm"); + args_info->new_read_ac_orig = NULL; + args_info->new_read_ac_chatbit_arg = NULL; + args_info->new_read_ac_chatbit_orig = NULL; + args_info->new_write_ac_arg = gengetopt_strdup ("sm"); + args_info->new_write_ac_orig = NULL; + args_info->new_write_ac_chatbit_arg = NULL; + args_info->new_write_ac_chatbit_orig = NULL; + +} + +static +void init_args_info(struct gengetopt_args_info *args_info) +{ + + + args_info->help_help = gengetopt_args_info_help[0] ; + args_info->version_help = gengetopt_args_info_help[1] ; + args_info->reader_help = gengetopt_args_info_help[2] ; + args_info->verbose_help = gengetopt_args_info_help[3] ; + args_info->verbose_min = 0; + args_info->verbose_max = 0; + args_info->verify_pin_help = gengetopt_args_info_help[4] ; + args_info->verify_bio_help = gengetopt_args_info_help[5] ; + args_info->verify_pin_or_bio_help = gengetopt_args_info_help[6] ; + args_info->new_pin_help = gengetopt_args_info_help[9] ; + args_info->new_bio_help = gengetopt_args_info_help[10] ; + args_info->new_bio_min = 0; + args_info->new_bio_max = 0; + args_info->info_help = gengetopt_args_info_help[11] ; + args_info->certificate_help = gengetopt_args_info_help[13] ; + args_info->certificate_min = 0; + args_info->certificate_max = 0; + args_info->key_help = gengetopt_args_info_help[14] ; + args_info->print_cardid_help = gengetopt_args_info_help[15] ; + args_info->write_cardid_help = gengetopt_args_info_help[16] ; + args_info->print_paccessid_help = gengetopt_args_info_help[17] ; + args_info->write_paccessid_help = gengetopt_args_info_help[18] ; + args_info->read_dg_help = gengetopt_args_info_help[19] ; + args_info->read_dg_min = 0; + args_info->read_dg_max = 0; + args_info->out_file_help = gengetopt_args_info_help[20] ; + args_info->out_file_min = 0; + args_info->out_file_max = 0; + args_info->write_dg_help = gengetopt_args_info_help[21] ; + args_info->write_dg_min = 0; + args_info->write_dg_max = 0; + args_info->in_file_help = gengetopt_args_info_help[22] ; + args_info->in_file_min = 0; + args_info->in_file_max = 0; + args_info->delete_dg_help = gengetopt_args_info_help[23] ; + args_info->delete_dg_min = 0; + args_info->delete_dg_max = 0; + args_info->create_dg_help = gengetopt_args_info_help[24] ; + args_info->create_dg_min = 0; + args_info->create_dg_max = 0; + args_info->new_size_help = gengetopt_args_info_help[25] ; + args_info->new_read_ac_help = gengetopt_args_info_help[26] ; + args_info->new_read_ac_chatbit_help = gengetopt_args_info_help[27] ; + args_info->new_read_ac_chatbit_min = 0; + args_info->new_read_ac_chatbit_max = 0; + args_info->new_write_ac_help = gengetopt_args_info_help[28] ; + args_info->new_write_ac_chatbit_help = gengetopt_args_info_help[29] ; + args_info->new_write_ac_chatbit_min = 0; + args_info->new_write_ac_chatbit_max = 0; + +} + +void +cmdline_parser_print_version (void) +{ + printf ("%s %s\n", + (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE), + CMDLINE_PARSER_VERSION); + + if (strlen(gengetopt_args_info_versiontext) > 0) + printf("\n%s\n", gengetopt_args_info_versiontext); +} + +static void print_help_common(void) +{ + size_t len_purpose = strlen(gengetopt_args_info_purpose); + size_t len_usage = strlen(gengetopt_args_info_usage); + + if (len_usage > 0) { + printf("%s\n", gengetopt_args_info_usage); + } + if (len_purpose > 0) { + printf("%s\n", gengetopt_args_info_purpose); + } + + if (len_usage || len_purpose) { + printf("\n"); + } + + if (strlen(gengetopt_args_info_description) > 0) { + printf("%s\n\n", gengetopt_args_info_description); + } +} + +void +cmdline_parser_print_help (void) +{ + int i = 0; + print_help_common(); + while (gengetopt_args_info_help[i]) + printf("%s\n", gengetopt_args_info_help[i++]); +} + +void +cmdline_parser_init (struct gengetopt_args_info *args_info) +{ + clear_given (args_info); + clear_args (args_info); + init_args_info (args_info); +} + +void +cmdline_parser_params_init(struct cmdline_parser_params *params) +{ + if (params) + { + params->override = 0; + params->initialize = 1; + params->check_required = 1; + params->check_ambiguity = 0; + params->print_errors = 1; + } +} + +struct cmdline_parser_params * +cmdline_parser_params_create(void) +{ + struct cmdline_parser_params *params = + (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params)); + cmdline_parser_params_init(params); + return params; +} + +static void +free_string_field (char **s) +{ + if (*s) + { + free (*s); + *s = 0; + } +} + +/** @brief generic value variable */ +union generic_value { + short short_arg; + char *string_arg; + const char *default_string_arg; +}; + +/** @brief holds temporary values for multiple options */ +struct generic_list +{ + union generic_value arg; + char *orig; + struct generic_list *next; +}; + +/** + * @brief add a node at the head of the list + */ +static void add_node(struct generic_list **list) { + struct generic_list *new_node = (struct generic_list *) malloc (sizeof (struct generic_list)); + new_node->next = *list; + *list = new_node; + new_node->arg.string_arg = 0; + new_node->orig = 0; +} + +/** + * The passed arg parameter is NOT set to 0 from this function + */ +static void +free_multiple_field(unsigned int len, void *arg, char ***orig) +{ + unsigned int i; + if (arg) { + for (i = 0; i < len; ++i) + { + free_string_field(&((*orig)[i])); + } + + free (arg); + free (*orig); + *orig = 0; + } +} + +static void +free_multiple_string_field(unsigned int len, char ***arg, char ***orig) +{ + unsigned int i; + if (*arg) { + for (i = 0; i < len; ++i) + { + free_string_field(&((*arg)[i])); + free_string_field(&((*orig)[i])); + } + free_string_field(&((*arg)[0])); /* free default string */ + + free (*arg); + *arg = 0; + free (*orig); + *orig = 0; + } +} + +static void +cmdline_parser_release (struct gengetopt_args_info *args_info) +{ + + free_string_field (&(args_info->reader_arg)); + free_string_field (&(args_info->reader_orig)); + free_multiple_string_field (args_info->certificate_given, &(args_info->certificate_arg), &(args_info->certificate_orig)); + free_string_field (&(args_info->key_arg)); + free_string_field (&(args_info->key_orig)); + free_string_field (&(args_info->write_cardid_arg)); + free_string_field (&(args_info->write_cardid_orig)); + free_string_field (&(args_info->write_paccessid_arg)); + free_string_field (&(args_info->write_paccessid_orig)); + free_multiple_field (args_info->read_dg_given, (void *)(args_info->read_dg_arg), &(args_info->read_dg_orig)); + args_info->read_dg_arg = 0; + free_multiple_string_field (args_info->out_file_given, &(args_info->out_file_arg), &(args_info->out_file_orig)); + free_multiple_field (args_info->write_dg_given, (void *)(args_info->write_dg_arg), &(args_info->write_dg_orig)); + args_info->write_dg_arg = 0; + free_multiple_string_field (args_info->in_file_given, &(args_info->in_file_arg), &(args_info->in_file_orig)); + free_multiple_field (args_info->delete_dg_given, (void *)(args_info->delete_dg_arg), &(args_info->delete_dg_orig)); + args_info->delete_dg_arg = 0; + free_multiple_field (args_info->create_dg_given, (void *)(args_info->create_dg_arg), &(args_info->create_dg_orig)); + args_info->create_dg_arg = 0; + free_string_field (&(args_info->new_size_orig)); + free_string_field (&(args_info->new_read_ac_arg)); + free_string_field (&(args_info->new_read_ac_orig)); + free_multiple_field (args_info->new_read_ac_chatbit_given, (void *)(args_info->new_read_ac_chatbit_arg), &(args_info->new_read_ac_chatbit_orig)); + args_info->new_read_ac_chatbit_arg = 0; + free_string_field (&(args_info->new_write_ac_arg)); + free_string_field (&(args_info->new_write_ac_orig)); + free_multiple_field (args_info->new_write_ac_chatbit_given, (void *)(args_info->new_write_ac_chatbit_arg), &(args_info->new_write_ac_chatbit_orig)); + args_info->new_write_ac_chatbit_arg = 0; + + + + clear_given (args_info); +} + +/** + * @param val the value to check + * @param values the possible values + * @return the index of the matched value: + * -1 if no value matched, + * -2 if more than one value has matched + */ +static int +check_possible_values(const char *val, const char *values[]) +{ + int i, found, last; + size_t len; + + if (!val) /* otherwise strlen() crashes below */ + return -1; /* -1 means no argument for the option */ + + found = last = 0; + + for (i = 0, len = strlen(val); values[i]; ++i) + { + if (strncmp(val, values[i], len) == 0) + { + ++found; + last = i; + if (strlen(values[i]) == len) + return i; /* exact match no need to check more */ + } + } + + if (found == 1) /* one match: OK */ + return last; + + return (found ? -2 : -1); /* return many values or none matched */ +} + + +static void +write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[]) +{ + int found = -1; + if (arg) { + if (values) { + found = check_possible_values(arg, values); + } + if (found >= 0) + fprintf(outfile, "%s=\"%s\" # %s\n", opt, arg, values[found]); + else + fprintf(outfile, "%s=\"%s\"\n", opt, arg); + } else { + fprintf(outfile, "%s\n", opt); + } +} + +static void +write_multiple_into_file(FILE *outfile, int len, const char *opt, char **arg, const char *values[]) +{ + int i; + + for (i = 0; i < len; ++i) + write_into_file(outfile, opt, (arg ? arg[i] : 0), values); +} + +int +cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) +{ + int i = 0; + + if (!outfile) + { + fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE); + return EXIT_FAILURE; + } + + if (args_info->help_given) + write_into_file(outfile, "help", 0, 0 ); + if (args_info->version_given) + write_into_file(outfile, "version", 0, 0 ); + if (args_info->reader_given) + write_into_file(outfile, "reader", args_info->reader_orig, 0); + write_multiple_into_file(outfile, args_info->verbose_given, "verbose", 0, 0); + if (args_info->verify_pin_given) + write_into_file(outfile, "verify-pin", 0, 0 ); + if (args_info->verify_bio_given) + write_into_file(outfile, "verify-bio", 0, 0 ); + if (args_info->verify_pin_or_bio_given) + write_into_file(outfile, "verify-pin-or-bio", 0, 0 ); + if (args_info->new_pin_given) + write_into_file(outfile, "new-pin", 0, 0 ); + write_multiple_into_file(outfile, args_info->new_bio_given, "new-bio", 0, 0); + if (args_info->info_given) + write_into_file(outfile, "info", 0, 0 ); + write_multiple_into_file(outfile, args_info->certificate_given, "certificate", args_info->certificate_orig, 0); + if (args_info->key_given) + write_into_file(outfile, "key", args_info->key_orig, 0); + if (args_info->print_cardid_given) + write_into_file(outfile, "print-cardid", 0, 0 ); + if (args_info->write_cardid_given) + write_into_file(outfile, "write-cardid", args_info->write_cardid_orig, 0); + if (args_info->print_paccessid_given) + write_into_file(outfile, "print-paccessid", 0, 0 ); + if (args_info->write_paccessid_given) + write_into_file(outfile, "write-paccessid", args_info->write_paccessid_orig, 0); + write_multiple_into_file(outfile, args_info->read_dg_given, "read-dg", args_info->read_dg_orig, 0); + write_multiple_into_file(outfile, args_info->out_file_given, "out-file", args_info->out_file_orig, 0); + write_multiple_into_file(outfile, args_info->write_dg_given, "write-dg", args_info->write_dg_orig, 0); + write_multiple_into_file(outfile, args_info->in_file_given, "in-file", args_info->in_file_orig, 0); + write_multiple_into_file(outfile, args_info->delete_dg_given, "delete-dg", args_info->delete_dg_orig, 0); + write_multiple_into_file(outfile, args_info->create_dg_given, "create-dg", args_info->create_dg_orig, 0); + if (args_info->new_size_given) + write_into_file(outfile, "new-size", args_info->new_size_orig, 0); + if (args_info->new_read_ac_given) + write_into_file(outfile, "new-read-ac", args_info->new_read_ac_orig, cmdline_parser_new_read_ac_values); + write_multiple_into_file(outfile, args_info->new_read_ac_chatbit_given, "new-read-ac-chatbit", args_info->new_read_ac_chatbit_orig, 0); + if (args_info->new_write_ac_given) + write_into_file(outfile, "new-write-ac", args_info->new_write_ac_orig, cmdline_parser_new_write_ac_values); + write_multiple_into_file(outfile, args_info->new_write_ac_chatbit_given, "new-write-ac-chatbit", args_info->new_write_ac_chatbit_orig, 0); + + + i = EXIT_SUCCESS; + return i; +} + +int +cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) +{ + FILE *outfile; + int i = 0; + + outfile = fopen(filename, "w"); + + if (!outfile) + { + fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); + return EXIT_FAILURE; + } + + i = cmdline_parser_dump(outfile, args_info); + fclose (outfile); + + return i; +} + +void +cmdline_parser_free (struct gengetopt_args_info *args_info) +{ + cmdline_parser_release (args_info); +} + +/** @brief replacement of strdup, which is not standard */ +char * +gengetopt_strdup (const char *s) +{ + char *result = 0; + if (!s) + return result; + + result = (char*)malloc(strlen(s) + 1); + if (result == (char*)0) + return (char*)0; + strcpy(result, s); + return result; +} + +static char * +get_multiple_arg_token(const char *arg) +{ + const char *tok; + char *ret; + size_t len, num_of_escape, i, j; + + if (!arg) + return 0; + + tok = strchr (arg, ','); + num_of_escape = 0; + + /* make sure it is not escaped */ + while (tok) + { + if (*(tok-1) == '\\') + { + /* find the next one */ + tok = strchr (tok+1, ','); + ++num_of_escape; + } + else + break; + } + + if (tok) + len = (size_t)(tok - arg + 1); + else + len = strlen (arg) + 1; + + len -= num_of_escape; + + ret = (char *) malloc (len); + + i = 0; + j = 0; + while (arg[i] && (j < len-1)) + { + if (arg[i] == '\\' && + arg[ i + 1 ] && + arg[ i + 1 ] == ',') + ++i; + + ret[j++] = arg[i++]; + } + + ret[len-1] = '\0'; + + return ret; +} + +static const char * +get_multiple_arg_token_next(const char *arg) +{ + const char *tok; + + if (!arg) + return 0; + + tok = strchr (arg, ','); + + /* make sure it is not escaped */ + while (tok) + { + if (*(tok-1) == '\\') + { + /* find the next one */ + tok = strchr (tok+1, ','); + } + else + break; + } + + if (! tok || strlen(tok) == 1) + return 0; + + return tok+1; +} + +static int +check_multiple_option_occurrences(const char *prog_name, unsigned int option_given, unsigned int min, unsigned int max, const char *option_desc); + +int +check_multiple_option_occurrences(const char *prog_name, unsigned int option_given, unsigned int min, unsigned int max, const char *option_desc) +{ + int error_occurred = 0; + + if (option_given && (min > 0 || max > 0)) + { + if (min > 0 && max > 0) + { + if (min == max) + { + /* specific occurrences */ + if (option_given != (unsigned int) min) + { + fprintf (stderr, "%s: %s option occurrences must be %d\n", + prog_name, option_desc, min); + error_occurred = 1; + } + } + else if (option_given < (unsigned int) min + || option_given > (unsigned int) max) + { + /* range occurrences */ + fprintf (stderr, "%s: %s option occurrences must be between %d and %d\n", + prog_name, option_desc, min, max); + error_occurred = 1; + } + } + else if (min > 0) + { + /* at least check */ + if (option_given < min) + { + fprintf (stderr, "%s: %s option occurrences must be at least %d\n", + prog_name, option_desc, min); + error_occurred = 1; + } + } + else if (max > 0) + { + /* at most check */ + if (option_given > max) + { + fprintf (stderr, "%s: %s option occurrences must be at most %d\n", + prog_name, option_desc, max); + error_occurred = 1; + } + } + } + + return error_occurred; +} +int +cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info) +{ + return cmdline_parser2 (argc, argv, args_info, 0, 1, 1); +} + +int +cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params) +{ + int result; + result = cmdline_parser_internal (argc, argv, args_info, params, 0); + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) +{ + int result; + struct cmdline_parser_params params; + + params.override = override; + params.initialize = initialize; + params.check_required = check_required; + params.check_ambiguity = 0; + params.print_errors = 1; + + result = cmdline_parser_internal (argc, argv, args_info, ¶ms, 0); + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name) +{ + int result = EXIT_SUCCESS; + + if (cmdline_parser_required2(args_info, prog_name, 0) > 0) + result = EXIT_FAILURE; + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser_required2 (struct gengetopt_args_info *args_info, const char *prog_name, const char *additional_error) +{ + int error_occurred = 0; + FIX_UNUSED (additional_error); + + /* checks for required options */ + if (check_multiple_option_occurrences(prog_name, args_info->verbose_given, args_info->verbose_min, args_info->verbose_max, "'--verbose' ('-v')")) + error_occurred = 1; + + if (args_info->soc_mode_counter && check_multiple_option_occurrences(prog_name, args_info->new_bio_given, args_info->new_bio_min, args_info->new_bio_max, "'--new-bio'")) + error_occurred = 1; + + if (args_info->pxs_mode_counter && check_multiple_option_occurrences(prog_name, args_info->certificate_given, args_info->certificate_min, args_info->certificate_max, "'--certificate' ('-c')")) + error_occurred = 1; + + if (args_info->pxs_mode_counter && check_multiple_option_occurrences(prog_name, args_info->read_dg_given, args_info->read_dg_min, args_info->read_dg_max, "'--read-dg'")) + error_occurred = 1; + + if (args_info->pxs_mode_counter && check_multiple_option_occurrences(prog_name, args_info->out_file_given, args_info->out_file_min, args_info->out_file_max, "'--out-file'")) + error_occurred = 1; + + if (args_info->pxs_mode_counter && check_multiple_option_occurrences(prog_name, args_info->write_dg_given, args_info->write_dg_min, args_info->write_dg_max, "'--write-dg'")) + error_occurred = 1; + + if (args_info->pxs_mode_counter && check_multiple_option_occurrences(prog_name, args_info->in_file_given, args_info->in_file_min, args_info->in_file_max, "'--in-file'")) + error_occurred = 1; + + if (args_info->pxs_mode_counter && check_multiple_option_occurrences(prog_name, args_info->delete_dg_given, args_info->delete_dg_min, args_info->delete_dg_max, "'--delete-dg'")) + error_occurred = 1; + + if (args_info->pxs_mode_counter && check_multiple_option_occurrences(prog_name, args_info->create_dg_given, args_info->create_dg_min, args_info->create_dg_max, "'--create-dg'")) + error_occurred = 1; + + if (args_info->pxs_mode_counter && check_multiple_option_occurrences(prog_name, args_info->new_read_ac_chatbit_given, args_info->new_read_ac_chatbit_min, args_info->new_read_ac_chatbit_max, "'--new-read-ac-chatbit'")) + error_occurred = 1; + + if (args_info->pxs_mode_counter && check_multiple_option_occurrences(prog_name, args_info->new_write_ac_chatbit_given, args_info->new_write_ac_chatbit_min, args_info->new_write_ac_chatbit_max, "'--new-write-ac-chatbit'")) + error_occurred = 1; + + + /* checks for dependences among options */ + if (args_info->certificate_given && ! args_info->key_given) + { + fprintf (stderr, "%s: '--certificate' ('-c') option depends on option 'key'%s\n", prog_name, (additional_error ? additional_error : "")); + error_occurred = 1; + } + if (args_info->key_given && ! args_info->certificate_given) + { + fprintf (stderr, "%s: '--key' ('-k') option depends on option 'certificate'%s\n", prog_name, (additional_error ? additional_error : "")); + error_occurred = 1; + } + if (args_info->new_size_given && ! args_info->create_dg_given) + { + fprintf (stderr, "%s: '--new-size' option depends on option 'create-dg'%s\n", prog_name, (additional_error ? additional_error : "")); + error_occurred = 1; + } + if (args_info->new_read_ac_given && ! args_info->create_dg_given) + { + fprintf (stderr, "%s: '--new-read-ac' option depends on option 'create-dg'%s\n", prog_name, (additional_error ? additional_error : "")); + error_occurred = 1; + } + if (args_info->new_read_ac_chatbit_given && ! args_info->create_dg_given) + { + fprintf (stderr, "%s: '--new-read-ac-chatbit' option depends on option 'create-dg'%s\n", prog_name, (additional_error ? additional_error : "")); + error_occurred = 1; + } + if (args_info->new_write_ac_given && ! args_info->create_dg_given) + { + fprintf (stderr, "%s: '--new-write-ac' option depends on option 'create-dg'%s\n", prog_name, (additional_error ? additional_error : "")); + error_occurred = 1; + } + if (args_info->new_write_ac_chatbit_given && ! args_info->create_dg_given) + { + fprintf (stderr, "%s: '--new-write-ac-chatbit' option depends on option 'create-dg'%s\n", prog_name, (additional_error ? additional_error : "")); + error_occurred = 1; + } + + return error_occurred; +} + + +static char *package_name = 0; + +/** + * @brief updates an option + * @param field the generic pointer to the field to update + * @param orig_field the pointer to the orig field + * @param field_given the pointer to the number of occurrence of this option + * @param prev_given the pointer to the number of occurrence already seen + * @param value the argument for this option (if null no arg was specified) + * @param possible_values the possible values for this option (if specified) + * @param default_value the default value (in case the option only accepts fixed values) + * @param arg_type the type of this option + * @param check_ambiguity @see cmdline_parser_params.check_ambiguity + * @param override @see cmdline_parser_params.override + * @param no_free whether to free a possible previous value + * @param multiple_option whether this is a multiple option + * @param long_opt the corresponding long option + * @param short_opt the corresponding short option (or '-' if none) + * @param additional_error possible further error specification + */ +static +int update_arg(void *field, char **orig_field, + unsigned int *field_given, unsigned int *prev_given, + char *value, const char *possible_values[], + const char *default_value, + cmdline_parser_arg_type arg_type, + int check_ambiguity, int override, + int no_free, int multiple_option, + const char *long_opt, char short_opt, + const char *additional_error) +{ + char *stop_char = 0; + const char *val = value; + int found; + char **string_field; + FIX_UNUSED (field); + + stop_char = 0; + found = 0; + + if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given))) + { + if (short_opt != '-') + fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", + package_name, long_opt, short_opt, + (additional_error ? additional_error : "")); + else + fprintf (stderr, "%s: `--%s' option given more than once%s\n", + package_name, long_opt, + (additional_error ? additional_error : "")); + return 1; /* failure */ + } + + if (possible_values && (found = check_possible_values((value ? value : default_value), possible_values)) < 0) + { + if (short_opt != '-') + fprintf (stderr, "%s: %s argument, \"%s\", for option `--%s' (`-%c')%s\n", + package_name, (found == -2) ? "ambiguous" : "invalid", value, long_opt, short_opt, + (additional_error ? additional_error : "")); + else + fprintf (stderr, "%s: %s argument, \"%s\", for option `--%s'%s\n", + package_name, (found == -2) ? "ambiguous" : "invalid", value, long_opt, + (additional_error ? additional_error : "")); + return 1; /* failure */ + } + + if (field_given && *field_given && ! override) + return 0; + if (prev_given) + (*prev_given)++; + if (field_given) + (*field_given)++; + if (possible_values) + val = possible_values[found]; + + switch(arg_type) { + case ARG_SHORT: + if (val) *((short *)field) = (short)strtol (val, &stop_char, 0); + break; + case ARG_STRING: + if (val) { + string_field = (char **)field; + if (!no_free && *string_field) + free (*string_field); /* free previous string */ + *string_field = gengetopt_strdup (val); + } + break; + default: + break; + }; + + /* check numeric conversion */ + switch(arg_type) { + case ARG_SHORT: + if (val && !(stop_char && *stop_char == '\0')) { + fprintf(stderr, "%s: invalid numeric value: %s\n", package_name, val); + return 1; /* failure */ + } + break; + default: + ; + }; + + /* store the original value */ + switch(arg_type) { + case ARG_NO: + break; + default: + if (value && orig_field) { + if (no_free) { + *orig_field = value; + } else { + if (*orig_field) + free (*orig_field); /* free previous string */ + *orig_field = gengetopt_strdup (value); + } + } + }; + + return 0; /* OK */ +} + +/** + * @brief store information about a multiple option in a temporary list + * @param list where to (temporarily) store multiple options + */ +static +int update_multiple_arg_temp(struct generic_list **list, + unsigned int *prev_given, const char *val, + const char *possible_values[], const char *default_value, + cmdline_parser_arg_type arg_type, + const char *long_opt, char short_opt, + const char *additional_error) +{ + /* store single arguments */ + char *multi_token; + const char *multi_next; + + if (arg_type == ARG_NO) { + (*prev_given)++; + return 0; /* OK */ + } + + multi_token = get_multiple_arg_token(val); + multi_next = get_multiple_arg_token_next (val); + + while (1) + { + add_node (list); + if (update_arg((void *)&((*list)->arg), &((*list)->orig), 0, + prev_given, multi_token, possible_values, default_value, + arg_type, 0, 1, 1, 1, long_opt, short_opt, additional_error)) { + if (multi_token) free(multi_token); + return 1; /* failure */ + } + + if (multi_next) + { + multi_token = get_multiple_arg_token(multi_next); + multi_next = get_multiple_arg_token_next (multi_next); + } + else + break; + } + + return 0; /* OK */ +} + +/** + * @brief free the passed list (including possible string argument) + */ +static +void free_list(struct generic_list *list, short string_arg) +{ + if (list) { + struct generic_list *tmp; + while (list) + { + tmp = list; + if (string_arg && list->arg.string_arg) + free (list->arg.string_arg); + if (list->orig) + free (list->orig); + list = list->next; + free (tmp); + } + } +} + +/** + * @brief updates a multiple option starting from the passed list + */ +static +void update_multiple_arg(void *field, char ***orig_field, + unsigned int field_given, unsigned int prev_given, union generic_value *default_value, + cmdline_parser_arg_type arg_type, + struct generic_list *list) +{ + int i; + struct generic_list *tmp; + + if (prev_given && list) { + *orig_field = (char **) realloc (*orig_field, (field_given + prev_given) * sizeof (char *)); + + switch(arg_type) { + case ARG_SHORT: + *((short **)field) = (short *)realloc (*((short **)field), (field_given + prev_given) * sizeof (short)); break; + case ARG_STRING: + *((char ***)field) = (char **)realloc (*((char ***)field), (field_given + prev_given) * sizeof (char *)); break; + default: + break; + }; + + for (i = (prev_given - 1); i >= 0; --i) + { + tmp = list; + + switch(arg_type) { + case ARG_SHORT: + (*((short **)field))[i + field_given] = tmp->arg.short_arg; break; + case ARG_STRING: + (*((char ***)field))[i + field_given] = tmp->arg.string_arg; break; + default: + break; + } + (*orig_field) [i + field_given] = list->orig; + list = list->next; + free (tmp); + } + } else { /* set the default value */ + if (default_value && ! field_given) { + switch(arg_type) { + case ARG_SHORT: + if (! *((short **)field)) { + *((short **)field) = (short *)malloc (sizeof (short)); + (*((short **)field))[0] = default_value->short_arg; + } + break; + case ARG_STRING: + if (! *((char ***)field)) { + *((char ***)field) = (char **)malloc (sizeof (char *)); + (*((char ***)field))[0] = gengetopt_strdup(default_value->string_arg); + } + break; + default: break; + } + if (!(*orig_field)) { + *orig_field = (char **) malloc (sizeof (char *)); + (*orig_field)[0] = 0; + } + } + } +} + +static int check_modes( + int given1[], const char *options1[], + int given2[], const char *options2[]) +{ + int i = 0, j = 0, errors = 0; + + while (given1[i] >= 0) { + if (given1[i]) { + while (given2[j] >= 0) { + if (given2[j]) { + ++errors; + fprintf(stderr, "%s: option %s conflicts with option %s\n", + package_name, options1[i], options2[j]); + } + ++j; + } + } + ++i; + } + + return errors; +} + +int +cmdline_parser_internal ( + int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params, const char *additional_error) +{ + int c; /* Character of the parsed option. */ + + struct generic_list * certificate_list = NULL; + struct generic_list * read_dg_list = NULL; + struct generic_list * out_file_list = NULL; + struct generic_list * write_dg_list = NULL; + struct generic_list * in_file_list = NULL; + struct generic_list * delete_dg_list = NULL; + struct generic_list * create_dg_list = NULL; + struct generic_list * new_read_ac_chatbit_list = NULL; + struct generic_list * new_write_ac_chatbit_list = NULL; + int error_occurred = 0; + struct gengetopt_args_info local_args_info; + + int override; + int initialize; + int check_required; + int check_ambiguity; + + package_name = argv[0]; + + /* TODO: Why is this here? It is not used anywhere. */ + override = params->override; + FIX_UNUSED(override); + + initialize = params->initialize; + check_required = params->check_required; + + /* TODO: Why is this here? It is not used anywhere. */ + check_ambiguity = params->check_ambiguity; + FIX_UNUSED(check_ambiguity); + + if (initialize) + cmdline_parser_init (args_info); + + cmdline_parser_init (&local_args_info); + + optarg = 0; + optind = 0; + opterr = params->print_errors; + optopt = '?'; + + while (1) + { + int option_index = 0; + + static struct option long_options[] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { "reader", 1, NULL, 'r' }, + { "verbose", 0, NULL, 'v' }, + { "verify-pin", 0, NULL, 'p' }, + { "verify-bio", 0, NULL, 'b' }, + { "verify-pin-or-bio", 0, NULL, 0 }, + { "new-pin", 0, NULL, 0 }, + { "new-bio", 0, NULL, 0 }, + { "info", 0, NULL, 0 }, + { "certificate", 1, NULL, 'c' }, + { "key", 1, NULL, 'k' }, + { "print-cardid", 0, NULL, 0 }, + { "write-cardid", 1, NULL, 0 }, + { "print-paccessid", 0, NULL, 0 }, + { "write-paccessid", 1, NULL, 0 }, + { "read-dg", 1, NULL, 0 }, + { "out-file", 1, NULL, 0 }, + { "write-dg", 1, NULL, 0 }, + { "in-file", 1, NULL, 0 }, + { "delete-dg", 1, NULL, 0 }, + { "create-dg", 1, NULL, 0 }, + { "new-size", 1, NULL, 0 }, + { "new-read-ac", 1, NULL, 0 }, + { "new-read-ac-chatbit", 1, NULL, 0 }, + { "new-write-ac", 1, NULL, 0 }, + { "new-write-ac-chatbit", 1, NULL, 0 }, + { 0, 0, 0, 0 } + }; + + c = getopt_long (argc, argv, "hVr:vpbc:k:", long_options, &option_index); + + if (c == -1) break; /* Exit from `while (1)' loop. */ + + switch (c) + { + case 'h': /* Print help and exit. */ + cmdline_parser_print_help (); + cmdline_parser_free (&local_args_info); + exit (EXIT_SUCCESS); + + case 'V': /* Print version and exit. */ + cmdline_parser_print_version (); + cmdline_parser_free (&local_args_info); + exit (EXIT_SUCCESS); + + case 'r': /* Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen.. */ + + + if (update_arg( (void *)&(args_info->reader_arg), + &(args_info->reader_orig), &(args_info->reader_given), + &(local_args_info.reader_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "reader", 'r', + additional_error)) + goto failure; + + break; + case 'v': /* Use (several times) to be more verbose. */ + + local_args_info.verbose_given++; + + break; + case 'p': /* Verify PIN. */ + + + if (update_arg( 0 , + 0 , &(args_info->verify_pin_given), + &(local_args_info.verify_pin_given), optarg, 0, 0, ARG_NO, + check_ambiguity, override, 0, 0, + "verify-pin", 'p', + additional_error)) + goto failure; + + break; + case 'b': /* Verify finger print. */ + + + if (update_arg( 0 , + 0 , &(args_info->verify_bio_given), + &(local_args_info.verify_bio_given), optarg, 0, 0, ARG_NO, + check_ambiguity, override, 0, 0, + "verify-bio", 'b', + additional_error)) + goto failure; + + break; + case 'c': /* Use (several times) to pass CV certificates. */ + args_info->pxs_mode_counter += 1; + + if (update_multiple_arg_temp(&certificate_list, + &(local_args_info.certificate_given), optarg, 0, 0, ARG_STRING, + "certificate", 'c', + additional_error)) + goto failure; + + break; + case 'k': /* Private key for the CV certificate. */ + args_info->pxs_mode_counter += 1; + + + if (update_arg( (void *)&(args_info->key_arg), + &(args_info->key_orig), &(args_info->key_given), + &(local_args_info.key_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "key", 'k', + additional_error)) + goto failure; + + break; + + case 0: /* Long option with no short option */ + /* Verify PIN or finger print (user's choice). */ + if (strcmp (long_options[option_index].name, "verify-pin-or-bio") == 0) + { + + + if (update_arg( 0 , + 0 , &(args_info->verify_pin_or_bio_given), + &(local_args_info.verify_pin_or_bio_given), optarg, 0, 0, ARG_NO, + check_ambiguity, override, 0, 0, + "verify-pin-or-bio", '-', + additional_error)) + goto failure; + + } + /* Change PIN. */ + else if (strcmp (long_options[option_index].name, "new-pin") == 0) + { + args_info->soc_mode_counter += 1; + + + if (update_arg( 0 , + 0 , &(args_info->new_pin_given), + &(local_args_info.new_pin_given), optarg, 0, 0, ARG_NO, + check_ambiguity, override, 0, 0, + "new-pin", '-', + additional_error)) + goto failure; + + } + /* Use (several times) to change one or more biometric templates. */ + else if (strcmp (long_options[option_index].name, "new-bio") == 0) + { + args_info->soc_mode_counter += 1; + + local_args_info.new_bio_given++; + + } + /* Dump Information about the SoCManager's configuration. */ + else if (strcmp (long_options[option_index].name, "info") == 0) + { + args_info->soc_mode_counter += 1; + + + if (update_arg( 0 , + 0 , &(args_info->info_given), + &(local_args_info.info_given), optarg, 0, 0, ARG_NO, + check_ambiguity, override, 0, 0, + "info", '-', + additional_error)) + goto failure; + + } + /* Print the card ID. */ + else if (strcmp (long_options[option_index].name, "print-cardid") == 0) + { + args_info->pxs_mode_counter += 1; + + + if (update_arg( 0 , + 0 , &(args_info->print_cardid_given), + &(local_args_info.print_cardid_given), optarg, 0, 0, ARG_NO, + check_ambiguity, override, 0, 0, + "print-cardid", '-', + additional_error)) + goto failure; + + } + /* Write the specified card ID. */ + else if (strcmp (long_options[option_index].name, "write-cardid") == 0) + { + args_info->pxs_mode_counter += 1; + + + if (update_arg( (void *)&(args_info->write_cardid_arg), + &(args_info->write_cardid_orig), &(args_info->write_cardid_given), + &(local_args_info.write_cardid_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "write-cardid", '-', + additional_error)) + goto failure; + + } + /* Print the PAccess ID. */ + else if (strcmp (long_options[option_index].name, "print-paccessid") == 0) + { + args_info->pxs_mode_counter += 1; + + + if (update_arg( 0 , + 0 , &(args_info->print_paccessid_given), + &(local_args_info.print_paccessid_given), optarg, 0, 0, ARG_NO, + check_ambiguity, override, 0, 0, + "print-paccessid", '-', + additional_error)) + goto failure; + + } + /* Write the specified PAccess ID. */ + else if (strcmp (long_options[option_index].name, "write-paccessid") == 0) + { + args_info->pxs_mode_counter += 1; + + + if (update_arg( (void *)&(args_info->write_paccessid_arg), + &(args_info->write_paccessid_orig), &(args_info->write_paccessid_given), + &(local_args_info.write_paccessid_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "write-paccessid", '-', + additional_error)) + goto failure; + + } + /* Read the specified data group; use several times to read out multiple files. */ + else if (strcmp (long_options[option_index].name, "read-dg") == 0) + { + args_info->pxs_mode_counter += 1; + + if (update_multiple_arg_temp(&read_dg_list, + &(local_args_info.read_dg_given), optarg, 0, 0, ARG_SHORT, + "read-dg", '-', + additional_error)) + goto failure; + + } + /* Write output to a file instead of printing it; use once for each use of `--read-dg'. */ + else if (strcmp (long_options[option_index].name, "out-file") == 0) + { + args_info->pxs_mode_counter += 1; + + if (update_multiple_arg_temp(&out_file_list, + &(local_args_info.out_file_given), optarg, 0, 0, ARG_STRING, + "out-file", '-', + additional_error)) + goto failure; + + } + /* Write the specified data group; use several times to write multiple files. */ + else if (strcmp (long_options[option_index].name, "write-dg") == 0) + { + args_info->pxs_mode_counter += 1; + + if (update_multiple_arg_temp(&write_dg_list, + &(local_args_info.write_dg_given), optarg, 0, 0, ARG_SHORT, + "write-dg", '-', + additional_error)) + goto failure; + + } + /* Read input from a file; use once for each use of `--write-dg'. */ + else if (strcmp (long_options[option_index].name, "in-file") == 0) + { + args_info->pxs_mode_counter += 1; + + if (update_multiple_arg_temp(&in_file_list, + &(local_args_info.in_file_given), optarg, 0, 0, ARG_STRING, + "in-file", '-', + additional_error)) + goto failure; + + } + /* Delete the specified data group; use several times to delete multiple files. */ + else if (strcmp (long_options[option_index].name, "delete-dg") == 0) + { + args_info->pxs_mode_counter += 1; + + if (update_multiple_arg_temp(&delete_dg_list, + &(local_args_info.delete_dg_given), optarg, 0, 0, ARG_SHORT, + "delete-dg", '-', + additional_error)) + goto failure; + + } + /* Create the specified data group; use several times to create multiple files. */ + else if (strcmp (long_options[option_index].name, "create-dg") == 0) + { + args_info->pxs_mode_counter += 1; + + if (update_multiple_arg_temp(&create_dg_list, + &(local_args_info.create_dg_given), optarg, 0, 0, ARG_SHORT, + "create-dg", '-', + additional_error)) + goto failure; + + } + /* File size of newly created DGs. */ + else if (strcmp (long_options[option_index].name, "new-size") == 0) + { + args_info->pxs_mode_counter += 1; + + + if (update_arg( (void *)&(args_info->new_size_arg), + &(args_info->new_size_orig), &(args_info->new_size_given), + &(local_args_info.new_size_given), optarg, 0, "256", ARG_SHORT, + check_ambiguity, override, 0, 0, + "new-size", '-', + additional_error)) + goto failure; + + } + /* Access condition for reading newly created DGs. */ + else if (strcmp (long_options[option_index].name, "new-read-ac") == 0) + { + args_info->pxs_mode_counter += 1; + + + if (update_arg( (void *)&(args_info->new_read_ac_arg), + &(args_info->new_read_ac_orig), &(args_info->new_read_ac_given), + &(local_args_info.new_read_ac_given), optarg, cmdline_parser_new_read_ac_values, "sm", ARG_STRING, + check_ambiguity, override, 0, 0, + "new-read-ac", '-', + additional_error)) + goto failure; + + } + /* Required access bit in certificate's CHAT for reading newly created DGs. */ + else if (strcmp (long_options[option_index].name, "new-read-ac-chatbit") == 0) + { + args_info->pxs_mode_counter += 1; + + if (update_multiple_arg_temp(&new_read_ac_chatbit_list, + &(local_args_info.new_read_ac_chatbit_given), optarg, 0, 0, ARG_SHORT, + "new-read-ac-chatbit", '-', + additional_error)) + goto failure; + + } + /* Access condition for writing newly created DGs. */ + else if (strcmp (long_options[option_index].name, "new-write-ac") == 0) + { + args_info->pxs_mode_counter += 1; + + + if (update_arg( (void *)&(args_info->new_write_ac_arg), + &(args_info->new_write_ac_orig), &(args_info->new_write_ac_given), + &(local_args_info.new_write_ac_given), optarg, cmdline_parser_new_write_ac_values, "sm", ARG_STRING, + check_ambiguity, override, 0, 0, + "new-write-ac", '-', + additional_error)) + goto failure; + + } + /* Required access bit in certificate's CHAT for reading newly created DGs. */ + else if (strcmp (long_options[option_index].name, "new-write-ac-chatbit") == 0) + { + args_info->pxs_mode_counter += 1; + + if (update_multiple_arg_temp(&new_write_ac_chatbit_list, + &(local_args_info.new_write_ac_chatbit_given), optarg, 0, 0, ARG_SHORT, + "new-write-ac-chatbit", '-', + additional_error)) + goto failure; + + } + + break; + case '?': /* Invalid option. */ + /* `getopt_long' already printed an error message. */ + goto failure; + + default: /* bug: option not considered. */ + fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); + abort (); + } /* switch */ + } /* while */ + + + update_multiple_arg((void *)&(args_info->certificate_arg), + &(args_info->certificate_orig), args_info->certificate_given, + local_args_info.certificate_given, 0, + ARG_STRING, certificate_list); + update_multiple_arg((void *)&(args_info->read_dg_arg), + &(args_info->read_dg_orig), args_info->read_dg_given, + local_args_info.read_dg_given, 0, + ARG_SHORT, read_dg_list); + update_multiple_arg((void *)&(args_info->out_file_arg), + &(args_info->out_file_orig), args_info->out_file_given, + local_args_info.out_file_given, 0, + ARG_STRING, out_file_list); + update_multiple_arg((void *)&(args_info->write_dg_arg), + &(args_info->write_dg_orig), args_info->write_dg_given, + local_args_info.write_dg_given, 0, + ARG_SHORT, write_dg_list); + update_multiple_arg((void *)&(args_info->in_file_arg), + &(args_info->in_file_orig), args_info->in_file_given, + local_args_info.in_file_given, 0, + ARG_STRING, in_file_list); + update_multiple_arg((void *)&(args_info->delete_dg_arg), + &(args_info->delete_dg_orig), args_info->delete_dg_given, + local_args_info.delete_dg_given, 0, + ARG_SHORT, delete_dg_list); + update_multiple_arg((void *)&(args_info->create_dg_arg), + &(args_info->create_dg_orig), args_info->create_dg_given, + local_args_info.create_dg_given, 0, + ARG_SHORT, create_dg_list); + update_multiple_arg((void *)&(args_info->new_read_ac_chatbit_arg), + &(args_info->new_read_ac_chatbit_orig), args_info->new_read_ac_chatbit_given, + local_args_info.new_read_ac_chatbit_given, 0, + ARG_SHORT, new_read_ac_chatbit_list); + update_multiple_arg((void *)&(args_info->new_write_ac_chatbit_arg), + &(args_info->new_write_ac_chatbit_orig), args_info->new_write_ac_chatbit_given, + local_args_info.new_write_ac_chatbit_given, 0, + ARG_SHORT, new_write_ac_chatbit_list); + + args_info->verbose_given += local_args_info.verbose_given; + local_args_info.verbose_given = 0; + args_info->new_bio_given += local_args_info.new_bio_given; + local_args_info.new_bio_given = 0; + args_info->certificate_given += local_args_info.certificate_given; + local_args_info.certificate_given = 0; + args_info->read_dg_given += local_args_info.read_dg_given; + local_args_info.read_dg_given = 0; + args_info->out_file_given += local_args_info.out_file_given; + local_args_info.out_file_given = 0; + args_info->write_dg_given += local_args_info.write_dg_given; + local_args_info.write_dg_given = 0; + args_info->in_file_given += local_args_info.in_file_given; + local_args_info.in_file_given = 0; + args_info->delete_dg_given += local_args_info.delete_dg_given; + local_args_info.delete_dg_given = 0; + args_info->create_dg_given += local_args_info.create_dg_given; + local_args_info.create_dg_given = 0; + args_info->new_read_ac_chatbit_given += local_args_info.new_read_ac_chatbit_given; + local_args_info.new_read_ac_chatbit_given = 0; + args_info->new_write_ac_chatbit_given += local_args_info.new_write_ac_chatbit_given; + local_args_info.new_write_ac_chatbit_given = 0; + + if (args_info->pxs_mode_counter && args_info->soc_mode_counter) { + int pxs_given[] = {args_info->certificate_given, args_info->key_given, args_info->print_cardid_given, args_info->write_cardid_given, args_info->print_paccessid_given, args_info->write_paccessid_given, args_info->read_dg_given, args_info->out_file_given, args_info->write_dg_given, args_info->in_file_given, args_info->delete_dg_given, args_info->create_dg_given, args_info->new_size_given, args_info->new_read_ac_given, args_info->new_read_ac_chatbit_given, args_info->new_write_ac_given, args_info->new_write_ac_chatbit_given, -1}; + const char *pxs_desc[] = {"--certificate", "--key", "--print-cardid", "--write-cardid", "--print-paccessid", "--write-paccessid", "--read-dg", "--out-file", "--write-dg", "--in-file", "--delete-dg", "--create-dg", "--new-size", "--new-read-ac", "--new-read-ac-chatbit", "--new-write-ac", "--new-write-ac-chatbit", 0}; + int soc_given[] = {args_info->new_pin_given, args_info->new_bio_given, args_info->info_given, -1}; + const char *soc_desc[] = {"--new-pin", "--new-bio", "--info", 0}; + error_occurred += check_modes(pxs_given, pxs_desc, soc_given, soc_desc); + } + + if (check_required) + { + error_occurred += cmdline_parser_required2 (args_info, argv[0], additional_error); + } + + cmdline_parser_release (&local_args_info); + + if ( error_occurred ) + return (EXIT_FAILURE); + + return 0; + +failure: + free_list (certificate_list, 1 ); + free_list (read_dg_list, 0 ); + free_list (out_file_list, 1 ); + free_list (write_dg_list, 0 ); + free_list (in_file_list, 1 ); + free_list (delete_dg_list, 0 ); + free_list (create_dg_list, 0 ); + free_list (new_read_ac_chatbit_list, 0 ); + free_list (new_write_ac_chatbit_list, 0 ); + + cmdline_parser_release (&local_args_info); + return (EXIT_FAILURE); +} +/* vim: set ft=c noet ts=8 sts=8 sw=8 tw=80 nojs spell : */ diff --git a/src/tools/goid-tool-cmdline.h b/src/tools/goid-tool-cmdline.h new file mode 100644 index 0000000000..50002c6a7f --- /dev/null +++ b/src/tools/goid-tool-cmdline.h @@ -0,0 +1,282 @@ +/** @file goid-tool-cmdline.h + * @brief The header file for the command line option parser + * generated by GNU Gengetopt version 2.23 + * http://www.gnu.org/software/gengetopt. + * DO NOT modify this file, since it can be overwritten + * @author GNU Gengetopt */ + +#ifndef GOID_TOOL_CMDLINE_H +#define GOID_TOOL_CMDLINE_H + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include /* for FILE */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef CMDLINE_PARSER_PACKAGE +/** @brief the program name (used for printing errors) */ +#define CMDLINE_PARSER_PACKAGE "goid-tool" +#endif + +#ifndef CMDLINE_PARSER_PACKAGE_NAME +/** @brief the complete program name (used for help and version) */ +#define CMDLINE_PARSER_PACKAGE_NAME "goid-tool" +#endif + +#ifndef CMDLINE_PARSER_VERSION +/** @brief the program version */ +#define CMDLINE_PARSER_VERSION VERSION +#endif + +/** @brief Where the command line options are stored */ +struct gengetopt_args_info +{ + const char *help_help; /**< @brief Print help and exit help description. */ + const char *version_help; /**< @brief Print version and exit help description. */ + char * reader_arg; /**< @brief Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen.. */ + char * reader_orig; /**< @brief Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen. original value given at command line. */ + const char *reader_help; /**< @brief Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen. help description. */ + unsigned int verbose_min; /**< @brief Use (several times) to be more verbose's minimum occurreces */ + unsigned int verbose_max; /**< @brief Use (several times) to be more verbose's maximum occurreces */ + const char *verbose_help; /**< @brief Use (several times) to be more verbose help description. */ + const char *verify_pin_help; /**< @brief Verify PIN help description. */ + const char *verify_bio_help; /**< @brief Verify finger print help description. */ + const char *verify_pin_or_bio_help; /**< @brief Verify PIN or finger print (user's choice) help description. */ + const char *new_pin_help; /**< @brief Change PIN help description. */ + unsigned int new_bio_min; /**< @brief Use (several times) to change one or more biometric templates's minimum occurreces */ + unsigned int new_bio_max; /**< @brief Use (several times) to change one or more biometric templates's maximum occurreces */ + const char *new_bio_help; /**< @brief Use (several times) to change one or more biometric templates help description. */ + const char *info_help; /**< @brief Dump Information about the SoCManager's configuration help description. */ + char ** certificate_arg; /**< @brief Use (several times) to pass CV certificates. */ + char ** certificate_orig; /**< @brief Use (several times) to pass CV certificates original value given at command line. */ + unsigned int certificate_min; /**< @brief Use (several times) to pass CV certificates's minimum occurreces */ + unsigned int certificate_max; /**< @brief Use (several times) to pass CV certificates's maximum occurreces */ + const char *certificate_help; /**< @brief Use (several times) to pass CV certificates help description. */ + char * key_arg; /**< @brief Private key for the CV certificate. */ + char * key_orig; /**< @brief Private key for the CV certificate original value given at command line. */ + const char *key_help; /**< @brief Private key for the CV certificate help description. */ + const char *print_cardid_help; /**< @brief Print the card ID help description. */ + char * write_cardid_arg; /**< @brief Write the specified card ID. */ + char * write_cardid_orig; /**< @brief Write the specified card ID original value given at command line. */ + const char *write_cardid_help; /**< @brief Write the specified card ID help description. */ + const char *print_paccessid_help; /**< @brief Print the PAccess ID help description. */ + char * write_paccessid_arg; /**< @brief Write the specified PAccess ID. */ + char * write_paccessid_orig; /**< @brief Write the specified PAccess ID original value given at command line. */ + const char *write_paccessid_help; /**< @brief Write the specified PAccess ID help description. */ + short* read_dg_arg; /**< @brief Read the specified data group; use several times to read out multiple files. */ + char ** read_dg_orig; /**< @brief Read the specified data group; use several times to read out multiple files original value given at command line. */ + unsigned int read_dg_min; /**< @brief Read the specified data group; use several times to read out multiple files's minimum occurreces */ + unsigned int read_dg_max; /**< @brief Read the specified data group; use several times to read out multiple files's maximum occurreces */ + const char *read_dg_help; /**< @brief Read the specified data group; use several times to read out multiple files help description. */ + char ** out_file_arg; /**< @brief Write output to a file instead of printing it; use once for each use of `--read-dg'. */ + char ** out_file_orig; /**< @brief Write output to a file instead of printing it; use once for each use of `--read-dg' original value given at command line. */ + unsigned int out_file_min; /**< @brief Write output to a file instead of printing it; use once for each use of `--read-dg''s minimum occurreces */ + unsigned int out_file_max; /**< @brief Write output to a file instead of printing it; use once for each use of `--read-dg''s maximum occurreces */ + const char *out_file_help; /**< @brief Write output to a file instead of printing it; use once for each use of `--read-dg' help description. */ + short* write_dg_arg; /**< @brief Write the specified data group; use several times to write multiple files. */ + char ** write_dg_orig; /**< @brief Write the specified data group; use several times to write multiple files original value given at command line. */ + unsigned int write_dg_min; /**< @brief Write the specified data group; use several times to write multiple files's minimum occurreces */ + unsigned int write_dg_max; /**< @brief Write the specified data group; use several times to write multiple files's maximum occurreces */ + const char *write_dg_help; /**< @brief Write the specified data group; use several times to write multiple files help description. */ + char ** in_file_arg; /**< @brief Read input from a file; use once for each use of `--write-dg'. */ + char ** in_file_orig; /**< @brief Read input from a file; use once for each use of `--write-dg' original value given at command line. */ + unsigned int in_file_min; /**< @brief Read input from a file; use once for each use of `--write-dg''s minimum occurreces */ + unsigned int in_file_max; /**< @brief Read input from a file; use once for each use of `--write-dg''s maximum occurreces */ + const char *in_file_help; /**< @brief Read input from a file; use once for each use of `--write-dg' help description. */ + short* delete_dg_arg; /**< @brief Delete the specified data group; use several times to delete multiple files. */ + char ** delete_dg_orig; /**< @brief Delete the specified data group; use several times to delete multiple files original value given at command line. */ + unsigned int delete_dg_min; /**< @brief Delete the specified data group; use several times to delete multiple files's minimum occurreces */ + unsigned int delete_dg_max; /**< @brief Delete the specified data group; use several times to delete multiple files's maximum occurreces */ + const char *delete_dg_help; /**< @brief Delete the specified data group; use several times to delete multiple files help description. */ + short* create_dg_arg; /**< @brief Create the specified data group; use several times to create multiple files. */ + char ** create_dg_orig; /**< @brief Create the specified data group; use several times to create multiple files original value given at command line. */ + unsigned int create_dg_min; /**< @brief Create the specified data group; use several times to create multiple files's minimum occurreces */ + unsigned int create_dg_max; /**< @brief Create the specified data group; use several times to create multiple files's maximum occurreces */ + const char *create_dg_help; /**< @brief Create the specified data group; use several times to create multiple files help description. */ + short new_size_arg; /**< @brief File size of newly created DGs (default='256'). */ + char * new_size_orig; /**< @brief File size of newly created DGs original value given at command line. */ + const char *new_size_help; /**< @brief File size of newly created DGs help description. */ + char * new_read_ac_arg; /**< @brief Access condition for reading newly created DGs (default='sm'). */ + char * new_read_ac_orig; /**< @brief Access condition for reading newly created DGs original value given at command line. */ + const char *new_read_ac_help; /**< @brief Access condition for reading newly created DGs help description. */ + short* new_read_ac_chatbit_arg; /**< @brief Required access bit in certificate's CHAT for reading newly created DGs. */ + char ** new_read_ac_chatbit_orig; /**< @brief Required access bit in certificate's CHAT for reading newly created DGs original value given at command line. */ + unsigned int new_read_ac_chatbit_min; /**< @brief Required access bit in certificate's CHAT for reading newly created DGs's minimum occurreces */ + unsigned int new_read_ac_chatbit_max; /**< @brief Required access bit in certificate's CHAT for reading newly created DGs's maximum occurreces */ + const char *new_read_ac_chatbit_help; /**< @brief Required access bit in certificate's CHAT for reading newly created DGs help description. */ + char * new_write_ac_arg; /**< @brief Access condition for writing newly created DGs (default='sm'). */ + char * new_write_ac_orig; /**< @brief Access condition for writing newly created DGs original value given at command line. */ + const char *new_write_ac_help; /**< @brief Access condition for writing newly created DGs help description. */ + short* new_write_ac_chatbit_arg; /**< @brief Required access bit in certificate's CHAT for reading newly created DGs. */ + char ** new_write_ac_chatbit_orig; /**< @brief Required access bit in certificate's CHAT for reading newly created DGs original value given at command line. */ + unsigned int new_write_ac_chatbit_min; /**< @brief Required access bit in certificate's CHAT for reading newly created DGs's minimum occurreces */ + unsigned int new_write_ac_chatbit_max; /**< @brief Required access bit in certificate's CHAT for reading newly created DGs's maximum occurreces */ + const char *new_write_ac_chatbit_help; /**< @brief Required access bit in certificate's CHAT for reading newly created DGs help description. */ + + unsigned int help_given ; /**< @brief Whether help was given. */ + unsigned int version_given ; /**< @brief Whether version was given. */ + unsigned int reader_given ; /**< @brief Whether reader was given. */ + unsigned int verbose_given ; /**< @brief Whether verbose was given. */ + unsigned int verify_pin_given ; /**< @brief Whether verify-pin was given. */ + unsigned int verify_bio_given ; /**< @brief Whether verify-bio was given. */ + unsigned int verify_pin_or_bio_given ; /**< @brief Whether verify-pin-or-bio was given. */ + unsigned int new_pin_given ; /**< @brief Whether new-pin was given. */ + unsigned int new_bio_given ; /**< @brief Whether new-bio was given. */ + unsigned int info_given ; /**< @brief Whether info was given. */ + unsigned int certificate_given ; /**< @brief Whether certificate was given. */ + unsigned int key_given ; /**< @brief Whether key was given. */ + unsigned int print_cardid_given ; /**< @brief Whether print-cardid was given. */ + unsigned int write_cardid_given ; /**< @brief Whether write-cardid was given. */ + unsigned int print_paccessid_given ; /**< @brief Whether print-paccessid was given. */ + unsigned int write_paccessid_given ; /**< @brief Whether write-paccessid was given. */ + unsigned int read_dg_given ; /**< @brief Whether read-dg was given. */ + unsigned int out_file_given ; /**< @brief Whether out-file was given. */ + unsigned int write_dg_given ; /**< @brief Whether write-dg was given. */ + unsigned int in_file_given ; /**< @brief Whether in-file was given. */ + unsigned int delete_dg_given ; /**< @brief Whether delete-dg was given. */ + unsigned int create_dg_given ; /**< @brief Whether create-dg was given. */ + unsigned int new_size_given ; /**< @brief Whether new-size was given. */ + unsigned int new_read_ac_given ; /**< @brief Whether new-read-ac was given. */ + unsigned int new_read_ac_chatbit_given ; /**< @brief Whether new-read-ac-chatbit was given. */ + unsigned int new_write_ac_given ; /**< @brief Whether new-write-ac was given. */ + unsigned int new_write_ac_chatbit_given ; /**< @brief Whether new-write-ac-chatbit was given. */ + + int pxs_mode_counter; /**< @brief Counter for mode pxs */ + int soc_mode_counter; /**< @brief Counter for mode soc */ +} ; + +/** @brief The additional parameters to pass to parser functions */ +struct cmdline_parser_params +{ + int override; /**< @brief whether to override possibly already present options (default 0) */ + int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */ + int check_required; /**< @brief whether to check that all required options were provided (default 1) */ + int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */ + int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */ +} ; + +/** @brief the purpose string of the program */ +extern const char *gengetopt_args_info_purpose; +/** @brief the usage string of the program */ +extern const char *gengetopt_args_info_usage; +/** @brief the description string of the program */ +extern const char *gengetopt_args_info_description; +/** @brief all the lines making the help output */ +extern const char *gengetopt_args_info_help[]; + +/** + * The command line parser + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser (int argc, char **argv, + struct gengetopt_args_info *args_info); + +/** + * The command line parser (version with additional parameters - deprecated) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param override whether to override possibly already present options + * @param initialize whether to initialize the option structure my_args_info + * @param check_required whether to check that all required options were provided + * @return 0 if everything went fine, NON 0 if an error took place + * @deprecated use cmdline_parser_ext() instead + */ +int cmdline_parser2 (int argc, char **argv, + struct gengetopt_args_info *args_info, + int override, int initialize, int check_required); + +/** + * The command line parser (version with additional parameters) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param params additional parameters for the parser + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_ext (int argc, char **argv, + struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params); + +/** + * Save the contents of the option struct into an already open FILE stream. + * @param outfile the stream where to dump options + * @param args_info the option struct to dump + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_dump(FILE *outfile, + struct gengetopt_args_info *args_info); + +/** + * Save the contents of the option struct into a (text) file. + * This file can be read by the config file parser (if generated by gengetopt) + * @param filename the file where to save + * @param args_info the option struct to save + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_file_save(const char *filename, + struct gengetopt_args_info *args_info); + +/** + * Print the help + */ +void cmdline_parser_print_help(void); +/** + * Print the version + */ +void cmdline_parser_print_version(void); + +/** + * Initializes all the fields a cmdline_parser_params structure + * to their default values + * @param params the structure to initialize + */ +void cmdline_parser_params_init(struct cmdline_parser_params *params); + +/** + * Allocates dynamically a cmdline_parser_params structure and initializes + * all its fields to their default values + * @return the created and initialized cmdline_parser_params structure + */ +struct cmdline_parser_params *cmdline_parser_params_create(void); + +/** + * Initializes the passed gengetopt_args_info structure's fields + * (also set default values for options that have a default) + * @param args_info the structure to initialize + */ +void cmdline_parser_init (struct gengetopt_args_info *args_info); +/** + * Deallocates the string fields of the gengetopt_args_info structure + * (but does not deallocate the structure itself) + * @param args_info the structure to deallocate + */ +void cmdline_parser_free (struct gengetopt_args_info *args_info); + +/** + * Checks that all the required options were specified + * @param args_info the structure to check + * @param prog_name the name of the program that will be used to print + * possible errors + * @return + */ +int cmdline_parser_required (struct gengetopt_args_info *args_info, + const char *prog_name); + +extern const char *cmdline_parser_new_read_ac_values[]; /**< @brief Possible values for new-read-ac. */ +extern const char *cmdline_parser_new_write_ac_values[]; /**< @brief Possible values for new-write-ac. */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* GOID_TOOL_CMDLINE_H */ diff --git a/src/tools/goid-tool.c b/src/tools/goid-tool.c new file mode 100644 index 0000000000..c8bdf6f63e --- /dev/null +++ b/src/tools/goid-tool.c @@ -0,0 +1,896 @@ +/* + * Copyright (C) 2018 Frank Morgner + * + * This file is part of OpenSC. + * + * 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 + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fread_to_eof.h" +#include +#include "goid-tool-cmdline.h" +#include "libopensc/asn1.h" +#include "libopensc/log.h" +#include "libopensc/opensc.h" +#include "sm/sm-eac.h" +#ifdef ENABLE_OPENPACE +#include +#endif +#include +#include "util.h" +#include + +const unsigned char aid_soc_manager[] = { + 0xD2, 0x76, 0x00, 0x01, 0x72, 0x53, 0x6F, 0x43, 0x4D, 0x01 +}; +static const unsigned char paccess_aid[] = { + 0xD2, 0x76, 0x00, 0x01, 0x72, 0x50, 0x41, 0x63, 0x63, 0x01, +}; +static const char *app_name = "goid-tool"; + +#define SOCM_AUTHOBJECT_PIN 0x80 +#define SOCM_AUTHOBJECT_BIO 0x40 +#define SOCM_AUTHOBJECT_GP 0x20 + +void +print_permissions(u8 permissions) +{ + size_t perms_printed = 0; + if (permissions & SOCM_AUTHOBJECT_PIN) { + printf("verification of PIN"); + perms_printed++; + } + if (permissions & SOCM_AUTHOBJECT_BIO) { + printf("%s BIO", perms_printed ? " or" : "verification of"); + perms_printed++; + } + if (permissions & SOCM_AUTHOBJECT_GP) { + printf("%s GP key", perms_printed ? " or" : "verification of"); + perms_printed++; + } + printf("\n"); +} + +void +soc_info(sc_context_t *ctx, sc_card_t *card) +{ + sc_apdu_t apdu; + unsigned char rbuf[SC_MAX_APDU_RESP_SIZE]; + u8 information_applets[SC_MAX_APDU_RESP_SIZE]; + size_t information_applets_len = sizeof information_applets; + int pin_initialized = 0, bio_initialized = 0; + int pin_max_retries = 0, pin_cur_retries = 0, bio_max_retries = 0, bio_cur_retries = 0; + int pin_length = 0; + u8 pin_unblock = 0, pin_change = 0, bio_unblock = 0, bio_change = 0; + size_t pin_change_len = sizeof pin_change, pin_unblock_len = sizeof pin_unblock, + bio_change_len = sizeof bio_change, bio_unblock_len = sizeof bio_unblock; + int bio_count = 0; + u8 bio_initialized_templates[2]; + size_t bio_initialized_templates_len = sizeof bio_initialized_templates; + + struct sc_asn1_entry rapdu_get_information[] = { + { "Sequence of (applet register)", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE|SC_ASN1_CONS, 0, NULL, NULL }, + { "Initialized PIN", SC_ASN1_STRUCT, SC_ASN1_APP|SC_ASN1_CONS|0x02, SC_ASN1_OPTIONAL, NULL, NULL }, + { "Initialized BIO", SC_ASN1_STRUCT, SC_ASN1_APP|SC_ASN1_CONS|0x03, SC_ASN1_OPTIONAL, NULL, NULL }, + { NULL , 0 , 0 , 0 , NULL , NULL } + }; + + struct sc_asn1_entry rapdu_get_information_pin[] = { + { "Initialization state of the PIN", SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, 0, NULL, NULL }, + { "maximum remaining tries", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "current remaining tries", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "Unblock requirements Mask", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x1, 0, NULL, NULL }, + { "Change requirements Mask", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x2, 0, NULL, NULL }, + { "PIN size", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { NULL , 0 , 0 , 0 , NULL , NULL } + }; + + struct sc_asn1_entry rapdu_get_information_bio[] = { + { "Initialization state of the BIO", SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, 0, NULL, NULL }, + { "maximum remaining tries", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "current remaining tries", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "Unblock requirements Mask", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x1, 0, NULL, NULL }, + { "Change requirements Mask", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x2, 0, NULL, NULL }, + { "Min minutiae", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "Max minutiae", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "number of templates", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "Bitmap of initialized templates", SC_ASN1_BIT_STRING, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, + { "Algorithm parameters, allocation strategy", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL }, + { NULL , 0 , 0 , 0 , NULL , NULL } + }; + + sc_format_apdu_ex(&apdu, 0x00, 0x61, 0x00, 0x00, NULL, 0, rbuf, sizeof rbuf); + apdu.cla = 0x80; + + if (sc_transmit_apdu(card, &apdu) != SC_SUCCESS) { + return; + } + + sc_format_asn1_entry(rapdu_get_information + 0, information_applets, &information_applets_len, 0); + sc_format_asn1_entry(rapdu_get_information + 1, rapdu_get_information_pin, NULL, 0); + sc_format_asn1_entry(rapdu_get_information + 2, rapdu_get_information_bio, NULL, 0); + + sc_format_asn1_entry(rapdu_get_information_pin + 0, &pin_initialized, NULL, 0); + sc_format_asn1_entry(rapdu_get_information_pin + 1, &pin_max_retries, NULL, 0); + sc_format_asn1_entry(rapdu_get_information_pin + 2, &pin_cur_retries, NULL, 0); + sc_format_asn1_entry(rapdu_get_information_pin + 3, &pin_unblock, &pin_unblock_len, 0); + sc_format_asn1_entry(rapdu_get_information_pin + 4, &pin_change, &pin_change_len, 0); + sc_format_asn1_entry(rapdu_get_information_pin + 5, &pin_length, NULL, 0); + + sc_format_asn1_entry(rapdu_get_information_bio + 0, &bio_initialized, NULL, 0); + sc_format_asn1_entry(rapdu_get_information_bio + 1, &bio_max_retries, NULL, 0); + sc_format_asn1_entry(rapdu_get_information_bio + 2, &bio_cur_retries, NULL, 0); + sc_format_asn1_entry(rapdu_get_information_bio + 3, &bio_unblock, &bio_unblock_len, 0); + sc_format_asn1_entry(rapdu_get_information_bio + 4, &bio_change, &bio_change_len, 0); + sc_format_asn1_entry(rapdu_get_information_bio + 7, &bio_count, NULL, 0); + sc_format_asn1_entry(rapdu_get_information_bio + 8, bio_initialized_templates, &bio_initialized_templates_len, 0); + + if (sc_asn1_decode(ctx, rapdu_get_information, + apdu.resp, apdu.resplen, NULL, NULL) != SC_SUCCESS) { + return; + } + + if (rapdu_get_information[0].flags & SC_ASN1_PRESENT && information_applets_len > 0) { + const unsigned char *p = information_applets, *end = information_applets + information_applets_len; + unsigned int cla = 0, tag = 0; + size_t length = information_applets_len; + + if (SC_SUCCESS == sc_asn1_read_tag(&p, length, &cla, &tag, &length) + && cla == SC_ASN1_TAG_UNIVERSAL && tag == SC_ASN1_TAG_INTEGER) { + int applet_count = 0; + /* number of applets */ + if (SC_SUCCESS == sc_asn1_decode_integer(p, length, &applet_count, 0)) { + printf("SoCManager knows %d applet%s%s\n", applet_count, + applet_count == 1 ? "" : "s", applet_count == 0 ? "" : ":"); + /* AID of client applet #x */ + for (p += length, length = end - p; + p < end; + p += length, length = end - p) { + size_t i; + if (SC_SUCCESS != sc_asn1_read_tag(&p, length, &cla, &tag, &length) + || p == NULL || cla != SC_ASN1_TAG_CONTEXT) { + break; + } + putchar('\t'); + util_hex_dump(stdout, p, length, ""); + /* align with the maximum length of an AID */ + for (i = length; i < 0x10 + 1; i++) + printf(" "); + + /* i now counts the number of flags that were printed */ + i = 0; + if (tag & 0x02) { + printf("default selected"); + i++; + } + if (tag & 0x01) { + printf("%sinteracts with SoCManager", i ? ", " : ""); + i++; + } + if (tag & 0x04) { + printf("%sBIO enabled", i ? ", " : ""); + i++; + } + if (tag & 0x08) { + printf("%sPIN enabled", i ? ", " : ""); + i++; + } + printf("\n"); + } + } + } + } + + if (rapdu_get_information[1].flags & SC_ASN1_PRESENT) { + if (pin_initialized) { + printf("PIN is initialized with %d digits (%d of %d tries left).\n", + pin_length, pin_cur_retries, pin_max_retries); + } else { + printf("PIN is not initialized.\n"); + } + printf("\tChanging PIN requires "); + print_permissions(pin_change); + printf("\tUnblocking PIN requires "); + print_permissions(pin_unblock); + } + + if (rapdu_get_information[2].flags & SC_ASN1_PRESENT) { + if (bio_initialized) { + int bio_used = 0; + size_t i, j; + for (i = 0; i < sizeof bio_initialized_templates; i++) { + for (j = 0; j < 8; j++) { + if (bio_initialized_templates[i] >> j & 0x1) + bio_used++; + } + } + printf("BIO is initialized with %d of %d templates (%d of %d tries left).\n", + bio_used, bio_count, bio_cur_retries, bio_max_retries); + } else { + printf("BIO is not initialized.\n"); + } + printf("\tChanging BIO requires "); + print_permissions(bio_change); + printf("\tUnblocking BIO requires "); + print_permissions(bio_unblock); + } +} + +int +soc_verify(sc_card_t *card, unsigned char p2) +{ + int ok = 0; + sc_apdu_t apdu; + sc_format_apdu_ex(&apdu, 0x00, 0x20, 0x00, p2, NULL, 0, NULL, 0); + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + sc_transmit_apdu(card, &apdu), + "Verification failed"); + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + sc_check_sw(card, apdu.sw1, apdu.sw2), + "Verification failed"); + ok = 1; +err: + return ok; +} + +int +soc_reset(sc_card_t *card, unsigned char p2) +{ + int ok = 0; + sc_apdu_t apdu; + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0xFF, p2); + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + sc_transmit_apdu(card, &apdu), + "Reset failed"); + if (apdu.sw1 != 0x63) { + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + sc_check_sw(card, apdu.sw1, apdu.sw2), + "Reset failed"); + } + ok = 1; +err: + return ok; +} + +int +soc_change(sc_card_t *card, unsigned char p1, unsigned char p2) +{ + int ok = 0; + sc_apdu_t apdu; + sc_format_apdu_ex(&apdu, 0x00, 0x24, p1, p2, NULL, 0, NULL, 0); + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + sc_transmit_apdu(card, &apdu), + "Changing secret failed"); + while (apdu.sw1 == 0x91 && apdu.sw2 == 0x00) { + switch (p2) { + case SOCM_AUTHOBJECT_PIN: + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Verify your PIN on the card using the same position."); + break; + case SOCM_AUTHOBJECT_BIO: + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Verify your finger print on the card using the same position."); + break; + } + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + sc_transmit_apdu(card, &apdu), + "Changing secret failed"); + } + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + sc_check_sw(card, apdu.sw1, apdu.sw2), + "Verification failed"); + ok = 1; +err: + return ok; +} + +int soc_main(struct sc_context *ctx, sc_card_t *card, struct gengetopt_args_info *cmdline) +{ + int ok = 0; + sc_file_t *file = NULL; + struct sc_path path; + unsigned char soc_manager_minor = 0; + unsigned char soc_manager_major = 0; + unsigned char soc_reset_authobject = 0; + + sc_path_set(&path, SC_PATH_TYPE_DF_NAME, aid_soc_manager, sizeof aid_soc_manager, 0, 0); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + sc_select_file(card, &path, &file), "SoCManager not found."); + if (file && file->prop_attr && file->prop_attr_len) { + size_t prop_len = 0; + const u8 *prop = sc_asn1_find_tag(ctx, file->prop_attr, + file->prop_attr_len, 0x81, &prop_len); + if (prop && prop_len == 2) { + soc_manager_major = prop[0]; + soc_manager_minor = prop[1]; + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "SoCManager version %u.%u", + soc_manager_major, soc_manager_minor); + } + } + + if (cmdline->info_given) { + if ((soc_manager_major == 2 && soc_manager_minor < 7) + || soc_manager_major < 2) + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + SC_ERROR_NOT_SUPPORTED, "Get Information only supported with version 2.07 and later."); + soc_info(ctx, card); + } + if (cmdline->verify_pin_given) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Verify PIN on the card."); + if (!soc_verify(card, SOCM_AUTHOBJECT_PIN)) + goto err; + soc_reset_authobject |= SOCM_AUTHOBJECT_PIN; + } + if (cmdline->verify_bio_given) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Verify finger print on the card."); + if (!soc_verify(card, SOCM_AUTHOBJECT_BIO)) + goto err; + soc_reset_authobject |= SOCM_AUTHOBJECT_BIO; + } + if (cmdline->verify_pin_or_bio_given) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Verify finger print or PIN on the card."); + if (!soc_verify(card, SOCM_AUTHOBJECT_PIN|SOCM_AUTHOBJECT_BIO)) + goto err; + soc_reset_authobject |= SOCM_AUTHOBJECT_PIN|SOCM_AUTHOBJECT_BIO; + } + + if (cmdline->new_pin_given) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Initialize the PIN on the card."); + if (!soc_change(card, 0x00, SOCM_AUTHOBJECT_BIO)) + goto err; + } + if (cmdline->new_bio_given) { + size_t i = 0; + while (i < cmdline->new_bio_given) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Initialize finger print template %u on the card.", + (unsigned char) i); + if (!soc_change(card, (unsigned char) i, SOCM_AUTHOBJECT_BIO)) + goto err; + i++; + } + } + + ok = 1; + +err: + if (soc_reset_authobject) + soc_reset(card, soc_reset_authobject); + + return ok; +} + +static int +paccess_construct_fci(struct sc_card *card, const sc_file_t *file, + u8 *out, size_t *outlen) +{ + u8 *p = out; + u8 buf[64]; + + if (*outlen < 2) + return SC_ERROR_BUFFER_TOO_SMALL; + *p++ = 0x62; + p++; + + buf[0] = (file->size >> 8) & 0xFF; + buf[1] = file->size & 0xFF; + sc_asn1_put_tag(0x80, buf, 2, p, *outlen - (p - out), &p); + + buf[0] = (file->id >> 8) & 0xFF; + buf[1] = file->id & 0xFF; + sc_asn1_put_tag(0x83, buf, 2, p, *outlen - (p - out), &p); + + memcpy(buf, file->sec_attr, file->sec_attr_len); + sc_asn1_put_tag(0x86, buf, file->sec_attr_len, + p, *outlen - (p - out), &p); + + buf[0] = file->sid & 0xFF; + sc_asn1_put_tag(0x88, buf, 1, p, *outlen - (p - out), &p); + + out[1] = p - out - 2; + + *outlen = p - out; + return 0; +} + +int +paccess_create_file(struct sc_card *card, size_t size, int fid, u8 *sec_attr, size_t sec_attr_len, int sfid) +{ + int ok = 0; + sc_file_t *file = sc_file_new(); + if (!file) + goto err; + + file->size = size; + file->id = fid; + file->sid = sfid; + file->sec_attr = sec_attr; + file->sec_attr_len = sec_attr_len; + + card->ops->construct_fci = paccess_construct_fci; + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + sc_create_file(card, file), "Create file failed."); + + ok = 1; +err: + return ok; +} + +int +paccess_delete_file(struct sc_card *card, int fid) +{ + int ok = 0; + u8 buf[2]; + struct sc_path path; + buf[0] = (fid >> 8) & 0xFF; + buf[1] = fid & 0xFF; + sc_path_set(&path, SC_PATH_TYPE_FILE_ID, buf, sizeof buf, 0, 0); + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + sc_delete_file(card, &path), "Delete file failed."); + ok = 1; +err: + return ok; +} + +int +paccess_get_security_attributes(struct sc_context *ctx, const char *ac, short *chatbits, size_t chatbits_len, u8 sec_attr[2]) +{ + int ok = 0; + memset(sec_attr, 0, 2); + if (!ac || 0 == strcmp(ac, "never")) { + sec_attr[0] |= 0xFF; + } else if (0 == strcmp(ac, "always")) { + /* nothing else to do */ + } else { + size_t i; + if (0 == strcmp(ac, "ta")) { + sec_attr[0] |= 0xA0; + } else if (0 == strcmp(ac, "sm")) { + sec_attr[0] |= 0xC0; + } else { + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + SC_ERROR_INVALID_ARGUMENTS, "unknown access condition."); + } + for (i = 0; i < chatbits_len; i++) { + u8 byte = chatbits[i] / 8; + u8 bit = chatbits[i] % 8 + 1; + if (byte > 5) + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + SC_ERROR_INVALID_ARGUMENTS, "Only CHAT bits with index 0..39 are available."); + sec_attr[0] |= 0x8 | byte; + sec_attr[1] |= bit; + } + } + ok = 1; +err: + return ok; +} + +#define PXS_AUTHOBJECT_PIN 0x80 +#define PXS_AUTHOBJECT_BIO 0x40 + +int +paccess_verify(sc_card_t *card, unsigned char p2) +{ + int ok = 0; + sc_apdu_t apdu; + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2E, 0x24, p2); + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + sc_transmit_apdu(card, &apdu), + "Verification failed"); + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + sc_check_sw(card, apdu.sw1, apdu.sw2), + "Verification failed"); + ok = 1; +err: + return ok; +} + +int paccess_main(struct sc_context *ctx, sc_card_t *card, struct gengetopt_args_info *cmdline) +{ + int ok = 0, r; + sc_file_t *file = NULL; + struct sc_path path; + size_t i, ef_cardsecurity_len = 0, privkey_len = 0, *certs_lens = NULL; + unsigned char *ef_cardsecurity = NULL, *privkey = NULL, + **certs = NULL; + unsigned char auxiliary_data[] = {0x67, 0x00}; + unsigned char paccess_minor = 0; + unsigned char paccess_major = 0; + int pxs_reset_authobjects = 0; + + sc_path_set(&path, SC_PATH_TYPE_DF_NAME, paccess_aid, sizeof paccess_aid, 0, 0); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + sc_select_file(card, &path, &file), "PAccess not found."); + if (file && file->prop_attr && file->prop_attr_len) { + const unsigned char *p, *end; + unsigned int cla = 0, tag = 0; + size_t length; + + for (p = file->prop_attr, length = file->prop_attr_len, end = file->prop_attr + file->prop_attr_len; + p < end; + p += length, length = end - p) { + if (SC_SUCCESS != sc_asn1_read_tag(&p, length, &cla, &tag, &length) + || p == NULL) { + break; + } + switch (cla | tag) { + case 0x81: + if (p && length == 2) { + paccess_major = p[0]; + paccess_minor = p[1]; + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "PAccess version %u.%u", + paccess_major, paccess_minor); + } + break; + case 0x82: + if (p && length == 1) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Number of Session Contexts %u", + p[0]); + } + break; + case 0x87: + sc_debug_hex(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Certificate Authority Reference of the primary CVCA trust anchor", + p, length); + break; + case 0x88: + sc_debug_hex(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Certificate Authority Reference of the secondary CVCA trust anchor", + p, length); + break; + case 0x1fe5: + case 0x9F65: + if (p && length == 2) { + size_t max_command_size = (p[0]<<8)|p[1]; + card->max_recv_size = max_command_size; + card->max_send_size = max_command_size; + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Maximum data length in command message %"SC_FORMAT_LEN_SIZE_T"u bytes", + max_command_size); + } + break; + } + } + } + + if (cmdline->certificate_given || cmdline->key_given) { + if (!fread_to_eof(cmdline->key_arg, + &privkey, &privkey_len)) { + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + SC_ERROR_INVALID_ARGUMENTS, "Could not parse private key.\n"); + } + + certs = calloc(cmdline->certificate_given + 1, sizeof *certs); + certs_lens = calloc(cmdline->certificate_given + 1, + sizeof *certs_lens); + if (!certs || !certs_lens) { + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_NOT_ENOUGH_MEMORY, + "Internal error."); + } + for (i = 0; i < cmdline->certificate_given; i++) { + if (!fread_to_eof(cmdline->certificate_arg[i], + (unsigned char **) &certs[i], &certs_lens[i])) { + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + SC_ERROR_INVALID_ARGUMENTS, "Could not read certificate.\n"); + } + } + + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + perform_terminal_authentication(card, + (const unsigned char **) certs, certs_lens, + privkey, privkey_len, + auxiliary_data, sizeof auxiliary_data), + "Terminal authentication failed."); + + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + perform_chip_authentication(card, + &ef_cardsecurity, &ef_cardsecurity_len), + "Chip authentication failed."); + } + + if (cmdline->verify_pin_given) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Verify PIN on the card."); + if (!paccess_verify(card, PXS_AUTHOBJECT_PIN)) + goto err; + pxs_reset_authobjects++; + } + if (cmdline->verify_bio_given) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Verify finger print on the card."); + if (!paccess_verify(card, PXS_AUTHOBJECT_BIO)) + goto err; + pxs_reset_authobjects++; + } + if (cmdline->verify_pin_or_bio_given) { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Verify finger print or PIN on the card."); + if (!paccess_verify(card, PXS_AUTHOBJECT_PIN|PXS_AUTHOBJECT_BIO)) + goto err; + pxs_reset_authobjects++; + } + + for (i = 0; i < cmdline->delete_dg_given; i++) { + int fid = 0x0100 | cmdline->delete_dg_arg[i]; + + if ((paccess_major == 2 && paccess_minor < 6) + || paccess_major < 2) + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + SC_ERROR_NOT_SUPPORTED, "Create File only supported with version 2.06 and later."); + if (!paccess_delete_file(card, fid)) + goto err; + } + + for (i = 0; i < cmdline->create_dg_given; i++) { + u8 sec_attr[4]; + int fid = 0x0100 | cmdline->create_dg_arg[i]; + + if ((paccess_major == 2 && paccess_minor < 6) + || paccess_major < 2) + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + SC_ERROR_NOT_SUPPORTED, "Create File only supported with version 2.06 and later."); + + if (cmdline->new_size_arg < 0) + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + SC_ERROR_INVALID_ARGUMENTS, "`--new-size' needs a positive size.\n"); + if (!paccess_get_security_attributes(ctx, cmdline->new_read_ac_arg, + cmdline->new_read_ac_chatbit_arg, + cmdline->new_read_ac_chatbit_given, sec_attr + 0) + || !paccess_get_security_attributes(ctx, cmdline->new_write_ac_arg, + cmdline->new_write_ac_chatbit_arg, + cmdline->new_write_ac_chatbit_given, sec_attr + 2) + || !paccess_create_file(card, cmdline->new_size_arg, fid, + sec_attr, sizeof sec_attr, cmdline->create_dg_arg[i])) + goto err; + } + + if (cmdline->out_file_given > 0 && cmdline->out_file_given != cmdline->read_dg_given) { + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + SC_ERROR_INVALID_ARGUMENTS, "If `--out-file' is specified, it must be used as many times as `--read-dg'.\n"); + } + + for (i = 0; i < cmdline->read_dg_given; i++) { + u8 *ef = NULL; + size_t ef_len = 0; + r = iso7816_read_binary_sfid(card, cmdline->read_dg_arg[i], + &ef, &ef_len); + if (r >= 0) { + if (cmdline->out_file_given == cmdline->read_dg_given) { + FILE *f = fopen(cmdline->out_file_arg[i], "wb"); + if (f) { + fwrite(ef, ef_len, 1, f); + fclose(f); + } else { + sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + "Error opening %s: %s\n", + cmdline->out_file_arg[i], strerror(errno)); + r = SC_ERROR_FILE_NOT_FOUND; + } + } else { + char label[32]; + snprintf(label, sizeof label, "Data Group %u", (unsigned char) cmdline->read_dg_arg[i]); + sc_debug_hex(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, label, ef, ef_len); + } + free(ef); + } + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, + "Error reading data group."); + } + + if (cmdline->print_cardid_given) { + u8 *ef = NULL; + size_t ef_len = 0; + r = iso7816_read_binary_sfid(card, 0x1E, &ef, &ef_len); + if (r >= 0) { + const u8 *p = ef; + unsigned int cla = 0, tag = 0; + if (SC_SUCCESS == sc_asn1_read_tag(&p, ef_len, + &cla, &tag, &ef_len) + && (tag | cla) == 0x7E + && SC_SUCCESS == sc_asn1_read_tag(&p, ef_len, + &cla, &tag, &ef_len) + && (tag | cla) == 0x13) { + const unsigned char *cardid = (const unsigned char *) p; + while (cardid && ef_len) { + if (isprint(*cardid)) { + printf("%c", *cardid); + } else { + printf("."); + } + cardid++; + ef_len--; + } + if (cardid) + printf("\n"); + } else { + r = SC_ERROR_INVALID_DATA; + } + } + free(ef); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, + "Error reading card ID."); + } + + if (cmdline->print_paccessid_given) { + u8 *ef = NULL; + size_t ef_len = 0; + r = iso7816_read_binary_sfid(card, 0x06, &ef, &ef_len); + if (r >= 0) { + const u8 *p = ef; + unsigned int cla = 0, tag = 0; + if (SC_SUCCESS == sc_asn1_read_tag(&p, ef_len, + &cla, &tag, &ef_len) + && (tag | cla) == 0x66 + && SC_SUCCESS == sc_asn1_read_tag((const u8 **) &p, ef_len, + &cla, &tag, &ef_len) + && (tag | cla) == 0x13) { + const unsigned char *paccessid = (const unsigned char *) p; + while (paccessid && ef_len) { + if (isprint(*paccessid)) { + printf("%c", *paccessid); + } else { + printf("."); + } + paccessid++; + ef_len--; + } + if (paccessid) + printf("\n"); + } else { + r = SC_ERROR_INVALID_DATA; + } + } + free(ef); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, + "Error reading card ID."); + } + + if (cmdline->in_file_given != cmdline->write_dg_given) { + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + SC_ERROR_INVALID_ARGUMENTS, "If `--in-file' is specified, it must be used as many times as `--write-dg'.\n"); + } + + for (i = 0; i < cmdline->write_dg_given; i++) { + u8 *ef = NULL; + size_t ef_len = 0; + if (!fread_to_eof(cmdline->in_file_arg[i], + &ef, &ef_len)) { + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, + SC_ERROR_INVALID_ARGUMENTS, "Could not read input file.\n"); + } + r = iso7816_update_binary_sfid(card, cmdline->write_dg_arg[i], ef, ef_len); + free(ef); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, + "Error writing data group."); + } + + if (cmdline->write_cardid_arg) { + size_t cardid_len = strlen(cmdline->write_cardid_arg); + u8 ef[256]; + if (cardid_len > (sizeof ef) - 4) { + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_INVALID_ARGUMENTS, + "Card ID too long."); + } + ef[0] = 0x7E; + ef[1] = 2 + cardid_len; + ef[2] = 0x13; + ef[3] = cardid_len; + memcpy(ef + 4, cmdline->write_cardid_arg, cardid_len); + r = iso7816_update_binary_sfid(card, 0x1E, ef, 4 + cardid_len); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, + "Error writing card ID."); + } + + if (cmdline->write_paccessid_arg) { + size_t paccessid_len = strlen(cmdline->write_paccessid_arg); + u8 ef[256]; + if (paccessid_len > (sizeof ef) - 4) { + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_INVALID_ARGUMENTS, + "Card ID too long."); + } + ef[0] = 0x66; + ef[1] = 2 + paccessid_len; + ef[2] = 0x13; + ef[3] = paccessid_len; + memcpy(ef + 4, cmdline->write_paccessid_arg, paccessid_len); + r = iso7816_update_binary_sfid(card, 0x06, ef, 4 + paccessid_len); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, + "Error writing PAccess ID."); + } + + ok = 1; + +err: + if (pxs_reset_authobjects) + sc_reset(card, 0); + if (certs) { + for (i = 0; certs[i]; i++) { + free((unsigned char *) certs[i]); + } + free(certs); + } + free(ef_cardsecurity); + free(certs_lens); + free(privkey); + sc_file_free(file); + + return ok; +} + +int +main(int argc, char **argv) +{ + struct gengetopt_args_info cmdline; + struct sc_context *ctx = NULL; + struct sc_card *card = NULL; + int r, fail = 1; + sc_context_param_t ctx_param; + + if (cmdline_parser(argc, argv, &cmdline) != 0) + exit(1); + + memset(&ctx_param, 0, sizeof(ctx_param)); + ctx_param.ver = 0; + ctx_param.app_name = app_name; + ctx_param.debug = cmdline.verbose_given; + if (cmdline.verbose_given > 1) + ctx_param.debug_file = stderr; + + r = sc_context_create(&ctx, &ctx_param); + if (r) { + fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); + exit(1); + } + + r = sc_set_card_driver(ctx, "default"); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, + "Error selecting card driver."); + + r = util_connect_card_ex(ctx, &card, cmdline.reader_arg, 0, 0); + SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, + "Error connecting to card."); + + if (cmdline.soc_mode_counter && !soc_main(ctx, card, &cmdline)) + goto err; + if (cmdline.pxs_mode_counter && !paccess_main(ctx, card, &cmdline)) + goto err; + if (cmdline.soc_mode_counter == 0 && cmdline.pxs_mode_counter == 0 + && (cmdline.verify_pin_given + || cmdline.verify_bio_given + || cmdline.verify_pin_or_bio_given) + && !soc_main(ctx, card, &cmdline)) + goto err; + + fail = 0; + +err: + sc_disconnect_card(card); + sc_release_context(ctx); + cmdline_parser_free (&cmdline); + + return fail; +} +/*printf("%s:%d\n", __FILE__, __LINE__);*/ diff --git a/src/tools/goid-tool.ggo.in b/src/tools/goid-tool.ggo.in new file mode 100644 index 0000000000..7d8b126cce --- /dev/null +++ b/src/tools/goid-tool.ggo.in @@ -0,0 +1,87 @@ +package "goid-tool" +purpose "@PACKAGE_SUMMARY@" + +option "reader" r + "Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen." + string optional +option "verbose" v + "Use (several times) to be more verbose" + multiple optional +option "verify-pin" p + "Verify PIN" + optional +option "verify-bio" b + "Verify finger print" + optional +option "verify-pin-or-bio" - + "Verify PIN or finger print (user's choice)" + optional + +defmode "soc" modedesc="Options for SoCManager Applet" +modeoption "new-pin" - + "Change PIN" + mode="soc" optional +modeoption "new-bio" - + "Use (several times) to change one or more biometric templates" + mode="soc" multiple optional +modeoption "info" - + "Dump Information about the SoCManager's configuration" + mode="soc" optional + +defmode "pxs" modedesc="Options for PAccess Applet" +modeoption "certificate" c + "Use (several times) to pass CV certificates" + string mode="pxs" multiple optional dependon="key" typestr="FILENAME" +modeoption "key" k + "Private key for the CV certificate" + string mode="pxs" optional dependon="certificate" typestr="FILENAME" +modeoption "print-cardid" - + "Print the card ID" + mode="pxs" optional +modeoption "write-cardid" - + "Write the specified card ID" + string mode="pxs" optional typestr="CARDID" +modeoption "print-paccessid" - + "Print the PAccess ID" + mode="pxs" optional +modeoption "write-paccessid" - + "Write the specified PAccess ID" + string mode="pxs" optional typestr="PACCESSID" +modeoption "read-dg" - + "Read the specified data group; use several times to read out multiple files" + short mode="pxs" optional multiple typestr="ID" +modeoption "out-file" - + "Write output to a file instead of printing it; use once for each use of `--read-dg'" + string mode="pxs" optional multiple typestr="FILENAME" +modeoption "write-dg" - + "Write the specified data group; use several times to write multiple files" + short mode="pxs" optional multiple typestr="ID" +modeoption "in-file" - + "Read input from a file; use once for each use of `--write-dg'" + string mode="pxs" optional multiple typestr="FILENAME" +modeoption "delete-dg" - + "Delete the specified data group; use several times to delete multiple files" + short mode="pxs" optional multiple typestr="ID" +modeoption "create-dg" - + "Create the specified data group; use several times to create multiple files" + short mode="pxs" optional multiple typestr="ID" +modeoption "new-size" - + "File size of newly created DGs" + short mode="pxs" optional dependon="create-dg" typestr="SIZE" default="256" +modeoption "new-read-ac" - + "Access condition for reading newly created DGs" + values="always","never","ta","sm" default="sm" mode="pxs" optional dependon="create-dg" +modeoption "new-read-ac-chatbit" - + "Required access bit in certificate's CHAT for reading newly created DGs" + short mode="pxs" optional dependon="create-dg" multiple typestr="INDEX" +modeoption "new-write-ac" - + "Access condition for writing newly created DGs" + values="always","never","ta","sm" default="sm" mode="pxs" optional dependon="create-dg" +modeoption "new-write-ac-chatbit" - + "Required access bit in certificate's CHAT for reading newly created DGs" + short mode="pxs" optional dependon="create-dg" multiple typestr="INDEX" + +text " +Report bugs to @PACKAGE_BUGREPORT@ + +Written by Frank Morgner " diff --git a/src/tools/iasecc-tool.c b/src/tools/iasecc-tool.c index 6b79494b17..1b279dae08 100644 --- a/src/tools/iasecc-tool.c +++ b/src/tools/iasecc-tool.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -68,7 +68,7 @@ static const char *option_help[] = { "List the on-card PKCS#15 applications", "List the SDOs with the tag in the current ADF", "Wait for card insertion", - "Verbose operation. Use several times to enable debug output.", + "Verbose operation, may be used several times", NULL }; @@ -124,20 +124,21 @@ static int list_sdos(char *sdo_tag) { struct iasecc_sdo sdo; struct iasecc_se_info se; - unsigned sdo_class = 0; + unsigned char sdo_class = 0; + long sdo_class_l; int rv, ii, jj; if (!sdo_tag) goto usage; if (*sdo_tag == 'x' || *sdo_tag == 'X') - sdo_class = strtol(sdo_tag + 1, NULL, 16); + sdo_class_l = strtol(sdo_tag + 1, NULL, 16); else if ((strlen(sdo_tag) > 2) && (*(sdo_tag + 1) == 'x' || *(sdo_tag + 1) == 'X')) - sdo_class = strtol(sdo_tag + 2, NULL, 16); + sdo_class_l = strtol(sdo_tag + 2, NULL, 16); else - sdo_class = strtol(sdo_tag, NULL, 10); + sdo_class_l = strtol(sdo_tag, NULL, 10); - sdo_class &= 0x7F; + sdo_class = sdo_class_l & 0x7F; if (sdo_class == IASECC_SDO_CLASS_SE) { for (ii=1; ii<0x20; ii++) { memset(&se, 0, sizeof(se)); @@ -197,7 +198,7 @@ static int list_apps(FILE *fout) return 0; } -int main(int argc, char * const argv[]) +int main(int argc, char *argv[]) { int err = 0, r, c, long_optind = 0; int do_list_sdos = 0; @@ -205,9 +206,6 @@ int main(int argc, char * const argv[]) int action_count = 0; sc_context_param_t ctx_param; - setbuf(stderr, NULL); - setbuf(stdout, NULL); - while (1) { c = getopt_long(argc, argv, "v", options, &long_optind); if (c == -1) @@ -240,6 +238,9 @@ int main(int argc, char * const argv[]) memset(&ctx_param, 0, sizeof(sc_context_param_t)); ctx_param.app_name = app_name; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; r = sc_context_create(&ctx, &ctx_param); if (r != SC_SUCCESS) { @@ -247,20 +248,14 @@ int main(int argc, char * const argv[]) return 1; } - /* Only change if not in opensc.conf */ - if (verbose > 1 && ctx->debug == 0) { - ctx->debug = verbose; - sc_ctx_log_to_file(ctx, "stderr"); - } - if (action_count <= 0) goto end; - err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); + err = util_connect_card(ctx, &card, opt_reader, opt_wait); if (err) goto end; - if (opt_bind_to_aid) { + if (opt_bind_to_aid) { struct sc_aid aid; aid.len = sizeof(aid.value); @@ -274,6 +269,10 @@ int main(int argc, char * const argv[]) else if (!do_list_sdos) { r = sc_pkcs15_bind(card, NULL, &p15card); } + if (r != SC_SUCCESS) { + fprintf(stderr, "Failed to bind card: %s\n", sc_strerror(r)); + goto end; + } if (do_list_sdos) { if ((err = list_sdos(opt_sdo_tag))) @@ -293,8 +292,7 @@ int main(int argc, char * const argv[]) sc_unlock(card); sc_disconnect_card(card); } - if (ctx) - sc_release_context(ctx); + sc_release_context(ctx); return err; } diff --git a/src/tools/lteid-tool.c b/src/tools/lteid-tool.c new file mode 100644 index 0000000000..3a8c6e9c42 --- /dev/null +++ b/src/tools/lteid-tool.c @@ -0,0 +1,591 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "common/compat_strlcat.h" +#include "libopensc/opensc.h" +#include "libopensc/pkcs15.h" +#include "sm/sm-eac.h" +#include "util.h" + +/* win32 needs this in open(2) */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +static const char *app_name = "lteid-tool"; + +#define OP_NONE 0 /* no operation requested */ +#define OP_RESUME 1 /* resume pin entry using CAN code */ +#define OP_UNBLOCK 2 /* unblock using PUK code */ +#define OP_CHANGE_PIN 3 + +static const struct option options[] = { + {"help", 0, NULL, 'h'}, + {"verbose", 0, NULL, 'v'}, + {"reader", 1, NULL, 'r'}, + {"wait", 0, NULL, 'w'}, + {"can", 1, NULL, 'c'}, + {"pin", 1, NULL, 'p'}, + {"puk", 1, NULL, 'u'}, + {"change-pin", 0, NULL, 'C'}, + {"resume", 0, NULL, 'R'}, + {"unblock", 0, NULL, 'U'}, + {"verify-can", 0, NULL, 'V'}, + {NULL, 0, NULL, 0 } +}; + +static const char *option_help[] = { + "Display tool options", + "Display all the information available", + "Uses reader number [0]", + "Wait for a card to be inserted", + "Specify CAN", + "Specify PIN", + "Specify PUK", + "Change PIN", + "Resume authentication key PIN after 2 incorrect attempts", + "Unblock PIN using PUK", + "Verify Card Access Number (CAN)", +}; + +static int +get_tries_left(sc_pkcs15_card_t *p15card, u8 pin_reference, int *pin_tries_left) +{ + int rv, i; + struct sc_pkcs15_object *objs[32]; + + *pin_tries_left = -1; + + rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH, objs, 32); + if (rv < 0) { + fprintf(stderr, "AUTH objects enumeration failed: %s\n", sc_strerror(rv)); + return 1; + } + + for (i = 0; i < rv; i++) { + sc_pkcs15_get_pin_info(p15card, objs[i]); + + const struct sc_pkcs15_auth_info *auth_info = (const struct sc_pkcs15_auth_info *)objs[i]->data; + + if (auth_info->auth_id.len == 1 && auth_info->auth_id.value[0] == pin_reference) { + *pin_tries_left = auth_info->tries_left; + return SC_SUCCESS; + } + } + + return SC_ERROR_OBJECT_NOT_FOUND; +} + +static int +display_pin_tries_left(sc_pkcs15_card_t *p15card) +{ + int pace_pin_tries_left, qes_pin_tries_left, puk_tries_left; + + get_tries_left(p15card, PACE_PIN_ID_PUK, &puk_tries_left); + get_tries_left(p15card, PACE_PIN_ID_PIN, &pace_pin_tries_left); + get_tries_left(p15card, 0x81, &qes_pin_tries_left); + + printf("\n"); + printf("PUK tries left: %i\n", puk_tries_left); + printf("PIN (for authentication) tries left: %i\n", pace_pin_tries_left); + printf("PIN (for electronic signatures) tries left: %i\n", qes_pin_tries_left); + printf("\n"); + + return SC_SUCCESS; +} + +static int +display_basic_details(sc_pkcs15_card_t *p15card) +{ + printf("\nCard label: %s\n", p15card->tokeninfo->label); + printf("Serial number: %s\n", p15card->tokeninfo->serial_number); + + display_pin_tries_left(p15card); + + return SC_SUCCESS; +} + +int +input_number(const char *description, size_t min_len, size_t max_len, const char *provided_via_cli, char **number) +{ + size_t number_len = 0; + + if (provided_via_cli && strlen(provided_via_cli) >= min_len && strlen(provided_via_cli) <= max_len) { + *number = strdup(provided_via_cli); + + printf("Using %s provided via command line arguments.\n", description); + + return SC_SUCCESS; + } + + printf("Enter %s ", description); + + if (min_len == max_len) { + printf("(%zu digits): ", min_len); + } else { + printf("(%zu..%zu digits): ", min_len, max_len); + } + + number_len = util_getpass(number, NULL, stdin); + + if (number_len < min_len || number_len > max_len) { + free(*number); + *number = NULL; + return SC_ERROR_INTERNAL; + } + + for (size_t i = 0; i < number_len; i++) { + if ((*number)[i] < '0' || (*number)[i] > '9') { + free(*number); + *number = NULL; + return SC_ERROR_INTERNAL; + } + } + + return SC_SUCCESS; +} + +int +verify_and_cache_pace_can(sc_card_t *card, const char *opt_can) +{ + int rv; + struct sc_path path; + char *can = NULL; + + rv = input_number("number from the bottom right corner of the card", 6, 6, opt_can, &can); + + if (rv != SC_SUCCESS) { + fprintf(stderr, "CAN number blank, too short or too long.\n"); + return SC_ERROR_PIN_CODE_INCORRECT; + } + + sc_format_path("3F00", &path); + rv = sc_select_file(card, &path, NULL); + if (rv != SC_SUCCESS) { + fprintf(stderr, "Failed to select MF.\n"); + return rv; + } + + struct sc_pin_cmd_data can_verify_cmd = {0}; + can_verify_cmd.cmd = SC_PIN_CMD_VERIFY; + can_verify_cmd.pin_type = SC_AC_CHV; + can_verify_cmd.pin_reference = PACE_PIN_ID_CAN; + can_verify_cmd.pin1.data = (unsigned char *)can; + can_verify_cmd.pin1.len = strlen(can); + + rv = card->ops->pin_cmd(card, &can_verify_cmd); + + if (rv != SC_SUCCESS) { + fprintf(stderr, "CAN number verification failed: %s\nCheck the number and try again.\n", sc_strerror(rv)); + return SC_ERROR_PIN_CODE_INCORRECT; + } + + printf("\nThe new CAN code is now persisted.\n"); + printf("\nIf you are about to use the card in your web browser - you may have to remove and re-insert it.\n"); + + return SC_SUCCESS; +} + +/* + * Officially card has a single PIN. But under the hood it's really two separate PINs: + * + * - PACE-PIN with ID 0x03, tied to the key intended for authentication. + * - PIN.QES with ID 0x81, tied to the key intended for signature + * + * The procedure below follows this and applies change to both PINs. + */ +int +change_pin(sc_card_t *card, const char *opt_pin) +{ + int rv; + struct sc_path path; + char *pin = NULL; + char *new_pin = NULL; + char *new_pin_repeated = NULL; + unsigned char pace_pin_changed = 0, qes_pin_changed = 0; + + rv = input_number("Current PIN", 6, 12, opt_pin, &pin); + + if (rv != SC_SUCCESS) { + fprintf(stderr, "PIN number blank, too short or too long.\n"); + return SC_ERROR_PIN_CODE_INCORRECT; + } + + sc_format_path("3F00", &path); + rv = sc_select_file(card, &path, NULL); + if (rv != SC_SUCCESS) { + fprintf(stderr, "Failed to select MF.\n"); + return rv; + } + + struct sc_pin_cmd_data pin_verify_cmd = {0}; + pin_verify_cmd.cmd = SC_PIN_CMD_VERIFY; + pin_verify_cmd.pin_type = SC_AC_CHV; + pin_verify_cmd.pin_reference = PACE_PIN_ID_PIN; + pin_verify_cmd.pin1.data = (unsigned char *)pin; + pin_verify_cmd.pin1.len = strlen(pin); + + rv = card->ops->pin_cmd(card, &pin_verify_cmd); + + if (rv != SC_SUCCESS) { + fprintf(stderr, "PIN code verification failed: %s\n", sc_strerror(rv)); + return SC_ERROR_PIN_CODE_INCORRECT; + } + + rv = input_number("New PIN", 6, 12, NULL, &new_pin); + + if (rv != SC_SUCCESS) { + fprintf(stderr, "PIN number blank, too short or too long.\n"); + return SC_ERROR_PIN_CODE_INCORRECT; + } + + input_number("New PIN (repeat)", 6, 12, NULL, &new_pin_repeated); + + if (new_pin_repeated == NULL || strcmp(new_pin, new_pin_repeated) != 0) { + fprintf(stderr, "New PIN and repeated entry do not match. PIN was not changed.\n"); + return SC_ERROR_INTERNAL; + } + + printf("\n"); + + struct sc_pin_cmd_data pace_pin_cmd = {0}; + pace_pin_cmd.cmd = SC_PIN_CMD_CHANGE; + pace_pin_cmd.pin_type = SC_AC_CHV; + pace_pin_cmd.pin_reference = PACE_PIN_ID_PIN; + pace_pin_cmd.pin2.data = (unsigned char *)new_pin; + pace_pin_cmd.pin2.len = strlen(new_pin); + + rv = card->ops->pin_cmd(card, &pace_pin_cmd); + + if (rv != SC_SUCCESS) { + fprintf(stderr, "PIN for authentication change failed: %s\n", sc_strerror(rv)); + } else { + printf("PIN for authentication changed.\n"); + pace_pin_changed = 1; + } + + sc_format_path("3F00DF02", &path); + rv = sc_select_file(card, &path, NULL); + if (rv != SC_SUCCESS) { + fprintf(stderr, "Failed to select 3F:00:DF:02.\n"); + return rv; + } + + struct sc_pin_cmd_data qes_pin_cmd = {0}; + qes_pin_cmd.cmd = SC_PIN_CMD_CHANGE; + qes_pin_cmd.pin_type = SC_AC_CHV; + qes_pin_cmd.pin_reference = 0x81; + qes_pin_cmd.pin1.data = (unsigned char *)pin; + qes_pin_cmd.pin1.len = strlen(pin); + qes_pin_cmd.pin2.data = (unsigned char *)new_pin; + qes_pin_cmd.pin2.len = strlen(new_pin); + + rv = card->ops->pin_cmd(card, &qes_pin_cmd); + + if (rv != SC_SUCCESS) { + fprintf(stderr, "PIN for signature change failed: %s\n", sc_strerror(rv)); + } else { + printf("PIN for signature changed.\n"); + qes_pin_changed = 1; + } + + return (pace_pin_changed && qes_pin_changed) ? SC_SUCCESS : SC_ERROR_INTERNAL; +} + +int +resume(sc_pkcs15_card_t *p15card, const char *opt_can, const char *opt_pin) +{ + int rv; + struct sc_card *card = p15card->card; + struct establish_pace_channel_input pace_input = {0}; + struct establish_pace_channel_output pace_output = {0}; + struct sc_path path; + char *pin = NULL; + int tries_left; + + rv = get_tries_left(p15card, PACE_PIN_ID_PIN, &tries_left); + + if (rv != SC_SUCCESS) { + fprintf(stderr, "Cannot get remaining tries left: %s\n", sc_strerror(rv)); + return rv; + } + + if (tries_left > 1) { + fprintf(stderr, "PIN for authentication is not blocked, there's %i tries remaining.\n", tries_left); + return SC_ERROR_NOT_ALLOWED; + } + + if (tries_left == 0) { + fprintf(stderr, "PIN for authentication is fully blocked with 0 attempts remaining. Use 'lteid-tool --unblock' instead.\n"); + return SC_ERROR_NOT_ALLOWED; + } + + rv = input_number("PIN number", 6, 12, opt_pin, &pin); + + if (rv != SC_SUCCESS) { + fprintf(stderr, "PIN number blank, too short or too long.\n"); + return SC_ERROR_PIN_CODE_INCORRECT; + } + + sc_format_path("3F00", &path); + rv = sc_select_file(card, &path, NULL); + if (rv != SC_SUCCESS) { + fprintf(stderr, "Failed to select MF.\n"); + return rv; + } + + struct sc_pin_cmd_data can_verify_cmd = {0}; + can_verify_cmd.cmd = SC_PIN_CMD_VERIFY; + can_verify_cmd.pin_type = SC_AC_CHV; + can_verify_cmd.pin_reference = PACE_PIN_ID_CAN; + can_verify_cmd.pin1.data = (unsigned char *)opt_can; + if (opt_can) + can_verify_cmd.pin1.len = strlen(opt_can); + + rv = card->ops->pin_cmd(card, &can_verify_cmd); + + if (rv != SC_SUCCESS) { + fprintf(stderr, "CAN code verification failed: %s\n", sc_strerror(rv)); + free(pin); + return SC_ERROR_PIN_CODE_INCORRECT; + } + + pace_input.pin_id = PACE_PIN_ID_PIN; + pace_input.pin = (unsigned char *)pin; + pace_input.pin_length = strlen(pin); + + rv = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02); + free(pin); + if (rv != SC_SUCCESS) { + fprintf(stderr, "PIN code verification failed: %s\n", sc_strerror(rv)); + return SC_ERROR_PIN_CODE_INCORRECT; + } + + printf("PIN for authentication unblocked.\n"); + + return SC_SUCCESS; +} + +int +unblock_using_puk(sc_pkcs15_card_t *p15card, const char *opt_puk) +{ + int rv; + struct sc_card *card = p15card->card; + struct sc_path path; + char *puk = NULL; + int pace_pin_tries_left, qes_pin_tries_left; + + get_tries_left(p15card, PACE_PIN_ID_PIN, &pace_pin_tries_left); + get_tries_left(p15card, 0x81, &qes_pin_tries_left); + + if (pace_pin_tries_left > 0 && qes_pin_tries_left > 0) { + fprintf(stderr, "None of the PINs require unblocking.\n"); + return SC_ERROR_INTERNAL; + } + + rv = input_number("PUK number", 8, 12, opt_puk, &puk); + + if (rv != SC_SUCCESS) { + fprintf(stderr, "PIN number blank, too short or too long.\n"); + return SC_ERROR_PIN_CODE_INCORRECT; + } + + sc_format_path("3F00", &path); + rv = sc_select_file(card, &path, NULL); + if (rv != SC_SUCCESS) { + fprintf(stderr, "Failed to select MF.\n"); + return rv; + } + + struct sc_pin_cmd_data puk_verify_cmd = {0}; + puk_verify_cmd.cmd = SC_PIN_CMD_VERIFY; + puk_verify_cmd.pin_type = SC_AC_CHV; + puk_verify_cmd.pin_reference = PACE_PIN_ID_PUK; + puk_verify_cmd.pin1.data = (unsigned char *)puk; + puk_verify_cmd.pin1.len = strlen(puk); + + rv = card->ops->pin_cmd(card, &puk_verify_cmd); + + if (rv != SC_SUCCESS) { + fprintf(stderr, "PUK code verification failed: %s\n", sc_strerror(rv)); + return SC_ERROR_PIN_CODE_INCORRECT; + } + + if (pace_pin_tries_left == 0) { + struct sc_pin_cmd_data pace_pin_cmd = {0}; + pace_pin_cmd.cmd = SC_PIN_CMD_UNBLOCK; + pace_pin_cmd.pin_type = SC_AC_CHV; + pace_pin_cmd.pin_reference = PACE_PIN_ID_PIN; + + rv = card->ops->pin_cmd(card, &pace_pin_cmd); + + if (rv != SC_SUCCESS) { + fprintf(stderr, "PIN for authentication reset failed: %s\n", sc_strerror(rv)); + return rv; + } + } + + if (qes_pin_tries_left == 0) { + struct sc_pin_cmd_data qes_pin_cmd = {0}; + qes_pin_cmd.cmd = SC_PIN_CMD_UNBLOCK; + qes_pin_cmd.pin_type = SC_AC_CHV; + qes_pin_cmd.pin_reference = 0x81; + + sc_format_path("3F00DF02", &path); + rv = sc_select_file(card, &path, NULL); + if (rv != SC_SUCCESS) { + fprintf(stderr, "Failed to select 3F:00:DF:02.\n"); + return rv; + } + + rv = card->ops->pin_cmd(card, &qes_pin_cmd); + + if (rv != SC_SUCCESS) { + fprintf(stderr, "PIN for signature reset failed: %s\n", sc_strerror(rv)); + return rv; + } + } + + printf("PIN unblocked.\n"); + + return SC_SUCCESS; +} + +int +main(int argc, char *argv[]) +{ + int opt_wait = 0; + const char *opt_can = NULL; + const char *opt_pin = NULL; + const char *opt_puk = NULL; + const char *opt_reader = NULL; + int verbose = 0; + int opt_change_pin = 0; + int opt_resume = 0; + int opt_unblock = 0; + int opt_verify_can = 0; + + int err = 0; + sc_context_t *ctx = NULL; + sc_context_param_t ctx_param = {0}; + sc_card_t *card = NULL; + struct sc_pkcs15_card *p15card = NULL; + int c, rv; + + while ((c = getopt_long(argc, argv, "hr:wc:p:u:vCRUV", options, (int *)0)) != -1) { + switch (c) { + case 'r': + opt_reader = optarg; + break; + case 'w': + opt_wait = 1; + break; + case 'c': + util_get_pin(optarg, &opt_can); + break; + case 'p': + util_get_pin(optarg, &opt_pin); + break; + case 'u': + util_get_pin(optarg, &opt_puk); + break; + case 'v': + verbose++; + break; + case 'C': + opt_change_pin = 1; + break; + case 'R': + opt_resume = 1; + break; + case 'U': + opt_unblock = 1; + break; + case 'V': + opt_verify_can = 1; + break; + case 'h': + default: + util_print_usage_and_die(app_name, options, option_help, NULL); + } + } + + ctx_param.app_name = app_name; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; + rv = sc_context_create(&ctx, &ctx_param); + if (rv) { + fprintf(stderr, "Error: Failed to establish context: %s\n", sc_strerror(rv)); + err = -1; + goto cleanup; + } + + if (util_connect_card(ctx, &card, opt_reader, opt_wait)) { + fprintf(stderr, "Error: Cannot connect with card\n"); + err = -1; + goto cleanup; + } + + if (strcmp("lteid", card->driver->short_name) != 0) { + fprintf(stderr, "Error: Card in the reader does not appear to be Lithuanian identity card.\n"); + err = -1; + goto cleanup; + } + + rv = sc_pkcs15_bind(card, NULL, &p15card); + + if (rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED || opt_verify_can) { + printf("\nCAN number is not set/stored.\n\n"); + err = verify_and_cache_pace_can(card, opt_can); + goto cleanup; + } + + if (rv != SC_SUCCESS) { + fprintf(stderr, "PKCS#15 binding failed: %s\n", sc_strerror(rv)); + err = -1; + goto cleanup; + } + + display_basic_details(p15card); + + if (opt_change_pin) { + err = change_pin(card, opt_pin); + } else if (opt_resume) { + err = resume(p15card, opt_can, opt_pin); + } else if (opt_unblock) { + err = unblock_using_puk(p15card, opt_puk); + } + +cleanup: + if (card) { + sc_unlock(card); + sc_disconnect_card(card); + } + sc_release_context(ctx); + return err; +} diff --git a/src/tools/netkey-tool.c b/src/tools/netkey-tool.c index 2c52d67251..3dae4bc881 100644 --- a/src/tools/netkey-tool.c +++ b/src/tools/netkey-tool.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -27,8 +27,9 @@ #include #include -#include "common/compat_getopt.h" +#include #include "libopensc/opensc.h" +#include "libopensc/log.h" static struct { const char *path; @@ -81,7 +82,7 @@ static void show_pin(sc_card_t *card, int pin) if(f->type!=SC_FILE_TYPE_WORKING_EF || f->ef_structure!=SC_FILE_EF_LINEAR_VARIABLE_TLV || f->prop_attr_len!=5 || f->prop_attr[0]!=0x01 || f->prop_attr[1]!=0x80 ){ - printf("\nInvald PIN-file: Type=%d, EF-Structure=%d, Prop-Len=%lu %02X:%02X:%02X\n", + printf("\nInvalid PIN-file: Type=%d, EF-Structure=%d, Prop-Len=%lu %02X:%02X:%02X\n", f->type, f->ef_structure, (unsigned long) f->prop_attr_len, f->prop_attr[0], f->prop_attr[1], f->prop_attr[2] ); @@ -130,7 +131,11 @@ static void show_certs(sc_card_t *card) continue; } if(f->type!=SC_FILE_TYPE_WORKING_EF || f->ef_structure!=SC_FILE_EF_TRANSPARENT){ - printf(", Invald Cert-file: Type=%d, EF-Structure=%d\n", f->type, f->ef_structure); + printf(", Invalid Cert-file: Type=%d, EF-Structure=%d\n", f->type, f->ef_structure); + continue; + } + if (f->size > sizeof(buf)) { + printf(", Certificate too large for buffer (%zu > %zu)\n", f->size, sizeof(buf)); continue; } if((j=sc_read_binary(card,0,buf,f->size,0))<0){ @@ -143,13 +148,39 @@ static void show_certs(sc_card_t *card) if(q[4]==6 && q[5]<10 && q[q[5]+6]==0x30 && q[q[5]+7]==0x82) q+=q[5]+6; printf(", Len=%d\n", (q[2]<<8)|q[3]); if((c=d2i_X509(NULL,&q,f->size))){ - char buf2[2000]; - X509_NAME_get_text_by_NID(X509_get_subject_name(c), NID_commonName, buf2,sizeof(buf2)); - printf(" Subject-CN: %s\n", buf2); - X509_NAME_get_text_by_NID(X509_get_issuer_name(c), NID_commonName, buf2,sizeof(buf2)); - printf(" Issuer-CN: %s\n", buf2); + int idx; + const X509_NAME_ENTRY *ne = NULL; /* no free */ + const ASN1_STRING *a_str = NULL; /* no free */ + int out_len = 0; + unsigned char *tmp = NULL; + + idx = X509_NAME_get_index_by_NID(X509_get_subject_name(c), NID_commonName, -1); + if (idx >= 0 && + ((ne = X509_NAME_get_entry(X509_get_subject_name(c), idx)) != NULL) && + ((a_str = X509_NAME_ENTRY_get_data(ne)) != NULL) && + ((out_len = ASN1_STRING_to_UTF8(&tmp, a_str)) > 0)) { + printf(" Subject-CN: %s\n", tmp); + OPENSSL_free(tmp); + tmp = NULL; + } else { + sc_log_openssl(card->ctx); + printf(" Invalid Subject-CN\n"); + } + + idx = X509_NAME_get_index_by_NID(X509_get_issuer_name(c), NID_commonName, -1); + if (idx >= 0 && + ((ne = X509_NAME_get_entry(X509_get_issuer_name(c), idx)) != NULL) && + ((a_str = X509_NAME_ENTRY_get_data(ne)) != NULL) && + ((out_len = ASN1_STRING_to_UTF8(&tmp, a_str)) > 0)) { + printf(" Issuer-CN: %s\n", tmp); + OPENSSL_free(tmp); + tmp = NULL; + } else { + sc_log_openssl(card->ctx); + printf(" Invalid Issuer-CN\n"); + } X509_free(c); - } else printf(" Invalid Certificate-Data\n"); + } } else printf(", empty\n"); } } @@ -162,18 +193,18 @@ static void show_initial_puk(sc_card_t *card) u8 buf1[128], buf2[128]; int i; - printf("\nReading crypted Initial-PUK-file: "); + printf("\nReading encrypted Initial-PUK-file: "); sc_format_path("3F004350",&p); if((i=sc_select_file(card,&p,&f))<0){ - printf("Cannot select crypted Initial-PUK-file, %s\n", sc_strerror(i)); + printf("Cannot select encrypted Initial-PUK-file, %s\n", sc_strerror(i)); return; } if((i=sc_read_binary(card,0,buf1,128,0))!=128){ - printf("Cannot read crypted Initial-PUK-file, %s\n", sc_strerror(i)); + printf("Cannot read encrypted Initial-PUK-file, %s\n", sc_strerror(i)); return; } - printf("OK\nDecrypting crypted Initial-PUK-file: "); + printf("OK\nDecrypting encrypted Initial-PUK-file: "); sc_format_path("3F00DF01",&p); if((i=sc_select_file(card,&p,&f))<0){ printf("Cannot select DF01, %s\n", sc_strerror(i)); @@ -219,7 +250,7 @@ static void show_card(sc_card_t *card) if(file->type!=SC_FILE_TYPE_WORKING_EF || file->ef_structure!=SC_FILE_EF_TRANSPARENT || file->size!=12 || (len=sc_read_binary(card,0,buf,12,0))!=12 || buf[0]!=0x5A || buf[1]!=0x0A ){ - printf("\nInvald Serial-Number: Type=%d, EF-Structure=%d, Size=%lu\n", + printf("\nInvalid Serial-Number: Type=%d, EF-Structure=%d, Size=%lu\n", file->type, file->ef_structure, (unsigned long) file->size ); return; @@ -235,8 +266,8 @@ static void show_card(sc_card_t *card) } -static void handle_change( sc_card_t *card, int pin1, int pin2, - int do_change, u8 *newpin, int newlen) +static void +handle_change(sc_card_t *card, long pin1, long pin2, int do_change, u8 *newpin, size_t newlen) { sc_path_t p; sc_file_t *f; @@ -271,26 +302,27 @@ static void handle_change( sc_card_t *card, int pin1, int pin2, } -static void handle_nullpin(sc_card_t *card, u8 *newpin, int newlen) +static void handle_nullpin(sc_card_t *card, u8 *newpin, size_t newlen) { sc_path_t p; sc_file_t *f; struct sc_apdu a; u8 ref, buf[40]; - int i; + size_t i; + int r; printf("\nSetting initial PIN-value: "); sc_format_path(pinlist[0].path,&p); - if((i=sc_select_file(card,&p,&f))<0){ - printf("\nCannot select %s, %s\n", pinlist[0].label, sc_strerror(i)); + if ((r = sc_select_file(card, &p, &f)) < 0) { + printf("\nCannot select %s, %s\n", pinlist[0].label, sc_strerror(r)); return; } ref=f->prop_attr[2]; sc_format_apdu(card, &a, SC_APDU_CASE_1, 0x20, 0x00, f->prop_attr[2]); - if((i=sc_transmit_apdu(card, &a))<0){ - printf("sc_transmit_apdu() failed, %s\n", sc_strerror(i)); + if ((r = sc_transmit_apdu(card, &a)) < 0) { + printf("sc_transmit_apdu() failed, %s\n", sc_strerror(r)); return; } if(a.sw1!=0x69 && a.sw2!=0x85){ @@ -302,8 +334,8 @@ static void handle_nullpin(sc_card_t *card, u8 *newpin, int newlen) for(i=0;i<6;++i) buf[i]=0; for(i=0;isize > sizeof(buf)) { + printf("Certificate too large for buffer (%zu > %zu)\n", f->size, sizeof(buf)); + return; + } if((len=sc_read_binary(card,0,buf,f->size,0))<0){ printf("Cannot read Cert, %s\n", sc_strerror(len)); return; @@ -338,21 +374,26 @@ static void handle_readcert(sc_card_t *card, int cert, char *file) q=buf; if(q[0]==0x30 && q[1]==0x82 && q[4]==6 && q[5]<10 && q[q[5]+6]==0x30 && q[q[5]+7]==0x82) q+=q[5]+6; if((c=d2i_X509(NULL,&q,len))==NULL){ + sc_log_openssl(card->ctx); printf("cardfile contains %d bytes which are not a certificate\n", len); return; } printf("Writing Cert to %s: ", file); fflush(stdout); if((fp=fopen(file,"w"))==NULL) printf("Cannot open file, %s\n", strerror(errno)); else { - fprintf(fp,"Certificate %d from Netkey E4 card\n\n", cert); - PEM_write_X509(fp,c); - printf("OK\n"); + fprintf(fp,"Certificate %ld from Netkey E4 card\n\n", cert); + if (PEM_write_X509(fp, c) != 1) { + sc_log_openssl(card->ctx); + printf("Cannot write certificate %ld\n", cert); + } else { + printf("OK\n"); + } } X509_free(c); } -static void handle_writecert(sc_card_t *card, int cert, char *file) +static void handle_writecert(sc_card_t *card, long cert, char *file) { sc_path_t p; sc_file_t *f; @@ -370,10 +411,10 @@ static void handle_writecert(sc_card_t *card, int cert, char *file) c=PEM_read_X509(fp,NULL,NULL,NULL); fclose(fp); if(c==NULL){ - printf("file does not conatin PEM-encoded certificate\n"); + printf("file does not contain PEM-encoded certificate\n"); return; } - printf("OK\nStoring Cert into Card-Certificate %d: ", cert); fflush(stdout); + printf("OK\nStoring Cert into Card-Certificate %ld: ", cert); fflush(stdout); q=buf; len=i2d_X509(c,NULL); if(len>0 && len<=(int)sizeof(buf)) @@ -397,10 +438,13 @@ static void handle_writecert(sc_card_t *card, int cert, char *file) } -static int pin_string2int(char *s) { - size_t i; +static long pin_string2int(char *s) +{ + long i; - for(i=0;i\n"); @@ -488,7 +535,7 @@ int main( fprintf(stderr,"\nstore Certificate into card at position 2 and read it back into file\n"); fprintf(stderr," %s --pin1 123456 cert /tmp/cert1 2\n", argv[0]); fprintf(stderr," %s cert 2 /tmp/cert2\n", argv[0]); - fprintf(stderr,"\nBe carful - this tool may destroy your card\n"); + fprintf(stderr,"\nBe careful - this tool may destroy your card\n"); fprintf(stderr,"\nQuestions? Comments? ==> opensc-user@opensc-project.org\n"); exit(1); } @@ -509,12 +556,12 @@ int main( } if(optind==argc-3 && !strcmp(argv[optind],"cert")){ ++optind; - cert_nr=strtol(argv[optind],&p,10); + cert_nr = strtol(argv[optind], &p, 10); if(argv[optind][0] && !*p && cert_nr>=0 && cert_nr<(int)(sizeof(certlist)/sizeof(certlist[0]))){ do_readcert=1, certfile=argv[optind+1]; } else { do_writecert=1, certfile=argv[optind]; - cert_nr=strtol(argv[optind+1],&p,10); + cert_nr = strtol(argv[optind + 1], &p, 10); if(!argv[optind][0] || *p || cert_nr<0 || cert_nr>=(int)(sizeof(certlist)/sizeof(certlist[0]))) ++oerr; } optind+=2; @@ -533,10 +580,7 @@ int main( fprintf(stderr,"Establish-Context failed: %s\n", sc_strerror(r)); exit(1); } - if (debug > 1) { - ctx->debug = debug; - sc_ctx_log_to_file(ctx, "stderr"); - } + ctx->debug = debug; if(ctx->debug>0) printf("Context for application \"%s\" created, Debug=%d\n", ctx->app_name, ctx->debug); @@ -544,17 +588,20 @@ int main( if(!strcmp("tcos", ctx->card_drivers[i]->short_name)) break; if(!ctx->card_drivers[i]){ fprintf(stderr,"Context does not support TCOS-cards\n"); + sc_release_context(ctx); exit(1); } printf("%d Readers detected\n", sc_ctx_get_reader_count(ctx)); if(reader < 0 || reader >= (int)sc_ctx_get_reader_count(ctx)){ fprintf(stderr,"Cannot open reader %d\n", reader); + sc_release_context(ctx); exit(1); } if((r = sc_connect_card(sc_ctx_get_reader(ctx, 0), &card))<0){ fprintf(stderr,"Connect-Card failed: %s\n", sc_strerror(r)); + sc_release_context(ctx); exit(1); } printf("\nCard detected (driver: %s)\nATR:", card->driver->name); @@ -564,13 +611,15 @@ int main( if((r = sc_lock(card))<0){ fprintf(stderr,"Lock failed: %s\n", sc_strerror(r)); + sc_disconnect_card(card); + sc_release_context(ctx); exit(1); } show_card(card); if(do_unblock || do_change){ - int i1=pinlist[pin_nr].p1, i2=pinlist[pin_nr].p2; + long i1 = pinlist[pin_nr].p1, i2 = pinlist[pin_nr].p2; if((do_unblock || !pinlist[pin_nr].len) && (i1<0 || !pinlist[i1].len) && (i2<0 || !pinlist[i2].len) @@ -594,9 +643,9 @@ int main( if(do_readcert) handle_readcert(card, cert_nr, certfile); if(do_writecert){ if(certlist[cert_nr].readonly){ - fprintf(stderr, "\nReadonly-Certificate %d cannot be changed\n", cert_nr); + fprintf(stderr, "\nReadonly-Certificate %ld cannot be changed\n", cert_nr); } else if(!pinlist[0].len && !pinlist[3].len){ - fprintf(stderr, "\nNeed %s or %s to change Card-Certificate %d\n", + fprintf(stderr, "\nNeed %s or %s to change Card-Certificate %ld\n", pinlist[0].label, pinlist[3].label, cert_nr ); } else handle_writecert(card, cert_nr, certfile); diff --git a/src/tools/npa-tool-cmdline.c b/src/tools/npa-tool-cmdline.c index 7a92e148cb..fa8ba87812 100644 --- a/src/tools/npa-tool-cmdline.c +++ b/src/tools/npa-tool-cmdline.c @@ -1,7 +1,7 @@ /* - File autogenerated by gengetopt version 2.22.6 + File autogenerated by gengetopt version 2.23 generated with the following command: - /usr/bin/gengetopt --include-getopt --file-name=npa-tool-cmdline --output-dir=. + /usr/bin/gengetopt --file-name=npa-tool-cmdline --output-dir=. The developers of gengetopt consider the fixed text that goes in all gengetopt output files to be in the public domain: @@ -21,12 +21,13 @@ #define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ #endif +#include #include "npa-tool-cmdline.h" const char *gengetopt_args_info_purpose = ""; -const char *gengetopt_args_info_usage = "Usage: npa-tool [OPTIONS]..."; +const char *gengetopt_args_info_usage = "Usage: npa-tool [OPTION]..."; const char *gengetopt_args_info_versiontext = ""; @@ -35,7 +36,7 @@ const char *gengetopt_args_info_description = ""; const char *gengetopt_args_info_help[] = { " -h, --help Print help and exit", " -V, --version Print version and exit", - " -r, --reader=INT Number of the PC/SC reader to use (-1 for\n autodetect) (default=`-1')", + " -r, --reader=STRING Number of the reader to use. By default, the\n first reader with a present card is used. If\n the argument is an ATR, the reader with a\n matching card will be chosen.", " -v, --verbose Use (several times) to be more verbose", "\nPassword Authenticated Connection Establishment (PACE):", " -p, --pin[=STRING] Run PACE with (transport) eID-PIN", @@ -53,37 +54,40 @@ const char *gengetopt_args_info_help[] = { " --chat=HEX_STRING Card holder authorization template to use\n (default is terminal's CHAT). Use\n 7F4C0E060904007F000703010203530103 to trigger\n EAC on the CAT-C (Komfortleser).", " -A, --auxiliary-data=HEX_STRING\n Terminal's auxiliary data (default is\n determined by verification of validity, age\n and community ID).", " -P, --private-key=FILENAME Terminal's private key", - " --cvc-dir=DIRECTORY Where to look for the CVCA's certificate\n (default=`/home/fm/.local/etc/eac/cvc')", - " --x509-dir=DIRECTORY Where to look for the CSCA's certificate\n (default=`/home/fm/.local/etc/eac/x509')", - " --disable-ta-checks Disable checking the validity period of CV\n certifcates (default=off)", + " --cvc-dir=DIRECTORY Where to look for the CVCA's certificate\n (default=`')", + " --x509-dir=DIRECTORY Where to look for the CSCA's certificate\n (default=`')", + " --disable-ta-checks Disable checking the validity period of CV\n certificates (default=off)", " --disable-ca-checks Disable passive authentication (default=off)", + "\nCard application:", + " --application=ENUM What card application to select (possible\n values=\"eID\", \"eMRTD\" default=`eID')", "\nRead and write data groups:", - " --read-dg1 Read DG 1 (Document Type) (default=off)", - " --read-dg2 Read DG 2 (Issuing State) (default=off)", - " --read-dg3 Read DG 3 (Date of Expiry) (default=off)", - " --read-dg4 Read DG 4 (Given Names) (default=off)", - " --read-dg5 Read DG 5 (Family Names) (default=off)", - " --read-dg6 Read DG 6 (Religious/Artistic Name)\n (default=off)", - " --read-dg7 Read DG 7 (Academic Title) (default=off)", - " --read-dg8 Read DG 8 (Date of Birth) (default=off)", - " --read-dg9 Read DG 9 (Place of Birth) (default=off)", - " --read-dg10 Read DG 10 (Nationality) (default=off)", - " --read-dg11 Read DG 11 (Sex) (default=off)", - " --read-dg12 Read DG 12 (Optional Data) (default=off)", - " --read-dg13 Read DG 13 (Birth Name) (default=off)", - " --read-dg14 Read DG 14 (default=off)", - " --read-dg15 Read DG 15 (default=off)", - " --read-dg16 Read DG 16 (default=off)", - " --read-dg17 Read DG 17 (Normal Place of Residence)\n (default=off)", - " --read-dg18 Read DG 18 (Community ID) (default=off)", - " --read-dg19 Read DG 19 (Residence Permit I) (default=off)", - " --read-dg20 Read DG 20 (Residence Permit II)\n (default=off)", - " --read-dg21 Read DG 21 (Optional Data) (default=off)", - " --write-dg17=HEX_STRING Write DG 17 (Normal Place of Residence)", - " --write-dg18=HEX_STRING Write DG 18 (Community ID)", - " --write-dg19=HEX_STRING Write DG 19 (Residence Permit I)", - " --write-dg20=HEX_STRING Write DG 20 (Residence Permit II)", - " --write-dg21=HEX_STRING Write DG 21 (Optional Data)", + " --read-all-dgs Read all available data groups (default=off)", + " --read-dg1 Read data group 1 (default=off)", + " --read-dg2 Read data group 2 (default=off)", + " --read-dg3 Read data group 3 (default=off)", + " --read-dg4 Read data group 4 (default=off)", + " --read-dg5 Read data group 5 (default=off)", + " --read-dg6 Read data group 6 (default=off)", + " --read-dg7 Read data group 7 (default=off)", + " --read-dg8 Read data group 8 (default=off)", + " --read-dg9 Read data group 9 (default=off)", + " --read-dg10 Read data group 10 (default=off)", + " --read-dg11 Read data group 11 (default=off)", + " --read-dg12 Read data group 12 (default=off)", + " --read-dg13 Read data group 13 (default=off)", + " --read-dg14 Read data group 14 (default=off)", + " --read-dg15 Read data group 15 (default=off)", + " --read-dg16 Read data group 16 (default=off)", + " --read-dg17 Read data group 17 (default=off)", + " --read-dg18 Read data group 18 (default=off)", + " --read-dg19 Read data group 19 (default=off)", + " --read-dg20 Read data group 20 (default=off)", + " --read-dg21 Read data group 21 (default=off)", + " --write-dg17=HEX_STRING Write data group 17", + " --write-dg18=HEX_STRING Write data group 18", + " --write-dg19=HEX_STRING Write data group 19", + " --write-dg20=HEX_STRING Write data group 20", + " --write-dg21=HEX_STRING Write data group 21", "\nVerification of validity, age and community ID:", " --verify-validity=YYYYMMDD\n Verify chip's validity with a reference date", " --older-than=YYYYMMDD Verify age with a reference date", @@ -92,15 +96,14 @@ const char *gengetopt_args_info_help[] = { " -b, --break Brute force PIN, CAN or PUK. Use together with\n -p, -a or -u (default=off)", " -t, --translate=FILENAME File with APDUs of HEX_STRINGs to send through\n the secure channel (default=`stdin')", " --tr-03110v201 Force compliance to BSI TR-03110 version 2.01\n (default=off)", - " --disable-all-checks Disable all checking of fly-by-data\n (default=off)", - "\nReport bugs to opensc-devel@lists.sourceforge.net\n\nWritten by Frank Morgner ", + "\nReport bugs to https://github.com/OpenSC/OpenSC/issues\n\nWritten by Frank Morgner ", 0 }; typedef enum {ARG_NO , ARG_FLAG , ARG_STRING - , ARG_INT + , ARG_ENUM } cmdline_parser_arg_type; static @@ -115,6 +118,8 @@ cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args static int cmdline_parser_required2 (struct gengetopt_args_info *args_info, const char *prog_name, const char *additional_error); +const char *cmdline_parser_application_values[] = {"eID", "eMRTD", 0}; /*< Possible values for application. */ + static char * gengetopt_strdup (const char *s); @@ -142,6 +147,8 @@ void clear_given (struct gengetopt_args_info *args_info) args_info->x509_dir_given = 0 ; args_info->disable_ta_checks_given = 0 ; args_info->disable_ca_checks_given = 0 ; + args_info->application_given = 0 ; + args_info->read_all_dgs_given = 0 ; args_info->read_dg1_given = 0 ; args_info->read_dg2_given = 0 ; args_info->read_dg3_given = 0 ; @@ -174,14 +181,13 @@ void clear_given (struct gengetopt_args_info *args_info) args_info->break_given = 0 ; args_info->translate_given = 0 ; args_info->tr_03110v201_given = 0 ; - args_info->disable_all_checks_given = 0 ; } static void clear_args (struct gengetopt_args_info *args_info) { FIX_UNUSED (args_info); - args_info->reader_arg = -1; + args_info->reader_arg = NULL; args_info->reader_orig = NULL; args_info->pin_arg = NULL; args_info->pin_orig = NULL; @@ -206,12 +212,15 @@ void clear_args (struct gengetopt_args_info *args_info) args_info->auxiliary_data_orig = NULL; args_info->private_key_arg = NULL; args_info->private_key_orig = NULL; - args_info->cvc_dir_arg = gengetopt_strdup ("/home/fm/.local/etc/eac/cvc"); + args_info->cvc_dir_arg = gengetopt_strdup (""); args_info->cvc_dir_orig = NULL; - args_info->x509_dir_arg = gengetopt_strdup ("/home/fm/.local/etc/eac/x509"); + args_info->x509_dir_arg = gengetopt_strdup (""); args_info->x509_dir_orig = NULL; args_info->disable_ta_checks_flag = 0; args_info->disable_ca_checks_flag = 0; + args_info->application_arg = application_arg_eID; + args_info->application_orig = NULL; + args_info->read_all_dgs_flag = 0; args_info->read_dg1_flag = 0; args_info->read_dg2_flag = 0; args_info->read_dg3_flag = 0; @@ -253,7 +262,6 @@ void clear_args (struct gengetopt_args_info *args_info) args_info->translate_arg = gengetopt_strdup ("stdin"); args_info->translate_orig = NULL; args_info->tr_03110v201_flag = 0; - args_info->disable_all_checks_flag = 0; } @@ -287,39 +295,40 @@ void init_args_info(struct gengetopt_args_info *args_info) args_info->x509_dir_help = gengetopt_args_info_help[21] ; args_info->disable_ta_checks_help = gengetopt_args_info_help[22] ; args_info->disable_ca_checks_help = gengetopt_args_info_help[23] ; - args_info->read_dg1_help = gengetopt_args_info_help[25] ; - args_info->read_dg2_help = gengetopt_args_info_help[26] ; - args_info->read_dg3_help = gengetopt_args_info_help[27] ; - args_info->read_dg4_help = gengetopt_args_info_help[28] ; - args_info->read_dg5_help = gengetopt_args_info_help[29] ; - args_info->read_dg6_help = gengetopt_args_info_help[30] ; - args_info->read_dg7_help = gengetopt_args_info_help[31] ; - args_info->read_dg8_help = gengetopt_args_info_help[32] ; - args_info->read_dg9_help = gengetopt_args_info_help[33] ; - args_info->read_dg10_help = gengetopt_args_info_help[34] ; - args_info->read_dg11_help = gengetopt_args_info_help[35] ; - args_info->read_dg12_help = gengetopt_args_info_help[36] ; - args_info->read_dg13_help = gengetopt_args_info_help[37] ; - args_info->read_dg14_help = gengetopt_args_info_help[38] ; - args_info->read_dg15_help = gengetopt_args_info_help[39] ; - args_info->read_dg16_help = gengetopt_args_info_help[40] ; - args_info->read_dg17_help = gengetopt_args_info_help[41] ; - args_info->read_dg18_help = gengetopt_args_info_help[42] ; - args_info->read_dg19_help = gengetopt_args_info_help[43] ; - args_info->read_dg20_help = gengetopt_args_info_help[44] ; - args_info->read_dg21_help = gengetopt_args_info_help[45] ; - args_info->write_dg17_help = gengetopt_args_info_help[46] ; - args_info->write_dg18_help = gengetopt_args_info_help[47] ; - args_info->write_dg19_help = gengetopt_args_info_help[48] ; - args_info->write_dg20_help = gengetopt_args_info_help[49] ; - args_info->write_dg21_help = gengetopt_args_info_help[50] ; - args_info->verify_validity_help = gengetopt_args_info_help[52] ; - args_info->older_than_help = gengetopt_args_info_help[53] ; - args_info->verify_community_help = gengetopt_args_info_help[54] ; - args_info->break_help = gengetopt_args_info_help[56] ; - args_info->translate_help = gengetopt_args_info_help[57] ; - args_info->tr_03110v201_help = gengetopt_args_info_help[58] ; - args_info->disable_all_checks_help = gengetopt_args_info_help[59] ; + args_info->application_help = gengetopt_args_info_help[25] ; + args_info->read_all_dgs_help = gengetopt_args_info_help[27] ; + args_info->read_dg1_help = gengetopt_args_info_help[28] ; + args_info->read_dg2_help = gengetopt_args_info_help[29] ; + args_info->read_dg3_help = gengetopt_args_info_help[30] ; + args_info->read_dg4_help = gengetopt_args_info_help[31] ; + args_info->read_dg5_help = gengetopt_args_info_help[32] ; + args_info->read_dg6_help = gengetopt_args_info_help[33] ; + args_info->read_dg7_help = gengetopt_args_info_help[34] ; + args_info->read_dg8_help = gengetopt_args_info_help[35] ; + args_info->read_dg9_help = gengetopt_args_info_help[36] ; + args_info->read_dg10_help = gengetopt_args_info_help[37] ; + args_info->read_dg11_help = gengetopt_args_info_help[38] ; + args_info->read_dg12_help = gengetopt_args_info_help[39] ; + args_info->read_dg13_help = gengetopt_args_info_help[40] ; + args_info->read_dg14_help = gengetopt_args_info_help[41] ; + args_info->read_dg15_help = gengetopt_args_info_help[42] ; + args_info->read_dg16_help = gengetopt_args_info_help[43] ; + args_info->read_dg17_help = gengetopt_args_info_help[44] ; + args_info->read_dg18_help = gengetopt_args_info_help[45] ; + args_info->read_dg19_help = gengetopt_args_info_help[46] ; + args_info->read_dg20_help = gengetopt_args_info_help[47] ; + args_info->read_dg21_help = gengetopt_args_info_help[48] ; + args_info->write_dg17_help = gengetopt_args_info_help[49] ; + args_info->write_dg18_help = gengetopt_args_info_help[50] ; + args_info->write_dg19_help = gengetopt_args_info_help[51] ; + args_info->write_dg20_help = gengetopt_args_info_help[52] ; + args_info->write_dg21_help = gengetopt_args_info_help[53] ; + args_info->verify_validity_help = gengetopt_args_info_help[55] ; + args_info->older_than_help = gengetopt_args_info_help[56] ; + args_info->verify_community_help = gengetopt_args_info_help[57] ; + args_info->break_help = gengetopt_args_info_help[59] ; + args_info->translate_help = gengetopt_args_info_help[60] ; + args_info->tr_03110v201_help = gengetopt_args_info_help[61] ; } @@ -334,19 +343,25 @@ cmdline_parser_print_version (void) printf("\n%s\n", gengetopt_args_info_versiontext); } -static void print_help_common(void) { - cmdline_parser_print_version (); - - if (strlen(gengetopt_args_info_purpose) > 0) - printf("\n%s\n", gengetopt_args_info_purpose); +static void print_help_common(void) +{ + size_t len_purpose = strlen(gengetopt_args_info_purpose); + size_t len_usage = strlen(gengetopt_args_info_usage); - if (strlen(gengetopt_args_info_usage) > 0) - printf("\n%s\n", gengetopt_args_info_usage); + if (len_usage > 0) { + printf("%s\n", gengetopt_args_info_usage); + } + if (len_purpose > 0) { + printf("%s\n", gengetopt_args_info_purpose); + } - printf("\n"); + if (len_usage || len_purpose) { + printf("\n"); + } - if (strlen(gengetopt_args_info_description) > 0) - printf("%s\n\n", gengetopt_args_info_description); + if (strlen(gengetopt_args_info_description) > 0) { + printf("%s\n\n", gengetopt_args_info_description); + } } void @@ -448,6 +463,7 @@ static void cmdline_parser_release (struct gengetopt_args_info *args_info) { + free_string_field (&(args_info->reader_arg)); free_string_field (&(args_info->reader_orig)); free_string_field (&(args_info->pin_arg)); free_string_field (&(args_info->pin_orig)); @@ -472,6 +488,7 @@ cmdline_parser_release (struct gengetopt_args_info *args_info) free_string_field (&(args_info->cvc_dir_orig)); free_string_field (&(args_info->x509_dir_arg)); free_string_field (&(args_info->x509_dir_orig)); + free_string_field (&(args_info->application_orig)); free_string_field (&(args_info->write_dg17_arg)); free_string_field (&(args_info->write_dg17_orig)); free_string_field (&(args_info->write_dg18_arg)); @@ -496,13 +513,54 @@ cmdline_parser_release (struct gengetopt_args_info *args_info) clear_given (args_info); } +/** + * @param val the value to check + * @param values the possible values + * @return the index of the matched value: + * -1 if no value matched, + * -2 if more than one value has matched + */ +static int +check_possible_values(const char *val, const char *values[]) +{ + int i, found, last; + size_t len; + + if (!val) /* otherwise strlen() crashes below */ + return -1; /* -1 means no argument for the option */ + + found = last = 0; + + for (i = 0, len = strlen(val); values[i]; ++i) + { + if (strncmp(val, values[i], len) == 0) + { + ++found; + last = i; + if (strlen(values[i]) == len) + return i; /* exact match no need to check more */ + } + } + + if (found == 1) /* one match: OK */ + return last; + + return (found ? -2 : -1); /* return many values or none matched */ +} + static void write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[]) { - FIX_UNUSED (values); + int found = -1; if (arg) { - fprintf(outfile, "%s=\"%s\"\n", opt, arg); + if (values) { + found = check_possible_values(arg, values); + } + if (found >= 0) + fprintf(outfile, "%s=\"%s\" # %s\n", opt, arg, values[found]); + else + fprintf(outfile, "%s=\"%s\"\n", opt, arg); } else { fprintf(outfile, "%s\n", opt); } @@ -568,6 +626,10 @@ cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) write_into_file(outfile, "disable-ta-checks", 0, 0 ); if (args_info->disable_ca_checks_given) write_into_file(outfile, "disable-ca-checks", 0, 0 ); + if (args_info->application_given) + write_into_file(outfile, "application", args_info->application_orig, cmdline_parser_application_values); + if (args_info->read_all_dgs_given) + write_into_file(outfile, "read-all-dgs", 0, 0 ); if (args_info->read_dg1_given) write_into_file(outfile, "read-dg1", 0, 0 ); if (args_info->read_dg2_given) @@ -632,8 +694,6 @@ cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) write_into_file(outfile, "translate", args_info->translate_orig, 0); if (args_info->tr_03110v201_given) write_into_file(outfile, "tr-03110v201", 0, 0 ); - if (args_info->disable_all_checks_given) - write_into_file(outfile, "disable-all-checks", 0, 0 ); i = EXIT_SUCCESS; @@ -897,598 +957,6 @@ cmdline_parser_required2 (struct gengetopt_args_info *args_info, const char *pro return error_occurred; } -/* - * Extracted from the glibc source tree, version 2.3.6 - * - * Licensed under the GPL as per the whole glibc source tree. - * - * This file was modified so that getopt_long can be called - * many times without risking previous memory to be spoiled. - * - * Modified by Andre Noll and Lorenzo Bettini for use in - * GNU gengetopt generated files. - * - */ - -/* - * we must include anything we need since this file is not thought to be - * inserted in a file already using getopt.h - * - * Lorenzo - */ - -struct option -{ - const char *name; - /* has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. */ - int has_arg; - int *flag; - int val; -}; - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. -*/ -/* - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `custom_optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -/* Names for the values of the `has_arg' field of `struct option'. */ -#ifndef no_argument -#define no_argument 0 -#endif - -#ifndef required_argument -#define required_argument 1 -#endif - -#ifndef optional_argument -#define optional_argument 2 -#endif - -struct custom_getopt_data { - /* - * These have exactly the same meaning as the corresponding global variables, - * except that they are used for the reentrant versions of getopt. - */ - int custom_optind; - int custom_opterr; - int custom_optopt; - char *custom_optarg; - - /* True if the internal members have been initialized. */ - int initialized; - - /* - * The next char to be scanned in the option-element in which the last option - * character we returned was found. This allows us to pick up the scan where - * we left off. If this is zero, or a null string, it means resume the scan by - * advancing to the next ARGV-element. - */ - char *nextchar; - - /* - * Describe the part of ARGV that contains non-options that have been skipped. - * `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is - * the index after the last of them. - */ - int first_nonopt; - int last_nonopt; -}; - -/* - * the variables optarg, optind, opterr and optopt are renamed with - * the custom_ prefix so that they don't interfere with getopt ones. - * - * Moreover they're static so they are visible only from within the - * file where this very file will be included. - */ - -/* - * For communication from `custom_getopt' to the caller. When `custom_getopt' finds an - * option that takes an argument, the argument value is returned here. - */ -static char *custom_optarg; - -/* - * Index in ARGV of the next element to be scanned. This is used for - * communication to and from the caller and for communication between - * successive calls to `custom_getopt'. - * - * On entry to `custom_getopt', 1 means this is the first call; initialize. - * - * When `custom_getopt' returns -1, this is the index of the first of the non-option - * elements that the caller should itself scan. - * - * Otherwise, `custom_optind' communicates from one call to the next how much of ARGV - * has been scanned so far. - * - * 1003.2 says this must be 1 before any call. - */ -static int custom_optind = 1; - -/* - * Callers store zero here to inhibit the error message for unrecognized - * options. - */ -static int custom_opterr = 1; - -/* - * Set to an option character which was unrecognized. This must be initialized - * on some systems to avoid linking in the system's own getopt implementation. - */ -static int custom_optopt = '?'; - -/* - * Exchange two adjacent subsequences of ARGV. One subsequence is elements - * [first_nonopt,last_nonopt) which contains all the non-options that have been - * skipped so far. The other is elements [last_nonopt,custom_optind), which contains - * all the options processed since those non-options were skipped. - * `first_nonopt' and `last_nonopt' are relocated so that they describe the new - * indices of the non-options in ARGV after they are moved. - */ -static void exchange(char **argv, struct custom_getopt_data *d) -{ - int bottom = d->first_nonopt; - int middle = d->last_nonopt; - int top = d->custom_optind; - char *tem; - - /* - * Exchange the shorter segment with the far end of the longer segment. - * That puts the shorter segment into the right place. It leaves the - * longer segment in the right place overall, but it consists of two - * parts that need to be swapped next. - */ - while (top > middle && middle > bottom) { - if (top - middle > middle - bottom) { - /* Bottom segment is the short one. */ - int len = middle - bottom; - int i; - - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) { - tem = argv[bottom + i]; - argv[bottom + i] = - argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } else { - /* Top segment is the short one. */ - int len = top - middle; - int i; - - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } - } - /* Update records for the slots the non-options now occupy. */ - d->first_nonopt += (d->custom_optind - d->last_nonopt); - d->last_nonopt = d->custom_optind; -} - -/* Initialize the internal data when the first call is made. */ -static void custom_getopt_initialize(struct custom_getopt_data *d) -{ - /* - * Start processing options with ARGV-element 1 (since ARGV-element 0 - * is the program name); the sequence of previously skipped non-option - * ARGV-elements is empty. - */ - d->first_nonopt = d->last_nonopt = d->custom_optind; - d->nextchar = NULL; - d->initialized = 1; -} - -#define NONOPTION_P (argv[d->custom_optind][0] != '-' || argv[d->custom_optind][1] == '\0') - -/* return: zero: continue, nonzero: return given value to user */ -static int shuffle_argv(int argc, char *const *argv,const struct option *longopts, - struct custom_getopt_data *d) -{ - /* - * Give FIRST_NONOPT & LAST_NONOPT rational values if CUSTOM_OPTIND has been - * moved back by the user (who may also have changed the arguments). - */ - if (d->last_nonopt > d->custom_optind) - d->last_nonopt = d->custom_optind; - if (d->first_nonopt > d->custom_optind) - d->first_nonopt = d->custom_optind; - /* - * If we have just processed some options following some - * non-options, exchange them so that the options come first. - */ - if (d->first_nonopt != d->last_nonopt && - d->last_nonopt != d->custom_optind) - exchange((char **) argv, d); - else if (d->last_nonopt != d->custom_optind) - d->first_nonopt = d->custom_optind; - /* - * Skip any additional non-options and extend the range of - * non-options previously skipped. - */ - while (d->custom_optind < argc && NONOPTION_P) - d->custom_optind++; - d->last_nonopt = d->custom_optind; - /* - * The special ARGV-element `--' means premature end of options. Skip - * it like a null option, then exchange with previous non-options as if - * it were an option, then skip everything else like a non-option. - */ - if (d->custom_optind != argc && !strcmp(argv[d->custom_optind], "--")) { - d->custom_optind++; - if (d->first_nonopt != d->last_nonopt - && d->last_nonopt != d->custom_optind) - exchange((char **) argv, d); - else if (d->first_nonopt == d->last_nonopt) - d->first_nonopt = d->custom_optind; - d->last_nonopt = argc; - d->custom_optind = argc; - } - /* - * If we have done all the ARGV-elements, stop the scan and back over - * any non-options that we skipped and permuted. - */ - if (d->custom_optind == argc) { - /* - * Set the next-arg-index to point at the non-options that we - * previously skipped, so the caller will digest them. - */ - if (d->first_nonopt != d->last_nonopt) - d->custom_optind = d->first_nonopt; - return -1; - } - /* - * If we have come to a non-option and did not permute it, either stop - * the scan or describe it to the caller and pass it by. - */ - if (NONOPTION_P) { - d->custom_optarg = argv[d->custom_optind++]; - return 1; - } - /* - * We have found another option-ARGV-element. Skip the initial - * punctuation. - */ - d->nextchar = (argv[d->custom_optind] + 1 + (longopts != NULL && argv[d->custom_optind][1] == '-')); - return 0; -} - -/* - * Check whether the ARGV-element is a long option. - * - * If there's a long option "fubar" and the ARGV-element is "-fu", consider - * that an abbreviation of the long option, just like "--fu", and not "-f" with - * arg "u". - * - * This distinction seems to be the most useful approach. - * - */ -static int check_long_opt(int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longind, - int print_errors, struct custom_getopt_data *d) -{ - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = -1; - int option_index; - - for (nameend = d->nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match or abbreviated matches */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp(p->name, d->nextchar, nameend - d->nextchar)) { - if ((unsigned int) (nameend - d->nextchar) - == (unsigned int) strlen(p->name)) { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } else if (pfound == NULL) { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } else if (pfound->has_arg != p->has_arg - || pfound->flag != p->flag - || pfound->val != p->val) - /* Second or later nonexact match found. */ - ambig = 1; - } - if (ambig && !exact) { - if (print_errors) { - fprintf(stderr, - "%s: option `%s' is ambiguous\n", - argv[0], argv[d->custom_optind]); - } - d->nextchar += strlen(d->nextchar); - d->custom_optind++; - d->custom_optopt = 0; - return '?'; - } - if (pfound) { - option_index = indfound; - d->custom_optind++; - if (*nameend) { - if (pfound->has_arg != no_argument) - d->custom_optarg = nameend + 1; - else { - if (print_errors) { - if (argv[d->custom_optind - 1][1] == '-') { - /* --option */ - fprintf(stderr, "%s: option `--%s' doesn't allow an argument\n", - argv[0], pfound->name); - } else { - /* +option or -option */ - fprintf(stderr, "%s: option `%c%s' doesn't allow an argument\n", - argv[0], argv[d->custom_optind - 1][0], pfound->name); - } - - } - d->nextchar += strlen(d->nextchar); - d->custom_optopt = pfound->val; - return '?'; - } - } else if (pfound->has_arg == required_argument) { - if (d->custom_optind < argc) - d->custom_optarg = argv[d->custom_optind++]; - else { - if (print_errors) { - fprintf(stderr, - "%s: option `%s' requires an argument\n", - argv[0], - argv[d->custom_optind - 1]); - } - d->nextchar += strlen(d->nextchar); - d->custom_optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - d->nextchar += strlen(d->nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - /* - * Can't find it as a long option. If this is not getopt_long_only, or - * the option starts with '--' or is not a valid short option, then - * it's an error. Otherwise interpret it as a short option. - */ - if (print_errors) { - if (argv[d->custom_optind][1] == '-') { - /* --option */ - fprintf(stderr, - "%s: unrecognized option `--%s'\n", - argv[0], d->nextchar); - } else { - /* +option or -option */ - fprintf(stderr, - "%s: unrecognized option `%c%s'\n", - argv[0], argv[d->custom_optind][0], - d->nextchar); - } - } - d->nextchar = (char *) ""; - d->custom_optind++; - d->custom_optopt = 0; - return '?'; -} - -static int check_short_opt(int argc, char *const *argv, const char *optstring, - int print_errors, struct custom_getopt_data *d) -{ - char c = *d->nextchar++; - const char *temp = strchr(optstring, c); - - /* Increment `custom_optind' when we start to process its last character. */ - if (*d->nextchar == '\0') - ++d->custom_optind; - if (!temp || c == ':') { - if (print_errors) - fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c); - - d->custom_optopt = c; - return '?'; - } - if (temp[1] == ':') { - if (temp[2] == ':') { - /* This is an option that accepts an argument optionally. */ - if (*d->nextchar != '\0') { - d->custom_optarg = d->nextchar; - d->custom_optind++; - } else - d->custom_optarg = NULL; - d->nextchar = NULL; - } else { - /* This is an option that requires an argument. */ - if (*d->nextchar != '\0') { - d->custom_optarg = d->nextchar; - /* - * If we end this ARGV-element by taking the - * rest as an arg, we must advance to the next - * element now. - */ - d->custom_optind++; - } else if (d->custom_optind == argc) { - if (print_errors) { - fprintf(stderr, - "%s: option requires an argument -- %c\n", - argv[0], c); - } - d->custom_optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } else - /* - * We already incremented `custom_optind' once; - * increment it again when taking next ARGV-elt - * as argument. - */ - d->custom_optarg = argv[d->custom_optind++]; - d->nextchar = NULL; - } - } - return c; -} - -/* - * Scan elements of ARGV for option characters given in OPTSTRING. - * - * If an element of ARGV starts with '-', and is not exactly "-" or "--", - * then it is an option element. The characters of this element - * (aside from the initial '-') are option characters. If `getopt' - * is called repeatedly, it returns successively each of the option characters - * from each of the option elements. - * - * If `getopt' finds another option character, it returns that character, - * updating `custom_optind' and `nextchar' so that the next call to `getopt' can - * resume the scan with the following option character or ARGV-element. - * - * If there are no more option characters, `getopt' returns -1. - * Then `custom_optind' is the index in ARGV of the first ARGV-element - * that is not an option. (The ARGV-elements have been permuted - * so that those that are not options now come last.) - * - * OPTSTRING is a string containing the legitimate option characters. - * If an option character is seen that is not listed in OPTSTRING, - * return '?' after printing an error message. If you set `custom_opterr' to - * zero, the error message is suppressed but we still return '?'. - * - * If a char in OPTSTRING is followed by a colon, that means it wants an arg, - * so the following text in the same ARGV-element, or the text of the following - * ARGV-element, is returned in `custom_optarg'. Two colons mean an option that - * wants an optional arg; if there is text in the current ARGV-element, - * it is returned in `custom_optarg', otherwise `custom_optarg' is set to zero. - * - * If OPTSTRING starts with `-' or `+', it requests different methods of - * handling the non-option ARGV-elements. - * See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - * - * Long-named options begin with `--' instead of `-'. - * Their names may be abbreviated as long as the abbreviation is unique - * or is an exact match for some defined option. If they have an - * argument, it follows the option name in the same ARGV-element, separated - * from the option name by a `=', or else the in next ARGV-element. - * When `getopt' finds a long-named option, it returns 0 if that option's - * `flag' field is nonzero, the value of the option's `val' field - * if the `flag' field is zero. - * - * The elements of ARGV aren't really const, because we permute them. - * But we pretend they're const in the prototype to be compatible - * with other systems. - * - * LONGOPTS is a vector of `struct option' terminated by an - * element containing a name which is zero. - * - * LONGIND returns the index in LONGOPT of the long-named option found. - * It is only valid when a long-named option has been found by the most - * recent call. - * - * Return the option character from OPTS just read. Return -1 when there are - * no more options. For unrecognized options, or options missing arguments, - * `custom_optopt' is set to the option letter, and '?' is returned. - * - * The OPTS string is a list of characters which are recognized option letters, - * optionally followed by colons, specifying that that letter takes an - * argument, to be placed in `custom_optarg'. - * - * If a letter in OPTS is followed by two colons, its argument is optional. - * This behavior is specific to the GNU `getopt'. - * - * The argument `--' causes premature termination of argument scanning, - * explicitly telling `getopt' that there are no more options. If OPTS begins - * with `--', then non-option arguments are treated as arguments to the option - * '\0'. This behavior is specific to the GNU `getopt'. - */ - -static int getopt_internal_r(int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longind, - struct custom_getopt_data *d) -{ - int ret, print_errors = d->custom_opterr; - - if (optstring[0] == ':') - print_errors = 0; - if (argc < 1) - return -1; - d->custom_optarg = NULL; - - /* - * This is a big difference with GNU getopt, since optind == 0 - * means initialization while here 1 means first call. - */ - if (d->custom_optind == 0 || !d->initialized) { - if (d->custom_optind == 0) - d->custom_optind = 1; /* Don't scan ARGV[0], the program name. */ - custom_getopt_initialize(d); - } - if (d->nextchar == NULL || *d->nextchar == '\0') { - ret = shuffle_argv(argc, argv, longopts, d); - if (ret) - return ret; - } - if (longopts && (argv[d->custom_optind][1] == '-' )) - return check_long_opt(argc, argv, optstring, longopts, - longind, print_errors, d); - return check_short_opt(argc, argv, optstring, print_errors, d); -} - -static int custom_getopt_internal(int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longind) -{ - int result; - /* Keep a global copy of all internal members of d */ - static struct custom_getopt_data d; - - d.custom_optind = custom_optind; - d.custom_opterr = custom_opterr; - result = getopt_internal_r(argc, argv, optstring, longopts, - longind, &d); - custom_optind = d.custom_optind; - custom_optarg = d.custom_optarg; - custom_optopt = d.custom_optopt; - return result; -} - -static int custom_getopt_long (int argc, char *const *argv, const char *options, - const struct option *long_options, int *opt_index) -{ - return custom_getopt_internal(argc, argv, options, long_options, - opt_index); -} - static char *package_name = 0; @@ -1543,7 +1011,18 @@ int update_arg(void *field, char **orig_field, return 1; /* failure */ } - FIX_UNUSED (default_value); + if (possible_values && (found = check_possible_values((value ? value : default_value), possible_values)) < 0) + { + if (short_opt != '-') + fprintf (stderr, "%s: %s argument, \"%s\", for option `--%s' (`-%c')%s\n", + package_name, (found == -2) ? "ambiguous" : "invalid", value, long_opt, short_opt, + (additional_error ? additional_error : "")); + else + fprintf (stderr, "%s: %s argument, \"%s\", for option `--%s'%s\n", + package_name, (found == -2) ? "ambiguous" : "invalid", value, long_opt, + (additional_error ? additional_error : "")); + return 1; /* failure */ + } if (field_given && *field_given && ! override) return 0; @@ -1558,8 +1037,8 @@ int update_arg(void *field, char **orig_field, case ARG_FLAG: *((int *)field) = !*((int *)field); break; - case ARG_INT: - if (val) *((int *)field) = strtol (val, &stop_char, 0); + case ARG_ENUM: + if (val) *((int *)field) = found; break; case ARG_STRING: if (val) { @@ -1573,18 +1052,8 @@ int update_arg(void *field, char **orig_field, break; }; - /* check numeric conversion */ - switch(arg_type) { - case ARG_INT: - if (val && !(stop_char && *stop_char == '\0')) { - fprintf(stderr, "%s: invalid numeric value: %s\n", package_name, val); - return 1; /* failure */ - } - break; - default: - ; - }; - + FIX_UNUSED(stop_char); + /* store the original value */ switch(arg_type) { case ARG_NO: @@ -1688,7 +1157,7 @@ void update_multiple_arg(void *field, char ***orig_field, *orig_field = (char **) realloc (*orig_field, (field_given + prev_given) * sizeof (char *)); switch(arg_type) { - case ARG_INT: + case ARG_ENUM: *((int **)field) = (int *)realloc (*((int **)field), (field_given + prev_given) * sizeof (int)); break; case ARG_STRING: *((char ***)field) = (char **)realloc (*((char ***)field), (field_given + prev_given) * sizeof (char *)); break; @@ -1701,7 +1170,7 @@ void update_multiple_arg(void *field, char ***orig_field, tmp = list; switch(arg_type) { - case ARG_INT: + case ARG_ENUM: (*((int **)field))[i + field_given] = tmp->arg.int_arg; break; case ARG_STRING: (*((char ***)field))[i + field_given] = tmp->arg.string_arg; break; @@ -1715,7 +1184,7 @@ void update_multiple_arg(void *field, char ***orig_field, } else { /* set the default value */ if (default_value && ! field_given) { switch(arg_type) { - case ARG_INT: + case ARG_ENUM: if (! *((int **)field)) { *((int **)field) = (int *)malloc (sizeof (int)); (*((int **)field))[0] = default_value->int_arg; @@ -1752,18 +1221,19 @@ cmdline_parser_internal ( int initialize; int check_required; int check_ambiguity; - - char *optarg; - int optind; - int opterr; - int optopt; package_name = argv[0]; + /* TODO: Why is this here? It is not used anywhere. */ override = params->override; + FIX_UNUSED(override); + initialize = params->initialize; check_required = params->check_required; + + /* TODO: Why is this here? It is not used anywhere. */ check_ambiguity = params->check_ambiguity; + FIX_UNUSED(check_ambiguity); if (initialize) cmdline_parser_init (args_info); @@ -1801,6 +1271,8 @@ cmdline_parser_internal ( { "x509-dir", 1, NULL, 0 }, { "disable-ta-checks", 0, NULL, 0 }, { "disable-ca-checks", 0, NULL, 0 }, + { "application", 1, NULL, 0 }, + { "read-all-dgs", 0, NULL, 0 }, { "read-dg1", 0, NULL, 0 }, { "read-dg2", 0, NULL, 0 }, { "read-dg3", 0, NULL, 0 }, @@ -1833,21 +1305,10 @@ cmdline_parser_internal ( { "break", 0, NULL, 'b' }, { "translate", 1, NULL, 't' }, { "tr-03110v201", 0, NULL, 0 }, - { "disable-all-checks", 0, NULL, 0 }, { 0, 0, 0, 0 } }; - custom_optarg = optarg; - custom_optind = optind; - custom_opterr = opterr; - custom_optopt = optopt; - - c = custom_getopt_long (argc, argv, "hVr:vp::u::c::m::N::RUC:A:P:bt:", long_options, &option_index); - - optarg = custom_optarg; - optind = custom_optind; - opterr = custom_opterr; - optopt = custom_optopt; + c = getopt_long (argc, argv, "hVr:vp::u::c::m::N::RUC:A:P:bt:", long_options, &option_index); if (c == -1) break; /* Exit from `while (1)' loop. */ @@ -1863,12 +1324,12 @@ cmdline_parser_internal ( cmdline_parser_free (&local_args_info); exit (EXIT_SUCCESS); - case 'r': /* Number of the PC/SC reader to use (-1 for autodetect). */ + case 'r': /* Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen.. */ if (update_arg( (void *)&(args_info->reader_arg), &(args_info->reader_orig), &(args_info->reader_given), - &(local_args_info.reader_given), optarg, 0, "-1", ARG_INT, + &(local_args_info.reader_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "reader", 'r', additional_error)) @@ -2064,7 +1525,7 @@ cmdline_parser_internal ( if (update_arg( (void *)&(args_info->cvc_dir_arg), &(args_info->cvc_dir_orig), &(args_info->cvc_dir_given), - &(local_args_info.cvc_dir_given), optarg, 0, "/home/fm/.local/etc/eac/cvc", ARG_STRING, + &(local_args_info.cvc_dir_given), optarg, 0, "", ARG_STRING, check_ambiguity, override, 0, 0, "cvc-dir", '-', additional_error)) @@ -2078,14 +1539,14 @@ cmdline_parser_internal ( if (update_arg( (void *)&(args_info->x509_dir_arg), &(args_info->x509_dir_orig), &(args_info->x509_dir_given), - &(local_args_info.x509_dir_given), optarg, 0, "/home/fm/.local/etc/eac/x509", ARG_STRING, + &(local_args_info.x509_dir_given), optarg, 0, "", ARG_STRING, check_ambiguity, override, 0, 0, "x509-dir", '-', additional_error)) goto failure; } - /* Disable checking the validity period of CV certifcates. */ + /* Disable checking the validity period of CV certificates. */ else if (strcmp (long_options[option_index].name, "disable-ta-checks") == 0) { @@ -2109,7 +1570,33 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 1 (Document Type). */ + /* What card application to select. */ + else if (strcmp (long_options[option_index].name, "application") == 0) + { + + + if (update_arg( (void *)&(args_info->application_arg), + &(args_info->application_orig), &(args_info->application_given), + &(local_args_info.application_given), optarg, cmdline_parser_application_values, "eID", ARG_ENUM, + check_ambiguity, override, 0, 0, + "application", '-', + additional_error)) + goto failure; + + } + /* Read all available data groups. */ + else if (strcmp (long_options[option_index].name, "read-all-dgs") == 0) + { + + + if (update_arg((void *)&(args_info->read_all_dgs_flag), 0, &(args_info->read_all_dgs_given), + &(local_args_info.read_all_dgs_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "read-all-dgs", '-', + additional_error)) + goto failure; + + } + /* Read data group 1. */ else if (strcmp (long_options[option_index].name, "read-dg1") == 0) { @@ -2121,7 +1608,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 2 (Issuing State). */ + /* Read data group 2. */ else if (strcmp (long_options[option_index].name, "read-dg2") == 0) { @@ -2133,7 +1620,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 3 (Date of Expiry). */ + /* Read data group 3. */ else if (strcmp (long_options[option_index].name, "read-dg3") == 0) { @@ -2145,7 +1632,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 4 (Given Names). */ + /* Read data group 4. */ else if (strcmp (long_options[option_index].name, "read-dg4") == 0) { @@ -2157,7 +1644,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 5 (Family Names). */ + /* Read data group 5. */ else if (strcmp (long_options[option_index].name, "read-dg5") == 0) { @@ -2169,7 +1656,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 6 (Religious/Artistic Name). */ + /* Read data group 6. */ else if (strcmp (long_options[option_index].name, "read-dg6") == 0) { @@ -2181,7 +1668,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 7 (Academic Title). */ + /* Read data group 7. */ else if (strcmp (long_options[option_index].name, "read-dg7") == 0) { @@ -2193,7 +1680,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 8 (Date of Birth). */ + /* Read data group 8. */ else if (strcmp (long_options[option_index].name, "read-dg8") == 0) { @@ -2205,7 +1692,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 9 (Place of Birth). */ + /* Read data group 9. */ else if (strcmp (long_options[option_index].name, "read-dg9") == 0) { @@ -2217,7 +1704,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 10 (Nationality). */ + /* Read data group 10. */ else if (strcmp (long_options[option_index].name, "read-dg10") == 0) { @@ -2229,7 +1716,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 11 (Sex). */ + /* Read data group 11. */ else if (strcmp (long_options[option_index].name, "read-dg11") == 0) { @@ -2241,7 +1728,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 12 (Optional Data). */ + /* Read data group 12. */ else if (strcmp (long_options[option_index].name, "read-dg12") == 0) { @@ -2253,7 +1740,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 13 (Birth Name). */ + /* Read data group 13. */ else if (strcmp (long_options[option_index].name, "read-dg13") == 0) { @@ -2265,7 +1752,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 14. */ + /* Read data group 14. */ else if (strcmp (long_options[option_index].name, "read-dg14") == 0) { @@ -2277,7 +1764,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 15. */ + /* Read data group 15. */ else if (strcmp (long_options[option_index].name, "read-dg15") == 0) { @@ -2289,7 +1776,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 16. */ + /* Read data group 16. */ else if (strcmp (long_options[option_index].name, "read-dg16") == 0) { @@ -2301,7 +1788,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 17 (Normal Place of Residence). */ + /* Read data group 17. */ else if (strcmp (long_options[option_index].name, "read-dg17") == 0) { @@ -2313,7 +1800,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 18 (Community ID). */ + /* Read data group 18. */ else if (strcmp (long_options[option_index].name, "read-dg18") == 0) { @@ -2325,7 +1812,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 19 (Residence Permit I). */ + /* Read data group 19. */ else if (strcmp (long_options[option_index].name, "read-dg19") == 0) { @@ -2337,7 +1824,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 20 (Residence Permit II). */ + /* Read data group 20. */ else if (strcmp (long_options[option_index].name, "read-dg20") == 0) { @@ -2349,7 +1836,7 @@ cmdline_parser_internal ( goto failure; } - /* Read DG 21 (Optional Data). */ + /* Read data group 21. */ else if (strcmp (long_options[option_index].name, "read-dg21") == 0) { @@ -2361,7 +1848,7 @@ cmdline_parser_internal ( goto failure; } - /* Write DG 17 (Normal Place of Residence). */ + /* Write data group 17. */ else if (strcmp (long_options[option_index].name, "write-dg17") == 0) { @@ -2375,7 +1862,7 @@ cmdline_parser_internal ( goto failure; } - /* Write DG 18 (Community ID). */ + /* Write data group 18. */ else if (strcmp (long_options[option_index].name, "write-dg18") == 0) { @@ -2389,7 +1876,7 @@ cmdline_parser_internal ( goto failure; } - /* Write DG 19 (Residence Permit I). */ + /* Write data group 19. */ else if (strcmp (long_options[option_index].name, "write-dg19") == 0) { @@ -2403,7 +1890,7 @@ cmdline_parser_internal ( goto failure; } - /* Write DG 20 (Residence Permit II). */ + /* Write data group 20. */ else if (strcmp (long_options[option_index].name, "write-dg20") == 0) { @@ -2417,7 +1904,7 @@ cmdline_parser_internal ( goto failure; } - /* Write DG 21 (Optional Data). */ + /* Write data group 21. */ else if (strcmp (long_options[option_index].name, "write-dg21") == 0) { @@ -2484,18 +1971,6 @@ cmdline_parser_internal ( additional_error)) goto failure; - } - /* Disable all checking of fly-by-data. */ - else if (strcmp (long_options[option_index].name, "disable-all-checks") == 0) - { - - - if (update_arg((void *)&(args_info->disable_all_checks_flag), 0, &(args_info->disable_all_checks_given), - &(local_args_info.disable_all_checks_given), optarg, 0, 0, ARG_FLAG, - check_ambiguity, override, 1, 0, "disable-all-checks", '-', - additional_error)) - goto failure; - } break; @@ -2538,3 +2013,4 @@ cmdline_parser_internal ( cmdline_parser_release (&local_args_info); return (EXIT_FAILURE); } +/* vim: set ft=c noet ts=8 sts=8 sw=8 tw=80 nojs spell : */ diff --git a/src/tools/npa-tool-cmdline.h b/src/tools/npa-tool-cmdline.h index 4c724ead06..35dcff4350 100644 --- a/src/tools/npa-tool-cmdline.h +++ b/src/tools/npa-tool-cmdline.h @@ -1,9 +1,9 @@ /** @file npa-tool-cmdline.h * @brief The header file for the command line option parser - * generated by GNU Gengetopt version 2.22.6 + * generated by GNU Gengetopt version 2.23 * http://www.gnu.org/software/gengetopt. * DO NOT modify this file, since it can be overwritten - * @author GNU Gengetopt by Lorenzo Bettini */ + * @author GNU Gengetopt */ #ifndef NPA_TOOL_CMDLINE_H #define NPA_TOOL_CMDLINE_H @@ -34,14 +34,16 @@ extern "C" { #define CMDLINE_PARSER_VERSION VERSION #endif +enum enum_application { application__NULL = -1, application_arg_eID = 0, application_arg_eMRTD }; + /** @brief Where the command line options are stored */ struct gengetopt_args_info { const char *help_help; /**< @brief Print help and exit help description. */ const char *version_help; /**< @brief Print version and exit help description. */ - int reader_arg; /**< @brief Number of the PC/SC reader to use (-1 for autodetect) (default='-1'). */ - char * reader_orig; /**< @brief Number of the PC/SC reader to use (-1 for autodetect) original value given at command line. */ - const char *reader_help; /**< @brief Number of the PC/SC reader to use (-1 for autodetect) help description. */ + char * reader_arg; /**< @brief Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen.. */ + char * reader_orig; /**< @brief Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen. original value given at command line. */ + const char *reader_help; /**< @brief Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen. help description. */ unsigned int verbose_min; /**< @brief Use (several times) to be more verbose's minimum occurreces */ unsigned int verbose_max; /**< @brief Use (several times) to be more verbose's maximum occurreces */ const char *verbose_help; /**< @brief Use (several times) to be more verbose help description. */ @@ -83,73 +85,78 @@ struct gengetopt_args_info char * private_key_arg; /**< @brief Terminal's private key. */ char * private_key_orig; /**< @brief Terminal's private key original value given at command line. */ const char *private_key_help; /**< @brief Terminal's private key help description. */ - char * cvc_dir_arg; /**< @brief Where to look for the CVCA's certificate (default='/home/fm/.local/etc/eac/cvc'). */ + char * cvc_dir_arg; /**< @brief Where to look for the CVCA's certificate (default=''). */ char * cvc_dir_orig; /**< @brief Where to look for the CVCA's certificate original value given at command line. */ const char *cvc_dir_help; /**< @brief Where to look for the CVCA's certificate help description. */ - char * x509_dir_arg; /**< @brief Where to look for the CSCA's certificate (default='/home/fm/.local/etc/eac/x509'). */ + char * x509_dir_arg; /**< @brief Where to look for the CSCA's certificate (default=''). */ char * x509_dir_orig; /**< @brief Where to look for the CSCA's certificate original value given at command line. */ const char *x509_dir_help; /**< @brief Where to look for the CSCA's certificate help description. */ - int disable_ta_checks_flag; /**< @brief Disable checking the validity period of CV certifcates (default=off). */ - const char *disable_ta_checks_help; /**< @brief Disable checking the validity period of CV certifcates help description. */ + int disable_ta_checks_flag; /**< @brief Disable checking the validity period of CV certificates (default=off). */ + const char *disable_ta_checks_help; /**< @brief Disable checking the validity period of CV certificates help description. */ int disable_ca_checks_flag; /**< @brief Disable passive authentication (default=off). */ const char *disable_ca_checks_help; /**< @brief Disable passive authentication help description. */ - int read_dg1_flag; /**< @brief Read DG 1 (Document Type) (default=off). */ - const char *read_dg1_help; /**< @brief Read DG 1 (Document Type) help description. */ - int read_dg2_flag; /**< @brief Read DG 2 (Issuing State) (default=off). */ - const char *read_dg2_help; /**< @brief Read DG 2 (Issuing State) help description. */ - int read_dg3_flag; /**< @brief Read DG 3 (Date of Expiry) (default=off). */ - const char *read_dg3_help; /**< @brief Read DG 3 (Date of Expiry) help description. */ - int read_dg4_flag; /**< @brief Read DG 4 (Given Names) (default=off). */ - const char *read_dg4_help; /**< @brief Read DG 4 (Given Names) help description. */ - int read_dg5_flag; /**< @brief Read DG 5 (Family Names) (default=off). */ - const char *read_dg5_help; /**< @brief Read DG 5 (Family Names) help description. */ - int read_dg6_flag; /**< @brief Read DG 6 (Religious/Artistic Name) (default=off). */ - const char *read_dg6_help; /**< @brief Read DG 6 (Religious/Artistic Name) help description. */ - int read_dg7_flag; /**< @brief Read DG 7 (Academic Title) (default=off). */ - const char *read_dg7_help; /**< @brief Read DG 7 (Academic Title) help description. */ - int read_dg8_flag; /**< @brief Read DG 8 (Date of Birth) (default=off). */ - const char *read_dg8_help; /**< @brief Read DG 8 (Date of Birth) help description. */ - int read_dg9_flag; /**< @brief Read DG 9 (Place of Birth) (default=off). */ - const char *read_dg9_help; /**< @brief Read DG 9 (Place of Birth) help description. */ - int read_dg10_flag; /**< @brief Read DG 10 (Nationality) (default=off). */ - const char *read_dg10_help; /**< @brief Read DG 10 (Nationality) help description. */ - int read_dg11_flag; /**< @brief Read DG 11 (Sex) (default=off). */ - const char *read_dg11_help; /**< @brief Read DG 11 (Sex) help description. */ - int read_dg12_flag; /**< @brief Read DG 12 (Optional Data) (default=off). */ - const char *read_dg12_help; /**< @brief Read DG 12 (Optional Data) help description. */ - int read_dg13_flag; /**< @brief Read DG 13 (Birth Name) (default=off). */ - const char *read_dg13_help; /**< @brief Read DG 13 (Birth Name) help description. */ - int read_dg14_flag; /**< @brief Read DG 14 (default=off). */ - const char *read_dg14_help; /**< @brief Read DG 14 help description. */ - int read_dg15_flag; /**< @brief Read DG 15 (default=off). */ - const char *read_dg15_help; /**< @brief Read DG 15 help description. */ - int read_dg16_flag; /**< @brief Read DG 16 (default=off). */ - const char *read_dg16_help; /**< @brief Read DG 16 help description. */ - int read_dg17_flag; /**< @brief Read DG 17 (Normal Place of Residence) (default=off). */ - const char *read_dg17_help; /**< @brief Read DG 17 (Normal Place of Residence) help description. */ - int read_dg18_flag; /**< @brief Read DG 18 (Community ID) (default=off). */ - const char *read_dg18_help; /**< @brief Read DG 18 (Community ID) help description. */ - int read_dg19_flag; /**< @brief Read DG 19 (Residence Permit I) (default=off). */ - const char *read_dg19_help; /**< @brief Read DG 19 (Residence Permit I) help description. */ - int read_dg20_flag; /**< @brief Read DG 20 (Residence Permit II) (default=off). */ - const char *read_dg20_help; /**< @brief Read DG 20 (Residence Permit II) help description. */ - int read_dg21_flag; /**< @brief Read DG 21 (Optional Data) (default=off). */ - const char *read_dg21_help; /**< @brief Read DG 21 (Optional Data) help description. */ - char * write_dg17_arg; /**< @brief Write DG 17 (Normal Place of Residence). */ - char * write_dg17_orig; /**< @brief Write DG 17 (Normal Place of Residence) original value given at command line. */ - const char *write_dg17_help; /**< @brief Write DG 17 (Normal Place of Residence) help description. */ - char * write_dg18_arg; /**< @brief Write DG 18 (Community ID). */ - char * write_dg18_orig; /**< @brief Write DG 18 (Community ID) original value given at command line. */ - const char *write_dg18_help; /**< @brief Write DG 18 (Community ID) help description. */ - char * write_dg19_arg; /**< @brief Write DG 19 (Residence Permit I). */ - char * write_dg19_orig; /**< @brief Write DG 19 (Residence Permit I) original value given at command line. */ - const char *write_dg19_help; /**< @brief Write DG 19 (Residence Permit I) help description. */ - char * write_dg20_arg; /**< @brief Write DG 20 (Residence Permit II). */ - char * write_dg20_orig; /**< @brief Write DG 20 (Residence Permit II) original value given at command line. */ - const char *write_dg20_help; /**< @brief Write DG 20 (Residence Permit II) help description. */ - char * write_dg21_arg; /**< @brief Write DG 21 (Optional Data). */ - char * write_dg21_orig; /**< @brief Write DG 21 (Optional Data) original value given at command line. */ - const char *write_dg21_help; /**< @brief Write DG 21 (Optional Data) help description. */ + enum enum_application application_arg; /**< @brief What card application to select (default='eID'). */ + char * application_orig; /**< @brief What card application to select original value given at command line. */ + const char *application_help; /**< @brief What card application to select help description. */ + int read_all_dgs_flag; /**< @brief Read all available data groups (default=off). */ + const char *read_all_dgs_help; /**< @brief Read all available data groups help description. */ + int read_dg1_flag; /**< @brief Read data group 1 (default=off). */ + const char *read_dg1_help; /**< @brief Read data group 1 help description. */ + int read_dg2_flag; /**< @brief Read data group 2 (default=off). */ + const char *read_dg2_help; /**< @brief Read data group 2 help description. */ + int read_dg3_flag; /**< @brief Read data group 3 (default=off). */ + const char *read_dg3_help; /**< @brief Read data group 3 help description. */ + int read_dg4_flag; /**< @brief Read data group 4 (default=off). */ + const char *read_dg4_help; /**< @brief Read data group 4 help description. */ + int read_dg5_flag; /**< @brief Read data group 5 (default=off). */ + const char *read_dg5_help; /**< @brief Read data group 5 help description. */ + int read_dg6_flag; /**< @brief Read data group 6 (default=off). */ + const char *read_dg6_help; /**< @brief Read data group 6 help description. */ + int read_dg7_flag; /**< @brief Read data group 7 (default=off). */ + const char *read_dg7_help; /**< @brief Read data group 7 help description. */ + int read_dg8_flag; /**< @brief Read data group 8 (default=off). */ + const char *read_dg8_help; /**< @brief Read data group 8 help description. */ + int read_dg9_flag; /**< @brief Read data group 9 (default=off). */ + const char *read_dg9_help; /**< @brief Read data group 9 help description. */ + int read_dg10_flag; /**< @brief Read data group 10 (default=off). */ + const char *read_dg10_help; /**< @brief Read data group 10 help description. */ + int read_dg11_flag; /**< @brief Read data group 11 (default=off). */ + const char *read_dg11_help; /**< @brief Read data group 11 help description. */ + int read_dg12_flag; /**< @brief Read data group 12 (default=off). */ + const char *read_dg12_help; /**< @brief Read data group 12 help description. */ + int read_dg13_flag; /**< @brief Read data group 13 (default=off). */ + const char *read_dg13_help; /**< @brief Read data group 13 help description. */ + int read_dg14_flag; /**< @brief Read data group 14 (default=off). */ + const char *read_dg14_help; /**< @brief Read data group 14 help description. */ + int read_dg15_flag; /**< @brief Read data group 15 (default=off). */ + const char *read_dg15_help; /**< @brief Read data group 15 help description. */ + int read_dg16_flag; /**< @brief Read data group 16 (default=off). */ + const char *read_dg16_help; /**< @brief Read data group 16 help description. */ + int read_dg17_flag; /**< @brief Read data group 17 (default=off). */ + const char *read_dg17_help; /**< @brief Read data group 17 help description. */ + int read_dg18_flag; /**< @brief Read data group 18 (default=off). */ + const char *read_dg18_help; /**< @brief Read data group 18 help description. */ + int read_dg19_flag; /**< @brief Read data group 19 (default=off). */ + const char *read_dg19_help; /**< @brief Read data group 19 help description. */ + int read_dg20_flag; /**< @brief Read data group 20 (default=off). */ + const char *read_dg20_help; /**< @brief Read data group 20 help description. */ + int read_dg21_flag; /**< @brief Read data group 21 (default=off). */ + const char *read_dg21_help; /**< @brief Read data group 21 help description. */ + char * write_dg17_arg; /**< @brief Write data group 17. */ + char * write_dg17_orig; /**< @brief Write data group 17 original value given at command line. */ + const char *write_dg17_help; /**< @brief Write data group 17 help description. */ + char * write_dg18_arg; /**< @brief Write data group 18. */ + char * write_dg18_orig; /**< @brief Write data group 18 original value given at command line. */ + const char *write_dg18_help; /**< @brief Write data group 18 help description. */ + char * write_dg19_arg; /**< @brief Write data group 19. */ + char * write_dg19_orig; /**< @brief Write data group 19 original value given at command line. */ + const char *write_dg19_help; /**< @brief Write data group 19 help description. */ + char * write_dg20_arg; /**< @brief Write data group 20. */ + char * write_dg20_orig; /**< @brief Write data group 20 original value given at command line. */ + const char *write_dg20_help; /**< @brief Write data group 20 help description. */ + char * write_dg21_arg; /**< @brief Write data group 21. */ + char * write_dg21_orig; /**< @brief Write data group 21 original value given at command line. */ + const char *write_dg21_help; /**< @brief Write data group 21 help description. */ char * verify_validity_arg; /**< @brief Verify chip's validity with a reference date. */ char * verify_validity_orig; /**< @brief Verify chip's validity with a reference date original value given at command line. */ const char *verify_validity_help; /**< @brief Verify chip's validity with a reference date help description. */ @@ -166,8 +173,6 @@ struct gengetopt_args_info const char *translate_help; /**< @brief File with APDUs of HEX_STRINGs to send through the secure channel help description. */ int tr_03110v201_flag; /**< @brief Force compliance to BSI TR-03110 version 2.01 (default=off). */ const char *tr_03110v201_help; /**< @brief Force compliance to BSI TR-03110 version 2.01 help description. */ - int disable_all_checks_flag; /**< @brief Disable all checking of fly-by-data (default=off). */ - const char *disable_all_checks_help; /**< @brief Disable all checking of fly-by-data help description. */ unsigned int help_given ; /**< @brief Whether help was given. */ unsigned int version_given ; /**< @brief Whether version was given. */ @@ -190,6 +195,8 @@ struct gengetopt_args_info unsigned int x509_dir_given ; /**< @brief Whether x509-dir was given. */ unsigned int disable_ta_checks_given ; /**< @brief Whether disable-ta-checks was given. */ unsigned int disable_ca_checks_given ; /**< @brief Whether disable-ca-checks was given. */ + unsigned int application_given ; /**< @brief Whether application was given. */ + unsigned int read_all_dgs_given ; /**< @brief Whether read-all-dgs was given. */ unsigned int read_dg1_given ; /**< @brief Whether read-dg1 was given. */ unsigned int read_dg2_given ; /**< @brief Whether read-dg2 was given. */ unsigned int read_dg3_given ; /**< @brief Whether read-dg3 was given. */ @@ -222,7 +229,6 @@ struct gengetopt_args_info unsigned int break_given ; /**< @brief Whether break was given. */ unsigned int translate_given ; /**< @brief Whether translate was given. */ unsigned int tr_03110v201_given ; /**< @brief Whether tr-03110v201 was given. */ - unsigned int disable_all_checks_given ; /**< @brief Whether disable-all-checks was given. */ } ; @@ -347,6 +353,8 @@ void cmdline_parser_free (struct gengetopt_args_info *args_info); int cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name); +extern const char *cmdline_parser_application_values[]; /**< @brief Possible values for application. */ + #ifdef __cplusplus } diff --git a/src/tools/npa-tool.1 b/src/tools/npa-tool.1 deleted file mode 100644 index c1a655e36d..0000000000 --- a/src/tools/npa-tool.1 +++ /dev/null @@ -1,205 +0,0 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.4. -.TH NPA-TOOL "1" "July 2016" "OpenSC 0.16.0" "User Commands" -.SH NAME -npa-tool \- manual page for npa-tool 0.16.0 -.SH SYNOPSIS -.B npa-tool -[\fI\,OPTIONS\/\fR]... -.SH DESCRIPTION -npa\-tool 0.16.0 -.TP -\fB\-h\fR, \fB\-\-help\fR -Print help and exit -.TP -\fB\-V\fR, \fB\-\-version\fR -Print version and exit -.TP -\fB\-r\fR, \fB\-\-reader\fR=\fI\,INT\/\fR -Number of the PC/SC reader to use (\fB\-1\fR for -autodetect) (default=`\-1') -.TP -\fB\-v\fR, \fB\-\-verbose\fR -Use (several times) to be more verbose -.SS "Password Authenticated Connection Establishment (PACE):" -.TP -\fB\-p\fR, \fB\-\-pin\fR[=\fI\,STRING\/\fR] -Run PACE with (transport) eID\-PIN -.TP -\fB\-u\fR, \fB\-\-puk\fR[=\fI\,STRING\/\fR] -Run PACE with PUK -.TP -\fB\-c\fR, \fB\-\-can\fR[=\fI\,STRING\/\fR] -Run PACE with CAN -.TP -\fB\-m\fR, \fB\-\-mrz\fR[=\fI\,STRING\/\fR] -Run PACE with MRZ (insert MRZ without newlines) -.TP -\fB\-\-env\fR -Whether to use environment variables PIN, PUK, -CAN, MRZ and NEWPIN. You may want to clean -your environment before enabling this. -(default=off) -.SS "PIN management:" -.TP -\fB\-N\fR, \fB\-\-new\-pin\fR[=\fI\,STRING\/\fR] -Install a new PIN -.TP -\fB\-R\fR, \fB\-\-resume\fR -Resume eID\-PIN (uses CAN to activate last -retry) (default=off) -.TP -\fB\-U\fR, \fB\-\-unblock\fR -Unblock PIN (uses PUK to activate three more -retries) (default=off) -.SS "Terminal Authentication (TA) and Chip Authentication (CA):" -.TP -\fB\-C\fR, \fB\-\-cv\-certificate\fR=\fI\,FILENAME\/\fR Card Verifiable Certificate to create a -certificate chain. Can be used multiple times -(order is important). -.TP -\fB\-\-cert\-desc\fR=\fI\,HEX_STRING\/\fR -Certificate description to show for Terminal -Authentication -.TP -\fB\-\-chat\fR=\fI\,HEX_STRING\/\fR -Card holder authorization template to use -(default is terminal's CHAT). Use -7F4C0E060904007F000703010203530103 to trigger -EAC on the CAT\-C (Komfortleser). -.TP -\fB\-A\fR, \fB\-\-auxiliary\-data\fR=\fI\,HEX_STRING\/\fR -Terminal's auxiliary data (default is -.TP -determined by verification of validity, age -and community ID). -.TP -\fB\-P\fR, \fB\-\-private\-key\fR=\fI\,FILENAME\/\fR -Terminal's private key -.TP -\fB\-\-cvc\-dir\fR=\fI\,DIRECTORY\/\fR -Where to look for the CVCA's certificate -(default=`/home/fm/.local/etc/eac/cvc') -.TP -\fB\-\-x509\-dir\fR=\fI\,DIRECTORY\/\fR -Where to look for the CSCA's certificate -(default=`/home/fm/.local/etc/eac/x509') -.TP -\fB\-\-disable\-ta\-checks\fR -Disable checking the validity period of CV -certifcates (default=off) -.TP -\fB\-\-disable\-ca\-checks\fR -Disable passive authentication (default=off) -.SS "Read and write data groups:" -.TP -\fB\-\-read\-dg1\fR -Read DG 1 (Document Type) (default=off) -.TP -\fB\-\-read\-dg2\fR -Read DG 2 (Issuing State) (default=off) -.TP -\fB\-\-read\-dg3\fR -Read DG 3 (Date of Expiry) (default=off) -.TP -\fB\-\-read\-dg4\fR -Read DG 4 (Given Names) (default=off) -.TP -\fB\-\-read\-dg5\fR -Read DG 5 (Family Names) (default=off) -.TP -\fB\-\-read\-dg6\fR -Read DG 6 (Religious/Artistic Name) -(default=off) -.TP -\fB\-\-read\-dg7\fR -Read DG 7 (Academic Title) (default=off) -.TP -\fB\-\-read\-dg8\fR -Read DG 8 (Date of Birth) (default=off) -.TP -\fB\-\-read\-dg9\fR -Read DG 9 (Place of Birth) (default=off) -.TP -\fB\-\-read\-dg10\fR -Read DG 10 (Nationality) (default=off) -.TP -\fB\-\-read\-dg11\fR -Read DG 11 (Sex) (default=off) -.TP -\fB\-\-read\-dg12\fR -Read DG 12 (Optional Data) (default=off) -.TP -\fB\-\-read\-dg13\fR -Read DG 13 (Birth Name) (default=off) -.TP -\fB\-\-read\-dg14\fR -Read DG 14 (default=off) -.TP -\fB\-\-read\-dg15\fR -Read DG 15 (default=off) -.TP -\fB\-\-read\-dg16\fR -Read DG 16 (default=off) -.TP -\fB\-\-read\-dg17\fR -Read DG 17 (Normal Place of Residence) -(default=off) -.TP -\fB\-\-read\-dg18\fR -Read DG 18 (Community ID) (default=off) -.TP -\fB\-\-read\-dg19\fR -Read DG 19 (Residence Permit I) (default=off) -.TP -\fB\-\-read\-dg20\fR -Read DG 20 (Residence Permit II) -(default=off) -.TP -\fB\-\-read\-dg21\fR -Read DG 21 (Optional Data) (default=off) -.TP -\fB\-\-write\-dg17\fR=\fI\,HEX_STRING\/\fR -Write DG 17 (Normal Place of Residence) -.TP -\fB\-\-write\-dg18\fR=\fI\,HEX_STRING\/\fR -Write DG 18 (Community ID) -.TP -\fB\-\-write\-dg19\fR=\fI\,HEX_STRING\/\fR -Write DG 19 (Residence Permit I) -.TP -\fB\-\-write\-dg20\fR=\fI\,HEX_STRING\/\fR -Write DG 20 (Residence Permit II) -.TP -\fB\-\-write\-dg21\fR=\fI\,HEX_STRING\/\fR -Write DG 21 (Optional Data) -.SS "Verification of validity, age and community ID:" -.TP -\fB\-\-verify\-validity\fR=\fI\,YYYYMMDD\/\fR -Verify chip's validity with a reference date -.TP -\fB\-\-older\-than\fR=\fI\,YYYYMMDD\/\fR -Verify age with a reference date -.TP -\fB\-\-verify\-community\fR=\fI\,HEX_STRING\/\fR -Verify community ID with a reference ID -.SS "Special options, not always useful:" -.TP -\fB\-b\fR, \fB\-\-break\fR -Brute force PIN, CAN or PUK. Use together with -\fB\-p\fR, \fB\-a\fR or \fB\-u\fR (default=off) -.TP -\fB\-t\fR, \fB\-\-translate\fR=\fI\,FILENAME\/\fR -File with APDUs of HEX_STRINGs to send through -the secure channel (default=`stdin') -.TP -\fB\-\-tr\-03110v201\fR -Force compliance to BSI TR\-03110 version 2.01 -(default=off) -.TP -\fB\-\-disable\-all\-checks\fR -Disable all checking of fly\-by\-data -(default=off) -.SH AUTHOR -Written by Frank Morgner -.SH "REPORTING BUGS" -Report bugs to opensc\-devel@lists.sourceforge.net diff --git a/src/tools/npa-tool.c b/src/tools/npa-tool.c index 11b557598e..d74b5a8917 100644 --- a/src/tools/npa-tool.c +++ b/src/tools/npa-tool.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Frank Morgner + * Copyright (C) 2010-2018 Frank Morgner * * This file is part of OpenSC. * @@ -15,18 +15,19 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif -#ifdef ENABLE_OPENPACE -#include "npa-tool-cmdline.h" #include "fread_to_eof.h" -#include "sm/sslutil.h" +#include "npa-tool-cmdline.h" #include "sm/sm-eac.h" +#include "util.h" +#include #include +#include #include #include #include @@ -62,7 +63,8 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) #endif #ifndef HAVE_GETLINE -static int getline(char **lineptr, size_t *n, FILE *stream) +static ssize_t +getline(char **lineptr, size_t *n, FILE *stream) { char *p; @@ -87,14 +89,15 @@ static int getline(char **lineptr, size_t *n, FILE *stream) #define ASN1_APP_IMP_OPT(stname, field, type, tag) ASN1_EX_TYPE(ASN1_TFLG_IMPTAG|ASN1_TFLG_APPLICATION|ASN1_TFLG_OPTIONAL, tag, stname, field, type) #define ASN1_APP_IMP(stname, field, type, tag) ASN1_EX_TYPE(ASN1_TFLG_IMPTAG|ASN1_TFLG_APPLICATION, tag, stname, field, type) +typedef ASN1_AUXILIARY_DATA ASN1_AUXILIARY_DATA_NPA_TOOL; /* 0x67 * Auxiliary authenticated data */ -ASN1_ITEM_TEMPLATE(ASN1_AUXILIARY_DATA) = +ASN1_ITEM_TEMPLATE(ASN1_AUXILIARY_DATA_NPA_TOOL) = ASN1_EX_TEMPLATE_TYPE( ASN1_TFLG_SEQUENCE_OF|ASN1_TFLG_IMPTAG|ASN1_TFLG_APPLICATION, 7, AuxiliaryAuthenticatedData, CVC_DISCRETIONARY_DATA_TEMPLATE) -ASN1_ITEM_TEMPLATE_END(ASN1_AUXILIARY_DATA) -IMPLEMENT_ASN1_FUNCTIONS(ASN1_AUXILIARY_DATA) +ASN1_ITEM_TEMPLATE_END(ASN1_AUXILIARY_DATA_NPA_TOOL) +IMPLEMENT_ASN1_FUNCTIONS(ASN1_AUXILIARY_DATA_NPA_TOOL) /** * @brief Print binary data to a file stream @@ -109,71 +112,20 @@ IMPLEMENT_ASN1_FUNCTIONS(ASN1_AUXILIARY_DATA) label, (unsigned int) len, len==1?"":"s", len==0?"":":\n", sc_dump_hex(data, len)); \ } -static int initialize(int reader_id, int verbose, - sc_context_t **ctx, sc_reader_t **reader) -{ - unsigned int i, reader_count; - int r; - - if (!ctx || !reader) - return SC_ERROR_INVALID_ARGUMENTS; - - r = sc_establish_context(ctx, ""); - if (r < 0 || !*ctx) { - fprintf(stderr, "Failed to create initial context: %s", sc_strerror(r)); - return r; - } - - (*ctx)->debug = verbose; - (*ctx)->flags |= SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER; - - reader_count = sc_ctx_get_reader_count(*ctx); - - if (reader_count == 0) { - sc_debug(*ctx, SC_LOG_DEBUG_NORMAL, "No reader not found.\n"); - return SC_ERROR_NO_READERS_FOUND; - } - - if (reader_id < 0) { - /* Automatically try to skip to a reader with a card if reader not specified */ - for (i = 0; i < reader_count; i++) { - *reader = sc_ctx_get_reader(*ctx, i); - if (sc_detect_card_presence(*reader) & SC_READER_CARD_PRESENT) { - reader_id = i; - sc_debug(*ctx, SC_LOG_DEBUG_NORMAL, "Using the first reader" - " with a card: %s", (*reader)->name); - break; - } - } - if ((unsigned int) reader_id >= reader_count) { - sc_debug(*ctx, SC_LOG_DEBUG_NORMAL, "No card found, using the first reader."); - reader_id = 0; - } - } - - if ((unsigned int) reader_id >= reader_count) { - sc_debug(*ctx, SC_LOG_DEBUG_NORMAL, "Invalid reader number " - "(%d), only %d available.\n", reader_id, reader_count); - return SC_ERROR_NO_READERS_FOUND; - } - - *reader = sc_ctx_get_reader(*ctx, reader_id); - - return SC_SUCCESS; -} - +static const char *app_name = "npa-tool"; static void read_dg(sc_card_t *card, unsigned char sfid, const char *dg_str, unsigned char **dg, size_t *dg_len) { int r = iso7816_read_binary_sfid(card, sfid, dg, dg_len); if (r < 0) - fprintf(stderr, "Coult not read DG %02u %s (%s)\n", + fprintf(stderr, "Could not read DG %02u %s (%s)\n", sfid, dg_str, sc_strerror(r)); else { - char buf[0x200]; - sc_hex_dump(NULL, 0, *dg, *dg_len, buf, sizeof buf); - fprintf(stdout, "Read %s", buf); + /* 32768 is the maximum size of dg according to ICAO Doc 9303 */ + char buf[32768*5]; + sc_hex_dump(*dg, *dg_len, buf, sizeof buf); + fprintf(stdout, "Read DG %02u %s:\n%s", sfid, dg_str, buf); } } @@ -205,15 +157,12 @@ static void verify(sc_card_t *card, const char *verify_str, sc_apdu_t apdu; int r; - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, ISO_VERIFY, 0x80, 0); + sc_format_apdu_ex(&apdu, 0x00, ISO_VERIFY, 0x80, 0, data, data_len, NULL, 0); apdu.cla = 0x80; - apdu.data = data; - apdu.datalen = data_len; - apdu.lc = data_len; r = sc_transmit_apdu(card, &apdu); if (r < 0) - fprintf(stderr, "Coult not verify %s (%s)\n", + fprintf(stderr, "Could not verify %s (%s)\n", verify_str, sc_strerror(r)); else printf("Verified %s\n", verify_str); @@ -225,7 +174,7 @@ int npa_translate_apdus(sc_card_t *card, FILE *input) char *read = NULL; size_t readlen = 0, apdulen; sc_apdu_t apdu; - int linelen; + ssize_t linelen; int r; memset(&apdu, 0, sizeof apdu); @@ -259,7 +208,7 @@ int npa_translate_apdus(sc_card_t *card, FILE *input) r = sc_bytes2apdu(card->ctx, buf, apdulen, &apdu); if (r < 0) { - sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid C-APDU", buf, apdulen); + sc_log_hex(card->ctx, "Invalid C-APDU", buf, apdulen); continue; } @@ -284,8 +233,8 @@ int npa_translate_apdus(sc_card_t *card, FILE *input) return r; } -static int add_to_ASN1_AUXILIARY_DATA( - ASN1_AUXILIARY_DATA **auxiliary_data, +static int add_to_ASN1_AUXILIARY_DATA_NPA_TOOL( + ASN1_AUXILIARY_DATA_NPA_TOOL **auxiliary_data, int nid, const unsigned char *data, size_t data_len) { int r; @@ -297,7 +246,7 @@ static int add_to_ASN1_AUXILIARY_DATA( } if (!*auxiliary_data) { - *auxiliary_data = ASN1_AUXILIARY_DATA_new(); + *auxiliary_data = ASN1_AUXILIARY_DATA_NPA_TOOL_new(); if (!*auxiliary_data) { r = SC_ERROR_INTERNAL; goto err; @@ -310,7 +259,11 @@ static int add_to_ASN1_AUXILIARY_DATA( goto err; } +#ifndef HAVE_EAC_OBJ_NID2OBJ template->type = OBJ_nid2obj(nid); +#else + template->type = EAC_OBJ_nid2obj(nid); +#endif if (!template->type) { r = SC_ERROR_INTERNAL; goto err; @@ -318,9 +271,8 @@ static int add_to_ASN1_AUXILIARY_DATA( if (data && data_len) { template->discretionary_data3 = ASN1_OCTET_STRING_new(); - if (!template->discretionary_data3 - || !M_ASN1_OCTET_STRING_set( - template->discretionary_data3, data, data_len)) { + if (!template->discretionary_data3 || !ASN1_OCTET_STRING_set( + template->discretionary_data3, data, (int)data_len)) { r = SC_ERROR_INTERNAL; goto err; } @@ -359,7 +311,7 @@ main (int argc, char **argv) sc_context_t *ctx = NULL; sc_card_t *card = NULL; - sc_reader_t *reader; + sc_context_param_t ctx_param; int r, tr_version = EAC_TR_VERSION_2_02; struct establish_pace_channel_input pace_input; @@ -371,7 +323,7 @@ main (int argc, char **argv) unsigned char *certs_chat = NULL; unsigned char *dg = NULL; size_t dg_len = 0; - ASN1_AUXILIARY_DATA *templates = NULL; + ASN1_AUXILIARY_DATA_NPA_TOOL *templates = NULL; unsigned char *ef_cardsecurity = NULL; size_t ef_cardsecurity_len = 0; @@ -390,12 +342,13 @@ main (int argc, char **argv) pin = getenv("PIN"); puk = getenv("PUK"); newpin = getenv("NEWPIN"); + } else { + can = cmdline.can_arg; + mrz = cmdline.mrz_arg; + pin = cmdline.pin_arg; + puk = cmdline.puk_arg; + newpin = cmdline.new_pin_arg; } - can = cmdline.can_arg; - mrz = cmdline.mrz_arg; - pin = cmdline.pin_arg; - puk = cmdline.puk_arg; - newpin = cmdline.new_pin_arg; if (cmdline.chat_given) { pace_input.chat = chat; pace_input.chat_length = sizeof chat; @@ -417,31 +370,38 @@ main (int argc, char **argv) } if (cmdline.tr_03110v201_flag) tr_version = EAC_TR_VERSION_2_01; - if (cmdline.disable_all_checks_flag) - npa_default_flags |= NPA_FLAG_DISABLE_CHECK_ALL; if (cmdline.disable_ta_checks_flag) - npa_default_flags |= NPA_FLAG_DISABLE_CHECK_TA; + eac_default_flags |= EAC_FLAG_DISABLE_CHECK_TA; if (cmdline.disable_ca_checks_flag) - npa_default_flags |= NPA_FLAG_DISABLE_CHECK_CA; + eac_default_flags |= EAC_FLAG_DISABLE_CHECK_CA; - r = initialize(cmdline.reader_arg, cmdline.verbose_given, &ctx, &reader); - if (r < 0) { - fprintf(stderr, "Can't initialize reader\n"); - exit(1); - } + memset(&ctx_param, 0, sizeof(ctx_param)); + ctx_param.ver = 0; + ctx_param.app_name = app_name; + ctx_param.debug = cmdline.verbose_given; + if (cmdline.verbose_given) + ctx_param.debug_file = stderr; - if (sc_connect_card(reader, &card) < 0) { - fprintf(stderr, "Could not connect to card\n"); - sc_release_context(ctx); + r = sc_context_create(&ctx, &ctx_param); + if (r) { + fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); exit(1); } + r = sc_set_card_driver(ctx, "default"); + if (r) + goto err; + + r = util_connect_card_ex(ctx, &card, cmdline.reader_arg, 0, 0); + if (r) + goto err; + EAC_init(); if (cmdline.cvc_dir_given) EAC_set_cvc_default_dir(cmdline.cvc_dir_arg); if (cmdline.x509_dir_given) - EAC_set_x509_default_dir(cmdline.cvc_dir_arg); + EAC_set_x509_default_dir(cmdline.x509_dir_arg); if (cmdline.break_flag) { /* The biggest number sprintf could write with "%llu is 18446744073709551615 */ @@ -456,12 +416,12 @@ main (int argc, char **argv) if (pin) { if (sscanf(pin, "%llu", &secret) != 1) { fprintf(stderr, "%s is not an unsigned long long.\n", - npa_secret_name(pace_input.pin_id)); + eac_secret_name(pace_input.pin_id)); exit(2); } if (strlen(pin) > pace_input.pin_length) { fprintf(stderr, "%s too big, only %u digits allowed.\n", - npa_secret_name(pace_input.pin_id), + eac_secret_name(pace_input.pin_id), (unsigned int) pace_input.pin_length); exit(2); } @@ -473,12 +433,12 @@ main (int argc, char **argv) if (can) { if (sscanf(can, "%llu", &secret) != 1) { fprintf(stderr, "%s is not an unsigned long long.\n", - npa_secret_name(pace_input.pin_id)); + eac_secret_name(pace_input.pin_id)); exit(2); } if (strlen(can) > pace_input.pin_length) { fprintf(stderr, "%s too big, only %u digits allowed.\n", - npa_secret_name(pace_input.pin_id), + eac_secret_name(pace_input.pin_id), (unsigned int) pace_input.pin_length); exit(2); } @@ -490,12 +450,12 @@ main (int argc, char **argv) if (puk) { if (sscanf(puk, "%llu", &secret) != 1) { fprintf(stderr, "%s is not an unsigned long long.\n", - npa_secret_name(pace_input.pin_id)); + eac_secret_name(pace_input.pin_id)); exit(2); } if (strlen(puk) > pace_input.pin_length) { fprintf(stderr, "%s too big, only %u digits allowed.\n", - npa_secret_name(pace_input.pin_id), + eac_secret_name(pace_input.pin_id), (unsigned int) pace_input.pin_length); exit(2); } @@ -514,7 +474,7 @@ main (int argc, char **argv) gettimeofday(&tv, NULL); printf("%u,%06u: Trying %s=%s\n", (unsigned int) tv.tv_sec, (unsigned int) tv.tv_usec, - npa_secret_name(pace_input.pin_id), pace_input.pin); + eac_secret_name(pace_input.pin_id), pace_input.pin); r = perform_pace(card, pace_input, &pace_output, tr_version); @@ -525,12 +485,12 @@ main (int argc, char **argv) if (0 > r) { printf("%u,%06u: Tried breaking %s without success.\n", (unsigned int) tv.tv_sec, (unsigned int) tv.tv_usec, - npa_secret_name(pace_input.pin_id)); + eac_secret_name(pace_input.pin_id)); goto err; } else { printf("%u,%06u: Tried breaking %s with success (=%s).\n", (unsigned int) tv.tv_sec, (unsigned int) tv.tv_usec, - npa_secret_name(pace_input.pin_id), + eac_secret_name(pace_input.pin_id), pace_input.pin); } } @@ -615,9 +575,9 @@ main (int argc, char **argv) exit(1); } - certs = calloc(sizeof *certs, cmdline.cv_certificate_given + 1); - certs_lens = calloc(sizeof *certs_lens, - cmdline.cv_certificate_given + 1); + certs = calloc(cmdline.cv_certificate_given + 1, sizeof *certs); + certs_lens = calloc(cmdline.cv_certificate_given + 1, + sizeof *certs_lens); if (!certs || !certs_lens) { r = SC_ERROR_OUT_OF_MEMORY; goto err; @@ -638,15 +598,15 @@ main (int argc, char **argv) || !cvc_cert->body->certificate_authority_reference || !cvc_cert->body->chat) { fprintf(stderr, "Could not parse certificate.\n"); - ssl_error(ctx); + sc_log_openssl(ctx); r = SC_ERROR_INVALID_DATA; goto err; } pace_input.chat_length = i2d_CVC_CHAT(cvc_cert->body->chat, &certs_chat); if (0 >= (int) pace_input.chat_length) { fprintf(stderr, "Could not parse CHAT.\n"); + sc_log_openssl(ctx); r = SC_ERROR_INVALID_DATA; - ssl_error(ctx); goto err; } pace_input.chat = certs_chat; @@ -669,7 +629,7 @@ main (int argc, char **argv) } } else { if (cmdline.older_than_given) { - r = add_to_ASN1_AUXILIARY_DATA(&templates, + r = add_to_ASN1_AUXILIARY_DATA_NPA_TOOL(&templates, NID_id_DateOfBirth, (unsigned char *) cmdline.older_than_arg, strlen(cmdline.older_than_arg)); @@ -677,7 +637,7 @@ main (int argc, char **argv) goto err; } if (cmdline.verify_validity_given) { - r = add_to_ASN1_AUXILIARY_DATA(&templates, + r = add_to_ASN1_AUXILIARY_DATA_NPA_TOOL(&templates, NID_id_DateOfExpiry, (unsigned char *) cmdline.verify_validity_arg, strlen(cmdline.verify_validity_arg)); @@ -691,7 +651,7 @@ main (int argc, char **argv) fprintf(stderr, "Could not parse community ID.\n"); exit(2); } - r = add_to_ASN1_AUXILIARY_DATA(&templates, + r = add_to_ASN1_AUXILIARY_DATA_NPA_TOOL(&templates, NID_id_CommunityID, community_id, community_id_len); if (r < 0) @@ -699,7 +659,7 @@ main (int argc, char **argv) } if (templates) { unsigned char *p = NULL; - auxiliary_data_len = i2d_ASN1_AUXILIARY_DATA( + auxiliary_data_len = i2d_ASN1_AUXILIARY_DATA_NPA_TOOL( templates, &p); if (0 > (int) auxiliary_data_len || auxiliary_data_len > sizeof auxiliary_data) { @@ -749,13 +709,10 @@ main (int argc, char **argv) if (r < 0) goto err; printf("Established PACE channel with %s.\n", - npa_secret_name(pace_input.pin_id)); + eac_secret_name(pace_input.pin_id)); nopace: if (cmdline.cv_certificate_given || cmdline.private_key_given) { - unsigned char eid_aid[] = { 0xE8, 0x07, 0x04, 0x00, 0x7f, 0x00, 0x07, 0x03, 0x02}; - sc_path_t path; - r = perform_terminal_authentication(card, (const unsigned char **) certs, certs_lens, privkey, privkey_len, auxiliary_data, auxiliary_data_len); @@ -767,67 +724,116 @@ main (int argc, char **argv) if (r < 0) goto err; printf("Performed Chip Authentication.\n"); - - sc_path_set(&path, SC_PATH_TYPE_DF_NAME, eid_aid, sizeof eid_aid, 0, 0); - r = sc_select_file(card, &path, NULL); - if (r < 0) - goto err; - printf("Selected eID application.\n"); } - if (cmdline.read_dg1_flag) - read_dg(card, 1, "Document Type", &dg, &dg_len); - if (cmdline.read_dg2_flag) - read_dg(card, 2, "Issuing State", &dg, &dg_len); - if (cmdline.read_dg3_flag) - read_dg(card, 3, "Date of Expiry", &dg, &dg_len); - if (cmdline.read_dg4_flag) - read_dg(card, 4, "Given Names", &dg, &dg_len); - if (cmdline.read_dg5_flag) - read_dg(card, 5, "Family Names", &dg, &dg_len); - if (cmdline.read_dg6_flag) - read_dg(card, 6, "Religious/Artistic Name", &dg, &dg_len); - if (cmdline.read_dg7_flag) - read_dg(card, 7, "Academic Title", &dg, &dg_len); - if (cmdline.read_dg8_flag) - read_dg(card, 8, "Date of Birth", &dg, &dg_len); - if (cmdline.read_dg9_flag) - read_dg(card, 9, "Place of Birth", &dg, &dg_len); - if (cmdline.read_dg10_flag) - read_dg(card, 10, "Nationality", &dg, &dg_len); - if (cmdline.read_dg11_flag) - read_dg(card, 11, "Sex", &dg, &dg_len); - if (cmdline.read_dg12_flag) - read_dg(card, 12, "Optional Data", &dg, &dg_len); - if (cmdline.read_dg13_flag) - read_dg(card, 13, "Birth Name", &dg, &dg_len); - if (cmdline.read_dg14_flag) - read_dg(card, 14, "DG 14", &dg, &dg_len); - if (cmdline.read_dg15_flag) - read_dg(card, 15, "DG 15", &dg, &dg_len); - if (cmdline.read_dg16_flag) - read_dg(card, 16, "DG 16", &dg, &dg_len); - if (cmdline.read_dg17_flag) - read_dg(card, 17, "Normal Place of Residence", &dg, &dg_len); - if (cmdline.read_dg18_flag) - read_dg(card, 18, "Community ID", &dg, &dg_len); - if (cmdline.read_dg19_flag) - read_dg(card, 19, "Residence Permit I", &dg, &dg_len); - if (cmdline.read_dg20_flag) - read_dg(card, 20, "Residence Permit II", &dg, &dg_len); - if (cmdline.read_dg21_flag) - read_dg(card, 21, "Optional Data", &dg, &dg_len); - - if (cmdline.write_dg17_given) - write_dg(card, 17, "Normal Place of Residence", cmdline.write_dg17_arg); - if (cmdline.write_dg18_given) - write_dg(card, 18, "Community ID", cmdline.write_dg18_arg); - if (cmdline.write_dg19_given) - write_dg(card, 19, "Residence Permit I", cmdline.write_dg19_arg); - if (cmdline.write_dg20_given) - write_dg(card, 20, "Residence Permit II", cmdline.write_dg20_arg); - if (cmdline.write_dg21_given) - write_dg(card, 21, "Optional Data", cmdline.write_dg21_arg); + const unsigned char eid_aid[] = { 0xE8, 0x07, 0x04, 0x00, 0x7f, 0x00, 0x07, 0x03, 0x02}; + const unsigned char emrtd_aid[] = { 0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01}; + sc_path_t path; + switch (cmdline.application_arg) { + case application_arg_eMRTD: + sc_path_set(&path, SC_PATH_TYPE_DF_NAME, emrtd_aid, sizeof emrtd_aid, 0, 0); + r = sc_select_file(card, &path, NULL); + if (r < 0) + goto err; + printf("Selected eMRTD application.\n"); + + if (cmdline.read_dg1_flag || cmdline.read_all_dgs_flag) + read_dg(card, 1, "Machine Readable Zone Information", &dg, &dg_len); + if (cmdline.read_dg2_flag || cmdline.read_all_dgs_flag) + read_dg(card, 2, "Encoded Identification Features - Face", &dg, &dg_len); + if (cmdline.read_dg3_flag || cmdline.read_all_dgs_flag) + read_dg(card, 3, "Additional Identification Feature - Finger(s)", &dg, &dg_len); + if (cmdline.read_dg4_flag || cmdline.read_all_dgs_flag) + read_dg(card, 4, "Additional Identification Feature - Iris(es)", &dg, &dg_len); + if (cmdline.read_dg5_flag || cmdline.read_all_dgs_flag) + read_dg(card, 5, "Iris(es)", &dg, &dg_len); + if (cmdline.read_dg6_flag || cmdline.read_all_dgs_flag) + read_dg(card, 6, "Reserved For Future Use", &dg, &dg_len); + if (cmdline.read_dg7_flag || cmdline.read_all_dgs_flag) + read_dg(card, 7, "Displayed Signature or Usual Mark", &dg, &dg_len); + if (cmdline.read_dg8_flag || cmdline.read_all_dgs_flag) + read_dg(card, 8, "Data Feature(s)", &dg, &dg_len); + if (cmdline.read_dg9_flag || cmdline.read_all_dgs_flag) + read_dg(card, 9, "Structure Feature(s)", &dg, &dg_len); + if (cmdline.read_dg10_flag || cmdline.read_all_dgs_flag) + read_dg(card, 10, "Substance Feature(s)", &dg, &dg_len); + if (cmdline.read_dg11_flag || cmdline.read_all_dgs_flag) + read_dg(card, 11, "Additional Personal Detail(s)", &dg, &dg_len); + if (cmdline.read_dg12_flag || cmdline.read_all_dgs_flag) + read_dg(card, 12, "Additional Document Detail(s)", &dg, &dg_len); + if (cmdline.read_dg13_flag || cmdline.read_all_dgs_flag) + read_dg(card, 13, "Optional Details(s)", &dg, &dg_len); + if (cmdline.read_dg14_flag || cmdline.read_all_dgs_flag) + read_dg(card, 14, "Security Options", &dg, &dg_len); + if (cmdline.read_dg15_flag || cmdline.read_all_dgs_flag) + read_dg(card, 15, "Active Authentication Public Key Info", &dg, &dg_len); + if (cmdline.read_dg16_flag || cmdline.read_all_dgs_flag) + read_dg(card, 16, "Person(s) to Notify", &dg, &dg_len); + break; + + case application_arg_eID: + default: + sc_path_set(&path, SC_PATH_TYPE_DF_NAME, eid_aid, sizeof eid_aid, 0, 0); + r = sc_select_file(card, &path, NULL); + if (r < 0) + goto err; + printf("Selected eMRTD application.\n"); + + if (cmdline.read_dg1_flag || cmdline.read_all_dgs_flag) + read_dg(card, 1, "Document Type", &dg, &dg_len); + if (cmdline.read_dg2_flag || cmdline.read_all_dgs_flag) + read_dg(card, 2, "Issuing State", &dg, &dg_len); + if (cmdline.read_dg3_flag || cmdline.read_all_dgs_flag) + read_dg(card, 3, "Date of Expiry", &dg, &dg_len); + if (cmdline.read_dg4_flag || cmdline.read_all_dgs_flag) + read_dg(card, 4, "Given Names", &dg, &dg_len); + if (cmdline.read_dg5_flag || cmdline.read_all_dgs_flag) + read_dg(card, 5, "Family Names", &dg, &dg_len); + if (cmdline.read_dg6_flag || cmdline.read_all_dgs_flag) + read_dg(card, 6, "Religious/Artistic Name", &dg, &dg_len); + if (cmdline.read_dg7_flag || cmdline.read_all_dgs_flag) + read_dg(card, 7, "Academic Title", &dg, &dg_len); + if (cmdline.read_dg8_flag || cmdline.read_all_dgs_flag) + read_dg(card, 8, "Date of Birth", &dg, &dg_len); + if (cmdline.read_dg9_flag || cmdline.read_all_dgs_flag) + read_dg(card, 9, "Place of Birth", &dg, &dg_len); + if (cmdline.read_dg10_flag || cmdline.read_all_dgs_flag) + read_dg(card, 10, "Nationality", &dg, &dg_len); + if (cmdline.read_dg11_flag || cmdline.read_all_dgs_flag) + read_dg(card, 11, "Sex", &dg, &dg_len); + if (cmdline.read_dg12_flag || cmdline.read_all_dgs_flag) + read_dg(card, 12, "Optional Data", &dg, &dg_len); + if (cmdline.read_dg13_flag || cmdline.read_all_dgs_flag) + read_dg(card, 13, "Birth Name", &dg, &dg_len); + if (cmdline.read_dg14_flag || cmdline.read_all_dgs_flag) + read_dg(card, 14, "DG 14", &dg, &dg_len); + if (cmdline.read_dg15_flag || cmdline.read_all_dgs_flag) + read_dg(card, 15, "DG 15", &dg, &dg_len); + if (cmdline.read_dg16_flag || cmdline.read_all_dgs_flag) + read_dg(card, 16, "DG 16", &dg, &dg_len); + if (cmdline.read_dg17_flag || cmdline.read_all_dgs_flag) + read_dg(card, 17, "Normal Place of Residence", &dg, &dg_len); + if (cmdline.read_dg18_flag || cmdline.read_all_dgs_flag) + read_dg(card, 18, "Community ID", &dg, &dg_len); + if (cmdline.read_dg19_flag || cmdline.read_all_dgs_flag) + read_dg(card, 19, "Residence Permit I", &dg, &dg_len); + if (cmdline.read_dg20_flag || cmdline.read_all_dgs_flag) + read_dg(card, 20, "Residence Permit II", &dg, &dg_len); + if (cmdline.read_dg21_flag || cmdline.read_all_dgs_flag) + read_dg(card, 21, "Optional Data", &dg, &dg_len); + + if (cmdline.write_dg17_given) + write_dg(card, 17, "Normal Place of Residence", cmdline.write_dg17_arg); + if (cmdline.write_dg18_given) + write_dg(card, 18, "Community ID", cmdline.write_dg18_arg); + if (cmdline.write_dg19_given) + write_dg(card, 19, "Residence Permit I", cmdline.write_dg19_arg); + if (cmdline.write_dg20_given) + write_dg(card, 20, "Residence Permit II", cmdline.write_dg20_arg); + if (cmdline.write_dg21_given) + write_dg(card, 21, "Optional Data", cmdline.write_dg21_arg); + break; + } if (cmdline.older_than_given) { unsigned char id_DateOfBirth[] = {6, 9, 4, 0, 127, 0, 7, 3, 1, 4, 1}; @@ -890,7 +896,7 @@ main (int argc, char **argv) free(privkey); free(dg); if (templates) - ASN1_AUXILIARY_DATA_free(templates); + ASN1_AUXILIARY_DATA_NPA_TOOL_free(templates); sc_sm_stop(card); sc_reset(card, 1); @@ -903,10 +909,4 @@ main (int argc, char **argv) return -r; } -#else -int -main (int argc, char **argv) -{ - return 1; -} -#endif + diff --git a/src/tools/npa-tool.ggo.in b/src/tools/npa-tool.ggo.in index 9652c89620..8c0cddada0 100644 --- a/src/tools/npa-tool.ggo.in +++ b/src/tools/npa-tool.ggo.in @@ -2,9 +2,8 @@ package "npa-tool" purpose "@PACKAGE_SUMMARY@" option "reader" r - "Number of the PC/SC reader to use (-1 for autodetect)" - int - default="-1" + "Number of the reader to use. By default, the first reader with a present card is used. If the argument is an ATR, the reader with a matching card will be chosen." + string optional option "verbose" v "Use (several times) to be more verbose" @@ -89,98 +88,108 @@ option "x509-dir" - default="@X509DIR@" optional option "disable-ta-checks" - - "Disable checking the validity period of CV certifcates" + "Disable checking the validity period of CV certificates" flag off option "disable-ca-checks" - "Disable passive authentication" flag off +section "Card application" +option "application" - + "What card application to select" + values="eID","eMRTD" default="eID" + enum + optional + section "Read and write data groups" +option "read-all-dgs" - + "Read all available data groups" + flag off option "read-dg1" - - "Read DG 1 (Document Type)" + "Read data group 1" flag off option "read-dg2" - - "Read DG 2 (Issuing State)" + "Read data group 2" flag off option "read-dg3" - - "Read DG 3 (Date of Expiry)" + "Read data group 3" flag off option "read-dg4" - - "Read DG 4 (Given Names)" + "Read data group 4" flag off option "read-dg5" - - "Read DG 5 (Family Names)" + "Read data group 5" flag off option "read-dg6" - - "Read DG 6 (Religious/Artistic Name)" + "Read data group 6" flag off option "read-dg7" - - "Read DG 7 (Academic Title)" + "Read data group 7" flag off option "read-dg8" - - "Read DG 8 (Date of Birth)" + "Read data group 8" flag off option "read-dg9" - - "Read DG 9 (Place of Birth)" + "Read data group 9" flag off option "read-dg10" - - "Read DG 10 (Nationality)" + "Read data group 10" flag off option "read-dg11" - - "Read DG 11 (Sex)" + "Read data group 11" flag off option "read-dg12" - - "Read DG 12 (Optional Data)" + "Read data group 12" flag off option "read-dg13" - - "Read DG 13 (Birth Name)" + "Read data group 13" flag off option "read-dg14" - - "Read DG 14" + "Read data group 14" flag off option "read-dg15" - - "Read DG 15" + "Read data group 15" flag off option "read-dg16" - - "Read DG 16" + "Read data group 16" flag off option "read-dg17" - - "Read DG 17 (Normal Place of Residence)" + "Read data group 17" flag off option "read-dg18" - - "Read DG 18 (Community ID)" + "Read data group 18" flag off option "read-dg19" - - "Read DG 19 (Residence Permit I)" + "Read data group 19" flag off option "read-dg20" - - "Read DG 20 (Residence Permit II)" + "Read data group 20" flag off option "read-dg21" - - "Read DG 21 (Optional Data)" + "Read data group 21" flag off option "write-dg17" - - "Write DG 17 (Normal Place of Residence)" + "Write data group 17" string typestr="HEX_STRING" optional option "write-dg18" - - "Write DG 18 (Community ID)" + "Write data group 18" string typestr="HEX_STRING" optional option "write-dg19" - - "Write DG 19 (Residence Permit I)" + "Write data group 19" string typestr="HEX_STRING" optional option "write-dg20" - - "Write DG 20 (Residence Permit II)" + "Write data group 20" string typestr="HEX_STRING" optional option "write-dg21" - - "Write DG 21 (Optional Data)" + "Write data group 21" string typestr="HEX_STRING" optional @@ -215,9 +224,6 @@ option "translate" t option "tr-03110v201" - "Force compliance to BSI TR-03110 version 2.01" flag off -option "disable-all-checks" - - "Disable all checking of fly-by-data" - flag off text " Report bugs to @PACKAGE_BUGREPORT@ diff --git a/src/tools/openpgp-tool-helpers.c b/src/tools/openpgp-tool-helpers.c new file mode 100644 index 0000000000..35c2ef9e97 --- /dev/null +++ b/src/tools/openpgp-tool-helpers.c @@ -0,0 +1,228 @@ +/* + * openpgp-tool-helpers.c: OpenPGP card utility + * + * Copyright (C) 2012-2020 Peter Marschall + * + * 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 + */ + +#include "config.h" + +#include +#include +#include "common/compat_strnlen.h" +#include "openpgp-tool-helpers.h" +#include "util.h" + + +/* prettify hex */ +char *prettify_hex(const u8 *data, size_t length, char *buffer, size_t buflen) +{ + if (data != NULL) { + int r = sc_bin_to_hex(data, length, buffer, buflen, ':'); + + if (r == SC_SUCCESS) + return buffer; + } + return NULL; +} + + +/* prettify algorithm parameters */ +char *prettify_algorithm(const u8 *data, size_t length) +{ + if (data != NULL && length >= 1) { + static char result[64]; /* large enough */ + + if (data[0] == 0x01 && length >= 5) { /* RSA */ + unsigned short modulus = (data[1] << 8) + data[2]; + snprintf(result, sizeof(result), "RSA%u", modulus); + return result; + } + else if (data[0] == 0x12) { /* ECDH */ + strcpy(result, "ECDH"); + return result; + } + else if (data[0] == 0x13) { /* ECDSA */ + strcpy(result, "ECDSA"); + return result; + } + else if (data[0] == 0x16) { /* EDDSA */ + strcpy(result, "EDDSA"); + return result; + } + } + return NULL; +} + + +/* prettify date/time */ +char *prettify_date(const u8 *data, size_t length) +{ + if (data != NULL && length == 4) { + time_t time = (time_t) (data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]); + struct tm tm; + static char result[64]; /* large enough */ + +#ifdef _WIN32 + if (0 != gmtime_s(&tm, &time)) + return NULL; +#else + if (NULL == gmtime_r(&time, &tm)) + return NULL; +#endif + strftime(result, sizeof(result), "%Y-%m-%d %H:%M:%S", &tm); + return result; + } + return NULL; +} + + +#define BCD2CHAR(x) (((((x) & 0xF0) >> 4) * 10) + ((x) & 0x0F)) + +/* prettify OpenPGP card version */ +char *prettify_version(const u8 *data, size_t length) +{ + if (data != NULL && length >= 2) { + static char result[10]; /* large enough for even 2*3 digits + separator */ + int major = BCD2CHAR(data[0]); + int minor = BCD2CHAR(data[1]); + + sprintf(result, "%d.%d", major, minor); + return result; + } + return NULL; +} + + +/* prettify manufacturer */ +char *prettify_manufacturer(const u8 *data, size_t length) +{ + if (data != NULL && length >= 2) { + unsigned int manuf = (data[0] << 8) + data[1]; + + switch (manuf) { + case 0x0001: return "PPC Card Systems"; + case 0x0002: return "Prism"; + case 0x0003: return "OpenFortress"; + case 0x0004: return "Wewid"; + case 0x0005: return "ZeitControl"; + case 0x0006: return "Yubico"; + case 0x0007: return "OpenKMS"; + case 0x0008: return "LogoEmail"; + case 0x0009: return "Fidesmo"; + case 0x000A: return "Dangerous Things"; + case 0x000B: return "Feitian Technologies"; + + case 0x002A: return "Magrathea"; + case 0x0042: return "GnuPG e.V."; + + case 0x1337: return "Warsaw Hackerspace"; + case 0x2342: return "warpzone"; /* hackerspace Muenster. */ + case 0x4354: return "Confidential Technologies"; /* cotech.de */ + case 0x5443: return "TIF-IT e.V."; + case 0x63AF: return "Trustica"; + case 0xBA53: return "c-base e.V."; + case 0xBD0E: return "Paranoidlabs"; + case 0xF517: return "FSIJ"; + case 0xF5EC: return "F-Secure"; + + /* 0x0000 and 0xFFFF are defined as test cards per spec, + 0xFF00 to 0xFFFE are assigned for use with randomly created + serial numbers. */ + case 0x0000: + case 0xffff: return "test card"; + default: return (manuf & 0xff00) == 0xff00 ? "unmanaged S/N range" : "unknown"; + } + } + return NULL; +} + + +/* prettify pure serial number */ +char *prettify_serialnumber(const u8 *data, size_t length) +{ + if (data != NULL && length >= 4) { + static char result[15]; /* large enough for even 2*3 digits + separator */ + sprintf(result, "%02X%02X%02X%02X", data[0], data[1], data[2], data[3]); + return result; + } + return NULL; +} + + +/* prettify card holder's name */ +char *prettify_name(const u8 *data, size_t length) +{ + if (data != NULL && length > 0) { + static char result[100]; /* should be large enough */ + char *src = (char *) data; + char *dst = result; + if (length > sizeof(result) - 1) + length = sizeof(result) - 1; + + while (*src != '\0' && length > 0) { + *dst = *src++; + length--; + if (*dst == '<') { + if (length > 0 && *src == '<') { + src++; + length--; + } + *dst = ' '; + } + dst++; + } + *dst = '\0'; + return result; + } + return NULL; +} + + +/* prettify language */ +char *prettify_language(const u8 *data, size_t length) +{ + if (data != NULL && length > 0) { + static char result[12]; /* 8 chars, 3 separators, 1 null */ + char *src = (char *) data; + size_t used_length = strnlen(src, length) >> 1; + int i = 0; + + while (used_length) { + used_length--; + result[i++] = *src++; + result[i++] = *src++; + result[i++] = used_length ? ',' : '\0'; + } + return result; + } + return NULL; +} + + +/* convert the raw ISO-5218 SEX value to an english word */ +char *prettify_gender(const u8 *data, size_t length) +{ + if (data != NULL && length > 0) { + switch (*data) { + case '0': return "unknown"; + case '1': return "male"; + case '2': return "female"; + case '9': return "not announced"; + } + } + return NULL; +} diff --git a/src/tools/openpgp-tool-helpers.h b/src/tools/openpgp-tool-helpers.h new file mode 100644 index 0000000000..d2986ef0b1 --- /dev/null +++ b/src/tools/openpgp-tool-helpers.h @@ -0,0 +1,38 @@ +/* + * openpgp-tool-helpers.h: OpenPGP card utility + * + * Copyright (C) 2012-2020 Peter Marschall + * + * 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 + */ + +#ifndef OPENPGP_TOOL_HELPERS_H +#define OPENPGP_TOOL_HELPERS_H + +#include "util.h" + + +char *prettify_hex(const u8 *data, size_t length, char *buffer, size_t buflen); +char *prettify_algorithm(const u8 *data, size_t length); +char *prettify_date(const u8 *data, size_t length); +char *prettify_version(const u8 *data, size_t length); +char *prettify_manufacturer(const u8 *data, size_t length); +char *prettify_serialnumber(const u8 *data, size_t length); +char *prettify_name(const u8 *data, size_t length); +char *prettify_language(const u8 *data, size_t length); +char *prettify_gender(const u8 *data, size_t length); + + +#endif /* OPENPGP_TOOL_HELPERS_H */ diff --git a/src/tools/openpgp-tool.c b/src/tools/openpgp-tool.c index e1fa256530..3a694d69af 100644 --- a/src/tools/openpgp-tool.c +++ b/src/tools/openpgp-tool.c @@ -1,7 +1,7 @@ /* * openpgp-tool.c: OpenPGP card utility * - * Copyright (C) 2012 Peter Marschall + * Copyright (C) 2012-2020 Peter Marschall * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -35,16 +35,24 @@ #endif #include #include +#include -#include "common/compat_getopt.h" +#include + +#include "common/compat_strlcat.h" +#include "common/compat_strlcpy.h" #include "libopensc/opensc.h" #include "libopensc/asn1.h" #include "libopensc/cards.h" +#include "libopensc/internal.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "libopensc/errors.h" #include "util.h" #include "libopensc/log.h" +#include "libopensc/card-openpgp.h" + +#include "openpgp-tool-helpers.h" #define OPT_RAW 256 #define OPT_PRETTY 257 @@ -52,22 +60,28 @@ #define OPT_PIN 259 #define OPT_DELKEY 260 +enum code_types { + TYPE_NULL, + TYPE_HEX, + TYPE_STRING +}; + /* define structures */ struct ef_name_map { const char *name; const char *env_name; const char *ef; - char *(*prettify_value)(char *); + enum code_types type; + size_t offset; + size_t length; /* exact length: 0 <=> potentially infinite */ + char *(*prettify_value)(const u8 *, size_t); }; /* declare functions */ static void show_version(void); -static char *prettify_name(char *str); -static char *prettify_language(char *str); -static char *prettify_gender(char *str); -static void display_data(const struct ef_name_map *mapping, char *value); +static void display_data(const struct ef_name_map *mapping, u8 *data, size_t length); static int decode_options(int argc, char **argv); -static int do_userinfo(sc_card_t *card); +static int do_info(sc_card_t *card, const struct ef_name_map *map); /* define global variables */ static int actions = 0; @@ -77,19 +91,19 @@ static int opt_raw = 0; static int verbose = 0; static int opt_userinfo = 0; static int opt_cardinfo = 0; +static int opt_keyinfo = 0; static char *exec_program = NULL; static int opt_genkey = 0; -static int opt_keylen = 0; static u8 key_id = 0; -static unsigned int key_len = 2048; +static char *opt_keytype = NULL; static int opt_verify = 0; static char *verifytype = NULL; static int opt_pin = 0; static const char *pin = NULL; static int opt_erase = 0; static int opt_delkey = 0; -static int opt_dump_do = 0; -static u8 do_dump_idx; +static size_t opt_dump_do = 0; +static unsigned int do_dump_idx[200]; /* large enough and checked on input */ static const char *app_name = "openpgp-tool"; @@ -101,8 +115,9 @@ static const struct option options[] = { { "pretty", no_argument, NULL, OPT_PRETTY }, { "card-info", no_argument, NULL, 'C' }, { "user-info", no_argument, NULL, 'U' }, + { "key-info", no_argument, NULL, 'K' }, { "gen-key", required_argument, NULL, 'G' }, - { "key-length",required_argument, NULL, 'L' }, + { "key-type", required_argument, NULL, 't' }, { "help", no_argument, NULL, 'h' }, { "verbose", no_argument, NULL, 'v' }, { "version", no_argument, NULL, 'V' }, @@ -120,31 +135,54 @@ static const char *option_help[] = { /* x */ "Execute program with data in env vars", "Print values in raw format", "Print values in pretty format", -/* C */ NULL, +/* C */ "Show card information", /* U */ "Show card holder information", +/* K */ "Show key information", /* G */ "Generate key", -/* L */ "Key length (default 2048)", +/* t */ "Key type (default: rsa2048)", /* h */ "Print this help message", -/* v */ "Verbose operation. Use several times to enable debug output.", +/* v */ "Verbose operation, may be used several times", /* V */ "Show version number", /* E */ "Erase (reset) the card", "Verify PIN (CHV1, CHV2, CHV3...)", "PIN string", "Delete key (1, 2, 3 or all)", -/* d */ "Dump private data object number (i.e. PRIVATE-DO-)", +/* d */ "Dump private data object number (i.e. DO )", +}; + + +static const struct ef_name_map card_data[] = { + { "AID", "OPENPGP_AID", "3F00:004F", TYPE_HEX, 0, 16, NULL }, + { "Version", "OPENPGP_VERSION", "3F00:004F", TYPE_HEX, 6, 2, prettify_version }, + { "Manufacturer", "OPENPGP_MANUFACTURER", "3F00:004F", TYPE_HEX, 8, 2, prettify_manufacturer }, + { "Serial number", "OPENPGP_SERIALNO", "3F00:004F", TYPE_HEX, 10, 4, prettify_serialnumber }, + { NULL, NULL, NULL, TYPE_NULL, 0, 0, NULL } +}; + +static const struct ef_name_map key_data[] = { + { "Aut Algorithm", "OPENPGP_AUT_ALGORITHM", "3F00:006E:0073:00C3", TYPE_HEX, 0, 0, prettify_algorithm }, + { "Aut Create Date", "OPENPGP_AUT_DATE", "3F00:006E:0073:00CD", TYPE_HEX, 8, 4, prettify_date }, + { "Aut Fingerprint", "OPENPGP_AUT_FP", "3F00:006E:0073:00C5", TYPE_HEX, 40, 20, NULL }, + { "Dec Algorithm", "OPENPGP_DEC_ALGORITHM", "3F00:006E:0073:00C2", TYPE_HEX, 0, 0, prettify_algorithm }, + { "Dec Create Date", "OPENPGP_DEC_DATE", "3F00:006E:0073:00CD", TYPE_HEX, 4, 4, prettify_date }, + { "Dec Fingerprint", "OPENPGP_DEC_FP", "3F00:006E:0073:00C5", TYPE_HEX, 20, 20, NULL }, + { "Sig Algorithm", "OPENPGP_SIG_ALGORITHM", "3F00:006E:0073:00C1", TYPE_HEX, 0, 0, prettify_algorithm }, + { "Sig Create Date", "OPENPGP_SIG_DATE", "3F00:006E:0073:00CD", TYPE_HEX, 0, 4, prettify_date }, + { "Sig Fingerprint", "OPENPGP_SIG_FP", "3F00:006E:0073:00C5", TYPE_HEX, 0, 20, NULL }, + { NULL, NULL, NULL, TYPE_NULL, 0, 0, NULL } }; -static const struct ef_name_map openpgp_data[] = { - { "Account", "OPENGPG_ACCOUNT", "3F00:005E", NULL }, - { "URL", "OPENPGP_URL", "3F00:5F50", NULL }, - { "Name", "OPENPGP_NAME", "3F00:0065:005B", prettify_name }, - { "Language", "OPENPGP_LANG", "3F00:0065:5F2D", prettify_language }, - { "Gender", "OPENPGP_GENDER", "3F00:0065:5F35", prettify_gender }, - { "DO 0101", "OPENPGP_DO0101", "3F00:0101", NULL }, - { "DO 0102", "OPENPGP_DO0102", "3F00:0102", NULL }, -// { "DO 0103", "OPENPGP_DO0103", "3F00:0103", NULL }, -// { "DO 0104", "OPENPGP_DO0104", "3F00:0104", NULL }, - { NULL, NULL, NULL, NULL } +static const struct ef_name_map user_data[] = { + { "Account", "OPENPGP_ACCOUNT", "3F00:005E", TYPE_STRING, 0, 0, NULL }, + { "URL", "OPENPGP_URL", "3F00:5F50", TYPE_STRING, 0, 0, NULL }, + { "Name", "OPENPGP_NAME", "3F00:0065:005B", TYPE_STRING, 0, 0, prettify_name }, + { "Language", "OPENPGP_LANG", "3F00:0065:5F2D", TYPE_STRING, 0, 0, prettify_language }, + { "Gender", "OPENPGP_GENDER", "3F00:0065:5F35", TYPE_STRING, 0, 0, prettify_gender }, + { "DO 0101", "OPENPGP_DO0101", "3F00:0101", TYPE_STRING, 0, 0, NULL }, + { "DO 0102", "OPENPGP_DO0102", "3F00:0102", TYPE_STRING, 0, 0, NULL }, +// { "DO 0103", "OPENPGP_DO0103", "3F00:0103", TYPE_STRING, 0, 0, NULL }, +// { "DO 0104", "OPENPGP_DO0104", "3F00:0104", TYPE_STRING, 0, 0, NULL }, + { NULL, NULL, NULL, TYPE_NULL, 0, 0, NULL } }; @@ -153,91 +191,68 @@ static void show_version(void) fprintf(stderr, "openpgp-tool - OpenPGP card utility version " PACKAGE_VERSION "\n" "\n" - "Copyright (c) 2012 Peter Marschall \n" + "Copyright (c) 2012-2020 Peter Marschall \n" "Licensed under LGPL v2\n"); } -/* prettify card holder's name */ -static char *prettify_name(char *str) -{ - if (str != NULL) { - char *src = str; - char *dst = str; - - while (*src != '\0') { - *dst = *src++; - if (*dst == '<') { - if (*src == '<') - src++; - *dst = ' '; - } - dst++; - } - *dst = '\0'; - } - return str; -} - +#define INDENT 16 -/* prettify language */ -static char *prettify_language(char *str) +static void display_data(const struct ef_name_map *map, u8 *data, size_t length) { - if (str != NULL) { - switch (strlen(str)) { - case 8: memmove(str+7, str+6, 1+strlen(str+6)); - str[6] = ','; - /* fall through */ - case 6: memmove(str+5, str+4, 1+strlen(str+4)); - str[4] = ','; - /* fall through */ - case 4: memmove(str+3, str+2, 1+strlen(str+2)); - str[2] = ','; - /* fall through */ - case 2: return str; + if (map != NULL && data != NULL) { + char buffer[8192]; + char *value = NULL; + + if (opt_raw) { + /* length-wise safe, but may cut off data (safe for OpenPGP cards 2.x) */ + if (length > sizeof(buffer)) + length = sizeof(buffer); + + if (map->type == TYPE_HEX) { + if (exec_program) { + value = prettify_hex(data, length, buffer, sizeof(buffer)); + } + else { + sc_hex_dump(data, length, buffer, sizeof(buffer)); + /* remove trailing newline */ + if (*buffer != '\0' && buffer[strlen(buffer)-1] == '\n') + buffer[strlen(buffer)-1] = '\0'; + value = buffer; + } + } + else { + value = (char *) data; + } } - } - return NULL; -} - - -/* convert the raw ISO-5218 SEX value to an english word */ -static char *prettify_gender(char *str) -{ - if (str != NULL) { - switch (*str) { - case '0': return "unknown"; - case '1': return "male"; - case '2': return "female"; - case '9': return "not announced"; + else { + if (map->prettify_value != NULL) + value = map->prettify_value(data, length); + else { + value = (map->type == TYPE_HEX) + ? prettify_hex(data, length, buffer, sizeof(buffer)) + : (char *) data; + } } - } - return NULL; -} - - -static void display_data(const struct ef_name_map *mapping, char *value) -{ - if (mapping != NULL && value != NULL) { - if (mapping->prettify_value != NULL && !opt_raw) - value = mapping->prettify_value(value); if (value != NULL) { if (exec_program) { - char *envvar; + size_t envvar_size = strlen(map->env_name) + strlen(value) + 2; + char *envvar = malloc(envvar_size); - envvar = malloc(strlen(mapping->env_name) + - strlen(value) + 2); if (envvar != NULL) { - strcpy(envvar, mapping->env_name); - strcat(envvar, "="); - strcat(envvar, value); + strlcpy(envvar, map->env_name, envvar_size); + strlcat(envvar, "=", envvar_size); + strlcat(envvar, value, envvar_size); putenv(envvar); + /* envvar deliberately kept: see putenv(3) */ } - } else { - const char *label = mapping->name; + } + else { + const char *label = map->name; + int fill = (int) (INDENT - strlen(label)); - printf("%s:%*s%s\n", label, (int)(10-strlen(label)), "", value); + printf("%s:%*s%s\n", label, fill, "", value); } } } @@ -247,8 +262,10 @@ static void display_data(const struct ef_name_map *mapping, char *value) static int decode_options(int argc, char **argv) { int c; + char *endptr; + unsigned long val; - while ((c = getopt_long(argc, argv,"r:x:CUG:L:EhwvVd:", options, (int *) 0)) != EOF) { + while ((c = getopt_long(argc, argv,"r:x:CUG:KEht:wvVd:", options, (int *) 0)) != -1) { switch (c) { case 'r': opt_reader = optarg; @@ -283,15 +300,19 @@ static int decode_options(int argc, char **argv) opt_userinfo++; actions++; break; + case 'K': + opt_keyinfo++; + actions++; + break; case 'G': opt_genkey++; key_id = optarg[0] - '0'; actions++; break; - case 'L': - opt_keylen++; - key_len = atoi(optarg); - actions++; + case 't': + if (opt_keytype) + free(opt_keytype); + opt_keytype = strdup(optarg); break; case 'h': util_print_usage_and_die(app_name, options, option_help, NULL); @@ -316,10 +337,19 @@ static int decode_options(int argc, char **argv) key_id = optarg[0] - '0'; else /* Arg string is 'all' */ key_id = 'a'; + actions++; break; case 'd': - do_dump_idx = optarg[0] - '0'; - opt_dump_do++; + endptr = NULL; + val = strtoul(optarg, &endptr, 16); + if (endptr == NULL || endptr == optarg || *endptr != '\0') { + util_error("Unable to parse DO identifier"); + exit(EXIT_FAILURE); + } + if (opt_dump_do < sizeof(do_dump_idx) / sizeof(*do_dump_idx)) { + do_dump_idx[opt_dump_do] = (unsigned int) (val | 0x100); + opt_dump_do++; + } actions++; break; default: @@ -331,22 +361,21 @@ static int decode_options(int argc, char **argv) } -static int do_userinfo(sc_card_t *card) +static int do_info(sc_card_t *card, const struct ef_name_map *map) { int i; - /* FIXME there are no length checks on buf. */ - unsigned char buf[2048]; + u8 buf[2048]; - for (i = 0; openpgp_data[i].ef != NULL; i++) { + for (i = 0; map[i].ef != NULL; i++) { sc_path_t path; sc_file_t *file; size_t count; int r; - sc_format_path(openpgp_data[i].ef, &path); + sc_format_path(map[i].ef, &path); r = sc_select_file(card, &path, &file); if (r) { - fprintf(stderr, "Failed to select EF %s: %s\n", openpgp_data[i].ef, sc_strerror(r)); + util_error("failed to select EF %s: %s", map[i].ef, sc_strerror(r)); return EXIT_FAILURE; } @@ -355,24 +384,30 @@ static int do_userinfo(sc_card_t *card) continue; if (count > sizeof(buf) - 1) { - fprintf(stderr, "Too small buffer to read the OpenPGP data\n"); + util_error("too small buffer to read the OpenPGP map"); return EXIT_FAILURE; } r = sc_read_binary(card, 0, buf, count, 0); if (r < 0) { - fprintf(stderr, "%s: read failed - %s\n", openpgp_data[i].ef, sc_strerror(r)); + util_error("failed to read %s: %s", map[i].ef, sc_strerror(r)); return EXIT_FAILURE; } - if (r != (signed)count) { - fprintf(stderr, "%s: expecting %"SC_FORMAT_LEN_SIZE_T"d, got only %d bytes\n", - openpgp_data[i].ef, count, r); + if (r != (signed) count || (size_t) r < map[i].offset + map[i].length) { + util_error("%s: expecting %"SC_FORMAT_LEN_SIZE_T"d bytes, got only %d", + map[i].ef, count, r); return EXIT_FAILURE; } + if (map[i].offset > 0) { + memmove(buf, buf + map[i].offset, map[i].length); + count -= map[i].offset; + } + if (map[i].length > 0 && count > map[i].length) + count = map[i].length; + if (map[i].type == TYPE_STRING) + buf[count] = '\0'; - buf[count] = '\0'; - - display_data(openpgp_data + i, (char *) buf); + display_data(&map[i], buf, count); } return EXIT_SUCCESS; @@ -380,99 +415,178 @@ static int do_userinfo(sc_card_t *card) static int do_dump_do(sc_card_t *card, unsigned int tag) { - int r, tmp; - FILE *fp; + struct pgp_priv_data *priv = DRVDATA(card); + int r; + size_t length; + unsigned char *buffer; - // Private DO are specified up to 254 bytes - unsigned char buffer[254]; - memset(buffer, '\0', sizeof(buffer)); + if (tag < 0x101 || tag > 0x104) { + util_error("illegal DO identifier %04X", tag); + return SC_ERROR_INVALID_ARGUMENTS; + } - r = sc_get_data(card, tag, buffer, sizeof(buffer)); + buffer = calloc(priv->max_specialDO_size, sizeof(unsigned char)); + if (buffer == NULL) { + util_error("error allocating memory for DO %04X", tag); + return SC_ERROR_OUT_OF_MEMORY; + } + + r = sc_get_data(card, tag, buffer, priv->max_specialDO_size); if (r < 0) { - printf("Failed to get data object: %s\n", sc_strerror(r)); - if(SC_ERROR_SECURITY_STATUS_NOT_SATISFIED == r) { - printf("Make sure the 'verify' and 'pin' parameters are correct.\n"); + free(buffer); + util_error("failed to get data object DO %04X: %s", tag, sc_strerror(r)); + if (SC_ERROR_SECURITY_STATUS_NOT_SATISFIED == r) { + util_error("make sure the 'verify' and 'pin' parameters are correct"); } return r; } + length = (size_t) r; /* r is guaranteed to be non-negative */ - if(opt_raw) { - r = 0; - #ifndef _WIN32 + if (opt_raw) { + int tmp; + FILE *fp; + +#ifndef _WIN32 tmp = dup(fileno(stdout)); - #else +#else tmp = _dup(_fileno(stdout)); - #endif +#endif if (tmp < 0) return EXIT_FAILURE; fp = freopen(NULL, "wb", stdout); if (fp) { - r = (int)fwrite(buffer, sizeof(char), sizeof(buffer), fp); + r = (int) fwrite(buffer, sizeof(char), length, fp); } - #ifndef _WIN32 +#ifndef _WIN32 dup2(tmp, fileno(stdout)); - #else +#else _dup2(tmp, _fileno(stdout)); - #endif +#endif clearerr(stdout); close(tmp); - if (sizeof(buffer) != r) + + if (length != (size_t) r) { /* fail on write errors */ + free(buffer); return EXIT_FAILURE; + } } else { - util_hex_dump_asc(stdout, buffer, sizeof(buffer), -1); + util_hex_dump_asc(stdout, buffer, length, -1); } + free(buffer); return EXIT_SUCCESS; } -int do_genkey(sc_card_t *card, u8 key_id, unsigned int key_len) +int do_genkey(sc_card_t *card, u8 in_key_id, const char *keytype) { int r; - sc_cardctl_openpgp_keygen_info_t key_info; + sc_cardctl_openpgp_key_gen_store_info_t key_info; u8 fingerprints[60]; sc_path_t path; sc_file_t *file; - if (key_id < 1 || key_id > 3) { - printf("Unknown key ID %d.\n", key_id); - return 1; + /* validate in_key_id */ + if (in_key_id < 1 || in_key_id > 3) { + util_error("unknown key ID %d", in_key_id); + return SC_ERROR_INVALID_ARGUMENTS; } - memset(&key_info, 0, sizeof(sc_cardctl_openpgp_keygen_info_t)); - key_info.keytype = key_id; - key_info.modulus_len = key_len; - key_info.modulus = malloc(key_len/8); - r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_GENERATE_KEY, &key_info); - free(key_info.modulus); - if (r < 0) { - printf("Failed to generate key. Error %s.\n", sc_strerror(r)); - return 1; + + /* fall back to RSA 2048 if keytype is not given */ + if (!keytype) + keytype = "RSA2048"; + + memset(&key_info, 0, sizeof(sc_cardctl_openpgp_key_gen_store_info_t)); + + /* generate key depending on keytype passed */ + if (strncasecmp("RSA", keytype, strlen("RSA")) == 0) { + const char *keylen_ptr = keytype + strlen("RSA"); + size_t keylen = 2048; /* default key length for RSA keys */ + size_t expolen = 32; /* default exponent length for RSA keys */ + u8 keyformat = SC_OPENPGP_KEYFORMAT_RSA_STD; /* default keyformat */ + char pathstr[SC_MAX_PATH_STRING_SIZE]; + + /* try to get key length from keytype, e.g. "rsa3072" -> 3072 */ + if (strlen(keylen_ptr) > 0) { + if (strspn(keylen_ptr, "0123456789") == strlen(keylen_ptr) && + atol(keylen_ptr) >= 1024 && atol(keylen_ptr) <= 4096) { + keylen = atol(keylen_ptr); + } + else { + util_error("illegal key type: %s", keytype); + return EXIT_FAILURE; + } + } + + /* get some algorithm attributes from respective DO - ignore errors */ + snprintf(pathstr, sizeof(pathstr), "006E007300C%d", in_key_id); + sc_format_path(pathstr, &path); + if (sc_select_file(card, &path, &file) >= 0) { + u8 attrs[6]; /* algorithm attrs DO for RSA is <= 6 bytes */ + + sc_file_free(file); + r = sc_read_binary(card, 0, attrs, sizeof(attrs), 0); + if (r >= 5 && attrs[0] == SC_OPENPGP_KEYALGO_RSA) { + expolen = (unsigned short) attrs[3] << 8 + | (unsigned short) attrs[4]; + if (r > 5) + keyformat = attrs[5]; + } + } + + /* set key_info */ + key_info.key_id = in_key_id; + key_info.algorithm = SC_OPENPGP_KEYALGO_RSA; + key_info.u.rsa.modulus_len = keylen; + key_info.u.rsa.modulus = calloc(1, BYTES4BITS(keylen)); + key_info.u.rsa.exponent_len = expolen; + key_info.u.rsa.exponent = calloc(1, BYTES4BITS(expolen)); + key_info.u.rsa.keyformat = keyformat; + + r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_GENERATE_KEY, &key_info); + free(key_info.u.rsa.modulus); + free(key_info.u.rsa.exponent); + if (r < 0) { + util_error("failed to generate key: %s", sc_strerror(r)); + return EXIT_FAILURE; + } + } + else { + //TODO: deal with EC keys + util_error("Generating non-RSA keys is not yet implemented"); + return EXIT_FAILURE; } + sc_format_path("006E007300C5", &path); r = sc_select_file(card, &path, &file); + sc_file_free(file); + if (r < 0) { + util_error("failed to retrieve fingerprints: %s", sc_strerror(r)); + return EXIT_FAILURE; + } r = sc_read_binary(card, 0, fingerprints, 60, 0); if (r < 0) { - printf("Failed to retrieve fingerprints. Error %s.\n", sc_strerror(r)); - return 1; + util_error("failed to retrieve fingerprints: %s", sc_strerror(r)); + return EXIT_FAILURE; } - printf("Fingerprint:\n%s\n", (char *)sc_dump_hex(fingerprints + 20*(key_id - 1), 20)); - return 0; + printf("Fingerprint:\n%s\n", (char *)sc_dump_hex(fingerprints + 20*(in_key_id - 1), 20)); + + return EXIT_SUCCESS; } -int do_verify(sc_card_t *card, char *type, const char *pin) +int do_verify(sc_card_t *card, char *type, const char *in_pin) { struct sc_pin_cmd_data data; - int tries_left; int r; - if (!type || !pin) + if (!type || !in_pin) return SC_ERROR_INVALID_ARGUMENTS; if (strncasecmp("CHV", type, 3) != 0) { - printf("Invalid PIN type. Please use CHV1, CHV2 or CHV3.\n"); + util_error("invalid PIN type. Please use CHV1, CHV2 or CHV3"); return SC_ERROR_INVALID_ARGUMENTS; } if (type[3] < '1' || type[3] > '3' || type[4] != '\0') { - printf("Invalid PIN reference. Please use CHV1, CHV2 or CHV3.\n"); + util_error("invalid PIN reference. Please use CHV1, CHV2 or CHV3"); return SC_ERROR_INVALID_PIN_REFERENCE; } @@ -480,9 +594,9 @@ int do_verify(sc_card_t *card, char *type, const char *pin) data.cmd = SC_PIN_CMD_VERIFY; data.pin_type = SC_AC_CHV; data.pin_reference = type[3] - '0'; - data.pin1.data = (unsigned char *) pin; - data.pin1.len = (int)strlen(pin); - r = sc_pin_cmd(card, &data, &tries_left); + data.pin1.data = (unsigned char *) in_pin; + data.pin1.len = (int)strlen(in_pin); + r = sc_pin_cmd(card, &data); return r; } @@ -490,7 +604,7 @@ int do_verify(sc_card_t *card, char *type, const char *pin) * Delete key, for OpenPGP card. * This function is not complete and is reserved for future version (> 2) of OpenPGP card. **/ -int delete_key_openpgp(sc_card_t *card, u8 key_id) +int delete_key_openpgp(sc_card_t *card, u8 in_key_id) { char *del_fingerprint = "00:DA:00:C6:14:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"; char *del_creationtime = "00:DA:00:CD:04:00:00:00:00"; @@ -513,12 +627,12 @@ int delete_key_openpgp(sc_card_t *card, u8 key_id) sc_hex_to_bin(apdustring, buf, &len0); /* Replace DO tag, subject to key ID */ - buf[3] = buf[3] + key_id; + buf[3] = buf[3] + in_key_id; /* Build APDU from binary array */ r = sc_bytes2apdu(card->ctx, buf, len0, &apdu); if (r) { - fprintf(stderr, "Failed to build APDU: %s\n", sc_strerror(r)); + util_error("failed to build APDU: %s", sc_strerror(r)); return r; } apdu.resp = rbuf; @@ -527,39 +641,40 @@ int delete_key_openpgp(sc_card_t *card, u8 key_id) /* Send APDU to card */ r = sc_transmit_apdu(card, &apdu); if (r) { - fprintf(stderr, "Transmiting APDU failed: %s\n", sc_strerror(r)); + util_error("transmitting APDU failed: %s", sc_strerror(r)); return r; } } /* TODO: Rewrite Extended Header List. - * Not support by OpenGPG v2 yet */ + * Not support by OpenPGP v2 yet */ return r; } -int do_delete_key(sc_card_t *card, u8 key_id) +int do_delete_key(sc_card_t *card, u8 in_key_id) { sc_path_t path; int r = SC_SUCCESS; /* Currently, only Gnuk supports deleting keys */ if (card->type != SC_CARD_TYPE_OPENPGP_GNUK) { - printf("Only Gnuk supports deleting keys. General OpenPGP doesn't."); + util_error("only Gnuk supports deleting keys. General OpenPGP doesn't"); return SC_ERROR_NOT_SUPPORTED; } - if (key_id < 1 || (key_id > 3 && key_id != 'a')) { - printf("Error: Invalid key id %d", key_id); + if (in_key_id < 1 || (in_key_id > 3 && in_key_id != 'a')) { + util_error("invalid key id %d", in_key_id); return SC_ERROR_INVALID_ARGUMENTS; } - if (key_id == 1 || key_id == 'a') { + + if (in_key_id == 1 || in_key_id == 'a') { sc_format_path("B601", &path); r |= sc_delete_file(card, &path); } - if (key_id == 2 || key_id == 'a') { + if (in_key_id == 2 || in_key_id == 'a') { sc_format_path("B801", &path); r |= sc_delete_file(card, &path); } - if (key_id == 3 || key_id == 'a') { + if (in_key_id == 3 || in_key_id == 'a') { sc_format_path("A401", &path); r |= sc_delete_file(card, &path); } @@ -568,14 +683,8 @@ int do_delete_key(sc_card_t *card, u8 key_id) int do_erase(sc_card_t *card) { - int r; - /* Check card version */ - if (card->type != SC_CARD_TYPE_OPENPGP_V2) { - printf("Do not erase card which is not OpenPGP v2\n"); - } printf("Erase card\n"); - r = sc_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL); - return r; + return sc_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL); } int main(int argc, char **argv) @@ -594,32 +703,38 @@ int main(int argc, char **argv) memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.ver = 0; ctx_param.app_name = app_name; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; r = sc_context_create(&ctx, &ctx_param); if (r) { - util_fatal("failed to establish context: %s\n", - sc_strerror(r)); + util_fatal("failed to establish context: %s", sc_strerror(r)); return EXIT_FAILURE; } - if (verbose > 1) { - ctx->debug = verbose; - sc_ctx_log_to_file(ctx, "stderr"); + /* force OpenPGP card driver */ + r = sc_set_card_driver(ctx, "openpgp"); + if (r) { + sc_release_context(ctx); + util_fatal("OpenPGP card driver not found!\n"); + return EXIT_FAILURE; } - r = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); + r = util_connect_card(ctx, &card, opt_reader, opt_wait); if (r) { - util_fatal("failed to connect to card: %s\n", - sc_strerror(r)); + sc_release_context(ctx); + util_fatal("failed to connect to card: %s", sc_strerror(r)); return EXIT_FAILURE; } /* check card type */ - if ((card->type != SC_CARD_TYPE_OPENPGP_V1) && - (card->type != SC_CARD_TYPE_OPENPGP_V2) && - (card->type != SC_CARD_TYPE_OPENPGP_GNUK)) { - util_error("not an OpenPGP card"); - fprintf(stderr, "Card type %X\n", card->type); + if ((card->type != SC_CARD_TYPE_OPENPGP_BASE) && + (card->type != SC_CARD_TYPE_OPENPGP_V1) && + (card->type != SC_CARD_TYPE_OPENPGP_V2) && + (card->type != SC_CARD_TYPE_OPENPGP_V3) && + (card->type != SC_CARD_TYPE_OPENPGP_GNUK)) { + util_error("card type %X: not an OpenPGP card", card->type); exit_status = EXIT_FAILURE; goto out; } @@ -632,19 +747,29 @@ int main(int argc, char **argv) if (!actions) opt_userinfo = 1; + if (opt_cardinfo) + exit_status |= do_info(card, card_data); + if (opt_userinfo) - exit_status |= do_userinfo(card); + exit_status |= do_info(card, user_data); + + if (opt_keyinfo) + exit_status |= do_info(card, key_data); if (opt_verify && opt_pin) { exit_status |= do_verify(card, verifytype, pin); } if (opt_dump_do) { - exit_status |= do_dump_do(card, 0x0100 + do_dump_idx); + size_t n; + + for (n = 0; n < opt_dump_do; n++) { + exit_status |= do_dump_do(card, do_dump_idx[n]); + } } if (opt_genkey) - exit_status |= do_genkey(card, key_id, key_len); + exit_status |= do_genkey(card, key_id, opt_keytype); if (exec_program) { char *const largv[] = {exec_program, NULL}; diff --git a/src/tools/opensc-asn1-cmdline.c b/src/tools/opensc-asn1-cmdline.c new file mode 100644 index 0000000000..a571762b27 --- /dev/null +++ b/src/tools/opensc-asn1-cmdline.c @@ -0,0 +1,423 @@ +/* + File autogenerated by gengetopt version 2.23 + generated with the following command: + /opt/homebrew/bin/gengetopt --file-name=opensc-asn1-cmdline --output-dir=. --unamed-opts + + The developers of gengetopt consider the fixed text that goes in all + gengetopt output files to be in the public domain: + we make no copyright claims on it. +*/ + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#ifndef FIX_UNUSED +#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ +#endif + +#include + +#include "opensc-asn1-cmdline.h" + +const char *gengetopt_args_info_purpose = ""; + +const char *gengetopt_args_info_usage = "Usage: opensc-asn1 [OPTION]... [FILE]..."; + +const char *gengetopt_args_info_versiontext = ""; + +const char *gengetopt_args_info_description = "Parse ASN.1 data."; + +const char *gengetopt_args_info_help[] = { + " -h, --help Print help and exit", + " -V, --version Print version and exit", + "\nReport bugs to https://github.com/OpenSC/OpenSC/issues\n\nWritten by Frank Morgner ", + 0 +}; + +typedef enum {ARG_NO +} cmdline_parser_arg_type; + +static +void clear_given (struct gengetopt_args_info *args_info); +static +void clear_args (struct gengetopt_args_info *args_info); + +static int +cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params, const char *additional_error); + + +static char * +gengetopt_strdup (const char *s); + +static +void clear_given (struct gengetopt_args_info *args_info) +{ + args_info->help_given = 0 ; + args_info->version_given = 0 ; +} + +static +void clear_args (struct gengetopt_args_info *args_info) +{ + FIX_UNUSED (args_info); + +} + +static +void init_args_info(struct gengetopt_args_info *args_info) +{ + + + args_info->help_help = gengetopt_args_info_help[0] ; + args_info->version_help = gengetopt_args_info_help[1] ; + +} + +void +cmdline_parser_print_version (void) +{ + printf ("%s %s\n", + (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE), + CMDLINE_PARSER_VERSION); + + if (strlen(gengetopt_args_info_versiontext) > 0) + printf("\n%s\n", gengetopt_args_info_versiontext); +} + +static void print_help_common(void) +{ + size_t len_purpose = strlen(gengetopt_args_info_purpose); + size_t len_usage = strlen(gengetopt_args_info_usage); + + if (len_usage > 0) { + printf("%s\n", gengetopt_args_info_usage); + } + if (len_purpose > 0) { + printf("%s\n", gengetopt_args_info_purpose); + } + + if (len_usage || len_purpose) { + printf("\n"); + } + + if (strlen(gengetopt_args_info_description) > 0) { + printf("%s\n\n", gengetopt_args_info_description); + } +} + +void +cmdline_parser_print_help (void) +{ + int i = 0; + print_help_common(); + while (gengetopt_args_info_help[i]) + printf("%s\n", gengetopt_args_info_help[i++]); +} + +void +cmdline_parser_init (struct gengetopt_args_info *args_info) +{ + clear_given (args_info); + clear_args (args_info); + init_args_info (args_info); + + args_info->inputs = 0; + args_info->inputs_num = 0; +} + +void +cmdline_parser_params_init(struct cmdline_parser_params *params) +{ + if (params) + { + params->override = 0; + params->initialize = 1; + params->check_required = 1; + params->check_ambiguity = 0; + params->print_errors = 1; + } +} + +struct cmdline_parser_params * +cmdline_parser_params_create(void) +{ + struct cmdline_parser_params *params = + (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params)); + cmdline_parser_params_init(params); + return params; +} + + + +static void +cmdline_parser_release (struct gengetopt_args_info *args_info) +{ + unsigned int i; + + + for (i = 0; i < args_info->inputs_num; ++i) + free (args_info->inputs [i]); + + if (args_info->inputs_num) + free (args_info->inputs); + + clear_given (args_info); +} + + +static void +write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[]) +{ + FIX_UNUSED (values); + if (arg) { + fprintf(outfile, "%s=\"%s\"\n", opt, arg); + } else { + fprintf(outfile, "%s\n", opt); + } +} + + +int +cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) +{ + int i = 0; + + if (!outfile) + { + fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE); + return EXIT_FAILURE; + } + + if (args_info->help_given) + write_into_file(outfile, "help", 0, 0 ); + if (args_info->version_given) + write_into_file(outfile, "version", 0, 0 ); + + + i = EXIT_SUCCESS; + return i; +} + +int +cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) +{ + FILE *outfile; + int i = 0; + + outfile = fopen(filename, "w"); + + if (!outfile) + { + fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); + return EXIT_FAILURE; + } + + i = cmdline_parser_dump(outfile, args_info); + fclose (outfile); + + return i; +} + +void +cmdline_parser_free (struct gengetopt_args_info *args_info) +{ + cmdline_parser_release (args_info); +} + +/** @brief replacement of strdup, which is not standard */ +char * +gengetopt_strdup (const char *s) +{ + char *result = 0; + if (!s) + return result; + + result = (char*)malloc(strlen(s) + 1); + if (result == (char*)0) + return (char*)0; + strcpy(result, s); + return result; +} + +int +cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info) +{ + return cmdline_parser2 (argc, argv, args_info, 0, 1, 1); +} + +int +cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params) +{ + int result; + result = cmdline_parser_internal (argc, argv, args_info, params, 0); + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) +{ + int result; + struct cmdline_parser_params params; + + params.override = override; + params.initialize = initialize; + params.check_required = check_required; + params.check_ambiguity = 0; + params.print_errors = 1; + + result = cmdline_parser_internal (argc, argv, args_info, ¶ms, 0); + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name) +{ + FIX_UNUSED (args_info); + FIX_UNUSED (prog_name); + return EXIT_SUCCESS; +} + + +static char *package_name = 0; + + + +int +cmdline_parser_internal ( + int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params, const char *additional_error) +{ + int c; /* Character of the parsed option. */ + + int error_occurred = 0; + struct gengetopt_args_info local_args_info; + + int override; + int initialize; + int check_required; + int check_ambiguity; + + package_name = argv[0]; + + /* TODO: Why is this here? It is not used anywhere. */ + override = params->override; + FIX_UNUSED(override); + + initialize = params->initialize; + check_required = params->check_required; + + /* TODO: Why is this here? It is not used anywhere. */ + check_ambiguity = params->check_ambiguity; + FIX_UNUSED(check_ambiguity); + + if (initialize) + cmdline_parser_init (args_info); + + cmdline_parser_init (&local_args_info); + + optarg = 0; + optind = 0; + opterr = params->print_errors; + optopt = '?'; + + while (1) + { + int option_index = 0; + + static struct option long_options[] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long (argc, argv, "hV", long_options, &option_index); + + if (c == -1) break; /* Exit from `while (1)' loop. */ + + switch (c) + { + case 'h': /* Print help and exit. */ + cmdline_parser_print_help (); + cmdline_parser_free (&local_args_info); + exit (EXIT_SUCCESS); + + case 'V': /* Print version and exit. */ + cmdline_parser_print_version (); + cmdline_parser_free (&local_args_info); + exit (EXIT_SUCCESS); + + + case 0: /* Long option with no short option */ + case '?': /* Invalid option. */ + /* `getopt_long' already printed an error message. */ + goto failure; + + default: /* bug: option not considered. */ + fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); + abort (); + } /* switch */ + } /* while */ + + + + FIX_UNUSED(check_required); + + cmdline_parser_release (&local_args_info); + + if ( error_occurred ) + return (EXIT_FAILURE); + + if (optind < argc) + { + int i = 0 ; + int found_prog_name = 0; + /* whether program name, i.e., argv[0], is in the remaining args + (this may happen with some implementations of getopt, + but surely not with the one included by gengetopt) */ + + i = optind; + while (i < argc) + if (argv[i++] == argv[0]) { + found_prog_name = 1; + break; + } + i = 0; + + args_info->inputs_num = argc - optind - found_prog_name; + args_info->inputs = + (char **)(malloc ((args_info->inputs_num)*sizeof(char *))) ; + while (optind < argc) + if (argv[optind++] != argv[0]) + args_info->inputs[ i++ ] = gengetopt_strdup (argv[optind-1]) ; + } + + return 0; + +failure: + + cmdline_parser_release (&local_args_info); + return (EXIT_FAILURE); +} +/* vim: set ft=c noet ts=8 sts=8 sw=8 tw=80 nojs spell : */ diff --git a/src/tools/opensc-asn1-cmdline.h b/src/tools/opensc-asn1-cmdline.h new file mode 100644 index 0000000000..93073da96b --- /dev/null +++ b/src/tools/opensc-asn1-cmdline.h @@ -0,0 +1,175 @@ +/** @file opensc-asn1-cmdline.h + * @brief The header file for the command line option parser + * generated by GNU Gengetopt version 2.23 + * http://www.gnu.org/software/gengetopt. + * DO NOT modify this file, since it can be overwritten + * @author GNU Gengetopt */ + +#ifndef OPENSC_ASN1_CMDLINE_H +#define OPENSC_ASN1_CMDLINE_H + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include /* for FILE */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef CMDLINE_PARSER_PACKAGE +/** @brief the program name (used for printing errors) */ +#define CMDLINE_PARSER_PACKAGE "opensc-asn1" +#endif + +#ifndef CMDLINE_PARSER_PACKAGE_NAME +/** @brief the complete program name (used for help and version) */ +#define CMDLINE_PARSER_PACKAGE_NAME "opensc-asn1" +#endif + +#ifndef CMDLINE_PARSER_VERSION +/** @brief the program version */ +#define CMDLINE_PARSER_VERSION VERSION +#endif + +/** @brief Where the command line options are stored */ +struct gengetopt_args_info +{ + const char *help_help; /**< @brief Print help and exit help description. */ + const char *version_help; /**< @brief Print version and exit help description. */ + + unsigned int help_given ; /**< @brief Whether help was given. */ + unsigned int version_given ; /**< @brief Whether version was given. */ + + char **inputs ; /**< @brief unnamed options (options without names) */ + unsigned inputs_num ; /**< @brief unnamed options number */ +} ; + +/** @brief The additional parameters to pass to parser functions */ +struct cmdline_parser_params +{ + int override; /**< @brief whether to override possibly already present options (default 0) */ + int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */ + int check_required; /**< @brief whether to check that all required options were provided (default 1) */ + int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */ + int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */ +} ; + +/** @brief the purpose string of the program */ +extern const char *gengetopt_args_info_purpose; +/** @brief the usage string of the program */ +extern const char *gengetopt_args_info_usage; +/** @brief the description string of the program */ +extern const char *gengetopt_args_info_description; +/** @brief all the lines making the help output */ +extern const char *gengetopt_args_info_help[]; + +/** + * The command line parser + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser (int argc, char **argv, + struct gengetopt_args_info *args_info); + +/** + * The command line parser (version with additional parameters - deprecated) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param override whether to override possibly already present options + * @param initialize whether to initialize the option structure my_args_info + * @param check_required whether to check that all required options were provided + * @return 0 if everything went fine, NON 0 if an error took place + * @deprecated use cmdline_parser_ext() instead + */ +int cmdline_parser2 (int argc, char **argv, + struct gengetopt_args_info *args_info, + int override, int initialize, int check_required); + +/** + * The command line parser (version with additional parameters) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param params additional parameters for the parser + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_ext (int argc, char **argv, + struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params); + +/** + * Save the contents of the option struct into an already open FILE stream. + * @param outfile the stream where to dump options + * @param args_info the option struct to dump + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_dump(FILE *outfile, + struct gengetopt_args_info *args_info); + +/** + * Save the contents of the option struct into a (text) file. + * This file can be read by the config file parser (if generated by gengetopt) + * @param filename the file where to save + * @param args_info the option struct to save + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_file_save(const char *filename, + struct gengetopt_args_info *args_info); + +/** + * Print the help + */ +void cmdline_parser_print_help(void); +/** + * Print the version + */ +void cmdline_parser_print_version(void); + +/** + * Initializes all the fields a cmdline_parser_params structure + * to their default values + * @param params the structure to initialize + */ +void cmdline_parser_params_init(struct cmdline_parser_params *params); + +/** + * Allocates dynamically a cmdline_parser_params structure and initializes + * all its fields to their default values + * @return the created and initialized cmdline_parser_params structure + */ +struct cmdline_parser_params *cmdline_parser_params_create(void); + +/** + * Initializes the passed gengetopt_args_info structure's fields + * (also set default values for options that have a default) + * @param args_info the structure to initialize + */ +void cmdline_parser_init (struct gengetopt_args_info *args_info); +/** + * Deallocates the string fields of the gengetopt_args_info structure + * (but does not deallocate the structure itself) + * @param args_info the structure to deallocate + */ +void cmdline_parser_free (struct gengetopt_args_info *args_info); + +/** + * Checks that all the required options were specified + * @param args_info the structure to check + * @param prog_name the name of the program that will be used to print + * possible errors + * @return + */ +int cmdline_parser_required (struct gengetopt_args_info *args_info, + const char *prog_name); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* OPENSC_ASN1_CMDLINE_H */ diff --git a/src/tools/opensc-asn1.c b/src/tools/opensc-asn1.c new file mode 100644 index 0000000000..a26e94fc23 --- /dev/null +++ b/src/tools/opensc-asn1.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017 Frank Morgner + * + * This file is part of OpenSC. + * + * 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 + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fread_to_eof.h" +#include "libopensc/asn1.h" +#include "opensc-asn1-cmdline.h" +#include + +int +main (int argc, char **argv) +{ + struct gengetopt_args_info cmdline; + unsigned char *buf = NULL; + size_t buflen = 0, i; + + if (cmdline_parser(argc, argv, &cmdline) != 0) + return 1; + + for (i = 0; i < cmdline.inputs_num; i++) { + if (!fread_to_eof(cmdline.inputs[i], &buf, &buflen)) + continue; + + printf("Parsing '%s' (%"SC_FORMAT_LEN_SIZE_T"u byte%s)\n", + cmdline.inputs[i], buflen, buflen == 1 ? "" : "s"); + sc_asn1_print_tags(buf, buflen); + } + + free(buf); + cmdline_parser_free (&cmdline); + + return 0; +} diff --git a/src/tools/opensc-asn1.ggo.in b/src/tools/opensc-asn1.ggo.in new file mode 100644 index 0000000000..2f44ef2e6c --- /dev/null +++ b/src/tools/opensc-asn1.ggo.in @@ -0,0 +1,8 @@ +package "opensc-asn1" +purpose "@PACKAGE_SUMMARY@" +description "Parse ASN.1 data." + +text " +Report bugs to @PACKAGE_BUGREPORT@ + +Written by Frank Morgner " diff --git a/src/tools/opensc-explorer.c b/src/tools/opensc-explorer.c index 8ec63cf630..0341652020 100644 --- a/src/tools/opensc-explorer.c +++ b/src/tools/opensc-explorer.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -24,6 +24,7 @@ #include #include #include +#include #ifdef ENABLE_READLINE #include #include @@ -32,6 +33,11 @@ #include /* for htons() */ #endif +#ifdef _WIN32 +#include /* for_setmode() */ +#include /* for _O_TEXT and _O_BINARY */ +#endif + #ifdef HAVE_IO_H #include #endif @@ -40,8 +46,11 @@ #include "libopensc/asn1.h" #include "libopensc/cardctl.h" #include "libopensc/cards.h" +#include "libopensc/log.h" +#include "libopensc/internal.h" +#include "libopensc/iso7816.h" #include "common/compat_strlcpy.h" -#include "common/compat_getopt.h" +#include #include "util.h" #define DIM(v) (sizeof(v)/sizeof((v)[0])) @@ -63,6 +72,7 @@ static sc_file_t *current_file = NULL; static sc_path_t current_path; static sc_context_t *ctx = NULL; static sc_card_t *card = NULL; +static int interactive = 1; static const struct option options[] = { { "reader", 1, NULL, 'r' }, @@ -74,10 +84,10 @@ static const struct option options[] = { }; static const char *option_help[] = { "Uses reader number [0]", - "Forces the use of driver [auto-detect]", + "Forces the use of driver [auto-detect; '?' for list]", "Selects path on start-up, or none if empty [3F00]", "Wait for card insertion", - "Verbose operation. Use several times to enable debug output.", + "Verbose operation, may be used several times", }; @@ -92,10 +102,12 @@ static int do_info(int argc, char **argv); static int do_create(int argc, char **argv); static int do_mkdir(int argc, char **argv); static int do_delete(int argc, char **argv); +static int do_pininfo(int argc, char **argv); static int do_verify(int argc, char **argv); static int do_change(int argc, char **argv); static int do_unblock(int argc, char **argv); static int do_get(int argc, char **argv); +static int do_get_record(int argc, char **argv); static int do_update_binary(int argc, char **argv); static int do_update_record(int argc, char **argv); static int do_put(int argc, char **argv); @@ -120,79 +132,85 @@ struct command { static struct command cmds[] = { { do_echo, - "echo", "[ ..]", + "echo", "[ ...]", "display arguments" }, { do_ls, - "ls", "[ ..]", + "ls", "[ ...]", "list files in the current DF" }, { do_find, - "find", "[ []]", + "find", "[ []]", "find all files in the current DF" }, { do_find_tags, - "find_tags", "[ []]", + "find_tags", "[ []]", "find all tags of data objects in the current context" }, { do_cd, - "cd", "{.. | | aid:}", + "cd", "{.. | | aid:}", "change to another DF" }, { do_cat, - "cat", "[ | sfi:]" - , "print the contents of an EF" }, + "cat", "[{ | sfi:} []]" + , "print the contents of [a record in] an EF" }, { do_info, - "info", "[]", + "info", "[]", "display attributes of card file" }, { do_create, - "create", " ", + "create", " ", "create a new EF" }, { do_mkdir, - "mkdir", " ", + "mkdir", " ", "create a new DF" }, { do_delete, - "delete", "", + "delete", "", "remove an EF/DF" }, { do_delete, - "rm", "", + "rm", "", "remove an EF/DF" }, + { do_pininfo, + "pin_info", "{CHV|KEY|AUT|PRO}", + "get information on PIN or key from the card" }, { do_verify, - "verify", "{CHV|KEY|AUT|PRO} []", + "verify", "{CHV|KEY|AUT|PRO} []", "present a PIN or key to the card" }, { do_change, - "change", "CHV [[] ]", + "change", "CHV [[] ]", "change a PIN" }, { do_unblock, - "unblock", "CHV [ []]", + "unblock", "CHV [ []]", "unblock a PIN" }, { do_put, - "put", " []", + "put", " []", "copy a local file to the card" }, { do_get, - "get", " []", + "get", " []", "copy an EF to a local file" }, + { do_get_record, + "get_record", " []", + "copy a record of an EF to a local file" }, { do_get_data, - "do_get", " []", + "do_get", " []", "get a data object" }, { do_put_data, - "do_put", " ", + "do_put", " ", "put a data object" }, { do_erase, "erase", "", "erase card" }, { do_random, - "random", "", + "random", " []", "obtain random bytes from card" }, { do_update_record, - "update_record", " ", + "update_record", " ", "update record" }, { do_update_binary, - "update_binary", " ", + "update_binary", " ", "update binary" }, { do_apdu, - "apdu", "+", + "apdu", " ...", "send a custom apdu command" }, { do_asn1, - "asn1", "[]", - "decode an ASN.1 file" }, + "asn1", "[ [] []]", + "decode an ASN.1 file or record" }, { do_sm, - "sm", "open|close", + "sm", "{open|close}", "call SM 'open' or 'close' handlers, if available"}, { do_debug, "debug", "[]", @@ -210,16 +228,21 @@ static struct command cmds[] = { }; -static char *path_to_filename(const sc_path_t *path, const char sep) +static char *path_to_filename(const sc_path_t *path, const char sep, size_t rec) { - static char buf[2*SC_MAX_PATH_STRING_SIZE]; + static char buf[2*SC_MAX_PATH_STRING_SIZE+10]; size_t i, j; + /* build file name from path elements */ for (i = 0, j = 0; path != NULL && i < path->len; i++) { if (sep != '\0' && i > 0 && (i & 1) == 0) j += sprintf(buf+j, "%c", sep); j += sprintf(buf+j, "%02X", path->value[i]); } + /* single record: append record-number */ + if (rec > 0) + j += sprintf(buf+j, "%c%"SC_FORMAT_LEN_SIZE_T"u", + (sep == '-') ? '_' : '-', rec); buf[j] = '\0'; return buf; @@ -262,7 +285,6 @@ static void die(int ret) { sc_file_free(current_file); if (card) { - sc_unlock(card); sc_disconnect_card(card); } if (ctx) @@ -273,30 +295,45 @@ static void die(int ret) static void select_current_path_or_die(void) { if (current_path.type || current_path.len) { - int r = sc_select_file(card, ¤t_path, NULL); + int r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, ¤t_path, NULL); + sc_unlock(card); if (r) { - printf("unable to select parent DF: %s\n", sc_strerror(r)); + fprintf(stderr, "unable to select parent DF: %s\n", sc_strerror(r)); die(1); } } } static struct command * -ambiguous_match(struct command *table, const char *cmd) +ambiguous_match(struct command *table, const char *cmd, int *ambiguous) { struct command *last_match = NULL; - int matches = 0; - for (; table->name; table++) { - if (strncasecmp(cmd, table->name, strlen(cmd)) == 0) { - last_match = table; - matches++; + if (table != NULL && cmd != NULL) { + for (; table->name; table++) { + size_t cmdlen = strlen(cmd); + + /* compare cmd with prefix of known command */ + if (strncasecmp(table->name, cmd, cmdlen) == 0) { + /* succeed immediately if lengths match too, i.e. exact match */ + if (cmdlen == strlen(table->name)) + return table; + /* fail on multiple prefix-only matches */ + if (last_match != NULL) { + if (ambiguous != NULL) + *ambiguous = 1; + return NULL; + } + /* remember latest prefix-only match */ + last_match = table; + } } } - if (matches > 1) { - printf("Ambiguous command: %s\n", cmd); - return NULL; - } + /* indicate as non-ambiguous if there was no match */ + if (ambiguous != NULL && last_match == NULL) + *ambiguous = 0; return last_match; } @@ -316,12 +353,12 @@ arg_to_fid(const char *arg, u8 *fid) unsigned int fid0, fid1; if (strlen(arg) != 4) { - printf("Wrong ID length.\n"); + fprintf(stderr, "Wrong ID length.\n"); return -1; } if (sscanf(arg, "%02X%02X", &fid0, &fid1) != 2) { - printf("Invalid ID.\n"); + fprintf(stderr, "Invalid ID.\n"); return -1; } @@ -345,13 +382,14 @@ arg_to_path(const char *arg, sc_path_t *path, int is_id) path->type = SC_PATH_TYPE_DF_NAME; path->len = sizeof(path->value); if ((r = sc_hex_to_bin(p, path->value, &path->len)) < 0) { - printf("Error parsing AID: %s\n", p); + fprintf(stderr, "Error parsing AID: %s\n", p); return r; } } else { /* file id */ u8 cbuf[2]; - if (arg_to_fid(arg, cbuf) < 0) + + if (arg_to_fid(arg, cbuf) < 0) return -1; if ((cbuf[0] == 0x3F && cbuf[1] == 0x00) || is_id) { @@ -360,9 +398,9 @@ arg_to_path(const char *arg, sc_path_t *path, int is_id) path->type = (is_id) ? SC_PATH_TYPE_FILE_ID : SC_PATH_TYPE_PATH; } else { *path = current_path; - if (path->type == SC_PATH_TYPE_DF_NAME) { - if (path->len > sizeof(path->aid.value)) { - printf("Invalid length of DF_NAME path\n"); + if (path->type == SC_PATH_TYPE_DF_NAME) { + if (path->len > sizeof(path->aid.value)) { + fprintf(stderr, "Invalid length of DF_NAME path\n"); return -1; } @@ -430,7 +468,7 @@ static int pattern_match(const char *pattern, const char *string) return 0; while (*pattern != '\0' && *string != '\0') { - /* wildcard matching multple characters */ + /* wildcard matching multiple characters */ if (*pattern == '*') { for (pattern++; *string != '\0' ; string++) if (pattern_match(pattern, string)) @@ -439,11 +477,11 @@ static int pattern_match(const char *pattern, const char *string) } /* simple character class matching a single character */ else if (*pattern == '[') { - char *end = strchr(pattern, ']'); + const char *end = strchr(pattern, ']'); int match = 0; for (pattern++; end != NULL && pattern != end; pattern++) { - if (tolower(*pattern) == tolower(*string)) + if (tolower((unsigned char) *pattern) == tolower((unsigned char) *string)) match++; } if (!match) @@ -452,7 +490,7 @@ static int pattern_match(const char *pattern, const char *string) string++; } /* single character comparison / wildcard matching a single character */ - else if (tolower(*pattern) == tolower(*string) || *pattern == '?') { + else if (tolower((unsigned char) *pattern) == tolower((unsigned char) *string) || *pattern == '?') { pattern++; string++; } @@ -462,25 +500,27 @@ static int pattern_match(const char *pattern, const char *string) if (*string == '\0' || *pattern == '\0') break; } - return (*pattern != '\0' || *string != '\0' || tolower(*pattern) != tolower(*string)) ? 0 : 1; + return (*pattern != '\0' || *string != '\0' || tolower((unsigned char) *pattern) != tolower((unsigned char) *string)) ? 0 : 1; } static int do_ls(int argc, char **argv) { - u8 buf[256], *cur = buf; + u8 buf[SC_MAX_EXT_APDU_RESP_SIZE], *cur = buf; int r, count; - r = sc_list_files(card, buf, sizeof(buf)); + memset(buf, 0, sizeof(buf)); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_list_files(card, buf, sizeof(buf)); + sc_unlock(card); if (r < 0) { - check_ret(r, SC_AC_OP_LIST_FILES, "unable to receive file listing", current_file); + check_ret(r, SC_AC_OP_LIST_FILES, "Unable to receive file listing", current_file); return -1; } count = r; printf("FileID\tType Size\n"); while (count >= 2) { - sc_path_t path; - sc_file_t *file = NULL; - char filename[10]; + char filename[SC_MAX_PATH_STRING_SIZE]; int i = 0; int matches = 0; @@ -497,22 +537,29 @@ static int do_ls(int argc, char **argv) /* if any filename pattern were given, filter only matching file names */ if (argc == 0 || matches) { + sc_path_t path; + sc_file_t *file = NULL; + if (current_path.type != SC_PATH_TYPE_DF_NAME) { path = current_path; sc_append_path_id(&path, cur, 2); } else { if (sc_path_set(&path, SC_PATH_TYPE_FILE_ID, cur, 2, 0, 0) != SC_SUCCESS) { - printf("unable to set path.\n"); + fprintf(stderr, "Unable to set path.\n"); die(1); } } - r = sc_select_file(card, &path, &file); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, &path, &file); + sc_unlock(card); if (r) { - printf(" %02X%02X unable to select file, %s\n", cur[0], cur[1], sc_strerror(r)); + fprintf(stderr, "Unable to select file %02X%02X: %s\n", + cur[0], cur[1], sc_strerror(r)); } else { file->id = (cur[0] << 8) | cur[1]; - print_file(file); + print_file(file); sc_file_free(file); } } @@ -525,14 +572,11 @@ static int do_ls(int argc, char **argv) static int do_find(int argc, char **argv) { - u8 fid[2], end[2]; + u8 fid[2] = { 0x00, 0x00 }; + u8 end[2] = { 0xFF, 0xFF }; sc_path_t path; int r; - fid[0] = 0; - fid[1] = 0; - end[0] = 0xFF; - end[1] = 0xFF; switch (argc) { case 2: if (arg_to_fid(argv[1], end) != 0) @@ -557,15 +601,18 @@ static int do_find(int argc, char **argv) if (current_path.type != SC_PATH_TYPE_DF_NAME) { path = current_path; - sc_append_path_id(&path, fid, sizeof fid); + sc_append_path_id(&path, fid, sizeof(fid)); } else { - if (sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, 0) != SC_SUCCESS) { - printf("unable to set path.\n"); + if (sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0) != SC_SUCCESS) { + fprintf(stderr, "Unable to set path.\n"); die(1); } } - r = sc_select_file(card, &path, &file); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, &path, &file); + sc_unlock(card); switch (r) { case SC_SUCCESS: file->id = (fid[0] << 8) | fid[1]; @@ -590,14 +637,12 @@ static int do_find(int argc, char **argv) static int do_find_tags(int argc, char **argv) { - u8 start[2], end[2], rbuf[256]; + u8 start[2] = { 0x00, 0x00 }; + u8 end[2] = { 0xFF, 0xFF }; + u8 rbuf[SC_MAX_EXT_APDU_RESP_SIZE]; int r; unsigned int tag, tag_end; - start[0] = 0x00; - start[1] = 0x00; - end[0] = 0xFF; - end[1] = 0xFF; switch (argc) { case 2: if (arg_to_fid(argv[1], end) != 0) @@ -620,7 +665,12 @@ static int do_find_tags(int argc, char **argv) printf("(%04X)\r", tag); fflush(stdout); - r = sc_get_data(card, tag, rbuf, sizeof rbuf); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_get_data(card, tag, rbuf, sizeof(rbuf)); + else + r = SC_ERROR_READER_LOCKED; + sc_unlock(card); if (r >= 0) { printf(" %04X ", tag); if (tag == 0) @@ -667,20 +717,23 @@ static int do_cd(int argc, char **argv) if (strcmp(argv[0], "..") == 0) { path = current_path; if (path.len < 4) { - printf("unable to go up, already in MF.\n"); + fprintf(stderr, "unable to go up, already in MF.\n"); return -1; } - if (path.type == SC_PATH_TYPE_DF_NAME) { + if (path.type == SC_PATH_TYPE_DF_NAME) { sc_format_path("3F00", &path); } - else { + else { path.len -= 2; } - r = sc_select_file(card, &path, &file); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, &path, &file); + sc_unlock(card); if (r) { - printf("unable to go up: %s\n", sc_strerror(r)); + fprintf(stderr, "unable to go up: %s\n", sc_strerror(r)); return -1; } sc_file_free(current_file); @@ -691,13 +744,17 @@ static int do_cd(int argc, char **argv) if (arg_to_path(argv[0], &path, 0) != 0) return usage(do_cd); - r = sc_select_file(card, &path, &file); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, &path, &file); + sc_unlock(card); if (r) { check_ret(r, SC_AC_OP_SELECT, "unable to select DF", current_file); return -1; } - if ((file->type != SC_FILE_TYPE_DF) && (card->type != SC_CARD_TYPE_BELPIC_EID)) { - printf("Error: file is not a DF.\n"); + if ((file->type != SC_FILE_TYPE_DF) && (file->type != SC_FILE_TYPE_UNKNOWN) && + (card->type != SC_CARD_TYPE_BELPIC_EID)) { + fprintf(stderr, "Error: file is not a DF.\n"); sc_file_free(file); select_current_path_or_die(); return -1; @@ -709,65 +766,76 @@ static int do_cd(int argc, char **argv) return 0; } -static int read_and_util_print_binary_file(sc_file_t *file) +static int read_and_print_binary_file(sc_file_t *file) { - unsigned char *buf = NULL; - int r; - size_t size; + u8 *buf; + size_t size = (file->size > 0) ? file->size : SC_MAX_EXT_APDU_RESP_SIZE; + int r, ret = -1; - if (file->size) { - size = file->size; - } else { - size = 1024; - } - buf = malloc(size); - if (!buf) + buf = calloc(1, size); + if (buf == NULL) return -1; - r = sc_read_binary(card, 0, buf, size, 0); - if (r < 0) { - check_ret(r, SC_AC_OP_READ, "read failed", file); - return -1; + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_read_binary(card, 0, buf, size, 0); + sc_unlock(card); + if (r < 0) { + check_ret(r, SC_AC_OP_READ, "Read failed", file); + goto err; } - if ((r == 0) && (card->type == SC_CARD_TYPE_BELPIC_EID)) - return -1; util_hex_dump_asc(stdout, buf, r, 0); + ret = 0; + +err: free(buf); - return 0; + return ret; } -static int read_and_print_record_file(sc_file_t *file, unsigned char sfi) +static int read_and_print_record_file(sc_file_t *file, unsigned char sfi, unsigned int wanted) { - u8 buf[256]; - int rec, r; + u8 buf[SC_MAX_EXT_APDU_RESP_SIZE]; + int r; + unsigned int rec; - for (rec = 1; ; rec++) { - r = sc_read_record(card, rec, buf, sizeof(buf), - SC_RECORD_BY_REC_NR | sfi); - if (r == SC_ERROR_RECORD_NOT_FOUND) + for (rec = (wanted > 0) ? wanted : 1; wanted == 0 || wanted == rec; rec++) { + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_read_record(card, rec, 0, buf, sizeof(buf), + SC_RECORD_BY_REC_NR | sfi); + else + r = SC_ERROR_READER_LOCKED; + sc_unlock(card); + if (r == SC_ERROR_RECORD_NOT_FOUND && wanted == 0) return 0; if (r < 0) { - check_ret(r, SC_AC_OP_READ, "read failed", file); + check_ret(r, SC_AC_OP_READ, "Read failed", file); return -1; } printf("Record %d:\n", rec); util_hex_dump_asc(stdout, buf, r, 0); } + + return 0; } static int do_cat(int argc, char **argv) { int r, err = 1; + unsigned int rec = 0; sc_path_t path; sc_file_t *file = NULL; int not_current = 1; int sfi = 0; - if (argc > 1) + if (argc > 2) return usage(do_cat); + if (argc > 1) + rec = (unsigned int) strtoul(argv[1], NULL, 10); + if (!argc) { path = current_path; file = current_file; @@ -779,7 +847,7 @@ static int do_cat(int argc, char **argv) const char *sfi_n = argv[0] + strlen(sfi_prefix); if(!current_file) { - printf("A DF must be selected to read by SFI\n"); + fprintf(stderr, "A DF must be selected to read by SFI\n"); goto err; } path = current_path; @@ -787,14 +855,17 @@ static int do_cat(int argc, char **argv) not_current = 0; sfi = atoi(sfi_n); if ((sfi < 1) || (sfi > 30)) { - printf("Invalid SFI: %s\n", sfi_n); + fprintf(stderr, "Invalid SFI: %s\n", sfi_n); return usage(do_cat); } } else { if (arg_to_path(argv[0], &path, 0) != 0) return usage(do_cat); - r = sc_select_file(card, &path, &file); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, &path, &file); + sc_unlock(card); if (r) { check_ret(r, SC_AC_OP_SELECT, "unable to select file", current_file); @@ -804,13 +875,18 @@ static int do_cat(int argc, char **argv) } if (file->type != SC_FILE_TYPE_WORKING_EF && !(file->type == SC_FILE_TYPE_DF && sfi)) { - printf("only working EFs may be read\n"); + fprintf(stderr, "only working EFs may be read\n"); goto err; } - if (file->ef_structure == SC_FILE_EF_TRANSPARENT && !sfi) - read_and_util_print_binary_file(file); + if (file->ef_structure == SC_FILE_EF_TRANSPARENT && !sfi) { + if (argc > 1) { + fprintf(stderr, "Transparent files do not have records\n"); + goto err; + } + read_and_print_binary_file(file); + } else - read_and_print_record_file(file, sfi); + read_and_print_record_file(file, sfi, rec); err = 0; err: @@ -831,6 +907,25 @@ static int do_info(int argc, char **argv) int r, not_current = 1; const id2str_t *ac_ops = NULL; + const char *lifecycle = "unknown value"; + + static const id2str_t lc[] = { + { SC_FILE_STATUS_NO_INFO, "No information given" }, + { SC_FILE_STATUS_CREATION, "Creation state" }, + { SC_FILE_STATUS_RFU_2, "RFU (2)" }, + { SC_FILE_STATUS_INITIALISATION, "Initialisation state" }, + { SC_FILE_STATUS_INVALIDATED, "Operational, deactivated" }, + { SC_FILE_STATUS_ACTIVATED, "Operational, activated" }, + { SC_FILE_STATUS_RFU_8, "RFU (8)" }, + { SC_FILE_STATUS_RFU_9, "RFU (9)" }, + { SC_FILE_STATUS_RFU_10, "RFU (10)" }, + { SC_FILE_STATUS_RFU_11, "RFU (11)" }, + { SC_FILE_STATUS_TERMINATION, "Termination state" }, + { SC_FILE_STATUS_PROPRIETARY, "Proprietary state" }, + { SC_FILE_STATUS_UNKNOWN, "LCSB is not present" }, + { 0, NULL } + }; + if (!argc) { path = current_path; file = current_file; @@ -839,21 +934,26 @@ static int do_info(int argc, char **argv) if (arg_to_path(argv[0], &path, 0) != 0) return usage(do_info); - r = sc_select_file(card, &path, &file); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, &path, &file); + sc_unlock(card); if (r) { - printf("unable to select file: %s\n", sc_strerror(r)); + fprintf(stderr, "unable to select file: %s\n", sc_strerror(r)); return -1; } } else return usage(do_info); - if(!file->type_attr_len) + if (!file->type_attr_len) st = "Unknown File"; switch (file->type) { case SC_FILE_TYPE_WORKING_EF: + st = "Working Elementary File"; + break; case SC_FILE_TYPE_INTERNAL_EF: - st = "Elementary File"; + st = "Internal Elementary File"; break; case SC_FILE_TYPE_DF: st = "Dedicated File"; @@ -865,8 +965,8 @@ static int do_info(int argc, char **argv) printf("\n%s ID %04X", st, file->id); if (file->sid) printf(", SFI %02X", file->sid); - printf("\n\n%-15s%s\n", "File path:", path_to_filename(&path, '/')); - printf("%-15s%lu bytes\n", "File size:", (unsigned long) file->size); + printf("\n\n%-25s%s\n", "File path:", path_to_filename(&path, '/', 0)); + printf("%-25s%"SC_FORMAT_LEN_SIZE_T"u bytes\n", "File size:", file->size); if (file->type == SC_FILE_TYPE_DF) { static const id2str_t ac_ops_df[] = { @@ -883,7 +983,7 @@ static int do_info(int argc, char **argv) }; if (file->namelen) { - printf("%-15s", "DF name:"); + printf("%-25s", "DF name:"); util_print_binary(stdout, file->name, file->namelen); printf("\n"); } @@ -916,30 +1016,47 @@ static int do_info(int argc, char **argv) for (i = 0; ef_type_name[i].str != NULL; i++) if (file->ef_structure == ef_type_name[i].id) ef_type = ef_type_name[i].str; - printf("%-15s%s\n", "EF structure:", ef_type); + printf("%-25s%s\n", "EF structure:", ef_type); + + if (file->record_count > 0) + printf("%-25s%"SC_FORMAT_LEN_SIZE_T"u\n", + "Number of records:", file->record_count); + + if (file->record_length > 0) + printf("%-25s%"SC_FORMAT_LEN_SIZE_T"u bytes\n", + "Max. record size:", file->record_length); ac_ops = ac_ops_ef; } for (i = 0; ac_ops != NULL && ac_ops[i].str != NULL; i++) { - int len = strlen(ac_ops[i].str); + size_t len = strlen(ac_ops[i].str); printf("ACL for %s:%*s %s\n", ac_ops[i].str, - (12 > len) ? (12 - len) : 0, "", + (15 > len) ? (int)(15 - len) : 0, "", util_acl_to_str(sc_file_get_acl_entry(file, ac_ops[i].id))); } - if (file->prop_attr_len) { + if (file->type_attr_len > 0) { + printf("%-25s", "Type attributes:"); + util_hex_dump(stdout, file->type_attr, file->type_attr_len, " "); + printf("\n"); + } + if (file->prop_attr_len > 0) { printf("%-25s", "Proprietary attributes:"); util_hex_dump(stdout, file->prop_attr, file->prop_attr_len, " "); printf("\n"); } - if (file->sec_attr_len) { + if (file->sec_attr_len > 0) { printf("%-25s", "Security attributes:"); util_hex_dump(stdout, file->sec_attr, file->sec_attr_len, " "); printf("\n"); } + for (i = 0; lc[i].str != NULL; i++) + if (file->status == lc[i].id) + lifecycle = lc[i].str; + printf("%-25s%s\n", "Life cycle: ", lifecycle); printf("\n"); if (not_current) { sc_file_free(file); @@ -952,7 +1069,10 @@ static int create_file(sc_file_t *file) { int r; - r = sc_create_file(card, file); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_create_file(card, file); + sc_unlock(card); if (r) { check_ret(r, SC_AC_OP_CREATE, "CREATE FILE failed", current_file); return -1; @@ -1035,7 +1155,10 @@ static int do_delete(int argc, char **argv) if (path.len != 2) return usage(do_delete); path.type = SC_PATH_TYPE_FILE_ID; - r = sc_delete_file(card, &path); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_delete_file(card, &path); + sc_unlock(card); if (r) { check_ret(r, SC_AC_OP_DELETE, "DELETE FILE failed", current_file); return -1; @@ -1043,6 +1166,72 @@ static int do_delete(int argc, char **argv) return 0; } +static int do_pininfo(int argc, char **argv) +{ + const id2str_t typeNames[] = { + { SC_AC_CHV, "CHV" }, + { SC_AC_AUT, "KEY" }, + { SC_AC_AUT, "AUT" }, + { SC_AC_PRO, "PRO" }, + { SC_AC_NONE, NULL, } + }; + int r; + size_t i; + struct sc_pin_cmd_data data; + size_t prefix_len = 0; + + if (argc != 1) + return usage(do_pininfo); + + memset(&data, 0, sizeof(data)); + data.cmd = SC_PIN_CMD_GET_INFO; + + data.pin_type = SC_AC_NONE; + for (i = 0; typeNames[i].str; i++) { + prefix_len = strlen(typeNames[i].str); + if (strncasecmp(argv[0], typeNames[i].str, prefix_len) == 0) { + data.pin_type = typeNames[i].id; + break; + } + } + if (data.pin_type == SC_AC_NONE) { + fprintf(stderr, "Invalid type.\n"); + return usage(do_pininfo); + } + if (sscanf(argv[0] + prefix_len, "%d", &data.pin_reference) != 1) { + fprintf(stderr, "Invalid key reference.\n"); + return usage(do_pininfo); + } + + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_pin_cmd(card, &data); + sc_unlock(card); + + if (r) { + fprintf(stderr, "Unable to get PIN info: %s\n", sc_strerror(r)); + return -1; + } + printf("State:\n\t"); + if (data.pin1.logged_in == SC_PIN_STATE_UNKNOWN) { + printf("Login status unknown."); + } else { + if (data.pin1.logged_in & SC_PIN_STATE_LOGGED_IN) { + printf("Logged in "); + } + if (data.pin1.logged_in & SC_PIN_STATE_LOGGED_OUT) { + printf("Logged out "); + } + if (data.pin1.logged_in & SC_PIN_STATE_NEEDS_CHANGE) { + printf("Needs change "); + } + printf("\n"); + } + if (data.pin1.tries_left >= 0) + printf("%d tries left.\n", data.pin1.tries_left); + return 0; +} + static int do_verify(int argc, char **argv) { const id2str_t typeNames[] = { @@ -1052,11 +1241,11 @@ static int do_verify(int argc, char **argv) { SC_AC_PRO, "PRO" }, { SC_AC_NONE, NULL, } }; - int r, tries_left = -1; - u8 buf[64]; + int r; + u8 buf[SC_MAX_PIN_SIZE]; size_t buflen = sizeof(buf), i; struct sc_pin_cmd_data data; - int prefix_len = 0; + size_t prefix_len = 0; if (argc < 1 || argc > 2) return usage(do_verify); @@ -1073,11 +1262,11 @@ static int do_verify(int argc, char **argv) } } if (data.pin_type == SC_AC_NONE) { - printf("Invalid type.\n"); + fprintf(stderr, "Invalid type.\n"); return usage(do_verify); } if (sscanf(argv[0] + prefix_len, "%d", &data.pin_reference) != 1) { - printf("Invalid key reference.\n"); + fprintf(stderr, "Invalid key reference.\n"); return usage(do_verify); } @@ -1094,13 +1283,13 @@ static int do_verify(int argc, char **argv) printf("Please enter PIN: "); r = util_getpass(&pin, &len, stdin); if (r < 0) { - printf("util_getpass error.\n"); + fprintf(stderr, "util_getpass error.\n"); return -1; } if (strlcpy((char *)buf, pin, sizeof(buf)) >= sizeof(buf)) { free(pin); - printf("PIN too long - aborting VERIFY.\n"); + fprintf(stderr, "PIN too long - aborting VERIFY.\n"); return -1; } free(pin); @@ -1110,22 +1299,25 @@ static int do_verify(int argc, char **argv) } else { r = parse_string_or_hexdata(argv[1], buf, &buflen); if (0 != r) { - printf("Invalid key value.\n"); + fprintf(stderr, "Invalid key value.\n"); return usage(do_verify); } data.pin1.data = buf; data.pin1.len = buflen; } - r = sc_pin_cmd(card, &data, &tries_left); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_pin_cmd(card, &data); + sc_unlock(card); if (r) { if (r == SC_ERROR_PIN_CODE_INCORRECT) { - if (tries_left >= 0) - printf("Incorrect code, %d tries left.\n", tries_left); + if (data.pin1.tries_left >= 0) + printf("Incorrect code, %d tries left.\n", data.pin1.tries_left); else printf("Incorrect code.\n"); } else - printf("Unable to verify PIN code: %s\n", sc_strerror(r)); + fprintf(stderr, "Unable to verify PIN code: %s\n", sc_strerror(r)); return -1; } printf("Code correct.\n"); @@ -1134,9 +1326,9 @@ static int do_verify(int argc, char **argv) static int do_change(int argc, char **argv) { - int ref, r, tries_left = -1; - u8 oldpin[64]; - u8 newpin[64]; + int ref, r; + u8 oldpin[SC_MAX_PIN_SIZE]; + u8 newpin[SC_MAX_PIN_SIZE]; size_t oldpinlen = 0; size_t newpinlen = 0; struct sc_pin_cmd_data data; @@ -1147,18 +1339,18 @@ static int do_change(int argc, char **argv) if (argc < 1 || argc > 3) return usage(do_change); if (strncasecmp(argv[0], "CHV", 3)) { - printf("Invalid type.\n"); + fprintf(stderr, "Invalid type.\n"); return usage(do_change); } if (sscanf(argv[0] + 3, "%d", &ref) != 1) { - printf("Invalid key reference.\n"); + fprintf(stderr, "Invalid key reference.\n"); return usage(do_change); } if (argc == 3) { oldpinlen = sizeof(oldpin); if (parse_string_or_hexdata(argv[1], oldpin, &oldpinlen) != 0) { - printf("Invalid key value.\n"); + fprintf(stderr, "Invalid key value.\n"); return usage(do_change); } } @@ -1166,7 +1358,7 @@ static int do_change(int argc, char **argv) if (argc >= 2) { newpinlen = sizeof(newpin); if (parse_string_or_hexdata(argv[argc-1], newpin, &newpinlen) != 0) { - printf("Invalid key value.\n"); + fprintf(stderr, "Invalid key value.\n"); return usage(do_change); } } @@ -1178,15 +1370,18 @@ static int do_change(int argc, char **argv) data.pin2.data = newpinlen ? newpin : NULL; data.pin2.len = newpinlen; - r = sc_pin_cmd(card, &data, &tries_left); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_pin_cmd(card, &data); + sc_unlock(card); if (r) { if (r == SC_ERROR_PIN_CODE_INCORRECT) { - if (tries_left >= 0) - printf("Incorrect code, %d tries left.\n", tries_left); + if (data.pin1.tries_left >= 0) + printf("Incorrect code, %d tries left.\n", data.pin1.tries_left); else printf("Incorrect code.\n"); } - printf("Unable to change PIN code: %s\n", sc_strerror(r)); + fprintf(stderr, "Unable to change PIN code: %s\n", sc_strerror(r)); return -1; } printf("PIN changed.\n"); @@ -1197,8 +1392,8 @@ static int do_change(int argc, char **argv) static int do_unblock(int argc, char **argv) { int ref, r; - u8 puk[64]; - u8 newpin[64]; + u8 puk[SC_MAX_PIN_SIZE]; + u8 newpin[SC_MAX_PIN_SIZE]; size_t puklen = 0; size_t newpinlen = 0; struct sc_pin_cmd_data data; @@ -1209,7 +1404,7 @@ static int do_unblock(int argc, char **argv) if (argc < 1 || argc > 3) return usage(do_unblock); if (strncasecmp(argv[0], "CHV", 3)) { - printf("Invalid type.\n"); + fprintf(stderr, "Invalid type.\n"); return usage(do_unblock); } if (sscanf(argv[0] + 3, "%d", &ref) != 1) { @@ -1220,7 +1415,7 @@ static int do_unblock(int argc, char **argv) if (argc > 1) { puklen = sizeof(puk); if (parse_string_or_hexdata(argv[1], puk, &puklen) != 0) { - printf("Invalid key value.\n"); + fprintf(stderr, "Invalid key value.\n"); return usage(do_unblock); } } @@ -1228,7 +1423,7 @@ static int do_unblock(int argc, char **argv) if (argc > 2) { newpinlen = sizeof(newpin); if (parse_string_or_hexdata(argv[2], newpin, &newpinlen) != 0) { - printf("Invalid key value.\n"); + fprintf(stderr, "Invalid key value.\n"); return usage(do_unblock); } } @@ -1240,11 +1435,14 @@ static int do_unblock(int argc, char **argv) data.pin2.data = newpinlen ? newpin : NULL; data.pin2.len = newpinlen; - r = sc_pin_cmd(card, &data, NULL); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_pin_cmd(card, &data); + sc_unlock(card); if (r) { if (r == SC_ERROR_PIN_CODE_INCORRECT) - printf("Incorrect code.\n"); - printf("Unable to unblock PIN code: %s\n", sc_strerror(r)); + fprintf(stderr, "Incorrect code.\n"); + fprintf(stderr, "Unable to unblock PIN code: %s\n", sc_strerror(r)); return -1; } printf("PIN unblocked.\n"); @@ -1253,7 +1451,7 @@ static int do_unblock(int argc, char **argv) static int do_get(int argc, char **argv) { - u8 buf[256]; + u8 buf[SC_MAX_EXT_APDU_RESP_SIZE]; int r, err = 1; size_t count = 0; unsigned int idx = 0; @@ -1267,7 +1465,7 @@ static int do_get(int argc, char **argv) if (arg_to_path(argv[0], &path, 0) != 0) return usage(do_get); - filename = (argc == 2) ? argv[1] : path_to_filename(&path, '_'); + filename = (argc == 2) ? argv[1] : path_to_filename(&path, '_', 0); outf = (strcmp(filename, "-") == 0) ? stdout : fopen(filename, "wb"); @@ -1275,27 +1473,35 @@ static int do_get(int argc, char **argv) perror(filename); goto err; } - r = sc_select_file(card, &path, &file); - if (r) { - check_ret(r, SC_AC_OP_SELECT, "unable to select file", current_file); +#ifdef _WIN32 + if (outf == stdout) + _setmode(fileno(stdout), _O_BINARY); +#endif + + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, &path, &file); + sc_unlock(card); + if (r || file == NULL) { + check_ret(r, SC_AC_OP_SELECT, "Unable to select file", current_file); goto err; } - if (file->type != SC_FILE_TYPE_WORKING_EF) { - printf("only working EFs may be read\n"); + if (file->type != SC_FILE_TYPE_WORKING_EF || file->ef_structure != SC_FILE_EF_TRANSPARENT) { + fprintf(stderr, "Only transparent working EFs may be read\n"); goto err; } count = file->size; while (count) { /* FIXME sc_read_binary does this kind of fetching in a loop already */ - int c = count > sizeof(buf) ? sizeof(buf) : count; + size_t c = count > sizeof(buf) ? sizeof(buf) : count; r = sc_read_binary(card, idx, buf, c, 0); if (r < 0) { - check_ret(r, SC_AC_OP_READ, "read failed", file); + check_ret(r, SC_AC_OP_READ, "Read failed", file); goto err; } - if ((r != c) && (card->type != SC_CARD_TYPE_BELPIC_EID)) { - printf("expecting %d, got only %d bytes.\n", c, r); + if (((size_t)r != c) && (card->type != SC_CARD_TYPE_BELPIC_EID)) { + fprintf(stderr, "Expecting %zu, got only %d bytes.\n", c, r); goto err; } if ((r == 0) && (card->type == SC_CARD_TYPE_BELPIC_EID)) @@ -1315,6 +1521,105 @@ static int do_get(int argc, char **argv) err = 0; err: sc_file_free(file); +#ifdef _WIN32 + if (outf == stdout) + _setmode(fileno(stdout), _O_TEXT); +#endif + if (outf != NULL && outf != stdout) + fclose(outf); + select_current_path_or_die(); + return -err; +} + +static int do_get_record(int argc, char **argv) +{ + u8 buf[SC_MAX_EXT_APDU_RESP_SIZE]; + int r, err = 1; + size_t count = 0; + unsigned long rec; + sc_path_t path; + sc_file_t *file = NULL; + char *filename; + FILE *outf = NULL; + + if (argc < 2 || argc > 3) + return usage(do_get); + if (arg_to_path(argv[0], &path, 0) != 0) + return usage(do_get); + rec = strtoul(argv[1], NULL, 10); + + filename = (argc == 3) ? argv[2] : path_to_filename(&path, '_', rec); + outf = (strcmp(filename, "-") == 0) + ? stdout + : fopen(filename, "wb"); + if (outf == NULL) { + perror(filename); + goto err; + } +#ifdef _WIN32 + if (outf == stdout) + _setmode(fileno(stdout), _O_BINARY); +#endif + + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, &path, &file); + sc_unlock(card); + if (r || file == NULL) { + check_ret(r, SC_AC_OP_SELECT, "Unable to select file", current_file); + goto err; + } + + /* fail on wrong file type */ + if (file->type != SC_FILE_TYPE_WORKING_EF || + (file->ef_structure != SC_FILE_EF_LINEAR_FIXED && + file->ef_structure != SC_FILE_EF_LINEAR_FIXED_TLV && + file->ef_structure != SC_FILE_EF_LINEAR_VARIABLE && + file->ef_structure != SC_FILE_EF_LINEAR_VARIABLE_TLV)) { + fprintf(stderr, "Only record-oriented working EFs may be read\n"); + goto err; + } + + /* fail on out of bound record index: + * must be > 0 and if record_count is set <= record_count */ + if (rec < 1 || (file->record_count > 0 && rec > file->record_count)) { + fprintf(stderr, "Invalid record number %lu\n", rec); + goto err; + } + + r = sc_read_record(card, (unsigned)rec, 0, buf, sizeof(buf), SC_RECORD_BY_REC_NR); + if (r < 0) { + fprintf(stderr, "Cannot read record %lu; return %i\n", rec, r); + goto err; + } + + count = r; + + if (count > 0) { + size_t written = fwrite(buf, 1, (size_t) r, outf); + + if (written != count) { + fprintf(stderr, "Cannot write to file %s (only %"SC_FORMAT_LEN_SIZE_T"u of %"SC_FORMAT_LEN_SIZE_T"u bytes written)", + filename, written, count); + goto err; + } + } + + if (outf == stdout) { + fwrite("\n", 1, 1, outf); + } + else { + printf("Total of %"SC_FORMAT_LEN_SIZE_T"u bytes read from %s and saved to %s.\n", + count, argv[0], filename); + } + + err = 0; +err: + sc_file_free(file); +#ifdef _WIN32 + if (outf == stdout) + _setmode(fileno(stdout), _O_TEXT); +#endif if (outf != NULL && outf != stdout) fclose(outf); select_current_path_or_die(); @@ -1323,10 +1628,10 @@ static int do_get(int argc, char **argv) static int do_update_binary(int argc, char **argv) { - u8 buf[240]; + u8 buf[SC_MAX_EXT_APDU_DATA_SIZE]; size_t buflen = sizeof(buf); int r, err = 1; - int offs; + unsigned long offs; sc_path_t path; sc_file_t *file; @@ -1334,34 +1639,38 @@ static int do_update_binary(int argc, char **argv) return usage(do_update_binary); if (arg_to_path(argv[0], &path, 0) != 0) return usage(do_update_binary); - offs = strtol(argv[1],NULL,10); - - printf("in: %i; %s\n", offs, argv[2]); + offs = strtol(argv[1], NULL, 10); r = parse_string_or_hexdata(argv[2], buf, &buflen); if (r < 0) { - printf("unable to parse data\n"); + fprintf(stderr, "Unable to parse input data: %s\n", sc_strerror(r)); return -1; } - r = sc_select_file(card, &path, &file); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, &path, &file); + sc_unlock(card); if (r) { - check_ret(r, SC_AC_OP_SELECT, "unable to select file", current_file); + check_ret(r, SC_AC_OP_SELECT, "Unable to select file", current_file); return -1; } - if (file->ef_structure != SC_FILE_EF_TRANSPARENT) { - printf("EF structure should be SC_FILE_EF_TRANSPARENT\n"); + if (file->ef_structure != SC_FILE_EF_TRANSPARENT) { + fprintf(stderr, "EF structure should be SC_FILE_EF_TRANSPARENT\n"); goto err; } - r = sc_update_binary(card, offs, buf, buflen, 0); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_update_binary(card, (unsigned)offs, buf, buflen, 0); + sc_unlock(card); if (r < 0) { - printf("Cannot update %04X; return %i\n", file->id, r); + fprintf(stderr, "Cannot update %04X: %s\n", file->id, sc_strerror(r)); goto err; } - printf("Total of %d bytes written to %04X at %i offset.\n", + printf("Total of %d bytes written to %04X at offset %lu.\n", r, file->id, offs); err = 0; @@ -1373,10 +1682,10 @@ static int do_update_binary(int argc, char **argv) static int do_update_record(int argc, char **argv) { - u8 buf[240]; + u8 buf[SC_MAX_EXT_APDU_DATA_SIZE]; size_t buflen; - int r, i, err = 1; - int rec, offs; + int r, err = 1; + size_t rec, offs; sc_path_t path; sc_file_t *file; @@ -1384,46 +1693,61 @@ static int do_update_record(int argc, char **argv) return usage(do_update_record); if (arg_to_path(argv[0], &path, 0) != 0) return usage(do_update_record); - rec = strtol(argv[1],NULL,10); - offs = strtol(argv[2],NULL,10); - - printf("in: %i; %i; %s\n", rec, offs, argv[3]); + rec = strtol(argv[1], NULL, 10); + offs = strtol(argv[2], NULL, 10); - r = sc_select_file(card, &path, &file); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, &path, &file); + sc_unlock(card); if (r) { - check_ret(r, SC_AC_OP_SELECT, "unable to select file", current_file); + check_ret(r, SC_AC_OP_SELECT, "Unable to select file", current_file); return -1; } - if (file->ef_structure != SC_FILE_EF_LINEAR_VARIABLE) { - printf("EF structure should be SC_FILE_EF_LINEAR_VARIABLE\n"); + if (file->ef_structure != SC_FILE_EF_LINEAR_VARIABLE) { + fprintf(stderr, "EF structure should be SC_FILE_EF_LINEAR_VARIABLE\n"); goto err; - } else if (rec < 1 || rec > file->record_count) { - printf("Invalid record number %i\n", rec); + } + + if (rec < 1 || rec > file->record_count) { + fprintf(stderr, "Invalid record number %"SC_FORMAT_LEN_SIZE_T"u\n", rec); + goto err; + } + + r = sc_read_record(card, (unsigned)rec, 0, buf, sizeof(buf), SC_RECORD_BY_REC_NR); + if (r < 0) { + fprintf(stderr, "Cannot read record %"SC_FORMAT_LEN_SIZE_T"u of %04X: %s\n", + rec, file->id, sc_strerror(r)); goto err; } - r = sc_read_record(card, rec, buf, sizeof(buf), SC_RECORD_BY_REC_NR); - if (r<0) { - printf("Cannot read record %i; return %i\n", rec, r); - goto err;; + /* do not allow gaps between data read and added */ + if (offs >= (size_t) r) { + fprintf(stderr, "Offset too large.\n"); + goto err; } buflen = sizeof(buf) - offs; - i = parse_string_or_hexdata(argv[3], buf + offs, &buflen); - if (!i) { - printf("unable to parse data\n"); + r = parse_string_or_hexdata(argv[3], buf + offs, &buflen); + if (r < 0) { + fprintf(stderr, "Unable to parse input data: %s.\n", sc_strerror(r)); goto err; } - r = sc_update_record(card, rec, buf, r, SC_RECORD_BY_REC_NR); - if (r<0) { - printf("Cannot update record %i; return %i\n", rec, r); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_update_record(card, (unsigned)rec, 0, buf, buflen, SC_RECORD_BY_REC_NR); + sc_unlock(card); + if (r < 0) { + fprintf(stderr, "Cannot update record %"SC_FORMAT_LEN_SIZE_T"u of %04X: %s\n.", + rec, file->id, sc_strerror(r)); goto err; } - printf("Total of %d bytes written to record %i at %i offset.\n", - i, rec, offs); + printf("Total of %d bytes written to %04X's record %"SC_FORMAT_LEN_SIZE_T"u " + "at offset %"SC_FORMAT_LEN_SIZE_T"u.\n", + r, file->id, rec, offs); err = 0; err: @@ -1435,61 +1759,68 @@ static int do_update_record(int argc, char **argv) static int do_put(int argc, char **argv) { - u8 buf[256]; + u8 buf[SC_MAX_EXT_APDU_DATA_SIZE]; int r, err = 1; size_t count = 0; unsigned int idx = 0; sc_path_t path; sc_file_t *file = NULL; const char *filename; - FILE *outf = NULL; + FILE *inf = NULL; + ssize_t sz; if (argc < 1 || argc > 2) return usage(do_put); if (arg_to_path(argv[0], &path, 0) != 0) return usage(do_put); - filename = (argc == 2) ? argv[1] : path_to_filename(&path, '_'); - outf = fopen(filename, "rb"); - if (outf == NULL) { + filename = (argc == 2) ? argv[1] : path_to_filename(&path, '_', 0); + inf = fopen(filename, "rb"); + if (inf == NULL) { perror(filename); goto err; } - r = sc_select_file(card, &path, &file); - if (r) { - check_ret(r, SC_AC_OP_SELECT, "unable to select file", current_file); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, &path, &file); + sc_unlock(card); + if (r || file == NULL) { + check_ret(r, SC_AC_OP_SELECT, "Unable to select file", current_file); goto err; } count = file->size; while (count) { - int c = count > sizeof(buf) ? sizeof(buf) : count; + size_t c = count > sizeof(buf) ? sizeof(buf) : count; - r = fread(buf, 1, c, outf); - if (r < 0) { + sz = fread(buf, 1, c, inf); + if (sz < 0) { perror("fread"); goto err; } - if (r != c) - count = c = r; - r = sc_update_binary(card, idx, buf, c, 0); + if ((size_t)sz != c) + count = c = sz; + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_update_binary(card, idx, buf, c, 0); + sc_unlock(card); if (r < 0) { - check_ret(r, SC_AC_OP_READ, "update failed", file); + check_ret(r, SC_AC_OP_READ, "Update failed", file); goto err; } - if (r != c) { - printf("expecting %d, wrote only %d bytes.\n", c, r); + if ((size_t)r != c) { + fprintf(stderr, "Expecting %zu, wrote only %d bytes.\n", c, r); goto err; } idx += c; count -= c; } - printf("Total of %d bytes written.\n", idx); + printf("Total of %d bytes written to %04X.\n", idx, file->id); err = 0; err: sc_file_free(file); - if (outf) - fclose(outf); + if (inf) + fclose(inf); select_current_path_or_die(); return -err; } @@ -1520,9 +1851,12 @@ static int do_erase(int argc, char **argv) if (argc != 0) return usage(do_erase); - r = sc_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL); + sc_unlock(card); if (r) { - printf("Failed to erase card: %s\n", sc_strerror (r)); + fprintf(stderr, "Failed to erase card: %s\n", sc_strerror(r)); return -1; } return 0; @@ -1530,56 +1864,130 @@ static int do_erase(int argc, char **argv) static int do_random(int argc, char **argv) { - unsigned char buffer[256]; + unsigned char buffer[SC_MAX_EXT_APDU_BUFFER_SIZE] = {0}; int r, count; + const char *filename = NULL; + FILE *outf = NULL; - if (argc != 1) + if (argc < 1 || argc > 2) return usage(do_random); - count = atoi(argv[0]); - if (count < 0 || count > 256) { - printf("Number must be in range 0..256\n"); + + if (count < 0 || (size_t) count > sizeof(buffer)) { + fprintf(stderr, "Number must be in range 0..%"SC_FORMAT_LEN_SIZE_T"u\n", + sizeof(buffer)); return -1; } - r = sc_get_challenge(card, buffer, count); + if (argc == 2) { + filename = argv[1]; + + if (interactive && strcmp(filename, "-") == 0) { + fprintf(stderr, "Binary writing to stdout not supported in interactive mode\n"); + return -1; + } + + outf = (strcmp(filename, "-") == 0) + ? stdout + : fopen(filename, "wb"); + if (outf == NULL) { + perror(filename); + return -1; + } +#ifdef _WIN32 + if (outf == stdout) + _setmode(fileno(stdout), _O_BINARY); +#endif + } + + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_get_challenge(card, buffer, count); + sc_unlock(card); if (r < 0) { - printf("Failed to get random bytes: %s\n", sc_strerror(r)); + fprintf(stderr, "Failed to get random bytes: %s\n", sc_strerror(r)); + if (argc == 2) { + fclose(outf); + } return -1; } - util_hex_dump_asc(stdout, buffer, count, 0); + if (argc == 2) { + /* outf is guaranteed to be non-NULL */ + size_t written = 0; + if (count > 0) + written = fwrite(buffer, 1, count, outf); + + if (written < (size_t) count) + perror(filename); + + if (outf == stdout) { +#ifdef _WIN32 + _setmode(fileno(stdout), _O_TEXT); +#endif + printf("\nTotal of %"SC_FORMAT_LEN_SIZE_T"u random bytes written\n", written); + } + else + printf("Total of %"SC_FORMAT_LEN_SIZE_T"u random bytes written to %s\n", + written, filename); + + fclose(outf); + + if (written < (size_t) count) + return -1; + } + else { + util_hex_dump_asc(stdout, buffer, count, 0); + } return 0; } static int do_get_data(int argc, char **argv) { - unsigned char buffer[256]; + u8 id[2] = { 0x00, 0x00 }; unsigned int tag; + u8 buffer[SC_MAX_EXT_APDU_RESP_SIZE]; FILE *fp; int r; if (argc != 1 && argc != 2) return usage(do_get_data); + if (arg_to_fid(argv[0], id) != 0) + return usage(do_get_data); + tag = id[0] << 8 | id[1]; - tag = strtoul(argv[0], NULL, 16); - r = sc_get_data(card, tag, buffer, sizeof(buffer)); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_get_data(card, tag, buffer, sizeof(buffer)); + sc_unlock(card); if (r < 0) { - printf("Failed to get data object: %s\n", sc_strerror(r)); + fprintf(stderr, "Failed to get DO %04X: %s\n", tag, sc_strerror(r)); return -1; } if (argc == 2) { - const char *filename = argv[1]; + const char *filename = argv[1]; - if (!(fp = fopen(filename, "w"))) { + fp = (strcmp(filename, "-") == 0) + ? stdout + : fopen(filename, "wb"); + if (fp == NULL) { perror(filename); return -1; } +#ifdef _WIN32 + if (fp == stdout) + _setmode(fileno(stdout), _O_BINARY); +#endif fwrite(buffer, r, 1, fp); - fclose(fp); +#ifdef _WIN32 + if (fp == stdout) + _setmode(fileno(stdout), _O_TEXT); +#endif + if (fp != stdout) + fclose(fp); } else { - printf("Object %04x:\n", tag & 0xFFFF); + printf("Data Object %04X:\n", tag & 0xFFFF); util_hex_dump_asc(stdout, buffer, r, 0); } @@ -1591,29 +1999,34 @@ static int do_get_data(int argc, char **argv) **/ static int do_put_data(int argc, char **argv) { + u8 id[2] = { 0x00, 0x00 }; unsigned int tag; - u8 buf[8192]; + u8 buf[SC_MAX_EXT_APDU_DATA_SIZE]; size_t buflen = sizeof(buf); int r; if (argc != 2) return usage(do_put_data); - /* Extract DO's tag */ - tag = strtoul(argv[0], NULL, 16); + if (arg_to_fid(argv[0], id) != 0) + return usage(do_get_data); + tag = id[0] << 8 | id[1]; /* Extract the new content */ /* buflen is the max length of reception buffer */ r = parse_string_or_hexdata(argv[1], buf, &buflen); if (r < 0) { - printf("unable to parse data\n"); - return -1; + fprintf(stderr, "Error parsing %s: %s\n", argv[1], sc_strerror(r)); + return r; } /* Call OpenSC to do put data */ - r = sc_put_data(card, tag, buf, buflen); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_put_data(card, tag, buf, buflen); + sc_unlock(card); if (r < 0) { - printf("Cannot put data to %04X; return %i\n", tag, r); + fprintf(stderr, "Failed to put data to DO %04X: %s\n", tag, sc_strerror(r)); return -1; } @@ -1625,16 +2038,17 @@ static int do_put_data(int argc, char **argv) static int do_apdu(int argc, char **argv) { sc_apdu_t apdu; - u8 buf[SC_MAX_APDU_BUFFER_SIZE * 2]; - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE * 2]; - size_t len, i; + u8 buf[SC_MAX_EXT_APDU_BUFFER_SIZE] = {0}; + u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; + size_t len = 0, i; int r; if (argc < 1) return usage(do_apdu); - for (i = 0, len = 0; i < (unsigned) argc; i++) { - size_t len0 = strlen(argv[i]); + /* loop over the args and parse them, making sure the result fits into buf[] */ + for (i = 0, len = 0; i < (unsigned) argc && len < sizeof(buf); i++) { + size_t len0 = sizeof(buf) - len; if ((r = parse_string_or_hexdata(argv[i], buf + len, &len0)) < 0) { fprintf(stderr, "error parsing %s: %s\n", argv[i], sc_strerror(r)); @@ -1655,7 +2069,10 @@ static int do_apdu(int argc, char **argv) printf("Sending: "); util_hex_dump(stdout, buf, len, " "); printf("\n"); - r = sc_transmit_apdu(card, &apdu); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_transmit_apdu(card, &apdu); + sc_unlock(card); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; @@ -1667,34 +2084,37 @@ static int do_apdu(int argc, char **argv) r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) - printf("Failure: %s\n", sc_strerror(r)); + fprintf(stderr, "Failure: %s\n", sc_strerror(r)); else printf("Success!\n"); - return 0; } static int do_asn1(int argc, char **argv) { int r, err = 1; + unsigned int offs = 0; + int offsu = 0; /* offset updated, set from argv */ sc_path_t path; sc_file_t *file = NULL; int not_current = 1; - size_t len; - unsigned char *buf = NULL; + u8 buf[SC_MAX_EXT_APDU_DATA_SIZE] = {0}; - if (argc > 1) + if (argc > 3) return usage(do_asn1); /* select file */ if (argc) { if (arg_to_path(argv[0], &path, 0) != 0) { - puts("Invalid file path"); + fprintf(stderr, "Invalid file path\n"); return -1; } - r = sc_select_file(card, &path, &file); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, &path, &file); + sc_unlock(card); if (r) { - check_ret(r, SC_AC_OP_SELECT, "unable to select file", current_file); + check_ret(r, SC_AC_OP_SELECT, "Unable to select file", current_file); goto err; } } else { @@ -1703,42 +2123,91 @@ static int do_asn1(int argc, char **argv) not_current = 0; } if (file->type != SC_FILE_TYPE_WORKING_EF) { - printf("only working EFs may be read\n"); + fprintf(stderr, "Only working EFs may be read\n"); goto err; } /* read */ - if (file->ef_structure != SC_FILE_EF_TRANSPARENT) { - printf("only transparent file type is supported at the moment\n"); - goto err; - } - len = file->size; - buf = calloc(1, len); - if (!buf) { - goto err; - } - r = sc_read_binary(card, 0, buf, len, 0); - if (r < 0) { - check_ret(r, SC_AC_OP_READ, "read failed", file); - goto err; + if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { + size_t size = (file->size > 0) ? file->size : sizeof(buf); + + if (argc > 2) { + fprintf(stderr, "Transparent EFs do not have records\n"); + goto err; + } + + if (argc > 1) { + offs = (unsigned int) strtoul(argv[1], NULL, 10); + offsu = 1; + } + + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_read_binary(card, 0, buf, MIN(size, sizeof(buf)), 0); + else + r = SC_ERROR_READER_LOCKED; + sc_unlock(card); + if (r < 0) { + check_ret(r, SC_AC_OP_READ, "read failed", file); + goto err; + } + if ((size_t) r != file->size) { + fprintf(stderr, "WARNING: expecting %"SC_FORMAT_LEN_SIZE_T"u, got %d bytes.\n", + file->size, r); + /* some cards return a bogus value for file length. + * As long as the actual length is not higher + * than the expected length, continue */ + if ((size_t) r > file->size) + goto err; + } } - if ((size_t)r != len) { - printf("WARNING: expecting %lu, got %d bytes.\n", (unsigned long) len, r); - /* some cards return a bogus value for file length. As - * long as the actual length is not higher than the expected - * length, continue */ - if(r > (signed)len) { + else { /* record-oriented file */ + unsigned int rec = 0; + + if (argc < 2) { + fprintf(stderr, "Record-oriented EFs require parameter record number.\n"); + goto err; + } + + rec = (unsigned int) strtoul(argv[1], NULL, 10); + if (argc > 2) { + offs = (unsigned int) strtoul(argv[2], NULL, 10); + offsu = 1; + } + + if (rec < 1 || rec > file->record_count) { + fprintf(stderr, "Invalid record number %u.\n", rec); + goto err; + } + + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_read_record(card, rec, 0, buf, sizeof(buf), SC_RECORD_BY_REC_NR); + else + r = SC_ERROR_READER_LOCKED; + sc_unlock(card); + if (r < 0) { + check_ret(r, SC_AC_OP_READ, "Read failed", file); goto err; } } - /* asn1 dump */ - sc_asn1_print_tags(buf, r); + /* workaround when the issuer of a card does prefix the EF.ATR payload with 0x80 */ + if (offsu == 0 /* do not apply the workaround if any offset */ + && r >= 1 + && buf[0] == ISO7816_II_CATEGORY_TLV + && path.len >= 4 + && memcmp(path.value, "\x3f\x00\x2f\x01", 4) == 0) + offs++; + /* if offset does not exceed the length read from file/record, ... */ + if (offs <= (unsigned int) r) { + /* ... perform the ASN.1 dump */ + sc_asn1_print_tags(buf + offs, (unsigned int) r - offs); + + err = 0; + } - err = 0; err: - if (buf) - free(buf); if (not_current) { sc_file_free(file); select_current_path_or_die(); @@ -1756,14 +2225,14 @@ static int do_sm(int argc, char **argv) #ifdef ENABLE_SM if (!strcmp(argv[0],"open")) { if (!card->sm_ctx.ops.open) { - printf("Not supported\n"); + fprintf(stderr, "Not supported\n"); return -1; } r = card->sm_ctx.ops.open(card); } else if (!strcmp(argv[0],"close")) { if (!card->sm_ctx.ops.close) { - printf("Not supported\n"); + fprintf(stderr, "Not supported\n"); return -1; } r = card->sm_ctx.ops.close(card); @@ -1774,7 +2243,7 @@ static int do_sm(int argc, char **argv) printf("Success!\n"); } else { - printf("Failure: %s\n", sc_strerror(r)); + fprintf(stderr, "Failure: %s\n", sc_strerror(r)); } return ret; @@ -1784,17 +2253,26 @@ static int do_help(int argc, char **argv) { struct command *cmd; - if (argc) - return usage(do_help); + printf("%s commands:\n", (argc) ? "Matching" : "Supported"); - printf("Supported commands:\n"); for (cmd = cmds; cmd->name; cmd++) { - int len = strlen(cmd->name) + strlen(cmd->args); - printf(" %s %s%*s %s\n", - cmd->name, cmd->args, - (len > 40) ? 0 : (40 - len), "", - cmd->help); + int i; + int match = 0; + + for (i = 0; i < argc; i++) { + if (strncmp(cmd->name, argv[i], strlen(argv[i])) == 0) + match++; + } + if (match || !argc) { + size_t len = strlen(cmd->name) + strlen(cmd->args); + + printf(" %s %s%*s %s\n", + cmd->name, cmd->args, + (len > 40) ? 0 : (int)(40 - len), "", + cmd->help); + } } + return 0; } @@ -1804,62 +2282,70 @@ static int do_quit(int argc, char **argv) return 0; } -static int parse_cmdline(char *in, char **argv, int maxargc) +static int parse_cmdline(char *in, char **argv, int argvsize) { int argc; - for (argc = 0; argc < maxargc; argc++) { + for (argc = 0; argc < argvsize-1; argc++) { in += strspn(in, " \t\n"); - if (*in == '\0') + + if (*in == '\0') { /* end of input reached */ + argv[argc] = NULL; return argc; - if (*in == '"') { - /* Parse quoted string */ + } + if (*in == '"') { /* double-quoted string */ argv[argc] = in++; in += strcspn(in, "\""); - if (*in++ != '"') + if (*in++ != '"') { /* error: unbalanced quote */ + argv[0] = NULL; return 0; - } else { - /* White space delimited word */ + } + } + else { /* white-space delimited word */ argv[argc] = in; in += strcspn(in, " \t\n"); } if (*in != '\0') *in++ = '\0'; } - return argc; + + /* error: too many arguments - argv[] exhausted */ + argv[0] = NULL; + return 0; } static char *read_cmdline(FILE *script, char *prompt) { - static char buf[256]; - static int initialized; - static int interactive; + static char buf[SC_MAX_EXT_APDU_BUFFER_SIZE]; - if (!initialized) { - initialized = 1; - interactive = isatty(fileno(script)); + if (interactive) { #ifdef ENABLE_READLINE - if (interactive) + static int initialized; + + if (!initialized) { + initialized = 1; using_history(); -#endif - } -#ifdef ENABLE_READLINE - if (interactive) { + } + char *line = readline(prompt); - if (line && strlen(line) > 2 ) + + /* add line to history if longer than 2 characters */ + if (line != NULL && strlen(line) > 2) add_history(line); + + /* return in interactive case with readline */ return line; - } -#endif - /* Either we don't have readline or we are not running - interactively */ -#ifndef ENABLE_READLINE - if (interactive) - printf("%s", prompt); +#else + sc_color_fprintf(SC_COLOR_FG_BLUE|SC_COLOR_BOLD, NULL, stdout, "%s", prompt); #endif + } + + /* either we don't have readline or we are not running interactively */ fflush(stdout); - if (fgets(buf, sizeof(buf), script) == NULL) + if (fgets(buf, sizeof(buf), script) == NULL) { + fputc('\n', stdout); return NULL; + } if (strlen(buf) == 0) return NULL; if (buf[strlen(buf)-1] == '\n') @@ -1867,15 +2353,12 @@ static char *read_cmdline(FILE *script, char *prompt) return buf; } -int main(int argc, char * const argv[]) +int main(int argc, char *argv[]) { int r, c, long_optind = 0, err = 0; - char *line; - int cargc; - char *cargv[260]; sc_context_param_t ctx_param; int lcycle = SC_CARDCTRL_LIFECYCLE_ADMIN; - FILE *script = stdin; + FILE *script; printf("OpenSC Explorer version %s\n", sc_get_version()); @@ -1907,6 +2390,9 @@ int main(int argc, char * const argv[]) memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.ver = 0; ctx_param.app_name = app_name; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; r = sc_context_create(&ctx, &ctx_param); if (r) { @@ -1922,6 +2408,12 @@ int main(int argc, char * const argv[]) } if (opt_driver != NULL) { + /* special card driver value "?" means: list available drivers */ + if (strncmp("?", opt_driver, sizeof("?")) == 0) { + err = util_list_card_drivers(ctx); + goto end; + } + err = sc_set_card_driver(ctx, opt_driver); if (err) { fprintf(stderr, "Driver '%s' not found!\n", opt_driver); @@ -1930,33 +2422,43 @@ int main(int argc, char * const argv[]) } } - err = util_connect_card(ctx, &card, opt_reader, opt_wait, 0); + err = util_connect_card_ex(ctx, &card, opt_reader, opt_wait, 0); if (err) goto end; if (opt_startfile) { if(*opt_startfile) { - char startpath[1024]; + char startpath[SC_MAX_PATH_STRING_SIZE * 2]; char *args[] = { startpath }; - strncpy(startpath, opt_startfile, sizeof(startpath)-1); + if (strlcpy(startpath, opt_startfile, sizeof(startpath)) >= sizeof(startpath)) { + fprintf(stderr, "unable to select file %s: name too long\n", + opt_startfile); + die(1); + } r = do_cd(1, args); if (r) { - printf("unable to select file %s: %s\n", + fprintf(stderr, "unable to select file %s: %s\n", opt_startfile, sc_strerror(r)); - return -1; + die(1); } } } else { sc_format_path("3F00", ¤t_path); - r = sc_select_file(card, ¤t_path, ¤t_file); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, ¤t_path, ¤t_file); + sc_unlock(card); if (r) { - printf("unable to select MF: %s\n", sc_strerror(r)); - return 1; + fprintf(stderr, "unable to select MF: %s\n", sc_strerror(r)); + die(1); } } - r = sc_card_ctl(card, SC_CARDCTL_LIFECYCLE_SET, &lcycle); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_card_ctl(card, SC_CARDCTL_LIFECYCLE_SET, &lcycle); + sc_unlock(card); if (r && r != SC_ERROR_NOT_SUPPORTED) printf("unable to change lifecycle: %s\n", sc_strerror(r)); @@ -1965,9 +2467,11 @@ int main(int argc, char * const argv[]) util_print_usage_and_die(app_name, options, option_help, "[SCRIPT]"); break; case 0: + interactive = 1; script = stdin; break; case 1: + interactive = 0; if (strcmp(argv[optind], "-") == 0) { script = stdin; } @@ -1978,23 +2482,31 @@ int main(int argc, char * const argv[]) } while (!feof(script)) { + char *line; + int cargc; + char *cargv[260]; + int multiple = 0; struct command *cmd; char prompt[3*SC_MAX_PATH_STRING_SIZE]; - sprintf(prompt, "OpenSC [%s]> ", path_to_filename(¤t_path, '/')); + sprintf(prompt, "OpenSC [%s]> ", path_to_filename(¤t_path, '/', 0)); line = read_cmdline(script, prompt); if (line == NULL) break; + cargc = parse_cmdline(line, cargv, DIM(cargv)); if ((cargc < 1) || (*cargv[0] == '#')) continue; - for (r=cargc; r < (int)DIM(cargv); r++) - cargv[r] = ""; - cmd = ambiguous_match(cmds, cargv[0]); + + cmd = ambiguous_match(cmds, cargv[0], &multiple); if (cmd == NULL) { - do_help(0, NULL); + fprintf(stderr, "%s command: %s\n", + (multiple) ? "Ambiguous" : "Unknown", cargv[0]); + if (interactive) + do_help((multiple) ? 1 : 0, cargv); + err = -1; } else { - cmd->func(cargc-1, cargv+1); + err = cmd->func(cargc-1, cargv+1); } } end: diff --git a/src/tools/opensc-notify-cmdline.c b/src/tools/opensc-notify-cmdline.c new file mode 100644 index 0000000000..7f148927c0 --- /dev/null +++ b/src/tools/opensc-notify-cmdline.c @@ -0,0 +1,655 @@ +/* + File autogenerated by gengetopt version 2.23 + generated with the following command: + /opt/homebrew/bin/gengetopt --file-name=opensc-notify-cmdline --output-dir=. + + The developers of gengetopt consider the fixed text that goes in all + gengetopt output files to be in the public domain: + we make no copyright claims on it. +*/ + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#ifndef FIX_UNUSED +#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ +#endif + +#include + +#include "opensc-notify-cmdline.h" + +const char *gengetopt_args_info_purpose = ""; + +const char *gengetopt_args_info_usage = "Usage: opensc-notify [OPTION]..."; + +const char *gengetopt_args_info_versiontext = ""; + +const char *gengetopt_args_info_description = "If no arguments are given, monitor smart card events and send the appropriate\nnotification."; + +const char *gengetopt_args_info_help[] = { + " -h, --help Print help and exit", + " -V, --version Print version and exit", + "\n Mode: customized\n Send customized notifications.", + " -t, --title[=STRING] Title of the notification", + " -m, --message[=STRING] Main text of the notification", + "\n Mode: standard\n Manually send standard notifications.", + " -I, --notify-card-inserted See notify_card_inserted in opensc.conf\n (default=off)", + " -R, --notify-card-removed See notify_card_removed in opensc.conf\n (default=off)", + " -G, --notify-pin-good See notify_pin_good in opensc.conf (default=off)", + " -B, --notify-pin-bad See notify_pin_bad in opensc.conf (default=off)", + "\nReport bugs to https://github.com/OpenSC/OpenSC/issues\n\nWritten by Frank Morgner ", + 0 +}; + +typedef enum {ARG_NO + , ARG_FLAG + , ARG_STRING +} cmdline_parser_arg_type; + +static +void clear_given (struct gengetopt_args_info *args_info); +static +void clear_args (struct gengetopt_args_info *args_info); + +static int +cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params, const char *additional_error); + + +static char * +gengetopt_strdup (const char *s); + +static +void clear_given (struct gengetopt_args_info *args_info) +{ + args_info->help_given = 0 ; + args_info->version_given = 0 ; + args_info->title_given = 0 ; + args_info->message_given = 0 ; + args_info->notify_card_inserted_given = 0 ; + args_info->notify_card_removed_given = 0 ; + args_info->notify_pin_good_given = 0 ; + args_info->notify_pin_bad_given = 0 ; + args_info->customized_mode_counter = 0 ; + args_info->daemon_mode_counter = 0 ; + args_info->standard_mode_counter = 0 ; +} + +static +void clear_args (struct gengetopt_args_info *args_info) +{ + FIX_UNUSED (args_info); + args_info->title_arg = NULL; + args_info->title_orig = NULL; + args_info->message_arg = NULL; + args_info->message_orig = NULL; + args_info->notify_card_inserted_flag = 0; + args_info->notify_card_removed_flag = 0; + args_info->notify_pin_good_flag = 0; + args_info->notify_pin_bad_flag = 0; + +} + +static +void init_args_info(struct gengetopt_args_info *args_info) +{ + + + args_info->help_help = gengetopt_args_info_help[0] ; + args_info->version_help = gengetopt_args_info_help[1] ; + args_info->title_help = gengetopt_args_info_help[3] ; + args_info->message_help = gengetopt_args_info_help[4] ; + args_info->notify_card_inserted_help = gengetopt_args_info_help[6] ; + args_info->notify_card_removed_help = gengetopt_args_info_help[7] ; + args_info->notify_pin_good_help = gengetopt_args_info_help[8] ; + args_info->notify_pin_bad_help = gengetopt_args_info_help[9] ; + +} + +void +cmdline_parser_print_version (void) +{ + printf ("%s %s\n", + (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE), + CMDLINE_PARSER_VERSION); + + if (strlen(gengetopt_args_info_versiontext) > 0) + printf("\n%s\n", gengetopt_args_info_versiontext); +} + +static void print_help_common(void) +{ + size_t len_purpose = strlen(gengetopt_args_info_purpose); + size_t len_usage = strlen(gengetopt_args_info_usage); + + if (len_usage > 0) { + printf("%s\n", gengetopt_args_info_usage); + } + if (len_purpose > 0) { + printf("%s\n", gengetopt_args_info_purpose); + } + + if (len_usage || len_purpose) { + printf("\n"); + } + + if (strlen(gengetopt_args_info_description) > 0) { + printf("%s\n\n", gengetopt_args_info_description); + } +} + +void +cmdline_parser_print_help (void) +{ + int i = 0; + print_help_common(); + while (gengetopt_args_info_help[i]) + printf("%s\n", gengetopt_args_info_help[i++]); +} + +void +cmdline_parser_init (struct gengetopt_args_info *args_info) +{ + clear_given (args_info); + clear_args (args_info); + init_args_info (args_info); +} + +void +cmdline_parser_params_init(struct cmdline_parser_params *params) +{ + if (params) + { + params->override = 0; + params->initialize = 1; + params->check_required = 1; + params->check_ambiguity = 0; + params->print_errors = 1; + } +} + +struct cmdline_parser_params * +cmdline_parser_params_create(void) +{ + struct cmdline_parser_params *params = + (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params)); + cmdline_parser_params_init(params); + return params; +} + +static void +free_string_field (char **s) +{ + if (*s) + { + free (*s); + *s = 0; + } +} + + +static void +cmdline_parser_release (struct gengetopt_args_info *args_info) +{ + + free_string_field (&(args_info->title_arg)); + free_string_field (&(args_info->title_orig)); + free_string_field (&(args_info->message_arg)); + free_string_field (&(args_info->message_orig)); + + + + clear_given (args_info); +} + + +static void +write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[]) +{ + FIX_UNUSED (values); + if (arg) { + fprintf(outfile, "%s=\"%s\"\n", opt, arg); + } else { + fprintf(outfile, "%s\n", opt); + } +} + + +int +cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) +{ + int i = 0; + + if (!outfile) + { + fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE); + return EXIT_FAILURE; + } + + if (args_info->help_given) + write_into_file(outfile, "help", 0, 0 ); + if (args_info->version_given) + write_into_file(outfile, "version", 0, 0 ); + if (args_info->title_given) + write_into_file(outfile, "title", args_info->title_orig, 0); + if (args_info->message_given) + write_into_file(outfile, "message", args_info->message_orig, 0); + if (args_info->notify_card_inserted_given) + write_into_file(outfile, "notify-card-inserted", 0, 0 ); + if (args_info->notify_card_removed_given) + write_into_file(outfile, "notify-card-removed", 0, 0 ); + if (args_info->notify_pin_good_given) + write_into_file(outfile, "notify-pin-good", 0, 0 ); + if (args_info->notify_pin_bad_given) + write_into_file(outfile, "notify-pin-bad", 0, 0 ); + + + i = EXIT_SUCCESS; + return i; +} + +int +cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) +{ + FILE *outfile; + int i = 0; + + outfile = fopen(filename, "w"); + + if (!outfile) + { + fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); + return EXIT_FAILURE; + } + + i = cmdline_parser_dump(outfile, args_info); + fclose (outfile); + + return i; +} + +void +cmdline_parser_free (struct gengetopt_args_info *args_info) +{ + cmdline_parser_release (args_info); +} + +/** @brief replacement of strdup, which is not standard */ +char * +gengetopt_strdup (const char *s) +{ + char *result = 0; + if (!s) + return result; + + result = (char*)malloc(strlen(s) + 1); + if (result == (char*)0) + return (char*)0; + strcpy(result, s); + return result; +} + +int +cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info) +{ + return cmdline_parser2 (argc, argv, args_info, 0, 1, 1); +} + +int +cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params) +{ + int result; + result = cmdline_parser_internal (argc, argv, args_info, params, 0); + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) +{ + int result; + struct cmdline_parser_params params; + + params.override = override; + params.initialize = initialize; + params.check_required = check_required; + params.check_ambiguity = 0; + params.print_errors = 1; + + result = cmdline_parser_internal (argc, argv, args_info, ¶ms, 0); + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name) +{ + FIX_UNUSED (args_info); + FIX_UNUSED (prog_name); + return EXIT_SUCCESS; +} + + +static char *package_name = 0; + +/** + * @brief updates an option + * @param field the generic pointer to the field to update + * @param orig_field the pointer to the orig field + * @param field_given the pointer to the number of occurrence of this option + * @param prev_given the pointer to the number of occurrence already seen + * @param value the argument for this option (if null no arg was specified) + * @param possible_values the possible values for this option (if specified) + * @param default_value the default value (in case the option only accepts fixed values) + * @param arg_type the type of this option + * @param check_ambiguity @see cmdline_parser_params.check_ambiguity + * @param override @see cmdline_parser_params.override + * @param no_free whether to free a possible previous value + * @param multiple_option whether this is a multiple option + * @param long_opt the corresponding long option + * @param short_opt the corresponding short option (or '-' if none) + * @param additional_error possible further error specification + */ +static +int update_arg(void *field, char **orig_field, + unsigned int *field_given, unsigned int *prev_given, + char *value, const char *possible_values[], + const char *default_value, + cmdline_parser_arg_type arg_type, + int check_ambiguity, int override, + int no_free, int multiple_option, + const char *long_opt, char short_opt, + const char *additional_error) +{ + char *stop_char = 0; + const char *val = value; + int found; + char **string_field; + FIX_UNUSED (field); + + stop_char = 0; + found = 0; + + if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given))) + { + if (short_opt != '-') + fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", + package_name, long_opt, short_opt, + (additional_error ? additional_error : "")); + else + fprintf (stderr, "%s: `--%s' option given more than once%s\n", + package_name, long_opt, + (additional_error ? additional_error : "")); + return 1; /* failure */ + } + + FIX_UNUSED (default_value); + + if (field_given && *field_given && ! override) + return 0; + if (prev_given) + (*prev_given)++; + if (field_given) + (*field_given)++; + if (possible_values) + val = possible_values[found]; + + switch(arg_type) { + case ARG_FLAG: + *((int *)field) = !*((int *)field); + break; + case ARG_STRING: + if (val) { + string_field = (char **)field; + if (!no_free && *string_field) + free (*string_field); /* free previous string */ + *string_field = gengetopt_strdup (val); + } + break; + default: + break; + }; + + FIX_UNUSED(stop_char); + + /* store the original value */ + switch(arg_type) { + case ARG_NO: + case ARG_FLAG: + break; + default: + if (value && orig_field) { + if (no_free) { + *orig_field = value; + } else { + if (*orig_field) + free (*orig_field); /* free previous string */ + *orig_field = gengetopt_strdup (value); + } + } + }; + + return 0; /* OK */ +} + + +static int check_modes( + int given1[], const char *options1[], + int given2[], const char *options2[]) +{ + int i = 0, j = 0, errors = 0; + + while (given1[i] >= 0) { + if (given1[i]) { + while (given2[j] >= 0) { + if (given2[j]) { + ++errors; + fprintf(stderr, "%s: option %s conflicts with option %s\n", + package_name, options1[i], options2[j]); + } + ++j; + } + } + ++i; + } + + return errors; +} + +int +cmdline_parser_internal ( + int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params, const char *additional_error) +{ + int c; /* Character of the parsed option. */ + + int error_occurred = 0; + struct gengetopt_args_info local_args_info; + + int override; + int initialize; + int check_required; + int check_ambiguity; + + package_name = argv[0]; + + /* TODO: Why is this here? It is not used anywhere. */ + override = params->override; + FIX_UNUSED(override); + + initialize = params->initialize; + check_required = params->check_required; + + /* TODO: Why is this here? It is not used anywhere. */ + check_ambiguity = params->check_ambiguity; + FIX_UNUSED(check_ambiguity); + + if (initialize) + cmdline_parser_init (args_info); + + cmdline_parser_init (&local_args_info); + + optarg = 0; + optind = 0; + opterr = params->print_errors; + optopt = '?'; + + while (1) + { + int option_index = 0; + + static struct option long_options[] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { "title", 2, NULL, 't' }, + { "message", 2, NULL, 'm' }, + { "notify-card-inserted", 0, NULL, 'I' }, + { "notify-card-removed", 0, NULL, 'R' }, + { "notify-pin-good", 0, NULL, 'G' }, + { "notify-pin-bad", 0, NULL, 'B' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long (argc, argv, "hVt::m::IRGB", long_options, &option_index); + + if (c == -1) break; /* Exit from `while (1)' loop. */ + + switch (c) + { + case 'h': /* Print help and exit. */ + cmdline_parser_print_help (); + cmdline_parser_free (&local_args_info); + exit (EXIT_SUCCESS); + + case 'V': /* Print version and exit. */ + cmdline_parser_print_version (); + cmdline_parser_free (&local_args_info); + exit (EXIT_SUCCESS); + + case 't': /* Title of the notification. */ + args_info->customized_mode_counter += 1; + + + if (update_arg( (void *)&(args_info->title_arg), + &(args_info->title_orig), &(args_info->title_given), + &(local_args_info.title_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "title", 't', + additional_error)) + goto failure; + + break; + case 'm': /* Main text of the notification. */ + args_info->customized_mode_counter += 1; + + + if (update_arg( (void *)&(args_info->message_arg), + &(args_info->message_orig), &(args_info->message_given), + &(local_args_info.message_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "message", 'm', + additional_error)) + goto failure; + + break; + case 'I': /* See notify_card_inserted in opensc.conf. */ + args_info->standard_mode_counter += 1; + + + if (update_arg((void *)&(args_info->notify_card_inserted_flag), 0, &(args_info->notify_card_inserted_given), + &(local_args_info.notify_card_inserted_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "notify-card-inserted", 'I', + additional_error)) + goto failure; + + break; + case 'R': /* See notify_card_removed in opensc.conf. */ + args_info->standard_mode_counter += 1; + + + if (update_arg((void *)&(args_info->notify_card_removed_flag), 0, &(args_info->notify_card_removed_given), + &(local_args_info.notify_card_removed_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "notify-card-removed", 'R', + additional_error)) + goto failure; + + break; + case 'G': /* See notify_pin_good in opensc.conf. */ + args_info->standard_mode_counter += 1; + + + if (update_arg((void *)&(args_info->notify_pin_good_flag), 0, &(args_info->notify_pin_good_given), + &(local_args_info.notify_pin_good_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "notify-pin-good", 'G', + additional_error)) + goto failure; + + break; + case 'B': /* See notify_pin_bad in opensc.conf. */ + args_info->standard_mode_counter += 1; + + + if (update_arg((void *)&(args_info->notify_pin_bad_flag), 0, &(args_info->notify_pin_bad_given), + &(local_args_info.notify_pin_bad_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "notify-pin-bad", 'B', + additional_error)) + goto failure; + + break; + + case 0: /* Long option with no short option */ + case '?': /* Invalid option. */ + /* `getopt_long' already printed an error message. */ + goto failure; + + default: /* bug: option not considered. */ + fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); + abort (); + } /* switch */ + } /* while */ + + + + if (args_info->customized_mode_counter && args_info->standard_mode_counter) { + int customized_given[] = {args_info->title_given, args_info->message_given, -1}; + const char *customized_desc[] = {"--title", "--message", 0}; + int standard_given[] = {args_info->notify_card_inserted_given, args_info->notify_card_removed_given, args_info->notify_pin_good_given, args_info->notify_pin_bad_given, -1}; + const char *standard_desc[] = {"--notify-card-inserted", "--notify-card-removed", "--notify-pin-good", "--notify-pin-bad", 0}; + error_occurred += check_modes(customized_given, customized_desc, standard_given, standard_desc); + } + + FIX_UNUSED(check_required); + + cmdline_parser_release (&local_args_info); + + if ( error_occurred ) + return (EXIT_FAILURE); + + return 0; + +failure: + + cmdline_parser_release (&local_args_info); + return (EXIT_FAILURE); +} +/* vim: set ft=c noet ts=8 sts=8 sw=8 tw=80 nojs spell : */ diff --git a/src/tools/opensc-notify-cmdline.h b/src/tools/opensc-notify-cmdline.h new file mode 100644 index 0000000000..e36be178d9 --- /dev/null +++ b/src/tools/opensc-notify-cmdline.h @@ -0,0 +1,196 @@ +/** @file opensc-notify-cmdline.h + * @brief The header file for the command line option parser + * generated by GNU Gengetopt version 2.23 + * http://www.gnu.org/software/gengetopt. + * DO NOT modify this file, since it can be overwritten + * @author GNU Gengetopt */ + +#ifndef OPENSC_NOTIFY_CMDLINE_H +#define OPENSC_NOTIFY_CMDLINE_H + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include /* for FILE */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef CMDLINE_PARSER_PACKAGE +/** @brief the program name (used for printing errors) */ +#define CMDLINE_PARSER_PACKAGE "opensc-notify" +#endif + +#ifndef CMDLINE_PARSER_PACKAGE_NAME +/** @brief the complete program name (used for help and version) */ +#define CMDLINE_PARSER_PACKAGE_NAME "opensc-notify" +#endif + +#ifndef CMDLINE_PARSER_VERSION +/** @brief the program version */ +#define CMDLINE_PARSER_VERSION VERSION +#endif + +/** @brief Where the command line options are stored */ +struct gengetopt_args_info +{ + const char *help_help; /**< @brief Print help and exit help description. */ + const char *version_help; /**< @brief Print version and exit help description. */ + char * title_arg; /**< @brief Title of the notification. */ + char * title_orig; /**< @brief Title of the notification original value given at command line. */ + const char *title_help; /**< @brief Title of the notification help description. */ + char * message_arg; /**< @brief Main text of the notification. */ + char * message_orig; /**< @brief Main text of the notification original value given at command line. */ + const char *message_help; /**< @brief Main text of the notification help description. */ + int notify_card_inserted_flag; /**< @brief See notify_card_inserted in opensc.conf (default=off). */ + const char *notify_card_inserted_help; /**< @brief See notify_card_inserted in opensc.conf help description. */ + int notify_card_removed_flag; /**< @brief See notify_card_removed in opensc.conf (default=off). */ + const char *notify_card_removed_help; /**< @brief See notify_card_removed in opensc.conf help description. */ + int notify_pin_good_flag; /**< @brief See notify_pin_good in opensc.conf (default=off). */ + const char *notify_pin_good_help; /**< @brief See notify_pin_good in opensc.conf help description. */ + int notify_pin_bad_flag; /**< @brief See notify_pin_bad in opensc.conf (default=off). */ + const char *notify_pin_bad_help; /**< @brief See notify_pin_bad in opensc.conf help description. */ + + unsigned int help_given ; /**< @brief Whether help was given. */ + unsigned int version_given ; /**< @brief Whether version was given. */ + unsigned int title_given ; /**< @brief Whether title was given. */ + unsigned int message_given ; /**< @brief Whether message was given. */ + unsigned int notify_card_inserted_given ; /**< @brief Whether notify-card-inserted was given. */ + unsigned int notify_card_removed_given ; /**< @brief Whether notify-card-removed was given. */ + unsigned int notify_pin_good_given ; /**< @brief Whether notify-pin-good was given. */ + unsigned int notify_pin_bad_given ; /**< @brief Whether notify-pin-bad was given. */ + + int customized_mode_counter; /**< @brief Counter for mode customized */ + int daemon_mode_counter; /**< @brief Counter for mode daemon */ + int standard_mode_counter; /**< @brief Counter for mode standard */ +} ; + +/** @brief The additional parameters to pass to parser functions */ +struct cmdline_parser_params +{ + int override; /**< @brief whether to override possibly already present options (default 0) */ + int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */ + int check_required; /**< @brief whether to check that all required options were provided (default 1) */ + int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */ + int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */ +} ; + +/** @brief the purpose string of the program */ +extern const char *gengetopt_args_info_purpose; +/** @brief the usage string of the program */ +extern const char *gengetopt_args_info_usage; +/** @brief the description string of the program */ +extern const char *gengetopt_args_info_description; +/** @brief all the lines making the help output */ +extern const char *gengetopt_args_info_help[]; + +/** + * The command line parser + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser (int argc, char **argv, + struct gengetopt_args_info *args_info); + +/** + * The command line parser (version with additional parameters - deprecated) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param override whether to override possibly already present options + * @param initialize whether to initialize the option structure my_args_info + * @param check_required whether to check that all required options were provided + * @return 0 if everything went fine, NON 0 if an error took place + * @deprecated use cmdline_parser_ext() instead + */ +int cmdline_parser2 (int argc, char **argv, + struct gengetopt_args_info *args_info, + int override, int initialize, int check_required); + +/** + * The command line parser (version with additional parameters) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param params additional parameters for the parser + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_ext (int argc, char **argv, + struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params); + +/** + * Save the contents of the option struct into an already open FILE stream. + * @param outfile the stream where to dump options + * @param args_info the option struct to dump + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_dump(FILE *outfile, + struct gengetopt_args_info *args_info); + +/** + * Save the contents of the option struct into a (text) file. + * This file can be read by the config file parser (if generated by gengetopt) + * @param filename the file where to save + * @param args_info the option struct to save + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_file_save(const char *filename, + struct gengetopt_args_info *args_info); + +/** + * Print the help + */ +void cmdline_parser_print_help(void); +/** + * Print the version + */ +void cmdline_parser_print_version(void); + +/** + * Initializes all the fields a cmdline_parser_params structure + * to their default values + * @param params the structure to initialize + */ +void cmdline_parser_params_init(struct cmdline_parser_params *params); + +/** + * Allocates dynamically a cmdline_parser_params structure and initializes + * all its fields to their default values + * @return the created and initialized cmdline_parser_params structure + */ +struct cmdline_parser_params *cmdline_parser_params_create(void); + +/** + * Initializes the passed gengetopt_args_info structure's fields + * (also set default values for options that have a default) + * @param args_info the structure to initialize + */ +void cmdline_parser_init (struct gengetopt_args_info *args_info); +/** + * Deallocates the string fields of the gengetopt_args_info structure + * (but does not deallocate the structure itself) + * @param args_info the structure to deallocate + */ +void cmdline_parser_free (struct gengetopt_args_info *args_info); + +/** + * Checks that all the required options were specified + * @param args_info the structure to check + * @param prog_name the name of the program that will be used to print + * possible errors + * @return + */ +int cmdline_parser_required (struct gengetopt_args_info *args_info, + const char *prog_name); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* OPENSC_NOTIFY_CMDLINE_H */ diff --git a/src/tools/opensc-notify.c b/src/tools/opensc-notify.c new file mode 100644 index 0000000000..7e72e91506 --- /dev/null +++ b/src/tools/opensc-notify.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2017 Frank Morgner + * + * This file is part of OpenSC. + * + * 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 + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "libopensc/log.h" +#include "ui/notify.h" +#include +#include +#include + +static int run_daemon = 0; +static struct sc_context *ctx = NULL; + +#ifndef _WIN32 +#include + +void Sleep(unsigned int Milliseconds) +{ + struct timespec req, rem; + + if (Milliseconds > 999) { + req.tv_sec = Milliseconds / 1000; /* Must be Non-Negative */ + req.tv_nsec = (Milliseconds - (req.tv_sec * 1000)) * 1000000; /* Must be in range of 0 to 999999999 */ + } else { + req.tv_sec = 0; /* Must be Non-Negative */ + req.tv_nsec = Milliseconds * 1000000; /* Must be in range of 0 to 999999999 */ + } + + nanosleep(&req , &rem); +} +#endif + +void notify_daemon() +{ + int r; + const unsigned int event_mask = SC_EVENT_CARD_EVENTS|SC_EVENT_READER_EVENTS; + unsigned int event; + struct sc_reader *event_reader = NULL; + void *reader_states = NULL; +#ifndef __APPLE__ + /* timeout adjusted to the maximum response time for WM_CLOSE in case + * canceling doesn't work */ + const int timeout = 20000; +#else + /* lower timeout, because Apple doesn't support hotplug events */ + const int timeout = 2000; +#endif + + r = sc_establish_context(&ctx, "opensc-notify"); + if (r < 0 || !ctx) { + fprintf(stderr, "Failed to create initial context: %s", sc_strerror(r)); + return; + } + + while (run_daemon) { + + r = sc_wait_for_event(ctx, event_mask, + &event_reader, &event, timeout, &reader_states); + if (r < 0) { + if (r == SC_ERROR_NO_READERS_FOUND) { + Sleep(timeout); + continue; + } + } + + if (event_reader) { + if (event & SC_EVENT_CARD_REMOVED + || (event & SC_EVENT_READER_DETACHED + && event_reader->flags & SC_READER_CARD_PRESENT)) { + /* sc_notify_id uses only the reader's name for displaying on + * removal, so use a dummy card here to get that information + * into the notification */ + struct sc_pkcs15_card p15card; + sc_card_t card; + memset(&card, 0, sizeof card); + card.reader = event_reader; + memset(&p15card, 0, sizeof p15card); + p15card.card = &card; + sc_notify_id(ctx, &event_reader->atr, &p15card, NOTIFY_CARD_REMOVED); + } else if (event & SC_EVENT_CARD_INSERTED + || (event & SC_EVENT_READER_ATTACHED + && event_reader->flags & SC_READER_CARD_PRESENT)) { + /* sc_notify_id prevers the reader's name for displaying on + * insertion, so use a dummy card here to get that information + * into the notification */ + struct sc_pkcs15_card p15card; + sc_card_t card; + memset(&card, 0, sizeof card); + card.reader = event_reader; + memset(&p15card, 0, sizeof p15card); + p15card.card = &card; + sc_notify_id(ctx, &event_reader->atr, &p15card, NOTIFY_CARD_INSERTED); + } + } + } + + if (ctx) { + /* free `reader_states` */ + sc_wait_for_event(ctx, 0, NULL, NULL, 0, &reader_states); + sc_release_context(ctx); + ctx = NULL; + } +} + +#ifdef _WIN32 +#include "ui/invisible_window.h" +#include + +LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (message == WM_CLOSE || message == WM_QUIT) { + run_daemon = 0; + sc_cancel(ctx); + return TRUE; + } + + return DefWindowProc(hwnd, message, wParam, lParam); +} + +DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter) +{ + notify_daemon(); + return 0; +} + +/* This application shall be executable without a console. Therefore we're + * creating a windows application that requires `WinMain()` rather than + * `main()` as entry point. As benefit, we can properly handle `WM_CLOSE`. */ +int WINAPI +WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + LPCTSTR lpszClassName = "OPENSC_NOTIFY_CLASS"; + HWND hwnd = create_invisible_window(lpszClassName, WndProc, hInstance); + + sc_notify_init(); + run_daemon = 1; + HANDLE hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); + + MSG msg; + BOOL bRet = FALSE; + while((bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) { + if (bRet == -1) { + // handle the error and possibly exit + } else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + if (msg.message == WM_COMMAND && LOWORD(msg.wParam) == WMAPP_EXIT) { + break; + } + } + + CloseHandle(hThread); + sc_notify_close(); + + delete_invisible_window(hwnd, lpszClassName, hInstance); + + return 0; +} + +#else + +#if defined(HAVE_SIGACTION) && defined(HAVE_PTHREAD) +#include +#include +#include +#include + +static int cancellation_fd[] = {-1, -1}; + +void sig_handler(int sig) { + run_daemon = 0; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-result" + (void)write(cancellation_fd[1], &sig, sizeof sig); +#pragma GCC diagnostic pop +} + +static void *cancellation_proc(void *arg) +{ + (void)arg; + while (run_daemon) { + int sig; + if (sizeof sig == read(cancellation_fd[0], &sig, sizeof sig)) { + break; + } + } + sc_cancel(ctx); + return NULL; +} + +void setup_cancellation(void) +{ + pthread_t cancellation_thread; + struct sigaction new_sig, old_sig; + new_sig.sa_handler = sig_handler; + sigemptyset(&new_sig.sa_mask); + new_sig.sa_flags = SA_RESTART; + + if (pipe(cancellation_fd) != 0 + || (errno = pthread_create(&cancellation_thread, NULL, cancellation_proc, NULL)) != 0 + || sigaction(SIGINT, &new_sig, &old_sig) != 0 + || sigaction(SIGTERM, &new_sig, &old_sig) != 0) { + fprintf(stderr, "Failed to setup cancellation: %s", strerror(errno)); + } +} + +#else + +void setup_cancellation(void) +{ +} + +#endif + +#include "opensc-notify-cmdline.h" + +int +main (int argc, char **argv) +{ + struct gengetopt_args_info cmdline; + memset(&cmdline, 0, sizeof cmdline); + + sc_notify_init(); + + if (cmdline_parser(argc, argv, &cmdline) != 0) + goto err; + + if (cmdline.customized_mode_counter) { + sc_notify(cmdline.title_arg, cmdline.message_arg); + } + + if (cmdline.standard_mode_counter) { + if (cmdline.notify_card_inserted_flag) { + sc_notify_id(NULL, NULL, NULL, NOTIFY_CARD_INSERTED); + } + if (cmdline.notify_card_removed_flag) { + sc_notify_id(NULL, NULL, NULL, NOTIFY_CARD_REMOVED); + } + if (cmdline.notify_pin_good_flag) { + sc_notify_id(NULL, NULL, NULL, NOTIFY_PIN_GOOD); + } + if (cmdline.notify_pin_bad_flag) { + sc_notify_id(NULL, NULL, NULL, NOTIFY_PIN_BAD); + } + } + + if ((!cmdline.customized_mode_counter && !cmdline.standard_mode_counter) + || cmdline.daemon_mode_counter) { + run_daemon = 1; + setup_cancellation(); + notify_daemon(); + } else { + /* give the notification process some time to spawn */ + Sleep(100); + } + +err: + sc_notify_close(); + cmdline_parser_free (&cmdline); + + return 0; +} +#endif diff --git a/src/tools/opensc-notify.ggo.in b/src/tools/opensc-notify.ggo.in new file mode 100644 index 0000000000..765f8aa353 --- /dev/null +++ b/src/tools/opensc-notify.ggo.in @@ -0,0 +1,45 @@ +package "opensc-notify" +purpose "@PACKAGE_SUMMARY@" +description "If no arguments are given, monitor smart card events and send the appropriate notification." + +defmode "daemon" + modedesc="Monitor smart card events to send notifications." +defmode "standard" + modedesc="Manually send standard notifications." +defmode "customized" + modedesc="Send customized notifications." + +modeoption "title" t + "Title of the notification" + string + mode="customized" + argoptional + optional +modeoption "message" m + "Main text of the notification" + string + mode="customized" + argoptional + optional + +modeoption "notify-card-inserted" I + "See notify_card_inserted in opensc.conf" + flag off + mode="standard" +modeoption "notify-card-removed" R + "See notify_card_removed in opensc.conf" + flag off + mode="standard" +modeoption "notify-pin-good" G + "See notify_pin_good in opensc.conf" + flag off + mode="standard" +modeoption "notify-pin-bad" B + "See notify_pin_bad in opensc.conf" + flag off + mode="standard" + +text " +Report bugs to @PACKAGE_BUGREPORT@ + +Written by Frank Morgner " diff --git a/src/tools/opensc-tool.c b/src/tools/opensc-tool.c index 4d7efc7a3e..4b1a040de3 100644 --- a/src/tools/opensc-tool.c +++ b/src/tools/opensc-tool.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -55,6 +55,7 @@ enum { OPT_RESET }; +// clang-format off static const struct option options[] = { { "version", 0, NULL, OPT_VERSION }, { "info", 0, NULL, 'i' }, @@ -75,6 +76,7 @@ static const struct option options[] = { { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; +// clang-format on static const char *option_help[] = { "Prints OpenSC package revision", @@ -82,18 +84,18 @@ static const char *option_help[] = { "Prints the ATR bytes of the card", "Prints the card serial number", "Identify the card and print its name", - "Get configuration key, format: section:name:key", - "Set configuration key, format: section:name:key:value", + "Get configuration, e.g. section:name:key", + "Set configuration, e.g. section:name:key:val", "Lists readers", "Lists all installed card drivers", "Recursively lists files stored on card", - "Sends an APDU in format AA:BB:CC:DD:EE:FF...", + "Sends an APDU (may need '-c default')", "Uses reader number [0]", "Does card reset of type [cold]", - "Forces the use of driver [auto-detect]", + "Forces a card driver (use '?' for list)", "Lists algorithms supported by card", "Wait for a card to be inserted", - "Verbose operation. Use several times to enable debug output.", + "Verbose operation, may be used several times", }; static sc_context_t *ctx = NULL; @@ -280,7 +282,7 @@ static int list_readers(void) reader->capabilities & SC_READER_CAP_PIN_PAD ? "PIN pad":"", reader->name); if (state & SC_READER_CARD_PRESENT && verbose) { - struct sc_card *card; + struct sc_card *c; int r; char tmp[SC_MAX_ATR_SIZE*3]; sc_bin_to_hex(reader->atr.value, reader->atr.len, tmp, sizeof(tmp) - 1, ':'); @@ -288,11 +290,11 @@ static int list_readers(void) if (state & SC_READER_CARD_EXCLUSIVE) printf(" %s [EXCLUSIVE]\n", tmp); else { - if ((r = sc_connect_card(reader, &card)) != SC_SUCCESS) { + if ((r = sc_connect_card(reader, &c)) != SC_SUCCESS) { fprintf(stderr, " failed: %s\n", sc_strerror(r)); } else { - printf(" %s %s %s\n", tmp, card->name ? card->name : "", state & SC_READER_CARD_INUSE ? "[IN USE]" : ""); - sc_disconnect_card(card); + printf(" %s %s %s\n", tmp, c->name ? c->name : "", state & SC_READER_CARD_INUSE ? "[IN USE]" : ""); + sc_disconnect_card(c); } } } @@ -300,22 +302,6 @@ static int list_readers(void) return 0; } -static int list_drivers(void) -{ - int i; - - if (ctx->card_drivers[0] == NULL) { - printf("No card drivers installed!\n"); - return 0; - } - printf("Configured card drivers:\n"); - for (i = 0; ctx->card_drivers[i] != NULL; i++) { - printf(" %-16s %s\n", ctx->card_drivers[i]->short_name, - ctx->card_drivers[i]->name); - } - return 0; -} - static int print_file(sc_card_t *in_card, const sc_file_t *file, const sc_path_t *path, int depth) { @@ -429,17 +415,23 @@ static int print_file(sc_card_t *in_card, const sc_file_t *file, return 1; } - r = sc_read_binary(in_card, 0, buf, file->size, 0); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_read_binary(in_card, 0, buf, file->size, 0); + sc_unlock(card); if (r > 0) util_hex_dump_asc(stdout, buf, r, 0); free(buf); } else { unsigned char buf[256]; - int i; - - for (i=0; i < file->record_count; i++) { - printf("Record %d\n", i); - r = sc_read_record(in_card, i, buf, 256, 0); + unsigned int rec_nr; + + for (rec_nr = 1; rec_nr <= file->record_count; rec_nr++) { + printf("Record %u\n", rec_nr); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_read_record(in_card, rec_nr, 0, buf, sizeof(buf), SC_RECORD_BY_REC_NR); + sc_unlock(card); if (r > 0) util_hex_dump_asc(stdout, buf, r, 0); } @@ -451,9 +443,12 @@ static int enum_dir(sc_path_t path, int depth) { sc_file_t *file; int r, file_type; - u8 files[SC_MAX_APDU_BUFFER_SIZE]; + u8 files[SC_MAX_EXT_APDU_BUFFER_SIZE]; - r = sc_select_file(card, &path, &file); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_select_file(card, &path, &file); + sc_unlock(card); if (r) { fprintf(stderr, "SELECT FILE failed: %s\n", sc_strerror(r)); return 1; @@ -464,22 +459,26 @@ static int enum_dir(sc_path_t path, int depth) if (file_type == SC_FILE_TYPE_DF) { int i; - r = sc_list_files(card, files, sizeof(files)); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_list_files(card, files, sizeof(files)); + sc_unlock(card); if (r < 0) { fprintf(stderr, "sc_list_files() failed: %s\n", sc_strerror(r)); return 1; } if (r == 0) { printf("Empty directory\n"); - } else - for (i = 0; i < r/2; i++) { - sc_path_t tmppath; - - memset(&tmppath, 0, sizeof(tmppath)); - memcpy(&tmppath, &path, sizeof(path)); - memcpy(tmppath.value + tmppath.len, files + 2*i, 2); - tmppath.len += 2; - enum_dir(tmppath, depth + 1); + } else { + for (i = 0; i < r/2; i++) { + sc_path_t tmppath; + + memset(&tmppath, 0, sizeof(tmppath)); + memcpy(&tmppath, &path, sizeof(path)); + memcpy(tmppath.value + tmppath.len, files + 2*i, 2); + tmppath.len += 2; + enum_dir(tmppath, depth + 1); + } } } return 0; @@ -498,18 +497,18 @@ static int list_files(void) static int send_apdu(void) { sc_apdu_t apdu; - u8 buf[SC_MAX_APDU_BUFFER_SIZE], - rbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 buf[SC_MAX_EXT_APDU_BUFFER_SIZE], + rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; size_t len0, r; - int c; + int c, rc; for (c = 0; c < opt_apdu_count; c++) { len0 = sizeof(buf); sc_hex_to_bin(opt_apdus[c], buf, &len0); - r = sc_bytes2apdu(card->ctx, buf, len0, &apdu); - if (r) { - fprintf(stderr, "Invalid APDU: %s\n", sc_strerror(r)); + rc = sc_bytes2apdu(card->ctx, buf, len0, &apdu); + if (rc) { + fprintf(stderr, "Invalid APDU: %s\n", sc_strerror(rc)); return 2; } @@ -520,9 +519,12 @@ static int send_apdu(void) for (r = 0; r < len0; r++) printf("%02X ", buf[r]); printf("\n"); - r = sc_transmit_apdu(card, &apdu); - if (r) { - fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); + rc = sc_lock(card); + if (rc == SC_SUCCESS) + rc = sc_transmit_apdu(card, &apdu); + sc_unlock(card); + if (rc) { + fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(rc)); return 1; } printf("Received (SW1=0x%02X, SW2=0x%02X)%s\n", apdu.sw1, apdu.sw2, @@ -538,7 +540,10 @@ static void print_serial(sc_card_t *in_card) int r; sc_serial_number_t serial; - r = sc_card_ctl(in_card, SC_CARDCTL_GET_SERIALNR, &serial); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_card_ctl(in_card, SC_CARDCTL_GET_SERIALNR, &serial); + sc_unlock(card); if (r) fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GET_SERIALNR, *) failed\n"); else @@ -550,10 +555,11 @@ static int list_algorithms(void) int i; const char *aname = "unknown"; + // clang-format off const id2str_t alg_type_names[] = { { SC_ALGORITHM_RSA, "rsa" }, - { SC_ALGORITHM_DSA, "dsa" }, { SC_ALGORITHM_EC, "ec" }, + { SC_ALGORITHM_EDDSA, "eddsa" }, { SC_ALGORITHM_GOSTR3410, "gostr3410" }, { SC_ALGORITHM_DES, "des" }, { SC_ALGORITHM_3DES, "3des" }, @@ -563,6 +569,7 @@ static int list_algorithms(void) { SC_ALGORITHM_GOSTR3411, "gostr3411" }, { SC_ALGORITHM_PBKDF2, "pbkdf2" }, { SC_ALGORITHM_PBES2, "pbes2" }, + { SC_ALGORITHM_AES, "aes" }, { 0, NULL } }; const id2str_t alg_flag_names[] = { @@ -571,19 +578,24 @@ static int list_algorithms(void) { 0, NULL } }; const id2str_t rsa_flag_names[] = { - { SC_ALGORITHM_RSA_PAD_PKCS1, "pkcs1" }, - { SC_ALGORITHM_RSA_PAD_ANSI, "ansi" }, - { SC_ALGORITHM_RSA_PAD_ISO9796, "iso9796" }, - { SC_ALGORITHM_RSA_HASH_SHA1, "sha1" }, - { SC_ALGORITHM_RSA_HASH_MD5, "MD5" }, - { SC_ALGORITHM_RSA_HASH_MD5_SHA1, "md5-sha1" }, - { SC_ALGORITHM_RSA_HASH_RIPEMD160, "ripemd160" }, - { SC_ALGORITHM_RSA_HASH_SHA256, "sha256" }, - { SC_ALGORITHM_RSA_HASH_SHA384, "sha384" }, - { SC_ALGORITHM_RSA_HASH_SHA512, "sha512" }, - { SC_ALGORITHM_RSA_HASH_SHA224, "sha224" }, + { SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01, "pkcs1-type1" }, + { SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02, "pkcs1-type2" }, + { SC_ALGORITHM_RSA_PAD_PKCS1, "pkcs1" }, + { SC_ALGORITHM_RSA_PAD_ANSI, "ansi" }, + { SC_ALGORITHM_RSA_PAD_PSS, "pss" }, + { SC_ALGORITHM_RSA_PAD_OAEP, "oaep" }, + { SC_ALGORITHM_RSA_PAD_ISO9796, "iso9796" }, + { SC_ALGORITHM_RSA_HASH_SHA1, "sha1" }, + { SC_ALGORITHM_RSA_HASH_MD5, "MD5" }, + { SC_ALGORITHM_RSA_HASH_MD5_SHA1, "md5-sha1" }, + { SC_ALGORITHM_RSA_HASH_RIPEMD160, "ripemd160" }, + { SC_ALGORITHM_RSA_HASH_SHA256, "sha256" }, + { SC_ALGORITHM_RSA_HASH_SHA384, "sha384" }, + { SC_ALGORITHM_RSA_HASH_SHA512, "sha512" }, + { SC_ALGORITHM_RSA_HASH_SHA224, "sha224" }, { 0, NULL } }; + // clang-format on if (verbose) printf("Card supports %d algorithm(s)\n\n",card->algorithm_count); @@ -600,7 +612,7 @@ static int list_algorithms(void) } printf("Algorithm: %s\n", aname); - printf("Key length: %d\n", card->algorithms[i].key_length); + printf("Key length: %zu\n", card->algorithms[i].key_length); printf("Flags:"); /* print general flags */ @@ -608,7 +620,7 @@ static int list_algorithms(void) if (card->algorithms[i].flags & alg_flag_names[j].id) printf(" %s", alg_flag_names[j].str); - /* print RSA spcific flags */ + /* print RSA specific flags */ if ( card->algorithms[i].algorithm == SC_ALGORITHM_RSA) { int padding = card->algorithms[i].flags & SC_ALGORITHM_RSA_PADS; @@ -658,7 +670,10 @@ static int card_reset(const char *reset_type) cold_reset = !reset_type || strcmp(reset_type, "cold") == 0; - r = sc_reset(card, cold_reset); + r = sc_lock(card); + if (r == SC_SUCCESS) + r = sc_reset(card, cold_reset); + sc_unlock(card); if (r) { fprintf(stderr, "sc_reset(%s) failed: %d\n", cold_reset ? "cold" : "warm", r); @@ -668,7 +683,7 @@ static int card_reset(const char *reset_type) return 0; } -int main(int argc, char * const argv[]) +int main(int argc, char *argv[]) { int err = 0, r, c, long_optind = 0; int do_info = 0; @@ -689,11 +704,9 @@ int main(int argc, char * const argv[]) const char *opt_conf_entry = NULL; const char *opt_reset_type = NULL; char **p; + struct sc_reader *reader = NULL; sc_context_param_t ctx_param; - setbuf(stderr, NULL); - setbuf(stdout, NULL); - while (1) { c = getopt_long(argc, argv, "inlG:S:fr:vs:Dc:aw", options, &long_optind); if (c == -1) @@ -762,6 +775,13 @@ int main(int argc, char * const argv[]) break; case 'c': opt_driver = optarg; + + /* treat argument "?" as request to list drivers */ + if (opt_driver && strncmp("?", opt_driver, sizeof("?")) == 0) { + opt_driver = NULL; + do_list_drivers = 1; + action_count++; + } break; case 'w': opt_wait = 1; @@ -797,6 +817,9 @@ int main(int argc, char * const argv[]) memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.ver = 0; ctx_param.app_name = app_name; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; r = sc_context_create(&ctx, &ctx_param); if (r) { @@ -806,11 +829,6 @@ int main(int argc, char * const argv[]) ctx->flags |= SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER; - if (verbose > 1) { - ctx->debug = verbose; - sc_ctx_log_to_file(ctx, "stderr"); - } - if (do_get_conf_entry) { if ((err = opensc_get_conf_entry (opt_conf_entry))) goto end; @@ -827,13 +845,33 @@ int main(int argc, char * const argv[]) action_count--; } if (do_list_drivers) { - if ((err = list_drivers())) + if ((err = util_list_card_drivers(ctx))) goto end; action_count--; } if (action_count <= 0) goto end; + err = util_connect_reader(ctx, &reader, opt_reader, opt_wait); + if (err) { + fprintf(stderr, "Failed to connect to reader: %s\n", sc_strerror(err)); + err = 1; + goto end; + } + if (do_print_atr) { + if (verbose) { + printf("Card ATR:\n"); + util_hex_dump_asc(stdout, reader->atr.value, reader->atr.len, -1); + } else { + char tmp[SC_MAX_ATR_SIZE*3]; + sc_bin_to_hex(reader->atr.value, reader->atr.len, tmp, sizeof(tmp) - 1, ':'); + fprintf(stdout,"%s\n",tmp); + } + action_count--; + } + if (action_count <= 0) + goto end; + if (opt_driver != NULL) { err = sc_set_card_driver(ctx, opt_driver); if (err) { @@ -843,21 +881,19 @@ int main(int argc, char * const argv[]) } } - err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); - if (err) - goto end; + if (verbose) + printf("Connecting to card in reader %s...\n", reader->name); - if (do_print_atr) { - if (verbose) { - printf("Card ATR:\n"); - util_hex_dump_asc(stdout, card->atr.value, card->atr.len, -1); - } else { - char tmp[SC_MAX_ATR_SIZE*3]; - sc_bin_to_hex(card->atr.value, card->atr.len, tmp, sizeof(tmp) - 1, ':'); - fprintf(stdout,"%s\n",tmp); - } - action_count--; + err = sc_connect_card(reader, &card); + if (err < 0) { + fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(err)); + err = 1; + goto end; } + + if (verbose) + printf("Using card driver %s.\n", card->driver->name); + if (do_print_serial) { if (verbose) printf("Card serial number:"); @@ -894,11 +930,7 @@ int main(int argc, char * const argv[]) action_count--; } end: - if (card) { - sc_unlock(card); - sc_disconnect_card(card); - } - if (ctx) - sc_release_context(ctx); + sc_disconnect_card(card); + sc_release_context(ctx); return err; } diff --git a/src/tools/org.opensc-project.mac.opensc-notify.plist.in b/src/tools/org.opensc-project.mac.opensc-notify.plist.in new file mode 100644 index 0000000000..8f45fb2503 --- /dev/null +++ b/src/tools/org.opensc-project.mac.opensc-notify.plist.in @@ -0,0 +1,16 @@ + + + + + Label + org.opensc-project.mac.opensc-notify + Program + @bindir@/opensc-notify + RunAtLoad + + StandardErrorPath + /dev/null + StandardOutPath + /dev/null + + diff --git a/src/tools/org.opensc-project.mac.pkcs11-register.plist.in b/src/tools/org.opensc-project.mac.pkcs11-register.plist.in new file mode 100644 index 0000000000..cf6cc471b5 --- /dev/null +++ b/src/tools/org.opensc-project.mac.pkcs11-register.plist.in @@ -0,0 +1,16 @@ + + + + + Label + org.opensc-project.mac.pkcs11-register + Program + @bindir@/pkcs11-register + RunAtLoad + + StandardErrorPath + /dev/null + StandardOutPath + /dev/null + + diff --git a/src/tools/org.opensc.notify.desktop.in b/src/tools/org.opensc.notify.desktop.in new file mode 100644 index 0000000000..1c8755c410 --- /dev/null +++ b/src/tools/org.opensc.notify.desktop.in @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=OpenSC Notify +GenericName=Smard card notification +Type=Application +Comment=Monitor smart card events to send notifications. +Exec=@bindir@/opensc-notify +Icon=utilities-system-monitor +Categories=Security;System; +NoDisplay=true diff --git a/src/tools/piv-tool.c b/src/tools/piv-tool.c index 89581064f4..326a11bd44 100644 --- a/src/tools/piv-tool.c +++ b/src/tools/piv-tool.c @@ -2,7 +2,7 @@ * piv-tool.c: Tool for accessing smart cards with libopensc * * Copyright (C) 2001 Juha Yrjölä - * Copyright (C) 2005,2010 Douglas E. Engert + * Copyright (C) 2005,2010-2026 Douglas E. Engert * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -33,17 +33,16 @@ /* Module only built if OPENSSL is enabled */ #include -#include "libopensc/sc-ossl-compat.h" -#if OPENSSL_VERSION_NUMBER >= 0x10000000L #include #include -#endif -#if OPENSSL_VERSION_NUMBER >= 0x00907000L #include -#endif #include -#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# include +# include +#endif +#if !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) #include #include #endif @@ -57,8 +56,11 @@ #include "libopensc/opensc.h" #include "libopensc/cardctl.h" +#include "libopensc/cards.h" #include "libopensc/asn1.h" +#include "libopensc/log.h" #include "util.h" +#include "libopensc/sc-ossl-compat.h" static const char *app_name = "piv-tool"; @@ -84,7 +86,6 @@ static const struct option options[] = { { "in", 1, NULL, 'i' }, { "send-apdu", 1, NULL, 's' }, { "reader", 1, NULL, 'r' }, - { "card-driver", 1, NULL, 'c' }, { "wait", 0, NULL, 'w' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } @@ -102,15 +103,13 @@ static const char *option_help[] = { "Input file for cert", "Sends an APDU in format AA:BB:CC:DD:EE:FF...", "Uses reader number [0]", - "Forces the use of driver [auto-detect]", "Wait for a card to be inserted", - "Verbose operation. Use several times to enable debug output.", + "Verbose operation, may be used several times", }; static sc_context_t *ctx = NULL; static sc_card_t *card = NULL; -static BIO * bp = NULL; -static EVP_PKEY * evpkey = NULL; +static BIO *bp = NULL; static int load_object(const char * object_id, const char * object_file) { @@ -123,14 +122,14 @@ static int load_object(const char * object_id, const char * object_file) int r = -1; struct stat stat_buf; - if(!object_file || (fp=fopen(object_file, "r")) == NULL){ - printf("Cannot open object file, %s %s\n", - (object_file)?object_file:"", strerror(errno)); + if (!object_file || (fp = fopen(object_file, "rb")) == NULL) { + printf("Cannot open object file, %s %s\n", + (object_file) ? object_file : "", strerror(errno)); goto err; - } + } if (0 != stat(object_file, &stat_buf)) { - printf("unable to read file %s\n",object_file); + printf("unable to read file %s\n", object_file); goto err; } derlen = stat_buf.st_size; @@ -146,7 +145,7 @@ static int load_object(const char * object_id, const char * object_file) } /* check if tag and length are valid */ body = (u8 *)sc_asn1_find_tag(card->ctx, der, derlen, 0x53, &bodylen); - if (body == NULL || derlen != body - der + bodylen) { + if (body == NULL || derlen != body - der + bodylen) { fprintf(stderr, "object tag or length not valid\n"); goto err; } @@ -185,20 +184,20 @@ static int load_cert(const char * cert_id, const char * cert_file, int r = -1; if (!cert_file) { - printf("Missing cert file\n"); + printf("Missing cert file\n"); goto err; } - if((fp=fopen(cert_file, "r"))==NULL){ - printf("Cannot open cert file, %s %s\n", + if ((fp = fopen(cert_file, "rb")) == NULL) { + printf("Cannot open cert file, %s %s\n", cert_file, strerror(errno)); - goto err; - } - if (compress) { /* file is gziped already */ + goto err; + } + if (compress) { /* file is gzipped already */ struct stat stat_buf; if (0 != stat(cert_file, &stat_buf)) { - printf("unable to read file %s\n",cert_file); + printf("unable to read file %s\n", cert_file); goto err; } derlen = stat_buf.st_size; @@ -209,16 +208,16 @@ static int load_cert(const char * cert_id, const char * cert_file, goto err; } if (1 != fread(der, derlen, 1, fp)) { - printf("unable to read file %s\n",cert_file); + printf("unable to read file %s\n", cert_file); goto err; } } else { cert = PEM_read_X509(fp, &cert, NULL, NULL); - if(cert == NULL){ - printf("file %s does not conatin PEM-encoded certificate\n", - cert_file); - goto err; - } + if (cert == NULL) { + sc_log_openssl(ctx); + printf("file %s does not contain PEM-encoded certificate\n", cert_file); + goto err; + } derlen = i2d_X509(cert, NULL); der = malloc(derlen); @@ -248,7 +247,7 @@ static int load_cert(const char * cert_id, const char * cert_file, } /* we pass length and 8 bits of flag to card-piv.c write_binary */ /* pass in its a cert and if needs compress */ - r = sc_write_binary(card, 0, der, derlen, (derlen<<8) | (compress<<4) | 1); + r = sc_write_binary(card, 0, der, derlen, (derlen << 8) | (compress << 4) | 1); err: free(der); @@ -264,13 +263,12 @@ static int admin_mode(const char* admin_info) size_t buflen = 2; - if (strlen(admin_info) == 7 && + if (admin_info && strlen(admin_info) == 7 && (admin_info[0] == 'A' || admin_info[0] == 'M') && admin_info[1] == ':' && (sc_hex_to_bin(admin_info+2, opts+1, &buflen) == 0) && buflen == 2) { opts[0] = admin_info[0]; - } else { fprintf(stderr, " admin_mode params ::\n"); return -1; @@ -278,26 +276,26 @@ static int admin_mode(const char* admin_info) r = sc_card_ctl(card, SC_CARDCTL_PIV_AUTHENTICATE, &opts); if (r) - fprintf(stderr, " admin_mode failed %d\n", r); + fprintf(stderr, " admin_mode failed %s\n", sc_strerror(r)); return r; } /* generate a new key pair, and save public key in newkey */ static int gen_key(const char * key_info) { - int r; + int r = 1; u8 buf[2]; size_t buflen = 2; sc_cardctl_piv_genkey_info_t - keydata = {0, 0, 0, 0, NULL, 0, NULL, 0, NULL, 0}; - unsigned long expl; - u8 expc[4]; -#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC) + keydata = {0, 0, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0}; + EVP_PKEY *evpkey = NULL; +#if !defined(OPENSSL_NO_EC) int nid = -1; #endif + sc_hex_to_bin(key_info, buf, &buflen); if (buflen != 2) { - fprintf(stderr, ": invalid, example: 9A:06\n"); + fprintf(stderr, ": invalid. Example: 9A:07 to create 9A key with RSA 2048\n"); return 2; } switch (buf[0]) { @@ -305,10 +303,30 @@ static int gen_key(const char * key_info) case 0x9c: case 0x9d: case 0x9e: + case 0x82: /* retired keys */ + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + case 0x8c: + case 0x8d: + case 0x8e: + case 0x8f: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: keydata.key_num = buf[0]; break; default: - fprintf(stderr, ": must be 9A, 9C, 9D or 9E\n"); + fprintf(stderr, " must be 9A, 9C, 9D, 9E or 82-95 \n"); return 2; } @@ -316,16 +334,24 @@ static int gen_key(const char * key_info) case 0x05: keydata.key_bits = 3072; break; case 0x06: keydata.key_bits = 1024; break; case 0x07: keydata.key_bits = 2048; break; -#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC) +#if !defined(OPENSSL_NO_EC) case 0x11: keydata.key_bits = 0; nid = NID_X9_62_prime256v1; /* We only support one curve per algid */ break; case 0x14: keydata.key_bits = 0; nid = NID_secp384r1; break; + case 0xE0: + keydata.key_bits = 0; + nid = NID_ED25519; + break; + case 0xE1: + keydata.key_bits = 0; + nid = NID_X25519; + break; #endif default: - fprintf(stderr, ": algid=RSA - 05, 06, 07 for 3072, 1024, 2048;EC - 11, 14 for 256, 384\n"); + fprintf(stderr, " must be: RSA - 05, 06, 07 for 3072, 1024, 2048; EC - 11, 14 for 256, 384; E0 for ED25519; or E1 X25519\n"); return 2; } @@ -334,76 +360,308 @@ static int gen_key(const char * key_info) r = sc_card_ctl(card, SC_CARDCTL_PIV_GENERATE_KEY, &keydata); if (r) { - fprintf(stderr, "gen_key failed %d\n", r); - return r; + fprintf(stderr, "gen_key failed %s\n", sc_strerror(r)); + return 1; } - evpkey = EVP_PKEY_new(); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + evpkey = EVP_PKEY_new(); + if (!evpkey) { + sc_log_openssl(ctx); + fprintf(stderr, "allocation of key failed\n"); + r = 1; + goto out; + } +#endif if (keydata.key_bits > 0) { /* RSA key */ - RSA * newkey = NULL; BIGNUM *newkey_n, *newkey_e; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA *newkey = NULL; +#else + EVP_PKEY_CTX *cctx = NULL; + OSSL_PARAM_BLD *bld = NULL; + OSSL_PARAM *params = NULL; +#endif + if (!keydata.pubkey || !keydata.exponent) { + fprintf(stderr, "gen_key failed %s\n", sc_strerror(r)); + r = 1; + goto out; + } + + newkey_n = BN_bin2bn(keydata.pubkey, (int)keydata.pubkey_len, NULL); + newkey_e = BN_bin2bn(keydata.exponent, (int)keydata.exponent_len, NULL); + if (!newkey_n || !newkey_e) { + sc_log_openssl(ctx); + fprintf(stderr, "conversion or key params failed\n"); + r = 1; + goto out; + } + +#if OPENSSL_VERSION_NUMBER < 0x30000000L newkey = RSA_new(); - if (newkey == NULL) { - fprintf(stderr, "gen_key RSA_new failed %d\n",r); - return -1; + if (!newkey) { + sc_log_openssl(ctx); + fprintf(stderr, "gen_key RSA_new failed\n"); + r = 1; + goto out; } - newkey_n = BN_bin2bn(keydata.pubkey, keydata.pubkey_len, NULL); - expl = keydata.exponent; - expc[3] = (u8) expl & 0xff; - expc[2] = (u8) (expl >>8) & 0xff; - expc[1] = (u8) (expl >>16) & 0xff; - expc[0] = (u8) (expl >>24) & 0xff; - newkey_e = BN_bin2bn(expc, 4, NULL); if (RSA_set0_key(newkey, newkey_n, newkey_e, NULL) != 1) { + sc_log_openssl(ctx); + RSA_free(newkey); + BN_free(newkey_n); + BN_free(newkey_e); fprintf(stderr, "gen_key unable to set RSA values"); - return -1; + r = 1; + goto out; + } + + if (verbose) + RSA_print_fp(stdout, newkey, 0); + + if (EVP_PKEY_assign_RSA(evpkey, newkey) != 1) { + sc_log_openssl(ctx); + RSA_free(newkey); + BN_free(newkey_n); + BN_free(newkey_e); + fprintf(stderr, "gen_key unable to set RSA values"); + r = 1; + goto out; + } +#else + if (!(bld = OSSL_PARAM_BLD_new()) || + OSSL_PARAM_BLD_push_BN(bld, "n", newkey_n) != 1 || + OSSL_PARAM_BLD_push_BN(bld, "e", newkey_e) != 1 || + !(params = OSSL_PARAM_BLD_to_param(bld))) { + sc_log_openssl(ctx); + OSSL_PARAM_BLD_free(bld); + BN_free(newkey_n); + BN_free(newkey_e); + r = 1; + goto out; } + OSSL_PARAM_BLD_free(bld); + BN_free(newkey_n); + BN_free(newkey_e); + + cctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + if (!cctx || + EVP_PKEY_fromdata_init(cctx) != 1 || + EVP_PKEY_fromdata(cctx, &evpkey, EVP_PKEY_KEYPAIR, params) != 1) { + sc_log_openssl(ctx); + EVP_PKEY_CTX_free(cctx); + OSSL_PARAM_free(params); + fprintf(stderr, "gen_key unable to gen RSA"); + r = 1; + goto out; + } if (verbose) - RSA_print_fp(stdout, newkey,0); + EVP_PKEY_print_public_fp(stdout, evpkey, 0, NULL); - EVP_PKEY_assign_RSA(evpkey, newkey); + EVP_PKEY_CTX_free(cctx); + OSSL_PARAM_free(params); +#endif +#ifdef EVP_PKEY_ED25519 + } else if (nid == NID_ED25519 || nid == NID_X25519) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L + fprintf(stderr, "This build of OpenSSL does not support ED25519 or X25519 keys\n"); + r = 1; + goto out; +#else + if (!keydata.ecpoint) { + fprintf(stderr, "gen_key failed\n"); + r = 1; + goto out; + } + evpkey = EVP_PKEY_new_raw_public_key(nid, NULL, keydata.ecpoint, keydata.ecpoint_len); + if (!evpkey) { + sc_log_openssl(ctx); + fprintf(stderr, "gen key failed ti copy 25519 pubkey\n"); + r = 1; + goto out; + } + + if (verbose) + EVP_PKEY_print_public_fp(stdout, evpkey, 0, NULL); +#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */ +#else + fprintf(stderr, "This build of OpenSSL does not support ED25519 or X25519 keys\n"); + return -1; +#endif /* EVP_PKEY_ED25519 */ } else { /* EC key */ -#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC) +#if !defined(OPENSSL_NO_EC) int i; - BIGNUM *x; - BIGNUM *y; - EC_KEY * eckey = NULL; + BIGNUM *x = NULL; + BIGNUM *y = NULL; EC_GROUP * ecgroup = NULL; EC_POINT * ecpoint = NULL; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + EC_KEY * eckey = NULL; +#else + EVP_PKEY_CTX *cctx = NULL; + OSSL_PARAM_BLD *bld = NULL; + OSSL_PARAM *params = NULL; + size_t len = 0; + unsigned char * buf = NULL; + const char *group_name; +#endif + + if (!keydata.ecpoint) { + fprintf(stderr, "gen_key failed\n"); + r = 1; + goto out; + } ecgroup = EC_GROUP_new_by_curve_name(nid); EC_GROUP_set_asn1_flag(ecgroup, OPENSSL_EC_NAMED_CURVE); ecpoint = EC_POINT_new(ecgroup); /* PIV returns 04||x||y and x and y are the same size */ - i = (keydata.ecpoint_len - 1)/2; + i = (int)(keydata.ecpoint_len - 1) / 2; x = BN_bin2bn(keydata.ecpoint + 1, i, NULL); - y = BN_bin2bn(keydata.ecpoint + 1 + i, i, NULL) ; - r = EC_POINT_set_affine_coordinates_GFp(ecgroup, ecpoint, x, y, NULL); + y = BN_bin2bn(keydata.ecpoint + 1 + i, i, NULL); + if (!x || !y) { + sc_log_openssl(ctx); + BN_free(x); + BN_free(y); + EC_GROUP_free(ecgroup); + EC_POINT_free(ecpoint); + r = 1; + goto out; + } + r = EC_POINT_set_affine_coordinates(ecgroup, ecpoint, x, y, NULL); + + BN_free(x); + BN_free(y); + + if (r == 0) { + sc_log_openssl(ctx); + fprintf(stderr, "EC_POINT_set_affine_coordinates_GFp failed\n"); + EC_GROUP_free(ecgroup); + EC_POINT_free(ecpoint); + r = 1; + goto out; + } +#if OPENSSL_VERSION_NUMBER < 0x30000000L eckey = EC_KEY_new(); + if (eckey == NULL) { + sc_log_openssl(ctx); + fprintf(stderr, "EC_KEY_new failed\n"); + EC_GROUP_free(ecgroup); + EC_POINT_free(ecpoint); + r = 1; + goto out; + } r = EC_KEY_set_group(eckey, ecgroup); + EC_GROUP_free(ecgroup); + if (r == 0) { + sc_log_openssl(ctx); + fprintf(stderr, "EC_KEY_set_group failed\n"); + EC_POINT_free(ecpoint); + EC_KEY_free(eckey); + r = 1; + goto out; + } r = EC_KEY_set_public_key(eckey, ecpoint); + EC_POINT_free(ecpoint); + if (r == 0) { + sc_log_openssl(ctx); + fprintf(stderr, "EC_KEY_set_public_key failed\n"); + EC_KEY_free(eckey); + r = 1; + goto out; + } if (verbose) EC_KEY_print_fp(stdout, eckey, 0); - EVP_PKEY_assign_EC_KEY(evpkey, eckey); + if (EVP_PKEY_assign_EC_KEY(evpkey, eckey) != 1) { + EC_KEY_free(eckey); + sc_log_openssl(ctx); + r = 1; + goto out; + } #else + group_name = OBJ_nid2sn(nid); + len = EC_POINT_point2oct(ecgroup, ecpoint, POINT_CONVERSION_COMPRESSED, NULL, 0, NULL); + if (!(buf = malloc(len))) { + sc_log_openssl(ctx); + fprintf(stderr, "EC_KEY_set_public_key out of memory\n"); + EC_GROUP_free(ecgroup); + EC_POINT_free(ecpoint); + r = 1; + goto out; + } + if (EC_POINT_point2oct(ecgroup, ecpoint, POINT_CONVERSION_COMPRESSED, buf, len, NULL) == 0) { + sc_log_openssl(ctx); + fprintf(stderr, "EC_KEY_set_public_key failed\n"); + EC_GROUP_free(ecgroup); + EC_POINT_free(ecpoint); + free(buf); + r = 1; + goto out; + } + + EC_GROUP_free(ecgroup); + EC_POINT_free(ecpoint); + + if (!(bld = OSSL_PARAM_BLD_new()) || + OSSL_PARAM_BLD_push_utf8_string(bld, "group", group_name, strlen(group_name)) != 1 || + OSSL_PARAM_BLD_push_octet_string(bld, "pub", buf, len) != 1 || + !(params = OSSL_PARAM_BLD_to_param(bld))) { + sc_log_openssl(ctx); + OSSL_PARAM_BLD_free(bld); + free(buf); + r = 1; + goto out; + } + free(buf); + OSSL_PARAM_BLD_free(bld); + + cctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); + if (!cctx || + EVP_PKEY_fromdata_init(cctx) != 1 || + EVP_PKEY_fromdata(cctx, &evpkey, EVP_PKEY_KEYPAIR, params) != 1) { + sc_log_openssl(ctx); + fprintf(stderr, "gen_key unable to gen EC key"); + EVP_PKEY_CTX_free(cctx); + OSSL_PARAM_free(params); + r = 1; + goto out; + } + if (verbose) + EVP_PKEY_print_public_fp(stdout, evpkey, 0, NULL); + + EVP_PKEY_CTX_free(cctx); + OSSL_PARAM_free(params); +#endif +#else /* OPENSSL_NO_EC */ fprintf(stderr, "This build of OpenSSL does not support EC keys\n"); r = 1; #endif /* OPENSSL_NO_EC */ } - if (bp) + + if (bp) { r = i2d_PUBKEY_bio(bp, evpkey); + if (r != 1) { + sc_log_openssl(ctx); + fprintf(stderr, "Failed to encode public key"); + r = 1; + goto out; + } + } + r = SC_SUCCESS; +out: + free(keydata.pubkey); + free(keydata.exponent); + free(keydata.ecpoint); - if (evpkey) - EVP_PKEY_free(evpkey); + EVP_PKEY_free(evpkey); return r; } @@ -414,7 +672,8 @@ static int send_apdu(void) sc_apdu_t apdu; u8 buf[SC_MAX_APDU_BUFFER_SIZE+3]; u8 rbuf[8192]; - size_t len0, r; + size_t len0, i; + int r; int c; for (c = 0; c < opt_apdu_count; c++) { @@ -431,8 +690,8 @@ static int send_apdu(void) apdu.resplen = sizeof(rbuf); printf("Sending: "); - for (r = 0; r < len0; r++) - printf("%02X ", buf[r]); + for (i = 0; i < len0; i++) + printf("%02X ", buf[i]); printf("\n"); r = sc_transmit_apdu(card, &apdu); if (r) { @@ -454,14 +713,14 @@ static void print_serial(sc_card_t *in_card) r = sc_card_ctl(in_card, SC_CARDCTL_GET_SERIALNR, &serial); if (r < 0) - fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GET_SERIALNR, *) failed %d\n", r); + fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GET_SERIALNR, *) failed %s\n", sc_strerror(r)); else util_hex_dump_asc(stdout, serial.value, serial.len, -1); } -int main(int argc, char * const argv[]) +int main(int argc, char *argv[]) { - int err = 0, r, c, long_optind = 0; + int err = 0, r, c; int do_send_apdu = 0; int do_admin_mode = 0; int do_gen_key = 0; @@ -471,7 +730,6 @@ int main(int argc, char * const argv[]) int do_print_serial = 0; int do_print_name = 0; int action_count = 0; - const char *opt_driver = NULL; const char *out_file = NULL; const char *in_file = NULL; const char *cert_id = NULL; @@ -479,25 +737,20 @@ int main(int argc, char * const argv[]) const char *key_info = NULL; const char *admin_info = NULL; sc_context_param_t ctx_param; + char **old_apdus = NULL; - setbuf(stderr, NULL); - setbuf(stdout, NULL); - - while (1) { - c = getopt_long(argc, argv, "nA:G:O:Z:C:i:o:fvs:c:w", options, &long_optind); - if (c == -1) - break; - if (c == '?') - util_print_usage_and_die(app_name, options, option_help, NULL); + while ((c = getopt_long(argc, argv, "nA:G:O:Z:C:i:o:r:fvs:c:w", options, (int *) 0)) != -1) { switch (c) { case OPT_SERIAL: do_print_serial = 1; action_count++; break; case 's': + old_apdus = opt_apdus; opt_apdus = (char **) realloc(opt_apdus, (opt_apdu_count + 1) * sizeof(char *)); if (!opt_apdus) { + free(old_apdus); err = 1; goto end; } @@ -546,38 +799,28 @@ int main(int argc, char * const argv[]) case 'v': verbose++; break; - case 'c': - opt_driver = optarg; - break; case 'w': opt_wait = 1; break; + default: + util_print_usage(app_name, options, option_help, NULL); + if (opt_apdus) + free(opt_apdus); + return 2; } } - if (action_count == 0) - util_print_usage_and_die(app_name, options, option_help, NULL); - -//#if (OPENSSL_VERSION_NUMBER >= 0x00907000L && OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) -// OPENSSL_config(NULL); -//#endif -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS - | OPENSSL_INIT_ADD_ALL_CIPHERS - | OPENSSL_INIT_ADD_ALL_DIGESTS, - NULL); -#else - /* OpenSSL magic */ - OPENSSL_malloc_init(); - ERR_load_crypto_strings(); - OpenSSL_add_all_algorithms(); - -#endif + if (action_count == 0) { + util_print_usage(app_name, options, option_help, NULL); + if (opt_apdus) + free(opt_apdus); + return 2; + } if (out_file) { bp = BIO_new(BIO_s_file()); if (!BIO_write_filename(bp, (char *)out_file)) - goto end; + goto end; } else { bp = BIO_new(BIO_s_file()); BIO_set_fp(bp,stdout,BIO_NOCLOSE); @@ -585,6 +828,9 @@ int main(int argc, char * const argv[]) memset(&ctx_param, 0, sizeof(sc_context_param_t)); ctx_param.app_name = app_name; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; r = sc_context_create(&ctx, &ctx_param); if (r != SC_SUCCESS) { @@ -592,28 +838,28 @@ int main(int argc, char * const argv[]) return 1; } - /* Only change if not in opensc.conf */ - if (verbose > 1 && ctx->debug == 0) { - ctx->debug = verbose; - sc_ctx_log_to_file(ctx, "stderr"); - } - if (action_count <= 0) goto end; - if (opt_driver != NULL) { - err = sc_set_card_driver(ctx, opt_driver); - if (err) { - fprintf(stderr, "Driver '%s' not found!\n", opt_driver); - err = 1; - goto end; - } + /* force PIV card driver */ + err = sc_set_card_driver(ctx, "PIV-II"); + if (err) { + fprintf(stderr, "PIV card driver not found!\n"); + err = 1; + goto end; } - err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); + err = util_connect_card(ctx, &card, opt_reader, opt_wait); if (err) goto end; + /* fail if card is not a PIV card */ + if (card->type < SC_CARD_TYPE_PIV_II_BASE || card->type >= SC_CARD_TYPE_PIV_II_BASE+1000) { + fprintf(stderr, "Card type %X: not a PIV card\n", card->type); + err = 1; + goto end; + } + if (do_admin_mode) { if ((err = admin_mode(admin_info))) goto end; @@ -658,8 +904,9 @@ int main(int argc, char * const argv[]) sc_unlock(card); sc_disconnect_card(card); } - if (ctx) - sc_release_context(ctx); + if (opt_apdus) + free(opt_apdus); + sc_release_context(ctx); ERR_print_errors_fp(stderr); return err; diff --git a/src/tools/pkcs11-register-cmdline.c b/src/tools/pkcs11-register-cmdline.c new file mode 100644 index 0000000000..1c6a11e781 --- /dev/null +++ b/src/tools/pkcs11-register-cmdline.c @@ -0,0 +1,673 @@ +/* + File autogenerated by gengetopt version 2.23 + generated with the following command: + /opt/homebrew/bin/gengetopt --file-name=pkcs11-register-cmdline --output-dir=. + + The developers of gengetopt consider the fixed text that goes in all + gengetopt output files to be in the public domain: + we make no copyright claims on it. +*/ + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#ifndef FIX_UNUSED +#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ +#endif + +#include + +#include "pkcs11-register-cmdline.h" + +const char *gengetopt_args_info_purpose = ""; + +const char *gengetopt_args_info_usage = "Usage: pkcs11-register [OPTION]..."; + +const char *gengetopt_args_info_versiontext = ""; + +const char *gengetopt_args_info_description = "Install a PKCS#11 module to known applications."; + +const char *gengetopt_args_info_help[] = { + " -h, --help Print help and exit", + " -V, --version Print version and exit", + " -m, --module=FILENAME Specify the module to load (default=`OpenSC's\n PKCS#11 module')", + " --skip-chrome=ENUM Don't install module to Chrome (possible\n values=\"on\", \"off\" default=`on')", + " --skip-firefox=ENUM Don't install module to Firefox (possible\n values=\"on\", \"off\" default=`on')", + " --skip-thunderbird=ENUM Don't install module to Thunderbird (possible\n values=\"on\", \"off\" default=`off')", + " --skip-seamonkey=ENUM Don't install module to SeaMonkey (possible\n values=\"on\", \"off\" default=`off')", + "\nReport bugs to https://github.com/OpenSC/OpenSC/issues\n\nWritten by Frank Morgner ", + 0 +}; + +typedef enum {ARG_NO + , ARG_STRING + , ARG_ENUM +} cmdline_parser_arg_type; + +static +void clear_given (struct gengetopt_args_info *args_info); +static +void clear_args (struct gengetopt_args_info *args_info); + +static int +cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params, const char *additional_error); + + +const char *cmdline_parser_skip_chrome_values[] = {"on", "off", 0}; /*< Possible values for skip-chrome. */ +const char *cmdline_parser_skip_firefox_values[] = {"on", "off", 0}; /*< Possible values for skip-firefox. */ +const char *cmdline_parser_skip_thunderbird_values[] = {"on", "off", 0}; /*< Possible values for skip-thunderbird. */ +const char *cmdline_parser_skip_seamonkey_values[] = {"on", "off", 0}; /*< Possible values for skip-seamonkey. */ + +static char * +gengetopt_strdup (const char *s); + +static +void clear_given (struct gengetopt_args_info *args_info) +{ + args_info->help_given = 0 ; + args_info->version_given = 0 ; + args_info->module_given = 0 ; + args_info->skip_chrome_given = 0 ; + args_info->skip_firefox_given = 0 ; + args_info->skip_thunderbird_given = 0 ; + args_info->skip_seamonkey_given = 0 ; +} + +static +void clear_args (struct gengetopt_args_info *args_info) +{ + FIX_UNUSED (args_info); + args_info->module_arg = gengetopt_strdup ("OpenSC's PKCS#11 module"); + args_info->module_orig = NULL; + args_info->skip_chrome_arg = skip_chrome_arg_on; + args_info->skip_chrome_orig = NULL; + args_info->skip_firefox_arg = skip_firefox_arg_on; + args_info->skip_firefox_orig = NULL; + args_info->skip_thunderbird_arg = skip_thunderbird_arg_off; + args_info->skip_thunderbird_orig = NULL; + args_info->skip_seamonkey_arg = skip_seamonkey_arg_off; + args_info->skip_seamonkey_orig = NULL; + +} + +static +void init_args_info(struct gengetopt_args_info *args_info) +{ + + + args_info->help_help = gengetopt_args_info_help[0] ; + args_info->version_help = gengetopt_args_info_help[1] ; + args_info->module_help = gengetopt_args_info_help[2] ; + args_info->skip_chrome_help = gengetopt_args_info_help[3] ; + args_info->skip_firefox_help = gengetopt_args_info_help[4] ; + args_info->skip_thunderbird_help = gengetopt_args_info_help[5] ; + args_info->skip_seamonkey_help = gengetopt_args_info_help[6] ; + +} + +void +cmdline_parser_print_version (void) +{ + printf ("%s %s\n", + (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE), + CMDLINE_PARSER_VERSION); + + if (strlen(gengetopt_args_info_versiontext) > 0) + printf("\n%s\n", gengetopt_args_info_versiontext); +} + +static void print_help_common(void) +{ + size_t len_purpose = strlen(gengetopt_args_info_purpose); + size_t len_usage = strlen(gengetopt_args_info_usage); + + if (len_usage > 0) { + printf("%s\n", gengetopt_args_info_usage); + } + if (len_purpose > 0) { + printf("%s\n", gengetopt_args_info_purpose); + } + + if (len_usage || len_purpose) { + printf("\n"); + } + + if (strlen(gengetopt_args_info_description) > 0) { + printf("%s\n\n", gengetopt_args_info_description); + } +} + +void +cmdline_parser_print_help (void) +{ + int i = 0; + print_help_common(); + while (gengetopt_args_info_help[i]) + printf("%s\n", gengetopt_args_info_help[i++]); +} + +void +cmdline_parser_init (struct gengetopt_args_info *args_info) +{ + clear_given (args_info); + clear_args (args_info); + init_args_info (args_info); +} + +void +cmdline_parser_params_init(struct cmdline_parser_params *params) +{ + if (params) + { + params->override = 0; + params->initialize = 1; + params->check_required = 1; + params->check_ambiguity = 0; + params->print_errors = 1; + } +} + +struct cmdline_parser_params * +cmdline_parser_params_create(void) +{ + struct cmdline_parser_params *params = + (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params)); + cmdline_parser_params_init(params); + return params; +} + +static void +free_string_field (char **s) +{ + if (*s) + { + free (*s); + *s = 0; + } +} + + +static void +cmdline_parser_release (struct gengetopt_args_info *args_info) +{ + + free_string_field (&(args_info->module_arg)); + free_string_field (&(args_info->module_orig)); + free_string_field (&(args_info->skip_chrome_orig)); + free_string_field (&(args_info->skip_firefox_orig)); + free_string_field (&(args_info->skip_thunderbird_orig)); + free_string_field (&(args_info->skip_seamonkey_orig)); + + + + clear_given (args_info); +} + +/** + * @param val the value to check + * @param values the possible values + * @return the index of the matched value: + * -1 if no value matched, + * -2 if more than one value has matched + */ +static int +check_possible_values(const char *val, const char *values[]) +{ + int i, found, last; + size_t len; + + if (!val) /* otherwise strlen() crashes below */ + return -1; /* -1 means no argument for the option */ + + found = last = 0; + + for (i = 0, len = strlen(val); values[i]; ++i) + { + if (strncmp(val, values[i], len) == 0) + { + ++found; + last = i; + if (strlen(values[i]) == len) + return i; /* exact match no need to check more */ + } + } + + if (found == 1) /* one match: OK */ + return last; + + return (found ? -2 : -1); /* return many values or none matched */ +} + + +static void +write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[]) +{ + int found = -1; + if (arg) { + if (values) { + found = check_possible_values(arg, values); + } + if (found >= 0) + fprintf(outfile, "%s=\"%s\" # %s\n", opt, arg, values[found]); + else + fprintf(outfile, "%s=\"%s\"\n", opt, arg); + } else { + fprintf(outfile, "%s\n", opt); + } +} + + +int +cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) +{ + int i = 0; + + if (!outfile) + { + fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE); + return EXIT_FAILURE; + } + + if (args_info->help_given) + write_into_file(outfile, "help", 0, 0 ); + if (args_info->version_given) + write_into_file(outfile, "version", 0, 0 ); + if (args_info->module_given) + write_into_file(outfile, "module", args_info->module_orig, 0); + if (args_info->skip_chrome_given) + write_into_file(outfile, "skip-chrome", args_info->skip_chrome_orig, cmdline_parser_skip_chrome_values); + if (args_info->skip_firefox_given) + write_into_file(outfile, "skip-firefox", args_info->skip_firefox_orig, cmdline_parser_skip_firefox_values); + if (args_info->skip_thunderbird_given) + write_into_file(outfile, "skip-thunderbird", args_info->skip_thunderbird_orig, cmdline_parser_skip_thunderbird_values); + if (args_info->skip_seamonkey_given) + write_into_file(outfile, "skip-seamonkey", args_info->skip_seamonkey_orig, cmdline_parser_skip_seamonkey_values); + + + i = EXIT_SUCCESS; + return i; +} + +int +cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) +{ + FILE *outfile; + int i = 0; + + outfile = fopen(filename, "w"); + + if (!outfile) + { + fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); + return EXIT_FAILURE; + } + + i = cmdline_parser_dump(outfile, args_info); + fclose (outfile); + + return i; +} + +void +cmdline_parser_free (struct gengetopt_args_info *args_info) +{ + cmdline_parser_release (args_info); +} + +/** @brief replacement of strdup, which is not standard */ +char * +gengetopt_strdup (const char *s) +{ + char *result = 0; + if (!s) + return result; + + result = (char*)malloc(strlen(s) + 1); + if (result == (char*)0) + return (char*)0; + strcpy(result, s); + return result; +} + +int +cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info) +{ + return cmdline_parser2 (argc, argv, args_info, 0, 1, 1); +} + +int +cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params) +{ + int result; + result = cmdline_parser_internal (argc, argv, args_info, params, 0); + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) +{ + int result; + struct cmdline_parser_params params; + + params.override = override; + params.initialize = initialize; + params.check_required = check_required; + params.check_ambiguity = 0; + params.print_errors = 1; + + result = cmdline_parser_internal (argc, argv, args_info, ¶ms, 0); + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name) +{ + FIX_UNUSED (args_info); + FIX_UNUSED (prog_name); + return EXIT_SUCCESS; +} + + +static char *package_name = 0; + +/** + * @brief updates an option + * @param field the generic pointer to the field to update + * @param orig_field the pointer to the orig field + * @param field_given the pointer to the number of occurrence of this option + * @param prev_given the pointer to the number of occurrence already seen + * @param value the argument for this option (if null no arg was specified) + * @param possible_values the possible values for this option (if specified) + * @param default_value the default value (in case the option only accepts fixed values) + * @param arg_type the type of this option + * @param check_ambiguity @see cmdline_parser_params.check_ambiguity + * @param override @see cmdline_parser_params.override + * @param no_free whether to free a possible previous value + * @param multiple_option whether this is a multiple option + * @param long_opt the corresponding long option + * @param short_opt the corresponding short option (or '-' if none) + * @param additional_error possible further error specification + */ +static +int update_arg(void *field, char **orig_field, + unsigned int *field_given, unsigned int *prev_given, + char *value, const char *possible_values[], + const char *default_value, + cmdline_parser_arg_type arg_type, + int check_ambiguity, int override, + int no_free, int multiple_option, + const char *long_opt, char short_opt, + const char *additional_error) +{ + char *stop_char = 0; + const char *val = value; + int found; + char **string_field; + FIX_UNUSED (field); + + stop_char = 0; + found = 0; + + if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given))) + { + if (short_opt != '-') + fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", + package_name, long_opt, short_opt, + (additional_error ? additional_error : "")); + else + fprintf (stderr, "%s: `--%s' option given more than once%s\n", + package_name, long_opt, + (additional_error ? additional_error : "")); + return 1; /* failure */ + } + + if (possible_values && (found = check_possible_values((value ? value : default_value), possible_values)) < 0) + { + if (short_opt != '-') + fprintf (stderr, "%s: %s argument, \"%s\", for option `--%s' (`-%c')%s\n", + package_name, (found == -2) ? "ambiguous" : "invalid", value, long_opt, short_opt, + (additional_error ? additional_error : "")); + else + fprintf (stderr, "%s: %s argument, \"%s\", for option `--%s'%s\n", + package_name, (found == -2) ? "ambiguous" : "invalid", value, long_opt, + (additional_error ? additional_error : "")); + return 1; /* failure */ + } + + if (field_given && *field_given && ! override) + return 0; + if (prev_given) + (*prev_given)++; + if (field_given) + (*field_given)++; + if (possible_values) + val = possible_values[found]; + + switch(arg_type) { + case ARG_ENUM: + if (val) *((int *)field) = found; + break; + case ARG_STRING: + if (val) { + string_field = (char **)field; + if (!no_free && *string_field) + free (*string_field); /* free previous string */ + *string_field = gengetopt_strdup (val); + } + break; + default: + break; + }; + + FIX_UNUSED(stop_char); + + /* store the original value */ + switch(arg_type) { + case ARG_NO: + break; + default: + if (value && orig_field) { + if (no_free) { + *orig_field = value; + } else { + if (*orig_field) + free (*orig_field); /* free previous string */ + *orig_field = gengetopt_strdup (value); + } + } + }; + + return 0; /* OK */ +} + + +int +cmdline_parser_internal ( + int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params, const char *additional_error) +{ + int c; /* Character of the parsed option. */ + + int error_occurred = 0; + struct gengetopt_args_info local_args_info; + + int override; + int initialize; + int check_required; + int check_ambiguity; + + package_name = argv[0]; + + /* TODO: Why is this here? It is not used anywhere. */ + override = params->override; + FIX_UNUSED(override); + + initialize = params->initialize; + check_required = params->check_required; + + /* TODO: Why is this here? It is not used anywhere. */ + check_ambiguity = params->check_ambiguity; + FIX_UNUSED(check_ambiguity); + + if (initialize) + cmdline_parser_init (args_info); + + cmdline_parser_init (&local_args_info); + + optarg = 0; + optind = 0; + opterr = params->print_errors; + optopt = '?'; + + while (1) + { + int option_index = 0; + + static struct option long_options[] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { "module", 1, NULL, 'm' }, + { "skip-chrome", 1, NULL, 0 }, + { "skip-firefox", 1, NULL, 0 }, + { "skip-thunderbird", 1, NULL, 0 }, + { "skip-seamonkey", 1, NULL, 0 }, + { 0, 0, 0, 0 } + }; + + c = getopt_long (argc, argv, "hVm:", long_options, &option_index); + + if (c == -1) break; /* Exit from `while (1)' loop. */ + + switch (c) + { + case 'h': /* Print help and exit. */ + cmdline_parser_print_help (); + cmdline_parser_free (&local_args_info); + exit (EXIT_SUCCESS); + + case 'V': /* Print version and exit. */ + cmdline_parser_print_version (); + cmdline_parser_free (&local_args_info); + exit (EXIT_SUCCESS); + + case 'm': /* Specify the module to load. */ + + + if (update_arg( (void *)&(args_info->module_arg), + &(args_info->module_orig), &(args_info->module_given), + &(local_args_info.module_given), optarg, 0, "OpenSC's PKCS#11 module", ARG_STRING, + check_ambiguity, override, 0, 0, + "module", 'm', + additional_error)) + goto failure; + + break; + + case 0: /* Long option with no short option */ + /* Don't install module to Chrome. */ + if (strcmp (long_options[option_index].name, "skip-chrome") == 0) + { + + + if (update_arg( (void *)&(args_info->skip_chrome_arg), + &(args_info->skip_chrome_orig), &(args_info->skip_chrome_given), + &(local_args_info.skip_chrome_given), optarg, cmdline_parser_skip_chrome_values, "on", ARG_ENUM, + check_ambiguity, override, 0, 0, + "skip-chrome", '-', + additional_error)) + goto failure; + + } + /* Don't install module to Firefox. */ + else if (strcmp (long_options[option_index].name, "skip-firefox") == 0) + { + + + if (update_arg( (void *)&(args_info->skip_firefox_arg), + &(args_info->skip_firefox_orig), &(args_info->skip_firefox_given), + &(local_args_info.skip_firefox_given), optarg, cmdline_parser_skip_firefox_values, "on", ARG_ENUM, + check_ambiguity, override, 0, 0, + "skip-firefox", '-', + additional_error)) + goto failure; + + } + /* Don't install module to Thunderbird. */ + else if (strcmp (long_options[option_index].name, "skip-thunderbird") == 0) + { + + + if (update_arg( (void *)&(args_info->skip_thunderbird_arg), + &(args_info->skip_thunderbird_orig), &(args_info->skip_thunderbird_given), + &(local_args_info.skip_thunderbird_given), optarg, cmdline_parser_skip_thunderbird_values, "off", ARG_ENUM, + check_ambiguity, override, 0, 0, + "skip-thunderbird", '-', + additional_error)) + goto failure; + + } + /* Don't install module to SeaMonkey. */ + else if (strcmp (long_options[option_index].name, "skip-seamonkey") == 0) + { + + + if (update_arg( (void *)&(args_info->skip_seamonkey_arg), + &(args_info->skip_seamonkey_orig), &(args_info->skip_seamonkey_given), + &(local_args_info.skip_seamonkey_given), optarg, cmdline_parser_skip_seamonkey_values, "off", ARG_ENUM, + check_ambiguity, override, 0, 0, + "skip-seamonkey", '-', + additional_error)) + goto failure; + + } + + break; + case '?': /* Invalid option. */ + /* `getopt_long' already printed an error message. */ + goto failure; + + default: /* bug: option not considered. */ + fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); + abort (); + } /* switch */ + } /* while */ + + + + FIX_UNUSED(check_required); + + cmdline_parser_release (&local_args_info); + + if ( error_occurred ) + return (EXIT_FAILURE); + + return 0; + +failure: + + cmdline_parser_release (&local_args_info); + return (EXIT_FAILURE); +} +/* vim: set ft=c noet ts=8 sts=8 sw=8 tw=80 nojs spell : */ diff --git a/src/tools/pkcs11-register-cmdline.h b/src/tools/pkcs11-register-cmdline.h new file mode 100644 index 0000000000..16d5179b35 --- /dev/null +++ b/src/tools/pkcs11-register-cmdline.h @@ -0,0 +1,203 @@ +/** @file pkcs11-register-cmdline.h + * @brief The header file for the command line option parser + * generated by GNU Gengetopt version 2.23 + * http://www.gnu.org/software/gengetopt. + * DO NOT modify this file, since it can be overwritten + * @author GNU Gengetopt */ + +#ifndef PKCS11_REGISTER_CMDLINE_H +#define PKCS11_REGISTER_CMDLINE_H + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include /* for FILE */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef CMDLINE_PARSER_PACKAGE +/** @brief the program name (used for printing errors) */ +#define CMDLINE_PARSER_PACKAGE "pkcs11-register" +#endif + +#ifndef CMDLINE_PARSER_PACKAGE_NAME +/** @brief the complete program name (used for help and version) */ +#define CMDLINE_PARSER_PACKAGE_NAME "pkcs11-register" +#endif + +#ifndef CMDLINE_PARSER_VERSION +/** @brief the program version */ +#define CMDLINE_PARSER_VERSION VERSION +#endif + +enum enum_skip_chrome { skip_chrome__NULL = -1, skip_chrome_arg_on = 0, skip_chrome_arg_off }; +enum enum_skip_firefox { skip_firefox__NULL = -1, skip_firefox_arg_on = 0, skip_firefox_arg_off }; +enum enum_skip_thunderbird { skip_thunderbird__NULL = -1, skip_thunderbird_arg_on = 0, skip_thunderbird_arg_off }; +enum enum_skip_seamonkey { skip_seamonkey__NULL = -1, skip_seamonkey_arg_on = 0, skip_seamonkey_arg_off }; + +/** @brief Where the command line options are stored */ +struct gengetopt_args_info +{ + const char *help_help; /**< @brief Print help and exit help description. */ + const char *version_help; /**< @brief Print version and exit help description. */ + char * module_arg; /**< @brief Specify the module to load (default='OpenSC's PKCS#11 module'). */ + char * module_orig; /**< @brief Specify the module to load original value given at command line. */ + const char *module_help; /**< @brief Specify the module to load help description. */ + enum enum_skip_chrome skip_chrome_arg; /**< @brief Don't install module to Chrome (default='on'). */ + char * skip_chrome_orig; /**< @brief Don't install module to Chrome original value given at command line. */ + const char *skip_chrome_help; /**< @brief Don't install module to Chrome help description. */ + enum enum_skip_firefox skip_firefox_arg; /**< @brief Don't install module to Firefox (default='on'). */ + char * skip_firefox_orig; /**< @brief Don't install module to Firefox original value given at command line. */ + const char *skip_firefox_help; /**< @brief Don't install module to Firefox help description. */ + enum enum_skip_thunderbird skip_thunderbird_arg; /**< @brief Don't install module to Thunderbird (default='off'). */ + char * skip_thunderbird_orig; /**< @brief Don't install module to Thunderbird original value given at command line. */ + const char *skip_thunderbird_help; /**< @brief Don't install module to Thunderbird help description. */ + enum enum_skip_seamonkey skip_seamonkey_arg; /**< @brief Don't install module to SeaMonkey (default='off'). */ + char * skip_seamonkey_orig; /**< @brief Don't install module to SeaMonkey original value given at command line. */ + const char *skip_seamonkey_help; /**< @brief Don't install module to SeaMonkey help description. */ + + unsigned int help_given ; /**< @brief Whether help was given. */ + unsigned int version_given ; /**< @brief Whether version was given. */ + unsigned int module_given ; /**< @brief Whether module was given. */ + unsigned int skip_chrome_given ; /**< @brief Whether skip-chrome was given. */ + unsigned int skip_firefox_given ; /**< @brief Whether skip-firefox was given. */ + unsigned int skip_thunderbird_given ; /**< @brief Whether skip-thunderbird was given. */ + unsigned int skip_seamonkey_given ; /**< @brief Whether skip-seamonkey was given. */ + +} ; + +/** @brief The additional parameters to pass to parser functions */ +struct cmdline_parser_params +{ + int override; /**< @brief whether to override possibly already present options (default 0) */ + int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */ + int check_required; /**< @brief whether to check that all required options were provided (default 1) */ + int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */ + int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */ +} ; + +/** @brief the purpose string of the program */ +extern const char *gengetopt_args_info_purpose; +/** @brief the usage string of the program */ +extern const char *gengetopt_args_info_usage; +/** @brief the description string of the program */ +extern const char *gengetopt_args_info_description; +/** @brief all the lines making the help output */ +extern const char *gengetopt_args_info_help[]; + +/** + * The command line parser + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser (int argc, char **argv, + struct gengetopt_args_info *args_info); + +/** + * The command line parser (version with additional parameters - deprecated) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param override whether to override possibly already present options + * @param initialize whether to initialize the option structure my_args_info + * @param check_required whether to check that all required options were provided + * @return 0 if everything went fine, NON 0 if an error took place + * @deprecated use cmdline_parser_ext() instead + */ +int cmdline_parser2 (int argc, char **argv, + struct gengetopt_args_info *args_info, + int override, int initialize, int check_required); + +/** + * The command line parser (version with additional parameters) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param params additional parameters for the parser + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_ext (int argc, char **argv, + struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params); + +/** + * Save the contents of the option struct into an already open FILE stream. + * @param outfile the stream where to dump options + * @param args_info the option struct to dump + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_dump(FILE *outfile, + struct gengetopt_args_info *args_info); + +/** + * Save the contents of the option struct into a (text) file. + * This file can be read by the config file parser (if generated by gengetopt) + * @param filename the file where to save + * @param args_info the option struct to save + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_file_save(const char *filename, + struct gengetopt_args_info *args_info); + +/** + * Print the help + */ +void cmdline_parser_print_help(void); +/** + * Print the version + */ +void cmdline_parser_print_version(void); + +/** + * Initializes all the fields a cmdline_parser_params structure + * to their default values + * @param params the structure to initialize + */ +void cmdline_parser_params_init(struct cmdline_parser_params *params); + +/** + * Allocates dynamically a cmdline_parser_params structure and initializes + * all its fields to their default values + * @return the created and initialized cmdline_parser_params structure + */ +struct cmdline_parser_params *cmdline_parser_params_create(void); + +/** + * Initializes the passed gengetopt_args_info structure's fields + * (also set default values for options that have a default) + * @param args_info the structure to initialize + */ +void cmdline_parser_init (struct gengetopt_args_info *args_info); +/** + * Deallocates the string fields of the gengetopt_args_info structure + * (but does not deallocate the structure itself) + * @param args_info the structure to deallocate + */ +void cmdline_parser_free (struct gengetopt_args_info *args_info); + +/** + * Checks that all the required options were specified + * @param args_info the structure to check + * @param prog_name the name of the program that will be used to print + * possible errors + * @return + */ +int cmdline_parser_required (struct gengetopt_args_info *args_info, + const char *prog_name); + +extern const char *cmdline_parser_skip_chrome_values[]; /**< @brief Possible values for skip-chrome. */ +extern const char *cmdline_parser_skip_firefox_values[]; /**< @brief Possible values for skip-firefox. */ +extern const char *cmdline_parser_skip_thunderbird_values[]; /**< @brief Possible values for skip-thunderbird. */ +extern const char *cmdline_parser_skip_seamonkey_values[]; /**< @brief Possible values for skip-seamonkey. */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PKCS11_REGISTER_CMDLINE_H */ diff --git a/src/tools/pkcs11-register.c b/src/tools/pkcs11-register.c new file mode 100644 index 0000000000..20a86213e6 --- /dev/null +++ b/src/tools/pkcs11-register.c @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2019 Frank Morgner + * + * This file is part of OpenSC. + * + * 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 + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fread_to_eof.h" +#include "pkcs11-register-cmdline.h" +#include +#include +#include + +#ifdef _WIN32 +#include +const char path_sep = '\\'; +#else +const char path_sep = '/'; +#endif + +const char *default_pkcs11_provider = DEFAULT_PKCS11_PROVIDER; +const char *default_onepin_pkcs11_provider = DEFAULT_ONEPIN_PKCS11_PROVIDER; + +int +get_profiles_ini(const char *home, const char *basedir, char **profiles_ini) +{ + size_t profiles_ini_len = 0; + char profiles_ini_path[PATH_MAX]; + if (home && basedir + && 0 <= snprintf(profiles_ini_path, sizeof profiles_ini_path, + "%s%c%s%c%s", home, path_sep, basedir, path_sep, "profiles.ini") + && fread_to_eof(profiles_ini_path, + (unsigned char **) profiles_ini, &profiles_ini_len)) { + char *p = realloc(*profiles_ini, profiles_ini_len+1); + if (p) { + p[profiles_ini_len] = '\0'; + *profiles_ini = p; + return 1; + } + } + return 0; +} + +const char * +get_next_profile_path(const char **profiles_ini, const char *home, const char *basedir) +{ + static char profile_path[PATH_MAX]; + + if (!home || !profiles_ini) + return NULL; + + while (*profiles_ini) { + const char *this_profile = strstr(*profiles_ini, "["); + if (!this_profile) { + return NULL; + } + + const char *next_profile = strstr(this_profile + 1, "["); + const char *is_relative = strstr(this_profile, "IsRelative=1"); + const char *path = strstr(this_profile, "Path="); + + /* advance profile_ini for the next iteration */ + if (next_profile) { + *profiles_ini = next_profile; + + if (next_profile < path) { + /* path belongs to the next profile */ + path = NULL; + } + if (next_profile < is_relative) { + /* IsRelative belongs to the next profile */ + is_relative = NULL; + } + } else { + *profiles_ini = NULL; + } + + if (!path) + continue; + + /* build the path to the profile */ + char *p = profile_path; + size_t p_len = sizeof profile_path; + if (is_relative) { + size_t l = strlen(home) + sizeof path_sep + strlen(basedir) + sizeof path_sep; + if (0 > snprintf(p, p_len, "%s%c%s%c", home, path_sep, basedir, path_sep)) + continue; + p_len -= l; + p += l; + } + /* adjust format to respect the maximum length of profile_path */ + char format[32]; + if (0 > snprintf(format, sizeof(format), "Path=%%%d[^\n]", (int)(p_len-1)) + || 1 != sscanf(path, format, p)) + continue; + + return profile_path; + } + + return NULL; +} + +void +add_module_pkcs11_txt(const char *profile_dir, + const char *module_path, const char *module_name, const char *exclude_module_path) +{ + char pkcs11_txt_path[PATH_MAX]; + char *pkcs11_txt = NULL; + size_t pkcs11_txt_len = 0; + unsigned char *txt = NULL; + + if (!profile_dir + || snprintf(pkcs11_txt_path, sizeof pkcs11_txt_path, + "%s%c%s", profile_dir, path_sep, "pkcs11.txt") < 0 + || !fread_to_eof(pkcs11_txt_path, &txt, &pkcs11_txt_len)) { + goto err; + } + pkcs11_txt = (char *)txt; + char *p = realloc(pkcs11_txt, pkcs11_txt_len+1); + if (!p) + goto err; + p[pkcs11_txt_len] = '\0'; + pkcs11_txt = p; + + if (!strstr(pkcs11_txt, module_path) + && (!exclude_module_path || !strstr(pkcs11_txt, exclude_module_path))) { + /* module is not yet present */ + FILE *f = fopen(pkcs11_txt_path, "a"); + if (f) { + if (fprintf(f, + "library=%s\n" + "name=%s\n" + "\n", module_path, module_name) >= 0) { + printf("Added %s to %s\n", module_name, pkcs11_txt_path); + } + fclose(f); + } + } +err: + free(pkcs11_txt); +} + +struct location { + const char *var; + const char *dir; +}; + +void +add_module_mozilla(const struct location *locations, size_t locations_len, + const char *module_path, const char *module_name, const char *exclude_module_path) +{ + size_t i; + + for (i = 0; i < locations_len; i++) { + char *profiles_ini = NULL; + const char *home = getenv(locations[i].var); + if (!home) + continue; + + if (get_profiles_ini(home, locations[i].dir, &profiles_ini)) { + const char *p = profiles_ini; + + while (1) { + const char *profile_path = get_next_profile_path(&p, home, locations[i].dir); + if (!profile_path) + break; + add_module_pkcs11_txt(profile_path, module_path, module_name, exclude_module_path); + } + } + free(profiles_ini); + } +} + +#include "pkcs11/pkcs11.h" +#include "common/libpkcs11.h" + +const char * +get_module_name(const char *module_path) +{ + const char *name = NULL; + CK_FUNCTION_LIST_PTR p11 = NULL; + void *module = C_LoadModule(module_path, &p11); + if (module) { + CK_INFO info; + if (CKR_OK == p11->C_Initialize(NULL) + && CKR_OK == p11->C_GetInfo(&info)) { + static char module_name[32+sizeof " (255.255)"]; + int libraryDescription_len = 32; + + while (libraryDescription_len > 0 + && info.libraryDescription[libraryDescription_len-1] == ' ') + libraryDescription_len--; + + snprintf(module_name, sizeof module_name, + "%.*s (%d.%d)", + libraryDescription_len, info.libraryDescription, + info.libraryVersion.major, info.libraryVersion.minor); + + name = module_name; + } + p11->C_Finalize(NULL); + C_UnloadModule(module); + } + return name; +} + +void +add_module_firefox(const char *module_path, const char *module_name, const char *exclude_module_path) +{ + struct location locations[] = { +#if defined(__APPLE__) + {"HOME", "Library/Application Support/Firefox"}, + {"HOME", "Library/Mozilla/Firefox"}, +#elif defined(_WIN32) + {"APPDATA", "Mozilla\\Firefox"}, +#else + {"XDG_CONFIG_HOME", "mozilla/firefox"}, /* Firefox >= 147 XDG profile */ + {"HOME", ".config/mozilla/firefox"}, /* XDG default when XDG_CONFIG_HOME unset */ + {"HOME", ".mozilla/firefox"}, + {"HOME", ".mozilla/firefox-esr"}, +#endif + }; + + add_module_mozilla(locations, sizeof locations/sizeof *locations, + module_path, module_name, exclude_module_path); +} + +void +add_module_thunderbird(const char *module_path, const char *module_name, const char *exclude_module_path) +{ + struct location locations[] = { +#if defined(__APPLE__) + {"HOME", "Library/Application Support/Thunderbird"}, + {"HOME", "Library/Mozilla/Thunderbird"}, +#elif defined(_WIN32) + {"APPDATA", "Mozilla\\Thunderbird"}, +#else + {"HOME", ".thunderbird"}, + {"HOME", ".mozilla-thunderbird"}, +#endif + }; + + add_module_mozilla(locations, sizeof locations/sizeof *locations, + module_path, module_name, exclude_module_path); +} + +void +add_module_seamonkey(const char *module_path, const char *module_name, const char *exclude_module_path) +{ + struct location locations[] = { +#if defined(__APPLE__) + {"HOME", "Library/Application Support/SeaMonkey"}, + {"HOME", "Library/Mozilla/SeaMonkey"}, +#elif defined(_WIN32) + {"APPDATA", "Mozilla\\SeaMonkey"}, +#else + {"HOME", ".mozilla/seamonkey"}, +#endif + }; + + add_module_mozilla(locations, sizeof locations/sizeof *locations, + module_path, module_name, exclude_module_path); +} + +void +add_module_chrome(const char *module_path, const char *module_name, const char *exclude_module_path) +{ +#if defined(__APPLE__) || defined(_WIN32) + /* OS specific framework will be used by Chrome instead of PKCS#11 */ +#else + char profile_path[PATH_MAX]; + const char *home = getenv("HOME"); + + if (0 == strcmp(module_path, default_pkcs11_provider)) { + module_path = default_onepin_pkcs11_provider; + exclude_module_path = default_pkcs11_provider; + } + + if (home && 0 <= snprintf(profile_path, sizeof profile_path, + "%s%c%s", home, path_sep, ".pki/nssdb")) { + add_module_pkcs11_txt(profile_path, module_path, module_name, exclude_module_path); + } + + /* Chrome M146+ uses XDG_DATA_HOME/pki/nssdb when ~/.pki doesn't exist */ + const char *xdg_data_home = getenv("XDG_DATA_HOME"); + if (xdg_data_home && 0 <= snprintf(profile_path, sizeof profile_path, + "%s%c%s", xdg_data_home, path_sep, "pki/nssdb")) { + add_module_pkcs11_txt(profile_path, module_path, module_name, exclude_module_path); + } else if (home && 0 <= snprintf(profile_path, sizeof profile_path, + "%s%c%s", home, path_sep, ".local/share/pki/nssdb")) { + add_module_pkcs11_txt(profile_path, module_path, module_name, exclude_module_path); + } +#endif +} + +#define expand(path, expanded, len) \ + len = ExpandEnvironmentStringsA(path, \ + expanded, sizeof expanded); \ + if (0 < len && len < sizeof expanded) \ + path = expanded; + +int +main(int argc, char **argv) +{ + struct gengetopt_args_info cmdline; + const char *exclude_module_path = NULL; + + if (cmdline_parser(argc, argv, &cmdline) != 0) + return 1; + + const char *module_path = cmdline.module_arg; + if (!cmdline.module_given) { + module_path = default_pkcs11_provider; + exclude_module_path = default_onepin_pkcs11_provider; + } +#ifdef _WIN32 + DWORD expanded_len; + char module_path_expanded[PATH_MAX], default_expanded[PATH_MAX], onepin_expanded[PATH_MAX]; + expand(module_path, module_path_expanded, expanded_len); + expand(default_pkcs11_provider, default_expanded, expanded_len); + expand(default_onepin_pkcs11_provider, onepin_expanded, expanded_len); +#endif + + const char *module_name = get_module_name(module_path); + if (!module_name) { + fprintf(stderr, "Could not load initialize %s\n", module_path); + cmdline_parser_free (&cmdline); + return 1; + } + + if (cmdline.skip_chrome_arg != skip_chrome_arg_on) + add_module_chrome(module_path, module_name, exclude_module_path); + if (cmdline.skip_firefox_arg != skip_firefox_arg_on) + add_module_firefox(module_path, module_name, exclude_module_path); + if (cmdline.skip_thunderbird_arg != skip_thunderbird_arg_on) + add_module_thunderbird(module_path, module_name, exclude_module_path); + if (cmdline.skip_seamonkey_arg != skip_seamonkey_arg_on) + add_module_seamonkey(module_path, module_name, exclude_module_path); + + cmdline_parser_free (&cmdline); + + return 0; +} diff --git a/src/tools/pkcs11-register.desktop.in b/src/tools/pkcs11-register.desktop.in new file mode 100644 index 0000000000..3a2e55907d --- /dev/null +++ b/src/tools/pkcs11-register.desktop.in @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=Install OpenSC PKCS#11 module +Type=Application +Exec=@bindir@/pkcs11-register --skip-chrome=off --skip-firefox=off +Categories=Security;System; +NoDisplay=true diff --git a/src/tools/pkcs11-register.ggo.in b/src/tools/pkcs11-register.ggo.in new file mode 100644 index 0000000000..b6e432ea7f --- /dev/null +++ b/src/tools/pkcs11-register.ggo.in @@ -0,0 +1,32 @@ +package "pkcs11-register" +purpose "@PACKAGE_SUMMARY@" +description "Install a PKCS#11 module to known applications." + +option "module" m + "Specify the module to load" + string optional default="OpenSC's PKCS#11 module" typestr="FILENAME" + +option "skip-chrome" - + "Don't install module to Chrome" + enum values="on","off" optional + default="@PKCS11_REGISTER_SKIP_FIREFOX@" + +option "skip-firefox" - + "Don't install module to Firefox" + enum values="on","off" optional + default="@PKCS11_REGISTER_SKIP_FIREFOX@" + +option "skip-thunderbird" - + "Don't install module to Thunderbird" + enum values="on","off" optional + default="off" + +option "skip-seamonkey" - + "Don't install module to SeaMonkey" + enum values="on","off" optional + default="off" + +text " +Report bugs to @PACKAGE_BUGREPORT@ + +Written by Frank Morgner " diff --git a/src/tools/pkcs11-tool.c b/src/tools/pkcs11-tool.c index 665318c250..e74c180e20 100644 --- a/src/tools/pkcs11-tool.c +++ b/src/tools/pkcs11-tool.c @@ -15,41 +15,48 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include +#include +#include +#include #include #include #ifndef _WIN32 -#include #include #include +#include +#ifdef HAVE_PTHREAD +#include +#endif #else +#include #include #endif #ifdef ENABLE_OPENSSL #include -#include "libopensc/sc-ossl-compat.h" -#if OPENSSL_VERSION_NUMBER >= 0x10000000L #include #include -#endif -#if OPENSSL_VERSION_NUMBER >= 0x00907000L #include -#endif #include #include #include #include #include #include -#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# include +# include +#include +#endif +#if !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) #include #include #endif @@ -57,62 +64,127 @@ #include #endif -#include "pkcs11/pkcs11.h" -#include "pkcs11/pkcs11-opensc.h" -#include "libopensc/asn1.h" #include "common/compat_strlcat.h" #include "common/compat_strlcpy.h" +#include "common/libpkcs11.h" +#include "libopensc/asn1.h" +#include "libopensc/internal.h" +#include "libopensc/log.h" +#include "libopensc/sc-ossl-compat.h" +#include "pkcs11/pkcs11-opensc.h" +#include "pkcs11/pkcs11.h" +#include "pkcs11_uri.h" #include "util.h" +/* pkcs11-tool uses libopensc routines that do not use an sc_context + * but does use some OpenSSL routines + */ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + static OSSL_PROVIDER *legacy_provider = NULL; + static OSSL_PROVIDER *default_provider = NULL; + static OSSL_LIB_CTX *osslctx = NULL; +#endif + #ifdef _WIN32 #ifndef STDOUT_FILENO #define STDOUT_FILENO 1 #endif #endif -extern void *C_LoadModule(const char *name, CK_FUNCTION_LIST_PTR_PTR); -extern CK_RV C_UnloadModule(void *module); +#ifndef ENABLE_SHARED +extern CK_FUNCTION_LIST_3_0 pkcs11_function_list_3_0; +#endif + +#if defined(_WIN32) || defined(HAVE_PTHREAD) +#define MAX_TEST_THREADS 10 +#endif + +#ifndef MIN +# define MIN(a, b) (((a) < (b))? (a) : (b)) +#endif #define NEED_SESSION_RO 0x01 #define NEED_SESSION_RW 0x02 +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +// clang-format off +/* EC, Edwards and Montgomery curves understood by pkcs11-tool */ static struct ec_curve_info { - const char *name; - const char *oid; - const char *oid_encoded; - size_t size; + const char *name; /* common name of curve */ + const char *oid; /* formatted printable OID */ + unsigned char *ec_params; /* der of OID or printable string */ + CK_ULONG ec_params_size; + CK_ULONG size; /* field_size in bits */ + CK_KEY_TYPE mechanism; } ec_curve_infos[] = { - {"secp192r1", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192}, - {"prime192v1", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192}, - {"prime192v2", "1.2.840.10045.3.1.2", "06082A8648CE3D030102", 192}, - {"prime192v3", "1.2.840.10045.3.1.3", "06082A8648CE3D030103", 192}, - {"nistp192", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192}, - {"ansiX9p192r1", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192}, - - {"secp224r1", "1.3.132.0.33", "06052b81040021", 224}, - {"nistp224", "1.3.132.0.33", "06052b81040021", 224}, - - {"prime256v1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, - {"secp256r1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, - {"ansiX9p256r1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, - {"frp256v1", "1.2.250.1.223.101.256.1", "060a2a817a01815f65820001", 256}, - - {"secp384r1", "1.3.132.0.34", "06052B81040022", 384}, - {"prime384v1", "1.3.132.0.34", "06052B81040022", 384}, - {"ansiX9p384r1", "1.3.132.0.34", "06052B81040022", 384}, - - {"secp521r1", "1.3.132.0.35", "06052B81040023", 521}, - {"nistp521", "1.3.132.0.35", "06052B81040023", 521}, - - {"brainpoolP192r1", "1.3.36.3.3.2.8.1.1.3", "06092B2403030208010103", 192}, - {"brainpoolP224r1", "1.3.36.3.3.2.8.1.1.5", "06092B2403030208010105", 224}, - {"brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7", "06092B2403030208010107", 256}, - {"brainpoolP320r1", "1.3.36.3.3.2.8.1.1.9", "06092B2403030208010109", 320}, - - {"secp192k1", "1.3.132.0.31", "06052B8104001F", 192}, - {"secp256k1", "1.3.132.0.10", "06052B8104000A", 256}, - {NULL, NULL, NULL, 0}, + {"secp192r1", "1.2.840.10045.3.1.1", (unsigned char*)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x01", 10, 192, 0}, + {"prime192v1", "1.2.840.10045.3.1.1", (unsigned char*)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x01", 10, 192, 0}, + {"prime192v2", "1.2.840.10045.3.1.2", (unsigned char*)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x02", 10, 192, 0}, + {"prime192v3", "1.2.840.10045.3.1.3", (unsigned char*)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x03", 10, 192, 0}, + {"nistp192", "1.2.840.10045.3.1.1", (unsigned char*)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x01", 10, 192, 0}, + {"ansiX9p192r1", "1.2.840.10045.3.1.1", (unsigned char*)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x01", 10, 192, 0}, + + {"secp224r1", "1.3.132.0.33", (unsigned char*)"\x06\x05\x2b\x81\x04\x00\x21", 7, 224, 0}, + {"nistp224", "1.3.132.0.33", (unsigned char*)"\x06\x05\x2b\x81\x04\x00\x21", 7, 224, 0}, + + {"prime256v1", "1.2.840.10045.3.1.7", (unsigned char*)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x07", 10, 256, 0}, + {"secp256r1", "1.2.840.10045.3.1.7", (unsigned char*)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x07", 10, 256, 0}, + {"nistp256", "1.2.840.10045.3.1.7", (unsigned char*)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x07", 10, 256, 0}, + {"ansiX9p256r1", "1.2.840.10045.3.1.7", (unsigned char*)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x07", 10, 256, 0}, + {"frp256v1", "1.2.250.1.223.101.256.1", (unsigned char*)"\x06\x0a\x2a\x81\x7a\x01\x81\x5f\x65\x82\x00\x01", 11, 256, 0}, + + {"secp384r1", "1.3.132.0.34", (unsigned char*)"\x06\x05\x2B\x81\x04\x00\x22", 7, 384, 0}, + {"prime384v1", "1.3.132.0.34", (unsigned char*)"\x06\x05\x2B\x81\x04\x00\x22", 7, 384, 0}, + {"ansiX9p384r1", "1.3.132.0.34", (unsigned char*)"\x06\x05\x2B\x81\x04\x00\x22", 7, 384, 0}, + + {"prime521v1", "1.3.132.0.35", (unsigned char*)"\x06\x05\x2B\x81\x04\x00\x23", 7, 521, 0}, + {"secp521r1", "1.3.132.0.35", (unsigned char*)"\x06\x05\x2B\x81\x04\x00\x23", 7, 521, 0}, + {"nistp521", "1.3.132.0.35", (unsigned char*)"\x06\x05\x2B\x81\x04\x00\x23", 7, 521, 0}, + + {"brainpoolP192r1", "1.3.36.3.3.2.8.1.1.3", (unsigned char*)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x03", 11, 192, 0}, + {"brainpoolP224r1", "1.3.36.3.3.2.8.1.1.5", (unsigned char*)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x05", 11, 224, 0}, + {"brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7", (unsigned char*)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x07", 11, 256, 0}, + {"brainpoolP320r1", "1.3.36.3.3.2.8.1.1.9", (unsigned char*)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x09", 11, 320, 0}, + {"brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11", (unsigned char*)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x0B", 11, 384, 0}, + {"brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13", (unsigned char*)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x0D", 11, 512, 0}, + + {"brainpoolP192t1", "1.3.36.3.3.2.8.1.1.4", (unsigned char*)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x04", 11, 192, 0}, + {"brainpoolP224t1", "1.3.36.3.3.2.8.1.1.6", (unsigned char*)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x06", 11, 224, 0}, + {"brainpoolP256t1", "1.3.36.3.3.2.8.1.1.8", (unsigned char*)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x08", 11, 256, 0}, + {"brainpoolP320t1", "1.3.36.3.3.2.8.1.1.10", (unsigned char*)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x0A", 11, 320, 0}, + {"brainpoolP384t1", "1.3.36.3.3.2.8.1.1.12", (unsigned char*)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x0C", 11, 384, 0}, + {"brainpoolP512t1", "1.3.36.3.3.2.8.1.1.14", (unsigned char*)"\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x0E", 11, 512, 0}, + + {"secp192k1", "1.3.132.0.31", (unsigned char*)"\x06\x05\x2B\x81\x04\x00\x1F", 7, 192, 0}, + {"secp256k1", "1.3.132.0.10", (unsigned char*)"\x06\x05\x2B\x81\x04\x00\x0A", 7, 256, 0}, + {"secp521k1", "1.3.132.0.35", (unsigned char*)"\x06\x05\x2B\x81\x04\x00\x23", 7, 521, 0}, + + /* Some of the following may not yet be supported by the OpenSC module, but may be by other modules */ + /* OpenPGP extensions by Yubikey and GNUK are not defined in RFCs, so pass by printable string */ + /* See PKCS#11 3.0 2.3.7 */ + {"edwards25519", "1.3.6.1.4.1.11591.15.1", (unsigned char*)"\x13\x0c\x65\x64\x77\x61\x72\x64\x73\x32\x35\x35\x31\x39", 14, 255, CKM_EC_EDWARDS_KEY_PAIR_GEN}, /* send by curve name */ + {"curve25519", "1.3.6.1.4.1.3029.1.5.1", (unsigned char*)"\x13\x0a\x63\x75\x72\x76\x65\x32\x35\x35\x31\x39", 12, 255, CKM_EC_MONTGOMERY_KEY_PAIR_GEN}, /* send by curve name */ + + /* RFC8410, EDWARDS and MONTGOMERY curves are used by GnuPG and also by OpenSSL */ + + {"X25519", "1.3.101.110", (unsigned char*)"\x06\x03\x2b\x65\x6e", 5, 255, CKM_EC_MONTGOMERY_KEY_PAIR_GEN}, /* RFC 4810 send by OID */ + {"X448", "1.3.101.111", (unsigned char*)"\x06\x03\x2b\x65\x6f", 5, 448, CKM_EC_MONTGOMERY_KEY_PAIR_GEN}, /* RFC 4810 send by OID */ + {"Ed25519", "1.3.101.112", (unsigned char*)"\x06\x03\x2b\x65\x70", 5, 255, CKM_EC_EDWARDS_KEY_PAIR_GEN}, /* RFC 4810 send by OID */ + {"Ed448", "1.3.101.113", (unsigned char*)"\x06\x03\x2b\x65\x71", 5, 448, CKM_EC_EDWARDS_KEY_PAIR_GEN}, /* RFC 4810 send by OID */ + + /* GnuPG openpgp curves as used in gnupg-card are equivalent to RFC8410 OIDs */ + {"cv25519", "1.3.101.110", (unsigned char*)"\x06\x03\x2b\x65\x6e", 5, 255, CKM_EC_MONTGOMERY_KEY_PAIR_GEN}, + {"ed25519", "1.3.101.112", (unsigned char*)"\x06\x03\x2b\x65\x70", 5, 255, CKM_EC_EDWARDS_KEY_PAIR_GEN}, + /* OpenSC card-openpgp.c will map these to what is need on the card */ + + {NULL, NULL, NULL, 0, 0, 0}, }; +// clang-format on + +static const struct sc_aid GOST_HASH2001_PARAMSET_OID = { { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01 }, 9 }; +static const struct sc_aid GOST_HASH2012_256_PARAMSET_OID = { { 0x06, 0x08, 0x2A, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02 }, 10 }; +static const struct sc_aid GOST_HASH2012_512_PARAMSET_OID = { { 0x06, 0x08, 0x2A, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03 }, 10 }; enum { OPT_MODULE = 0x100, @@ -132,21 +204,55 @@ enum { OPT_KEY_USAGE_SIGN, OPT_KEY_USAGE_DECRYPT, OPT_KEY_USAGE_DERIVE, + OPT_KEY_USAGE_WRAP, + OPT_KEY_USAGE_ENCAPSULATE, OPT_PRIVATE, + OPT_SENSITIVE, + OPT_EXTRACTABLE, + OPT_UNDESTROYABLE, OPT_TEST_HOTPLUG, OPT_UNLOCK_PIN, OPT_PUK, OPT_NEW_PIN, + OPT_SESSION_RW, OPT_LOGIN_TYPE, OPT_TEST_EC, OPT_DERIVE, OPT_DERIVE_PASS_DER, OPT_DECRYPT, + OPT_ENCRYPT, + OPT_UNWRAP, + OPT_WRAP, OPT_TEST_FORK, +#if defined(_WIN32) || defined(HAVE_PTHREAD) + OPT_TEST_THREADS, + OPT_USE_LOCKING, +#endif + OPT_GENERATE_KEY, OPT_GENERATE_RANDOM, + OPT_HASH_ALGORITHM, + OPT_MGF, + OPT_SALT, + OPT_VERIFY, + OPT_SIGNATURE_FILE, + OPT_ALWAYS_AUTH, + OPT_ALLOWED_MECHANISMS, + OPT_OBJECT_INDEX, + OPT_ALLOW_SW, + OPT_LIST_INTERFACES, + OPT_IV, + OPT_MAC_GEN_PARAM, + OPT_AAD, + OPT_TAG_BITS, + OPT_SALT_FILE, + OPT_INFO_FILE, + OPT_PUBLIC_KEY_INFO, + OPT_URI, + OPT_URI_WITH_SLOT_ID }; +// clang-format off static const struct option options[] = { { "module", 1, NULL, OPT_MODULE }, { "show-info", 0, NULL, 'I' }, @@ -154,14 +260,23 @@ static const struct option options[] = { { "list-token-slots", 0, NULL, 'T' }, { "list-mechanisms", 0, NULL, 'M' }, { "list-objects", 0, NULL, 'O' }, + { "list-interfaces", 0, NULL, OPT_LIST_INTERFACES }, { "sign", 0, NULL, 's' }, + { "verify", 0, NULL, OPT_VERIFY }, { "decrypt", 0, NULL, OPT_DECRYPT }, + { "encrypt", 0, NULL, OPT_ENCRYPT }, + { "unwrap", 0, NULL, OPT_UNWRAP }, + { "wrap", 0, NULL, OPT_WRAP }, { "hash", 0, NULL, 'h' }, { "derive", 0, NULL, OPT_DERIVE }, { "derive-pass-der", 0, NULL, OPT_DERIVE_PASS_DER }, { "mechanism", 1, NULL, 'm' }, + { "hash-algorithm", 1, NULL, OPT_HASH_ALGORITHM }, + { "mgf", 1, NULL, OPT_MGF }, + { "salt-len", 1, NULL, OPT_SALT }, + { "session-rw", 0, NULL, OPT_SESSION_RW }, { "login", 0, NULL, 'l' }, { "login-type", 1, NULL, OPT_LOGIN_TYPE }, { "pin", 1, NULL, 'p' }, @@ -178,6 +293,8 @@ static const struct option options[] = { { "usage-sign", 0, NULL, OPT_KEY_USAGE_SIGN }, { "usage-decrypt", 0, NULL, OPT_KEY_USAGE_DECRYPT }, { "usage-derive", 0, NULL, OPT_KEY_USAGE_DERIVE }, + { "usage-wrap", 0, NULL, OPT_KEY_USAGE_WRAP }, + { "usage-encapsulate", 0, NULL, OPT_KEY_USAGE_ENCAPSULATE }, { "write-object", 1, NULL, 'w' }, { "read-object", 0, NULL, 'r' }, { "delete-object", 0, NULL, 'b' }, @@ -191,88 +308,142 @@ static const struct option options[] = { { "slot", 1, NULL, OPT_SLOT }, { "slot-description", 1, NULL, OPT_SLOT_DESCRIPTION }, { "slot-index", 1, NULL, OPT_SLOT_INDEX }, + { "object-index", 1, NULL, OPT_OBJECT_INDEX }, { "token-label", 1, NULL, OPT_TOKEN_LABEL }, { "set-id", 1, NULL, 'e' }, { "attr-from", 1, NULL, OPT_ATTR_FROM }, { "input-file", 1, NULL, 'i' }, + { "signature-file", 1, NULL, OPT_SIGNATURE_FILE }, { "output-file", 1, NULL, 'o' }, { "signature-format", 1, NULL, 'f' }, + { "allowed-mechanisms", 1, NULL, OPT_ALLOWED_MECHANISMS }, { "test", 0, NULL, 't' }, { "test-hotplug", 0, NULL, OPT_TEST_HOTPLUG }, { "moz-cert", 1, NULL, 'z' }, { "verbose", 0, NULL, 'v' }, { "private", 0, NULL, OPT_PRIVATE }, + { "sensitive", 0, NULL, OPT_SENSITIVE }, + { "extractable", 0, NULL, OPT_EXTRACTABLE }, + { "undestroyable", 0, NULL, OPT_UNDESTROYABLE }, + { "always-auth", 0, NULL, OPT_ALWAYS_AUTH }, { "test-ec", 0, NULL, OPT_TEST_EC }, #ifndef _WIN32 { "test-fork", 0, NULL, OPT_TEST_FORK }, +#endif +#if defined(_WIN32) || defined(HAVE_PTHREAD) + { "use-locking", 0, NULL, OPT_USE_LOCKING }, + { "test-threads", 1, NULL, OPT_TEST_THREADS }, #endif { "generate-random", 1, NULL, OPT_GENERATE_RANDOM }, - - { NULL, 0, NULL, 0 } + { "allow-sw", 0, NULL, OPT_ALLOW_SW }, + { "iv", 1, NULL, OPT_IV }, + { "mac-general-param", 1, NULL, OPT_MAC_GEN_PARAM}, + { "aad", 1, NULL, OPT_AAD }, + { "tag-bits-len", 1, NULL, OPT_TAG_BITS}, + { "salt-file", 1, NULL, OPT_SALT_FILE}, + { "info-file", 1, NULL, OPT_INFO_FILE}, + { "public-key-info", 0, NULL, OPT_PUBLIC_KEY_INFO}, + { "uri", 1, NULL, OPT_URI}, + { "uri-with-slot-id", 0, NULL, OPT_URI_WITH_SLOT_ID}, + { NULL, 0, NULL, 0 }, }; +// clang-format on static const char *option_help[] = { - "Specify the module to load (default:" DEFAULT_PKCS11_PROVIDER ")", - "Show global token information", - "List available slots", - "List slots with tokens", - "List mechanisms supported by the token", - "Show objects on token", - - "Sign some data", - "Decrypt some data", - "Hash some data", - "Derive a secret key using another key and some data", - "Derive ECDHpass DER encoded pubkey for compatibility with some PKCS#11 implementations", - "Specify mechanism (use -M for a list of supported mechanisms)", - - "Log into the token first", - "Specify login type ('so', 'user', 'context-specific'; default:'user')", - "Supply User PIN on the command line (if used in scripts: careful!)", - "Supply User PUK on the command line", - "Supply new User PIN on the command line", - "Supply SO PIN on the command line (if used in scripts: careful!)", - "Initialize the token, its label and its SO PIN (use with --label and --so-pin)", - "Initialize the User PIN (use with --pin and --login)", - "Change User PIN", - "Unlock User PIN (without '--login' unlock in logged in session; otherwise '--login-type' has to be 'context-specific')", - "Key pair generation", - "Key generation", - "Specify the type and length of the key to create, for example rsa:1024 or EC:prime256v1 or GOSTR3410:A", - "Specify 'sign' key usage flag (sets SIGN in privkey, sets VERIFY in pubkey)", - "Specify 'decrypt' key usage flag (RSA only, set DECRYPT privkey, ENCRYPT in pubkey)", - "Specify 'derive' key usage flag (EC only)", - "Write an object (key, cert, data) to the card", - "Get object's CKA_VALUE attribute (use with --type)", - "Delete an object (use with --type cert/data/privkey/pubkey/secrkey)", - "Specify the application label of the data object (use with --type data)", - "Specify the application ID of the data object (use with --type data)", - "Specify the issuer in hexadecimal format (use with --type cert)", - "Specify the subject in hexadecimal format (use with --type cert/privkey/pubkey)", - "Specify the type of object (e.g. cert, privkey, pubkey, data)", - "Specify the ID of the object", - "Specify the label of the object", - "Specify the ID of the slot to use", - "Specify the description of the slot to use", - "Specify the index of the slot to use", - "Specify the token label of the slot to use", - "Set the CKA_ID of an object, = the (new) CKA_ID", - "Use to create some attributes when writing an object", - "Specify the input file", - "Specify the output file", - "Format for ECDSA signature : 'rs' (default), 'sequence', 'openssl'", - - "Test (best used with the --login or --pin option)", - "Test hotplug capabilities (C_GetSlotList + C_WaitForSlotEvent)", - "Test Mozilla-like keypair gen and cert req, =certfile", - "Verbose operation. (Set OPENSC_DEBUG to enable OpenSC specific debugging)", - "Set the CKA_PRIVATE attribute (object is only viewable after a login)", - "Test EC (best used with the --login or --pin option)", + "Specify the module to load (default:" DEFAULT_PKCS11_PROVIDER ")", + "Show global token information", + "List available slots", + "List slots with tokens", + "List mechanisms supported by the token", + "Show objects on token", + "List interfaces of PKCS #11 3.0 library", + + "Sign some data", + "Verify a signature of some data", + "Decrypt some data", + "Encrypt some data", + "Unwrap key", + "Wrap key", + "Hash some data", + "Derive a secret key using another key and some data", + "Derive ECDHpass DER encoded pubkey for compatibility with some PKCS#11 implementations", + "Specify mechanism (use -M for a list of supported mechanisms), or by hexadecimal, e.g., 0x80001234", + "Specify hash algorithm used with RSA-PKCS-PSS signature and RSA-PKCS-OAEP decryption", + "Specify MGF (Message Generation Function) used for RSA-PSS signature and RSA-OAEP decryption (possible values are MGF1-SHA1 to MGF1-SHA512)", + "Specify how many bytes should be used for salt in RSA-PSS signatures (default is digest size)", + + "Forces to open the PKCS#11 session with CKF_RW_SESSION", + "Log into the token first", + "Specify login type ('so', 'user', 'context-specific'; default:'user')", + "Supply User PIN on the command line (if used in scripts: careful!)", + "Supply User PUK on the command line", + "Supply new User PIN on the command line", + "Supply SO PIN on the command line (if used in scripts: careful!)", + "Initialize the token, its label and its SO PIN (use with --label and --so-pin)", + "Initialize the User PIN (use with --pin and --login)", + "Change User PIN", + "Unlock User PIN (without '--login' unlock in logged in session; otherwise '--login-type' has to be 'context-specific')", + "Key pair generation", + "Key generation", + "Specify the type and (not always compulsory) flavour (byte-wise symmetric key length, bit-wise asymmetric key length, elliptic curve identifier, etc.) of the key to create, for example RSA:2048, EC:prime256v1, EC:ED25519, EC:X448, GOSTR3410-2012-256:B, DES:8, DES3:24, AES:16, AES: or GENERIC:64", + "Specify 'sign' key usage flag (sets SIGN in privkey, sets VERIFY in pubkey)", + "Specify 'decrypt' key usage flag (sets DECRYPT in privkey and ENCRYPT in pubkey for RSA, sets both DECRYPT and ENCRYPT for secret keys)", + "Specify 'derive' key usage flag (EC only)", + "Specify 'wrap' key usage flag", + "Specify 'encapsulate' key usage flag (sets ENCAPSULATE in public and DECAPSULATE in private)", + "Write an object (key, cert, data) to the card", + "Get object's CKA_VALUE attribute (use with --type)", + "Delete an object (use with --type cert/data/privkey/pubkey/secrkey)", + "Specify the application label of the data object (use with --type data)", + "Specify the application ID of the data object (use with --type data)", + "Specify the issuer in hexadecimal format (use with --type cert)", + "Specify the subject in hexadecimal format (use with --type cert/privkey/pubkey)", + "Specify the type of object (e.g. cert, privkey, pubkey, secrkey, data)", + "Specify the ID of the object", + "Specify the label of the object", + "Specify the ID of the slot to use (accepts HEX format with 0x.. prefix or decimal number)", + "Specify the description of the slot to use", + "Specify the index of the slot to use", + "Specify the index of the object to use", + "Specify the token label of the slot to use", + "Set the CKA_ID of an object, = the (new) CKA_ID", + "Use to create some attributes when writing an object", + "Specify the input file", + "Specify the file with signature for verification", + "Specify the output file", + "Format for ECDSA signature : 'rs' (default), 'sequence', 'openssl'", + "Specify the comma-separated list of allowed mechanisms when creating an object.", + + "Test (best used with the --login or --pin option)", + "Test hotplug capabilities (C_GetSlotList + C_WaitForSlotEvent)", + "Test Mozilla-like key pair gen and cert req, =certfile", + "Verbose operation. (Set OPENSC_DEBUG to enable OpenSC specific debugging)", + "Set the CKA_PRIVATE attribute (object is only viewable after a login)", + "Set the CKA_SENSITIVE attribute (object cannot be revealed in plaintext)", + "Set the CKA_EXTRACTABLE attribute (object can be extracted)", + "Set the CKA_DESTROYABLE attribute to false (object cannot be destroyed)", + "Set the CKA_ALWAYS_AUTHENTICATE attribute to a key object (require PIN verification for each use)", + "Test EC (best used with the --login or --pin option)", #ifndef _WIN32 - "Test forking and calling C_Initialize() in the child", + "Test forking and calling C_Initialize() in the child", #endif - "Generate given amount of random data", + "Call C_initialize() with CKF_OS_LOCKING_OK.", +#if defined(_WIN32) || defined(HAVE_PTHREAD) + "Test threads. Multiple times to start additional threads, arg is string or 2 byte commands", +#endif + "Generate given amount of random data", + "Allow using software mechanisms (without CKF_HW)", + "Initialization vector", + "Specify the value of the mechanism parameter CK_MAC_GENERAL_PARAMS", + "Specify additional authenticated data for AEAD ciphers as a hex string", + "Specify the required length (in bits) for the authentication tag for AEAD ciphers", + "Specify the file containing the salt for HKDF (optional)", + "Specify the file containing the info for HKDF (optional)", + "When reading a public key, try to read PUBLIC_KEY_INFO (DER encoding of SPKI)", + "Specify the PKCS#11 URI for module, slot, token or object", + "Include SlotId in PKCS#11 URI", + "", }; static const char * app_name = "pkcs11-tool"; /* for utils.c */ @@ -280,13 +451,16 @@ static const char * app_name = "pkcs11-tool"; /* for utils.c */ static int verbose = 0; static const char * opt_input = NULL; static const char * opt_output = NULL; -static const char * opt_module = DEFAULT_PKCS11_PROVIDER; +static const char * opt_signature_file = NULL; +static const char *opt_module = NULL; static int opt_slot_set = 0; static CK_SLOT_ID opt_slot = 0; static const char * opt_slot_description = NULL; static const char * opt_token_label = NULL; static CK_ULONG opt_slot_index = 0; static int opt_slot_index_set = 0; +static CK_ULONG opt_object_index = 0; +static int opt_object_index_set = 0; static CK_MECHANISM_TYPE opt_mechanism = 0; static int opt_mechanism_used = 0; static const char * opt_file_to_write = NULL; @@ -306,35 +480,97 @@ static char * opt_issuer = NULL; static char * opt_subject = NULL; static char * opt_key_type = NULL; static char * opt_sig_format = NULL; +#define MAX_ALLOWED_MECHANISMS 20 +static CK_MECHANISM_TYPE opt_allowed_mechanisms[MAX_ALLOWED_MECHANISMS]; +static size_t opt_allowed_mechanisms_len = 0; static int opt_is_private = 0; +static int opt_is_sensitive = 0; +static int opt_is_extractable = 0; +static int opt_is_destroyable = 1; static int opt_test_hotplug = 0; static int opt_login_type = -1; static int opt_key_usage_sign = 0; static int opt_key_usage_decrypt = 0; static int opt_key_usage_derive = 0; +static int opt_key_usage_wrap = 0; +static int opt_key_usage_encapsulate = 0; static int opt_key_usage_default = 1; /* uses defaults if no opt_key_usage options */ static int opt_derive_pass_der = 0; static unsigned long opt_random_bytes = 0; +static CK_MECHANISM_TYPE opt_hash_alg = 0; +static unsigned long opt_mgf = 0; +static long opt_salt_len = 0; +static int opt_salt_len_given = 0; /* 0 - not given, 1 - given with input parameters */ +static int opt_always_auth = 0; +static CK_FLAGS opt_allow_sw = CKF_HW; +static const char * opt_iv = NULL; +static unsigned long opt_mac_gen_param = 0; +static const char *opt_aad = NULL; +static unsigned long opt_tag_bits = 0; +static const char *opt_salt_file = NULL; +static const char *opt_info_file = NULL; +static int opt_public_key_info = 0; /* return pubkey as SPKI DER */ +static struct pkcs11_uri *opt_uri = NULL; +static int opt_uri_with_slot_id = 0; /* include slot-id in PKCS#11 URI */ static void *module = NULL; -static CK_FUNCTION_LIST_PTR p11 = NULL; +static CK_FUNCTION_LIST_3_0_PTR p11 = NULL; static CK_SLOT_ID_PTR p11_slots = NULL; static CK_ULONG p11_num_slots = 0; static int suppress_warn = 0; +static CK_C_INITIALIZE_ARGS_PTR c_initialize_args_ptr = NULL; + +#if defined(_WIN32) || defined(HAVE_PTHREAD) +static CK_C_INITIALIZE_ARGS c_initialize_args_OS = {NULL, NULL, NULL, NULL, CKF_OS_LOCKING_OK, NULL}; +#ifdef _WIN32 +static HANDLE test_threads_handles[MAX_TEST_THREADS]; +#else +static pthread_t test_threads_handles[MAX_TEST_THREADS]; +#endif +struct test_threads_data { + int tnum; + char * tests; +}; +static struct test_threads_data test_threads_datas[MAX_TEST_THREADS]; +static int test_threads_num = 0; +#endif /* defined(_WIN32) || defined(HAVE_PTHREAD) */ struct flag_info { CK_FLAGS value; const char * name; }; + +/* + * Flags for mech_info. These flags can provide meta-data for + * pkcs11 mechanisms and are tracked per mechanism. Thus for figuring + * out if sign is valid for this mechanism, one can query the mechanism + * table over having to build conditional statements. + * + * Note that the tool takes in raw 0x prefixed mechanisms that may not exist in + * the table, so we just assume MF_UNKOWN for flags. + * + * TODO these flags are only the tip of the iceberg, but can be filled out as time progresses. + */ +#define MF_UNKNOWN 0 /* Used to indicate additional information is not available */ +#define MF_SIGN (1 << 0) /* C_Sign interface supported */ +#define MF_VERIFY (1 << 1) /* C_verify interface supported */ +#define MF_HMAC (1 << 2) /* Is an Hashed Message Authentication Code (HMAC) */ +#define MF_MGF (1 << 3) /* Is an Mask Generation Function (MGF) */ +#define MF_CKO_SECRET_KEY (1 << 4) /* Uses a CKO_SECRET_KEY class object */ + +/* Handy initializers */ +#define MF_GENERIC_HMAC_FLAGS (MF_SIGN | MF_VERIFY | MF_HMAC | MF_CKO_SECRET_KEY) + struct mech_info { CK_MECHANISM_TYPE mech; const char * name; const char * short_name; + uint16_t mf_flags; }; struct x509cert_info { - unsigned char subject[256]; + unsigned char subject[512]; int subject_len; - unsigned char issuer[256]; + unsigned char issuer[512]; int issuer_len; unsigned char serialnum[128]; int serialnum_len; @@ -357,17 +593,28 @@ struct rsakey_info { unsigned char *coefficient; int coefficient_len; }; -struct gostkey_info { +struct generalkey_info { struct sc_lv_data param_oid; struct sc_lv_data public; struct sc_lv_data private; +#ifdef EC_POINT_NO_ASN1_OCTET_STRING + size_t header_len; +#endif +}; + +struct pqckey_info { + struct sc_lv_data public; + struct sc_lv_data private; + struct sc_lv_data seed; + long int type; }; static void show_cryptoki_info(void); static void list_slots(int, int, int); static void show_token(CK_SLOT_ID); static void list_mechs(CK_SLOT_ID); -static void list_objects(CK_SESSION_HANDLE, CK_OBJECT_CLASS); +static void list_objects(CK_SESSION_HANDLE); +static void list_interfaces(void); static int login(CK_SESSION_HANDLE, int); static void init_token(CK_SLOT_ID); static void init_pin(CK_SLOT_ID, CK_SESSION_HANDLE); @@ -377,21 +624,41 @@ static void show_object(CK_SESSION_HANDLE, CK_OBJECT_HANDLE); static void show_key(CK_SESSION_HANDLE, CK_OBJECT_HANDLE); static void show_cert(CK_SESSION_HANDLE, CK_OBJECT_HANDLE); static void show_dobj(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj); +static void show_profile(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj); static void sign_data(CK_SLOT_ID, CK_SESSION_HANDLE, CK_OBJECT_HANDLE); +static void verify_signature(CK_SLOT_ID, CK_SESSION_HANDLE, CK_OBJECT_HANDLE); static void decrypt_data(CK_SLOT_ID, CK_SESSION_HANDLE, CK_OBJECT_HANDLE); +static void encrypt_data(CK_SLOT_ID, CK_SESSION_HANDLE, CK_OBJECT_HANDLE); static void hash_data(CK_SLOT_ID, CK_SESSION_HANDLE); static void derive_key(CK_SLOT_ID, CK_SESSION_HANDLE, CK_OBJECT_HANDLE); static int gen_keypair(CK_SLOT_ID slot, CK_SESSION_HANDLE, CK_OBJECT_HANDLE *, CK_OBJECT_HANDLE *, const char *); static int gen_key(CK_SLOT_ID slot, CK_SESSION_HANDLE, CK_OBJECT_HANDLE *, const char *, char *); -static int write_object(CK_SESSION_HANDLE session); +static int unwrap_key(CK_SESSION_HANDLE session); +static int wrap_key(CK_SESSION_HANDLE session); + +static CK_RV write_object(CK_SESSION_HANDLE session); static int read_object(CK_SESSION_HANDLE session); static int delete_object(CK_SESSION_HANDLE session); static void set_id_attr(CK_SESSION_HANDLE session); +static int find_object_type_or_id_or_label(CK_SESSION_HANDLE sess, + CK_OBJECT_CLASS cls, int cls_set, + CK_OBJECT_HANDLE_PTR ret, + const unsigned char *, size_t id_len, + const char *, int obj_index); static int find_object(CK_SESSION_HANDLE, CK_OBJECT_CLASS, CK_OBJECT_HANDLE_PTR, - const unsigned char *, size_t id_len, int obj_index); -static int find_mechanism(CK_SLOT_ID, CK_FLAGS, CK_MECHANISM_TYPE_PTR, size_t, CK_MECHANISM_TYPE_PTR); + const unsigned char *, size_t id_len, const char *label, int obj_index); +static int find_object_flags(CK_SESSION_HANDLE, uint16_t flags, + CK_OBJECT_HANDLE_PTR, + const unsigned char *, size_t id_len, const char *label, int obj_index); +static int find_object_flags_and_type_or_id_or_label(CK_SESSION_HANDLE sess, + uint16_t mf_flags, CK_OBJECT_HANDLE_PTR ret, + CK_OBJECT_CLASS cls, int cls_set, + const unsigned char *id, size_t id_len, const char *label, int obj_index); +static CK_ULONG find_mechanism(CK_SLOT_ID, CK_FLAGS, CK_MECHANISM_TYPE_PTR, size_t, CK_MECHANISM_TYPE_PTR); +static int get_default_slot(CK_SLOT_ID_PTR result); +static int find_slot_by_uri(struct pkcs11_uri *uri, CK_SLOT_ID_PTR result); static int find_slot_by_description(const char *, CK_SLOT_ID_PTR); static int find_slot_by_token_label(const char *, CK_SLOT_ID_PTR); static void get_token_info(CK_SLOT_ID, CK_TOKEN_INFO_PTR); @@ -402,24 +669,40 @@ static void p11_warn(const char *, CK_RV); static const char * p11_slot_info_flags(CK_FLAGS); static const char * p11_token_info_flags(CK_FLAGS); static const char * p11_utf8_to_local(CK_UTF8CHAR *, size_t); +static char *p11_utf8_to_string(CK_UTF8CHAR *, size_t); static const char * p11_flag_names(struct flag_info *, CK_FLAGS); static const char * p11_mechanism_to_name(CK_MECHANISM_TYPE); static CK_MECHANISM_TYPE p11_name_to_mechanism(const char *); +static uint16_t p11_mechanism_to_flags(CK_MECHANISM_TYPE mech); +static const char * p11_mgf_to_name(CK_RSA_PKCS_MGF_TYPE); +static CK_MECHANISM_TYPE p11_name_to_mgf(const char *); +static const char * p11_profile_to_name(CK_ULONG); static void p11_perror(const char *, CK_RV); static const char * CKR2Str(CK_ULONG res); static int p11_test(CK_SESSION_HANDLE session); static int test_card_detection(int); -static int hex_to_bin(const char *in, CK_BYTE *out, size_t *outlen); +static CK_BYTE_PTR hex_string_to_byte_array(const char *iv_input, size_t *iv_size, const char *buffer_name); static void pseudo_randomize(unsigned char *data, size_t dataLen); static CK_SESSION_HANDLE test_kpgen_certwrite(CK_SLOT_ID slot, CK_SESSION_HANDLE session); static void test_ec(CK_SLOT_ID slot, CK_SESSION_HANDLE session); #ifndef _WIN32 static void test_fork(void); #endif +#if defined(_WIN32) || defined(HAVE_PTHREAD) +static void test_threads(); +static int test_threads_start(int tnum); +static int test_threads_cleanup(); +#ifdef _WIN32 +static DWORD WINAPI test_threads_run(_In_ LPVOID pttd); +#else +static void * test_threads_run(void * pttd); +#endif +#endif /* defined(_WIN32) || defined(HAVE_PTHREAD) */ static void generate_random(CK_SESSION_HANDLE session); static CK_RV find_object_with_attributes(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *out, CK_ATTRIBUTE *attrs, CK_ULONG attrsLen, CK_ULONG obj_index); static CK_ULONG get_private_key_length(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE prkey); +static const char *percent_encode(CK_UTF8CHAR *, size_t); /* win32 needs this in open(2) */ #ifndef O_BINARY @@ -461,7 +744,7 @@ get##ATTR(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, CK_ULONG_PTR pulCount) \ } \ if (pulCount) \ *pulCount = attr.ulValueLen / sizeof(TYPE); \ - } else { \ + } else if (rv != CKR_ATTRIBUTE_TYPE_INVALID) { \ p11_warn("C_GetAttributeValue(" #ATTR ")", rv); \ } \ return (TYPE *) attr.pValue; \ @@ -470,35 +753,51 @@ get##ATTR(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, CK_ULONG_PTR pulCount) \ /* * Define attribute accessors */ -ATTR_METHOD(CLASS, CK_OBJECT_CLASS); -ATTR_METHOD(ALWAYS_AUTHENTICATE, CK_BBOOL); -ATTR_METHOD(PRIVATE, CK_BBOOL); -ATTR_METHOD(MODIFIABLE, CK_BBOOL); -ATTR_METHOD(ENCRYPT, CK_BBOOL); -ATTR_METHOD(DECRYPT, CK_BBOOL); -ATTR_METHOD(SIGN, CK_BBOOL); -ATTR_METHOD(VERIFY, CK_BBOOL); -ATTR_METHOD(WRAP, CK_BBOOL); -ATTR_METHOD(UNWRAP, CK_BBOOL); -ATTR_METHOD(DERIVE, CK_BBOOL); -ATTR_METHOD(OPENSC_NON_REPUDIATION, CK_BBOOL); -ATTR_METHOD(KEY_TYPE, CK_KEY_TYPE); -ATTR_METHOD(CERTIFICATE_TYPE, CK_CERTIFICATE_TYPE); -ATTR_METHOD(MODULUS_BITS, CK_ULONG); -ATTR_METHOD(VALUE_LEN, CK_ULONG); -VARATTR_METHOD(LABEL, char); -VARATTR_METHOD(APPLICATION, char); -VARATTR_METHOD(ID, unsigned char); -VARATTR_METHOD(OBJECT_ID, unsigned char); -VARATTR_METHOD(MODULUS, CK_BYTE); +ATTR_METHOD(CLASS, CK_OBJECT_CLASS); /* getCLASS */ +ATTR_METHOD(ALWAYS_AUTHENTICATE, CK_BBOOL); /* getALWAYS_AUTHENTICATE */ +ATTR_METHOD(PRIVATE, CK_BBOOL); /* getPRIVATE */ +ATTR_METHOD(MODIFIABLE, CK_BBOOL); /* getMODIFIABLE */ +ATTR_METHOD(ENCRYPT, CK_BBOOL); /* getENCRYPT */ +ATTR_METHOD(DECRYPT, CK_BBOOL); /* getDECRYPT */ +ATTR_METHOD(SIGN, CK_BBOOL); /* getSIGN */ +ATTR_METHOD(SIGN_RECOVER, CK_BBOOL); /* getSIGN_RECOVER */ +ATTR_METHOD(VERIFY, CK_BBOOL); /* getVERIFY */ +ATTR_METHOD(VERIFY_RECOVER, CK_BBOOL); /* getVERIFY_RECOVER */ +ATTR_METHOD(WRAP, CK_BBOOL); /* getWRAP */ +ATTR_METHOD(UNWRAP, CK_BBOOL); /* getUNWRAP */ +ATTR_METHOD(DERIVE, CK_BBOOL); /* getDERIVE */ +ATTR_METHOD(SENSITIVE, CK_BBOOL); /* getSENSITIVE */ +ATTR_METHOD(ALWAYS_SENSITIVE, CK_BBOOL); /* getALWAYS_SENSITIVE */ +ATTR_METHOD(EXTRACTABLE, CK_BBOOL); /* getEXTRACTABLE */ +ATTR_METHOD(NEVER_EXTRACTABLE, CK_BBOOL); /* getNEVER_EXTRACTABLE */ +ATTR_METHOD(LOCAL, CK_BBOOL); /* getLOCAL */ +ATTR_METHOD(OPENSC_NON_REPUDIATION, CK_BBOOL); /* getOPENSC_NON_REPUDIATION */ +ATTR_METHOD(ENCAPSULATE, CK_BBOOL); /* getENCAPSULATE */ +ATTR_METHOD(DECAPSULATE, CK_BBOOL); /* getDECAPSULATE */ +ATTR_METHOD(KEY_TYPE, CK_KEY_TYPE); /* getKEY_TYPE */ +ATTR_METHOD(CERTIFICATE_TYPE, CK_CERTIFICATE_TYPE); /* getCERTIFICATE_TYPE */ +ATTR_METHOD(MODULUS_BITS, CK_ULONG); /* getMODULUS_BITS */ +ATTR_METHOD(VALUE_LEN, CK_ULONG); /* getVALUE_LEN */ +ATTR_METHOD(PROFILE_ID, CK_ULONG); /* getPROFILE_ID */ +ATTR_METHOD(PARAMETER_SET, CK_ULONG); /* getPARAMETER_SET */ +VARATTR_METHOD(LABEL, char); /* getLABEL */ +VARATTR_METHOD(UNIQUE_ID, char); /* getUNIQUE_ID */ +VARATTR_METHOD(APPLICATION, char); /* getAPPLICATION */ +VARATTR_METHOD(ID, unsigned char); /* getID */ +VARATTR_METHOD(OBJECT_ID, unsigned char); /* getOBJECT_ID */ +VARATTR_METHOD(MODULUS, CK_BYTE); /* getMODULUS */ #ifdef ENABLE_OPENSSL -VARATTR_METHOD(PUBLIC_EXPONENT, CK_BYTE); +VARATTR_METHOD(SUBJECT, unsigned char); /* getSUBJECT */ +VARATTR_METHOD(SERIAL_NUMBER, unsigned char); /* getSERIAL_NUMBER */ #endif -VARATTR_METHOD(VALUE, unsigned char); -VARATTR_METHOD(GOSTR3410_PARAMS, unsigned char); -VARATTR_METHOD(EC_POINT, unsigned char); -VARATTR_METHOD(EC_PARAMS, unsigned char); - +VARATTR_METHOD(PUBLIC_EXPONENT, CK_BYTE); /* getPUBLIC_EXPONENT */ +VARATTR_METHOD(VALUE, unsigned char); /* getVALUE */ +VARATTR_METHOD(GOSTR3410_PARAMS, unsigned char); /* getGOSTR3410_PARAMS */ +VARATTR_METHOD(GOSTR3411_PARAMS, unsigned char); /* getGOSTR3411_PARAMS */ +VARATTR_METHOD(EC_POINT, unsigned char); /* getEC_POINT */ +VARATTR_METHOD(EC_PARAMS, unsigned char); /* getEC_PARAMS */ +VARATTR_METHOD(ALLOWED_MECHANISMS, CK_MECHANISM_TYPE); /* getALLOWED_MECHANISMS */ +VARATTR_METHOD(PUBLIC_KEY_INFO, unsigned char); /* getPUBLIC_KEY_INFO */ int main(int argc, char * argv[]) { @@ -510,8 +809,13 @@ int main(int argc, char * argv[]) int list_token_slots = 0; int do_list_mechs = 0; int do_list_objects = 0; + int do_list_interfaces = 0; int do_sign = 0; + int do_verify = 0; int do_decrypt = 0; + int do_encrypt = 0; + int do_unwrap = 0; + int do_wrap = 0; int do_hash = 0; int do_derive = 0; int do_gen_keypair = 0; @@ -525,6 +829,9 @@ int main(int argc, char * argv[]) int do_test_ec = 0; #ifndef _WIN32 int do_test_fork = 0; +#endif +#if defined(_WIN32) || defined(HAVE_PTHREAD) + int do_test_threads = 0; #endif int need_session = 0; int opt_login = 0; @@ -534,31 +841,32 @@ int main(int argc, char * argv[]) int do_unlock_pin = 0; int action_count = 0; int do_generate_random = 0; + char *s = NULL; CK_RV rv; + static_assert(sizeof(options) / sizeof(options[0]) == sizeof(option_help) / sizeof(option_help[0]), + "Inconsistent size of the options and options_help structures!"); + #ifdef _WIN32 + char expanded_val[PATH_MAX]; + DWORD expanded_len; + if(_setmode(_fileno(stdout), _O_BINARY ) == -1) util_fatal("Cannot set FMODE to O_BINARY"); if(_setmode(_fileno(stdin), _O_BINARY ) == -1) util_fatal("Cannot set FMODE to O_BINARY"); #endif -#ifdef ENABLE_OPENSSL -#if (OPENSSL_VERSION_NUMBER >= 0x00907000L && OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) - OPENSSL_config(NULL); -#endif -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS - | OPENSSL_INIT_ADD_ALL_CIPHERS - | OPENSSL_INIT_ADD_ALL_DIGESTS - | OPENSSL_INIT_LOAD_CONFIG, - NULL); -#else - /* OpenSSL magic */ - OpenSSL_add_all_algorithms(); - OPENSSL_malloc_init(); -#endif +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (!(osslctx = OSSL_LIB_CTX_new())) { + util_fatal("Failed to create OpenSSL OSSL_LIB_CTX\n"); + } + if (!(default_provider = OSSL_PROVIDER_load(osslctx, "default"))) { + util_fatal("Failed to load OpenSSL \"default\" provider\n"); + } + legacy_provider = OSSL_PROVIDER_try_load(NULL, "legacy", 1); #endif + while (1) { c = getopt_long(argc, argv, "ILMOTa:bd:e:hi:klm:o:p:scvf:ty:w:z:r", options, &long_optind); @@ -622,7 +930,7 @@ int main(int argc, char * argv[]) need_session |= NEED_SESSION_RW; do_set_id = 1; new_object_id_len = sizeof(new_object_id); - if (!hex_to_bin(optarg, new_object_id, &new_object_id_len)) { + if (sc_hex_to_bin(optarg, new_object_id, &new_object_id_len)) { fprintf(stderr, "Invalid ID \"%s\"\n", optarg); util_print_usage_and_die(app_name, options, option_help, NULL); } @@ -650,7 +958,7 @@ int main(int argc, char * argv[]) break; case 'd': opt_object_id_len = sizeof(opt_object_id); - if (!hex_to_bin(optarg, opt_object_id, &opt_object_id_len)) { + if (sc_hex_to_bin(optarg, opt_object_id, &opt_object_id_len)) { fprintf(stderr, "Invalid ID \"%s\"\n", optarg); util_print_usage_and_die(app_name, options, option_help, NULL); } @@ -661,19 +969,35 @@ int main(int argc, char * argv[]) case 'i': opt_input = optarg; break; - case 'l': + case OPT_SIGNATURE_FILE: + opt_signature_file = optarg; + break; + case OPT_SESSION_RW: need_session |= NEED_SESSION_RW; + break; + case 'l': + need_session |= NEED_SESSION_RO; opt_login = 1; break; case 'm': opt_mechanism_used = 1; opt_mechanism = p11_name_to_mechanism(optarg); break; + case OPT_HASH_ALGORITHM: + opt_hash_alg = p11_name_to_mechanism(optarg); + break; + case OPT_MGF: + opt_mgf = p11_name_to_mgf(optarg); + break; + case OPT_SALT: + opt_salt_len = (CK_ULONG) strtoul(optarg, NULL, 0); + opt_salt_len_given = 1; + break; case 'o': opt_output = optarg; break; case 'p': - need_session |= NEED_SESSION_RW; + need_session |= NEED_SESSION_RO; opt_login = 1; util_get_pin(optarg, &opt_pin); break; @@ -688,20 +1012,40 @@ int main(int argc, char * argv[]) action_count++; break; case 's': - need_session |= NEED_SESSION_RW; + need_session |= NEED_SESSION_RO; do_sign = 1; action_count++; break; + case OPT_VERIFY: + need_session |= NEED_SESSION_RO; + do_verify = 1; + action_count++; + break; case OPT_DECRYPT: - need_session |= NEED_SESSION_RW; + need_session |= NEED_SESSION_RO; do_decrypt = 1; action_count++; break; + case OPT_ENCRYPT: + need_session |= NEED_SESSION_RO; + do_encrypt = 1; + action_count++; + break; + case OPT_UNWRAP: + need_session |= NEED_SESSION_RW; + do_unwrap = 1; + action_count++; + break; + case OPT_WRAP: + need_session |= NEED_SESSION_RO; + do_wrap = 1; + action_count++; + break; case 'f': opt_sig_format = optarg; break; case 't': - need_session |= NEED_SESSION_RO; + need_session |= NEED_SESSION_RW; do_test = 1; action_count++; break; @@ -721,22 +1065,26 @@ int main(int argc, char * argv[]) break; case OPT_SLOT_DESCRIPTION: if (opt_slot_set) { - fprintf(stderr, "Error: Only one of --slot, --slot-label, --slot-index or --token-label can be used\n"); + fprintf(stderr, "Error: Only one of --slot, --slot-description, --slot-index or --token-label can be used\n"); util_print_usage_and_die(app_name, options, option_help, NULL); } opt_slot_description = optarg; break; case OPT_SLOT_INDEX: if (opt_slot_set || opt_slot_description) { - fprintf(stderr, "Error: Only one of --slot, --slot-label, --slot-index or --token-label can be used\n"); + fprintf(stderr, "Error: Only one of --slot, --slot-description, --slot-index or --token-label can be used\n"); util_print_usage_and_die(app_name, options, option_help, NULL); } opt_slot_index = (CK_ULONG) strtoul(optarg, NULL, 0); opt_slot_index_set = 1; break; + case OPT_OBJECT_INDEX: + opt_object_index = (CK_ULONG) strtoul(optarg, NULL, 0); + opt_object_index_set = 1; + break; case OPT_TOKEN_LABEL: if (opt_slot_set || opt_slot_description || opt_slot_index_set) { - fprintf(stderr, "Error: Only one of --slot, --slot-label, --slot-index or --token-label can be used\n"); + fprintf(stderr, "Error: Only one of --slot, --slot-description, --slot-index or --token-label can be used\n"); util_print_usage_and_die(app_name, options, option_help, NULL); } opt_token_label = optarg; @@ -801,9 +1149,55 @@ int main(int argc, char * argv[]) opt_key_usage_derive = 1; opt_key_usage_default = 0; break; + case OPT_KEY_USAGE_WRAP: + opt_key_usage_wrap = 1; + opt_key_usage_default = 0; + break; + case OPT_KEY_USAGE_ENCAPSULATE: + opt_key_usage_encapsulate = 1; + opt_key_usage_default = 0; + break; case OPT_PRIVATE: opt_is_private = 1; break; + case OPT_SENSITIVE: + opt_is_sensitive = 1; + break; + case OPT_EXTRACTABLE: + opt_is_extractable = 1; + break; + case OPT_UNDESTROYABLE: + opt_is_destroyable = 0; + break; + case OPT_MAC_GEN_PARAM: + if (optarg != NULL) { + char *end_ptr; + opt_mac_gen_param = strtoul(optarg, &end_ptr, 10); + } else { + util_fatal("--mac-general-param option needs a decimal value argument"); + } + break; + case OPT_AAD: + opt_aad = optarg; + break; + case OPT_TAG_BITS: + if (optarg != NULL) { + char *end_ptr; + opt_tag_bits = strtoul(optarg, &end_ptr, 10); + } else { + util_fatal("--tag-bits-len option needs a decimal value argument"); + } + break; + case OPT_URI: + if (optarg == NULL) { + util_fatal("--uri option needs an argument"); + } + opt_uri = pkcs11_uri_new(); + if (parse_pkcs11_uri(optarg, opt_uri) != 0) { + pkcs11_uri_free(opt_uri); + util_fatal("--uri option argument invalid"); + } + break; case OPT_TEST_HOTPLUG: opt_test_hotplug = 1; action_count++; @@ -825,6 +1219,23 @@ int main(int argc, char * argv[]) do_test_fork = 1; action_count++; break; +#endif +#if defined(_WIN32) || defined(HAVE_PTHREAD) + case OPT_USE_LOCKING: + c_initialize_args_ptr = &c_initialize_args_OS; + break; + case OPT_TEST_THREADS: + do_test_threads = 1; + if (test_threads_num < MAX_TEST_THREADS) { + test_threads_datas[test_threads_num].tnum = test_threads_num; + test_threads_datas[test_threads_num].tests = optarg; + test_threads_num++; + } else { + fprintf(stderr,"Too many --test_threads options limit is %d\n", MAX_TEST_THREADS); + util_print_usage_and_die(app_name, options, option_help, NULL); + } + action_count++; + break; #endif case OPT_GENERATE_RANDOM: need_session |= NEED_SESSION_RO; @@ -832,7 +1243,47 @@ int main(int argc, char * argv[]) do_generate_random = 1; action_count++; break; + case OPT_ALWAYS_AUTH: + opt_always_auth = 1; + break; + case OPT_ALLOW_SW: + opt_allow_sw = 0; + break; + case OPT_ALLOWED_MECHANISMS: + /* Parse the mechanism list and fail early */ + s = strtok(optarg, ","); + while (s != NULL) { + if (opt_allowed_mechanisms_len > MAX_ALLOWED_MECHANISMS) { + fprintf(stderr, "Too many mechanisms provided" + " (max %d). Skipping the rest.", MAX_ALLOWED_MECHANISMS); + break; + } + opt_allowed_mechanisms[opt_allowed_mechanisms_len] = + p11_name_to_mechanism(s); + opt_allowed_mechanisms_len++; + s = strtok(NULL, ","); + } + break; + case OPT_LIST_INTERFACES: + do_list_interfaces = 1; + action_count++; + break; + case OPT_IV: + opt_iv = optarg; + break; + case OPT_SALT_FILE: + opt_salt_file = optarg; + break; + case OPT_INFO_FILE: + opt_info_file = optarg; + break; + case OPT_PUBLIC_KEY_INFO: + opt_public_key_info = 1; + break; + case OPT_URI_WITH_SLOT_ID: + opt_uri_with_slot_id = 1; + break; default: util_print_usage_and_die(app_name, options, option_help, NULL); } @@ -844,14 +1295,99 @@ int main(int argc, char * argv[]) if (action_count == 0) util_print_usage_and_die(app_name, options, option_help, NULL); - module = C_LoadModule(opt_module, &p11); - if (module == NULL) - util_fatal("Failed to load pkcs11 module"); + if (opt_uri) { + /* Check that no interfering options were set */ + if (opt_module || opt_slot_set || opt_slot_index_set || opt_slot_description || + opt_pin || opt_object_label || opt_object_class_str || opt_object_id_len) { + if (opt_module) + fprintf(stderr, "The --module cannot be specified with --uri option\n"); + if (opt_slot_set || opt_slot_index_set || opt_slot_description) + fprintf(stderr, "Options specifying slot cannot be specified with --uri option\n"); + if (opt_pin) + fprintf(stderr, "The PIN option cannot be specified with --uri option\n"); + if (opt_object_label || opt_object_class_str || opt_object_id_len) + fprintf(stderr, "Options specifying object cannot be specified with --uri option\n"); + util_print_usage_and_die(app_name, options, option_help, NULL); + } + if (opt_uri->slot_id) { + opt_slot = (CK_SLOT_ID)strtoul(opt_uri->slot_id, NULL, 0); + opt_slot_set = 1; + } + opt_module = opt_uri->module_path; + util_get_pin(opt_uri->pin_value, &opt_pin); + util_get_pin(opt_uri->pin_source, &opt_pin); + if (opt_pin) + opt_login = 1; + opt_object_label = opt_uri->object; + if (opt_uri->type) { + if (strcmp(opt_uri->type, "cert") == 0) { + opt_object_class = CKO_CERTIFICATE; + opt_object_class_str = "cert"; + } else if (strcmp(opt_uri->type, "private") == 0) { + opt_object_class = CKO_PRIVATE_KEY; + opt_object_class_str = "privkey"; + } else if (strcmp(opt_uri->type, "secret-key") == 0) { + opt_object_class = CKO_SECRET_KEY; + opt_object_class_str = "secrkey"; + } else if (strcmp(opt_uri->type, "public") == 0) { + opt_object_class = CKO_PUBLIC_KEY; + opt_object_class_str = "pubkey,"; + } else if (strcmp(opt_uri->type, "data") == 0) { + opt_object_class = CKO_DATA; + opt_object_class_str = "data"; + } else { + fprintf(stderr, "Unsupported object type \"%s\" specified in PKCS#11 URI\n", opt_uri->type); + util_print_usage_and_die(app_name, options, option_help, NULL); + } + } + if (opt_uri->id) { + opt_object_id_len = opt_uri->id_len; + if (opt_object_id_len > sizeof(opt_object_id)) + util_fatal("URI's object ID too long"); + memcpy(opt_object_id, opt_uri->id, opt_object_id_len); + } + } + + if (!opt_module) { + opt_module = DEFAULT_PKCS11_PROVIDER; + } +#ifdef _WIN32 + expanded_len = PATH_MAX; + expanded_len = ExpandEnvironmentStringsA(opt_module, expanded_val, expanded_len); + if (0 < expanded_len && expanded_len < sizeof expanded_val) + opt_module = expanded_val; +#endif + +#ifndef ENABLE_SHARED + if (strcmp(opt_module, DEFAULT_PKCS11_PROVIDER) == 0) + p11 = &pkcs11_function_list_3_0; + else +#endif + { + CK_FUNCTION_LIST_PTR p11_v2 = NULL; + + module = C_LoadModule(opt_module, &p11_v2); + if (module == NULL) + util_fatal("Failed to load pkcs11 module"); + p11 = (CK_FUNCTION_LIST_3_0_PTR) p11_v2; + } - rv = p11->C_Initialize(NULL); + /* This can be done even before initialization */ + if (do_list_interfaces) + list_interfaces(); + + rv = p11->C_Initialize(c_initialize_args_ptr); + +#if defined(_WIN32) || defined(HAVE_PTHREAD) + if (do_test_threads || rv != CKR_OK) + fprintf(stderr,"Main C_Initialize(%s) rv:%s\n", + (c_initialize_args_ptr) ? "CKF_OS_LOCKING_OK" : "NULL", CKR2Str(rv)); +#else if (rv == CKR_CRYPTOKI_ALREADY_INITIALIZED) fprintf(stderr, "\n*** Cryptoki library has already been initialized ***\n"); - else if (rv != CKR_OK) +#endif /* defined(_WIN32) || defined(HAVE_PTHREAD) */ + + if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) p11_fatal("C_Initialize", rv); #ifndef _WIN32 @@ -875,7 +1411,23 @@ int main(int argc, char * argv[]) goto end; } - if (!opt_slot_set && (action_count > do_list_slots)) { + if (opt_uri && !opt_slot_set && (action_count > do_list_slots)) { + if (opt_uri->token_manufacturer || opt_uri->token_model || opt_uri->serial || + opt_uri->slot_description || opt_uri->slot_manufacturer || opt_uri->token_label) { + if (!find_slot_by_uri(opt_uri, &opt_slot)) { + fprintf(stderr, "No slot matching URI found\n"); + err = 1; + goto end; + } + if (verbose) + fprintf(stderr, "Using slot matched by URI(0x%lx)\n", opt_slot); + } else { + if (!get_default_slot(&opt_slot)) { + err = 1; + goto end; + } + } + } else if (!opt_slot_set && (action_count > do_list_slots)) { if (opt_slot_description) { if (!find_slot_by_description(opt_slot_description, &opt_slot)) { fprintf(stderr, "No slot named \"%s\" found\n", opt_slot_description); @@ -898,39 +1450,23 @@ int main(int argc, char * argv[]) fprintf(stderr, "Using slot with index %lu (0x%lx)\n", opt_slot_index, opt_slot); } else { fprintf(stderr, "Slot with index %lu (counting from 0) is not available.\n", opt_slot_index); - fprintf(stderr, "You must specify a valid slot with either --slot, --slot-index or --slot-label.\n"); + fprintf(stderr, "You must specify a valid slot with either --slot, --slot-description, --slot-index or --token-label.\n"); err = 1; goto end; } } else { - /* use first slot with token present (or default slot on error) */ - unsigned int i, found = 0; - for (i = 0; i < p11_num_slots; i++) { - CK_SLOT_INFO info; - rv = p11->C_GetSlotInfo(p11_slots[i], &info); - if (rv != CKR_OK) - p11_fatal("C_GetSlotInfo", rv); - if (info.flags & CKF_TOKEN_PRESENT) { - opt_slot = p11_slots[i]; - fprintf(stderr, "Using slot %u with a present token (0x%lx)\n", i, opt_slot); - found = 1; - break; - } - } - if (!found) { - fprintf(stderr, "No slot with a token was found.\n"); + if (!get_default_slot(&opt_slot)) { err = 1; goto end; } - } } if (do_list_mechs) list_mechs(opt_slot); - if (do_sign || do_decrypt) { - CK_TOKEN_INFO info; + if (do_sign || do_decrypt || do_encrypt || do_unwrap || do_wrap) { + CK_TOKEN_INFO info; get_token_info(opt_slot, &info); if (!(info.flags & CKF_TOKEN_INITIALIZED)) @@ -986,26 +1522,173 @@ int main(int argc, char * argv[]) goto end; } - if (do_sign || do_derive || do_decrypt) { - if (!find_object(session, CKO_PRIVATE_KEY, &object, - opt_object_id_len ? opt_object_id : NULL, - opt_object_id_len, 0)) - util_fatal("Private key not found"); + uint16_t mf_flags = MF_UNKNOWN; + if (opt_mechanism_used) { + mf_flags = p11_mechanism_to_flags(opt_mechanism); + } + + if (do_sign || do_derive) { + /* + * Newer mechanisms have their details in the mechanism table, however + * if it's not known fall back to the old code always assuming it was a + * CKO_PRIVATE_KEY. + */ + if (mf_flags != MF_UNKNOWN) { + /* this function dies on error via util_fatal */ + if (opt_uri) { + find_object_flags_and_type_or_id_or_label(session, mf_flags, &object, + opt_object_class, opt_uri->type == NULL ? 0 : 1, + opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, opt_object_label, 0); + } else { + find_object_flags(session, mf_flags, &object, + opt_object_id_len ? opt_object_id : NULL, + opt_object_id_len, opt_object_label, 0); + } + } else if (opt_uri && opt_uri->type) { + /* If type in PKCS#11 URI is set, check the type and use it */ + if (opt_object_class != CKO_PRIVATE_KEY && opt_object_class != CKO_SECRET_KEY) { + util_fatal("Incorrect type of key"); + } + if (!find_object_type_or_id_or_label(session, opt_object_class, opt_uri->type == NULL ? 0 : 1, + &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, opt_object_label, 0)) { + util_fatal("Private/secret key not found"); + } + } else if (!find_object(session, CKO_PRIVATE_KEY, &object, + opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, opt_object_label, 0)) { + if (!find_object(session, CKO_SECRET_KEY, &object, + opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, opt_object_label, 0)) + util_fatal("Private/secret key not found"); + } + } + + if (do_decrypt) { + /* + * Newer mechanisms have their details in the mechanism table, however + * if it's not known fall back to the old code always assuming it was a + * CKO_PUBLIC_KEY then a CKO_CERTIFICATE. + */ + if (mf_flags != MF_UNKNOWN) { + /* this function dies on error via util_fatal */ + if (opt_uri) { + find_object_flags_and_type_or_id_or_label(session, mf_flags, &object, + opt_object_class, opt_uri->type == NULL ? 0 : 1, + opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, opt_object_label, 0); + } else { + find_object_flags(session, mf_flags, &object, + opt_object_id_len ? opt_object_id : NULL, + opt_object_id_len, opt_object_label, 0); + } + } else if (opt_uri && opt_uri->type) { + /* If type in PKCS#11 URI is set, check the type and use it */ + if (opt_object_class != CKO_PRIVATE_KEY && opt_object_class != CKO_SECRET_KEY) { + util_fatal("Incorrect type of key"); + } + if (!find_object_type_or_id_or_label(session, opt_object_class, opt_uri->type == NULL ? 0 : 1, + &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, opt_object_label, 0)) { + util_fatal("Private/secret key not found"); + } + } else if (!find_object(session, CKO_PRIVATE_KEY, &object, + opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, opt_object_label, 0)) + if (!find_object(session, CKO_SECRET_KEY, &object, + opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, opt_object_label, 0)) + util_fatal("Private/secret key not found"); + } + + if (do_encrypt) { + /* + * Newer mechanisms have their details in the mechanism table, however + * if it's not known fall back to the old code always assuming it was a + * CKO_PUBLIC_KEY then a CKO_CERTIFICATE. + */ + + if (mf_flags != MF_UNKNOWN) { + /* this function dies on error via util_fatal */ + if (opt_uri) { + find_object_flags_and_type_or_id_or_label(session, mf_flags, &object, + opt_object_class, opt_uri->type == NULL ? 0 : 1, + opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, opt_object_label, 0); + } else { + find_object_flags(session, mf_flags, &object, + opt_object_id_len ? opt_object_id : NULL, + opt_object_id_len, opt_object_label, 0); + } + } else if (opt_uri && opt_uri->type) { + /* If type in PKCS#11 URI is set, check the type and use it */ + if (opt_object_class != CKO_PUBLIC_KEY && opt_object_class != CKO_SECRET_KEY) { + util_fatal("Incorrect type of key"); + } + if (!find_object_type_or_id_or_label(session, opt_object_class, opt_uri->type == NULL ? 0 : 1, + &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, opt_object_label, 0)) { + util_fatal("Public/Secret key not found"); + } + } else if (!find_object(session, CKO_PUBLIC_KEY, &object, + opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, opt_object_label, 0)) + if (!find_object(session, CKO_SECRET_KEY, &object, + opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, opt_object_label, 0)) + util_fatal("Public/Secret key not found"); + } + + if (do_verify) { + /* + * Newer mechanisms have their details in the mechanism table, however + * if it's not known fall back to the old code always assuming it was a + * CKO_PUBLIC_KEY then a CKO_CERTIFICATE. + */ + + if (mf_flags != MF_UNKNOWN) { + /* this function dies on error via util_fatal */ + if (opt_uri) { + find_object_flags_and_type_or_id_or_label(session, mf_flags, &object, + opt_object_class, opt_uri->type == NULL ? 0 : 1, + opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, opt_object_label, 0); + } else { + find_object_flags(session, mf_flags, &object, + opt_object_id_len ? opt_object_id : NULL, + opt_object_id_len, opt_object_label, 0); + } + } else if (opt_uri && opt_uri->type) { + /* If type in PKCS#11 URI is set, check the type and use it */ + if (opt_object_class != CKO_PUBLIC_KEY && opt_object_class != CKO_CERTIFICATE) { + util_fatal("Incorrect type of key"); + } + if (!find_object_type_or_id_or_label(session, opt_object_class, opt_uri->type == NULL ? 0 : 1, + &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, opt_object_label, 0)) { + util_fatal("Public key nor certificate not found"); + } + } else if (!find_object(session, CKO_PUBLIC_KEY, &object, + opt_object_id_len ? opt_object_id : NULL, + opt_object_id_len, opt_object_label, 0) && + !find_object(session, CKO_CERTIFICATE, &object, + opt_object_id_len ? opt_object_id : NULL, + opt_object_id_len, opt_object_label, 0)) + util_fatal("Public key nor certificate not found"); } + if (do_unwrap) + unwrap_key(session); + + if (do_wrap) + wrap_key(session); + /* before list objects, so we can see a derived key */ if (do_derive) derive_key(opt_slot, session, object); if (do_list_objects) - list_objects(session, opt_object_class); + list_objects(session); if (do_sign) sign_data(opt_slot, session, object); + if (do_verify) + verify_signature(opt_slot, session, object); + if (do_decrypt) decrypt_data(opt_slot, session, object); + if (do_encrypt) + encrypt_data(opt_slot, session, object); + if (do_hash) hash_data(opt_slot, session); @@ -1040,17 +1723,18 @@ int main(int argc, char * argv[]) if (opt_object_class_str == NULL) util_fatal("You should specify type of the object to delete"); if (opt_object_id_len == 0 && opt_object_label == NULL && - opt_application_label == NULL && opt_application_id == NULL) + opt_application_label == NULL && opt_application_id == NULL && + opt_object_index_set == 0) util_fatal("You should specify at least one of the " - "object ID, object label, application label or application ID"); + "object ID, object label, application label, application ID or object index"); delete_object(session); } if (do_set_id) { if (opt_object_class_str == NULL) util_fatal("You should specify the object type with the -y option"); - if (opt_object_id_len == 0) - util_fatal("You should specify the current ID with the -d option"); + if (opt_object_id_len == 0 && opt_object_label == NULL) + util_fatal("You should specify the current object with the -d or -a option"); set_id_attr(session); } @@ -1081,6 +1765,17 @@ int main(int argc, char * argv[]) if (rv != CKR_OK) p11_fatal("C_CloseSession", rv); } + pkcs11_uri_free(opt_uri); + +#if defined(_WIN32) || defined(HAVE_PTHREAD) + if (do_test_threads) { + /* running threading tests is deliberately placed after opt_slot was + * initialized so that the command line options allow detailed + * configuration when running with `--test-threads LT` */ + test_threads(); + test_threads_cleanup(); + } +#endif /* defined(_WIN32) || defined(HAVE_PTHREAD) */ if (p11) p11->C_Finalize(NULL_PTR); @@ -1090,7 +1785,6 @@ int main(int argc, char * argv[]) return err; } - static void show_cryptoki_info(void) { CK_INFO info; @@ -1113,6 +1807,41 @@ static void show_cryptoki_info(void) info.libraryVersion.minor); } +static void list_interfaces(void) +{ + CK_ULONG count = 0, i; + CK_INTERFACE_PTR interfaces = NULL; + CK_RV rv; + + if (p11->version.major < 3) { + fprintf(stderr, "Interfaces are supported only in PKCS #11 3.0 and newer\n"); + exit(1); + } + + rv = p11->C_GetInterfaceList(NULL, &count); + if (rv != CKR_OK) { + p11_fatal("C_GetInterfaceList(size inquire)", rv); + } + + interfaces = malloc(count * sizeof(CK_INTERFACE)); + if (interfaces == NULL) { + perror("malloc failed"); + exit(1); + } + rv = p11->C_GetInterfaceList(interfaces, &count); + if (rv != CKR_OK) { + p11_fatal("C_GetInterfaceList", rv); + } + for (i = 0; i < count; i++) { + printf("Interface '%s'\n version: %d.%d\n funcs=%p\n flags=0x%lu\n", + interfaces[i].pInterfaceName, + ((CK_VERSION *)interfaces[i].pFunctionList)->major, + ((CK_VERSION *)interfaces[i].pFunctionList)->minor, + interfaces[i].pFunctionList, + interfaces[i].flags); + } + +} static void list_slots(int tokens, int refresh, int print) { CK_SLOT_INFO info; @@ -1125,15 +1854,18 @@ static void list_slots(int tokens, int refresh, int print) if (rv != CKR_OK) p11_fatal("C_GetSlotList(NULL)", rv); free(p11_slots); - p11_slots = calloc(p11_num_slots, sizeof(CK_SLOT_ID)); - if (p11_slots == NULL) { - perror("calloc failed"); - exit(1); + p11_slots = NULL; + if (p11_num_slots > 0) { + p11_slots = calloc(p11_num_slots, sizeof(CK_SLOT_ID)); + if (p11_slots == NULL) { + perror("calloc failed"); + exit(1); + } + rv = p11->C_GetSlotList(tokens, p11_slots, &p11_num_slots); + if (rv != CKR_OK) + p11_fatal("C_GetSlotList()", rv); } - rv = p11->C_GetSlotList(tokens, p11_slots, &p11_num_slots); - if (rv != CKR_OK) - p11_fatal("C_GetSlotList()", rv); } if (!print) @@ -1170,6 +1902,50 @@ static void list_slots(int tokens, int refresh, int print) } } +static const char * +copy_key_value_to_uri(const char *key, const char *value, CK_BBOOL last) +{ + static char URI[1024]; + static size_t shift = 0; + if (key && (shift + strlen(key) < sizeof(URI))) { + strcpy(&URI[shift], key); + shift += strlen(key); + } + if (value && (shift + strlen(value) < sizeof(URI))) { + strcpy(&URI[shift], value); + shift += strlen(value); + } + if (key && value && !last && shift < sizeof(URI)) { + URI[shift++] = ';'; + } + if (last && shift < sizeof(URI)) { + URI[shift] = '\0'; + shift = 0; + } + return URI; +} + +static const char * +get_uri(CK_TOKEN_INFO_PTR info, CK_SLOT_ID slot) +{ + copy_key_value_to_uri("pkcs11:", NULL, CK_FALSE); + const char *model = percent_encode(info->model, sizeof(info->model)); + copy_key_value_to_uri("model=", model, CK_FALSE); + const char *manufacturer = percent_encode(info->manufacturerID, sizeof(info->manufacturerID)); + copy_key_value_to_uri("manufacturer=", manufacturer, CK_FALSE); + const char *serial = percent_encode(info->serialNumber, sizeof(info->serialNumber)); + copy_key_value_to_uri("serial=", serial, CK_FALSE); + + if (opt_uri_with_slot_id) { + static char slot_id_str[32]; + snprintf(slot_id_str, sizeof(slot_id_str), "%lu", slot); + copy_key_value_to_uri("slot-id=", slot_id_str, CK_FALSE); + } + + const char *token = percent_encode(info->label, sizeof(info->label)); + return copy_key_value_to_uri("token=", token, CK_TRUE); +} + static void show_token(CK_SLOT_ID slot) { CK_TOKEN_INFO info; @@ -1204,6 +1980,7 @@ static void show_token(CK_SLOT_ID slot) printf(" serial num : %s\n", p11_utf8_to_local(info.serialNumber, sizeof(info.serialNumber))); printf(" pin min/max : %lu/%lu\n", info.ulMinPinLen, info.ulMaxPinLen); + printf(" uri : %s\n", get_uri(&info, slot)); } static void list_mechs(CK_SLOT_ID slot) @@ -1282,6 +2059,34 @@ static void list_mechs(CK_SLOT_ID slot) printf(", derive"); info.flags &= ~CKF_DERIVE; } + if (info.flags & CKF_EC_F_P) { + printf(", EC F_P"); + info.flags &= ~CKF_EC_F_P; + } + if (info.flags & CKF_EC_F_2M) { + printf(", EC F_2M"); + info.flags &= ~CKF_EC_F_2M; + } + if (info.flags & CKF_EC_ECPARAMETERS) { + printf(", EC parameters"); + info.flags &= ~CKF_EC_ECPARAMETERS; + } + if (info.flags & CKF_EC_OID) { + printf(", EC OID"); + info.flags &= ~CKF_EC_OID; + } + if (info.flags & CKF_EC_UNCOMPRESS) { + printf(", EC uncompressed"); + info.flags &= ~CKF_EC_UNCOMPRESS; + } + if (info.flags & CKF_EC_COMPRESS) { + printf(", EC compressed"); + info.flags &= ~CKF_EC_COMPRESS; + } + if (info.flags & CKF_EC_CURVENAME) { + printf(", EC curve name"); + info.flags &= ~CKF_EC_CURVENAME; + } if (info.flags) printf(", other flags=0x%x", (unsigned int) info.flags); } @@ -1318,7 +2123,7 @@ static int login(CK_SESSION_HANDLE session, int login_type) pin_flags=info.flags & ( CKF_SO_PIN_COUNT_LOW | CKF_SO_PIN_FINAL_TRY | - CKF_SO_PIN_LOCKED | + CKF_SO_PIN_LOCKED | CKF_SO_PIN_TO_BE_CHANGED); if(pin_flags) printf("WARNING: %s\n",p11_token_info_flags(pin_flags)); @@ -1329,7 +2134,7 @@ static int login(CK_SESSION_HANDLE session, int login_type) pin_flags=info.flags & ( CKF_USER_PIN_COUNT_LOW | CKF_USER_PIN_FINAL_TRY | - CKF_USER_PIN_LOCKED | + CKF_USER_PIN_LOCKED | CKF_USER_PIN_TO_BE_CHANGED); if(pin_flags) printf("WARNING: %s\n",p11_token_info_flags(pin_flags)); @@ -1353,7 +2158,7 @@ static int login(CK_SESSION_HANDLE session, int login_type) rv = p11->C_Login(session, login_type, (CK_UTF8CHAR *) pin, pin == NULL ? 0 : strlen(pin)); - if (rv != CKR_OK) + if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) p11_fatal("C_Login", rv); if (pin_allocated) free(pin); @@ -1460,53 +2265,42 @@ static void init_pin(CK_SLOT_ID slot, CK_SESSION_HANDLE sess) static int change_pin(CK_SLOT_ID slot, CK_SESSION_HANDLE sess) { - char old_buf[21], *old_pin = NULL; - char new_buf[21], *new_pin = NULL; + char old_buf[21], *old_pin = opt_so_pin ? (char*)opt_so_pin : (char*)opt_pin; + char new_buf[21], *new_pin = (char *)opt_new_pin; CK_TOKEN_INFO info; CK_RV rv; int r; size_t len = 0; get_token_info(slot, &info); + const CK_FLAGS hasReaderPinPad = info.flags & CKF_PROTECTED_AUTHENTICATION_PATH; - if (!(info.flags & CKF_PROTECTED_AUTHENTICATION_PATH)) { - if (!opt_pin && !opt_so_pin) { - printf("Please enter the current PIN: "); - r = util_getpass(&old_pin, &len, stdin); - if (r < 0) - return 1; - if (!old_pin || !*old_pin || strlen(old_pin) > 20) - return 1; - strcpy(old_buf, old_pin); - old_pin = old_buf; - } - else { - if (opt_so_pin) - old_pin = (char *) opt_so_pin; - else - old_pin = (char *) opt_pin; - } - - if (!opt_new_pin) { - printf("Please enter the new PIN: "); - r = util_getpass(&new_pin, &len, stdin); - if (r < 0) - return 1; - if (!new_pin || !*new_pin || strlen(new_pin) > 20) - return 1; - strcpy(new_buf, new_pin); + if (!hasReaderPinPad && !old_pin) { + printf("Please enter the current PIN: "); + r = util_getpass(&old_pin, &len, stdin); + if (r < 0) + return 1; + if (!old_pin || !*old_pin || strlen(old_pin) > 20) + return 1; + strcpy(old_buf, old_pin); + old_pin = old_buf; + } + if (!hasReaderPinPad && !new_pin) { + printf("Please enter the new PIN: "); + r = util_getpass(&new_pin, &len, stdin); + if (r < 0) + return 1; + if (!new_pin || !*new_pin || strlen(new_pin) > 20) + return 1; + strcpy(new_buf, new_pin); - printf("Please enter the new PIN again: "); - r = util_getpass(&new_pin, &len, stdin); - if (r < 0) - return 1; - if (!new_pin || !*new_pin || strcmp(new_buf, new_pin) != 0) { - free(new_pin); - return 1; - } - } - else { - new_pin = (char *) opt_new_pin; + printf("Please enter the new PIN again: "); + r = util_getpass(&new_pin, &len, stdin); + if (r < 0) + return 1; + if (!new_pin || !*new_pin || strcmp(new_buf, new_pin) != 0) { + free(new_pin); + return 1; } } @@ -1574,7 +2368,7 @@ static int unlock_pin(CK_SLOT_ID slot, CK_SESSION_HANDLE sess, int login_type) return -1; } - if (!new_pin || !*new_pin || strlen(new_pin) > 20) { + if (strlen(new_pin) > 20) { if (new_pin != opt_new_pin) free(new_pin); return 1; @@ -1591,60 +2385,382 @@ static int unlock_pin(CK_SLOT_ID slot, CK_SESSION_HANDLE sess, int login_type) return 0; } +#ifdef ENABLE_OPENSSL +/* return matching ec_curve_info or NULL based on name */ +static struct ec_curve_info * +match_ec_curve_by_name(const char *name) +{ + for (size_t i = 0; ec_curve_infos[i].name != NULL; ++i) { + if (strcmp(ec_curve_infos[i].name, name) == 0) { + return &ec_curve_infos[i]; + } + } + + return NULL; +} +#endif + +/* return matching ec_curve_info or NULL based on ec_params */ +static const struct ec_curve_info * +match_ec_curve_by_params(const unsigned char *ec_params, CK_ULONG ec_params_size) +{ + if (ec_params_size < 3) + return NULL; + + for (size_t i = 0; ec_curve_infos[i].name != NULL; ++i) { + if ((ec_curve_infos[i].ec_params_size == ec_params_size) && + (memcmp(ec_curve_infos[i].ec_params, ec_params, ec_params_size) == 0)) { + return &ec_curve_infos[i]; + } + } + + return NULL; +} + +/* return digest length in bytes */ +static unsigned long hash_length(const unsigned long hash) { + unsigned long sLen = 0; + switch (hash) { + case CKM_SHA_1: + sLen = 20; + break; + case CKM_SHA224: + case CKM_SHA3_224: + sLen = 28; + break; + case CKM_SHA256: + case CKM_SHA3_256: + sLen = 32; + break; + case CKM_SHA384: + case CKM_SHA3_384: + sLen = 48; + break; + case CKM_SHA512: + case CKM_SHA3_512: + sLen = 64; + break; + default: + util_fatal("Unknown hash algorithm '%s' for RSA-PSS signatures", + p11_mechanism_to_name(hash)); + break; + } + return sLen; +} + +static unsigned long +parse_pss_params(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key, + CK_MECHANISM *mech, CK_RSA_PKCS_PSS_PARAMS *pss_params) +{ + unsigned long hashlen = 0; + + if (pss_params == NULL) + return 0; + + pss_params->hashAlg = 0; + + if (opt_hash_alg != 0 && opt_mechanism != CKM_RSA_PKCS_PSS) + util_fatal("The hash-algorithm is applicable only to " + "RSA-PKCS-PSS mechanism"); + + /* set "default" MGF and hash algorithms. We can overwrite MGF later */ + switch (opt_mechanism) { + case CKM_RSA_PKCS_PSS: + pss_params->hashAlg = opt_hash_alg; + + switch (opt_hash_alg) { + case CKM_SHA224: + pss_params->mgf = CKG_MGF1_SHA224; + break; + case CKM_SHA256: + pss_params->mgf = CKG_MGF1_SHA256; + break; + case CKM_SHA384: + pss_params->mgf = CKG_MGF1_SHA384; + break; + case CKM_SHA512: + pss_params->mgf = CKG_MGF1_SHA512; + break; + case CKM_SHA3_224: + pss_params->mgf = CKG_MGF1_SHA3_224; + break; + case CKM_SHA3_256: + pss_params->mgf = CKG_MGF1_SHA3_256; + break; + case CKM_SHA3_384: + pss_params->mgf = CKG_MGF1_SHA3_384; + break; + case CKM_SHA3_512: + pss_params->mgf = CKG_MGF1_SHA3_512; + break; + default: + /* the PSS should use SHA-1 if not specified */ + pss_params->hashAlg = CKM_SHA_1; + /* fallthrough */ + case CKM_SHA_1: + pss_params->mgf = CKG_MGF1_SHA1; + } + break; + + case CKM_SHA1_RSA_PKCS_PSS: + pss_params->hashAlg = CKM_SHA_1; + pss_params->mgf = CKG_MGF1_SHA1; + break; + + case CKM_SHA224_RSA_PKCS_PSS: + pss_params->hashAlg = CKM_SHA224; + pss_params->mgf = CKG_MGF1_SHA224; + break; + + case CKM_SHA256_RSA_PKCS_PSS: + pss_params->hashAlg = CKM_SHA256; + pss_params->mgf = CKG_MGF1_SHA256; + break; + + case CKM_SHA384_RSA_PKCS_PSS: + pss_params->hashAlg = CKM_SHA384; + pss_params->mgf = CKG_MGF1_SHA384; + break; + + case CKM_SHA512_RSA_PKCS_PSS: + pss_params->hashAlg = CKM_SHA512; + pss_params->mgf = CKG_MGF1_SHA512; + break; + + case CKM_SHA3_224_RSA_PKCS_PSS: + pss_params->hashAlg = CKM_SHA3_224; + pss_params->mgf = CKG_MGF1_SHA3_224; + break; + + case CKM_SHA3_256_RSA_PKCS_PSS: + pss_params->hashAlg = CKM_SHA3_256; + pss_params->mgf = CKG_MGF1_SHA3_256; + break; + + case CKM_SHA3_384_RSA_PKCS_PSS: + pss_params->hashAlg = CKM_SHA3_384; + pss_params->mgf = CKG_MGF1_SHA3_384; + break; + + case CKM_SHA3_512_RSA_PKCS_PSS: + pss_params->hashAlg = CKM_SHA3_512; + pss_params->mgf = CKG_MGF1_SHA3_512; + break; + + default: /* The non-RSA-PSS algorithms do not need any parameters */ + return 0; + } + + /* One of RSA-PSS mechanisms above: They need parameters */ + if (pss_params->hashAlg) { + if (opt_mgf != 0) + pss_params->mgf = opt_mgf; + + hashlen = hash_length(pss_params->hashAlg); + + if (opt_salt_len_given == 1) { /* salt size explicitly given */ + unsigned long modlen = 0; + + modlen = BYTES4BITS(get_private_key_length(session, key)); + if (modlen == 0) + util_fatal("Incorrect length of private key"); + switch (opt_salt_len) { + case -1: /* salt size equals to digest size */ + pss_params->sLen = hashlen; + break; + case -2: /* maximum permissible salt len */ + case -3: + pss_params->sLen = modlen - hashlen - 2; + break; + default: /* use given size but its value must be >= 0 */ + if (opt_salt_len < 0) + util_fatal("Salt length must be greater or equal " + "to zero, or equal to -1 (meaning: use digest size) " + "or to -2 or -3 (meaning: use maximum permissible size"); + + pss_params->sLen = opt_salt_len; + break; + } /* end switch (opt_salt_len_given) */ + } else { /* use default: salt len of digest size */ + pss_params->sLen = hashlen; + } + + mech->pParameter = pss_params; + mech->ulParameterLen = sizeof(*pss_params); + + fprintf(stderr, "PSS parameters: hashAlg=%s, mgf=%s, salt_len=%lu B\n", + p11_mechanism_to_name(pss_params->hashAlg), + p11_mgf_to_name(pss_params->mgf), + pss_params->sLen); + } + return hashlen; +} + static void sign_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) { - unsigned char in_buffer[1025], sig_buffer[512]; + unsigned char in_buffer[1025]; + CK_BYTE_PTR sig_buffer = NULL; CK_MECHANISM mech; + CK_RSA_PKCS_PSS_PARAMS pss_params; + CK_SIGN_ADDITIONAL_CONTEXT sign_params = {0}; + CK_HASH_SIGN_ADDITIONAL_CONTEXT hash_sign_params = {0}; + CK_MAC_GENERAL_PARAMS mac_gen_param; + CK_EDDSA_PARAMS eddsa_params = { + .phFlag = CK_FALSE, + }; CK_RV rv; - CK_ULONG sig_len; - int fd, r; + CK_ULONG sig_len = 0; + int fd; + ssize_t sz; + unsigned long hashlen; if (!opt_mechanism_used) - if (!find_mechanism(slot, CKF_SIGN|CKF_HW, NULL, 0, &opt_mechanism)) + if (!find_mechanism(slot, CKF_SIGN|opt_allow_sw, NULL, 0, &opt_mechanism)) util_fatal("Sign mechanism not supported"); fprintf(stderr, "Using signature algorithm %s\n", p11_mechanism_to_name(opt_mechanism)); memset(&mech, 0, sizeof(mech)); mech.mechanism = opt_mechanism; + hashlen = parse_pss_params(session, key, &mech, &pss_params); if (opt_input == NULL) fd = 0; - else if ((fd = open(opt_input, O_RDONLY|O_BINARY)) < 0) + else if ((fd = open(opt_input, O_RDONLY | O_BINARY)) < 0) util_fatal("Cannot open %s: %m", opt_input); - r = read(fd, in_buffer, sizeof(in_buffer)); - if (r < 0) + sz = read(fd, in_buffer, sizeof(in_buffer)); + if (sz < 0) util_fatal("Cannot read from %s: %m", opt_input); + switch (opt_mechanism) { + case CKM_EDDSA: { + /* support pure EdDSA only */ + const struct ec_curve_info *curve; + unsigned char *ec_params; + CK_ULONG ec_params_size = 0; + + ec_params = getEC_PARAMS(session, key, &ec_params_size); + if (ec_params == NULL) { + util_fatal("Key has no EC_PARAMS attribute"); + } + + curve = match_ec_curve_by_params(ec_params, ec_params_size); + if (curve == NULL) { + util_fatal("Unknown or unsupported EC curve used in key"); + } + + /* Ed448: need the params defined but default to false */ + if (curve->size == 448) { + mech.pParameter = &eddsa_params; + mech.ulParameterLen = (CK_ULONG)sizeof(eddsa_params); + } + break; + } + case CKM_RSA_PKCS_PSS: + if ((size_t)sz != hashlen) { + util_fatal("For %s mechanism, message size (got %zd bytes) " + "must be equal to specified digest length (%lu)\n", + p11_mechanism_to_name(opt_mechanism), sz, hashlen); + } + break; + case CKM_AES_CMAC_GENERAL: + if (opt_mac_gen_param == 0 || opt_mac_gen_param > 16) { + util_fatal("For %s mechanism, the option --mac-general-param " + "is mandatory and its value must be comprised between 1 and " + "16 (>=8 recommended).\n", + p11_mechanism_to_name(opt_mechanism)); + } + mac_gen_param = opt_mac_gen_param; + mech.pParameter = &mac_gen_param; + mech.ulParameterLen = sizeof(CK_MAC_GENERAL_PARAMS); + break; + case CKM_ML_DSA: + case CKM_HASH_ML_DSA_SHA224: + case CKM_HASH_ML_DSA_SHA256: + case CKM_HASH_ML_DSA_SHA384: + case CKM_HASH_ML_DSA_SHA512: + case CKM_HASH_ML_DSA_SHA3_224: + case CKM_HASH_ML_DSA_SHA3_256: + case CKM_HASH_ML_DSA_SHA3_384: + case CKM_HASH_ML_DSA_SHA3_512: + case CKM_HASH_ML_DSA_SHAKE128: + case CKM_HASH_ML_DSA_SHAKE256: + case CKM_SLH_DSA: + case CKM_HASH_SLH_DSA_SHA224: + case CKM_HASH_SLH_DSA_SHA256: + case CKM_HASH_SLH_DSA_SHA384: + case CKM_HASH_SLH_DSA_SHA512: + case CKM_HASH_SLH_DSA_SHA3_224: + case CKM_HASH_SLH_DSA_SHA3_256: + case CKM_HASH_SLH_DSA_SHA3_384: + case CKM_HASH_SLH_DSA_SHA3_512: + case CKM_HASH_SLH_DSA_SHAKE128: + case CKM_HASH_SLH_DSA_SHAKE256: + /* TODO allow setting hedge and context */ + mech.pParameter = &sign_params; + mech.ulParameterLen = sizeof(CK_SIGN_ADDITIONAL_CONTEXT); + break; + case CKM_HASH_ML_DSA: + case CKM_HASH_SLH_DSA: + if (opt_hash_alg == 0) { + util_fatal("The mechanism %s requires a hash-algorithm", + p11_mechanism_to_name(opt_mechanism)); + } + hashlen = hash_length(opt_hash_alg); + if ((size_t)sz != hashlen) { + util_fatal("For %s mechanism, message size (got %zd bytes) " + "must be equal to specified digest length (%lu)\n", + p11_mechanism_to_name(opt_mechanism), sz, hashlen); + } + hash_sign_params.hash = opt_hash_alg; + /* TODO allow setting hedge and context */ + mech.pParameter = &hash_sign_params; + mech.ulParameterLen = sizeof(CK_HASH_SIGN_ADDITIONAL_CONTEXT); + break; + } + rv = CKR_CANCEL; - if (r < (int) sizeof(in_buffer)) { + if ((size_t)sz < sizeof(in_buffer)) { rv = p11->C_SignInit(session, &mech, key); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); - if (getALWAYS_AUTHENTICATE(session, key)) + if ((getCLASS(session, key) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, key)) login(session,CKU_CONTEXT_SPECIFIC); - sig_len = sizeof(sig_buffer); - rv = p11->C_Sign(session, in_buffer, r, sig_buffer, &sig_len); + rv = p11->C_Sign(session, in_buffer, sz, sig_buffer, &sig_len); + if (rv == CKR_OK) { + sig_buffer = malloc(sig_len); + if (!sig_buffer) + util_fatal("malloc() failure\n"); + rv = p11->C_Sign(session, in_buffer, sz, sig_buffer, &sig_len); + } } if (rv != CKR_OK) { rv = p11->C_SignInit(session, &mech, key); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); - if (getALWAYS_AUTHENTICATE(session, key)) + if ((getCLASS(session, key) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, key)) login(session,CKU_CONTEXT_SPECIFIC); do { - rv = p11->C_SignUpdate(session, in_buffer, r); + rv = p11->C_SignUpdate(session, in_buffer, sz); if (rv != CKR_OK) p11_fatal("C_SignUpdate", rv); - r = read(fd, in_buffer, sizeof(in_buffer)); - } while (r > 0); + sz = read(fd, in_buffer, sizeof(in_buffer)); + } while (sz > 0); - sig_len = sizeof(sig_buffer); + /* Signature buffer may have been allocated above */ + free(sig_buffer); + sig_buffer = NULL; + rv = p11->C_SignFinal(session, sig_buffer, &sig_len); + if (rv != CKR_OK) + p11_fatal("C_SignFinal", rv); + sig_buffer = malloc(sig_len); + if (!sig_buffer) + util_fatal("malloc() failure\n"); rv = p11->C_SignFinal(session, sig_buffer, &sig_len); if (rv != CKR_OK) p11_fatal("C_SignFinal", rv); @@ -1659,85 +2775,620 @@ static void sign_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session, util_fatal("failed to open %s: %m", opt_output); } - if (opt_mechanism == CKM_ECDSA || opt_mechanism == CKM_ECDSA_SHA1) { - if (opt_sig_format && (!strcmp(opt_sig_format, "openssl") || !strcmp(opt_sig_format, "sequence"))) { + if (opt_mechanism == CKM_ECDSA || opt_mechanism == CKM_ECDSA_SHA1 || + opt_mechanism == CKM_ECDSA_SHA256 || opt_mechanism == CKM_ECDSA_SHA384 || + opt_mechanism == CKM_ECDSA_SHA512 || opt_mechanism == CKM_ECDSA_SHA224 || + opt_mechanism == CKM_ECDSA_SHA3_224 || opt_mechanism == CKM_ECDSA_SHA3_256 || + opt_mechanism == CKM_ECDSA_SHA3_384 || opt_mechanism == CKM_ECDSA_SHA3_512) { + if (opt_sig_format && (!strcmp(opt_sig_format, "openssl") || + !strcmp(opt_sig_format, "sequence"))) { unsigned char *seq; size_t seqlen; - if (sc_asn1_sig_value_rs_to_sequence(NULL, sig_buffer, sig_len, &seq, &seqlen)) { + if (sc_asn1_sig_value_rs_to_sequence(NULL, sig_buffer, + sig_len, &seq, &seqlen)) { util_fatal("Failed to convert signature to ASN.1 sequence format"); } - memcpy(sig_buffer, seq, seqlen); + free(sig_buffer); + sig_buffer = seq; sig_len = seqlen; - - free(seq); } } - r = write(fd, sig_buffer, sig_len); + sz = write(fd, sig_buffer, sig_len); - if (r < 0) + if (sz < 0) util_fatal("Failed to write to %s: %m", opt_output); if (fd != 1) close(fd); + free(sig_buffer); } - -static void decrypt_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session, +static void verify_signature(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) { - unsigned char in_buffer[1024], out_buffer[1024]; + unsigned char in_buffer[1025]; + CK_BYTE_PTR sig_buffer = NULL; CK_MECHANISM mech; + CK_RSA_PKCS_PSS_PARAMS pss_params; + CK_MAC_GENERAL_PARAMS mac_gen_param; + CK_EDDSA_PARAMS eddsa_params = { + .phFlag = CK_FALSE, + }; + CK_SIGN_ADDITIONAL_CONTEXT sign_params = {0}; + CK_HASH_SIGN_ADDITIONAL_CONTEXT hash_sign_params = {0}; CK_RV rv; - CK_ULONG in_len, out_len; - int fd, r; + CK_ULONG sig_len = 0; + int fd, fd2; + struct stat sig_st; + ssize_t sz, sz2; + unsigned long hashlen; + + if (!opt_mechanism_used) + if (!find_mechanism(slot, CKF_VERIFY|opt_allow_sw, NULL, 0, &opt_mechanism)) + util_fatal("Mechanism not supported for signature verification"); + + fprintf(stderr, "Using signature algorithm %s\n", p11_mechanism_to_name(opt_mechanism)); + memset(&mech, 0, sizeof(mech)); + mech.mechanism = opt_mechanism; + hashlen = parse_pss_params(session, key, &mech, &pss_params); + if (hashlen && opt_salt_len_given) { + if (opt_salt_len == -2) { + /* openssl allow us to set sLen to -2 for autodetecting salt length + * here maximal CK_ULONG value is used to pass this special code + * to openssl. For non OpenSC PKCS#11 module this is minimal limitation + * because there is no need to use extra long salt length. + */ + pss_params.sLen = ((CK_ULONG) 1 ) << (sizeof(CK_ULONG) * CHAR_BIT -1); + fprintf(stderr, "Warning, requesting salt length recovery from signature (supported only in in opensc pkcs11 module).\n"); + } + } + + /* Open a signature file */ + if (opt_signature_file == NULL) + util_fatal("No file with signature provided. Use --signature-file"); + else if ((fd2 = open(opt_signature_file, O_RDONLY | O_BINARY)) < 0) + util_fatal("Cannot open %s: %m", opt_signature_file); + else if (fstat(fd2, &sig_st) != 0) + util_fatal("Couldn't get size of file \"", opt_signature_file); + sig_len = sig_st.st_size; + sig_buffer = malloc(sig_len); + if (!sig_buffer) + util_fatal("malloc() failure\n"); + sz2 = read(fd2, sig_buffer, sig_len); + if (sz2 < 0) + util_fatal("Cannot read from %s: %m", opt_signature_file); + + close(fd2); + + /* Open the data file */ + if (opt_input == NULL) + fd = 0; + else if ((fd = open(opt_input, O_RDONLY | O_BINARY)) < 0) + util_fatal("Cannot open %s: %m", opt_input); + + sz = read(fd, in_buffer, sizeof(in_buffer)); + if (sz < 0) + util_fatal("Cannot read from %s: %m", opt_input); + + switch (opt_mechanism) { + case CKM_EDDSA: { + /* support pure EdDSA only */ + const struct ec_curve_info *curve; + unsigned char *ec_params; + CK_ULONG ec_params_size = 0; + + ec_params = getEC_PARAMS(session, key, &ec_params_size); + if (ec_params == NULL) { + util_fatal("Key has no EC_PARAMS attribute"); + } + + curve = match_ec_curve_by_params(ec_params, ec_params_size); + if (curve == NULL) { + util_fatal("Unknown, invalid or unsupported EC curve used in key"); + } + + /* Ed448: need the params defined but default to false */ + if (curve->size == 448) { + mech.pParameter = &eddsa_params; + mech.ulParameterLen = (CK_ULONG)sizeof(eddsa_params); + } + break; + } + case CKM_ECDSA: + case CKM_ECDSA_SHA1: + case CKM_ECDSA_SHA256: + case CKM_ECDSA_SHA384: + case CKM_ECDSA_SHA512: + case CKM_ECDSA_SHA224: + case CKM_ECDSA_SHA3_224: + case CKM_ECDSA_SHA3_256: + case CKM_ECDSA_SHA3_384: + case CKM_ECDSA_SHA3_512: + if (opt_sig_format && (!strcmp(opt_sig_format, "openssl") || + !strcmp(opt_sig_format, "sequence"))) { + + CK_BYTE *bytes, *tmp; + CK_ULONG len; + size_t rs_len = 0, taglen; + unsigned int cla, tag; + unsigned char rs_buffer[512]; + bytes = getEC_POINT(session, key, &len); + /* + * (We only support uncompressed for now) + * Uncompressed EC_POINT is DER OCTET STRING of "04||x||y" + * So a "256" bit key has x and y of 32 bytes each + * something like: "04 41 04||x||y" + * Extract length from ASN.1 + */ + tmp = bytes; + if (sc_asn1_read_tag((const u8 **)&tmp, len, &cla, &tag, &taglen) != SC_SUCCESS || + len < 2 || len > 255 || taglen < 1) { + free(bytes); + util_fatal("Key not supported"); + } + free(bytes); + rs_len = taglen - 1; // len is length of "04||x||y" + + if (sc_asn1_sig_value_sequence_to_rs(NULL, sig_buffer, sz2, + rs_buffer, rs_len)) { + util_fatal("Failed to convert ASN.1 signature"); + } + + memcpy(sig_buffer, rs_buffer, rs_len); + sz2 = rs_len; + } + break; + case CKM_RSA_PKCS_PSS: + if ((size_t)sz != hashlen) { + util_fatal("For %s mechanism, message size (got %zd bytes)" + " must be equal to specified digest length (%lu)\n", + p11_mechanism_to_name(opt_mechanism), sz, hashlen); + } + break; + case CKM_AES_CMAC_GENERAL: + if (opt_mac_gen_param == 0 || opt_mac_gen_param > 16) { + util_fatal("For %s mechanism, the option --mac-general-param " + "is mandatory and its value must be comprised between 1 and " + "16 (>=8 recommended).\n", + p11_mechanism_to_name(opt_mechanism)); + } + mac_gen_param = opt_mac_gen_param; + mech.pParameter = &mac_gen_param; + mech.ulParameterLen = sizeof(CK_MAC_GENERAL_PARAMS); + break; + case CKM_ML_DSA: + case CKM_HASH_ML_DSA_SHA224: + case CKM_HASH_ML_DSA_SHA256: + case CKM_HASH_ML_DSA_SHA384: + case CKM_HASH_ML_DSA_SHA512: + case CKM_HASH_ML_DSA_SHA3_224: + case CKM_HASH_ML_DSA_SHA3_256: + case CKM_HASH_ML_DSA_SHA3_384: + case CKM_HASH_ML_DSA_SHA3_512: + case CKM_HASH_ML_DSA_SHAKE128: + case CKM_HASH_ML_DSA_SHAKE256: + case CKM_SLH_DSA: + case CKM_HASH_SLH_DSA_SHA224: + case CKM_HASH_SLH_DSA_SHA256: + case CKM_HASH_SLH_DSA_SHA384: + case CKM_HASH_SLH_DSA_SHA512: + case CKM_HASH_SLH_DSA_SHA3_224: + case CKM_HASH_SLH_DSA_SHA3_256: + case CKM_HASH_SLH_DSA_SHA3_384: + case CKM_HASH_SLH_DSA_SHA3_512: + case CKM_HASH_SLH_DSA_SHAKE128: + case CKM_HASH_SLH_DSA_SHAKE256: + /* TODO allow setting hedge and context */ + mech.pParameter = &sign_params; + mech.ulParameterLen = sizeof(CK_SIGN_ADDITIONAL_CONTEXT); + break; + case CKM_HASH_ML_DSA: + case CKM_HASH_SLH_DSA: + if (opt_hash_alg == 0) { + util_fatal("The mechanism %s requires a hash-algorithm", + p11_mechanism_to_name(opt_mechanism)); + } + hashlen = hash_length(opt_hash_alg); + if ((size_t)sz != hashlen) { + util_fatal("For %s mechanism, message size (got %zd bytes) " + "must be equal to specified digest length (%lu)\n", + p11_mechanism_to_name(opt_mechanism), sz, hashlen); + } + hash_sign_params.hash = opt_hash_alg; + /* TODO allow setting hedge and context */ + mech.pParameter = &hash_sign_params; + mech.ulParameterLen = sizeof(CK_HASH_SIGN_ADDITIONAL_CONTEXT); + break; + } + + rv = CKR_CANCEL; + if ((size_t)sz < sizeof(in_buffer)) { + rv = p11->C_VerifyInit(session, &mech, key); + if (rv != CKR_OK) + p11_fatal("C_VerifyInit", rv); + + sig_len = sz2; + rv = p11->C_Verify(session, in_buffer, sz, sig_buffer, sig_len); + } + + if (rv != CKR_OK && rv != CKR_SIGNATURE_INVALID) { + rv = p11->C_VerifyInit(session, &mech, key); + if (rv != CKR_OK) + p11_fatal("C_VerifyInit", rv); + + do { + rv = p11->C_VerifyUpdate(session, in_buffer, sz); + if (rv != CKR_OK) + p11_fatal("C_VerifyUpdate", rv); + + sz = read(fd, in_buffer, sizeof(in_buffer)); + } while (sz > 0); + + sig_len = sz2; + rv = p11->C_VerifyFinal(session, sig_buffer, sig_len); + if (rv != CKR_OK && rv != CKR_SIGNATURE_INVALID) + p11_fatal("C_VerifyFinal", rv); + } + + if (fd != 0) + close(fd); + + if (rv == CKR_OK) + printf("Signature is valid\n"); + else if (rv == CKR_SIGNATURE_INVALID) + util_fatal("Invalid signature"); + else + util_fatal("Signature verification failed: rv = %s (0x%0x)\n", CKR2Str(rv), (unsigned int)rv); + free(sig_buffer); +} + +static void +build_rsa_oaep_params( + CK_RSA_PKCS_OAEP_PARAMS *oaep_params, + CK_MECHANISM *mech, + char *param, + int param_len) +{ + /* An RSA-OAEP mechanism needs parameters */ + + /* set "default" MGF and hash algorithms. We can overwrite MGF later */ + oaep_params->hashAlg = opt_hash_alg; + switch (opt_hash_alg) { + case CKM_SHA_1: + oaep_params->mgf = CKG_MGF1_SHA1; + break; + case CKM_SHA224: + oaep_params->mgf = CKG_MGF1_SHA224; + break; + case CKM_SHA3_224: + oaep_params->mgf = CKG_MGF1_SHA3_224; + break; + case CKM_SHA3_256: + oaep_params->mgf = CKG_MGF1_SHA3_256; + break; + case CKM_SHA3_384: + oaep_params->mgf = CKG_MGF1_SHA3_384; + break; + case CKM_SHA3_512: + oaep_params->mgf = CKG_MGF1_SHA3_512; + break; + default: + fprintf(stderr, "hash-algorithm %s unknown, defaulting to CKM_SHA256\n", p11_mechanism_to_name(opt_hash_alg)); + oaep_params->hashAlg = CKM_SHA256; + /* fall through */ + case CKM_SHA256: + oaep_params->mgf = CKG_MGF1_SHA256; + break; + case CKM_SHA384: + oaep_params->mgf = CKG_MGF1_SHA384; + break; + case CKM_SHA512: + oaep_params->mgf = CKG_MGF1_SHA512; + break; + } + + if (opt_mgf != 0) { + oaep_params->mgf = opt_mgf; + } else { + fprintf(stderr, "mgf not set, defaulting to %s\n", p11_mgf_to_name(oaep_params->mgf)); + } + + /* PKCS11 3.0 is vague about setting CKZ_DATA_SPECIFIED */ + oaep_params->source = CKZ_DATA_SPECIFIED; + oaep_params->pSourceData = param; + oaep_params->ulSourceDataLen = param_len; + + mech->pParameter = oaep_params; + mech->ulParameterLen = sizeof(*oaep_params); + + fprintf(stderr, "OAEP parameters: hashAlg=%s, mgf=%s, source_type=%lu, source_ptr=%p, source_len=%lu\n", + p11_mechanism_to_name(oaep_params->hashAlg), + p11_mgf_to_name(oaep_params->mgf), + oaep_params->source, + oaep_params->pSourceData, + oaep_params->ulSourceDataLen); +} + +static void +build_chacha20_params( + CK_CHACHA20_PARAMS *params, + CK_MECHANISM *mech, + CK_BYTE *iv) +{ + params->pBlockCounter = iv; + params->blockCounterBits = 64; + params->pNonce = &iv[8]; + params->ulNonceBits = 64; + + mech->pParameter = params; + mech->ulParameterLen = sizeof(*params); +} + +typedef union +{ + CK_RSA_PKCS_OAEP_PARAMS oaep; + CK_GCM_PARAMS gcm; + CK_CHACHA20_PARAMS chacha20; + CK_SALSA20_CHACHA20_POLY1305_PARAMS chacha20poly1305; + CK_AES_CTR_PARAMS ctr; +} params_t; + +static void +build_params( + params_t *params, + CK_MECHANISM *mech, + CK_BYTE_PTR *iv, + CK_BYTE_PTR *aad) +{ + size_t iv_size = 0; + size_t aad_size = 0; + + /* set "default" MGF and hash algorithms. We can overwrite MGF later */ + switch (opt_mechanism) { + case CKM_RSA_PKCS_OAEP: + build_rsa_oaep_params(¶ms->oaep, mech, NULL, 0); + break; + case CKM_RSA_X_509: + case CKM_RSA_PKCS: + case CKM_AES_ECB: + mech->pParameter = NULL; + mech->ulParameterLen = 0; + break; + case CKM_AES_CBC: + case CKM_AES_CBC_PAD: + iv_size = 16; + *iv = hex_string_to_byte_array(opt_iv, &iv_size, "IV"); + mech->pParameter = *iv; + mech->ulParameterLen = iv_size; + break; + case CKM_AES_GCM: + *iv = hex_string_to_byte_array(opt_iv, &iv_size, "IV"); + params->gcm.pIv = *iv; + params->gcm.ulIvLen = iv_size; + *aad = hex_string_to_byte_array(opt_aad, &aad_size, "AAD"); + params->gcm.pAAD = *aad; + params->gcm.ulAADLen = aad_size; + params->gcm.ulTagBits = opt_tag_bits; + mech->pParameter = ¶ms->gcm; + mech->ulParameterLen = sizeof(params->gcm); + break; + case CKM_AES_CTR: + *iv = hex_string_to_byte_array(opt_iv, &iv_size, "IV"); + if (iv_size != 16U) { + util_fatal("Invalid IV length %zu", iv_size); + } + memcpy(¶ms->ctr.cb[0], *iv, sizeof(params->ctr.cb)); + params->ctr.ulCounterBits = 128U; + mech->pParameter = ¶ms->ctr; + mech->ulParameterLen = sizeof(params->ctr); + break; + case CKM_CHACHA20: + *iv = hex_string_to_byte_array(opt_iv, &iv_size, "IV"); + if (iv_size != 16) { + util_fatal("Invalid iv size %zu\n", iv_size); + } + build_chacha20_params(¶ms->chacha20, mech, *iv); + break; + case CKM_CHACHA20_POLY1305: + *iv = hex_string_to_byte_array(opt_iv, &iv_size, "IV"); + params->chacha20poly1305.pNonce = *iv; + params->chacha20poly1305.ulNonceLen = iv_size; + *aad = hex_string_to_byte_array(opt_aad, &aad_size, "AAD"); + params->chacha20poly1305.pAAD = *aad; + params->chacha20poly1305.ulAADLen = aad_size; + mech->pParameter = ¶ms->chacha20poly1305; + mech->ulParameterLen = sizeof(params->chacha20poly1305); + break; + default: + break; + } +} + +static void +decrypt_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE key) +{ + unsigned char in_buffer[1024], out_buffer[1024]; + CK_MECHANISM mech; + CK_RV rv; + params_t params = {0}; + CK_BYTE_PTR iv = NULL; + CK_BYTE_PTR aad = NULL; + CK_ULONG in_len, out_len; + int fd_in, fd_out; + ssize_t sz; if (!opt_mechanism_used) - if (!find_mechanism(slot, CKF_DECRYPT|CKF_HW, NULL, 0, &opt_mechanism)) + if (!find_mechanism(slot, CKF_DECRYPT | opt_allow_sw, NULL, 0, &opt_mechanism)) util_fatal("Decrypt mechanism not supported"); fprintf(stderr, "Using decrypt algorithm %s\n", p11_mechanism_to_name(opt_mechanism)); memset(&mech, 0, sizeof(mech)); mech.mechanism = opt_mechanism; + if (opt_hash_alg != 0 && opt_mechanism != CKM_RSA_PKCS_OAEP) + util_fatal("The hash-algorithm is applicable only to " + "RSA-PKCS-OAEP mechanism"); + + build_params(¶ms, &mech, &iv, &aad); + if (opt_input == NULL) - fd = 0; - else if ((fd = open(opt_input, O_RDONLY|O_BINARY)) < 0) + fd_in = 0; + else if ((fd_in = open(opt_input, O_RDONLY | O_BINARY)) < 0) util_fatal("Cannot open %s: %m", opt_input); - r = read(fd, in_buffer, sizeof(in_buffer)); - if (r < 0) + if (opt_output == NULL) { + fd_out = 1; + } else { + fd_out = open(opt_output, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, S_IRUSR | S_IWUSR); + if (fd_out < 0) + util_fatal("failed to open %s: %m", opt_output); + } + + sz = read(fd_in, in_buffer, sizeof(in_buffer)); + in_len = sz; + + if (sz < 0) util_fatal("Cannot read from %s: %m", opt_input); - in_len = r; - rv = p11->C_DecryptInit(session, &mech, key); - if (rv != CKR_OK) - p11_fatal("C_DecryptInit", rv); - if (getALWAYS_AUTHENTICATE(session, key)) - login(session,CKU_CONTEXT_SPECIFIC); + rv = CKR_CANCEL; + if ((size_t)sz < sizeof(in_buffer)) { + out_len = sizeof(out_buffer); + rv = p11->C_DecryptInit(session, &mech, key); + if (rv != CKR_OK) + p11_fatal("C_DecryptInit", rv); + if ((getCLASS(session, key) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, key)) + login(session, CKU_CONTEXT_SPECIFIC); + rv = p11->C_Decrypt(session, in_buffer, in_len, out_buffer, &out_len); + } + if (rv != CKR_OK) { + rv = p11->C_DecryptInit(session, &mech, key); + if (rv != CKR_OK) + p11_fatal("C_DecryptInit", rv); + if ((getCLASS(session, key) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, key)) + login(session, CKU_CONTEXT_SPECIFIC); + do { + out_len = sizeof(out_buffer); + rv = p11->C_DecryptUpdate(session, in_buffer, in_len, out_buffer, &out_len); + if (rv != CKR_OK) + p11_fatal("C_DecryptUpdate", rv); + sz = write(fd_out, out_buffer, out_len); + if ((size_t)sz != out_len) + util_fatal("Cannot write to %s: %m", opt_output); + sz = read(fd_in, in_buffer, sizeof(in_buffer)); + in_len = sz; + } while (sz > 0); + out_len = sizeof(out_buffer); + rv = p11->C_DecryptFinal(session, out_buffer, &out_len); + if (rv != CKR_OK) + p11_fatal("C_DecryptFinal", rv); + } + if (out_len) { + sz = write(fd_out, out_buffer, out_len); + if ((size_t)sz != out_len) + util_fatal("Cannot write to %s: %m", opt_output); + } + if (fd_in != 0) + close(fd_in); + if (fd_out != 1) + close(fd_out); + + free(iv); + free(aad); +} + +static void encrypt_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE key) +{ + unsigned char in_buffer[1024], out_buffer[1024]; + CK_MECHANISM mech; + CK_RV rv; + params_t params = {0}; + CK_BYTE_PTR iv = NULL; + CK_BYTE_PTR aad = NULL; + CK_ULONG in_len, out_len; + int fd_in, fd_out; + ssize_t sz; + + if (!opt_mechanism_used) + if (!find_mechanism(slot, CKF_ENCRYPT | opt_allow_sw, NULL, 0, &opt_mechanism)) + util_fatal("Encrypt mechanism not supported"); + + fprintf(stderr, "Using encrypt algorithm %s\n", p11_mechanism_to_name(opt_mechanism)); + memset(&mech, 0, sizeof(mech)); + mech.mechanism = opt_mechanism; + + if (opt_hash_alg != 0 && opt_mechanism != CKM_RSA_PKCS_OAEP) + util_fatal("The hash-algorithm is applicable only to " + "RSA-PKCS-OAEP mechanism"); + + build_params(¶ms, &mech, &iv, &aad); + + if (opt_input == NULL) + fd_in = 0; + else if ((fd_in = open(opt_input, O_RDONLY | O_BINARY)) < 0) + util_fatal("Cannot open %s: %m", opt_input); + + if (opt_output == NULL) { + fd_out = 1; + } else { + fd_out = open(opt_output, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, S_IRUSR | S_IWUSR); + if (fd_out < 0) + util_fatal("failed to open %s: %m", opt_output); + } - out_len = sizeof(out_buffer); - rv = p11->C_Decrypt(session, in_buffer, in_len, out_buffer, &out_len); - if (rv != CKR_OK) - p11_fatal("C_Decrypt", rv); + sz = read(fd_in, in_buffer, sizeof(in_buffer)); + in_len = sz; - if (fd != 0) - close(fd); + if (sz < 0) + util_fatal("Cannot read from %s: %m", opt_input); - if (opt_output == NULL) { - fd = 1; + rv = CKR_CANCEL; + if ((size_t)sz < sizeof(in_buffer)) { + out_len = sizeof(out_buffer); + rv = p11->C_EncryptInit(session, &mech, key); + if (rv != CKR_OK) + p11_fatal("C_EncryptInit", rv); + if ((getCLASS(session, key) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, key)) + login(session, CKU_CONTEXT_SPECIFIC); + out_len = sizeof(out_buffer); + rv = p11->C_Encrypt(session, in_buffer, in_len, out_buffer, &out_len); } - else { - fd = open(opt_output, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, S_IRUSR|S_IWUSR); - if (fd < 0) - util_fatal("failed to open %s: %m", opt_output); + if (rv != CKR_OK) { + rv = p11->C_EncryptInit(session, &mech, key); + if (rv != CKR_OK) + p11_fatal("C_EncryptInit", rv); + if ((getCLASS(session, key) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, key)) + login(session, CKU_CONTEXT_SPECIFIC); + do { + out_len = sizeof(out_buffer); + rv = p11->C_EncryptUpdate(session, in_buffer, in_len, out_buffer, &out_len); + if (rv != CKR_OK) + p11_fatal("C_EncryptUpdate", rv); + sz = write(fd_out, out_buffer, out_len); + if ((size_t)sz != out_len) + util_fatal("Cannot write to %s: %m", opt_output); + sz = read(fd_in, in_buffer, sizeof(in_buffer)); + in_len = sz; + } while (sz > 0); + out_len = sizeof(out_buffer); + rv = p11->C_EncryptFinal(session, out_buffer, &out_len); + if (rv != CKR_OK) + p11_fatal("C_EncryptFinal", rv); + } + if (out_len) { + sz = write(fd_out, out_buffer, out_len); + if ((size_t)sz != out_len) + util_fatal("Cannot write to %s: %m", opt_output); } + if (fd_in != 0) + close(fd_in); + if (fd_out != 1) + close(fd_out); - r = write(fd, out_buffer, out_len); - if (r < 0) - util_fatal("Failed to write to %s: %m", opt_output); - if (fd != 1) - close(fd); + free(iv); + free(aad); } @@ -1747,7 +3398,8 @@ static void hash_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session) CK_MECHANISM mech; CK_RV rv; CK_ULONG hash_len; - int fd, r; + int fd; + ssize_t sz; if (!opt_mechanism_used) if (!find_mechanism(slot, CKF_DIGEST, NULL, 0, &opt_mechanism)) @@ -1766,8 +3418,8 @@ static void hash_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session) else if ((fd = open(opt_input, O_RDONLY|O_BINARY)) < 0) util_fatal("Cannot open %s: %m", opt_input); - while ((r = read(fd, buffer, sizeof(buffer))) > 0) { - rv = p11->C_DigestUpdate(session, buffer, r); + while ((sz = read(fd, buffer, sizeof(buffer))) > 0) { + rv = p11->C_DigestUpdate(session, buffer, sz); if (rv != CKR_OK) p11_fatal("C_DigestUpdate", rv); } @@ -1785,8 +3437,8 @@ static void hash_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session) else if ((fd = open(opt_output, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, S_IRUSR|S_IWUSR)) < 0) util_fatal("failed to open %s: %m", opt_output); - r = write(fd, buffer, hash_len); - if (r < 0) + sz = write(fd, buffer, hash_len); + if (sz < 0) util_fatal("Failed to write to %s: %m", opt_output); if (fd != 1) close(fd); @@ -1794,6 +3446,7 @@ static void hash_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session) #define FILL_ATTR(attr, typ, val, len) {(attr).type=(typ); (attr).pValue=(val); (attr).ulValueLen=len;} +/* Generate asymmetric key pair */ static int gen_keypair(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *hPublicKey, CK_OBJECT_HANDLE *hPrivateKey, const char *type) { @@ -1815,9 +3468,12 @@ static int gen_keypair(CK_SLOT_ID slot, CK_SESSION_HANDLE session, {CKA_PRIVATE, &_true, sizeof(_true)}, {CKA_SENSITIVE, &_true, sizeof(_true)}, }; + unsigned long int gost_key_type = -1; + CK_ML_DSA_PARAMETER_SET_TYPE ml_dsa_parameter_set = 0; + CK_ML_KEM_PARAMETER_SET_TYPE ml_kem_parameter_set = 0; + CK_SLH_DSA_PARAMETER_SET_TYPE slh_dsa_parameter_set = 0; int n_privkey_attr = 4; - unsigned char *ecparams = NULL; - size_t ecparams_size; + CK_ULONG key_type = CKK_RSA; CK_RV rv; if (type != NULL) { @@ -1831,11 +3487,10 @@ static int gen_keypair(CK_SLOT_ID slot, CK_SESSION_HANDLE session, if (!find_mechanism(slot, CKF_GENERATE_KEY_PAIR, mtypes, mtypes_num, &opt_mechanism)) util_fatal("Generate RSA mechanism not supported"); - if (size == NULL) - util_fatal("Unknown key type %s", type); key_length = (unsigned long)atol(size); - if (key_length != 0) - modulusBits = key_length; + if (key_length == 0) + util_fatal("Unknown key pair type %s, expecting RSA:", type); + modulusBits = key_length; FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_MODULUS_BITS, &modulusBits, sizeof(modulusBits)); n_pubkey_attr++; @@ -1856,36 +3511,62 @@ static int gen_keypair(CK_SLOT_ID slot, CK_SESSION_HANDLE session, n_privkey_attr++; } - FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_WRAP, &_true, sizeof(_true)); + if (opt_key_usage_wrap) { + FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_WRAP, &_true, sizeof(_true)); + n_pubkey_attr++; + FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_UNWRAP, &_true, sizeof(_true)); + n_privkey_attr++; + } + FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); n_pubkey_attr++; - FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_UNWRAP, &_true, sizeof(_true)); + FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); n_privkey_attr++; } - else if (!strncmp(type, "EC:", 3)) { + else if (strncmp(type, "EC:", strlen("EC:")) == 0 || strncmp(type, "ec:", strlen("ec:")) == 0) { CK_MECHANISM_TYPE mtypes[] = {CKM_EC_KEY_PAIR_GEN}; size_t mtypes_num = sizeof(mtypes)/sizeof(mtypes[0]); int ii; - if (!opt_mechanism_used) - if (!find_mechanism(slot, CKF_GENERATE_KEY_PAIR, mtypes, mtypes_num, &opt_mechanism)) - util_fatal("Generate EC key mechanism not supported\n"); + key_type = CKK_EC; for (ii=0; ec_curve_infos[ii].name; ii++) { - if (!strcmp(ec_curve_infos[ii].name, type + 3)) + if (!strcasecmp(ec_curve_infos[ii].name, type + 3)) break; if (!strcmp(ec_curve_infos[ii].oid, type + 3)) break; } - if (!ec_curve_infos[ii].name) - util_fatal("Unknown EC key params '%s'", type + 3); + if (!ec_curve_infos[ii].name) { + fprintf(stderr, "EC key parameters may be specified by their canonic name or object identifier. Possible values are:\n"); + for (ii = 0; ec_curve_infos[ii].name; ii++) { + fprintf(stderr, "%s (%s)\n", ec_curve_infos[ii].name, ec_curve_infos[ii].oid); + } + util_fatal("Unknown EC key parameter '%s'", type + 3); + } - ecparams_size = strlen(ec_curve_infos[ii].oid_encoded) / 2; - ecparams = malloc(ecparams_size); - if (!ecparams) - util_fatal("Allocation error", 0); - if (!hex_to_bin(ec_curve_infos[ii].oid_encoded, ecparams, &ecparams_size)) { - fprintf(stderr, "Cannot convert \"%s\"\n", ec_curve_infos[ii].oid_encoded); - util_print_usage_and_die(app_name, options, option_help, NULL); + switch (ec_curve_infos[ii].mechanism) { + case CKM_EC_EDWARDS_KEY_PAIR_GEN: + /* The Edwards key can not be used for derivation */ + opt_key_usage_derive = 0; + key_type = CKK_EC_EDWARDS; + /* This replaces the above default mechanism */ + if (!opt_mechanism_used) { + mtypes[0] = ec_curve_infos[ii].mechanism; + } + break; + case CKM_EC_MONTGOMERY_KEY_PAIR_GEN: + key_type = CKK_EC_MONTGOMERY; + /* This replaces the above default mechanism */ + if (!opt_mechanism_used) { + mtypes[0] = ec_curve_infos[ii].mechanism; + } + break; + } + + if (!opt_mechanism_used) { + if (!find_mechanism(slot, CKF_GENERATE_KEY_PAIR, mtypes, mtypes_num, + &opt_mechanism)) { + util_fatal("Generate key mechanism %s not supported by token", p11_mechanism_to_name(mtypes[0])); + } } if (opt_key_usage_default || opt_key_usage_sign) { @@ -1902,52 +3583,110 @@ static int gen_keypair(CK_SLOT_ID slot, CK_SESSION_HANDLE session, n_privkey_attr++; } - FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_EC_PARAMS, ecparams, ecparams_size); + if (opt_key_usage_decrypt) { + FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_ENCRYPT, &_true, sizeof(_true)); + n_pubkey_attr++; + FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_DECRYPT, &_true, sizeof(_true)); + n_privkey_attr++; + } + + FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_EC_PARAMS, ec_curve_infos[ii].ec_params, ec_curve_infos[ii].ec_params_size); n_pubkey_attr++; + FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); + n_pubkey_attr++; + FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); + n_privkey_attr++; } - else if (strncmp(type, "GOSTR3410:", strlen("GOSTR3410:")) == 0 || strncmp(type, "gostr3410:", strlen("gostr3410:")) == 0) { - CK_BYTE key_paramset_encoded_oid[9]; - CK_BYTE hash_paramset_encoded_oid[9]; - const CK_BYTE GOST_PARAMSET_A_OID[] = {0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01}; - const CK_BYTE GOST_PARAMSET_B_OID[] = {0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x02}; - const CK_BYTE GOST_PARAMSET_C_OID[] = {0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x03}; - const CK_BYTE GOST_HASH_PARAMSET_OID[] = {0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01}; - unsigned long int gost_key_type = CKK_GOSTR3410; - CK_MECHANISM_TYPE mtypes[] = {CKM_GOSTR3410_KEY_PAIR_GEN}; + else if (strncmp(type, "GOSTR3410", strlen("GOSTR3410")) == 0 || strncmp(type, "gostr3410", strlen("gostr3410")) == 0) { + const struct sc_aid GOST2001_PARAMSET_A_OID = { { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01 }, 9 }; + const struct sc_aid GOST2001_PARAMSET_B_OID = { { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x02 }, 9 }; + const struct sc_aid GOST2001_PARAMSET_C_OID = { { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x03 }, 9 }; + const struct sc_aid GOST2012_256_PARAMSET_A_OID = { { 0x06, 0x09, 0x2A, 0x85, 0x03, 0x07, 0x01, 0x02, 0x01, 0x01, 0x01 }, 11 }; + const struct sc_aid GOST2012_512_PARAMSET_A_OID = { { 0x06, 0x09, 0x2A, 0x85, 0x03, 0x07, 0x01, 0x02, 0x01, 0x02, 0x01 }, 11 }; + const struct sc_aid GOST2012_512_PARAMSET_B_OID = { { 0x06, 0x09, 0x2A, 0x85, 0x03, 0x07, 0x01, 0x02, 0x01, 0x02, 0x02 }, 11 }; + const struct sc_aid GOST2012_512_PARAMSET_C_OID = { { 0x06, 0x09, 0x2A, 0x85, 0x03, 0x07, 0x01, 0x02, 0x01, 0x02, 0x03 }, 11 }; + struct sc_aid key_paramset_encoded_oid; + struct sc_aid hash_paramset_encoded_oid; + CK_MECHANISM_TYPE mtypes[] = {-1}; size_t mtypes_num = sizeof(mtypes)/sizeof(mtypes[0]); - const char *p_param_set = type + strlen("GOSTR3410:"); + const char *p_param_set = type + strlen("GOSTR3410"); - if (!opt_mechanism_used) { - if (!find_mechanism(slot, CKF_GENERATE_KEY_PAIR, mtypes, mtypes_num, &opt_mechanism)) - util_fatal("Generate GOSTR3410 mechanism not supported"); + if (!strcmp(":A", p_param_set) || !strcmp("-2001:A", p_param_set)) { + gost_key_type = CKK_GOSTR3410; + mtypes[0] = CKM_GOSTR3410_KEY_PAIR_GEN; + key_paramset_encoded_oid = GOST2001_PARAMSET_A_OID; + hash_paramset_encoded_oid = GOST_HASH2001_PARAMSET_OID; } - - if (p_param_set == NULL) - util_fatal("Unknown key type %s", type); - - if (!strcmp("A", p_param_set)) { - memcpy(key_paramset_encoded_oid, GOST_PARAMSET_A_OID, sizeof(GOST_PARAMSET_A_OID)); - memcpy(hash_paramset_encoded_oid, GOST_HASH_PARAMSET_OID, sizeof(GOST_HASH_PARAMSET_OID)); + else if (!strcmp(":B", p_param_set) || !strcmp("-2001:B", p_param_set)) { + gost_key_type = CKK_GOSTR3410; + mtypes[0] = CKM_GOSTR3410_KEY_PAIR_GEN; + key_paramset_encoded_oid = GOST2001_PARAMSET_B_OID; + hash_paramset_encoded_oid = GOST_HASH2001_PARAMSET_OID; + } + else if (!strcmp(":C", p_param_set) || !strcmp("-2001:C", p_param_set)) { + gost_key_type = CKK_GOSTR3410; + mtypes[0] = CKM_GOSTR3410_KEY_PAIR_GEN; + key_paramset_encoded_oid = GOST2001_PARAMSET_C_OID; + hash_paramset_encoded_oid = GOST_HASH2001_PARAMSET_OID; + } else if (!strcmp("-2012-256:A", p_param_set)) { + gost_key_type = CKK_GOSTR3410; + mtypes[0] = CKM_GOSTR3410_KEY_PAIR_GEN; + key_paramset_encoded_oid = GOST2012_256_PARAMSET_A_OID; + hash_paramset_encoded_oid = GOST_HASH2012_256_PARAMSET_OID; + } + else if (!strcmp("-2012-256:B", p_param_set)) { + gost_key_type = CKK_GOSTR3410; + mtypes[0] = CKM_GOSTR3410_KEY_PAIR_GEN; + key_paramset_encoded_oid = GOST2001_PARAMSET_A_OID; + hash_paramset_encoded_oid = GOST_HASH2012_256_PARAMSET_OID; } - else if (!strcmp("B", p_param_set)) { - memcpy(key_paramset_encoded_oid, GOST_PARAMSET_B_OID, sizeof(GOST_PARAMSET_B_OID)); - memcpy(hash_paramset_encoded_oid, GOST_HASH_PARAMSET_OID, sizeof(GOST_HASH_PARAMSET_OID)); + else if (!strcmp("-2012-256:C", p_param_set)) { + gost_key_type = CKK_GOSTR3410; + mtypes[0] = CKM_GOSTR3410_KEY_PAIR_GEN; + key_paramset_encoded_oid = GOST2001_PARAMSET_B_OID; + hash_paramset_encoded_oid = GOST_HASH2012_256_PARAMSET_OID; } - else if (!strcmp("C", p_param_set)) { - memcpy(key_paramset_encoded_oid, GOST_PARAMSET_C_OID, sizeof(GOST_PARAMSET_C_OID)); - memcpy(hash_paramset_encoded_oid, GOST_HASH_PARAMSET_OID, sizeof(GOST_HASH_PARAMSET_OID)); + else if (!strcmp("-2012-256:D", p_param_set)) { + gost_key_type = CKK_GOSTR3410; + mtypes[0] = CKM_GOSTR3410_KEY_PAIR_GEN; + key_paramset_encoded_oid = GOST2001_PARAMSET_C_OID; + hash_paramset_encoded_oid = GOST_HASH2012_256_PARAMSET_OID; + } + else if (!strcmp("-2012-512:A", p_param_set)) { + gost_key_type = CKK_GOSTR3410_512; + mtypes[0] = CKM_GOSTR3410_512_KEY_PAIR_GEN; + key_paramset_encoded_oid = GOST2012_512_PARAMSET_A_OID; + hash_paramset_encoded_oid = GOST_HASH2012_512_PARAMSET_OID; + } + else if (!strcmp("-2012-512:B", p_param_set)) { + gost_key_type = CKK_GOSTR3410_512; + mtypes[0] = CKM_GOSTR3410_512_KEY_PAIR_GEN; + key_paramset_encoded_oid = GOST2012_512_PARAMSET_B_OID; + hash_paramset_encoded_oid = GOST_HASH2012_512_PARAMSET_OID; + } + else if (!strcmp("-2012-512:C", p_param_set)) { + gost_key_type = CKK_GOSTR3410_512; + mtypes[0] = CKM_GOSTR3410_512_KEY_PAIR_GEN; + key_paramset_encoded_oid = GOST2012_512_PARAMSET_C_OID; + hash_paramset_encoded_oid = GOST_HASH2012_512_PARAMSET_OID; } else - util_fatal("Unknown key type %s, valid key types for mechanism GOSTR3410 are GOSTR3410:A, GOSTR3410:B, GOSTR3410:C", type); + util_fatal("Unknown key pair type %s, valid key types for mechanism GOSTR3410 are GOSTR3410-2001:{A,B,C}," + " GOSTR3410-2012-256:{A,B,C,D}, GOSTR3410-2012-512:{A,B,C}", type); + + if (!opt_mechanism_used) { + if (!find_mechanism(slot, CKF_GENERATE_KEY_PAIR, mtypes, mtypes_num, &opt_mechanism)) + util_fatal("Generate GOSTR3410%s mechanism not supported", gost_key_type == CKK_GOSTR3410_512 ? "-2012-512" : ""); + } - FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_GOSTR3410_PARAMS, key_paramset_encoded_oid, sizeof(key_paramset_encoded_oid)); + FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_GOSTR3410_PARAMS, key_paramset_encoded_oid.value, key_paramset_encoded_oid.len); n_pubkey_attr++; - FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_GOSTR3410_PARAMS, key_paramset_encoded_oid, sizeof(key_paramset_encoded_oid)); + FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_GOSTR3410_PARAMS, key_paramset_encoded_oid.value, key_paramset_encoded_oid.len); n_privkey_attr++; - FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_GOSTR3411_PARAMS, hash_paramset_encoded_oid, sizeof(hash_paramset_encoded_oid)); + FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_GOSTR3411_PARAMS, hash_paramset_encoded_oid.value, hash_paramset_encoded_oid.len); n_pubkey_attr++; - FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_GOSTR3411_PARAMS, hash_paramset_encoded_oid, sizeof(hash_paramset_encoded_oid)); + FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_GOSTR3411_PARAMS, hash_paramset_encoded_oid.value, hash_paramset_encoded_oid.len); n_privkey_attr++; FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_KEY_TYPE, &gost_key_type, sizeof(gost_key_type)); @@ -1967,9 +3706,119 @@ static int gen_keypair(CK_SLOT_ID slot, CK_SESSION_HANDLE session, FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_DERIVE, &_true, sizeof(_true)); n_privkey_attr++; } - } - else { - util_fatal("Unknown key type %s", type); + } else if (strncasecmp(type, "ML-DSA-", strlen("ML-DSA-")) == 0) { + CK_MECHANISM_TYPE mtypes[] = {CKM_ML_DSA_KEY_PAIR_GEN}; + size_t mtypes_num = sizeof(mtypes) / sizeof(mtypes[0]); + + key_type = CKK_ML_DSA; + + if (strcmp(type, "ML-DSA-44") == 0) { + ml_dsa_parameter_set = CKP_ML_DSA_44; + } else if (strcmp(type, "ML-DSA-65") == 0) { + ml_dsa_parameter_set = CKP_ML_DSA_65; + } else if (strcmp(type, "ML-DSA-87") == 0) { + ml_dsa_parameter_set = CKP_ML_DSA_87; + } else { + util_fatal("Invalid ML-DSA key type '%s'", type); + } + + if (!opt_mechanism_used) + if (!find_mechanism(slot, CKF_GENERATE_KEY_PAIR, mtypes, mtypes_num, &opt_mechanism)) + util_fatal("Generate ML-DSA mechanism not supported"); + + FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_PARAMETER_SET, + &ml_dsa_parameter_set, sizeof(CK_ML_DSA_PARAMETER_SET_TYPE)); + n_pubkey_attr++; + + if (opt_key_usage_default || opt_key_usage_sign) { + FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_VERIFY, &_true, sizeof(_true)); + n_pubkey_attr++; + FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_SIGN, &_true, sizeof(_true)); + n_privkey_attr++; + } + + } else if (strncasecmp(type, "ML-KEM-", strlen("ML-KEM-")) == 0) { + CK_MECHANISM_TYPE mtypes[] = {CKM_ML_KEM_KEY_PAIR_GEN}; + size_t mtypes_num = sizeof(mtypes) / sizeof(mtypes[0]); + + key_type = CKK_ML_KEM; + + if (strcmp(type, "ML-KEM-512") == 0) { + ml_kem_parameter_set = CKP_ML_KEM_512; + } else if (strcmp(type, "ML-KEM-768") == 0) { + ml_kem_parameter_set = CKP_ML_KEM_768; + } else if (strcmp(type, "ML-KEM-1024") == 0) { + ml_kem_parameter_set = CKP_ML_KEM_1024; + } else { + util_fatal("Invalid ML-KEM key type '%s'", type); + } + + if (!opt_mechanism_used) + if (!find_mechanism(slot, CKF_GENERATE_KEY_PAIR, mtypes, mtypes_num, &opt_mechanism)) + util_fatal("Generate ML-KEM mechanism not supported"); + + FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_PARAMETER_SET, + &ml_kem_parameter_set, sizeof(CK_ML_KEM_PARAMETER_SET_TYPE)); + n_pubkey_attr++; + + if (opt_key_usage_default || opt_key_usage_encapsulate) { + FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_ENCAPSULATE, &_true, sizeof(_true)); + n_pubkey_attr++; + FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_DECAPSULATE, &_true, sizeof(_true)); + n_privkey_attr++; + } + + } else if (strncasecmp(type, "SLH-DSA-", strlen("SLH-DSA-")) == 0) { + CK_MECHANISM_TYPE mtypes[] = {CKM_SLH_DSA_KEY_PAIR_GEN}; + size_t mtypes_num = sizeof(mtypes) / sizeof(mtypes[0]); + + key_type = CKK_SLH_DSA; + + if (strcmp(type, "SLH-DSA-SHA2-128S") == 0) { + slh_dsa_parameter_set = CKP_SLH_DSA_SHA2_128S; + } else if (strcmp(type, "SLH-DSA-SHAKE-128S") == 0) { + slh_dsa_parameter_set = CKP_SLH_DSA_SHAKE_128S; + } else if (strcmp(type, "SLH-DSA-SHA2-128F") == 0) { + slh_dsa_parameter_set = CKP_SLH_DSA_SHA2_128F; + } else if (strcmp(type, "SLH-DSA-SHAKE-128F") == 0) { + slh_dsa_parameter_set = CKP_SLH_DSA_SHAKE_128F; + } else if (strcmp(type, "SLH-DSA-SHA2-192S") == 0) { + slh_dsa_parameter_set = CKP_SLH_DSA_SHA2_192S; + } else if (strcmp(type, "SLH-DSA-SHAKE-192S") == 0) { + slh_dsa_parameter_set = CKP_SLH_DSA_SHAKE_192S; + } else if (strcmp(type, "SLH-DSA-SHA2-192F") == 0) { + slh_dsa_parameter_set = CKP_SLH_DSA_SHA2_192F; + } else if (strcmp(type, "SLH-DSA-SHAKE-192F") == 0) { + slh_dsa_parameter_set = CKP_SLH_DSA_SHAKE_192F; + } else if (strcmp(type, "SLH-DSA-SHA2-256S") == 0) { + slh_dsa_parameter_set = CKP_SLH_DSA_SHA2_256S; + } else if (strcmp(type, "SLH-DSA-SHAKE-256S") == 0) { + slh_dsa_parameter_set = CKP_SLH_DSA_SHAKE_256S; + } else if (strcmp(type, "SLH-DSA-SHA2-256F") == 0) { + slh_dsa_parameter_set = CKP_SLH_DSA_SHA2_256F; + } else if (strcmp(type, "SLH-DSA-SHAKE-256F") == 0) { + slh_dsa_parameter_set = CKP_SLH_DSA_SHAKE_256F; + } else { + util_fatal("Invalid SLH-DSA key type '%s'", type); + } + + if (!opt_mechanism_used) + if (!find_mechanism(slot, CKF_GENERATE_KEY_PAIR, mtypes, mtypes_num, &opt_mechanism)) + util_fatal("Generate SLH-DSA mechanism not supported"); + + FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_PARAMETER_SET, + &slh_dsa_parameter_set, sizeof(CK_SLH_DSA_PARAMETER_SET_TYPE)); + n_pubkey_attr++; + + if (opt_key_usage_default || opt_key_usage_sign) { + FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_VERIFY, &_true, sizeof(_true)); + n_pubkey_attr++; + FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_SIGN, &_true, sizeof(_true)); + n_privkey_attr++; + } + + } else { + util_fatal("Unknown key pair type %s", type); } mechanism.mechanism = opt_mechanism; @@ -2004,6 +3853,29 @@ static int gen_keypair(CK_SLOT_ID slot, CK_SESSION_HANDLE session, n_pubkey_attr++; } + if (opt_always_auth != 0) { + FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_ALWAYS_AUTHENTICATE, + &_true, sizeof(_true)); + n_privkey_attr++; + } + + if (opt_is_extractable != 0) { + FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_EXTRACTABLE, + &_true, sizeof(_true)); + n_privkey_attr++; + } + + if (opt_allowed_mechanisms_len > 0) { + FILL_ATTR(publicKeyTemplate[n_pubkey_attr], + CKA_ALLOWED_MECHANISMS, opt_allowed_mechanisms, + sizeof(CK_MECHANISM_TYPE) * opt_allowed_mechanisms_len); + n_pubkey_attr++; + FILL_ATTR(privateKeyTemplate[n_privkey_attr], + CKA_ALLOWED_MECHANISMS, opt_allowed_mechanisms, + sizeof(CK_MECHANISM_TYPE) * opt_allowed_mechanisms_len); + n_privkey_attr++; + } + rv = p11->C_GenerateKeyPair(session, &mechanism, publicKeyTemplate, n_pubkey_attr, privateKeyTemplate, n_privkey_attr, @@ -2011,9 +3883,6 @@ static int gen_keypair(CK_SLOT_ID slot, CK_SESSION_HANDLE session, if (rv != CKR_OK) p11_fatal("C_GenerateKeyPair", rv); - if (ecparams) - free(ecparams); - printf("Key pair generated:\n"); show_object(session, *hPrivateKey); show_object(session, *hPublicKey); @@ -2021,6 +3890,7 @@ static int gen_keypair(CK_SLOT_ID slot, CK_SESSION_HANDLE session, return 1; } +/* generate symmetric key */ static int gen_key(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *hSecretKey, const char *type, char *label) @@ -2028,6 +3898,7 @@ gen_key(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *hSecretKey CK_MECHANISM mechanism = {CKM_AES_KEY_GEN, NULL_PTR, 0}; CK_OBJECT_CLASS secret_key_class = CKO_SECRET_KEY; CK_BBOOL _true = TRUE; + CK_BBOOL _false = FALSE; CK_KEY_TYPE key_type = CKK_AES; CK_ULONG key_length; CK_ATTRIBUTE keyTemplate[20] = { @@ -2038,9 +3909,9 @@ gen_key(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *hSecretKey CK_RV rv; if (type != NULL) { - if (strncmp(type, "AES:", strlen("AES:")) == 0 || strncmp(type, "aes:", strlen("aes:")) == 0) { + if (strncasecmp(type, "AES:", strlen("AES:")) == 0) { CK_MECHANISM_TYPE mtypes[] = {CKM_AES_KEY_GEN}; - size_t mtypes_num = sizeof(mtypes)/sizeof(mtypes[0]); + size_t mtypes_num = sizeof(mtypes) / sizeof(mtypes[0]); const char *size = type + strlen("AES:"); key_type = CKK_AES; @@ -2049,18 +3920,15 @@ gen_key(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *hSecretKey if (!find_mechanism(slot, CKF_GENERATE, mtypes, mtypes_num, &opt_mechanism)) util_fatal("Generate Key mechanism not supported\n"); - if (size == NULL) - util_fatal("Unknown key type %s", type); key_length = (unsigned long)atol(size); if (key_length == 0) - key_length = 32; + util_fatal("Unknown key type %s, expecting AES:", type); FILL_ATTR(keyTemplate[n_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); n_attr++; - } - else if (strncmp(type, "DES:", strlen("DES:")) == 0 || strncmp(type, "des:", strlen("des:")) == 0) { + } else if (strncasecmp(type, "DES:", strlen("DES:")) == 0) { CK_MECHANISM_TYPE mtypes[] = {CKM_DES_KEY_GEN}; - size_t mtypes_num = sizeof(mtypes)/sizeof(mtypes[0]); + size_t mtypes_num = sizeof(mtypes) / sizeof(mtypes[0]); const char *size = type + strlen("DES:"); key_type = CKK_DES; @@ -2069,18 +3937,15 @@ gen_key(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *hSecretKey if (!find_mechanism(slot, CKF_GENERATE, mtypes, mtypes_num, &opt_mechanism)) util_fatal("Generate Key mechanism not supported\n"); - if (size == NULL) - util_fatal("Unknown key type %s", type); key_length = (unsigned long)atol(size); if (key_length == 0) - key_length = 8; + util_fatal("Unknown key type %s, expecting DES:", type); FILL_ATTR(keyTemplate[n_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); n_attr++; - } - else if (strncmp(type, "DES3:", strlen("DES3:")) == 0 || strncmp(type, "des3:", strlen("des3:")) == 0) { + } else if (strncasecmp(type, "DES3:", strlen("DES3:")) == 0) { CK_MECHANISM_TYPE mtypes[] = {CKM_DES3_KEY_GEN}; - size_t mtypes_num = sizeof(mtypes)/sizeof(mtypes[0]); + size_t mtypes_num = sizeof(mtypes) / sizeof(mtypes[0]); const char *size = type + strlen("DES3:"); key_type = CKK_DES3; @@ -2089,68 +3954,404 @@ gen_key(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *hSecretKey if (!find_mechanism(slot, CKF_GENERATE, mtypes, mtypes_num, &opt_mechanism)) util_fatal("Generate Key mechanism not supported\n"); - if (size == NULL) - util_fatal("Unknown key type %s", type); key_length = (unsigned long)atol(size); if (key_length == 0) - key_length = 16; + util_fatal("Unknown key type %s, expecting DES3:", type); + + FILL_ATTR(keyTemplate[n_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); + n_attr++; + } else if (strncasecmp(type, "GENERIC:", strlen("GENERIC:")) == 0) { + CK_MECHANISM_TYPE mtypes[] = {CKM_GENERIC_SECRET_KEY_GEN}; + size_t mtypes_num = sizeof(mtypes) / sizeof(mtypes[0]); + const char *size = type + strlen("GENERIC:"); + + key_type = CKK_GENERIC_SECRET; + + if (!opt_mechanism_used) + if (!find_mechanism(slot, CKF_GENERATE, mtypes, mtypes_num, &opt_mechanism)) + util_fatal("Generate Key mechanism not supported\n"); + + key_length = (unsigned long)atol(size); + if (key_length == 0) + util_fatal("Unknown key type %s, expecting GENERIC:", type); + + FILL_ATTR(keyTemplate[n_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); + n_attr++; + } else if (strncasecmp(type, "HKDF:", strlen("HKDF:")) == 0) { + CK_MECHANISM_TYPE mtypes[] = {CKM_HKDF_KEY_GEN}; + size_t mtypes_num = sizeof(mtypes) / sizeof(mtypes[0]); + const char *size = type + strlen("HKDF:"); + + key_type = CKK_HKDF; + + if (!opt_mechanism_used) + if (!find_mechanism(slot, CKF_GENERATE, mtypes, mtypes_num, &opt_mechanism)) + util_fatal("Generate Key mechanism not supported\n"); + + key_length = (unsigned long)atol(size); + if (key_length == 0) + util_fatal("Unknown key type %s, expecting HKDF:", type); + + FILL_ATTR(keyTemplate[n_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); + n_attr++; + } else if (strncasecmp(type, "CHACHA20", strlen("CHACHA20")) == 0) { + CK_MECHANISM_TYPE mtypes[] = {CKM_CHACHA20_KEY_GEN}; + size_t mtypes_num = sizeof(mtypes) / sizeof(mtypes[0]); + + key_type = CKK_CHACHA20; + + if (!opt_mechanism_used) + if (!find_mechanism(slot, CKF_GENERATE, mtypes, mtypes_num, &opt_mechanism)) + util_fatal("Generate Key mechanism not supported\n"); + + key_length = 32U; + + FILL_ATTR(keyTemplate[n_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); + n_attr++; + } else if (strncasecmp(type, "POLY1305", strlen("POLY1305")) == 0) { + CK_MECHANISM_TYPE mtypes[] = {CKM_POLY1305_KEY_GEN}; + size_t mtypes_num = sizeof(mtypes) / sizeof(mtypes[0]); + + key_type = CKK_POLY1305; + + if (!opt_mechanism_used) + if (!find_mechanism(slot, CKF_GENERATE, mtypes, mtypes_num, &opt_mechanism)) + util_fatal("Generate Key mechanism not supported\n"); + + key_length = 32U; FILL_ATTR(keyTemplate[n_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); n_attr++; + } else { + util_fatal("Unknown key type %s", type); + } + + if (opt_is_sensitive != 0) { + FILL_ATTR(keyTemplate[n_attr], CKA_SENSITIVE, &_true, sizeof(_true)); + n_attr++; } else { - util_fatal("Unknown key type %s", type); + FILL_ATTR(keyTemplate[n_attr], CKA_SENSITIVE, &_false, sizeof(_false)); + n_attr++; + } + + if (opt_is_extractable != 0) { + FILL_ATTR(keyTemplate[n_attr], CKA_EXTRACTABLE, &_true, sizeof(_true)); + n_attr++; + } else { + FILL_ATTR(keyTemplate[n_attr], CKA_EXTRACTABLE, &_false, sizeof(_false)); + n_attr++; + } + + if (opt_is_private != 0) { + FILL_ATTR(keyTemplate[n_attr], CKA_PRIVATE, &_true, sizeof(_true)); + n_attr++; + } else { + FILL_ATTR(keyTemplate[n_attr], CKA_PRIVATE, &_false, sizeof(_false)); + n_attr++; + } + + if (opt_key_usage_default || opt_key_usage_decrypt) { + FILL_ATTR(keyTemplate[n_attr], CKA_ENCRYPT, &_true, sizeof(_true)); + n_attr++; + FILL_ATTR(keyTemplate[n_attr], CKA_DECRYPT, &_true, sizeof(_true)); + n_attr++; + } + + if (opt_key_usage_wrap) { + FILL_ATTR(keyTemplate[n_attr], CKA_WRAP, &_true, sizeof(_true)); + n_attr++; + FILL_ATTR(keyTemplate[n_attr], CKA_UNWRAP, &_true, sizeof(_true)); + n_attr++; + } + + if (opt_key_usage_sign != 0) { + FILL_ATTR(keyTemplate[n_attr], CKA_SIGN, &_true, sizeof(_true)); + n_attr++; + FILL_ATTR(keyTemplate[n_attr], CKA_VERIFY, &_true, sizeof(_true)); + n_attr++; + } + + if (opt_key_usage_derive != 0) { + FILL_ATTR(keyTemplate[n_attr], CKA_DERIVE, &_true, sizeof(_true)); + n_attr++; + } else { + FILL_ATTR(keyTemplate[n_attr], CKA_DERIVE, &_false, sizeof(_false)); + n_attr++; } - FILL_ATTR(keyTemplate[n_attr], CKA_ENCRYPT, &_true, sizeof(_true)); - n_attr++; + FILL_ATTR(keyTemplate[n_attr], CKA_VALUE_LEN, &key_length, sizeof(key_length)); + n_attr++; + + mechanism.mechanism = opt_mechanism; + } + + if (label != NULL) { + FILL_ATTR(keyTemplate[n_attr], CKA_LABEL, label, strlen(label)); + n_attr++; + } + else if (opt_object_label != NULL) { + FILL_ATTR(keyTemplate[n_attr], CKA_LABEL, opt_object_label, strlen(opt_object_label)); + n_attr++; + } + + if (opt_object_id_len != 0) { + FILL_ATTR(keyTemplate[n_attr], CKA_ID, opt_object_id, opt_object_id_len); + n_attr++; + } + + if (opt_allowed_mechanisms_len > 0) { + FILL_ATTR(keyTemplate[n_attr], + CKA_ALLOWED_MECHANISMS, opt_allowed_mechanisms, + sizeof(CK_MECHANISM_TYPE) * opt_allowed_mechanisms_len); + n_attr++; + } + + rv = p11->C_GenerateKey(session, &mechanism, keyTemplate, n_attr, hSecretKey); + if (rv != CKR_OK) + p11_fatal("C_GenerateKey", rv); + + printf("Key generated:\n"); + show_object(session, *hSecretKey); + return 1; +} + +static int +unwrap_key(CK_SESSION_HANDLE session) +{ + CK_MECHANISM mechanism; + CK_OBJECT_CLASS class = CKO_SECRET_KEY; + CK_BBOOL _true = TRUE; + CK_BBOOL _false = FALSE; + CK_KEY_TYPE key_type = CKK_AES; + CK_ULONG key_length; + const char *length; + CK_ATTRIBUTE keyTemplate[20] = { + {CKA_CLASS, &class, sizeof(class)}, + {CKA_TOKEN, &_true, sizeof(_true)}, + }; + CK_BYTE object_id[100]; + size_t id_len; + CK_OBJECT_HANDLE hSecretKey; + int n_attr = 2; + CK_RV rv; + int fd; + unsigned char in_buffer[16384]; + CK_ULONG wrapped_key_length; + CK_BYTE_PTR pWrappedKey; + params_t params = {0}; + CK_BYTE_PTR iv = NULL; + CK_BYTE_PTR aad = NULL; + CK_OBJECT_HANDLE hUnwrappingKey; + ssize_t sz; + + if (!find_object(session, CKO_PRIVATE_KEY, &hUnwrappingKey, + opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, NULL, 0)) + if (!find_object(session, CKO_SECRET_KEY, &hUnwrappingKey, + opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, NULL, 0)) + util_fatal("Private/secret key not found"); + + if (!opt_mechanism_used) + util_fatal("Unable to unwrap, no mechanism specified\n"); + + mechanism.mechanism = opt_mechanism; + mechanism.pParameter = NULL_PTR; + mechanism.ulParameterLen = 0; + + if (opt_input == NULL) + fd = 0; + else if ((fd = open(opt_input, O_RDONLY | O_BINARY)) < 0) + util_fatal("Cannot open %s: %m", opt_input); + + sz = read(fd, in_buffer, sizeof(in_buffer)); + if (sz < 0) + util_fatal("Cannot read from %s: %m", opt_input); + wrapped_key_length = sz; + if (fd != 0) + close(fd); + pWrappedKey = in_buffer; + + build_params(¶ms, &mechanism, &iv, &aad); + + if (opt_key_type == NULL) { + util_fatal("Key type must be specified"); + } + + if (strncasecmp(opt_key_type, "AES:", strlen("AES:")) == 0) { + length = opt_key_type + strlen("AES:"); + } else if (strncasecmp(opt_key_type, "GENERIC:", strlen("GENERIC:")) == 0) { + length = opt_key_type + strlen("GENERIC:"); + key_type = CKK_GENERIC_SECRET; + } else if (strncasecmp(opt_key_type, "HKDF:", strlen("HKDF:")) == 0) { + length = opt_key_type + strlen("HKDF:"); + key_type = CKK_HKDF; + } else if (strncasecmp(opt_key_type, "RSA:", strlen("RSA:")) == 0) { + length = "0"; // No key length for RSA keys + key_type = CKK_RSA; + class = CKO_PRIVATE_KEY; + } else if (strncasecmp(opt_key_type, "EC:", strlen("EC:")) == 0) { + length = "0"; // No key length for EC keys + key_type = CKK_EC; + class = CKO_PRIVATE_KEY; + } else { + util_fatal("Unsupported key type %s", opt_key_type); + } + /* TODO: Add PQC algorithms*/ + + FILL_ATTR(keyTemplate[n_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); + n_attr++; + + if (opt_is_sensitive != 0) { + FILL_ATTR(keyTemplate[n_attr], CKA_SENSITIVE, &_true, sizeof(_true)); + } else { + FILL_ATTR(keyTemplate[n_attr], CKA_SENSITIVE, &_false, sizeof(_false)); + } + n_attr++; + + if (opt_key_usage_default || opt_key_usage_decrypt) { + if (class != CKO_PRIVATE_KEY) { + FILL_ATTR(keyTemplate[n_attr], CKA_ENCRYPT, &_true, sizeof(_true)); + n_attr++; + } FILL_ATTR(keyTemplate[n_attr], CKA_DECRYPT, &_true, sizeof(_true)); n_attr++; - FILL_ATTR(keyTemplate[n_attr], CKA_WRAP, &_true, sizeof(_true)); - n_attr++; + } + if (opt_key_usage_wrap) { + if (class != CKO_PRIVATE_KEY) { + FILL_ATTR(keyTemplate[n_attr], CKA_WRAP, &_true, sizeof(_true)); + n_attr++; + } FILL_ATTR(keyTemplate[n_attr], CKA_UNWRAP, &_true, sizeof(_true)); n_attr++; - FILL_ATTR(keyTemplate[n_attr], CKA_VALUE_LEN, &key_length, sizeof(key_length)); + } + if (opt_key_usage_sign) { + if (class != CKO_PRIVATE_KEY) { + FILL_ATTR(keyTemplate[n_attr], CKA_VERIFY, &_true, sizeof(_true)); + n_attr++; + } + FILL_ATTR(keyTemplate[n_attr], CKA_SIGN, &_true, sizeof(_true)); n_attr++; + } - mechanism.mechanism = opt_mechanism; + if (opt_is_extractable != 0) { + FILL_ATTR(keyTemplate[n_attr], CKA_EXTRACTABLE, &_true, sizeof(_true)); + } else { + FILL_ATTR(keyTemplate[n_attr], CKA_EXTRACTABLE, &_false, sizeof(_false)); } + n_attr++; - if (label != NULL) { - FILL_ATTR(keyTemplate[n_attr], CKA_LABEL, label, strlen(label)); + /* softhsm2 does not allow to attribute CKA_VALUE_LEN, but MyEID card must have this attribute + specified. We set CKA_VALUE_LEN only if the user sets it in the key specification. */ + key_length = (unsigned long)atol(length); + if (key_length != 0) { + FILL_ATTR(keyTemplate[n_attr], CKA_VALUE_LEN, &key_length, sizeof(key_length)); n_attr++; } - else if (opt_object_label != NULL) { - FILL_ATTR(keyTemplate[n_attr], CKA_LABEL, opt_object_label, strlen(opt_object_label)); + + if (opt_application_label != NULL) { + FILL_ATTR(keyTemplate[n_attr], CKA_LABEL, opt_application_label, strlen(opt_application_label)); n_attr++; } - if (opt_object_id_len != 0) { - FILL_ATTR(keyTemplate[n_attr], CKA_ID, opt_object_id, opt_object_id_len); - n_attr++; + if (opt_application_id != NULL) { + id_len = sizeof(object_id); + if (!sc_hex_to_bin(opt_application_id, object_id, &id_len)) { + FILL_ATTR(keyTemplate[n_attr], CKA_ID, object_id, id_len); + n_attr++; + } } - rv = p11->C_GenerateKey(session, &mechanism, keyTemplate, n_attr, hSecretKey); + if (opt_allowed_mechanisms_len > 0) { + FILL_ATTR(keyTemplate[n_attr], CKA_ALLOWED_MECHANISMS, opt_allowed_mechanisms, + sizeof(CK_MECHANISM_TYPE) * opt_allowed_mechanisms_len); + n_attr++; + } + rv = p11->C_UnwrapKey(session, &mechanism, hUnwrappingKey, + pWrappedKey, wrapped_key_length, keyTemplate, n_attr, &hSecretKey); if (rv != CKR_OK) - p11_fatal("C_GenerateKey", rv); + p11_fatal("C_UnwrapKey", rv); - printf("Key generated:\n"); - show_object(session, *hSecretKey); + free(iv); + free(aad); + printf("Key unwrapped\n"); + show_object(session, hSecretKey); return 1; } +static int +wrap_key(CK_SESSION_HANDLE session) +{ + CK_BYTE pWrappedKey[4096]; + CK_ULONG pulWrappedKeyLen = sizeof(pWrappedKey); + CK_MECHANISM mechanism; + CK_OBJECT_HANDLE hWrappingKey; // wrapping key + CK_OBJECT_HANDLE hkey; // key to be wrapped + CK_RV rv; + CK_BYTE hkey_id[100]; + size_t hkey_id_len; + int fd; + ssize_t sz; + params_t params = {0}; + CK_BYTE_PTR iv = NULL; + CK_BYTE_PTR aad = NULL; + + if (NULL == opt_application_id) + util_fatal("Use --application-id to specify secret key (to be wrapped)"); + if (!opt_mechanism_used) + util_fatal("Unable to wrap, no mechanism specified\n"); + + mechanism.mechanism = opt_mechanism; + mechanism.pParameter = NULL_PTR; + mechanism.ulParameterLen = 0; + + build_params(¶ms, &mechanism, &iv, &aad); + + hkey_id_len = sizeof(hkey_id); + if (sc_hex_to_bin(opt_application_id, hkey_id, &hkey_id_len)) + util_fatal("Invalid application-id \"%s\"\n", opt_application_id); + + if (!find_object(session, CKO_SECRET_KEY, &hkey, hkey_id_len ? hkey_id : NULL, hkey_id_len, NULL, 0)) + if (!find_object(session, CKO_PRIVATE_KEY, &hkey, hkey_id_len ? hkey_id : NULL, hkey_id_len, NULL, 0)) + util_fatal("Key to be wrapped not found"); + + if (!find_object(session, CKO_PUBLIC_KEY, &hWrappingKey, + opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, NULL, 0)) + if (!find_object(session, CKO_SECRET_KEY, &hWrappingKey, + opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, NULL, 0)) + util_fatal("Wrapping key not found"); + + rv = p11->C_WrapKey(session, &mechanism, hWrappingKey, hkey, pWrappedKey, &pulWrappedKeyLen); + if (rv != CKR_OK) + p11_fatal("C_WrapKey", rv); + printf("Key wrapped\n"); + + if (opt_output == NULL) + fd = 1; + else if ((fd = open(opt_output, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, S_IRUSR | S_IWUSR)) < 0) + util_fatal("failed to open %s: %m", opt_output); + + sz = write(fd, pWrappedKey, pulWrappedKeyLen); + + if (sz < 0) + util_fatal("Failed to write to %s: %m", opt_output); + if (fd != 1) + close(fd); + free(iv); + free(aad); + return 1; +} #ifdef ENABLE_OPENSSL static void parse_certificate(struct x509cert_info *cert, - unsigned char *data, int len, unsigned char *contents, - int *contents_len) + unsigned char *data, ssize_t len, unsigned char *contents, + ssize_t *contents_len) { X509 *x = NULL; unsigned char *p; int n; if (strstr((char *)data, "-----BEGIN CERTIFICATE-----")) { - BIO *mem = BIO_new_mem_buf(data, len); + BIO *mem = BIO_new_mem_buf(data, (int)len); x = PEM_read_bio_X509(mem, NULL, NULL, NULL); /* Update what is written to the card to be DER encoded */ if (contents != NULL) { @@ -2189,11 +4390,10 @@ static void parse_certificate(struct x509cert_info *cert, util_fatal("issuer name too long"); /* green light, actually do it */ p = cert->issuer; - n =i2d_X509_NAME(X509_get_issuer_name(x), &p); + n = i2d_X509_NAME(X509_get_issuer_name(x), &p); cert->issuer_len = n; /* check length first */ - n = 0; n = i2d_ASN1_INTEGER(X509_get_serialNumber(x), NULL); if (n < 0) util_fatal("OpenSSL error while encoding serial number"); @@ -2203,27 +4403,49 @@ static void parse_certificate(struct x509cert_info *cert, p = cert->serialnum; n = i2d_ASN1_INTEGER(X509_get_serialNumber(x), &p); cert->serialnum_len = n; + + X509_free(x); } static int do_read_key(unsigned char *data, size_t data_len, int private, EVP_PKEY **key) { - BIO *mem = BIO_new_mem_buf(data, data_len);; + BIO *mem = BIO_new_mem_buf(data, (int)data_len); if (!key) return -1; if (private) { if (!strstr((char *)data, "-----BEGIN ")) +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + *key = d2i_PrivateKey_ex_bio(mem, NULL, osslctx, NULL); +#else *key = d2i_PrivateKey_bio(mem, NULL); +#endif else +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + *key = PEM_read_bio_PrivateKey_ex(mem, NULL, NULL, NULL, osslctx, NULL); +#else *key = PEM_read_bio_PrivateKey(mem, NULL, NULL, NULL); +#endif } else { if (!strstr((char *)data, "-----BEGIN ")) + /* + * d2i_PUBKEY_ex_bio is in OpenSSL master of 02/23/2023 + * committed Dec 26, 2022 expected in 3.2.0 + */ +#if OPENSSL_VERSION_NUMBER >= 0x30200000L + *key = d2i_PUBKEY_ex_bio(mem, NULL, osslctx, NULL); +#else *key = d2i_PUBKEY_bio(mem, NULL); +#endif else +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + *key = PEM_read_bio_PUBKEY_ex(mem, NULL, NULL, NULL, osslctx, NULL); +#else *key = PEM_read_bio_PUBKEY(mem, NULL, NULL, NULL); +#endif } BIO_free(mem); @@ -2236,7 +4458,7 @@ do_read_key(unsigned char *data, size_t data_len, int private, EVP_PKEY **key) #define RSA_GET_BN(RSA, LOCALNAME, BNVALUE) \ do { \ if (BNVALUE) { \ - RSA->LOCALNAME = malloc(BN_num_bytes(BNVALUE)); \ + RSA->LOCALNAME = OPENSSL_malloc(BN_num_bytes(BNVALUE)); \ if (!RSA->LOCALNAME) \ util_fatal("malloc() failure\n"); \ RSA->LOCALNAME##_len = BN_bn2bin(BNVALUE, RSA->LOCALNAME); \ @@ -2249,62 +4471,106 @@ do_read_key(unsigned char *data, size_t data_len, int private, EVP_PKEY **key) static int parse_rsa_pkey(EVP_PKEY *pkey, int private, struct rsakey_info *rsa) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L RSA *r; const BIGNUM *r_n, *r_e, *r_d; const BIGNUM *r_p, *r_q; const BIGNUM *r_dmp1, *r_dmq1, *r_iqmp; - r = EVP_PKEY_get1_RSA(pkey); if (!r) { - if (private) - util_fatal("OpenSSL error during RSA private key parsing"); - else - util_fatal("OpenSSL error during RSA public key parsing"); + util_fatal("OpenSSL error during RSA %s key parsing: %s", private ? "private" : "public", + ERR_error_string(ERR_peek_last_error(), NULL)); } RSA_get0_key(r, &r_n, &r_e, NULL); +#else + BIGNUM *r_n = NULL, *r_e = NULL, *r_d = NULL; + BIGNUM *r_p = NULL, *r_q = NULL; + BIGNUM *r_dmp1 = NULL, *r_dmq1 = NULL, *r_iqmp = NULL; + if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &r_n) != 1 || + EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &r_e) != 1) { + util_fatal("OpenSSL error during RSA %s key parsing: %s", private ? "private" : "public", + ERR_error_string(ERR_peek_last_error(), NULL)); + } +#endif RSA_GET_BN(rsa, modulus, r_n); RSA_GET_BN(rsa, public_exponent, r_e); if (private) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L RSA_get0_key(r, NULL, NULL, &r_d); + RSA_get0_factors(r, &r_p, &r_q); + RSA_get0_crt_params(r, &r_dmp1, &r_dmq1, &r_iqmp); +#else + if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_D, &r_d) != 1 || + EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR1, &r_p) != 1 || + EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR2, &r_q) != 1 || + EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_EXPONENT1, &r_dmp1) != 1 || + EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_EXPONENT2, &r_dmq1) != 1 || + EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, &r_iqmp) != 1) { + util_fatal("OpenSSL error during RSA private key parsing: %s", + ERR_error_string(ERR_peek_last_error(), NULL)); + } +#endif RSA_GET_BN(rsa, private_exponent, r_d); - RSA_get0_factors(r, &r_p, &r_q); RSA_GET_BN(rsa, prime_1, r_p); RSA_GET_BN(rsa, prime_2, r_q); - RSA_get0_crt_params(r, &r_dmp1, &r_dmq1, &r_iqmp); RSA_GET_BN(rsa, exponent_1, r_dmp1); RSA_GET_BN(rsa, exponent_2, r_dmq1); RSA_GET_BN(rsa, coefficient, r_iqmp); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + BN_clear_free(r_d); + BN_clear_free(r_p); + BN_clear_free(r_q); + BN_clear_free(r_dmp1); + BN_clear_free(r_dmq1); + BN_clear_free(r_iqmp); +#endif } - +#if OPENSSL_VERSION_NUMBER < 0x30000000L RSA_free(r); - +#else + BN_free(r_n); + BN_free(r_e); +#endif return 0; } -#if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) +#if !defined(OPENSSL_NO_EC) static int -parse_gost_pkey(EVP_PKEY *pkey, int private, struct gostkey_info *gost) +parse_gost_pkey(EVP_PKEY *pkey, int private, struct generalkey_info *gost) { - EC_KEY *src = EVP_PKEY_get0(pkey); unsigned char *pder; - const BIGNUM *bignum; BIGNUM *X, *Y; - const EC_POINT *point; int nid, rv; - +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const BIGNUM *bignum; + const EC_GROUP *group; + const EC_POINT *point; + EC_KEY *src = EVP_PKEY_get0(pkey); if (!src) return -1; + group = EC_KEY_get0_group(src); + nid = EC_GROUP_get_curve_name(group); +#else + unsigned char *pubkey = NULL; + size_t pubkey_len = 0; + BIGNUM *bignum = NULL; + EC_GROUP *group = NULL; + EC_POINT *point = NULL; + char name[256]; size_t len = 0; + if (EVP_PKEY_get_group_name(pkey, name, sizeof(name), &len) != 1) + return -1; - nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(EVP_PKEY_get0(pkey))); + nid = OBJ_txt2nid(name); +#endif rv = i2d_ASN1_OBJECT(OBJ_nid2obj(nid), NULL); if (rv < 0) return -1; - gost->param_oid.value = malloc(rv); + gost->param_oid.value = OPENSSL_malloc(rv); if (!gost->param_oid.value) return -1; @@ -2313,25 +4579,49 @@ parse_gost_pkey(EVP_PKEY *pkey, int private, struct gostkey_info *gost) gost->param_oid.len = rv; if (private) { - bignum = EC_KEY_get0_private_key(EVP_PKEY_get0(pkey)); - +#if OPENSSL_VERSION_NUMBER < 0x30000000L + bignum = EC_KEY_get0_private_key(src); +#else + if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &bignum) != 1) + return -1; +#endif gost->private.len = BN_num_bytes(bignum); - gost->private.value = malloc(gost->private.len); - if (!gost->private.value) + gost->private.value = OPENSSL_malloc(gost->private.len); + if (!gost->private.value) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + BN_free(bignum); +#endif return -1; + } BN_bn2bin(bignum, gost->private.value); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + BN_free(bignum); +#endif } else { X = BN_new(); Y = BN_new(); +#if OPENSSL_VERSION_NUMBER < 0x30000000L point = EC_KEY_get0_public_key(src); +#else + group = EC_GROUP_new_by_curve_name_ex(osslctx, NULL, nid); + EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, NULL, 0, &pubkey_len); + if (!(pubkey = OPENSSL_malloc(pubkey_len)) || + EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, pubkey, pubkey_len, NULL) != 1 || + !(point = EC_POINT_new(group)) || + EC_POINT_oct2point(group, point, pubkey, pubkey_len, NULL) != 1) { + EC_GROUP_free(group); + EC_POINT_free(point); + return -1; + } + OPENSSL_free(pubkey); +#endif rv = -1; - if (X && Y && point && EC_KEY_get0_group(src)) - rv = EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(src), - point, X, Y, NULL); + if (X && Y && point && group) + rv = EC_POINT_get_affine_coordinates(group, point, X, Y, NULL); if (rv == 1) { gost->public.len = BN_num_bytes(X) + BN_num_bytes(Y); - gost->public.value = malloc(gost->public.len); + gost->public.value = OPENSSL_malloc(gost->public.len); if (!gost->public.value) rv = -1; else @@ -2342,117 +4632,523 @@ parse_gost_pkey(EVP_PKEY *pkey, int private, struct gostkey_info *gost) } BN_free(X); BN_free(Y); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EC_GROUP_free(group); + EC_POINT_free(point); +#endif if (rv != 1) return -1; } - return 0; } static int -parse_ec_pkey(EVP_PKEY *pkey, int private, struct gostkey_info *gost) +parse_ec_pkey(EVP_PKEY *pkey, int private, struct generalkey_info *ec) { - EC_KEY *src = EVP_PKEY_get0(pkey); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const EC_KEY *src = EVP_PKEY_get0_EC_KEY(pkey); const BIGNUM *bignum; - if (!src) return -1; - - gost->param_oid.len = i2d_ECParameters(src, &gost->param_oid.value); - if (gost->param_oid.len <= 0) + ec->param_oid.len = i2d_ECParameters((EC_KEY *)src, &ec->param_oid.value); +#else + BIGNUM *bignum = NULL; + ec->param_oid.len = i2d_KeyParams(pkey, &ec->param_oid.value); +#endif + if (ec->param_oid.len <= 0) { return -1; + } if (private) { - bignum = EC_KEY_get0_private_key(EVP_PKEY_get0(pkey)); - - gost->private.len = BN_num_bytes(bignum); - gost->private.value = malloc(gost->private.len); - if (!gost->private.value) +#if OPENSSL_VERSION_NUMBER < 0x30000000L + bignum = EC_KEY_get0_private_key(src); +#else + if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &bignum) != 1) { return -1; - BN_bn2bin(bignum, gost->private.value); + } +#endif + ec->private.len = BN_num_bytes(bignum); + ec->private.value = OPENSSL_malloc(ec->private.len); + if (!ec->private.value) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + BN_free(bignum); +#endif + return -1; + } + BN_bn2bin(bignum, ec->private.value); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + BN_free(bignum); +#endif } else { unsigned char buf[512], *point; - int point_len, header_len; + size_t point_len, header_len; const int MAX_HEADER_LEN = 3; +#if OPENSSL_VERSION_NUMBER < 0x30000000L const EC_GROUP *ecgroup = EC_KEY_get0_group(src); const EC_POINT *ecpoint = EC_KEY_get0_public_key(src); if (!ecgroup || !ecpoint) return -1; point_len = EC_POINT_point2oct(ecgroup, ecpoint, POINT_CONVERSION_UNCOMPRESSED, buf, sizeof(buf), NULL); - gost->public.value = malloc(MAX_HEADER_LEN+point_len); - if (!gost->public.value) +#else + EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, buf, sizeof(buf), &point_len); +#endif + ec->public.value = OPENSSL_malloc(MAX_HEADER_LEN + point_len); + if (!ec->public.value) return -1; - point = gost->public.value; - ASN1_put_object(&point, 0, point_len, V_ASN1_OCTET_STRING, V_ASN1_UNIVERSAL); - header_len = point-gost->public.value; + point = ec->public.value; + ASN1_put_object(&point, 0, (int)point_len, V_ASN1_OCTET_STRING, V_ASN1_UNIVERSAL); + header_len = point - ec->public.value; memcpy(point, buf, point_len); - gost->public.len = header_len+point_len; -#ifndef EC_POINT_NO_ASN1_OCTET_STRING // workaround for non-compliant cards not expecting DER encoding - gost->public.len -= header_len; - gost->public.value += header_len; + ec->public.len = header_len + point_len; +#ifdef EC_POINT_NO_ASN1_OCTET_STRING // workaround for non-compliant cards not expecting DER encoding + ec->public.len -= header_len; + ec->public.value += header_len; + // store header_len to restore public.value before free it + ec->header_len = header_len; +#endif + } + + return 0; +} + +#if OPENSSL_VERSION_NUMBER >= 0x30500000L +static int +parse_pqc_pkey(EVP_PKEY *pkey, CK_KEY_TYPE type, int private, struct pqckey_info *pqc) +{ + int rc; + + const char *str_name = EVP_PKEY_get0_type_name(pkey); + if (strcmp(str_name, "ML-DSA-44") == 0) { + pqc->type = CKP_ML_DSA_44; + } else if (strcmp(str_name, "ML-DSA-65") == 0) { + pqc->type = CKP_ML_DSA_65; + } else if (strcmp(str_name, "ML-DSA-87") == 0) { + pqc->type = CKP_ML_DSA_87; + } else if (strcmp(str_name, "ML-KEM-512") == 0) { + pqc->type = CKP_ML_KEM_512; + } else if (strcmp(str_name, "ML-KEM-768") == 0) { + pqc->type = CKP_ML_KEM_768; + } else if (strcmp(str_name, "ML-KEM-1024") == 0) { + pqc->type = CKP_ML_KEM_1024; + } else if (strcmp(str_name, "SLH-DSA-SHA2-128f") == 0) { + pqc->type = CKP_SLH_DSA_SHA2_128F; + } else if (strcmp(str_name, "SLH-DSA-SHA2-128s") == 0) { + pqc->type = CKP_SLH_DSA_SHA2_128S; + } else if (strcmp(str_name, "SLH-DSA-SHA2-192f") == 0) { + pqc->type = CKP_SLH_DSA_SHA2_192F; + } else if (strcmp(str_name, "SLH-DSA-SHA2-192s") == 0) { + pqc->type = CKP_SLH_DSA_SHA2_192S; + } else if (strcmp(str_name, "SLH-DSA-SHA2-256f") == 0) { + pqc->type = CKP_SLH_DSA_SHA2_256F; + } else if (strcmp(str_name, "SLH-DSA-SHA2-256s") == 0) { + pqc->type = CKP_SLH_DSA_SHA2_256S; + } else if (strcmp(str_name, "SLH-DSA-SHAKE-128f") == 0) { + pqc->type = CKP_SLH_DSA_SHAKE_128F; + } else if (strcmp(str_name, "SLH-DSA-SHAKE-128s") == 0) { + pqc->type = CKP_SLH_DSA_SHAKE_128S; + } else if (strcmp(str_name, "SLH-DSA-SHAKE-192f") == 0) { + pqc->type = CKP_SLH_DSA_SHAKE_192F; + } else if (strcmp(str_name, "SLH-DSA-SHAKE-192s") == 0) { + pqc->type = CKP_SLH_DSA_SHAKE_192S; + } else if (strcmp(str_name, "SLH-DSA-SHAKE-256f") == 0) { + pqc->type = CKP_SLH_DSA_SHAKE_256F; + } else if (strcmp(str_name, "SLH-DSA-SHAKE-256s") == 0) { + pqc->type = CKP_SLH_DSA_SHAKE_256S; + } + if (private) { + size_t priv_len = 0; + char *seed_param = NULL; + + /* Some keys might have only the seed -- skip if not available */ + rc = EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0, &priv_len); + if (rc == 1) { + pqc->private.len = priv_len; + if (!(pqc->private.value = OPENSSL_malloc(priv_len))) { + return -1; + } + rc = EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, + pqc->private.value, priv_len, NULL); + if (rc != 1) { + OPENSSL_free(pqc->private.value); + return -1; + } + } else { + switch (type) { + case CKK_ML_DSA: + seed_param = OSSL_PKEY_PARAM_ML_DSA_SEED; + break; + case CKK_SLH_DSA: + seed_param = OSSL_PKEY_PARAM_SLH_DSA_SEED; + break; + } + if (seed_param != NULL) { + size_t seed_len = 0; + rc = EVP_PKEY_get_octet_string_param(pkey, seed_param, NULL, 0, &seed_len); + if (rc != 1) { + util_fatal("Neither PRIVATE key value nor seed available"); + } + pqc->seed.len = seed_len; + if (!(pqc->seed.value = OPENSSL_malloc(pqc->seed.len))) { + OPENSSL_free(pqc->private.value); + return -1; + } + rc = EVP_PKEY_get_octet_string_param(pkey, seed_param, pqc->seed.value, + pqc->seed.len, NULL); + if (rc != 1) { + OPENSSL_free(pqc->private.value); + OPENSSL_free(pqc->seed.value); + return -1; + } + } else { + util_fatal("PRIVATE key value not available"); + } + } + } else { + size_t pub_len; + + rc = EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, NULL, 0, &pub_len); + if (rc != 1) { + return -1; + } + pqc->public.len = pub_len; + if (!(pqc->public.value = OPENSSL_malloc(pub_len))) { + return -1; + } + rc = EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, pqc->public.value, + pqc->public.len, NULL); + if (rc != 1) { + OPENSSL_free(pqc->public.value); + return -1; + } + } + return 0; +} +#endif + +#ifdef ENABLE_OPENSSL +/* Return PKCS11 key type based on OpenSSL EVP_PKEY type + * which are supported by PKCS11 and OpenSSL used when compiling. + * PKCS11 defines CKK_EC_EDWARDS for both Ed25529 and Ed448 + * and CKK_EC_MONTGOMERY for X25519 and X448. + * If requested, return pointer to struct ec_curve_info containing OID and size + */ + +static CK_RV +evp_pkey2ck_key_type(EVP_PKEY *pkey, CK_KEY_TYPE *type, int *pk_type, struct ec_curve_info **ec_curve_info) +{ + if (!pkey || !pk_type || !type) + return CKR_GENERAL_ERROR; + + *pk_type = EVP_PKEY_base_id(pkey); + if (ec_curve_info) + *ec_curve_info = NULL; + + switch (*pk_type) { +#if defined(EVP_PKEY_RSA) + case EVP_PKEY_RSA: + *type = CKK_RSA; + return CKR_OK; +#endif + +#if defined(EVP_PKEY_EC) + case EVP_PKEY_EC: + *type = CKK_EC; + return CKR_OK; +#endif + +#if defined(EVP_PKEY_ED25519) + case EVP_PKEY_ED25519: + *type = CKK_EC_EDWARDS; + if (ec_curve_info == NULL) + goto err; + *ec_curve_info = match_ec_curve_by_name("Ed25519"); + return CKR_OK; #endif + +#if defined(EVP_PKEY_ED448) + case EVP_PKEY_ED448: + *type = CKK_EC_EDWARDS; + if (ec_curve_info == NULL) + goto err; + *ec_curve_info = match_ec_curve_by_name("Ed448"); + return CKR_OK; +#endif + +#if defined(EVP_PKEY_X25519) + case EVP_PKEY_X25519: + *type = CKK_EC_MONTGOMERY; + if (ec_curve_info == NULL) + goto err; + *ec_curve_info = match_ec_curve_by_name("X25519"); + return CKR_OK; +#endif + +#if defined(EVP_PKEY_X448) + case EVP_PKEY_X448: + *type = CKK_EC_MONTGOMERY; + if (ec_curve_info == NULL) + goto err; + *ec_curve_info = match_ec_curve_by_name("X448"); + return CKR_OK; +#endif + +#if defined(NID_id_GostR3410_2001) + case NID_id_GostR3410_2001: + *type = CKK_GOSTR3410; + return CKR_OK; +#endif + +#if defined(EVP_PKEY_GOSTR3411) + case EVP_PKEY_GOSTR3411: + *type = CKK_GOSTR3411; + return CKR_OK; +#endif + +#if defined(EVP_PKEY_GOST28147) + case EVP_PKEY_GOST28147: + *type = CKK_GOST28147; + return CKR_OK; +#endif + +#if defined(EVP_PKEY_ML_DSA_44) && defined(EVP_PKEY_ML_DSA_65) && defined(EVP_PKEY_ML_DSA_87) + case EVP_PKEY_ML_DSA_44: + case EVP_PKEY_ML_DSA_65: + case EVP_PKEY_ML_DSA_87: + *type = CKK_ML_DSA; + return CKR_OK; +#endif + +#if defined(EVP_PKEY_ML_KEM_512) && defined(EVP_PKEY_ML_KEM_768) && defined(EVP_PKEY_ML_KEM_1024) + case EVP_PKEY_ML_KEM_512: + case EVP_PKEY_ML_KEM_768: + case EVP_PKEY_ML_KEM_1024: + *type = CKK_ML_KEM; + return CKR_OK; +#endif + +#if defined(EVP_PKEY_SLH_DSA_SHA2_128S) + case EVP_PKEY_SLH_DSA_SHA2_128S: + case EVP_PKEY_SLH_DSA_SHAKE_128S: + case EVP_PKEY_SLH_DSA_SHA2_128F: + case EVP_PKEY_SLH_DSA_SHAKE_128F: + case EVP_PKEY_SLH_DSA_SHA2_192S: + case EVP_PKEY_SLH_DSA_SHAKE_192S: + case EVP_PKEY_SLH_DSA_SHA2_192F: + case EVP_PKEY_SLH_DSA_SHAKE_192F: + case EVP_PKEY_SLH_DSA_SHA2_256S: + case EVP_PKEY_SLH_DSA_SHAKE_256S: + case EVP_PKEY_SLH_DSA_SHA2_256F: + case EVP_PKEY_SLH_DSA_SHAKE_256F: + *type = CKK_SLH_DSA; + return CKR_OK; +#endif + } + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + /* Still no dice? Try the new OpenSSL 3.0 API */ + const char *str_name = EVP_PKEY_get0_type_name(pkey); + if (strcmp(str_name, "ML-DSA-44") == 0 || + strcmp(str_name, "ML-DSA-65") == 0 || + strcmp(str_name, "ML-DSA-87") == 0) { + *type = CKK_ML_DSA; + return CKR_OK; + } else if (strcmp(str_name, "ML-KEM-512") == 0 || + strcmp(str_name, "ML-KEM-768") == 0 || + strcmp(str_name, "ML-KEM-1024") == 0) { + *type = CKK_ML_KEM; + return CKR_OK; + } else if (strcmp(str_name, "SLH-DSA-SHA2-128f") == 0 || + strcmp(str_name, "SLH-DSA-SHA2-128s") == 0 || + strcmp(str_name, "SLH-DSA-SHA2-192f") == 0 || + strcmp(str_name, "SLH-DSA-SHA2-192s") == 0 || + strcmp(str_name, "SLH-DSA-SHA2-256f") == 0 || + strcmp(str_name, "SLH-DSA-SHA2-256s") == 0 || + strcmp(str_name, "SLH-DSA-SHAKE-128f") == 0 || + strcmp(str_name, "SLH-DSA-SHAKE-128s") == 0 || + strcmp(str_name, "SLH-DSA-SHAKE-192f") == 0 || + strcmp(str_name, "SLH-DSA-SHAKE-192s") == 0 || + strcmp(str_name, "SLH-DSA-SHAKE-256f") == 0 || + strcmp(str_name, "SLH-DSA-SHAKE-256s") == 0) { + *type = CKK_SLH_DSA; + return CKR_OK; + } + +#endif + +err: + /* unsupported by OpenSSL, PKCS11 or this program */ + printf(" ERR: Key type %d not supported\n", *pk_type); + *type = CKK_OPENSC_UNDEFINED; + *pk_type = -1; + return CKR_FUNCTION_NOT_SUPPORTED; +} +#endif /* ENABLE_OPENSSL */ + +/* Edwards and Montogmery keys have the same format */ +static int +parse_ed_mont_pkey(EVP_PKEY *pkey, CK_KEY_TYPE type, int pk_type, struct ec_curve_info *ec_curve_info, int private, struct generalkey_info *gost) +{ + unsigned char *key; + size_t key_size; + + /* set EC_PARAMS value + * The param passed is DER of an OID or a PRINTABLE STRING as defines in PKCS11 3.0 + * all of the ec_curve_info entries have one byte len values + */ + + gost->param_oid.value = OPENSSL_malloc(ec_curve_info->ec_params_size); + if (gost->param_oid.value == NULL) { + return -1; + } + gost->param_oid.len = ec_curve_info->ec_params_size; + memcpy(gost->param_oid.value, ec_curve_info->ec_params, ec_curve_info->ec_params_size); + + if (private) { + if (EVP_PKEY_get_raw_private_key(pkey, NULL, &key_size) != 1) { + return -1; + } + } else { + if (EVP_PKEY_get_raw_public_key(pkey, NULL, &key_size) != 1) { + return -1; + } + } + + key = OPENSSL_malloc(key_size); + if (key == NULL) { + return -1; + } + + if (private) { + if (EVP_PKEY_get_raw_private_key(pkey, key, &key_size) != 1) { + OPENSSL_free(key); + return -1; + } + gost->private.value = key; + gost->private.len = key_size; + } else { + if (EVP_PKEY_get_raw_public_key(pkey, key, &key_size) != 1) { + OPENSSL_free(key); + return -1; + } + gost->public.value = key; + gost->public.len = key_size; } return 0; } #endif +static void +general_key_info_free(struct generalkey_info key_info) +{ +#ifdef EC_POINT_NO_ASN1_OCTET_STRING // restore public.value before free it + if (key_info.public.value) + key_info.public.value -= key_info.header_len; +#endif + OPENSSL_free(key_info.param_oid.value); + OPENSSL_free(key_info.public.value); + OPENSSL_free(key_info.private.value); +} + +static void +rsa_info_free(struct rsakey_info rsa) +{ + OPENSSL_free(rsa.modulus); + OPENSSL_free(rsa.public_exponent); + OPENSSL_free(rsa.private_exponent); + OPENSSL_free(rsa.prime_1); + OPENSSL_free(rsa.prime_2); + OPENSSL_free(rsa.exponent_1); + OPENSSL_free(rsa.exponent_2); + OPENSSL_free(rsa.coefficient); +} #endif -#define MAX_OBJECT_SIZE 5000 +#ifdef ENABLE_OPENSSL +static void +pqc_key_info_free(struct pqckey_info pqc) +{ + OPENSSL_free(pqc.public.value); + OPENSSL_free(pqc.private.value); + OPENSSL_free(pqc.seed.value); +} +#endif /* ENABLE_OPENSSL */ /* Currently for certificates (-type cert), private keys (-type privkey), public keys (-type pubkey) and data objects (-type data). */ -static int write_object(CK_SESSION_HANDLE session) +static CK_RV write_object(CK_SESSION_HANDLE session) { CK_BBOOL _true = TRUE; CK_BBOOL _false = FALSE; - unsigned char contents[MAX_OBJECT_SIZE + 1]; - int contents_len = 0; - unsigned char certdata[MAX_OBJECT_SIZE]; - int certdata_len = 0; + unsigned char *contents = NULL; + ssize_t contents_len = 0; + unsigned char *certdata = NULL; + ssize_t certdata_len = 0; FILE *f; - CK_OBJECT_HANDLE cert_obj, privkey_obj, pubkey_obj, data_obj; - CK_ATTRIBUTE cert_templ[20], privkey_templ[20], pubkey_templ[20], data_templ[20]; - int n_cert_attr = 0, n_privkey_attr = 0, n_pubkey_attr = 0, n_data_attr = 0; + CK_OBJECT_HANDLE cert_obj, privkey_obj, pubkey_obj, seckey_obj, data_obj; + CK_ATTRIBUTE cert_templ[20], privkey_templ[30], pubkey_templ[20], seckey_templ[20], data_templ[20]; + int n_cert_attr = 0, n_privkey_attr = 0, n_pubkey_attr = 0, n_seckey_attr = 0, n_data_attr = 0; struct sc_object_id oid; CK_RV rv; int need_to_parse_certdata = 0; unsigned char *oid_buf = NULL; CK_OBJECT_CLASS clazz; CK_CERTIFICATE_TYPE cert_type; + CK_KEY_TYPE type = CKK_OPENSC_UNDEFINED; + size_t ret = 0; #ifdef ENABLE_OPENSSL - CK_KEY_TYPE type = CKK_RSA; struct x509cert_info cert; struct rsakey_info rsa; - struct gostkey_info gost; + struct generalkey_info general_key; + struct pqckey_info pqc_key; EVP_PKEY *evp_key = NULL; - int pk_type; + int pk_type = -1; + struct ec_curve_info *ec_curve_info = NULL; memset(&cert, 0, sizeof(cert)); memset(&rsa, 0, sizeof(rsa)); - memset(&gost, 0, sizeof(gost)); + memset(&general_key, 0, sizeof(general_key)); + memset(&pqc_key, 0, sizeof(pqc_key)); #endif - memset(contents, 0, sizeof(contents)); - memset(certdata, 0, sizeof(certdata)); - f = fopen(opt_file_to_write, "rb"); if (f == NULL) util_fatal("Couldn't open file \"%s\"", opt_file_to_write); - contents_len = fread(contents, 1, sizeof(contents) - 1, f); + if (fseek(f, 0L, SEEK_END) != 0) + util_fatal("Couldn't set file position to the end of the file \"%s\"", opt_file_to_write); + contents_len = ftell(f); if (contents_len < 0) + util_fatal("Couldn't get file position \"%s\"", opt_file_to_write); + contents = malloc(contents_len + 1); + if (contents == NULL) + util_fatal("malloc() failure\n"); + if (fseek(f, 0L, SEEK_SET) != 0) + util_fatal("Couldn't set file position to the beginning of the file \"%s\"", opt_file_to_write); + ret = fread(contents, 1, contents_len, f); + if (ret != (size_t)contents_len) util_fatal("Couldn't read from file \"%s\"", opt_file_to_write); fclose(f); contents[contents_len] = '\0'; if (opt_attr_from_file) { - if (!(f = fopen(opt_attr_from_file, "rb"))) + f = fopen(opt_attr_from_file, "rb"); + if (f == NULL) util_fatal("Couldn't open file \"%s\"", opt_attr_from_file); - certdata_len = fread(certdata, 1, sizeof(certdata), f); + if (fseek(f, 0L, SEEK_END) != 0) + util_fatal("Couldn't set file position to the end of the file \"%s\"", opt_attr_from_file); + certdata_len = ftell(f); if (certdata_len < 0) + util_fatal("Couldn't get file position \"%s\"", opt_attr_from_file); + certdata = malloc(certdata_len + 1); + if (certdata == NULL) + util_fatal("malloc() failure\n"); + if (fseek(f, 0L, SEEK_SET) != 0) + util_fatal("Couldn't set file position to the beginning of the file \"%s\"", opt_attr_from_file); + ret = fread(certdata, 1, certdata_len, f); + if (ret != (size_t)certdata_len) util_fatal("Couldn't read from file \"%s\"", opt_attr_from_file); fclose(f); + certdata[certdata_len] = '\0'; need_to_parse_certdata = 1; } if (opt_object_class == CKO_CERTIFICATE) { @@ -2466,7 +5162,10 @@ static int write_object(CK_SESSION_HANDLE session) util_fatal("No OpenSSL support, cannot parse certificate"); #endif } else { - memcpy(certdata, contents, MAX_OBJECT_SIZE); + certdata = malloc(contents_len + 1); + if (certdata == NULL) + util_fatal("malloc() failure\n"); + memcpy(certdata, contents, contents_len + 1); certdata_len = contents_len; need_to_parse_certdata = 1; } @@ -2496,21 +5195,26 @@ static int write_object(CK_SESSION_HANDLE session) util_fatal("Cannot read public key"); } - pk_type = EVP_PKEY_base_id(evp_key); + /* get CK_TYPE from EVP_PKEY if both supported by OpenSSL and PKCS11 */ + if (evp_pkey2ck_key_type(evp_key, &type, &pk_type, &ec_curve_info) != CKR_OK) + util_fatal("Key type not supported by OpenSSL and/or PKCS11"); - if (pk_type == EVP_PKEY_RSA) { + if (type == CKK_RSA) { rv = parse_rsa_pkey(evp_key, is_private, &rsa); } -#if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) - else if (pk_type == NID_id_GostR3410_2001) { - rv = parse_gost_pkey(evp_key, is_private, &gost); - type = CKK_GOSTR3410; - } else if (pk_type == EVP_PKEY_EC) { - rv = parse_ec_pkey(evp_key, is_private, &gost); - type = CKK_EC; - } +#if !defined(OPENSSL_NO_EC) + else if (type == CKK_GOSTR3410) { + rv = parse_gost_pkey(evp_key, is_private, &general_key); + } else if (type == CKK_EC) { + rv = parse_ec_pkey(evp_key, is_private, &general_key); + } else if (type == CKK_EC_EDWARDS || type == CKK_EC_MONTGOMERY) { + rv = parse_ed_mont_pkey(evp_key, type, pk_type, ec_curve_info, is_private, &general_key); #endif - else +#if OPENSSL_VERSION_NUMBER >= 0x30500000L + } else if (type == CKK_ML_DSA || type == CKK_ML_KEM || type == CKK_SLH_DSA) { + rv = parse_pqc_pkey(evp_key, type, is_private, &pqc_key); +#endif + } else util_fatal("Unsupported key type: 0x%X", pk_type); if (rv) @@ -2520,7 +5224,9 @@ static int write_object(CK_SESSION_HANDLE session) #endif } - if (opt_object_class == CKO_CERTIFICATE) { + switch(opt_object_class) + { + case CKO_CERTIFICATE: clazz = CKO_CERTIFICATE; cert_type = CKC_X_509; @@ -2528,7 +5234,11 @@ static int write_object(CK_SESSION_HANDLE session) FILL_ATTR(cert_templ[1], CKA_VALUE, contents, contents_len); FILL_ATTR(cert_templ[2], CKA_CLASS, &clazz, sizeof(clazz)); FILL_ATTR(cert_templ[3], CKA_CERTIFICATE_TYPE, &cert_type, sizeof(cert_type)); - FILL_ATTR(cert_templ[4], CKA_PRIVATE, &_false, sizeof(_false)); + if (opt_is_private == 1) { + FILL_ATTR(cert_templ[4], CKA_PRIVATE, &_true, sizeof(_true)); + } else { + FILL_ATTR(cert_templ[4], CKA_PRIVATE, &_false, sizeof(_false)); + } n_cert_attr = 5; if (opt_object_label != NULL) { @@ -2539,6 +5249,10 @@ static int write_object(CK_SESSION_HANDLE session) FILL_ATTR(cert_templ[n_cert_attr], CKA_ID, opt_object_id, opt_object_id_len); n_cert_attr++; } + if (opt_is_destroyable == 0) { + FILL_ATTR(cert_templ[n_cert_attr], CKA_DESTROYABLE, &_false, sizeof(_false)); + n_cert_attr++; + } #ifdef ENABLE_OPENSSL /* according to PKCS #11 CKA_SUBJECT MUST be specified */ FILL_ATTR(cert_templ[n_cert_attr], CKA_SUBJECT, cert.subject, cert.subject_len); @@ -2548,9 +5262,8 @@ static int write_object(CK_SESSION_HANDLE session) FILL_ATTR(cert_templ[n_cert_attr], CKA_SERIAL_NUMBER, cert.serialnum, cert.serialnum_len); n_cert_attr++; #endif - } - else - if (opt_object_class == CKO_PRIVATE_KEY) { + break; + case CKO_PRIVATE_KEY: clazz = CKO_PRIVATE_KEY; n_privkey_attr = 0; @@ -2562,7 +5275,6 @@ static int write_object(CK_SESSION_HANDLE session) n_privkey_attr++; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_SENSITIVE, &_true, sizeof(_true)); n_privkey_attr++; - if (opt_object_label != NULL) { FILL_ATTR(privkey_templ[n_privkey_attr], CKA_LABEL, opt_object_label, strlen(opt_object_label)); n_privkey_attr++; @@ -2579,20 +5291,42 @@ static int write_object(CK_SESSION_HANDLE session) FILL_ATTR(privkey_templ[n_privkey_attr], CKA_DECRYPT, &_true, sizeof(_true)); n_privkey_attr++; } - if (opt_key_usage_derive != 0) { - FILL_ATTR(privkey_templ[n_privkey_attr], CKA_DERIVE, &_true, sizeof(_true)); + if (opt_key_usage_derive != 0) { + FILL_ATTR(privkey_templ[n_privkey_attr], CKA_DERIVE, &_true, sizeof(_true)); + n_privkey_attr++; + } + if (opt_key_usage_wrap) { + FILL_ATTR(privkey_templ[n_privkey_attr], CKA_UNWRAP, &_true, sizeof(_true)); + n_privkey_attr++; + } + if (opt_key_usage_encapsulate) { + FILL_ATTR(privkey_templ[n_privkey_attr], CKA_DECAPSULATE, &_true, sizeof(_true)); + n_privkey_attr++; + } + if (opt_always_auth != 0) { + FILL_ATTR(privkey_templ[n_privkey_attr], CKA_ALWAYS_AUTHENTICATE, + &_true, sizeof(_true)); + n_privkey_attr++; + } + if (opt_is_extractable != 0) { + FILL_ATTR(privkey_templ[n_privkey_attr], CKA_EXTRACTABLE, &_true, sizeof(_true)); + n_privkey_attr++; + } + if (opt_allowed_mechanisms_len > 0) { + FILL_ATTR(privkey_templ[n_privkey_attr], + CKA_ALLOWED_MECHANISMS, opt_allowed_mechanisms, + sizeof(CK_MECHANISM_TYPE) * opt_allowed_mechanisms_len); n_privkey_attr++; } + #ifdef ENABLE_OPENSSL if (cert.subject_len != 0) { FILL_ATTR(privkey_templ[n_privkey_attr], CKA_SUBJECT, cert.subject, cert.subject_len); n_privkey_attr++; } - pk_type = EVP_PKEY_base_id(evp_key); - if (pk_type == EVP_PKEY_RSA) { - type = CKK_RSA; + if (type == CKK_RSA) { FILL_ATTR(privkey_templ[n_privkey_attr], CKA_KEY_TYPE, &type, sizeof(type)); n_privkey_attr++; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_MODULUS, rsa.modulus, rsa.modulus_len); @@ -2611,53 +5345,51 @@ static int write_object(CK_SESSION_HANDLE session) n_privkey_attr++; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_COEFFICIENT, rsa.coefficient, rsa.coefficient_len); n_privkey_attr++; - } -#if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) - else if (pk_type == EVP_PKEY_EC) { - type = CKK_EC; - + } else if ((type == CKK_EC) || (type == CKK_EC_EDWARDS) || (type == CKK_EC_MONTGOMERY)) { FILL_ATTR(privkey_templ[n_privkey_attr], CKA_KEY_TYPE, &type, sizeof(type)); n_privkey_attr++; - FILL_ATTR(privkey_templ[n_privkey_attr], CKA_EC_PARAMS, gost.param_oid.value, gost.param_oid.len); + FILL_ATTR(privkey_templ[n_privkey_attr], CKA_EC_PARAMS, general_key.param_oid.value, general_key.param_oid.len); n_privkey_attr++; - FILL_ATTR(privkey_templ[n_privkey_attr], CKA_VALUE, gost.private.value, gost.private.len); + FILL_ATTR(privkey_templ[n_privkey_attr], CKA_VALUE, general_key.private.value, general_key.private.len); n_privkey_attr++; - } - else if (pk_type == NID_id_GostR3410_2001) { - type = CKK_GOSTR3410; - + } else if (type == CKK_GOSTR3410) { FILL_ATTR(privkey_templ[n_privkey_attr], CKA_KEY_TYPE, &type, sizeof(type)); n_privkey_attr++; - FILL_ATTR(privkey_templ[n_privkey_attr], CKA_GOSTR3410_PARAMS, gost.param_oid.value, gost.param_oid.len); + FILL_ATTR(privkey_templ[n_privkey_attr], CKA_GOSTR3410_PARAMS, general_key.param_oid.value, general_key.param_oid.len); n_privkey_attr++; - FILL_ATTR(privkey_templ[n_privkey_attr], CKA_VALUE, gost.private.value, gost.private.len); + FILL_ATTR(privkey_templ[n_privkey_attr], CKA_VALUE, general_key.private.value, general_key.private.len); /* CKA_VALUE of the GOST key has to be in the little endian order */ rv = sc_mem_reverse(privkey_templ[n_privkey_attr].pValue, privkey_templ[n_privkey_attr].ulValueLen); if (rv) return rv; n_privkey_attr++; - } + } else if (type == CKK_ML_DSA || type == CKK_SLH_DSA || type == CKK_ML_KEM) { + FILL_ATTR(privkey_templ[n_privkey_attr], CKA_KEY_TYPE, &type, sizeof(type)); + n_privkey_attr++; + FILL_ATTR(privkey_templ[n_privkey_attr], CKA_PARAMETER_SET, &pqc_key.type, sizeof(pqc_key.type)); + n_privkey_attr++; + if (pqc_key.private.value != NULL) { + FILL_ATTR(privkey_templ[n_privkey_attr], CKA_VALUE, + pqc_key.private.value, pqc_key.private.len); + n_privkey_attr++; + } + if (pqc_key.seed.value != NULL) { + FILL_ATTR(privkey_templ[n_privkey_attr], CKA_SEED, + pqc_key.seed.value, pqc_key.seed.len); + n_privkey_attr++; + } + } else { + util_fatal("Unsupported CK_KEY_TYPE, cannot write private key"); + } +#else + util_fatal("No OpenSSL support, cannot write private key"); #endif -#endif - } - else - if (opt_object_class == CKO_PUBLIC_KEY) { + break; + case CKO_PUBLIC_KEY: clazz = CKO_PUBLIC_KEY; -#ifdef ENABLE_OPENSSL - pk_type = EVP_PKEY_base_id(evp_key); - if (pk_type == EVP_PKEY_RSA) - type = CKK_RSA; -#if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) - else if (pk_type == EVP_PKEY_EC) - type = CKK_EC; - else if (pk_type == NID_id_GostR3410_2001) - type = CKK_GOSTR3410; -#endif - else - util_fatal("Unsupported public key type: 0x%X", pk_type); -#endif +#ifdef ENABLE_OPENSSL n_pubkey_attr = 0; FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_CLASS, &clazz, sizeof(clazz)); n_pubkey_attr++; @@ -2695,16 +5427,21 @@ static int write_object(CK_SESSION_HANDLE session) FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_DERIVE, &_true, sizeof(_true)); n_pubkey_attr++; } + if (opt_key_usage_wrap) { + FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_WRAP, &_true, sizeof(_true)); + n_pubkey_attr++; + } + if (opt_key_usage_encapsulate) { + FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_ENCAPSULATE, &_true, sizeof(_true)); + n_pubkey_attr++; + } -#ifdef ENABLE_OPENSSL if (cert.subject_len != 0) { FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_SUBJECT, cert.subject, cert.subject_len); n_pubkey_attr++; } - pk_type = EVP_PKEY_base_id(evp_key); - if (pk_type == EVP_PKEY_RSA) { - type = CKK_RSA; + if (type == CKK_RSA) { FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_KEY_TYPE, &type, sizeof(type)); n_pubkey_attr++; FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_MODULUS, @@ -2712,41 +5449,153 @@ static int write_object(CK_SESSION_HANDLE session) n_pubkey_attr++; FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_PUBLIC_EXPONENT, rsa.public_exponent, rsa.public_exponent_len); n_pubkey_attr++; + } else if (type == CKK_ML_DSA || type == CKK_SLH_DSA || type == CKK_ML_KEM) { + FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_KEY_TYPE, &type, sizeof(type)); + n_pubkey_attr++; + FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_PARAMETER_SET, &pqc_key.type, sizeof(pqc_key.type)); + n_pubkey_attr++; + FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_VALUE, pqc_key.public.value, pqc_key.public.len); + n_pubkey_attr++; } -#if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) - else if (pk_type == EVP_PKEY_EC) { - type = CKK_EC; +#if !defined(OPENSSL_NO_EC) + + else if ((type == CKK_EC) || (type == CKK_EC_EDWARDS) || (type == CKK_EC_MONTGOMERY)) { FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_KEY_TYPE, &type, sizeof(type)); n_pubkey_attr++; - FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_EC_PARAMS, gost.param_oid.value, gost.param_oid.len); + FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_EC_PARAMS, general_key.param_oid.value, general_key.param_oid.len); n_pubkey_attr++; - FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_EC_POINT, gost.public.value, gost.public.len); + FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_EC_POINT, general_key.public.value, general_key.public.len); n_pubkey_attr++; - } - else if (pk_type == NID_id_GostR3410_2001) { + } else if (pk_type == NID_id_GostR3410_2001) { type = CKK_GOSTR3410; FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_KEY_TYPE, &type, sizeof(type)); n_pubkey_attr++; - FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_GOSTR3410_PARAMS, gost.param_oid.value, gost.param_oid.len); + FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_GOSTR3410_PARAMS, general_key.param_oid.value, general_key.param_oid.len); n_pubkey_attr++; - FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_VALUE, gost.public.value, gost.public.len); + FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_VALUE, general_key.public.value, general_key.public.len); /* CKA_VALUE of the GOST key has to be in the little endian order */ rv = sc_mem_reverse(pubkey_templ[n_pubkey_attr].pValue, pubkey_templ[n_pubkey_attr].ulValueLen); if (rv) return rv; n_pubkey_attr++; + } else { + util_fatal("Unsupported CK_KEY_TYPE, cannot write public key"); } #endif +#else + util_fatal("No OpenSSL support, cannot write public key"); #endif - } - else - if (opt_object_class == CKO_DATA) { + if (opt_allowed_mechanisms_len > 0) { + FILL_ATTR(pubkey_templ[n_pubkey_attr], + CKA_ALLOWED_MECHANISMS, opt_allowed_mechanisms, + sizeof(CK_MECHANISM_TYPE) * opt_allowed_mechanisms_len); + n_pubkey_attr++; + } + break; + case CKO_SECRET_KEY: + clazz = CKO_SECRET_KEY; + type = CKK_GENERIC_SECRET; + + if (opt_key_type != 0) { + if (strncasecmp(opt_key_type, "AES:", strlen("AES:")) == 0) + type = CKK_AES; + else if (strncasecmp(opt_key_type, "DES3:", strlen("DES3:")) == 0) + type = CKK_DES3; + else if (strncasecmp(opt_key_type, "GENERIC:", strlen("GENERIC:")) == 0) + type = CKK_GENERIC_SECRET; + else if (strncasecmp(opt_key_type, "HKDF:", strlen("HKDF:")) == 0) + type = CKK_HKDF; + else if (strncasecmp(opt_key_type, "CHACHA20", strlen("CHACHA20")) == 0) + type = CKK_CHACHA20; + else if (strncasecmp(opt_key_type, "POLY1305", strlen("POLY1305")) == 0) + type = CKK_POLY1305; + else + util_fatal("Unknown key type: 0x%lX\nSupported key types are AES, DES3, GENERIC, HKDF, CHACHA20 and POLY1305", type); + } + + FILL_ATTR(seckey_templ[0], CKA_CLASS, &clazz, sizeof(clazz)); + FILL_ATTR(seckey_templ[1], CKA_KEY_TYPE, &type, sizeof(type)); + FILL_ATTR(seckey_templ[2], CKA_TOKEN, &_true, sizeof(_true)); + FILL_ATTR(seckey_templ[3], CKA_VALUE, contents, contents_len); + n_seckey_attr = 4; + + if (opt_is_private != 0) { + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_PRIVATE, &_true, sizeof(_true)); + n_seckey_attr++; + } + else { + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_PRIVATE, &_false, sizeof(_false)); + n_seckey_attr++; + } + + if (opt_is_sensitive != 0) { + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_SENSITIVE, &_true, sizeof(_true)); + n_seckey_attr++; + } + else { + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_SENSITIVE, &_false, sizeof(_false)); + n_seckey_attr++; + } + if (opt_is_extractable != 0) { + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_EXTRACTABLE, &_true, sizeof(_true)); + n_seckey_attr++; + } + else { + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_EXTRACTABLE, &_false, sizeof(_false)); + n_seckey_attr++; + } + + if (opt_key_usage_default || opt_key_usage_decrypt) { + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_ENCRYPT, &_true, sizeof(_true)); + n_seckey_attr++; + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_DECRYPT, &_true, sizeof(_true)); + n_seckey_attr++; + } + + if (opt_key_usage_wrap) { + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_WRAP, &_true, sizeof(_true)); + n_seckey_attr++; + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_UNWRAP, &_true, sizeof(_true)); + n_seckey_attr++; + } + + if (opt_key_usage_sign != 0) { + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_SIGN, &_true, sizeof(_true)); + n_seckey_attr++; + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_VERIFY, &_true, sizeof(_true)); + n_seckey_attr++; + } + + if (opt_key_usage_derive != 0) { + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_DERIVE, &_true, sizeof(_true)); + n_seckey_attr++; + } else { + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_DERIVE, &_false, sizeof(_false)); + n_seckey_attr++; + } + + if (opt_object_label != NULL) { + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_LABEL, opt_object_label, strlen(opt_object_label)); + n_seckey_attr++; + } + if (opt_object_id_len != 0) { + FILL_ATTR(seckey_templ[n_seckey_attr], CKA_ID, opt_object_id, opt_object_id_len); + n_seckey_attr++; + } + if (opt_allowed_mechanisms_len > 0) { + FILL_ATTR(seckey_templ[n_seckey_attr], + CKA_ALLOWED_MECHANISMS, opt_allowed_mechanisms, + sizeof(CK_MECHANISM_TYPE) * opt_allowed_mechanisms_len); + n_seckey_attr++; + } + break; + case CKO_DATA: clazz = CKO_DATA; FILL_ATTR(data_templ[0], CKA_CLASS, &clazz, sizeof(clazz)); FILL_ATTR(data_templ[1], CKA_TOKEN, &_true, sizeof(_true)); - FILL_ATTR(data_templ[2], CKA_VALUE, &contents, contents_len); + FILL_ATTR(data_templ[2], CKA_VALUE, contents, contents_len); n_data_attr = 3; @@ -2782,10 +5631,11 @@ static int write_object(CK_SESSION_HANDLE session) FILL_ATTR(data_templ[n_data_attr], CKA_LABEL, opt_object_label, strlen(opt_object_label)); n_data_attr++; } - - } - else + break; + default: util_fatal("Writing of a \"%s\" type not (yet) supported", opt_object_class_str); + break; + } if (n_data_attr) { rv = p11->C_CreateObject(session, data_templ, n_data_attr, &data_obj); @@ -2822,6 +5672,24 @@ static int write_object(CK_SESSION_HANDLE session) show_object(session, privkey_obj); } + if (n_seckey_attr) { + rv = p11->C_CreateObject(session, seckey_templ, n_seckey_attr, &seckey_obj); + if (rv != CKR_OK) + p11_fatal("C_CreateObject", rv); + + printf("Created secret key:\n"); + show_object(session, seckey_obj); + } + +#ifdef ENABLE_OPENSSL + general_key_info_free(general_key); + pqc_key_info_free(pqc_key); + rsa_info_free(rsa); + EVP_PKEY_free(evp_key); +#endif /* ENABLE_OPENSSL */ + + free(contents); + free(certdata); if (oid_buf) free(oid_buf); return 1; @@ -2833,8 +5701,8 @@ static void set_id_attr(CK_SESSION_HANDLE session) CK_ATTRIBUTE templ[] = {{CKA_ID, new_object_id, new_object_id_len}}; CK_RV rv; - if (!find_object(session, opt_object_class, &obj, opt_object_id, opt_object_id_len, 0)) { - fprintf(stderr, "set_id(): couldn't find the object\n"); + if (!find_object_type_or_id_or_label(session, opt_object_class, 1, &obj, opt_object_id, opt_object_id_len, opt_object_label, 0)) { + fprintf(stderr, "set_id(): couldn't find the object by id %s label\n", (opt_object_label && opt_object_id_len) ? "and" : "or"); return; } @@ -2846,6 +5714,72 @@ static void set_id_attr(CK_SESSION_HANDLE session) show_object(session, obj); } +static int +get_default_slot(CK_SLOT_ID_PTR result) +{ + /* use first slot with token present (or default slot on error) */ + CK_RV rv; + for (unsigned int i = 0; i < p11_num_slots; i++) { + CK_SLOT_INFO info; + rv = p11->C_GetSlotInfo(p11_slots[i], &info); + if (rv != CKR_OK) + p11_fatal("C_GetSlotInfo", rv); + if (info.flags & CKF_TOKEN_PRESENT) { + *result = p11_slots[i]; + fprintf(stderr, "Using slot %u with a present token (0x%lx)\n", i, *result); + return 1; + } + } + fprintf(stderr, "No slot with a token was found.\n"); + return 0; +} + +static int +find_slot_by_uri(struct pkcs11_uri *uri, CK_SLOT_ID_PTR result) +{ + CK_SLOT_INFO slot_info; + CK_TOKEN_INFO token_info; + struct attr { + char *uri_attr; + char *actual_attr; + } attr_array[6]; + + for (CK_ULONG n = 0; n < p11_num_slots; n++) { + int invalid = 0; + + if (p11->C_GetSlotInfo(p11_slots[n], &slot_info) != CKR_OK || + p11->C_GetTokenInfo(p11_slots[n], &token_info) != CKR_OK) { + continue; + } + + attr_array[0] = (struct attr){uri->slot_description, p11_utf8_to_string(slot_info.slotDescription, sizeof(slot_info.slotDescription))}; + attr_array[1] = (struct attr){uri->slot_manufacturer, p11_utf8_to_string(slot_info.manufacturerID, sizeof(slot_info.manufacturerID))}; + attr_array[2] = (struct attr){uri->token_manufacturer, p11_utf8_to_string(token_info.manufacturerID, sizeof(token_info.manufacturerID))}; + attr_array[3] = (struct attr){uri->token_model, p11_utf8_to_string(token_info.model, sizeof(token_info.model))}; + attr_array[4] = (struct attr){uri->serial, p11_utf8_to_string(token_info.serialNumber, sizeof(token_info.serialNumber))}; + attr_array[5] = (struct attr){uri->token_label, p11_utf8_to_string(token_info.label, sizeof(token_info.label))}; + + for (int i = 0; i < 6; i++) { + if (!attr_array[i].uri_attr || !attr_array[i].actual_attr) + continue; + if (strlen(attr_array[i].uri_attr) != strlen(attr_array[i].actual_attr) || + strncmp(attr_array[i].actual_attr, attr_array[i].uri_attr, strlen(attr_array[i].actual_attr))) { + invalid = 1; + break; + } + } + if (invalid) + continue; + + for (int i = 0; i < 6; i++) { + free(attr_array[i].actual_attr); + } + *result = p11_slots[n]; + return 1; + } + return 0; +} + static int find_slot_by_description(const char *label, CK_SLOT_ID_PTR result) { CK_SLOT_INFO info; @@ -2896,27 +5830,38 @@ static int find_slot_by_token_label(const char *label, CK_SLOT_ID_PTR result) return 0; } - -static int find_object(CK_SESSION_HANDLE sess, CK_OBJECT_CLASS cls, +static int +find_object_type_or_id_or_label(CK_SESSION_HANDLE sess, + CK_OBJECT_CLASS cls, int cls_set, CK_OBJECT_HANDLE_PTR ret, - const unsigned char *id, size_t id_len, int obj_index) + const unsigned char *id, size_t id_len, + const char *label, + int obj_index) { - CK_ATTRIBUTE attrs[2]; + CK_ATTRIBUTE attrs[3]; unsigned int nattrs = 0; CK_ULONG count; CK_RV rv; int i; - attrs[0].type = CKA_CLASS; - attrs[0].pValue = &cls; - attrs[0].ulValueLen = sizeof(cls); - nattrs++; - if (id) { + if (cls_set) { + attrs[nattrs].type = CKA_CLASS; + attrs[nattrs].pValue = &cls; + attrs[nattrs].ulValueLen = sizeof(cls); + nattrs++; + } + if (id && id_len) { attrs[nattrs].type = CKA_ID; attrs[nattrs].pValue = (void *) id; attrs[nattrs].ulValueLen = id_len; nattrs++; } + if (label) { + attrs[nattrs].type = CKA_LABEL; + attrs[nattrs].pValue = (void *) label; + attrs[nattrs].ulValueLen = strlen(label); + nattrs++; + } rv = p11->C_FindObjectsInit(sess, attrs, nattrs); if (rv != CKR_OK) @@ -2933,11 +5878,59 @@ static int find_object(CK_SESSION_HANDLE sess, CK_OBJECT_CLASS cls, if (rv != CKR_OK) p11_fatal("C_FindObjects", rv); -done: if (count == 0) +done: + if (count == 0) *ret = CK_INVALID_HANDLE; p11->C_FindObjectsFinal(sess); - return count; + return (int)count; +} + +static int find_object(CK_SESSION_HANDLE sess, CK_OBJECT_CLASS cls, + CK_OBJECT_HANDLE_PTR ret, + const unsigned char *id, size_t id_len, const char *label, int obj_index) +{ + return find_object_type_or_id_or_label(sess, cls, 1, ret, id, id_len, label, obj_index); +} + +static int find_object_flags(CK_SESSION_HANDLE sess, uint16_t mf_flags, + CK_OBJECT_HANDLE_PTR ret, + const unsigned char *id, size_t id_len, const char *label, int obj_index) +{ + int count; + char err_key_types[1024] = {0}; + + if (mf_flags & MF_CKO_SECRET_KEY) { + count = find_object(sess, CKO_SECRET_KEY, ret, id, id_len, label, obj_index); + if (count) + return count; + + strncat(err_key_types, "Secret", sizeof(err_key_types) - 1); + } + + util_fatal("Could not find key of type: %s", err_key_types); +} + +static int +find_object_flags_and_type_or_id_or_label(CK_SESSION_HANDLE sess, + uint16_t mf_flags, CK_OBJECT_HANDLE_PTR ret, + CK_OBJECT_CLASS cls, int cls_set, + const unsigned char *id, size_t id_len, const char *label, int obj_index) +{ + int count; + char err_key_types[1024] = {0}; + + if (mf_flags & MF_CKO_SECRET_KEY) { + if (!cls_set || (cls_set && cls == CKO_SECRET_KEY)) { + count = find_object_type_or_id_or_label(sess, cls, cls_set, ret, id, id_len, label, obj_index); + if (count) + return count; + } + + strncat(err_key_types, "Secret", sizeof(err_key_types) - 1); + } + + util_fatal("Could not find key of type: %s", err_key_types); } static CK_RV find_object_with_attributes(CK_SESSION_HANDLE session, @@ -2978,7 +5971,7 @@ static CK_RV find_object_with_attributes(CK_SESSION_HANDLE session, } -static int +static CK_ULONG find_mechanism(CK_SLOT_ID slot, CK_FLAGS flags, CK_MECHANISM_TYPE_PTR list, size_t list_len, CK_MECHANISM_TYPE_PTR result) @@ -2989,7 +5982,7 @@ find_mechanism(CK_SLOT_ID slot, CK_FLAGS flags, count = get_mechanisms(slot, &mechs, flags); if (count) { if (list && list_len) { - unsigned ii, jj; + size_t ii = list_len, jj; for (jj=0; jjC_FindObjectsInit(sess, NULL, 0); + if (opt_object_class_str != NULL) { + FILL_ATTR(attrs[nn_attrs], CKA_CLASS, + &opt_object_class, sizeof(opt_object_class)); + nn_attrs++; + } + + if (opt_object_id_len != 0) { + FILL_ATTR(attrs[nn_attrs], CKA_ID, + opt_object_id, opt_object_id_len); + nn_attrs++; + } + + if (opt_object_label != NULL) { + FILL_ATTR(attrs[nn_attrs], CKA_LABEL, + opt_object_label, strlen(opt_object_label)); + nn_attrs++; + } + + if (opt_application_label != NULL) { + FILL_ATTR(attrs[nn_attrs], CKA_APPLICATION, + opt_application_label, strlen(opt_application_label)); + nn_attrs++; + } + + rv = p11->C_FindObjectsInit(sess, attrs, nn_attrs); if (rv != CKR_OK) p11_fatal("C_FindObjectsInit", rv); @@ -3030,8 +6049,8 @@ static void list_objects(CK_SESSION_HANDLE sess, CK_OBJECT_CLASS object_class) p11_fatal("C_FindObjects", rv); if (count == 0) break; - if ((int) object_class == -1 || object_class == getCLASS(sess, object)) - show_object(sess, object); + + show_object(sess, object); } p11->C_FindObjectsFinal(sess); } @@ -3052,6 +6071,9 @@ static void show_object(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) case CKO_DATA: show_dobj(sess, obj); break; + case CKO_PROFILE: + show_profile(sess, obj); + break; default: printf("Object %u, type %u\n", (unsigned int) obj, @@ -3063,31 +6085,31 @@ static void show_object(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) static CK_OBJECT_HANDLE derive_ec_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key, CK_MECHANISM_TYPE mech_mech) { -#if defined(ENABLE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) +#if defined(ENABLE_OPENSSL) && !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) CK_MECHANISM mech; CK_OBJECT_CLASS newkey_class= CKO_SECRET_KEY; CK_KEY_TYPE newkey_type = CKK_GENERIC_SECRET; - CK_BBOOL true = TRUE; - CK_BBOOL false = FALSE; + CK_BBOOL _true = TRUE; + CK_BBOOL _false = FALSE; CK_OBJECT_HANDLE newkey = 0; + + // clang-format off CK_ATTRIBUTE newkey_template[20] = { - {CKA_TOKEN, &false, sizeof(false)}, /* session only object */ - {CKA_CLASS, &newkey_class, sizeof(newkey_class)}, - {CKA_KEY_TYPE, &newkey_type, sizeof(newkey_type)}, - {CKA_SENSITIVE, &false, sizeof(false)}, - {CKA_EXTRACTABLE, &true, sizeof(true)}, - {CKA_ENCRYPT, &true, sizeof(true)}, - {CKA_DECRYPT, &true, sizeof(true)}, - {CKA_WRAP, &true, sizeof(true)}, - {CKA_UNWRAP, &true, sizeof(true)} - }; + {CKA_TOKEN, &_false, sizeof(_false) }, /* session only object */ + {CKA_CLASS, &newkey_class, sizeof(newkey_class)}, + {CKA_KEY_TYPE, &newkey_type, sizeof(newkey_type) }, + {CKA_SENSITIVE, &_false, sizeof(_false) }, + {CKA_EXTRACTABLE, &_true, sizeof(_true) }, + {CKA_ENCRYPT, &_true, sizeof(_true) }, + {CKA_DECRYPT, &_true, sizeof(_true) }, + {CKA_WRAP, &_true, sizeof(_true) }, + {CKA_UNWRAP, &_true, sizeof(_true) } + }; + // clang-format on int n_attrs = 9; CK_ECDH1_DERIVE_PARAMS ecdh_parms; CK_RV rv; BIO *bio_in = NULL; - EC_KEY *eckey = NULL; - const EC_GROUP *ecgroup = NULL; - const EC_POINT *ecpoint = NULL; unsigned char *buf = NULL; size_t buf_size = 0; CK_ULONG key_len = 0; @@ -3095,8 +6117,19 @@ derive_ec_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key, CK_MECHANISM_TYPE unsigned char * der = NULL; unsigned char * derp = NULL; size_t der_size = 0; + EVP_PKEY *pkey = NULL; /* peer key */ + int key_id = 0; /* nid of peer key must match nid of key */ +#if OPENSSL_VERSION_NUMBER < 0x30000000L + EC_KEY *eckey = NULL; + const EC_GROUP *ecgroup = NULL; + const EC_POINT *ecpoint = NULL; +#else + EC_GROUP *ecgroup = NULL; + char name[256]; size_t len = 0; + int nid = 0; +#endif - printf("Using derive algorithm 0x%8.8lx %s\n", opt_mechanism, p11_mechanism_to_name(mech_mech)); + printf("Using derive algorithm 0x%8.8lx %s\n", mech_mech, p11_mechanism_to_name(mech_mech)); memset(&mech, 0, sizeof(mech)); mech.mechanism = mech_mech; @@ -3105,32 +6138,107 @@ derive_ec_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key, CK_MECHANISM_TYPE if (BIO_read_filename(bio_in, opt_input) <= 0) util_fatal("Cannot open %s: %m", opt_input); - eckey = d2i_EC_PUBKEY_bio(bio_in, NULL); - if (!eckey) - util_fatal("Cannot read EC key from %s", opt_input); +#if OPENSSL_VERSION_NUMBER >= 0x30200000L + pkey = d2i_PUBKEY_ex_bio(bio_in, NULL, osslctx, NULL); +#else + pkey = d2i_PUBKEY_bio(bio_in, NULL); +#endif + + if (!pkey) + util_fatal("Cannot read peer EC key from %s", opt_input); + + key_id = EVP_PKEY_id(pkey); + if (key_id == 0) + util_fatal("Unknown key type of peer key"); + + switch (key_id) { + case EVP_PKEY_EC: /* CKK_EC need to get curves of pkey and key */ + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + eckey = EVP_PKEY_get0_EC_KEY(pkey); + if (eckey) { + ecpoint = EC_KEY_get0_public_key(eckey); + ecgroup = EC_KEY_get0_group(eckey); + } + + if (!eckey || !ecpoint || !ecgroup) + util_fatal("Failed to parse peer EC key from %s", opt_input); +#else + if (EVP_PKEY_get_group_name(pkey, name, sizeof(name), &len) != 1 || + (nid = OBJ_txt2nid(name)) == NID_undef || + (ecgroup = EC_GROUP_new_by_curve_name(nid)) == NULL) + util_fatal("Failed to parse peer EC key from %s", opt_input); +#endif + + /* both eckeys must be same curve */ + key_len = BYTES4BITS(EC_GROUP_get_degree(ecgroup)); + FILL_ATTR(newkey_template[n_attrs], CKA_VALUE_LEN, &key_len, sizeof(key_len)); + n_attrs++; + + if (opt_allowed_mechanisms_len > 0) { + FILL_ATTR(newkey_template[n_attrs], + CKA_ALLOWED_MECHANISMS, opt_allowed_mechanisms, + sizeof(CK_MECHANISM_TYPE) * opt_allowed_mechanisms_len); + n_attrs++; + } - ecpoint = EC_KEY_get0_public_key(eckey); - ecgroup = EC_KEY_get0_group(eckey); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + buf_size = EC_POINT_point2oct(ecgroup, ecpoint, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); + if (buf_size == 0) + util_fatal("Failed to parse peer EC key \n"); + buf = (unsigned char *)malloc(buf_size); + if (buf == NULL) + util_fatal("malloc() failure\n"); + buf_size = EC_POINT_point2oct(ecgroup, ecpoint, POINT_CONVERSION_UNCOMPRESSED, buf, buf_size, NULL); + if (buf_size == 0) + util_fatal("Failed to parse peer EC key \n"); +#else + EC_GROUP_free(ecgroup); + if (EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0, &buf_size) != 1) + util_fatal("Failed to get size of public key\n"); + if (buf_size == 0) + util_fatal("Failed to get size of public key\n"); + if ((buf = (unsigned char *)malloc(buf_size)) == NULL) + util_fatal("malloc() failure\n"); - if (!ecpoint || !ecgroup) - util_fatal("Failed to parse other EC key from %s", opt_input); + if (EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, buf, buf_size, NULL) != 1) { + free(buf); + util_fatal("Failed to parse other EC key from %s", opt_input); + } +#endif + if (mech_mech != CKM_ECDH1_DERIVE && mech_mech != CKM_ECDH1_COFACTOR_DERIVE) + util_fatal("Peer key %s not usable with %s", "CKK_EC", p11_mechanism_to_name(mech_mech)); + break; - /* both eckeys must be same curve */ - key_len = (EC_GROUP_get_degree(ecgroup) + 7) / 8; - FILL_ATTR(newkey_template[n_attrs], CKA_VALUE_LEN, &key_len, sizeof(key_len)); - n_attrs++; +#if defined(EVP_PKEY_X25519) + case EVP_PKEY_X25519: /* "CKK_EC_MONTGOMERY */ +#if defined(EVP_PKEY_X448) + case EVP_PKEY_X448: +#endif + if (EVP_PKEY_get_raw_public_key(pkey, NULL, &buf_size) != 1) + util_fatal("Unable to get public key of peer\n"); + if (buf_size == 0) + util_fatal("Unable to get public key of peer\n"); + buf = (unsigned char *)malloc(buf_size); + if (buf == NULL) + util_fatal("malloc() failure\n"); + if (EVP_PKEY_get_raw_public_key(pkey, buf, &buf_size) != 1) + util_fatal("Unable to get public key of peer\n"); - buf_size = EC_POINT_point2oct(ecgroup, ecpoint, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); - buf = (unsigned char *)malloc(buf_size); - if (buf == NULL) - util_fatal("malloc() failure\n"); - buf_size = EC_POINT_point2oct(ecgroup, ecpoint, POINT_CONVERSION_UNCOMPRESSED, buf, buf_size, NULL); + if (mech_mech != CKM_ECDH1_DERIVE) + util_fatal("Peer key %s not usable with %s", "CKK_EC_MONTGOMERY", p11_mechanism_to_name(mech_mech)); + break; +#endif /* defined(EVP_PKEY_X25519) */ + default: + util_fatal("Peer key not usable with derive or unknown %i", key_id); + break; + } if (opt_derive_pass_der) { octet = ASN1_OCTET_STRING_new(); if (octet == NULL) util_fatal("ASN1_OCTET_STRING_new failure\n"); - ASN1_OCTET_STRING_set(octet, buf, buf_size); + ASN1_OCTET_STRING_set(octet, buf, (int)buf_size); der_size = i2d_ASN1_OCTET_STRING(octet, NULL); derp = der = (unsigned char *) malloc(der_size); if (der == NULL) @@ -3139,7 +6247,7 @@ derive_ec_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key, CK_MECHANISM_TYPE } BIO_free(bio_in); - EC_KEY_free(eckey); + EVP_PKEY_free(pkey); memset(&ecdh_parms, 0, sizeof(ecdh_parms)); ecdh_parms.kdf = CKD_NULL; @@ -3159,40 +6267,178 @@ derive_ec_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key, CK_MECHANISM_TYPE if (rv != CKR_OK) p11_fatal("C_DeriveKey", rv); - if (der) - OPENSSL_free(der); - if (buf) - free(buf); + free(der); + free(buf); if (octet) ASN1_OCTET_STRING_free(octet); return newkey; -#else - util_fatal("Derive EC key not supported"); - return 0; -#endif /* ENABLE_OPENSSL && !OPENSSL_NO_EC && !OPENSSL_NO_ECDSA */ +#else + util_fatal("Derive EC key not supported"); + return 0; +#endif /* ENABLE_OPENSSL && !OPENSSL_NO_EC && !OPENSSL_NO_ECDSA */ +} + +static CK_BBOOL s_true = TRUE; +static CK_BBOOL s_false = FALSE; + +#define FILL_ATTR_EX(attr, index, max, typ, val, len) \ + { \ + if (*(index) >= max) { \ + util_fatal("Template is too small"); \ + } \ + (attr)[*(index)].type = (typ); \ + (attr)[*(index)].pValue = (val); \ + (attr)[*(index)].ulValueLen = (len); \ + (*(index))++; \ + } + +static void +fill_attributes_seckey(CK_ATTRIBUTE *template, int *n_attrs, int max_attrs) +{ + FILL_ATTR_EX(template, n_attrs, max_attrs, CKA_PRIVATE, opt_is_private ? &s_true : &s_false, sizeof(CK_BBOOL)); + FILL_ATTR_EX(template, n_attrs, max_attrs, CKA_SENSITIVE, opt_is_sensitive ? &s_true : &s_false, sizeof(CK_BBOOL)); + FILL_ATTR_EX(template, n_attrs, max_attrs, CKA_EXTRACTABLE, opt_is_extractable ? &s_true : &s_false, sizeof(CK_BBOOL)); + + if (opt_key_usage_default || opt_key_usage_decrypt) { + FILL_ATTR_EX(template, n_attrs, max_attrs, CKA_ENCRYPT, &s_true, sizeof(CK_BBOOL)); + FILL_ATTR_EX(template, n_attrs, max_attrs, CKA_DECRYPT, &s_true, sizeof(CK_BBOOL)); + } + + FILL_ATTR_EX(template, n_attrs, max_attrs, CKA_WRAP, opt_key_usage_wrap ? &s_true : &s_false, sizeof(CK_BBOOL)); + FILL_ATTR_EX(template, n_attrs, max_attrs, CKA_UNWRAP, opt_key_usage_wrap ? &s_true : &s_false, sizeof(CK_BBOOL)); + FILL_ATTR_EX(template, n_attrs, max_attrs, CKA_SIGN, opt_key_usage_sign ? &s_true : &s_false, sizeof(CK_BBOOL)); + FILL_ATTR_EX(template, n_attrs, max_attrs, CKA_VERIFY, opt_key_usage_sign ? &s_true : &s_false, sizeof(CK_BBOOL)); + FILL_ATTR_EX(template, n_attrs, max_attrs, CKA_DERIVE, opt_key_usage_derive ? &s_true : &s_false, sizeof(CK_BBOOL)); +} + +static CK_OBJECT_HANDLE +derive_hkdf(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) +{ + CK_MECHANISM mech; + CK_KEY_TYPE key_type = CKK_GENERIC_SECRET; + CK_ULONG key_length = hash_length(opt_hash_alg); + CK_OBJECT_HANDLE newkey = 0; + CK_ATTRIBUTE template[13] = { + {CKA_TOKEN, &s_false, sizeof(s_false) }, /* session only object */ + {CKA_KEY_TYPE, &key_type, sizeof(key_type) }, + {CKA_VALUE_LEN, &key_length, sizeof(key_length)} + }; + int n_attrs = 3; + CK_RV rv; + CK_HKDF_PARAMS hkdf_params; + void *salt = NULL; + ssize_t salt_len = 0; + void *info = NULL; + ssize_t info_len = 0; + + if (opt_key_type != NULL) { + if (strncasecmp(opt_key_type, "GENERIC:", strlen("GENERIC:")) != 0) { + util_fatal("Generic key type expected\n"); + } + const char *size = opt_key_type + strlen("GENERIC:"); + key_length = (unsigned long)atol(size); + if (key_length == 0) + util_fatal("Unknown key type %s, expecting GENERIC:", opt_key_type); + } + + fill_attributes_seckey(template, &n_attrs, ARRAY_SIZE(template)); + + if (opt_salt_file != NULL) { + FILE *f; + + f = fopen(opt_salt_file, "rb"); + if (f == NULL) + util_fatal("Cannot open %s: %m", opt_salt_file); + if (fseek(f, 0L, SEEK_END) != 0) + util_fatal("Couldn't set file position to the end of the file \"%s\"", opt_salt_file); + salt_len = ftell(f); + if (salt_len < 0) + util_fatal("Couldn't get file position \"%s\"", opt_salt_file); + salt = malloc(salt_len); + if (salt == NULL) + util_fatal("malloc() failure\n"); + if (fseek(f, 0L, SEEK_SET) != 0) + util_fatal("Couldn't set file position to the beginning of the file \"%s\"", opt_salt_file); + size_t ret = fread(salt, 1, salt_len, f); + if (ret != (size_t)salt_len) + util_fatal("Couldn't read from file \"%s\"", opt_salt_file); + fclose(f); + } + if (opt_info_file != NULL) { + FILE *f; + + f = fopen(opt_info_file, "rb"); + if (f == NULL) + util_fatal("Cannot open %s: %m", opt_info_file); + if (fseek(f, 0L, SEEK_END) != 0) + util_fatal("Couldn't set file position to the end of the file \"%s\"", opt_info_file); + info_len = ftell(f); + if (info_len < 0) + util_fatal("Couldn't get file position \"%s\"", opt_info_file); + info = malloc(info_len); + if (info == NULL) + util_fatal("malloc() failure\n"); + if (fseek(f, 0L, SEEK_SET) != 0) + util_fatal("Couldn't set file position to the beginning of the file \"%s\"", opt_info_file); + size_t ret = fread(info, 1, info_len, f); + if (ret != (size_t)info_len) + util_fatal("Couldn't read from file \"%s\"", opt_info_file); + fclose(f); + } + + memset(&hkdf_params, 0, sizeof(hkdf_params)); + hkdf_params.bExtract = TRUE; + hkdf_params.bExpand = TRUE; + hkdf_params.prfHashMechanism = opt_hash_alg; + if (salt == NULL) { + hkdf_params.ulSaltType = CKF_HKDF_SALT_NULL; + } else { + hkdf_params.ulSaltType = CKF_HKDF_SALT_DATA; + } + hkdf_params.pSalt = salt; + hkdf_params.ulSaltLen = (CK_ULONG)salt_len; + hkdf_params.hSaltKey = CK_INVALID_HANDLE; + hkdf_params.pInfo = info; + hkdf_params.ulInfoLen = (CK_ULONG)info_len; + + mech.mechanism = CKM_HKDF_DERIVE; + mech.pParameter = &hkdf_params; + mech.ulParameterLen = sizeof(hkdf_params); + + rv = p11->C_DeriveKey(session, &mech, key, template, n_attrs, &newkey); + if (rv != CKR_OK) + p11_fatal("C_DeriveKey", rv); + + free(salt); + free(info); + return newkey; } - static void derive_key(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) { CK_BYTE *value = NULL; CK_ULONG value_len = 0; CK_OBJECT_HANDLE derived_key = 0; - int rv, fd; + int fd; + ssize_t sz; + CK_KEY_TYPE key_type = getKEY_TYPE(session, key); if (!opt_mechanism_used) - if (!find_mechanism(slot, CKF_DERIVE|CKF_HW, NULL, 0, &opt_mechanism)) + if (!find_mechanism(slot, CKF_DERIVE|opt_allow_sw, NULL, 0, &opt_mechanism)) util_fatal("Derive mechanism not supported"); switch(opt_mechanism) { case CKM_ECDH1_COFACTOR_DERIVE: case CKM_ECDH1_DERIVE: - derived_key= derive_ec_key(session, key, opt_mechanism); + derived_key = derive_ec_key(session, key, opt_mechanism); + break; + case CKM_HKDF_DERIVE: + derived_key = derive_hkdf(session, key); break; default: - util_fatal("mechanism not supported for derive"); + util_fatal("Key type %lu does not support derive", key_type); break; } @@ -3205,8 +6451,9 @@ derive_key(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) util_fatal("failed to open %s: %m", opt_output); } - rv = write(fd, value, value_len); - if (rv < 0) + sz = write(fd, value, value_len); + free(value); + if (sz < 0) util_fatal("Failed to write to %s: %m", opt_output); if (opt_output) @@ -3214,17 +6461,50 @@ derive_key(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) } } +#define BYTES_PER_LINE 32 +void +print_hex(const u8 *bin_input, size_t input_size, int separator, bool newline) +{ + char out[BYTES_PER_LINE * 3] = {0}; + size_t out_len = BYTES_PER_LINE * 3; + unsigned int dec = 0; + unsigned int n, i; + + /* If it is small enough number, convert it to deccimal */ + if (input_size <= sizeof(unsigned int)) { + for (i = 0; i < input_size; i++) { + dec = (dec << 8) + (bin_input[i] & 0xff); + } + if (dec != 0) { + printf("%u (0x", dec); + } + } + + for (n = 0; n < input_size; n += BYTES_PER_LINE) { + size_t chunk_len = MIN(input_size - n, BYTES_PER_LINE); + const char *indent = n + chunk_len < input_size + ? "\n " /* continuation of block */ + : (newline ? "\n" : ""); /* end of the block */ + sc_bin_to_hex(bin_input + n, chunk_len, out, out_len, separator); + printf("%s%s%s", out, (dec != 0 ? ")" : ""), indent); + } +} static void show_key(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) { + CK_MECHANISM_TYPE_PTR mechs = NULL; CK_KEY_TYPE key_type = getKEY_TYPE(sess, obj); - CK_ULONG size = 0; + CK_ULONG size, idsize = 0; unsigned char *id, *oid, *value; const char *sepa; char *label; + char *unique_id; int pub = 1; int sec = 0; + CK_TOKEN_INFO info; + CK_ULONG parameter_set; + int kem = 0; switch(getCLASS(sess, obj)) { case CKO_PRIVATE_KEY: @@ -3245,98 +6525,158 @@ show_key(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) switch (key_type) { case CKK_RSA: - if (pub) - printf("; RSA %lu bits\n", - (unsigned long) getMODULUS_BITS(sess, obj)); - else - printf("; RSA \n"); + if (sec) { + /* uninitialized secret key (type 0) */ + printf("\n"); + } else { + unsigned char *modulus = NULL, *public_exponent = NULL; + CK_ULONG modulus_len, public_exponent_len; + unsigned long modulus_bits = 0; + + printf("; RSA "); + modulus = getMODULUS(sess, obj, &modulus_len); + + if (pub && (modulus_bits = getMODULUS_BITS(sess, obj)) != 0) { + printf(" %lu bits", modulus_bits); + } else if (modulus != NULL) { + /* estimate, for private key or missing*/ + printf(" %lu bits", modulus_len * 8); + } + printf("\n"); + if (modulus) { + printf(" Modulus: "); + print_hex(modulus, modulus_len, 0, true); + free(modulus); + } + public_exponent = getPUBLIC_EXPONENT(sess, obj, &public_exponent_len); + if (public_exponent) { + printf(" Public exp: "); + print_hex(public_exponent, public_exponent_len, 0, true); + free(public_exponent); + } + } break; case CKK_GOSTR3410: - printf("; GOSTR3410 \n"); - oid = getGOSTR3410_PARAMS(sess, obj, &size); + case CKK_GOSTR3410_512: + oid = getGOSTR3411_PARAMS(sess, obj, &size); if (oid) { - unsigned int n; + if (size == GOST_HASH2001_PARAMSET_OID.len && !memcmp(oid, GOST_HASH2001_PARAMSET_OID.value, size)) + printf("; GOSTR3410\n"); + else if (size == GOST_HASH2012_256_PARAMSET_OID.len && !memcmp(oid, GOST_HASH2012_256_PARAMSET_OID.value, size)) + printf("; GOSTR3410-2012-256\n"); + else if (size == GOST_HASH2012_512_PARAMSET_OID.len && !memcmp(oid, GOST_HASH2012_512_PARAMSET_OID.value, size)) + printf("; GOSTR3410-2012-512\n"); + else + printf("; unknown GOSTR3410 algorithm\n"); + free(oid); + } else { + printf("; unknown GOSTR3410 algorithm\n"); + } - printf(" PARAMS OID: "); - for (n = 0; n < size; n++) - printf("%02x", oid[n]); - printf("\n"); + oid = getGOSTR3410_PARAMS(sess, obj, &size); + if (oid) { + printf(" Params OID: "); + print_hex(oid, size, ':', true); free(oid); } if (pub) { value = getVALUE(sess, obj, &size); if (value) { - unsigned int n; - - printf(" VALUE: "); - for (n = 0; n < size; n++) { - if (n && (n%32)==0) - printf("\n "); - printf("%02x", value[n]); - } - printf("\n"); + printf(" Value: "); + print_hex(value, size, 0, true); free(value); } } break; - break; case CKK_EC: - printf("; EC"); + case CKK_EC_EDWARDS: + case CKK_EC_MONTGOMERY: + if (key_type == CKK_EC_EDWARDS) { + printf("; EC_EDWARDS"); + } else if (key_type == CKK_EC_MONTGOMERY) { + printf("; EC_MONTGOMERY"); + } else { + printf("; EC"); + } if (pub) { - unsigned char *bytes = NULL; + unsigned char *point_bytes = NULL; + CK_ULONG point_size = 0; + unsigned char *params_bytes = NULL; + CK_ULONG params_size = 0; + const struct ec_curve_info *curve_info = NULL; + unsigned int n; - int ksize; - bytes = getEC_POINT(sess, obj, &size); + point_bytes = getEC_POINT(sess, obj, &point_size); + params_bytes = getEC_PARAMS(sess, obj, ¶ms_size); + + /* get field length from known EC curves */ + curve_info = match_ec_curve_by_params(params_bytes, params_size); + if (curve_info) + printf(" EC_POINT %lu bits\n", curve_info->size); + else + printf(" EC_POINT size unknown\n"); + /* - * (We only support uncompressed for now) - * Uncompresed EC_POINT is DER OCTET STRING of "04||x||y" - * So a "256" bit key has x and y of 32 bytes each - * something like: "04 41 04||x||y" - * Do simple size calculation based on DER encoding + * PKCS11 EC_POINT is "DER-encoding of ANSI X9.62 ECPoint value Q" + * "ECPoint ::= OCTET STRING" */ - if ((size - 2) <= 127) - ksize = (size - 3) * 4; - else if ((size - 3) <= 255) - ksize = (size - 4) * 4; - else - ksize = (size - 5) * 4; - - printf(" EC_POINT %d bits\n", ksize); - if (bytes) { - if ((CK_LONG)size > 0) { /* Will print the point here */ - printf(" EC_POINT: "); - for (n = 0; n < size; n++) - printf("%02x", bytes[n]); - printf("\n"); - } - free(bytes); + if (point_bytes && point_size) { + printf(" EC Point: "); + print_hex(point_bytes, point_size, 0, true); } - bytes = NULL; - bytes = getEC_PARAMS(sess, obj, &size); - if (bytes){ - if ((CK_LONG)size > 0) { - printf(" EC_PARAMS: "); - for (n = 0; n < size; n++) - printf("%02x", bytes[n]); + + if (params_bytes && params_size > 0) { + struct sc_object_id oid; + + printf(" EC Params: "); + print_hex(params_bytes, params_size, ':', false); + + if (curve_info) { /* we matched it above, use printable OID */ + printf(" (\"%s\" OID:\"%s\")\n", curve_info->name, curve_info->oid); + } else if (params_bytes[0] == SC_ASN1_OBJECT) { + sc_init_oid(&oid); + if (sc_asn1_decode_object_id(params_bytes, params_size, &oid) == SC_SUCCESS) { + printf(" (OID: \"%i", oid.value[0]); + if (oid.value[0] >= 0) { + for (n = 1; (n < SC_MAX_OBJECT_ID_OCTETS) && (oid.value[n] >= 1); n++) { + printf(".%i", oid.value[n]); + } + } + printf("\""); + } printf("\n"); } - free(bytes); + } else { + printf(" EC_PARAMS: not available\n"); } - } else - printf("\n"); + + free(point_bytes); + free(params_bytes); + } else { + printf("\n"); + } break; case CKK_GENERIC_SECRET: case CKK_AES: case CKK_DES: case CKK_DES3: + case CKK_HKDF: + case CKK_CHACHA20: + case CKK_POLY1305: if (key_type == CKK_AES) printf("; AES"); else if (key_type == CKK_DES) printf("; DES"); else if (key_type == CKK_DES3) printf("; DES3"); + else if (key_type == CKK_HKDF) + printf("; HKDF"); + else if (key_type == CKK_CHACHA20) + printf("; CHACHA20"); + else if (key_type == CKK_POLY1305) + printf("; POLY1305"); else printf("; Generic secret"); size = getVALUE_LEN(sess, obj); @@ -3346,18 +6686,102 @@ show_key(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) printf("\n"); value = getVALUE(sess, obj, &size); if (value) { - unsigned int n; - - printf(" VALUE: "); - for (n = 0; n < size; n++) { - if (n && (n%32)==0) - printf("\n "); - printf("%02x", value[n]); - } - printf("\n"); + printf(" Value: "); + print_hex(value, size, 0, true); free(value); } break; + case CKK_ML_DSA: + case CKK_ML_KEM: + case CKK_SLH_DSA: + parameter_set = getPARAMETER_SET(sess, obj); + switch (key_type) { + case CKK_ML_DSA: + switch (parameter_set) { + case CKP_ML_DSA_44: + printf("; ML-DSA-44\n"); + break; + case CKP_ML_DSA_65: + printf("; ML-DSA-65\n"); + break; + case CKP_ML_DSA_87: + printf("; ML-DSA-87\n"); + break; + default: + printf("; ML-DSA: Unknown PARAMETER_SET\n"); + break; + } + break; + case CKK_ML_KEM: + kem = 1; + switch (parameter_set) { + case CKP_ML_KEM_512: + printf("; ML-KEM-512\n"); + break; + case CKP_ML_KEM_768: + printf("; ML-KEM-768\n"); + break; + case CKP_ML_KEM_1024: + printf("; ML-KEM-1024\n"); + break; + default: + printf("; ML-KEM: Unknown PARAMETER_SET\n"); + break; + } + break; + case CKK_SLH_DSA: + switch (parameter_set) { + case CKP_SLH_DSA_SHA2_128S: + printf("; SLH-DSA-SHA2-128S\n"); + break; + case CKP_SLH_DSA_SHAKE_128S: + printf("; SLH-DSA-SHAKE-128S\n"); + break; + case CKP_SLH_DSA_SHA2_128F: + printf("; SLH-DSA-SHA2-128F\n"); + break; + case CKP_SLH_DSA_SHAKE_128F: + printf("; SLH-DSA-SHAKE-128F\n"); + break; + case CKP_SLH_DSA_SHA2_192S: + printf("; SLH-DSA-SHA2-192S\n"); + break; + case CKP_SLH_DSA_SHAKE_192S: + printf("; SLH-DSA-SHAKE-192S\n"); + break; + case CKP_SLH_DSA_SHA2_192F: + printf("; SLH-DSA-SHA2-192F\n"); + break; + case CKP_SLH_DSA_SHAKE_192F: + printf("; SLH-DSA-SHAKE-192F\n"); + break; + case CKP_SLH_DSA_SHA2_256S: + printf("; SLH-DSA-SHA2-256S\n"); + break; + case CKP_SLH_DSA_SHAKE_256S: + printf("; SLH-DSA-SHAKE-256S\n"); + break; + case CKP_SLH_DSA_SHA2_256F: + printf("; SLH-DSA-SHA2-256F\n"); + break; + case CKP_SLH_DSA_SHAKE_256F: + printf("; SLH-DSA-SHAKE-256F\n"); + break; + default: + printf("; SLH-DSA: Unknown PARAMETER_SET\n"); + break; + } + break; + } + if (pub) { + value = getVALUE(sess, obj, &size); + if (value) { + printf(" Value: "); + print_hex(value, size, 0, true); + free(value); + } + } + break; default: printf("; unknown key algorithm %lu\n", (unsigned long) key_type); @@ -3366,17 +6790,11 @@ show_key(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) if ((label = getLABEL(sess, obj, NULL)) != NULL) { printf(" label: %s\n", label); - free(label); } - if ((id = getID(sess, obj, &size)) != NULL && size) { - unsigned int n; - + if ((id = getID(sess, obj, &idsize)) != NULL && idsize) { printf(" ID: "); - for (n = 0; n < size; n++) - printf("%02x", id[n]); - printf("\n"); - free(id); + print_hex(id, idsize, ':', true); } printf(" Usage: "); @@ -3389,10 +6807,14 @@ show_key(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) printf("%sdecrypt", sepa); sepa = ", "; } - if (!pub && getSIGN(sess, obj)) { + if ((!pub || sec) && getSIGN(sess, obj)) { printf("%ssign", sepa); sepa = ", "; } + if (!pub && getSIGN_RECOVER(sess, obj)) { + printf("%ssignRecover", sepa); + sepa = ", "; + } suppress_warn = 1; if (!pub && getOPENSC_NON_REPUDIATION(sess, obj)) { @@ -3405,6 +6827,10 @@ show_key(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) printf("%sverify", sepa); sepa = ", "; } + if (pub && !sec && getVERIFY_RECOVER(sess, obj)) { + printf("%sverifyRecover", sepa); + sepa = ", "; + } if ((pub || sec) && getWRAP(sess, obj)) { printf("%swrap", sepa); sepa = ", "; @@ -3417,12 +6843,86 @@ show_key(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) printf("%sderive", sepa); sepa = ", "; } + if (pub && kem && getENCAPSULATE(sess, obj)) { + printf("%sencapsulate", sepa); + sepa = ", "; + } + if (!pub && !sec && kem && getDECAPSULATE(sess, obj)) { + printf("%sdecapsulate", sepa); + sepa = ", "; + } + if (!*sepa) + printf("none"); + printf("\n"); + + printf(" Access: "); + sepa = ""; + if (!pub && getALWAYS_AUTHENTICATE(sess, obj)) { + printf("%salways authenticate", sepa); + sepa = ", "; + } + if (!pub || sec) { + if (getSENSITIVE(sess, obj)) { + printf("%ssensitive", sepa); + sepa = ", "; + } + if (getALWAYS_SENSITIVE(sess, obj)) { + printf("%salways sensitive", sepa); + sepa = ", "; + } + if (getEXTRACTABLE(sess, obj)) { + printf("%sextractable", sepa); + sepa = ", "; + } + if (getNEVER_EXTRACTABLE(sess, obj)) { + printf("%snever extractable", sepa); + sepa = ", "; + } + } + if (getLOCAL(sess, obj)) { + printf("%slocal", sepa); + sepa = ", "; + } if (!*sepa) printf("none"); printf("\n"); - if (!pub && getALWAYS_AUTHENTICATE(sess, obj)) - printf(" Access: always authenticate\n"); + mechs = getALLOWED_MECHANISMS(sess, obj, &size); + if (mechs && size) { + unsigned int n; + + printf(" Allowed mechanisms: "); + for (n = 0; n < size; n++) { + printf("%s%s", (n != 0 ? "," : ""), + p11_mechanism_to_name(mechs[n])); + } + printf("\n"); + } + free(mechs); + if ((unique_id = getUNIQUE_ID(sess, obj, NULL)) != NULL) { + printf(" Unique ID: %s\n", unique_id); + free(unique_id); + } + get_token_info(opt_slot, &info); + printf(" uri: %s", get_uri(&info, opt_slot)); + if (id != NULL && idsize) { + printf(";id="); + for (unsigned int n = 0; n < idsize; n++) + printf("%%%02x", id[n]); + free(id); + } + if (label != NULL) { + const char *pelabel = percent_encode((unsigned char *)label, strlen(label)); + printf(";object=%s", pelabel); + free(label); + } + if (sec) { + printf(";type=secret-key\n"); + } else if (pub) { + printf(";type=public\n"); + } else { + printf(";type=private\n"); + } suppress_warn = 0; } @@ -3430,8 +6930,14 @@ static void show_cert(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) { CK_CERTIFICATE_TYPE cert_type = getCERTIFICATE_TYPE(sess, obj); CK_ULONG size; + CK_TOKEN_INFO info; unsigned char *id; char *label; + char *unique_id; +#if defined(ENABLE_OPENSSL) + unsigned char *subject; + unsigned char *serial_number; +#endif /* ENABLE_OPENSSL */ printf("Certificate Object; type = "); switch (cert_type) { @@ -3451,43 +6957,90 @@ static void show_cert(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) if ((label = getLABEL(sess, obj, NULL)) != NULL) { printf(" label: %s\n", label); - free(label); } - if ((id = getID(sess, obj, &size)) != NULL && size) { - unsigned int n; +#if defined(ENABLE_OPENSSL) + if ((subject = getSUBJECT(sess, obj, &size)) != NULL) { + X509_NAME *name; + const unsigned char *tmp = subject; + + name = d2i_X509_NAME(NULL, &tmp, size); + if(name) { + BIO *bio = BIO_new(BIO_s_file()); + BIO_set_fp(bio, stdout, BIO_NOCLOSE); + printf(" subject: DN: "); + X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253); + printf("\n"); + BIO_free(bio); + X509_NAME_free(name); + } + free(subject); + } + if ((serial_number = getSERIAL_NUMBER(sess, obj, &size)) != NULL) { + ASN1_INTEGER* serial = NULL; + const unsigned char *tmp = serial_number; + serial = d2i_ASN1_INTEGER(NULL, &tmp, size); + if (serial) { + BIO *bio = BIO_new(BIO_s_file()); + BIO_set_fp(bio, stdout, BIO_NOCLOSE); + BIO_printf(bio, " serial: "); + i2a_ASN1_INTEGER(bio, serial); + BIO_printf(bio, "\n"); + BIO_free(bio); + ASN1_INTEGER_free(serial); + } + free(serial_number); + } +#endif /* ENABLE_OPENSSL */ + if ((id = getID(sess, obj, &size)) != NULL && size) { printf(" ID: "); - for (n = 0; n < size; n++) - printf("%02x", id[n]); - printf("\n"); + print_hex(id, size, ':', true); + } + if ((unique_id = getUNIQUE_ID(sess, obj, NULL)) != NULL) { + printf(" Unique ID: %s\n", unique_id); + free(unique_id); + } + get_token_info(opt_slot, &info); + printf(" uri: %s", get_uri(&info, opt_slot)); + if (id != NULL && size) { + printf(";id="); + for (unsigned int n = 0; n < size; n++) + printf("%%%02x", id[n]); free(id); } + if (label != NULL) { + const char *pelabel = percent_encode((unsigned char *)label, strlen(label)); + printf(";object=%s", pelabel); + free(label); + } + printf(";type=cert\n"); } static void show_dobj(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) { unsigned char *oid_buf; char *label; + char *application; CK_ULONG size = 0; + CK_TOKEN_INFO info; + CK_BBOOL modifiable = 0; + CK_BBOOL private = 0; suppress_warn = 1; printf("Data object %u\n", (unsigned int) obj); printf(" label: "); if ((label = getLABEL(sess, obj, NULL)) != NULL) { printf("'%s'\n", label); - free(label); - } - else { + } else { printf("\n"); } printf(" application: "); - if ((label = getAPPLICATION(sess, obj, NULL)) != NULL) { - printf("'%s'\n", label); - free(label); - } - else { + if ((application = getAPPLICATION(sess, obj, NULL)) != NULL) { + printf("'%s'\n", application); + free(application); + } else { printf("\n"); } @@ -3512,18 +7065,42 @@ static void show_dobj(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) } printf(" flags: "); - if (getMODIFIABLE(sess, obj)) + modifiable = getMODIFIABLE(sess, obj); + if (modifiable) printf(" modifiable"); - if (getPRIVATE(sess, obj)) + private = getPRIVATE(sess, obj); + if (private) printf(" private"); - if (!getMODIFIABLE(sess, obj) && !getPRIVATE(sess, obj)) + if (!modifiable && !private) printf(""); + printf("\n"); - printf ("\n"); + get_token_info(opt_slot, &info); + printf(" uri: %s", get_uri(&info, opt_slot)); + if (label != NULL) { + const char *pelabel = percent_encode((unsigned char *)label, strlen(label)); + printf(";object=%s", pelabel); + free(label); + } + printf(";type=data\n"); suppress_warn = 0; } +static void show_profile(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) +{ + CK_ULONG id = 0; + + printf("Profile object %u\n", (unsigned int) obj); + printf(" profile_id: "); + if ((id = getPROFILE_ID(sess, obj)) != 0) { + printf("%s (%lu)\n", p11_profile_to_name(id), id); + } else { + printf("\n"); + } +} + + static void get_token_info(CK_SLOT_ID slot, CK_TOKEN_INFO_PTR info) { @@ -3542,6 +7119,9 @@ get_mechanisms(CK_SLOT_ID slot, CK_MECHANISM_TYPE_PTR *pList, CK_FLAGS flags) CK_RV rv; rv = p11->C_GetMechanismList(slot, *pList, &ulCount); + if (rv != CKR_OK) + p11_fatal("C_GetMechanismList", rv); + *pList = calloc(ulCount, sizeof(**pList)); if (*pList == NULL) util_fatal("calloc failed: %m"); @@ -3568,9 +7148,9 @@ get_mechanisms(CK_SLOT_ID slot, CK_MECHANISM_TYPE_PTR *pList, CK_FLAGS flags) } #ifdef ENABLE_OPENSSL -unsigned char *BIO_copy_data(BIO *out, int *data_lenp) { +unsigned char *BIO_copy_data(BIO *out, long *data_lenp) { unsigned char *data, *tdata; - int data_len; + long data_len; data_len = BIO_get_mem_data(out, &tdata); data = malloc(data_len+1); @@ -3661,110 +7241,453 @@ static int read_object(CK_SESSION_HANDLE session) nn_attrs++; } - rv = find_object_with_attributes(session, &obj, attrs, nn_attrs, 0); + rv = find_object_with_attributes(session, &obj, attrs, nn_attrs, opt_object_index); if (rv != CKR_OK) p11_fatal("find_object_with_attributes()", rv); else if (obj==CK_INVALID_HANDLE) util_fatal("object not found"); -/* TODO: -DEE should look at object class, and get appropriate values - * based on the object, and other attributes. For example EC keys do - * not have a VALUE But have a EC_POINT. DvO: done for RSA and EC public keys. - */ if (clazz == CKO_PRIVATE_KEY) { fprintf(stderr, "sorry, reading private keys not (yet) supported\n"); return 0; } if (clazz == CKO_PUBLIC_KEY) { + /* If module supports CKA_PUBLIC_KEY_INFO which is DER of SPKI + * return whatever the module provides including ED448 and X448 + */ + if (opt_public_key_info) { + value = getPUBLIC_KEY_INFO(session, obj, &len); + /* softhsm2 may return length 0 and varattr may allocate memory treat as invalid */ + if (value && len == 0) { + p11_warn("getPUBLIC_KEY_INFO returned a value of length 0", 0); + free(value); + value = NULL; + } + } + if (value == NULL) { /* Do the old way */ #ifdef ENABLE_OPENSSL - int derlen; - BIO *pout = BIO_new(BIO_s_mem()); - if (!pout) - util_fatal("out of memory"); + long derlen; + EVP_PKEY *pkey = NULL; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_PKEY_CTX *ctx = NULL; + OSSL_PARAM *params = NULL; + OSSL_PARAM_BLD *bld = NULL; +#endif + BIO *pout = BIO_new(BIO_s_mem()); + if (!pout) + util_fatal("out of memory"); - type = getKEY_TYPE(session, obj); - if (type == CKK_RSA) { - RSA *rsa; - BIGNUM *rsa_n = NULL; - BIGNUM *rsa_e = NULL; + type = getKEY_TYPE(session, obj); + if (type == CKK_RSA) { + BIGNUM *rsa_n = NULL; + BIGNUM *rsa_e = NULL; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA *rsa = RSA_new(); + if (!rsa) + util_fatal("out of memory"); +#endif + if ((value = getMODULUS(session, obj, &len))) { + if (!(rsa_n = BN_bin2bn(value, (int)len, NULL))) + util_fatal("cannot parse MODULUS"); + free(value); + } else + util_fatal("cannot obtain MODULUS"); + + if ((value = getPUBLIC_EXPONENT(session, obj, &len))) { + if (!(rsa_e = BN_bin2bn(value, (int)len, NULL))) + util_fatal("cannot parse PUBLIC_EXPONENT"); + free(value); + } else + util_fatal("cannot obtain PUBLIC_EXPONENT"); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if (RSA_set0_key(rsa, rsa_n, rsa_e, NULL) != 1) + util_fatal("cannot set RSA values"); + + if (!i2d_RSA_PUBKEY_bio(pout, rsa)) + util_fatal("cannot convert RSA public key to DER"); + RSA_free(rsa); +#else + ctx = EVP_PKEY_CTX_new_from_name(osslctx, "RSA", NULL); + if (!ctx) + util_fatal("out of memory"); + if (!(bld = OSSL_PARAM_BLD_new()) || + OSSL_PARAM_BLD_push_BN(bld, "n", rsa_n) != 1 || + OSSL_PARAM_BLD_push_BN(bld, "e", rsa_e) != 1 || + !(params = OSSL_PARAM_BLD_to_param(bld))) { + BN_free(rsa_n); + BN_free(rsa_e); + OSSL_PARAM_BLD_free(bld); + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + util_fatal("cannot set RSA values"); + } + BN_free(rsa_n); + BN_free(rsa_e); + OSSL_PARAM_BLD_free(bld); + if (EVP_PKEY_fromdata_init(ctx) != 1 || + EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) != 1) { + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + util_fatal("cannot set RSA values"); + } + OSSL_PARAM_free(params); + if (i2d_PUBKEY_bio(pout, pkey) != 1) { + EVP_PKEY_CTX_free(ctx); + util_fatal("cannot convert RSA public key to DER"); + } + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(ctx); +#endif +#if !defined(OPENSSL_NO_EC) + } else if (type == CKK_EC) { + CK_BYTE *params; + const unsigned char *a; + size_t a_len = 0; + ASN1_OCTET_STRING *os; + int success = 0; + EC_POINT *point = NULL; + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const EC_GROUP *group = NULL; + EC_KEY *ec = EC_KEY_new(); + pkey = EVP_PKEY_new(); +#else + EC_GROUP *group = NULL; + char group_name[80]; + OSSL_PARAM *old = NULL, *new = NULL, *p = NULL; + OSSL_PARAM_BLD *bld = NULL; +#endif + if ((params = getEC_PARAMS(session, obj, &len))) { + const unsigned char *a = params; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if (!d2i_ECParameters(&ec, &a, (long)len)) + util_fatal("cannot parse EC_PARAMS"); + EVP_PKEY_assign_EC_KEY(pkey, ec); +#else + if (!d2i_KeyParams(EVP_PKEY_EC, &pkey, &a, len)) + util_fatal("cannot parse EC_PARAMS"); +#endif + free(params); + } else + util_fatal("cannot obtain EC_PARAMS"); - rsa = RSA_new(); - if (rsa == NULL) - util_fatal("out of memory"); + value = getEC_POINT(session, obj, &len); + /* PKCS#11-compliant modules should return ASN1_OCTET_STRING */ + /* will accept BIT STRING, accept both */ + a = value; + os = d2i_ASN1_OCTET_STRING(NULL, &a, (long)len); + if (!os) { + os = d2i_ASN1_BIT_STRING(NULL, &a, (long)len); + len = BYTES4BITS(len); + } - if ((value = getMODULUS(session, obj, &len))) { - if (!(rsa_n = BN_bin2bn(value, len, NULL))) - util_fatal("cannot parse MODULUS"); - free(value); - } else - util_fatal("cannot obtain MODULUS"); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + group = EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey)); +#else + if (EVP_PKEY_get_group_name(pkey, group_name, sizeof(group_name), NULL) != 1) + util_fatal("cannot obtain EC_PARAMS"); + group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(group_name)); +#endif + point = EC_POINT_new(group); + if (os) { + a = ASN1_STRING_get0_data(os); + a_len = ASN1_STRING_length(os); + success = EC_POINT_oct2point(group, point, a, a_len, NULL); + } + if (!success) { /* Workaround for broken PKCS#11 modules */ + ASN1_STRING_free(os); + a = value; + a_len = len; + if (!EC_POINT_oct2point(group, point, a, len, NULL)) { + free(value); + util_fatal("cannot obtain and parse EC_POINT"); + } + } - if ((value = getPUBLIC_EXPONENT(session, obj, &len))) { - if (!(rsa_e = BN_bin2bn(value, len, NULL))) - util_fatal("cannot parse PUBLIC_EXPONENT"); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if (success) + ASN1_STRING_free(os); + free(value); + EC_KEY_set_public_key(EVP_PKEY_get0_EC_KEY(pkey), point); +#else + if (!(bld = OSSL_PARAM_BLD_new()) || + EVP_PKEY_todata(pkey, EVP_PKEY_PUBLIC_KEY, &old) != 1 || + OSSL_PARAM_BLD_push_octet_string(bld, "pub", a, a_len) != 1 || + !(new = OSSL_PARAM_BLD_to_param(bld)) || + !(p = OSSL_PARAM_merge(old, new))) { + OSSL_PARAM_BLD_free(bld); + OSSL_PARAM_free(old); + OSSL_PARAM_free(new); + OSSL_PARAM_free(p); + if (success) + ASN1_STRING_free(os); + free(value); + util_fatal("cannot set OSSL_PARAM"); + } + OSSL_PARAM_BLD_free(bld); + if (success) + ASN1_STRING_free(os); free(value); - } else - util_fatal("cannot obtain PUBLIC_EXPONENT"); - if (RSA_set0_key(rsa, rsa_n, rsa_e, NULL) != 1) - util_fatal("cannot set RSA values"); + if (!(ctx = EVP_PKEY_CTX_new_from_name(osslctx, "EC", NULL)) || + EVP_PKEY_fromdata_init(ctx) != 1) { + OSSL_PARAM_free(p); + EVP_PKEY_CTX_free(ctx); + util_fatal("cannot set CTX"); + } + EVP_PKEY_free(pkey); + pkey = NULL; + if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, p) != 1) { + OSSL_PARAM_free(p); + EVP_PKEY_CTX_free(ctx); + util_fatal("cannot create EVP_PKEY"); + } + OSSL_PARAM_free(old); + OSSL_PARAM_free(new); - if (!i2d_RSA_PUBKEY_bio(pout, rsa)) - util_fatal("cannot convert RSA public key to DER"); - RSA_free(rsa); -#if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) - } else if (type == CKK_EC) { - EC_KEY *ec; - CK_BYTE *params; - const unsigned char *a; - ASN1_OCTET_STRING *os; - EC_KEY *success = NULL; - - ec = EC_KEY_new(); - if (ec == NULL) - util_fatal("out of memory"); +#endif + if (!i2d_PUBKEY_bio(pout, pkey)) + util_fatal("cannot convert EC public key to DER"); +#endif + /* only if compiled with a version of OpenSSL or libressl */ + /* do more tests for the other 3 as needed */ +#if defined(EVP_PKEY_ED25519) || defined(EVP_PKEY_ED448) || defined (EVP_PKEY_X25519) || defined(EVP_PKEY_X448) + } else if (type == CKK_EC_EDWARDS || type == CKK_EC_MONTGOMERY) { + EVP_PKEY *key = NULL; + CK_BYTE *params = NULL; + const unsigned char *a; + ASN1_OCTET_STRING *os; + int raw_pk = 0; + + if ((params = getEC_PARAMS(session, obj, &len))) { + ASN1_PRINTABLESTRING *curve = NULL; + ASN1_OBJECT *obj = NULL; + + a = params; + if (d2i_ASN1_PRINTABLESTRING(&curve, &a, (long)len) != NULL) { + if (strcmp((char *)ASN1_STRING_get0_data(curve), "edwards25519") && + strcmp((char *)ASN1_STRING_get0_data(curve), "curve25519")) { + util_fatal("Unknown curve name \"%si\"", (char *)ASN1_STRING_get0_data(curve)); + } + ASN1_PRINTABLESTRING_free(curve); + } else if (d2i_ASN1_OBJECT(&obj, &a, (long)len) != NULL) { + int nid = OBJ_obj2nid(obj); + if (nid != NID_ED25519 && + nid != NID_X25519 +#ifdef EVP_PKEY_ED448 + && nid != NID_ED448 +#endif +#ifdef EVP_PKEY_X448 + && nid != NID_X448 +#endif + ) { + util_fatal("Unknown curve OID, expected NID_ED25519 (%d), NID_X25519 (%d), " +#ifdef EVP_PKEY_ED448 + "NID_ED448 (%d), " +#endif +#ifdef EVP_PKEY_X448 + "NID_X448 (%d), " +#endif + "got %d", + NID_ED25519, NID_X25519, +#ifdef EVP_PKEY_ED448 + NID_ED448, +#endif +#ifdef EVP_PKEY_X448 + NID_X448, +#endif + nid); + } + ASN1_OBJECT_free(obj); + } else { + util_fatal("cannot parse curve name from EC_PARAMS"); + } + free(params); + } else { + util_fatal("cannot obtain EC_PARAMS"); + } - if ((params = getEC_PARAMS(session, obj, &len))) { - const unsigned char *a = params; - if (!d2i_ECParameters(&ec, &a, (long)len)) - util_fatal("cannot parse EC_PARAMS"); - OPENSSL_free(params); - } else - util_fatal("cannot obtain EC_PARAMS"); - - value = getEC_POINT(session, obj, &len); - /* PKCS#11-compliant modules should return ASN1_OCTET_STRING */ - a = value; - os = d2i_ASN1_OCTET_STRING(NULL, &a, (long)len); - if (os) { - a = os->data; - success = o2i_ECPublicKey(&ec, &a, os->length); - ASN1_STRING_free(os); - } - if (!success) { /* Workaround for broken PKCS#11 modules */ + value = getEC_POINT(session, obj, &len); + /* PKCS11 3.0 errata and 3.1 say Edwards and Montgomery + * return raw byte strings, convert to OCTET string for OpenSSL + * Will accept as OCTET STRING and BIT_STRING + */ a = value; - success = o2i_ECPublicKey(&ec, &a, len); - } - free(value); - if (!success) - util_fatal("cannot obtain and parse EC_POINT"); - if (!i2d_EC_PUBKEY_bio(pout, ec)) - util_fatal("cannot convert EC public key to DER"); - EC_KEY_free(ec); -#endif - } - else - util_fatal("Reading public keys of type 0x%X not (yet) supported", type); - value = BIO_copy_data(pout, &derlen); - BIO_free(pout); - len = derlen; + os = d2i_ASN1_OCTET_STRING(NULL, &a, (long)len); + if (!os) { + os = d2i_ASN1_BIT_STRING(NULL, &a, (long)len); + if (os) + len = BYTES4BITS(len); + } + if (!os) { + if ((os = ASN1_OCTET_STRING_new()) == NULL) + util_fatal("cannot decode EC_POINT"); + if (ASN1_OCTET_STRING_set(os, value, (int)len) == 0) { + util_fatal("cannot decode EC_POINT"); + } + } + + if (type == CKK_EC_EDWARDS && len == BYTES4BITS(256)) /* note extra bit */ + raw_pk = EVP_PKEY_ED25519; +#ifdef EVP_PKEY_ED448 + else if (type == CKK_EC_EDWARDS && len == ED448_KEY_SIZE_BYTES) + raw_pk = EVP_PKEY_ED448; +#endif /* EVP_PKEY_ED448 */ +#ifdef EVP_PKEY_X25519 + else if (type == CKK_EC_MONTGOMERY && len == BYTES4BITS(256)) /* note extra bit */ + raw_pk = EVP_PKEY_X25519; +#endif /*EVP_PKEY_X25519 */ +#ifdef EVP_PKEY_X448 + else if (type == CKK_EC_MONTGOMERY && len == BYTES4BITS(448)) + raw_pk = EVP_PKEY_X448; +#endif /* EVP_PKEY_X448 */ + else + util_fatal("Invalid or not supported CKK_EC_EDWARDS or CKK_EC_MONTGOMERY public key"); + + key = EVP_PKEY_new_raw_public_key(raw_pk, NULL, + (const uint8_t *)ASN1_STRING_get0_data(os), + ASN1_STRING_length(os)); + ASN1_STRING_free(os); + if (key == NULL) { + util_fatal("out of memory"); + } + if (i2d_PUBKEY_bio(pout, key) < 1) { + util_fatal("cannot write public key to output"); + } + + EVP_PKEY_free(key); +#endif /* defined(EVP_PKEY_ED25519) || defined(EVP_PKEY_ED448) || defined (EVP_PKEY_X25519) || defined(EVP_PKEY_X448) */ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + } else if (type == CKK_ML_DSA || type == CKK_ML_KEM || type == CKK_SLH_DSA) { + unsigned long parameter_set = getPARAMETER_SET(session, obj); + const char *evp_name = NULL; + unsigned char *value = NULL; + unsigned long value_len = 0; + + switch (type) { + case CKK_ML_DSA: + switch (parameter_set) { + case CKP_ML_DSA_44: + evp_name = "ML-DSA-44"; + break; + case CKP_ML_DSA_65: + evp_name = "ML-DSA-65"; + break; + case CKP_ML_DSA_87: + evp_name = "ML-DSA-87"; + break; + default: + util_fatal("Unknown parameter set (%lu)", + parameter_set); + } + break; + case CKK_ML_KEM: + switch (parameter_set) { + case CKP_ML_KEM_512: + evp_name = "ML-KEM-512"; + break; + case CKP_ML_KEM_768: + evp_name = "ML-KEM-768"; + break; + case CKP_ML_KEM_1024: + evp_name = "ML-KEM-1024"; + break; + default: + util_fatal("Unknown parameter set (%lu)", + parameter_set); + } + break; + case CKK_SLH_DSA: + switch (parameter_set) { + case CKP_SLH_DSA_SHA2_128S: + evp_name = "SLH-DSA-SHA2-128s"; + break; + case CKP_SLH_DSA_SHAKE_128S: + evp_name = "SLH-DSA-SHAKE-128s"; + break; + case CKP_SLH_DSA_SHA2_128F: + evp_name = "SLH-DSA-SHA2-128f"; + break; + case CKP_SLH_DSA_SHAKE_128F: + evp_name = "SLH-DSA-SHAKE-128f"; + break; + case CKP_SLH_DSA_SHA2_192S: + evp_name = "SLH-DSA-SHA2-192s"; + break; + case CKP_SLH_DSA_SHAKE_192S: + evp_name = "SLH-DSA-SHAKE-192s"; + break; + case CKP_SLH_DSA_SHA2_192F: + evp_name = "SLH-DSA-SHA2-192f"; + break; + case CKP_SLH_DSA_SHAKE_192F: + evp_name = "SLH-DSA-SHAKE-192f"; + break; + case CKP_SLH_DSA_SHA2_256S: + evp_name = "SLH-DSA-SHA2-256s"; + break; + case CKP_SLH_DSA_SHAKE_256S: + evp_name = "SLH-DSA-SHAKE-256s"; + break; + case CKP_SLH_DSA_SHA2_256F: + evp_name = "SLH-DSA-SHA2-256f"; + break; + case CKP_SLH_DSA_SHAKE_256F: + evp_name = "SLH-DSA-SHAKE-256f"; + break; + default: + util_fatal("Unknown parameter set (%lu)", + parameter_set); + } + break; + default: + util_fatal("Unknown key type (%lu)", type); + return -1; + } + + /* Public key value */ + value = getVALUE(session, obj, &value_len); + if (value == NULL) { + util_fatal("get CKA_VALUE failed"); + } + + if (!(ctx = EVP_PKEY_CTX_new_from_name(osslctx, evp_name, NULL)) || + !(bld = OSSL_PARAM_BLD_new()) || + OSSL_PARAM_BLD_push_octet_string(bld, "pub", value, value_len) != 1 || + !(params = OSSL_PARAM_BLD_to_param(bld))) { + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_BLD_free(bld); + util_fatal("Unable to set key params"); + } + OSSL_PARAM_BLD_free(bld); + if (EVP_PKEY_fromdata_init(ctx) != 1 || + EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) != 1) { + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + util_fatal("Unable to build key"); + } + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + + if (i2d_PUBKEY_bio(pout, pkey) < 1) { + util_fatal("cannot write public key to output"); + } + EVP_PKEY_free(pkey); +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + } else + util_fatal("Reading public keys of type 0x%lX not (yet) supported", type); + value = BIO_copy_data(pout, &derlen); + BIO_free(pout); + len = derlen; #else - util_fatal("No OpenSSL support, cannot read public key"); -#endif + util_fatal("No OpenSSL support, cannot read public key"); +#endif /* ENABLE_OPENSSL */ + } /* value is PUBKEY_KEY_INFO */ } else value = getVALUE(session, obj, &len); + if (value == NULL) util_fatal("get CKA_VALUE failed"); @@ -3837,7 +7760,7 @@ static int delete_object(CK_SESSION_HANDLE session) nn_attrs++; } - rv = find_object_with_attributes(session, &obj, attrs, nn_attrs, 0); + rv = find_object_with_attributes(session, &obj, attrs, nn_attrs, opt_object_index); if (rv != CKR_OK) p11_fatal("find_object_with_attributes()", rv); else if (obj==CK_INVALID_HANDLE) @@ -3865,7 +7788,7 @@ static CK_ULONG get_private_key_length(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE return 0; } - if (!find_object(sess, CKO_PUBLIC_KEY, &pubkey, id, idLen, 0)) { + if (!find_object(sess, CKO_PUBLIC_KEY, &pubkey, id, idLen, NULL, 0)) { free(id); fprintf(stderr, "couldn't find the corresponding pubkey\n"); return 0; @@ -3886,22 +7809,24 @@ static int test_digest(CK_SESSION_HANDLE session) CK_ULONG hashLen1, hashLen2; CK_MECHANISM_TYPE firstMechType; CK_SESSION_INFO sessionInfo; - CK_MECHANISM_TYPE mechTypes[] = { CKM_MD5, - CKM_SHA_1, CKM_RIPEMD160, + CKM_SHA_1, + CKM_SHA256, 0xffffff }; unsigned char *digests[] = { (unsigned char *) "\x7a\x08\xb0\x7e\x84\x64\x17\x03\xe5\xf2\xc8\x36\xaa\x59\xa1\x70", + (unsigned char *) "\xda\x79\xa5\x8f\xb8\x83\x3d\x61\xf6\x32\x16\x17\xe3\xfd\xf0\x56\x26\x5f\xb7\xcd", (unsigned char *) "\x29\xb0\xe7\x87\x82\x71\x64\x5f\xff\xb7\xee\xc7\xdb\x4a\x74\x73\xa1\xc0\x0b\xc1", - (unsigned char *) "\xda\x79\xa5\x8f\xb8\x83\x3d\x61\xf6\x32\x16\x17\xe3\xfd\xf0\x56\x26\x5f\xb7\xcd" + (unsigned char *) "\x9c\xfe\x7f\xaf\xf7\x5\x42\x98\xca\x87\x55\x7e\x15\xa1\x2\x62\xde\x8d\x3e\xee\x77\x82\x74\x17\xfb\xdf\xea\x1c\x41\xb9\xec\x23", }; CK_ULONG digestLens[] = { 16, 20, - 20 + 20, + 32, }; rv = p11->C_GetSessionInfo(session, &sessionInfo); @@ -3978,8 +7903,18 @@ static int test_digest(CK_SESSION_HANDLE session) for (j = 0; j < 10; j++) data[10 * i + j] = (unsigned char) (0x30 + j); - - for (i = 0; mechTypes[i] != 0xffffff; i++) { +#ifdef ENABLE_OPENSSL + i = (FIPS_mode() ? 2 : 0); +#else + i = 0; +#endif +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (!legacy_provider) { + printf("Failed to load legacy provider\n"); + return errors; + } +#endif + for (; mechTypes[i] != 0xffffff; i++) { ck_mech.mechanism = mechTypes[i]; rv = p11->C_DigestInit(session, &ck_mech); @@ -4037,6 +7972,266 @@ static int test_digest(CK_SESSION_HANDLE session) return errors; } +static CK_RV test_load_cipher_key(CK_SESSION_HANDLE session, uint8_t *key, size_t keysize, + CK_KEY_TYPE keytype, CK_OBJECT_HANDLE *hkey) +{ + CK_OBJECT_CLASS class = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = keytype; + CK_UTF8CHAR label[] = "testkey"; + CK_BBOOL _true = CK_TRUE; + CK_ULONG keylen = (CK_ULONG)keysize; + CK_ATTRIBUTE template[] = { + { CKA_CLASS, &class, sizeof(class) }, + { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, + { CKA_LABEL, label, sizeof(label)-1 }, + { CKA_ENCRYPT, &_true, sizeof(_true) }, + { CKA_DECRYPT, &_true, sizeof(_true) }, + { CKA_VALUE, key, keysize }, + { CKA_VALUE_LEN, &keylen, sizeof(keylen) }, + }; + + /* create a session key only */ + return p11->C_CreateObject(session, template, ARRAY_SIZE(template), hkey); +} + +static void test_delete_cipher_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE hkey) +{ + p11->C_DestroyObject(session, hkey); +} + +static int test_cipher(CK_SESSION_HANDLE session) +{ + CK_RV rv; + CK_SESSION_INFO sessionInfo; + static struct { + CK_MECHANISM_TYPE type; + uint8_t key[32]; + CK_ULONG keysz; + CK_KEY_TYPE keytype; + uint8_t iv[16]; + CK_ULONG ivsz; + uint8_t plaintext[128]; + CK_ULONG ptsz; + uint8_t ciphertext[128]; + CK_ULONG ctsz; + } cipher_algs[] = { + { + .type = CKM_AES_ECB, + .key = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}, + .keysz = 16, + .keytype = CKK_AES, + .plaintext = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff}, + .ptsz = 16, + .ciphertext = {0x69,0xc4,0xe0,0xd8,0x6a,0x7b,0x04,0x30,0xd8,0xcd,0xb7,0x80,0x70,0xb4,0xc5,0x5a}, + .ctsz = 16, + }, + { + .type = CKM_AES_CBC, + .key = {0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c}, + .keysz = 16, + .keytype = CKK_AES, + .iv = {0x76,0x49,0xab,0xac,0x81,0x19,0xb2,0x46,0xce,0xe9,0x8e,0x9b,0x12,0xe9,0x19,0x7d}, + .ivsz = 16, + .plaintext = {0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51}, + .ptsz = 16, + .ciphertext = {0x50,0x86,0xcb,0x9b,0x50,0x72,0x19,0xee,0x95,0xdb,0x11,0x3a,0x91,0x76,0x78,0xb2}, + .ctsz = 16, + }, + }; + int errors = 0; + int supported = 0; + + rv = p11->C_GetSessionInfo(session, &sessionInfo); + if (rv != CKR_OK) { + p11_fatal("C_GetSessionInfo", rv); + } + + /* encryption */ + for (size_t i = 0; i < ARRAY_SIZE(cipher_algs); ++i) { + + CK_OBJECT_HANDLE hkey = CK_INVALID_HANDLE; + CK_MECHANISM mech = { + .mechanism = cipher_algs[i].type, + .pParameter = cipher_algs[i].iv, + .ulParameterLen = cipher_algs[i].ivsz, + }; + const char *fct; + uint8_t ptext1[128] = {0}; + uint8_t ptext2[128] = {0}; + uint8_t ctext1[128] = {0}; + uint8_t ctext2[128] = {0}; + CK_ULONG coff = 0; + CK_ULONG poff = 0; + + rv = test_load_cipher_key(session, cipher_algs[i].key, cipher_algs[i].keysz, cipher_algs[i].keytype, &hkey); + if (rv != CKR_OK) { + continue; + } + + /* Testing Encryption */ + + fct = "C_EncryptInit"; + rv = p11->C_EncryptInit(session, &mech, hkey); + if (rv == CKR_MECHANISM_INVALID) + continue; /* mechanism not implemented, don't test */ + if (rv != CKR_OK) { + goto cipher_clup; + } + ++supported; + if (supported == 1) { + printf("Ciphers:\n"); + } + printf(" %s: ", p11_mechanism_to_name(mech.mechanism)); + +#define CIPHER_CHUNK (13) /* used to split input in sizes which are not block aligned */ + for (CK_ULONG ptlen = 0; ptlen < cipher_algs[i].ptsz;) { + + CK_ULONG isize = MIN(cipher_algs[i].ptsz - ptlen, CIPHER_CHUNK); + CK_ULONG osize = sizeof(ctext1) - coff; + + fct = "C_EncryptUpdate"; + rv = p11->C_EncryptUpdate(session, cipher_algs[i].plaintext + ptlen, isize, + ctext1 + coff, &osize); + if (rv == CKR_FUNCTION_NOT_SUPPORTED) { + printf(" Note: C_EncryptUpdate(), C_EncryptFinal() not supported\n"); + break; + } + if (rv != CKR_OK) { + goto cipher_clup; + } + + /* move offsets */ + ptlen += isize; + coff += osize; + } + + /* Only do final if update is supported */ + if (rv == CKR_OK) { + CK_ULONG osize = sizeof(ctext1) - coff; + fct = "C_EncryptFinal"; + rv = p11->C_EncryptFinal(session, ctext1 + coff, &osize); + if (rv != CKR_OK) { + goto cipher_clup; + } + + /* compare values for match */ + if (memcmp(ctext1, cipher_algs[i].ciphertext, cipher_algs[i].ctsz) != 0) { + printf("ERR: wrong ciphertext value\n"); + rv = CKR_GENERAL_ERROR; + goto cipher_clup; + } + } + + /* Second test is encrypt one shot */ + fct = "C_EncryptInit"; + rv = p11->C_EncryptInit(session, &mech, hkey); + if (rv != CKR_OK) { + goto cipher_clup; + } + + coff = sizeof(ctext2); + fct = "C_Encrypt"; + rv = p11->C_Encrypt(session, cipher_algs[i].plaintext, cipher_algs[i].ptsz, + ctext2, &coff); + if (rv == CKR_FUNCTION_NOT_SUPPORTED) { + printf(" Note: C_Encrypt() not supported\n"); + goto cipher_clup; + } + + /* compare values for match */ + if (memcmp(ctext2, cipher_algs[i].ciphertext, cipher_algs[i].ctsz) != 0) { + printf("ERR: wrong ciphertext value\n"); + rv = CKR_GENERAL_ERROR; + goto cipher_clup; + } + + /* Testing Decryption */ + + fct = "C_DecryptInit"; + rv = p11->C_DecryptInit(session, &mech, hkey); + if (rv == CKR_MECHANISM_INVALID) + continue; /* mechanism not implemented, don't test */ + if (rv != CKR_OK) { + goto cipher_clup; + } + + for (CK_ULONG ctlen = 0; ctlen < cipher_algs[i].ctsz;) { + + CK_ULONG isize = MIN(cipher_algs[i].ctsz - ctlen, CIPHER_CHUNK); + CK_ULONG osize = sizeof(ptext1) - poff; + + fct = "C_DecryptUpdate"; + rv = p11->C_DecryptUpdate(session, cipher_algs[i].ciphertext + ctlen, isize, + ptext1 + poff, &osize); + if (rv == CKR_FUNCTION_NOT_SUPPORTED) { + printf(" Note: C_DecryptUpdate(), C_DecryptFinal() not supported\n"); + break; + } + if (rv != CKR_OK) { + goto cipher_clup; + } + + /* move offsets */ + ctlen += isize; + poff += osize; + } + + /* Only do final if update is supported */ + if (rv == CKR_OK) { + CK_ULONG osize = sizeof(ptext1) - poff; + fct = "C_DecryptFinal"; + rv = p11->C_DecryptFinal(session, ptext1 + poff, &osize); + if (rv != CKR_OK) { + goto cipher_clup; + } + + /* compare values for match */ + if (memcmp(ptext1, cipher_algs[i].plaintext, cipher_algs[i].ptsz) != 0) { + printf("ERR: wrong plaintext value\n"); + rv = CKR_GENERAL_ERROR; + goto cipher_clup; + } + } + + /* Second test is decrypt one shot */ + fct = "C_DecryptInit"; + rv = p11->C_DecryptInit(session, &mech, hkey); + if (rv != CKR_OK) { + goto cipher_clup; + } + + poff = sizeof(ptext2); + fct = "C_Decrypt"; + rv = p11->C_Decrypt(session, cipher_algs[i].ciphertext, cipher_algs[i].ctsz, + ptext2, &poff); + if (rv == CKR_FUNCTION_NOT_SUPPORTED) { + printf(" Note: C_Decrypt() not supported\n"); + goto cipher_clup; + } + + /* compare values for match */ + if (memcmp(ptext2, cipher_algs[i].plaintext, cipher_algs[i].ptsz) != 0) { + printf("ERR: wrong plaintext value\n"); + rv = CKR_GENERAL_ERROR; + goto cipher_clup; + } + +cipher_clup: + test_delete_cipher_key(session, hkey); + if (rv != CKR_OK) { + p11_fatal(fct, rv); + } else { + printf("OK\n"); + } + } + + if (supported == 0) { + fprintf(stderr, "Ciphers: not implemented\n"); + } + + return errors; +} + #ifdef ENABLE_OPENSSL static EVP_PKEY *get_public_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE privKeyObject) { @@ -4046,9 +8241,15 @@ static EVP_PKEY *get_public_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE priv unsigned char *pubkey; const unsigned char *pubkey_c; CK_ULONG pubkeyLen; - EVP_PKEY *pkey; - RSA *rsa; + EVP_PKEY *pkey = NULL; BIGNUM *rsa_n, *rsa_e; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA *rsa; +#else + EVP_PKEY_CTX *ctx = NULL; + OSSL_PARAM_BLD *bld = NULL; + OSSL_PARAM *params = NULL; +#endif id = NULL; id = getID(session, privKeyObject, &idLen); @@ -4057,7 +8258,7 @@ static EVP_PKEY *get_public_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE priv return NULL; } - if (!find_object(session, CKO_PUBLIC_KEY, &pubkeyObject, id, idLen, 0)) { + if (!find_object(session, CKO_PUBLIC_KEY, &pubkeyObject, id, idLen, NULL, 0)) { free(id); fprintf(stderr, "couldn't find the corresponding pubkey for validation\n"); return NULL; @@ -4065,31 +8266,65 @@ static EVP_PKEY *get_public_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE priv free(id); switch(getKEY_TYPE(session, pubkeyObject)) { - case CKK_RSA: - pkey = EVP_PKEY_new(); + case CKK_RSA:; +#if OPENSSL_VERSION_NUMBER < 0x30000000L rsa = RSA_new(); - mod = getMODULUS(session, pubkeyObject, &modLen); - exp = getPUBLIC_EXPONENT(session, pubkeyObject, &expLen); - if ( !pkey || !rsa || !mod || !exp) { - fprintf(stderr, "public key not extractable\n"); + pkey = EVP_PKEY_new(); + if (!rsa || !pkey) { + fprintf(stderr, "public key not extractable\n"); if (pkey) EVP_PKEY_free(pkey); if (rsa) RSA_free(rsa); + } +#endif + mod = getMODULUS(session, pubkeyObject, &modLen); + exp = getPUBLIC_EXPONENT(session, pubkeyObject, &expLen); + if (!mod || !exp) { + fprintf(stderr, "public key not extractable\n"); if (mod) free(mod); if (exp) free(exp); return NULL; } - rsa_n = BN_bin2bn(mod, modLen, NULL); - rsa_e = BN_bin2bn(exp, expLen, NULL); + rsa_n = BN_bin2bn(mod, (int)modLen, NULL); + rsa_e = BN_bin2bn(exp, (int)expLen, NULL); + free(mod); + free(exp); +#if OPENSSL_VERSION_NUMBER < 0x30000000L if (RSA_set0_key(rsa, rsa_n, rsa_e, NULL) != 1) return NULL; - EVP_PKEY_assign_RSA(pkey, rsa); - free(mod); - free(exp); +#else + if (!(bld = OSSL_PARAM_BLD_new()) || + OSSL_PARAM_BLD_push_BN(bld, "n", rsa_n) != 1 || + OSSL_PARAM_BLD_push_BN(bld, "e", rsa_e) != 1 || + !(params = OSSL_PARAM_BLD_to_param(bld))) { + fprintf(stderr, "public key not extractable\n"); + OSSL_PARAM_BLD_free(bld); + OSSL_PARAM_free(params); + BN_free(rsa_n); + BN_free(rsa_e); + return NULL; + } + OSSL_PARAM_BLD_free(bld); + + if (!(ctx = EVP_PKEY_CTX_new_from_name(osslctx, "RSA", NULL)) || + EVP_PKEY_fromdata_init(ctx) != 1 || + EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) != 1) { + fprintf(stderr, "public key not extractable\n"); + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + BN_free(rsa_n); + BN_free(rsa_e); + return NULL; + } + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + BN_free(rsa_n); + BN_free(rsa_e); +#endif return pkey; case CKK_DSA: case CKK_ECDSA: @@ -4140,12 +8375,18 @@ static int sign_verify_openssl(CK_SESSION_HANDLE session, EVP_sha1(), EVP_sha1(), EVP_md5(), +#ifndef OPENSSL_NO_RIPEMD EVP_ripemd160(), -#if OPENSSL_VERSION_NUMBER >= 0x00908000L - EVP_sha256(), #endif + EVP_sha256(), }; #endif +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(OPENSSL_NO_RIPEMD) + if (!legacy_provider) { + printf("Failed to load legacy provider"); + return errors; + } +#endif rv = p11->C_SignInit(session, ck_mech, privKeyObject); /* mechanism not implemented, don't test */ @@ -4153,7 +8394,7 @@ static int sign_verify_openssl(CK_SESSION_HANDLE session, return errors; if (rv != CKR_OK) p11_fatal("C_SignInit", rv); - if (getALWAYS_AUTHENTICATE(session, privKeyObject)) + if ((getCLASS(session, privKeyObject) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, privKeyObject)) login(session,CKU_CONTEXT_SPECIFIC); printf(" %s: ", p11_mechanism_to_name(ck_mech->mechanism)); @@ -4180,9 +8421,12 @@ static int sign_verify_openssl(CK_SESSION_HANDLE session, if (!md_ctx) err = -1; else { - EVP_VerifyInit(md_ctx, evp_mds[evp_md_index]); - EVP_VerifyUpdate(md_ctx, verifyData, verifyDataLen); - err = EVP_VerifyFinal(md_ctx, sig1, sigLen1, pkey); + if (EVP_VerifyInit(md_ctx, evp_mds[evp_md_index]) + && EVP_VerifyUpdate(md_ctx, verifyData, verifyDataLen)) { + err = EVP_VerifyFinal(md_ctx, sig1, (unsigned)sigLen1, pkey); + } else { + err = -1; + } EVP_MD_CTX_destroy(md_ctx); EVP_PKEY_free(pkey); } @@ -4191,6 +8435,7 @@ static int sign_verify_openssl(CK_SESSION_HANDLE session, errors++; } else if (err != 1) { printf("openssl error during verification: 0x%0x (%d)\n", err, err); + ERR_print_errors_fp(stdout); } else printf("OK\n"); @@ -4207,11 +8452,11 @@ static int test_signature(CK_SESSION_HANDLE sess) { int errors = 0; CK_RV rv; - CK_OBJECT_HANDLE privKeyObject; + CK_OBJECT_HANDLE pubKeyObject, privKeyObject; CK_MECHANISM ck_mech = { CKM_MD5, NULL, 0 }; CK_MECHANISM_TYPE firstMechType; CK_SESSION_INFO sessionInfo; - CK_ULONG i, j; + int i, j; unsigned char data[512]; /* FIXME: Will not work for keys above 4096 bits */ CK_ULONG modLenBytes = 0; CK_ULONG dataLen; @@ -4225,10 +8470,10 @@ static int test_signature(CK_SESSION_HANDLE sess) CKM_RSA_PKCS, CKM_SHA1_RSA_PKCS, CKM_MD5_RSA_PKCS, +#ifndef OPENSSL_NO_RIPEMD CKM_RIPEMD160_RSA_PKCS, -#if OPENSSL_VERSION_NUMBER >= 0x00908000L - CKM_SHA256_RSA_PKCS, #endif + CKM_SHA256_RSA_PKCS, 0xffffff }; size_t mechTypes_num = sizeof(mechTypes)/sizeof(CK_MECHANISM_TYPE); @@ -4263,14 +8508,14 @@ static int test_signature(CK_SESSION_HANDLE sess) return errors; } - if (!find_mechanism(sessionInfo.slotID, CKF_SIGN | CKF_HW, mechTypes, mechTypes_num, &firstMechType)) { + if (!find_mechanism(sessionInfo.slotID, CKF_SIGN|opt_allow_sw, mechTypes, mechTypes_num, &firstMechType)) { printf("Signatures: not implemented\n"); return errors; } printf("Signatures (currently only for RSA)\n"); - for (j = 0; find_object(sess, CKO_PRIVATE_KEY, &privKeyObject, NULL, 0, j); j++) { - printf(" testing key %ld ", j); + for (j = 0; find_object(sess, CKO_PRIVATE_KEY, &privKeyObject, NULL, 0, NULL, j); j++) { + printf(" testing key %d ", j); if ((label = getLABEL(sess, privKeyObject, NULL)) != NULL) { printf("(%s) ", label); free(label); @@ -4285,7 +8530,7 @@ static int test_signature(CK_SESSION_HANDLE sess) continue; } - modLenBytes = (get_private_key_length(sess, privKeyObject) + 7) / 8; + modLenBytes = BYTES4BITS(get_private_key_length(sess, privKeyObject)); if(!modLenBytes) { printf(" -- can't be used for signature, skipping: can't obtain modulus\n"); continue; @@ -4308,17 +8553,17 @@ static int test_signature(CK_SESSION_HANDLE sess) break; case CKM_RSA_X_509: dataLen = modLenBytes; + pseudo_randomize(data, dataLen); break; default: dataLen = sizeof(data); /* let's hope it's OK */ + pseudo_randomize(data, dataLen); break; } - pseudo_randomize(data, dataLen); - if (firstMechType == CKM_RSA_X_509) { - /* make sure our data is smaller than the modulus */ - data[0] = 0x00; + /* make sure our data is smaller than the modulus - 11 */ + memset(data, 0, 11); /* in effect is zero padding */ } ck_mech.mechanism = firstMechType; @@ -4328,7 +8573,7 @@ static int test_signature(CK_SESSION_HANDLE sess) return errors; if (rv != CKR_OK) p11_fatal("C_SignInit", rv); - if (getALWAYS_AUTHENTICATE(sess, privKeyObject)) + if ((getCLASS(sess, privKeyObject) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(sess, privKeyObject)) login(sess,CKU_CONTEXT_SPECIFIC); rv = p11->C_SignUpdate(sess, data, 5); @@ -4357,7 +8602,7 @@ static int test_signature(CK_SESSION_HANDLE sess) rv = p11->C_SignInit(sess, &ck_mech, privKeyObject); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); - if (getALWAYS_AUTHENTICATE(sess, privKeyObject)) + if ((getCLASS(sess, privKeyObject) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(sess, privKeyObject)) login(sess,CKU_CONTEXT_SPECIFIC); sigLen2 = sizeof(sig2); @@ -4381,6 +8626,8 @@ static int test_signature(CK_SESSION_HANDLE sess) rv = p11->C_SignInit(sess, &ck_mech, privKeyObject); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); + if ((getCLASS(sess, privKeyObject) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(sess, privKeyObject)) + login(sess,CKU_CONTEXT_SPECIFIC); sigLen2 = 1; /* too short */ rv = p11->C_Sign(sess, data, dataLen, sig2, &sigLen2); @@ -4396,7 +8643,7 @@ static int test_signature(CK_SESSION_HANDLE sess) printf(" ERR: C_Sign() didn't return CKR_OK for a NULL output buf, but %s (0x%0x)\n", CKR2Str(rv), (int) rv); } - if (getALWAYS_AUTHENTICATE(sess, privKeyObject)) + if ((getCLASS(sess, privKeyObject) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(sess, privKeyObject)) login(sess,CKU_CONTEXT_SPECIFIC); rv = p11->C_Sign(sess, data, dataLen, sig2, &sigLen2); @@ -4418,7 +8665,8 @@ static int test_signature(CK_SESSION_HANDLE sess) data[0] = 0x00; data[1] = 0x01; memset(data + 2, 0xFF, dataLen - 3 - dataLens[1]); - data[dataLen - 36] = 0x00; + if (dataLen >= 36) + data[dataLen - 36] = 0x00; memcpy(data + (dataLen - dataLens[1]), datas[1], dataLens[1]); datas[0] = data; dataLens[0] = dataLen; @@ -4438,20 +8686,18 @@ static int test_signature(CK_SESSION_HANDLE sess) break; ck_mech.mechanism = mechTypes[i]; j = 1; /* j-th signature key */ - while (find_object(sess, CKO_PRIVATE_KEY, &privKeyObject, NULL, 0, j++) != 0) { + while (find_object(sess, CKO_PRIVATE_KEY, &privKeyObject, NULL, 0, NULL, j++) != 0) { + unsigned char *id; + CK_ULONG idLen; CK_ULONG modLenBits; - label = getLABEL(sess, privKeyObject, NULL); - modLenBits = get_private_key_length(sess, privKeyObject); - modLenBytes = (modLenBits + 7) / 8; - - printf(" testing key %d (%u bits%s%s) with 1 signature mechanism", - (int) (j-1), - (int) modLenBits, - label? ", label=" : "", - label? label : ""); - if (label) + printf(" testing key %d", (int) (j-1)); + if ((label = getLABEL(sess, privKeyObject, NULL)) != NULL) { + printf(" (%s)", label); free(label); + } + if ((int) (j-1) != 0) + printf(" with 1 mechanism"); if (getKEY_TYPE(sess, privKeyObject) != CKK_RSA) { printf(" -- non-RSA, skipping\n"); @@ -4461,13 +8707,28 @@ static int test_signature(CK_SESSION_HANDLE sess) printf(" -- can't be used to sign/verify, skipping\n"); continue; } - else if (!modLenBytes) { - printf(" -- can't be used to sign/verify, skipping: can't obtain modulus\n"); + if ((id = getID(sess, privKeyObject, &idLen)) != NULL) { + int r; + + r = find_object(sess, CKO_PUBLIC_KEY, &pubKeyObject, id, idLen, NULL, 0); + free(id); + if (r == 0) { + printf(" -- can't find corresponding public key, skipping\n"); + continue; + } + } + else { + printf(" -- can't get the ID for looking up the public key, skipping\n"); continue; } - else { - printf("\n"); + + modLenBits = get_private_key_length(sess, privKeyObject); + modLenBytes = BYTES4BITS(modLenBits); + if (!modLenBytes) { + printf(" -- can't be used to sign/verify, skipping: can't obtain modulus\n"); + continue; } + printf("\n"); /* Fill in data[0] and dataLens[0] */ dataLen = modLenBytes; @@ -4532,7 +8793,7 @@ static int sign_verify(CK_SESSION_HANDLE session, printf(" ERR: C_SignInit() returned %s (0x%0x)\n", CKR2Str(rv), (int) rv); return ++errors; } - if (getALWAYS_AUTHENTICATE(session, priv_key)) + if ((getCLASS(session, priv_key) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, priv_key)) login(session,CKU_CONTEXT_SPECIFIC); printf(" %s: ", p11_mechanism_to_name(*mech_type)); @@ -4569,7 +8830,8 @@ static int sign_verify(CK_SESSION_HANDLE session, static int test_verify(CK_SESSION_HANDLE sess) { - int key_len, i, errors = 0; + int i, errors = 0; + CK_ULONG key_len; CK_OBJECT_HANDLE priv_key, pub_key; CK_MECHANISM_TYPE first_mech_type; CK_SESSION_INFO sessionInfo; @@ -4590,7 +8852,7 @@ static int test_verify(CK_SESSION_HANDLE sess) printf("Verify (currently only for RSA)\n"); - for (i = 0; find_object(sess, CKO_PRIVATE_KEY, &priv_key, NULL, 0, i); i++) { + for (i = 0; find_object(sess, CKO_PRIVATE_KEY, &priv_key, NULL, 0, NULL, i); i++) { char *label; unsigned char *id; CK_ULONG id_len; @@ -4614,7 +8876,7 @@ static int test_verify(CK_SESSION_HANDLE sess) if ((id = getID(sess, priv_key, &id_len)) != NULL) { int r; - r = find_object(sess, CKO_PUBLIC_KEY, &pub_key, id, id_len, 0); + r = find_object(sess, CKO_PUBLIC_KEY, &pub_key, id, id_len, NULL, 0); free(id); if (r == 0) { printf(" -- can't find corresponding public key, skipping\n"); @@ -4626,14 +8888,14 @@ static int test_verify(CK_SESSION_HANDLE sess) continue; } - key_len = (get_private_key_length(sess, priv_key) + 7) / 8; - if(!key_len) { + key_len = BYTES4BITS(get_private_key_length(sess, priv_key)); + if (!key_len || key_len > INT_MAX) { printf(" -- can't get the modulus length, skipping\n"); continue; } printf("\n"); - errors += sign_verify(sess, priv_key, key_len, pub_key, i != 0); + errors += sign_verify(sess, priv_key, (int)key_len, pub_key, i != 0); } if (i == 0) @@ -4642,6 +8904,8 @@ static int test_verify(CK_SESSION_HANDLE sess) return errors; } +#if OPENSC_VERSION_MAJOR == 0 && OPENSC_VERSION_MINOR <= 27 +#else #ifdef ENABLE_OPENSSL static int wrap_unwrap(CK_SESSION_HANDLE session, const EVP_CIPHER *algo, CK_OBJECT_HANDLE privKeyObject) @@ -4713,7 +8977,7 @@ static int wrap_unwrap(CK_SESSION_HANDLE session, /* Try to decrypt */ key = getVALUE(session, cipherKeyObject, &key_len_ul); - key_len = key_len_ul; + key_len = (int)key_len_ul; if (key == NULL) { fprintf(stderr, "Could not get unwrapped key\n"); return 1; @@ -4756,6 +9020,7 @@ static int wrap_unwrap(CK_SESSION_HANDLE session, return 0; } #endif +#endif /* @@ -4763,12 +9028,16 @@ static int wrap_unwrap(CK_SESSION_HANDLE session, */ static int test_unwrap(CK_SESSION_HANDLE sess) { +#if OPENSC_VERSION_MAJOR == 0 && OPENSC_VERSION_MINOR <= 27 + /* temporarily disable test, see https://github.com/OpenSC/OpenSC/issues/1796 */ + return 0; +#else int errors = 0; CK_RV rv; CK_OBJECT_HANDLE privKeyObject; CK_MECHANISM_TYPE firstMechType; CK_SESSION_INFO sessionInfo; - CK_ULONG j; + int j; char *label; rv = p11->C_GetSessionInfo(sess, &sessionInfo); @@ -4779,85 +9048,267 @@ static int test_unwrap(CK_SESSION_HANDLE sess) return errors; } - if (!find_mechanism(sessionInfo.slotID, CKF_UNWRAP | CKF_HW, NULL, 0, &firstMechType)) { + if (!find_mechanism(sessionInfo.slotID, CKF_UNWRAP|opt_allow_sw, NULL, 0, &firstMechType)) { printf("Unwrap: not implemented\n"); return errors; } printf("Key unwrap (currently only for RSA)\n"); - for (j = 0; find_object(sess, CKO_PRIVATE_KEY, &privKeyObject, NULL, 0, j); j++) { - printf(" testing key %ld ", j); + for (j = 0; find_object(sess, CKO_PRIVATE_KEY, &privKeyObject, NULL, 0, NULL, j); j++) { + printf(" testing key %d ", j); if ((label = getLABEL(sess, privKeyObject, NULL)) != NULL) { printf("(%s) ", label); free(label); } - if (getKEY_TYPE(sess, privKeyObject) != CKK_RSA) { - printf(" -- non-RSA, skipping\n"); - continue; + if (getKEY_TYPE(sess, privKeyObject) != CKK_RSA) { + printf(" -- non-RSA, skipping\n"); + continue; + } + if (!getUNWRAP(sess, privKeyObject)) { + printf(" -- can't be used to unwrap, skipping\n"); + continue; + } + printf("\n"); + +#ifndef ENABLE_OPENSSL + printf("No OpenSSL support, unable to validate C_Unwrap\n"); +#else + errors += wrap_unwrap(sess, EVP_des_cbc(), privKeyObject); + errors += wrap_unwrap(sess, EVP_des_ede3_cbc(), privKeyObject); + errors += wrap_unwrap(sess, EVP_bf_cbc(), privKeyObject); +#ifndef OPENSSL_NO_CAST + errors += wrap_unwrap(sess, EVP_cast5_cfb(), privKeyObject); +#endif +#endif + } + + return errors; +#endif +} + +#ifdef ENABLE_OPENSSL +static int encrypt_decrypt(CK_SESSION_HANDLE session, + CK_MECHANISM_TYPE mech_type, + CK_OBJECT_HANDLE privKeyObject, + char *param, int param_len) +{ + EVP_PKEY *pkey; + unsigned char orig_data[512]; + unsigned char encrypted[512], data[512]; + CK_MECHANISM mech; + CK_ULONG encrypted_len, data_len; + int failed; + CK_RV rv; + int pad; + CK_RSA_PKCS_OAEP_PARAMS oaep_params; + + printf(" %s: ", p11_mechanism_to_name(mech_type)); + + pseudo_randomize(orig_data, sizeof(orig_data)); + orig_data[0] = 0; /* Make sure it is less then modulus */ + + pkey = get_public_key(session, privKeyObject); + if (pkey == NULL) + return 0; + + if (EVP_PKEY_size(pkey) > (int)sizeof(encrypted)) { + printf("Ciphertext buffer too small\n"); + EVP_PKEY_free(pkey); + return 0; + } + size_t in_len; + size_t max_in_len; + CK_ULONG mod_len = BYTES4BITS(get_private_key_length(session, privKeyObject)); + switch (mech_type) { + case CKM_RSA_PKCS: + pad = RSA_PKCS1_PADDING; + /* input length <= mod_len-11 */ + max_in_len = mod_len-11; + in_len = 10; + break; + case CKM_RSA_PKCS_OAEP: + build_rsa_oaep_params(&oaep_params, &mech, param, param_len); + + pad = RSA_PKCS1_OAEP_PADDING; + size_t len = 2 + 2 * hash_length(oaep_params.hashAlg); + if (len >= mod_len) { + printf("Incompatible mechanism and key size\n"); + return 0; + } + /* input length <= mod_len-2-2*hlen */ + max_in_len = mod_len-len; + in_len = 10; + break; + case CKM_RSA_X_509: + pad = RSA_NO_PADDING; + /* input length equals modulus length */ + max_in_len = mod_len; + in_len = mod_len; + break; + default: + printf("Unsupported mechanism %s, returning\n", p11_mechanism_to_name(mech_type)); + return 0; + } + + if (in_len > sizeof(orig_data)) { + printf("Input data is too large\n"); + return 0; + } + if (in_len > max_in_len) { + printf("Input data is too large for this key\n"); + return 0; + } + + EVP_PKEY_CTX *ctx; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + ctx = EVP_PKEY_CTX_new_from_pkey(osslctx, pkey, NULL); +#else + ctx = EVP_PKEY_CTX_new(pkey, NULL); +#endif + if (!ctx) { + EVP_PKEY_free(pkey); + printf("EVP_PKEY_CTX_new failed, returning\n"); + return 0; + } + if (EVP_PKEY_encrypt_init(ctx) <= 0) { + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pkey); + printf("EVP_PKEY_encrypt_init failed, returning\n"); + return 0; + } + if (EVP_PKEY_CTX_set_rsa_padding(ctx, pad) <= 0) { + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pkey); + printf("set padding failed, returning\n"); + return 0; + } + if (mech_type == CKM_RSA_PKCS_OAEP) { +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + const EVP_MD *md; + switch (oaep_params.hashAlg) { + case CKM_SHA_1: + md = EVP_sha1(); + break; + case CKM_SHA224: + md = EVP_sha224(); + break; + default: /* it should not happen, oaep_params.hashAlg is checked earlier */ + /* fall through */ + case CKM_SHA256: + md = EVP_sha256(); + break; + case CKM_SHA384: + md = EVP_sha384(); + break; + case CKM_SHA512: + md = EVP_sha512(); + break; + case CKM_SHA3_224: + md = EVP_sha3_224(); + break; + case CKM_SHA3_256: + md = EVP_sha3_256(); + break; + case CKM_SHA3_384: + md = EVP_sha3_384(); + break; + case CKM_SHA3_512: + md = EVP_sha3_512(); + break; + } + if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) <= 0) { + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pkey); + printf("set md failed, returning\n"); + return 0; + } + switch (oaep_params.mgf) { + case CKG_MGF1_SHA1: + md = EVP_sha1(); + break; + case CKG_MGF1_SHA224: + md = EVP_sha224(); + break; + case CKG_MGF1_SHA256: + md = EVP_sha256(); + break; + case CKG_MGF1_SHA384: + md = EVP_sha384(); + break; + case CKG_MGF1_SHA512: + md = EVP_sha512(); + break; + case CKG_MGF1_SHA3_224: + md = EVP_sha3_224(); + break; + case CKG_MGF1_SHA3_256: + md = EVP_sha3_256(); + break; + case CKG_MGF1_SHA3_384: + md = EVP_sha3_384(); + break; + case CKG_MGF1_SHA3_512: + md = EVP_sha3_512(); + break; + } + if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, md) <= 0) { + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pkey); + printf("set mgf1 md failed, returning\n"); + return 0; } - if (!getUNWRAP(sess, privKeyObject)) { - printf(" -- can't be used to unwrap, skipping\n"); - continue; + if (param_len != 0 && param != NULL) { + /* label is in ownership of openssl, do not free this ptr! */ + char *label = malloc(param_len); + memcpy(label, param, param_len); + + if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label, param_len) <= 0) { + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pkey); + printf("set OAEP label failed, returning\n"); + return 0; + } } - printf("\n"); - -#ifndef ENABLE_OPENSSL - printf("No OpenSSL support, unable to validate C_Unwrap\n"); #else - errors += wrap_unwrap(sess, EVP_des_cbc(), privKeyObject); - errors += wrap_unwrap(sess, EVP_des_ede3_cbc(), privKeyObject); - errors += wrap_unwrap(sess, EVP_bf_cbc(), privKeyObject); - errors += wrap_unwrap(sess, EVP_cast5_cfb(), privKeyObject); + if (oaep_params.hashAlg != CKM_SHA_1) { + printf("This version of OpenSSL only supports SHA1 for OAEP, returning\n"); + return 0; + } #endif } - return errors; -} - -#ifdef ENABLE_OPENSSL -static int encrypt_decrypt(CK_SESSION_HANDLE session, - CK_MECHANISM_TYPE mech_type, - CK_OBJECT_HANDLE privKeyObject) -{ - EVP_PKEY *pkey; - unsigned char orig_data[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', '\0'}; - unsigned char encrypted[512], data[512]; - CK_MECHANISM mech; - CK_ULONG encrypted_len, data_len; - int failed; - CK_RV rv; - - printf(" %s: ", p11_mechanism_to_name(mech_type)); - - pkey = get_public_key(session, privKeyObject); - if (pkey == NULL) - return 0; - - if (EVP_PKEY_size(pkey) > (int)sizeof(encrypted)) { - fprintf(stderr, "Ciphertext buffer too small\n"); + size_t out_len = sizeof(encrypted); + if (EVP_PKEY_encrypt(ctx, encrypted, &out_len, orig_data, in_len) <= 0) { + EVP_PKEY_CTX_free(ctx); EVP_PKEY_free(pkey); + printf("Encryption failed, returning\n"); return 0; } -#if OPENSSL_VERSION_NUMBER >= 0x00909000L - encrypted_len = EVP_PKEY_encrypt_old(encrypted, orig_data, sizeof(orig_data), pkey); -#else - encrypted_len = EVP_PKEY_encrypt(encrypted, orig_data, sizeof(orig_data), pkey); -#endif + EVP_PKEY_CTX_free(ctx); EVP_PKEY_free(pkey); - if (((int) encrypted_len) <= 0) { - fprintf(stderr, "Encryption failed, returning\n"); - return 0; + encrypted_len = out_len; + + switch (mech_type) { + case CKM_RSA_PKCS_OAEP: + break; + case CKM_RSA_X_509: + case CKM_RSA_PKCS: + mech.pParameter = NULL; + mech.ulParameterLen = 0; + break; + default: + util_fatal("Mechanism %s illegal or not supported\n", p11_mechanism_to_name(mech_type)); } mech.mechanism = mech_type; rv = p11->C_DecryptInit(session, &mech, privKeyObject); - if (rv == CKR_MECHANISM_INVALID) { - fprintf(stderr, "Mechanism not supported\n"); + if (rv == CKR_MECHANISM_INVALID || rv == CKR_MECHANISM_PARAM_INVALID) { + printf("Mechanism not supported\n"); return 0; } if (rv != CKR_OK) p11_fatal("C_DecryptInit", rv); - if (getALWAYS_AUTHENTICATE(session, privKeyObject)) + if ((getCLASS(session, privKeyObject) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, privKeyObject)) login(session,CKU_CONTEXT_SPECIFIC); data_len = encrypted_len; @@ -4865,24 +9316,14 @@ static int encrypt_decrypt(CK_SESSION_HANDLE session, if (rv != CKR_OK) p11_fatal("C_Decrypt", rv); - if (mech_type == CKM_RSA_X_509) - failed = (data[0] != 0) || (data[1] != 2) || (data_len <= sizeof(orig_data) - 2) || - memcmp(orig_data, data + data_len - sizeof(orig_data), sizeof(orig_data)); - else - failed = data_len != sizeof(orig_data) || memcmp(orig_data, data, data_len); + failed = data_len != in_len || memcmp(orig_data, data, data_len); if (failed) { - CK_ULONG n; - printf("resulting cleartext doesn't match input\n"); printf(" Original:"); - for (n = 0; n < sizeof(orig_data); n++) - printf(" %02x", orig_data[n]); - printf("\n"); + print_hex(orig_data, in_len, 0, true); printf(" Decrypted:"); - for (n = 0; n < data_len; n++) - printf(" %02x", data[n]); - printf("\n"); + print_hex(data, data_len, 0, true); return 1; } @@ -4899,10 +9340,12 @@ static int test_decrypt(CK_SESSION_HANDLE sess) { int errors = 0; CK_RV rv; - CK_OBJECT_HANDLE privKeyObject; + unsigned char *id; + CK_OBJECT_HANDLE pubKeyObject, privKeyObject; CK_MECHANISM_TYPE *mechs = NULL; CK_SESSION_INFO sessionInfo; - CK_ULONG j, num_mechs = 0; + CK_ULONG num_mechs = 0, id_len; + int j; #ifdef ENABLE_OPENSSL CK_ULONG n; #endif @@ -4923,10 +9366,10 @@ static int test_decrypt(CK_SESSION_HANDLE sess) } printf("Decryption (currently only for RSA)\n"); - for (j = 0; find_object(sess, CKO_PRIVATE_KEY, &privKeyObject, NULL, 0, j); j++) { - printf(" testing key %ld ", j); + for (j = 0; find_object(sess, CKO_PRIVATE_KEY, &privKeyObject, NULL, 0, NULL, j); j++) { + printf(" testing key %d", j); if ((label = getLABEL(sess, privKeyObject, NULL)) != NULL) { - printf("(%s) ", label); + printf(" (%s)", label); free(label); } if (getKEY_TYPE(sess, privKeyObject) != CKK_RSA) { @@ -4937,13 +9380,43 @@ static int test_decrypt(CK_SESSION_HANDLE sess) printf(" -- can't be used to decrypt, skipping\n"); continue; } + + if ((id = getID(sess, privKeyObject, &id_len)) != NULL) { + int r; + + r = find_object(sess, CKO_PUBLIC_KEY, &pubKeyObject, id, id_len, NULL, 0); + free(id); + if (r == 0) { + printf(" -- can't find corresponding public key, skipping\n"); + continue; + } + } + else { + printf(" -- can't get the ID for looking up the public key, skipping\n"); + continue; + } + printf("\n"); #ifndef ENABLE_OPENSSL printf("No OpenSSL support, unable to validate decryption\n"); #else for (n = 0; n < num_mechs; n++) { - errors += encrypt_decrypt(sess, mechs[n], privKeyObject); + switch (mechs[n]) { + case CKM_RSA_PKCS_OAEP: + /* one more OAEP test with param .. */ + /* SoftHSM2 and maybe others fail with pSourceData */ + /* the pSourceData is not used so the C_Decrypt fails with CKR_GENERAL_ERROR */ + // errors += encrypt_decrypt(sess, mechs[n], privKeyObject, "ABC", 3)) != 0); + printf(" -- CKM_RSA_PKCS_OAEP with pSourceData != NULL, skipping\n"); + /* fall through */ + case CKM_RSA_PKCS: + case CKM_RSA_X_509: + errors += encrypt_decrypt(sess, mechs[n], privKeyObject, NULL, 0); + break; + default: + printf(" -- mechanism can't be used to decrypt, skipping\n"); + } } #endif } @@ -4971,41 +9444,42 @@ static int test_random(CK_SESSION_HANDLE session) printf(" seeding (C_SeedRandom) not supported\n"); else if (rv != CKR_OK) { p11_perror("C_SeedRandom", rv); - return 1; + errors++; } rv = p11->C_GenerateRandom(session, buf1, 10); if (rv != CKR_OK) { p11_perror("C_GenerateRandom", rv); - return 1; + errors++; } rv = p11->C_GenerateRandom(session, buf1, 100); if (rv != CKR_OK) { p11_perror("C_GenerateRandom(buf1,100)", rv); - return 1; + errors++; } rv = p11->C_GenerateRandom(session, buf1, 0); if (rv != CKR_OK) { p11_perror("C_GenerateRandom(buf1,0)", rv); - return 1; + errors++; } rv = p11->C_GenerateRandom(session, buf2, 100); if (rv != CKR_OK) { p11_perror("C_GenerateRandom(buf2,100)", rv); - return 1; + errors++; } - if (memcmp(buf1, buf2, 100) == 0) { + if (errors == 0 && memcmp(buf1, buf2, 100) == 0) { printf(" ERR: C_GenerateRandom returned twice the same value!!!\n"); errors++; } - printf(" seems to be OK\n"); + if (!errors) + printf(" seems to be OK\n"); - return 0; + return errors; } static int test_card_detection(int wait_for_event) @@ -5049,6 +9523,8 @@ static int p11_test(CK_SESSION_HANDLE session) errors += test_digest(session); + errors += test_cipher(session); + errors += test_signature(session); errors += test_verify(session); @@ -5091,6 +9567,7 @@ static CK_SESSION_HANDLE test_kpgen_certwrite(CK_SLOT_ID slot, CK_SESSION_HANDLE {CKA_SUBJECT, (void *) "This won't be used in our lib", 29} }; FILE *f; + CK_FUNCTION_LIST_PTR p11_v2 = NULL; if (!opt_object_id_len) { fprintf(stderr, "ERR: must give an ID, e.g.: --id 01\n"); @@ -5118,7 +9595,7 @@ static CK_SESSION_HANDLE test_kpgen_certwrite(CK_SLOT_ID slot, CK_SESSION_HANDLE fclose(f); /* Get for a not-yet-existing ID */ - while(find_object(session, CKO_PRIVATE_KEY, &priv_key, id, id_len, 0)) + while(find_object(session, CKO_PRIVATE_KEY, &priv_key, id, id_len, NULL, 0)) id[0]++; printf("\n*** Generating a %s key pair ***\n", opt_key_type); @@ -5128,16 +9605,21 @@ static CK_SESSION_HANDLE test_kpgen_certwrite(CK_SLOT_ID slot, CK_SESSION_HANDLE return session; } - tmp = getID(session, priv_key, (CK_ULONG *) &opt_object_id_len); - if (opt_object_id_len == 0) { + tmp = getID(session, priv_key, &i); + if (i == 0) { fprintf(stderr, "ERR: newly generated private key has no (or an empty) CKA_ID\n"); return session; } + opt_object_id_len = (size_t) i; + if (opt_object_id_len > sizeof(opt_object_id)) { + fprintf(stderr, "ERR: object ID too long\n"); + return session; + } memcpy(opt_object_id, tmp, opt_object_id_len); /* This is done in NSS */ getMODULUS(session, priv_key, &mod_len); - if (mod_len < 5 || mod_len > 10000) { /* should be resonable limits */ + if (mod_len < 5 || mod_len > 10000) { /* should be reasonable limits */ fprintf(stderr, "ERR: GetAttribute(privkey, CKA_MODULUS) doesn't seem to work\n"); return session; } @@ -5159,7 +9641,7 @@ static CK_SESSION_HANDLE test_kpgen_certwrite(CK_SLOT_ID slot, CK_SESSION_HANDLE rv = p11->C_SignInit(session, &mech, priv_key); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); - if (getALWAYS_AUTHENTICATE(session, priv_key)) + if ((getCLASS(session, priv_key) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, priv_key)) login(session,CKU_CONTEXT_SPECIFIC); rv = p11->C_Sign(session, data, data_len, NULL, &sig_len); @@ -5191,8 +9673,9 @@ static CK_SESSION_HANDLE test_kpgen_certwrite(CK_SLOT_ID slot, CK_SESSION_HANDLE rv = p11->C_SignInit(session, &mech, priv_key); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); - if (getALWAYS_AUTHENTICATE(session, priv_key)) + if ((getCLASS(session, priv_key) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, priv_key)) login(session,CKU_CONTEXT_SPECIFIC); + rv = p11->C_Sign(session, data, data_len, sig, &sig_len); if (rv != CKR_OK) p11_fatal("C_Sign", rv); @@ -5225,17 +9708,18 @@ static CK_SESSION_HANDLE test_kpgen_certwrite(CK_SLOT_ID slot, CK_SESSION_HANDLE C_UnloadModule(module); /* Now we assume the user turns of her PC and comes back tomorrow to see - * if here cert is allready made and to install it (as is done next) */ + * if here cert is already made and to install it (as is done next) */ printf("\n*** In real life, the cert req should now be sent to the CA ***\n"); printf("\n*** Loading the pkcs11 lib, opening a session and logging in ***\n"); - module = C_LoadModule(opt_module, &p11); + module = C_LoadModule(opt_module, &p11_v2); if (module == NULL) util_fatal("Failed to load pkcs11 module"); + p11 = (CK_FUNCTION_LIST_3_0_PTR ) p11_v2; - rv = p11->C_Initialize(NULL); + rv = p11->C_Initialize(c_initialize_args_ptr); if (rv == CKR_CRYPTOKI_ALREADY_INITIALIZED) printf("\n*** Cryptoki library has already been initialized ***\n"); else if (rv != CKR_OK) @@ -5259,7 +9743,7 @@ static CK_SESSION_HANDLE test_kpgen_certwrite(CK_SLOT_ID slot, CK_SESSION_HANDLE if (!delete_object(session)) util_fatal("Failed to delete certificate"); - printf("\n==> OK, successfull! Should work with Mozilla\n"); + printf("\n==> OK, successful! Should work with Mozilla\n"); return session; } @@ -5309,11 +9793,16 @@ static void test_ec(CK_SLOT_ID slot, CK_SESSION_HANDLE session) if (!gen_keypair(slot, session, &pub_key, &priv_key, opt_key_type)) return; - tmp = getID(session, priv_key, (CK_ULONG *) &opt_object_id_len); - if (opt_object_id_len == 0) { + tmp = getID(session, priv_key, &i); + if (i == 0) { printf("ERR: newly generated private key has no (or an empty) CKA_ID\n"); return; } + opt_object_id_len = (size_t)i; + if (opt_object_id_len > sizeof(opt_object_id)) { + fprintf(stderr, "ERR: object ID too long\n"); + return; + } memcpy(opt_object_id, tmp, opt_object_id_len); /* This is done in NSS */ @@ -5323,6 +9812,10 @@ static void test_ec(CK_SLOT_ID slot, CK_SESSION_HANDLE session) return; } getEC_POINT(session, pub_key, &ec_point_len); + /* TODO if this routine us expanded to test EDDSA keys the following may be needed. + * a per 3.0 errata and 3.1 Edwards and Montgomery EC_POINT is just a byte string. + * Accept either BIT STRING, OCTET STRING or raw byte string. + */ if (ec_point_len < 5 || ec_point_len > 10000) { printf("ERR: GetAttribute(pubkey, CKA_EC_POINT) doesn't seem to work\n"); return; @@ -5343,7 +9836,7 @@ static void test_ec(CK_SLOT_ID slot, CK_SESSION_HANDLE session) rv = p11->C_SignInit(session, &mech, priv_key); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); - if (getALWAYS_AUTHENTICATE(session, priv_key)) + if ((getCLASS(session, priv_key) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, priv_key)) login(session,CKU_CONTEXT_SPECIFIC); rv = p11->C_Sign(session, data, data_len, NULL, &sig_len); if (rv != CKR_OK) @@ -5363,7 +9856,7 @@ static void test_ec(CK_SLOT_ID slot, CK_SESSION_HANDLE session) rv = p11->C_SignInit(session, &mech, priv_key); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); - if (getALWAYS_AUTHENTICATE(session, priv_key)) + if ((getCLASS(session, priv_key) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, priv_key)) login(session,CKU_CONTEXT_SPECIFIC); rv = p11->C_Sign(session, data, data_len, sig, &sig_len); if (rv != CKR_OK) @@ -5393,7 +9886,7 @@ static void test_fork(void) if (!pid) { printf("*** Calling C_Initialize in forked child process ***\n"); - rv = p11->C_Initialize(NULL); + rv = p11->C_Initialize(c_initialize_args_ptr); if (rv != CKR_OK) p11_fatal("C_Initialize in child\n", rv); exit(0); @@ -5516,6 +10009,86 @@ static const char *p11_utf8_to_local(CK_UTF8CHAR *string, size_t len) return buffer; } +static char * +p11_utf8_to_string(CK_UTF8CHAR *string, size_t len) +{ + char *buffer = NULL; + size_t n, m; + + while (len && string[len - 1] == ' ') + len--; + + if (len < 1 || !(buffer = calloc(len + 1, sizeof(char)))) { + return NULL; + } + + /* For now, simply copy this thing */ + for (n = m = 0; n < len; n++) { + if (m >= len) + break; + buffer[n] = string[m++]; + } + buffer[n] = '\0'; + return buffer; +} + +static CK_BBOOL +p11_is_percent_format_reserved_char(CK_UTF8CHAR c) +{ + switch (c) { + case ' ': + case '!': + case '"': + case '#': + case '$': + case '%': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case '/': + case ':': + case ';': + case '=': + case '?': + case '@': + case '[': + case ']': + return CK_TRUE; + } + return CK_FALSE; +} + +static const char * +percent_encode(CK_UTF8CHAR *string, size_t len) +{ + static char buffer[1024]; + memset(buffer, 0, 1024); + size_t output_index, input_index; + + while (len && string[len - 1] == ' ') + len--; + + for (output_index = input_index = 0; output_index < sizeof(buffer) - 3; + output_index++) { + if (input_index >= len) { + break; + } + if (p11_is_percent_format_reserved_char(string[input_index])) { + snprintf(&buffer[output_index], 4, "%%%x", string[input_index]); + output_index += 2; + } else { + buffer[output_index] = string[input_index]; + } + input_index++; + } + buffer[output_index] = '\0'; + return buffer; +} + static void p11_fatal(const char *func, CK_RV rv) { if (p11) @@ -5537,56 +10110,51 @@ static void p11_perror(const char *msg, CK_RV rv) fprintf(stderr, " ERR: %s failed: %s (0x%0x)\n", msg, CKR2Str(rv), (unsigned int) rv); } -static int hex_to_bin(const char *in, unsigned char *out, size_t *outlen) +#define MAX_HEX_STR_LEN (1U << 16) // Arbitrary, GCM IV and AAD can theoretically be much bigger +static CK_BYTE_PTR +hex_string_to_byte_array(const char *hex_input, size_t *input_size, const char *buffer_name) { - size_t left, count = 0; - int nybbles = 2; + CK_BYTE_PTR array; + size_t size = 0; - if (in == NULL || *in == '\0') { - *outlen = 0; - return 1; + /* no hex string supplied on command line */ + if (!hex_input) { + *input_size = 0; + return NULL; } - left = *outlen; + /* If no length is provided, determine the length of the hex string */ + if (*input_size == 0) { + size = strnlen(hex_input, MAX_HEX_STR_LEN); + if (size % 2 != 0) { + fprintf(stderr, "Odd length, provided %s is an invalid hex string.\n", buffer_name); + return NULL; + } + *input_size = size / 2; + } - if (strlen(in) % 2) - nybbles = 1; // any leading zero in output should be in most-significant byte, not last one! - while (*in != '\0') { - int byte = 0; + size = *input_size; - while (nybbles-- && *in && *in != ':') { - char c; - byte <<= 4; - c = *in++; - if ('0' <= c && c <= '9') - c -= '0'; - else - if ('a' <= c && c <= 'f') - c = c - 'a' + 10; - else - if ('A' <= c && c <= 'F') - c = c - 'A' + 10; - else { - fprintf(stderr, "hex_to_bin(): invalid char '%c' in hex string\n", c); - *outlen = 0; - return 0; - } - byte |= c; - } - if (*in == ':') - in++; - if (left <= 0) { - fprintf(stderr, "hex_to_bin(): hex string too long"); - *outlen = 0; - return 0; - } - out[count++] = (unsigned char) byte; - left--; - nybbles = 2; + array = calloc(*input_size, sizeof(CK_BYTE)); + if (!array) { + fprintf(stderr, "Warning, out of memory, %s will not be used.\n", buffer_name); + *input_size = 0; + return NULL; } - *outlen = count; - return 1; + if (sc_hex_to_bin(hex_input, array, &size)) { + fprintf(stderr, "Warning, unable to parse %s, %s will not be used.\n", + buffer_name, buffer_name); + *input_size = 0; + free(array); + return NULL; + } + + if (*input_size != size) + fprintf(stderr, "Warning: %s string is too short, %s will be padded from the right by zeros.\n", + buffer_name, buffer_name); + + return array; } static void pseudo_randomize(unsigned char *data, size_t dataLen) @@ -5600,200 +10168,321 @@ static void pseudo_randomize(unsigned char *data, size_t dataLen) } } +// clang-format off static struct mech_info p11_mechanisms[] = { - { CKM_RSA_PKCS_KEY_PAIR_GEN, "RSA-PKCS-KEY-PAIR-GEN", NULL }, - { CKM_RSA_PKCS, "RSA-PKCS", NULL }, - { CKM_RSA_9796, "RSA-9796", NULL }, - { CKM_RSA_X_509, "RSA-X-509", NULL }, - { CKM_MD2_RSA_PKCS, "MD2-RSA-PKCS", NULL }, - { CKM_MD5_RSA_PKCS, "MD5-RSA-PKCS", "rsa-md5" }, - { CKM_SHA1_RSA_PKCS, "SHA1-RSA-PKCS", "rsa-sha1" }, - { CKM_SHA256_RSA_PKCS, "SHA256-RSA-PKCS", "rsa-sha256" }, - { CKM_SHA384_RSA_PKCS, "SHA384-RSA-PKCS", "rsa-sha384" }, - { CKM_SHA512_RSA_PKCS, "SHA512-RSA-PKCS", "rsa-sha512" }, - { CKM_RIPEMD128_RSA_PKCS, "RIPEMD128-RSA-PKCS", NULL }, - { CKM_RIPEMD160_RSA_PKCS, "RIPEMD160-RSA-PKCS", "rsa-ripemd160" }, - { CKM_RSA_PKCS_OAEP, "RSA-PKCS-OAEP", NULL }, - { CKM_RSA_X9_31_KEY_PAIR_GEN,"RSA-X9-31-KEY-PAIR-GEN", NULL }, - { CKM_RSA_X9_31, "RSA-X9-31", NULL }, - { CKM_SHA1_RSA_X9_31, "SHA1-RSA-X9-31", NULL }, - { CKM_RSA_PKCS_PSS, "RSA-PKCS-PSS", NULL }, - { CKM_SHA1_RSA_PKCS_PSS, "SHA1-RSA-PKCS-PSS", NULL }, - { CKM_SHA256_RSA_PKCS, "SHA256-RSA-PKCS-PSS", NULL }, - { CKM_SHA384_RSA_PKCS, "SHA384-RSA-PKCS-PSS", NULL }, - { CKM_SHA512_RSA_PKCS, "SHA512-RSA-PKCS-PSS", NULL }, - { CKM_DSA_KEY_PAIR_GEN, "DSA-KEY-PAIR-GEN", NULL }, - { CKM_DSA, "DSA", NULL }, - { CKM_DSA_SHA1, "DSA-SHA1", NULL }, - { CKM_DH_PKCS_KEY_PAIR_GEN,"DH-PKCS-KEY-PAIR-GEN", NULL }, - { CKM_DH_PKCS_DERIVE, "DH-PKCS-DERIVE", NULL }, - { CKM_X9_42_DH_KEY_PAIR_GEN,"X9-42-DH-KEY-PAIR-GEN", NULL }, - { CKM_X9_42_DH_DERIVE, "X9-42-DH-DERIVE", NULL }, - { CKM_X9_42_DH_HYBRID_DERIVE,"X9-42-DH-HYBRID-DERIVE", NULL }, - { CKM_X9_42_MQV_DERIVE, "X9-42-MQV-DERIVE", NULL }, - { CKM_RC2_KEY_GEN, "RC2-KEY-GEN", NULL }, - { CKM_RC2_ECB, "RC2-ECB", NULL }, - { CKM_RC2_CBC, "RC2-CBC", NULL }, - { CKM_RC2_MAC, "RC2-MAC", NULL }, - { CKM_RC2_MAC_GENERAL, "RC2-MAC-GENERAL", NULL }, - { CKM_RC2_CBC_PAD, "RC2-CBC-PAD", NULL }, - { CKM_RC4_KEY_GEN, "RC4-KEY-GEN", NULL }, - { CKM_RC4, "RC4", NULL }, - { CKM_DES_KEY_GEN, "DES-KEY-GEN", NULL }, - { CKM_DES_ECB, "DES-ECB", NULL }, - { CKM_DES_CBC, "DES-CBC", NULL }, - { CKM_DES_MAC, "DES-MAC", NULL }, - { CKM_DES_MAC_GENERAL, "DES-MAC-GENERAL", NULL }, - { CKM_DES_CBC_PAD, "DES-CBC-PAD", NULL }, - { CKM_DES2_KEY_GEN, "DES2-KEY-GEN", NULL }, - { CKM_DES3_KEY_GEN, "DES3-KEY-GEN", NULL }, - { CKM_DES3_ECB, "DES3-ECB", NULL }, - { CKM_DES3_CBC, "DES3-CBC", NULL }, - { CKM_DES3_MAC, "DES3-MAC", NULL }, - { CKM_DES3_MAC_GENERAL, "DES3-MAC-GENERAL", NULL }, - { CKM_DES3_CBC_PAD, "DES3-CBC-PAD", NULL }, - { CKM_CDMF_KEY_GEN, "CDMF-KEY-GEN", NULL }, - { CKM_CDMF_ECB, "CDMF-ECB", NULL }, - { CKM_CDMF_CBC, "CDMF-CBC", NULL }, - { CKM_CDMF_MAC, "CDMF-MAC", NULL }, - { CKM_CDMF_MAC_GENERAL, "CDMF-MAC-GENERAL", NULL }, - { CKM_CDMF_CBC_PAD, "CDMF-CBC-PAD", NULL }, - { CKM_MD2, "MD2", NULL }, - { CKM_MD2_HMAC, "MD2-HMAC", NULL }, - { CKM_MD2_HMAC_GENERAL, "MD2-HMAC-GENERAL", NULL }, - { CKM_MD5, "MD5", NULL }, - { CKM_MD5_HMAC, "MD5-HMAC", NULL }, - { CKM_MD5_HMAC_GENERAL, "MD5-HMAC-GENERAL", NULL }, - { CKM_SHA_1, "SHA-1", NULL }, - { CKM_SHA_1_HMAC, "SHA-1-HMAC", NULL }, - { CKM_SHA_1_HMAC_GENERAL, "SHA-1-HMAC-GENERAL", NULL }, - { CKM_SHA256, "SHA256", NULL }, - { CKM_SHA384, "SHA384", NULL }, - { CKM_SHA512, "SHA512", NULL }, - { CKM_RIPEMD128, "RIPEMD128", NULL }, - { CKM_RIPEMD128_HMAC, "RIPEMD128-HMAC", NULL }, - { CKM_RIPEMD128_HMAC_GENERAL,"RIPEMD128-HMAC-GENERAL", NULL }, - { CKM_RIPEMD160, "RIPEMD160", NULL }, - { CKM_RIPEMD160_HMAC, "RIPEMD160-HMAC", NULL }, - { CKM_RIPEMD160_HMAC_GENERAL,"RIPEMD160-HMAC-GENERAL", NULL }, - { CKM_CAST_KEY_GEN, "CAST-KEY-GEN", NULL }, - { CKM_CAST_ECB, "CAST-ECB", NULL }, - { CKM_CAST_CBC, "CAST-CBC", NULL }, - { CKM_CAST_MAC, "CAST-MAC", NULL }, - { CKM_CAST_MAC_GENERAL, "CAST-MAC-GENERAL", NULL }, - { CKM_CAST_CBC_PAD, "CAST-CBC-PAD", NULL }, - { CKM_CAST3_KEY_GEN, "CAST3-KEY-GEN", NULL }, - { CKM_CAST3_ECB, "CAST3-ECB", NULL }, - { CKM_CAST3_CBC, "CAST3-CBC", NULL }, - { CKM_CAST3_MAC, "CAST3-MAC", NULL }, - { CKM_CAST3_MAC_GENERAL, "CAST3-MAC-GENERAL", NULL }, - { CKM_CAST3_CBC_PAD, "CAST3-CBC-PAD", NULL }, - { CKM_CAST5_KEY_GEN, "CAST5-KEY-GEN", NULL }, - { CKM_CAST5_ECB, "CAST5-ECB", NULL }, - { CKM_CAST5_CBC, "CAST5-CBC", NULL }, - { CKM_CAST5_MAC, "CAST5-MAC", NULL }, - { CKM_CAST5_MAC_GENERAL, "CAST5-MAC-GENERAL", NULL }, - { CKM_CAST5_CBC_PAD, "CAST5-CBC-PAD", NULL }, - { CKM_RC5_KEY_GEN, "RC5-KEY-GEN", NULL }, - { CKM_RC5_ECB, "RC5-ECB", NULL }, - { CKM_RC5_CBC, "RC5-CBC", NULL }, - { CKM_RC5_MAC, "RC5-MAC", NULL }, - { CKM_RC5_MAC_GENERAL, "RC5-MAC-GENERAL", NULL }, - { CKM_RC5_CBC_PAD, "RC5-CBC-PAD", NULL }, - { CKM_IDEA_KEY_GEN, "IDEA-KEY-GEN", NULL }, - { CKM_IDEA_ECB, "IDEA-ECB", NULL }, - { CKM_IDEA_CBC, "IDEA-CBC", NULL }, - { CKM_IDEA_MAC, "IDEA-MAC", NULL }, - { CKM_IDEA_MAC_GENERAL, "IDEA-MAC-GENERAL", NULL }, - { CKM_IDEA_CBC_PAD, "IDEA-CBC-PAD", NULL }, - { CKM_GENERIC_SECRET_KEY_GEN,"GENERIC-SECRET-KEY-GEN", NULL }, - { CKM_CONCATENATE_BASE_AND_KEY,"CONCATENATE-BASE-AND-KEY", NULL }, - { CKM_CONCATENATE_BASE_AND_DATA,"CONCATENATE-BASE-AND-DATA", NULL }, - { CKM_CONCATENATE_DATA_AND_BASE,"CONCATENATE-DATA-AND-BASE", NULL }, - { CKM_XOR_BASE_AND_DATA, "XOR-BASE-AND-DATA", NULL }, - { CKM_EXTRACT_KEY_FROM_KEY,"EXTRACT-KEY-FROM-KEY", NULL }, - { CKM_SSL3_PRE_MASTER_KEY_GEN,"SSL3-PRE-MASTER-KEY-GEN", NULL }, - { CKM_SSL3_MASTER_KEY_DERIVE,"SSL3-MASTER-KEY-DERIVE", NULL }, - { CKM_SSL3_KEY_AND_MAC_DERIVE,"SSL3-KEY-AND-MAC-DERIVE", NULL }, - { CKM_SSL3_MASTER_KEY_DERIVE_DH,"SSL3-MASTER-KEY-DERIVE-DH", NULL }, - { CKM_TLS_PRE_MASTER_KEY_GEN,"TLS-PRE-MASTER-KEY-GEN", NULL }, - { CKM_TLS_MASTER_KEY_DERIVE,"TLS-MASTER-KEY-DERIVE", NULL }, - { CKM_TLS_KEY_AND_MAC_DERIVE,"TLS-KEY-AND-MAC-DERIVE", NULL }, - { CKM_TLS_MASTER_KEY_DERIVE_DH,"TLS-MASTER-KEY-DERIVE-DH", NULL }, - { CKM_SSL3_MD5_MAC, "SSL3-MD5-MAC", NULL }, - { CKM_SSL3_SHA1_MAC, "SSL3-SHA1-MAC", NULL }, - { CKM_MD5_KEY_DERIVATION, "MD5-KEY-DERIVATION", NULL }, - { CKM_MD2_KEY_DERIVATION, "MD2-KEY-DERIVATION", NULL }, - { CKM_SHA1_KEY_DERIVATION,"SHA1-KEY-DERIVATION", NULL }, - { CKM_PBE_MD2_DES_CBC, "PBE-MD2-DES-CBC", NULL }, - { CKM_PBE_MD5_DES_CBC, "PBE-MD5-DES-CBC", NULL }, - { CKM_PBE_MD5_CAST_CBC, "PBE-MD5-CAST-CBC", NULL }, - { CKM_PBE_MD5_CAST3_CBC, "PBE-MD5-CAST3-CBC", NULL }, - { CKM_PBE_MD5_CAST5_CBC, "PBE-MD5-CAST5-CBC", NULL }, - { CKM_PBE_SHA1_CAST5_CBC, "PBE-SHA1-CAST5-CBC", NULL }, - { CKM_PBE_SHA1_RC4_128, "PBE-SHA1-RC4-128", NULL }, - { CKM_PBE_SHA1_RC4_40, "PBE-SHA1-RC4-40", NULL }, - { CKM_PBE_SHA1_DES3_EDE_CBC,"PBE-SHA1-DES3-EDE-CBC", NULL }, - { CKM_PBE_SHA1_DES2_EDE_CBC,"PBE-SHA1-DES2-EDE-CBC", NULL }, - { CKM_PBE_SHA1_RC2_128_CBC,"PBE-SHA1-RC2-128-CBC", NULL }, - { CKM_PBE_SHA1_RC2_40_CBC,"PBE-SHA1-RC2-40-CBC", NULL }, - { CKM_PKCS5_PBKD2, "PKCS5-PBKD2", NULL }, - { CKM_PBA_SHA1_WITH_SHA1_HMAC,"PBA-SHA1-WITH-SHA1-HMAC", NULL }, - { CKM_KEY_WRAP_LYNKS, "KEY-WRAP-LYNKS", NULL }, - { CKM_KEY_WRAP_SET_OAEP, "KEY-WRAP-SET-OAEP", NULL }, - { CKM_SKIPJACK_KEY_GEN, "SKIPJACK-KEY-GEN", NULL }, - { CKM_SKIPJACK_ECB64, "SKIPJACK-ECB64", NULL }, - { CKM_SKIPJACK_CBC64, "SKIPJACK-CBC64", NULL }, - { CKM_SKIPJACK_OFB64, "SKIPJACK-OFB64", NULL }, - { CKM_SKIPJACK_CFB64, "SKIPJACK-CFB64", NULL }, - { CKM_SKIPJACK_CFB32, "SKIPJACK-CFB32", NULL }, - { CKM_SKIPJACK_CFB16, "SKIPJACK-CFB16", NULL }, - { CKM_SKIPJACK_CFB8, "SKIPJACK-CFB8", NULL }, - { CKM_SKIPJACK_WRAP, "SKIPJACK-WRAP", NULL }, - { CKM_SKIPJACK_PRIVATE_WRAP,"SKIPJACK-PRIVATE-WRAP", NULL }, - { CKM_SKIPJACK_RELAYX, "SKIPJACK-RELAYX", NULL }, - { CKM_KEA_KEY_PAIR_GEN, "KEA-KEY-PAIR-GEN", NULL }, - { CKM_KEA_KEY_DERIVE, "KEA-KEY-DERIVE", NULL }, - { CKM_FORTEZZA_TIMESTAMP, "FORTEZZA-TIMESTAMP", NULL }, - { CKM_BATON_KEY_GEN, "BATON-KEY-GEN", NULL }, - { CKM_BATON_ECB128, "BATON-ECB128", NULL }, - { CKM_BATON_ECB96, "BATON-ECB96", NULL }, - { CKM_BATON_CBC128, "BATON-CBC128", NULL }, - { CKM_BATON_COUNTER, "BATON-COUNTER", NULL }, - { CKM_BATON_SHUFFLE, "BATON-SHUFFLE", NULL }, - { CKM_BATON_WRAP, "BATON-WRAP", NULL }, - { CKM_ECDSA_KEY_PAIR_GEN, "ECDSA-KEY-PAIR-GEN", NULL }, - { CKM_ECDSA, "ECDSA", NULL }, - { CKM_ECDSA_SHA1, "ECDSA-SHA1", NULL }, - { CKM_ECDSA_SHA224, "ECDSA-SHA224", NULL }, - { CKM_ECDSA_SHA256, "ECDSA-SHA256", NULL }, - { CKM_ECDSA_SHA384, "ECDSA-SHA348", NULL }, - { CKM_ECDSA_SHA512, "ECDSA-SHA512", NULL }, - { CKM_ECDH1_DERIVE, "ECDH1-DERIVE", NULL }, - { CKM_ECDH1_COFACTOR_DERIVE,"ECDH1-COFACTOR-DERIVE", NULL }, - { CKM_ECMQV_DERIVE, "ECMQV-DERIVE", NULL }, - { CKM_JUNIPER_KEY_GEN, "JUNIPER-KEY-GEN", NULL }, - { CKM_JUNIPER_ECB128, "JUNIPER-ECB128", NULL }, - { CKM_JUNIPER_CBC128, "JUNIPER-CBC128", NULL }, - { CKM_JUNIPER_COUNTER, "JUNIPER-COUNTER", NULL }, - { CKM_JUNIPER_SHUFFLE, "JUNIPER-SHUFFLE", NULL }, - { CKM_JUNIPER_WRAP, "JUNIPER-WRAP", NULL }, - { CKM_FASTHASH, "FASTHASH", NULL }, - { CKM_AES_KEY_GEN, "AES-KEY-GEN", NULL }, - { CKM_AES_ECB, "AES-ECB", NULL }, - { CKM_AES_CBC, "AES-CBC", NULL }, - { CKM_AES_MAC, "AES-MAC", NULL }, - { CKM_AES_MAC_GENERAL, "AES-MAC-GENERAL", NULL }, - { CKM_AES_CBC_PAD, "AES-CBC-PAD", NULL }, - { CKM_GOSTR3410_KEY_PAIR_GEN,"GOSTR3410-KEY-PAIR-GEN", NULL }, - { CKM_GOSTR3410, "GOSTR3410", NULL }, - { CKM_GOSTR3410_WITH_GOSTR3411,"GOSTR3410-WITH-GOSTR3411", NULL }, - { CKM_GOSTR3411, "GOSTR3411", NULL }, - { CKM_DSA_PARAMETER_GEN, "DSA-PARAMETER-GEN", NULL }, - { CKM_DH_PKCS_PARAMETER_GEN,"DH-PKCS-PARAMETER-GEN", NULL }, - { CKM_X9_42_DH_PARAMETER_GEN,"X9-42-DH-PARAMETER-GEN", NULL }, - { 0, NULL, NULL } + { CKM_RSA_PKCS_KEY_PAIR_GEN, "RSA-PKCS-KEY-PAIR-GEN", NULL, MF_UNKNOWN }, + { CKM_RSA_PKCS, "RSA-PKCS", NULL, MF_UNKNOWN }, + { CKM_RSA_9796, "RSA-9796", NULL, MF_UNKNOWN }, + { CKM_RSA_X_509, "RSA-X-509", NULL, MF_UNKNOWN }, + { CKM_MD2_RSA_PKCS, "MD2-RSA-PKCS", NULL, MF_UNKNOWN }, + { CKM_MD5_RSA_PKCS, "MD5-RSA-PKCS", "rsa-md5", MF_UNKNOWN }, + { CKM_SHA1_RSA_PKCS, "SHA1-RSA-PKCS", "rsa-sha1", MF_UNKNOWN }, + { CKM_SHA224_RSA_PKCS, "SHA224-RSA-PKCS", "rsa-sha224", MF_UNKNOWN }, + { CKM_SHA256_RSA_PKCS, "SHA256-RSA-PKCS", "rsa-sha256", MF_UNKNOWN }, + { CKM_SHA384_RSA_PKCS, "SHA384-RSA-PKCS", "rsa-sha384", MF_UNKNOWN }, + { CKM_SHA512_RSA_PKCS, "SHA512-RSA-PKCS", "rsa-sha512", MF_UNKNOWN }, + { CKM_SHA3_224_RSA_PKCS, "SHA3-224-RSA-PKCS", "rsa-sha3-224", MF_UNKNOWN }, + { CKM_SHA3_256_RSA_PKCS, "SHA3-256-RSA-PKCS", "rsa-sha3-256", MF_UNKNOWN }, + { CKM_SHA3_384_RSA_PKCS, "SHA3-384-RSA-PKCS", "rsa-sha3-384", MF_UNKNOWN }, + { CKM_SHA3_512_RSA_PKCS, "SHA3-512-RSA-PKCS", "rsa-sha3-512", MF_UNKNOWN }, + { CKM_RIPEMD128_RSA_PKCS, "RIPEMD128-RSA-PKCS", NULL, MF_UNKNOWN }, + { CKM_RIPEMD160_RSA_PKCS, "RIPEMD160-RSA-PKCS", "rsa-ripemd160", MF_UNKNOWN }, + { CKM_RSA_PKCS_OAEP, "RSA-PKCS-OAEP", NULL, MF_UNKNOWN }, + { CKM_RSA_X9_31_KEY_PAIR_GEN,"RSA-X9-31-KEY-PAIR-GEN", NULL, MF_UNKNOWN }, + { CKM_RSA_X9_31, "RSA-X9-31", NULL, MF_UNKNOWN }, + { CKM_SHA1_RSA_X9_31, "SHA1-RSA-X9-31", NULL, MF_UNKNOWN }, + { CKM_RSA_PKCS_PSS, "RSA-PKCS-PSS", NULL, MF_UNKNOWN }, + { CKM_SHA1_RSA_PKCS_PSS, "SHA1-RSA-PKCS-PSS", "rsa-pss-sha1", MF_UNKNOWN }, + { CKM_SHA224_RSA_PKCS_PSS,"SHA224-RSA-PKCS-PSS", "rsa-pss-sha224", MF_UNKNOWN }, + { CKM_SHA256_RSA_PKCS_PSS,"SHA256-RSA-PKCS-PSS", "rsa-pss-sha256", MF_UNKNOWN }, + { CKM_SHA384_RSA_PKCS_PSS,"SHA384-RSA-PKCS-PSS", "rsa-pss-sha384", MF_UNKNOWN }, + { CKM_SHA512_RSA_PKCS_PSS,"SHA512-RSA-PKCS-PSS", "rsa-pss-sha512", MF_UNKNOWN }, + { CKM_SHA3_224_RSA_PKCS_PSS,"SHA3-224-RSA-PKCS-PSS", "rsa-pss-sha3-224", MF_UNKNOWN }, + { CKM_SHA3_256_RSA_PKCS_PSS,"SHA3-256-RSA-PKCS-PSS", "rsa-pss-sha3-256", MF_UNKNOWN }, + { CKM_SHA3_384_RSA_PKCS_PSS,"SHA3-384-RSA-PKCS-PSS", "rsa-pss-sha3-384", MF_UNKNOWN }, + { CKM_SHA3_512_RSA_PKCS_PSS,"SHA3-512-RSA-PKCS-PSS", "rsa-pss-sha3-512", MF_UNKNOWN }, + { CKM_DSA_KEY_PAIR_GEN, "DSA-KEY-PAIR-GEN", NULL, MF_UNKNOWN }, + { CKM_DSA, "DSA", NULL, MF_UNKNOWN }, + { CKM_DSA_SHA1, "DSA-SHA1", NULL, MF_UNKNOWN }, + { CKM_DSA_SHA224, "DSA-SHA224", NULL, MF_UNKNOWN }, + { CKM_DSA_SHA256, "DSA-SHA256", NULL, MF_UNKNOWN }, + { CKM_DSA_SHA384, "DSA-SHA384", NULL, MF_UNKNOWN }, + { CKM_DSA_SHA512, "DSA-SHA512", NULL, MF_UNKNOWN }, + { CKM_DH_PKCS_KEY_PAIR_GEN,"DH-PKCS-KEY-PAIR-GEN", NULL, MF_UNKNOWN }, + { CKM_DH_PKCS_DERIVE, "DH-PKCS-DERIVE", NULL, MF_UNKNOWN }, + { CKM_X9_42_DH_KEY_PAIR_GEN,"X9-42-DH-KEY-PAIR-GEN", NULL, MF_UNKNOWN }, + { CKM_X9_42_DH_DERIVE, "X9-42-DH-DERIVE", NULL, MF_UNKNOWN }, + { CKM_X9_42_DH_HYBRID_DERIVE,"X9-42-DH-HYBRID-DERIVE", NULL, MF_UNKNOWN }, + { CKM_X9_42_MQV_DERIVE, "X9-42-MQV-DERIVE", NULL, MF_UNKNOWN }, + { CKM_RC2_KEY_GEN, "RC2-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_RC2_ECB, "RC2-ECB", NULL, MF_UNKNOWN }, + { CKM_RC2_CBC, "RC2-CBC", NULL, MF_UNKNOWN }, + { CKM_RC2_MAC, "RC2-MAC", NULL, MF_UNKNOWN }, + { CKM_RC2_MAC_GENERAL, "RC2-MAC-GENERAL", NULL, MF_UNKNOWN }, + { CKM_RC2_CBC_PAD, "RC2-CBC-PAD", NULL, MF_UNKNOWN }, + { CKM_RC4_KEY_GEN, "RC4-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_RC4, "RC4", NULL, MF_UNKNOWN }, + { CKM_DES_KEY_GEN, "DES-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_DES_ECB, "DES-ECB", NULL, MF_UNKNOWN }, + { CKM_DES_CBC, "DES-CBC", NULL, MF_UNKNOWN }, + { CKM_DES_MAC, "DES-MAC", NULL, MF_UNKNOWN }, + { CKM_DES_MAC_GENERAL, "DES-MAC-GENERAL", NULL, MF_UNKNOWN }, + { CKM_DES_CBC_PAD, "DES-CBC-PAD", NULL, MF_UNKNOWN }, + { CKM_DES2_KEY_GEN, "DES2-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_DES3_KEY_GEN, "DES3-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_DES3_ECB, "DES3-ECB", NULL, MF_UNKNOWN }, + { CKM_DES3_CBC, "DES3-CBC", NULL, MF_UNKNOWN }, + { CKM_DES3_MAC, "DES3-MAC", NULL, MF_UNKNOWN }, + { CKM_DES3_MAC_GENERAL, "DES3-MAC-GENERAL", NULL, MF_UNKNOWN }, + { CKM_DES3_CBC_PAD, "DES3-CBC-PAD", NULL, MF_UNKNOWN }, + { CKM_DES3_CMAC, "DES3-CMAC", NULL, MF_UNKNOWN }, + { CKM_CDMF_KEY_GEN, "CDMF-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_CDMF_ECB, "CDMF-ECB", NULL, MF_UNKNOWN }, + { CKM_CDMF_CBC, "CDMF-CBC", NULL, MF_UNKNOWN }, + { CKM_CDMF_MAC, "CDMF-MAC", NULL, MF_UNKNOWN }, + { CKM_CDMF_MAC_GENERAL, "CDMF-MAC-GENERAL", NULL, MF_UNKNOWN }, + { CKM_CDMF_CBC_PAD, "CDMF-CBC-PAD", NULL, MF_UNKNOWN }, + { CKM_MD2, "MD2", NULL, MF_UNKNOWN }, + { CKM_MD2_HMAC, "MD2-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_MD2_HMAC_GENERAL, "MD2-HMAC-GENERAL", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_MD5, "MD5", NULL, MF_UNKNOWN }, + { CKM_MD5_HMAC, "MD5-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_MD5_HMAC_GENERAL, "MD5-HMAC-GENERAL", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_SHA_1, "SHA-1", NULL, MF_UNKNOWN }, + { CKM_SHA_1_HMAC, "SHA-1-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_SHA_1_HMAC_GENERAL, "SHA-1-HMAC-GENERAL", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_SHA224, "SHA224", NULL, MF_UNKNOWN }, + { CKM_SHA224_HMAC, "SHA224-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_SHA256, "SHA256", NULL, MF_UNKNOWN }, + { CKM_SHA256_HMAC, "SHA256-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_SHA384, "SHA384", NULL, MF_UNKNOWN }, + { CKM_SHA384_HMAC, "SHA384-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_SHA512, "SHA512", NULL, MF_UNKNOWN }, + { CKM_SHA512_HMAC, "SHA512-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_SHA3_224, "SHA3-224", NULL, MF_UNKNOWN }, + { CKM_SHA3_224_HMAC, "SHA3-224-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_SHA3_256, "SHA3-256", NULL, MF_UNKNOWN }, + { CKM_SHA3_256_HMAC, "SHA3-256-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_SHA3_384, "SHA3-384", NULL, MF_UNKNOWN }, + { CKM_SHA3_384_HMAC, "SHA3-384-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_SHA3_512, "SHA3-512", NULL, MF_UNKNOWN }, + { CKM_SHA3_512_HMAC, "SHA3-512-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_RIPEMD128, "RIPEMD128", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_RIPEMD128_HMAC, "RIPEMD128-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_RIPEMD128_HMAC_GENERAL,"RIPEMD128-HMAC-GENERAL", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_RIPEMD160, "RIPEMD160", NULL, MF_UNKNOWN }, + { CKM_RIPEMD160_HMAC, "RIPEMD160-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_RIPEMD160_HMAC_GENERAL,"RIPEMD160-HMAC-GENERAL", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_CAST_KEY_GEN, "CAST-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_CAST_ECB, "CAST-ECB", NULL, MF_UNKNOWN }, + { CKM_CAST_CBC, "CAST-CBC", NULL, MF_UNKNOWN }, + { CKM_CAST_MAC, "CAST-MAC", NULL, MF_UNKNOWN }, + { CKM_CAST_MAC_GENERAL, "CAST-MAC-GENERAL", NULL, MF_UNKNOWN }, + { CKM_CAST_CBC_PAD, "CAST-CBC-PAD", NULL, MF_UNKNOWN }, + { CKM_CAST3_KEY_GEN, "CAST3-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_CAST3_ECB, "CAST3-ECB", NULL, MF_UNKNOWN }, + { CKM_CAST3_CBC, "CAST3-CBC", NULL, MF_UNKNOWN }, + { CKM_CAST3_MAC, "CAST3-MAC", NULL, MF_UNKNOWN }, + { CKM_CAST3_MAC_GENERAL, "CAST3-MAC-GENERAL", NULL, MF_UNKNOWN }, + { CKM_CAST3_CBC_PAD, "CAST3-CBC-PAD", NULL, MF_UNKNOWN }, + { CKM_CAST5_KEY_GEN, "CAST5-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_CAST5_ECB, "CAST5-ECB", NULL, MF_UNKNOWN }, + { CKM_CAST5_CBC, "CAST5-CBC", NULL, MF_UNKNOWN }, + { CKM_CAST5_MAC, "CAST5-MAC", NULL, MF_UNKNOWN }, + { CKM_CAST5_MAC_GENERAL, "CAST5-MAC-GENERAL", NULL, MF_UNKNOWN }, + { CKM_CAST5_CBC_PAD, "CAST5-CBC-PAD", NULL, MF_UNKNOWN }, + { CKM_RC5_KEY_GEN, "RC5-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_RC5_ECB, "RC5-ECB", NULL, MF_UNKNOWN }, + { CKM_RC5_CBC, "RC5-CBC", NULL, MF_UNKNOWN }, + { CKM_RC5_MAC, "RC5-MAC", NULL, MF_UNKNOWN }, + { CKM_RC5_MAC_GENERAL, "RC5-MAC-GENERAL", NULL, MF_UNKNOWN }, + { CKM_RC5_CBC_PAD, "RC5-CBC-PAD", NULL, MF_UNKNOWN }, + { CKM_IDEA_KEY_GEN, "IDEA-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_IDEA_ECB, "IDEA-ECB", NULL, MF_UNKNOWN }, + { CKM_IDEA_CBC, "IDEA-CBC", NULL, MF_UNKNOWN }, + { CKM_IDEA_MAC, "IDEA-MAC", NULL, MF_UNKNOWN }, + { CKM_IDEA_MAC_GENERAL, "IDEA-MAC-GENERAL", NULL, MF_UNKNOWN }, + { CKM_IDEA_CBC_PAD, "IDEA-CBC-PAD", NULL, MF_UNKNOWN }, + { CKM_GENERIC_SECRET_KEY_GEN,"GENERIC-SECRET-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_HKDF_KEY_GEN, "HKDF-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_HKDF_DATA, "HKDF-DATA", NULL, MF_UNKNOWN }, + { CKM_HKDF_DERIVE, "HKDF-DERIVE", NULL, MF_UNKNOWN }, + { CKM_CONCATENATE_BASE_AND_KEY,"CONCATENATE-BASE-AND-KEY", NULL, MF_UNKNOWN }, + { CKM_CONCATENATE_BASE_AND_DATA,"CONCATENATE-BASE-AND-DATA", NULL, MF_UNKNOWN }, + { CKM_CONCATENATE_DATA_AND_BASE,"CONCATENATE-DATA-AND-BASE", NULL, MF_UNKNOWN }, + { CKM_XOR_BASE_AND_DATA, "XOR-BASE-AND-DATA", NULL, MF_UNKNOWN }, + { CKM_EXTRACT_KEY_FROM_KEY,"EXTRACT-KEY-FROM-KEY", NULL, MF_UNKNOWN }, + { CKM_SSL3_PRE_MASTER_KEY_GEN,"SSL3-PRE-MASTER-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_SSL3_MASTER_KEY_DERIVE,"SSL3-MASTER-KEY-DERIVE", NULL, MF_UNKNOWN }, + { CKM_SSL3_KEY_AND_MAC_DERIVE,"SSL3-KEY-AND-MAC-DERIVE", NULL, MF_UNKNOWN }, + { CKM_SSL3_MASTER_KEY_DERIVE_DH,"SSL3-MASTER-KEY-DERIVE-DH", NULL, MF_UNKNOWN }, + { CKM_TLS_PRE_MASTER_KEY_GEN,"TLS-PRE-MASTER-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_TLS_MASTER_KEY_DERIVE,"TLS-MASTER-KEY-DERIVE", NULL, MF_UNKNOWN }, + { CKM_TLS_KEY_AND_MAC_DERIVE,"TLS-KEY-AND-MAC-DERIVE", NULL, MF_UNKNOWN }, + { CKM_TLS_MASTER_KEY_DERIVE_DH,"TLS-MASTER-KEY-DERIVE-DH", NULL, MF_UNKNOWN }, + { CKM_SSL3_MD5_MAC, "SSL3-MD5-MAC", NULL, MF_UNKNOWN }, + { CKM_SSL3_SHA1_MAC, "SSL3-SHA1-MAC", NULL, MF_UNKNOWN }, + { CKM_MD5_KEY_DERIVATION, "MD5-KEY-DERIVATION", NULL, MF_UNKNOWN }, + { CKM_MD2_KEY_DERIVATION, "MD2-KEY-DERIVATION", NULL, MF_UNKNOWN }, + { CKM_SHA1_KEY_DERIVATION,"SHA1-KEY-DERIVATION", NULL, MF_UNKNOWN }, + { CKM_PBE_MD2_DES_CBC, "PBE-MD2-DES-CBC", NULL, MF_UNKNOWN }, + { CKM_PBE_MD5_DES_CBC, "PBE-MD5-DES-CBC", NULL, MF_UNKNOWN }, + { CKM_PBE_MD5_CAST_CBC, "PBE-MD5-CAST-CBC", NULL, MF_UNKNOWN }, + { CKM_PBE_MD5_CAST3_CBC, "PBE-MD5-CAST3-CBC", NULL, MF_UNKNOWN }, + { CKM_PBE_MD5_CAST5_CBC, "PBE-MD5-CAST5-CBC", NULL, MF_UNKNOWN }, + { CKM_PBE_SHA1_CAST5_CBC, "PBE-SHA1-CAST5-CBC", NULL, MF_UNKNOWN }, + { CKM_PBE_SHA1_RC4_128, "PBE-SHA1-RC4-128", NULL, MF_UNKNOWN }, + { CKM_PBE_SHA1_RC4_40, "PBE-SHA1-RC4-40", NULL, MF_UNKNOWN }, + { CKM_PBE_SHA1_DES3_EDE_CBC,"PBE-SHA1-DES3-EDE-CBC", NULL, MF_UNKNOWN }, + { CKM_PBE_SHA1_DES2_EDE_CBC,"PBE-SHA1-DES2-EDE-CBC", NULL, MF_UNKNOWN }, + { CKM_PBE_SHA1_RC2_128_CBC,"PBE-SHA1-RC2-128-CBC", NULL, MF_UNKNOWN }, + { CKM_PBE_SHA1_RC2_40_CBC,"PBE-SHA1-RC2-40-CBC", NULL, MF_UNKNOWN }, + { CKM_PKCS5_PBKD2, "PKCS5-PBKD2", NULL, MF_UNKNOWN }, + { CKM_PBA_SHA1_WITH_SHA1_HMAC,"PBA-SHA1-WITH-SHA1-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_KEY_WRAP_LYNKS, "KEY-WRAP-LYNKS", NULL, MF_UNKNOWN }, + { CKM_KEY_WRAP_SET_OAEP, "KEY-WRAP-SET-OAEP", NULL, MF_UNKNOWN }, + { CKM_SKIPJACK_KEY_GEN, "SKIPJACK-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_SKIPJACK_ECB64, "SKIPJACK-ECB64", NULL, MF_UNKNOWN }, + { CKM_SKIPJACK_CBC64, "SKIPJACK-CBC64", NULL, MF_UNKNOWN }, + { CKM_SKIPJACK_OFB64, "SKIPJACK-OFB64", NULL, MF_UNKNOWN }, + { CKM_SKIPJACK_CFB64, "SKIPJACK-CFB64", NULL, MF_UNKNOWN }, + { CKM_SKIPJACK_CFB32, "SKIPJACK-CFB32", NULL, MF_UNKNOWN }, + { CKM_SKIPJACK_CFB16, "SKIPJACK-CFB16", NULL, MF_UNKNOWN }, + { CKM_SKIPJACK_CFB8, "SKIPJACK-CFB8", NULL, MF_UNKNOWN }, + { CKM_SKIPJACK_WRAP, "SKIPJACK-WRAP", NULL, MF_UNKNOWN }, + { CKM_SKIPJACK_PRIVATE_WRAP,"SKIPJACK-PRIVATE-WRAP", NULL, MF_UNKNOWN }, + { CKM_SKIPJACK_RELAYX, "SKIPJACK-RELAYX", NULL, MF_UNKNOWN }, + { CKM_KEA_KEY_PAIR_GEN, "KEA-KEY-PAIR-GEN", NULL, MF_UNKNOWN }, + { CKM_KEA_KEY_DERIVE, "KEA-KEY-DERIVE", NULL, MF_UNKNOWN }, + { CKM_FORTEZZA_TIMESTAMP, "FORTEZZA-TIMESTAMP", NULL, MF_UNKNOWN }, + { CKM_BATON_KEY_GEN, "BATON-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_BATON_ECB128, "BATON-ECB128", NULL, MF_UNKNOWN }, + { CKM_BATON_ECB96, "BATON-ECB96", NULL, MF_UNKNOWN }, + { CKM_BATON_CBC128, "BATON-CBC128", NULL, MF_UNKNOWN }, + { CKM_BATON_COUNTER, "BATON-COUNTER", NULL, MF_UNKNOWN }, + { CKM_BATON_SHUFFLE, "BATON-SHUFFLE", NULL, MF_UNKNOWN }, + { CKM_BATON_WRAP, "BATON-WRAP", NULL, MF_UNKNOWN }, + { CKM_ECDSA_KEY_PAIR_GEN, "ECDSA-KEY-PAIR-GEN", NULL, MF_UNKNOWN }, + { CKM_ECDSA, "ECDSA", NULL, MF_UNKNOWN }, + { CKM_ECDSA_SHA1, "ECDSA-SHA1", NULL, MF_UNKNOWN }, + { CKM_ECDSA_SHA224, "ECDSA-SHA224", NULL, MF_UNKNOWN }, + { CKM_ECDSA_SHA256, "ECDSA-SHA256", NULL, MF_UNKNOWN }, + { CKM_ECDSA_SHA384, "ECDSA-SHA384", NULL, MF_UNKNOWN }, + { CKM_ECDSA_SHA512, "ECDSA-SHA512", NULL, MF_UNKNOWN }, + { CKM_ECDSA_SHA3_224, "ECDSA-SHA3-224", NULL, MF_UNKNOWN }, + { CKM_ECDSA_SHA3_256, "ECDSA-SHA3-256", NULL, MF_UNKNOWN }, + { CKM_ECDSA_SHA3_384, "ECDSA-SHA3-384", NULL, MF_UNKNOWN }, + { CKM_ECDSA_SHA3_512, "ECDSA-SHA3-512", NULL, MF_UNKNOWN }, + { CKM_ECDH1_DERIVE, "ECDH1-DERIVE", NULL, MF_UNKNOWN }, + { CKM_ECDH1_COFACTOR_DERIVE,"ECDH1-COFACTOR-DERIVE", NULL, MF_UNKNOWN }, + { CKM_ECMQV_DERIVE, "ECMQV-DERIVE", NULL, MF_UNKNOWN }, + { CKM_EC_EDWARDS_KEY_PAIR_GEN,"EC-EDWARDS-KEY-PAIR-GEN", NULL, MF_UNKNOWN }, + { CKM_EC_MONTGOMERY_KEY_PAIR_GEN,"EC-MONTGOMERY-KEY-PAIR-GEN", NULL, MF_UNKNOWN }, + { CKM_EDDSA, "EDDSA", NULL, MF_UNKNOWN }, + { CKM_XEDDSA, "XEDDSA", NULL, MF_UNKNOWN }, + { CKM_JUNIPER_KEY_GEN, "JUNIPER-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_JUNIPER_ECB128, "JUNIPER-ECB128", NULL, MF_UNKNOWN }, + { CKM_JUNIPER_CBC128, "JUNIPER-CBC128", NULL, MF_UNKNOWN }, + { CKM_JUNIPER_COUNTER, "JUNIPER-COUNTER", NULL, MF_UNKNOWN }, + { CKM_JUNIPER_SHUFFLE, "JUNIPER-SHUFFLE", NULL, MF_UNKNOWN }, + { CKM_JUNIPER_WRAP, "JUNIPER-WRAP", NULL, MF_UNKNOWN }, + { CKM_FASTHASH, "FASTHASH", NULL, MF_UNKNOWN }, + { CKM_AES_KEY_GEN, "AES-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_AES_ECB, "AES-ECB", NULL, MF_UNKNOWN }, + { CKM_AES_CBC, "AES-CBC", NULL, MF_UNKNOWN }, + { CKM_AES_MAC, "AES-MAC", NULL, MF_UNKNOWN }, + { CKM_AES_MAC_GENERAL, "AES-MAC-GENERAL", NULL, MF_UNKNOWN }, + { CKM_AES_CBC_PAD, "AES-CBC-PAD", NULL, MF_UNKNOWN }, + { CKM_AES_CTR, "AES-CTR", NULL, MF_UNKNOWN }, + { CKM_AES_GCM, "AES-GCM", NULL, MF_UNKNOWN }, + { CKM_AES_CMAC, "AES-CMAC", NULL, (MF_SIGN | MF_VERIFY | MF_CKO_SECRET_KEY) }, + { CKM_AES_CMAC_GENERAL, "AES-CMAC-GENERAL", NULL, (MF_SIGN | MF_VERIFY | MF_CKO_SECRET_KEY) }, + { CKM_DES_ECB_ENCRYPT_DATA, "DES-ECB-ENCRYPT-DATA", NULL, MF_UNKNOWN }, + { CKM_DES_CBC_ENCRYPT_DATA, "DES-CBC-ENCRYPT-DATA", NULL, MF_UNKNOWN }, + { CKM_DES3_ECB_ENCRYPT_DATA, "DES3-ECB-ENCRYPT-DATA", NULL, MF_UNKNOWN }, + { CKM_DES3_CBC_ENCRYPT_DATA, "DES3-CBC-ENCRYPT-DATA", NULL, MF_UNKNOWN }, + { CKM_AES_ECB_ENCRYPT_DATA, "AES-ECB-ENCRYPT-DATA", NULL, MF_UNKNOWN }, + { CKM_AES_CBC_ENCRYPT_DATA, "AES-CBC-ENCRYPT-DATA", NULL, MF_UNKNOWN }, + { CKM_GOST28147_KEY_GEN, "GOST28147-KEY-GEN", NULL, MF_UNKNOWN }, + { CKM_GOST28147_ECB, "GOST28147-ECB", NULL, MF_UNKNOWN }, + { CKM_GOST28147, "GOST28147", NULL, MF_UNKNOWN }, + { CKM_GOST28147_MAC, "GOST28147-MAC", NULL, MF_UNKNOWN }, + { CKM_GOST28147_KEY_WRAP, "GOST28147-KEY-WRAP", NULL, MF_UNKNOWN }, + { CKM_GOSTR3410_KEY_PAIR_GEN,"GOSTR3410-KEY-PAIR-GEN", NULL, MF_UNKNOWN }, + { CKM_GOSTR3410, "GOSTR3410", NULL, MF_UNKNOWN }, + { CKM_GOSTR3410_DERIVE, "GOSTR3410-DERIVE", NULL, MF_UNKNOWN }, + { CKM_GOSTR3410_WITH_GOSTR3411,"GOSTR3410-WITH-GOSTR3411", NULL, MF_UNKNOWN }, + { CKM_GOSTR3410_512_KEY_PAIR_GEN, "GOSTR3410-512-KEY-PAIR-GEN", NULL, MF_UNKNOWN }, + { CKM_GOSTR3410_512, "GOSTR3410_512", NULL, MF_UNKNOWN }, + { CKM_GOSTR3410_12_DERIVE, "GOSTR3410-12-DERIVE", NULL, MF_UNKNOWN }, + { CKM_GOSTR3410_WITH_GOSTR3411_12_256, "GOSTR3410-WITH-GOSTR3411-12-256", NULL, MF_UNKNOWN }, + { CKM_GOSTR3410_WITH_GOSTR3411_12_512, "GOSTR3410-WITH-GOSTR3411-12-512", NULL, MF_UNKNOWN }, + { CKM_GOSTR3411, "GOSTR3411", NULL, MF_UNKNOWN }, + { CKM_GOSTR3411_HMAC, "GOSTR3411-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_GOSTR3411_12_256, "GOSTR3411-12-256", NULL, MF_UNKNOWN }, + { CKM_GOSTR3411_12_512, "GOSTR3411-12-512", NULL, MF_UNKNOWN }, + { CKM_GOSTR3411_12_256_HMAC, "GOSTR3411-12-256-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_GOSTR3411_12_512_HMAC, "GOSTR3411-12-512-HMAC", NULL, MF_GENERIC_HMAC_FLAGS }, + { CKM_DSA_PARAMETER_GEN, "DSA-PARAMETER-GEN", NULL, MF_UNKNOWN }, + { CKM_DH_PKCS_PARAMETER_GEN,"DH-PKCS-PARAMETER-GEN", NULL, MF_UNKNOWN }, + { CKM_X9_42_DH_PARAMETER_GEN,"X9-42-DH-PARAMETER-GEN", NULL, MF_UNKNOWN }, + { CKM_AES_KEY_WRAP, "AES-KEY-WRAP", NULL, MF_UNKNOWN }, + { CKM_AES_KEY_WRAP_PAD, "AES-KEY-WRAP-PAD", NULL, MF_UNKNOWN}, + { CKM_CHACHA20_KEY_GEN, "CHACHA20-KEY-GEN", NULL, MF_UNKNOWN}, + { CKM_CHACHA20, "CHACHA20", NULL, MF_UNKNOWN}, + { CKM_POLY1305_KEY_GEN, "POLY1305-KEY-GEN", NULL, MF_UNKNOWN}, + { CKM_POLY1305, "POLY1305", NULL, MF_GENERIC_HMAC_FLAGS}, + { CKM_CHACHA20_POLY1305, "CHACHA20-POLY1305", NULL, MF_UNKNOWN}, + { CKM_ML_DSA, "ML-DSA", "mldsa", MF_UNKNOWN}, + { CKM_HASH_ML_DSA_SHA224, "HASH-ML-DSA-SHA224", "mldsa-sha224", MF_UNKNOWN}, + { CKM_HASH_ML_DSA_SHA256, "HASH-ML-DSA-SHA256", "mldsa-sha256", MF_UNKNOWN}, + { CKM_HASH_ML_DSA_SHA384, "HASH-ML-DSA-SHA384", "mldsa-sha384", MF_UNKNOWN}, + { CKM_HASH_ML_DSA_SHA512, "HASH-ML-DSA-SHA512", "mldsa-sha512", MF_UNKNOWN}, + { CKM_HASH_ML_DSA_SHA3_224, "HASH-ML-DSA-SHA3_224", "mldsa-sha3-224", MF_UNKNOWN}, + { CKM_HASH_ML_DSA_SHA3_256, "HASH-ML-DSA-SHA3_256", "mldsa-sha3-256", MF_UNKNOWN}, + { CKM_HASH_ML_DSA_SHA3_384, "HASH-ML-DSA-SHA3_384", "mldsa-sha3-384", MF_UNKNOWN}, + { CKM_HASH_ML_DSA_SHA3_512, "HASH-ML-DSA-SHA3_512", "mldsa-sha3-512", MF_UNKNOWN}, + { CKM_HASH_ML_DSA_SHAKE128, "HASH-ML-DSA-SHAKE128", "mldsa-shake128", MF_UNKNOWN}, + { CKM_HASH_ML_DSA_SHAKE256, "HASH-ML-DSA-SHAKE256", "mldsa-shake256", MF_UNKNOWN}, + { CKM_ML_KEM, "ML-KEM", "mlkem", MF_UNKNOWN}, + { CKM_SLH_DSA, "SLH-DSA", "slhdsa", MF_UNKNOWN}, + { CKM_HASH_SLH_DSA_SHA224, "HASH-SLH-DSA-SHA224", "slhdsa-sha224", MF_UNKNOWN}, + { CKM_HASH_SLH_DSA_SHA256, "HASH-SLH-DSA-SHA256", "slhdsa-sha256", MF_UNKNOWN}, + { CKM_HASH_SLH_DSA_SHA384, "HASH-SLH-DSA-SHA384", "slhdsa-sha384", MF_UNKNOWN}, + { CKM_HASH_SLH_DSA_SHA512, "HASH-SLH-DSA-SHA512", "slhdsa-sha512", MF_UNKNOWN}, + { CKM_HASH_SLH_DSA_SHA3_224, "HASH-SLH-DSA-SHA3_224", "slhdsa-sha3-224", MF_UNKNOWN}, + { CKM_HASH_SLH_DSA_SHA3_256, "HASH-SLH-DSA-SHA3_256", "slhdsa-sha3-256", MF_UNKNOWN}, + { CKM_HASH_SLH_DSA_SHA3_384, "HASH-SLH-DSA-SHA3_384", "slhdsa-sha3-384", MF_UNKNOWN}, + { CKM_HASH_SLH_DSA_SHA3_512, "HASH-SLH-DSA-SHA3_512", "slhdsa-sha3-512", MF_UNKNOWN}, + { CKM_HASH_SLH_DSA_SHAKE128, "HASH-SLH-DSA-SHAKE128", "slhdsa-shake128", MF_UNKNOWN}, + { CKM_HASH_SLH_DSA_SHAKE256, "HASH-SLH-DSA-SHAKE256", "slhdsa-shake256", MF_UNKNOWN}, + { 0, NULL, NULL, MF_UNKNOWN }, +}; + +static struct mech_info p11_mgf[] = { + { CKG_MGF1_SHA1, "MGF1-SHA1", NULL, MF_MGF }, + { CKG_MGF1_SHA224, "MGF1-SHA224", NULL, MF_MGF }, + { CKG_MGF1_SHA256, "MGF1-SHA256", NULL, MF_MGF }, + { CKG_MGF1_SHA384, "MGF1-SHA384", NULL, MF_MGF }, + { CKG_MGF1_SHA512, "MGF1-SHA512", NULL, MF_MGF }, + { CKG_MGF1_SHA3_224, "MGF1-SHA3_224", NULL, MF_MGF }, + { CKG_MGF1_SHA3_256, "MGF1-SHA3_256", NULL, MF_MGF }, + { CKG_MGF1_SHA3_384, "MGF1-SHA3_384", NULL, MF_MGF }, + { CKG_MGF1_SHA3_512, "MGF1-SHA3_512", NULL, MF_MGF }, + + { 0, NULL, NULL, MF_UNKNOWN } +}; + +static struct mech_info p11_profile[] = { + { CKP_INVALID_ID, "CKP_INVALID_ID", NULL, MF_UNKNOWN }, + { CKP_BASELINE_PROVIDER, "CKP_BASELINE_PROVIDER", NULL, MF_UNKNOWN }, + { CKP_EXTENDED_PROVIDER, "CKP_EXTENDED_PROVIDER", NULL, MF_UNKNOWN }, + { CKP_AUTHENTICATION_TOKEN, "CKP_AUTHENTICATION_TOKEN", NULL, MF_UNKNOWN }, + { CKP_PUBLIC_CERTIFICATES_TOKEN, "CKP_PUBLIC_CERTIFICATES_TOKEN", NULL, MF_UNKNOWN }, + { CKP_VENDOR_DEFINED, "CKP_VENDOR_DEFINED", NULL, MF_UNKNOWN }, + { 0, NULL, NULL, MF_UNKNOWN } }; +// clang-format on static const char *p11_mechanism_to_name(CK_MECHANISM_TYPE mech) { @@ -5808,10 +10497,32 @@ static const char *p11_mechanism_to_name(CK_MECHANISM_TYPE mech) return temp; } +uint16_t p11_mechanism_to_flags(CK_MECHANISM_TYPE mech) +{ + struct mech_info *mi; + for (mi = p11_mechanisms; mi->name; mi++) { + if (mi->mech == mech) + return mi->mf_flags; + } + + /* + * XXX: Since populating the table is underway we won't warn until its done. Existing mechanisms function + * as they used to. So guard this on verbose. + */ + if (verbose) { + util_warn("mechanism 0x%lx not found, consider adding it to mechanism table", mech); + } + + return MF_UNKNOWN; +} + static CK_MECHANISM_TYPE p11_name_to_mechanism(const char *name) { struct mech_info *mi; + if (strncasecmp("0x", name, 2) == 0) { + return strtoul(name, NULL, 0); + } for (mi = p11_mechanisms; mi->name; mi++) { if (!strcasecmp(mi->name, name) || (mi->short_name && !strcasecmp(mi->short_name, name))) @@ -5821,6 +10532,43 @@ static CK_MECHANISM_TYPE p11_name_to_mechanism(const char *name) return 0; /* gcc food */ } +static CK_RSA_PKCS_MGF_TYPE p11_name_to_mgf(const char *name) +{ + struct mech_info *mi; + + for (mi = p11_mgf; mi->name; mi++) { + if (!strcasecmp(mi->name, name)) + return mi->mech; + } + util_fatal("Unknown PKCS11 MGF \"%s\"", name); +} + +static const char *p11_mgf_to_name(CK_RSA_PKCS_MGF_TYPE mgf) +{ + static char temp[64]; + struct mech_info *mi; + + for (mi = p11_mgf; mi->name; mi++) { + if (mi->mech == mgf) + return mi->name; + } + snprintf(temp, sizeof(temp), "mgf-0x%lX", (unsigned long) mgf); + return temp; +} + +static const char *p11_profile_to_name(CK_ULONG profile) +{ + static char temp[64]; + struct mech_info *mi; + + for (mi = p11_profile; mi->name; mi++) { + if (mi->mech == profile) + return mi->name; + } + snprintf(temp, sizeof(temp), "profile-0x%lX", (unsigned long) profile); + return temp; +} + static const char * CKR2Str(CK_ULONG res) { switch (res) { @@ -5997,3 +10745,196 @@ static const char * CKR2Str(CK_ULONG res) } return "unknown PKCS11 error"; } + +#if defined(_WIN32) || defined(HAVE_PTHREAD) +#ifdef _WIN32 +static DWORD WINAPI test_threads_run(_In_ LPVOID pttd) +#else +static void * test_threads_run(void * pttd) +#endif +{ + CK_RV rv = CKR_OK; + CK_INFO info; + int l_slots = 0; + CK_ULONG l_p11_num_slots = 0; + CK_SLOT_ID_PTR l_p11_slots = NULL; + char * pctest; + struct test_threads_data * ttd = (struct test_threads_data *)pttd; + + fprintf(stderr, "Test thread %d started with options:%s\n", ttd->tnum, ttd->tests); + /* call selected C_* routines with different options */ + pctest = ttd-> tests; + + /* series of two character commands */ + while (pctest && *pctest && *(pctest + 1)) { + /* Pn - pause where n is 0 to 9 iseconds */ + if (*pctest == 'P' && *(pctest + 1) >= '0' && *(pctest + 1) <= '9') { + fprintf(stderr, "Test thread %d pauseing for %d seconds\n", ttd->tnum, (*(pctest + 1) - '0')); +#ifdef _WIN32 + Sleep((*(pctest + 1) - '0') * 1000); +#else + sleep(*(pctest + 1) - '0'); +#endif + } + + else if (*pctest == 'I') { + /* IN - C_Initialize with NULL args */ + if (*(pctest + 1) == 'N') { + fprintf(stderr, "Test thread %d C_Initialize(NULL)\n", ttd->tnum); + rv = p11->C_Initialize(NULL); + fprintf(stderr, "Test thread %d C_Initialize returned %s\n", ttd->tnum, CKR2Str(rv)); + } + /* IL C_Initialize with CKF_OS_LOCKING_OK */ + else if (*(pctest + 1) == 'L') { + fprintf(stderr, "Test thread %d C_Initialize CKF_OS_LOCKING_OK \n", ttd->tnum); + rv = p11->C_Initialize(&c_initialize_args_OS); + fprintf(stderr, "Test thread %d C_Initialize returned %s\n", ttd->tnum, CKR2Str(rv)); + } + else + goto err; + } + + /* GI - C_GetInfo */ + else if (*pctest == 'G' && *(pctest + 1) == 'I') { + fprintf(stderr, "Test thread %d C_GetInfo\n", ttd->tnum); + rv = p11->C_GetInfo(&info); + fprintf(stderr, "Test thread %d C_GetInfo returned %s\n", ttd->tnum, CKR2Str(rv)); + } + + /* SL - C_GetSlotList */ + else if (*pctest == 'S' && *(pctest + 1) == 'L') { + fprintf(stderr, "Test thread %d C_GetSlotList to get l_p11_num_slots\n", ttd->tnum); + rv = p11->C_GetSlotList(1, NULL, &l_p11_num_slots); + fprintf(stderr, "Test thread %d C_GetSlotList returned %s\n", ttd->tnum, CKR2Str(rv)); + fprintf(stderr, "Test thread %d l_p11_num_slots:%ld\n", ttd->tnum, l_p11_num_slots); + if (rv == CKR_OK) { + free(l_p11_slots); + l_p11_slots = NULL; + if (l_p11_num_slots > 0) { + l_p11_slots = calloc(l_p11_num_slots, sizeof(CK_SLOT_ID)); + if (l_p11_slots == NULL) { + goto err; + } + fprintf(stderr, "Test thread %d C_GetSlotList\n", ttd->tnum); + rv = p11->C_GetSlotList(1, l_p11_slots, &l_p11_num_slots); + fprintf(stderr, "Test thread %d C_GetSlotList returned %s\n", ttd->tnum, CKR2Str(rv)); + fprintf(stderr, "Test thread %d l_p11_num_slots:%ld\n", ttd->tnum, l_p11_num_slots); + if (rv == CKR_OK && l_p11_num_slots && l_p11_slots) + l_slots = 1; + } + } + } + + /* Tn Get token from slot_index n C_GetTokenInfo, where n is 0 to 9 */ + else if (*pctest == 'T' && *(pctest + 1) >= '0' && *(pctest + 1) <= '9') { + fprintf(stderr, "Test thread %d C_GetTokenInfo from slot_index %d using show_token\n", ttd->tnum, (*(pctest + 1) - '0')); + if (l_slots && (CK_ULONG)(*(pctest + 1) - '0') < l_p11_num_slots) { + show_token(l_p11_slots[(*(pctest + 1) - '0')]); + } else { + fprintf(stderr, "Test thread %d slot not available, unable to call C_GetTokenInfo\n", ttd->tnum); + rv = CKR_TOKEN_NOT_PRESENT; + break; + } + } + + /* LT login and test, just like as if `--login --test` was specified. + * May be combined with `--pin=123456` */ + else if (*pctest == 'L' && *(pctest + 1) == 'T') { + CK_SESSION_HANDLE session = CK_INVALID_HANDLE; + + rv = p11->C_OpenSession(opt_slot, CKF_SERIAL_SESSION|CKF_RW_SESSION, NULL, NULL, &session); + if (rv == CKR_OK) { + if (opt_login_type == -1) + opt_login_type = CKU_USER; + login(session, opt_login_type); + + if (p11_test(session)) + rv = CKR_GENERAL_ERROR; + else + rv = CKR_OK; + } + } + + else { + err: + rv = CKR_GENERAL_ERROR; /* could be vendor error, */ + fprintf(stderr, "Test thread %d Unknown test '%c%c'\n", ttd->tnum, *pctest, *(pctest + 1)); + break; + } + + pctest ++; + if (*pctest != 0x00) + pctest ++; + if (*pctest == ':') + pctest++; + + + if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) + /* IN C_Initialize with NULL args */ + break; + } + + free(l_p11_slots); + fprintf(stderr, "Test thread %d returning rv:%s\n", ttd->tnum, CKR2Str(rv)); +#ifdef _WIN32 + ExitThread(0); +#else + pthread_exit(NULL); +#endif +} + +static int test_threads_cleanup() +{ + + int i; + + fprintf(stderr,"test_threads cleanup starting\n"); + + for (i = 0; i < test_threads_num; i++) { +#ifdef _WIN32 + WaitForSingleObject(test_threads_handles[i], INFINITE); +#else + pthread_join(test_threads_handles[i], NULL); +#endif + } + + fprintf(stderr,"test_threads cleanup finished\n"); + return 0; +} + +static int test_threads_start(int tnum) +{ + int r = 0; + +#ifdef _WIN32 + test_threads_handles[tnum] = CreateThread(NULL, 0, test_threads_run, (LPVOID) &test_threads_datas[tnum], + 0, NULL); + if (test_threads_handles[tnum] == NULL) { + r = GetLastError(); + } +#else + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + r = pthread_create(&test_threads_handles[tnum], &attr, test_threads_run, (void *) &test_threads_datas[tnum]); +#endif + if (r != 0) { + fprintf(stderr,"test_threads pthread_create failed %d for thread %d\n", r, tnum); + /* system error */ + } + return r; +} + +/*********************************************************************************************/ +static void test_threads() +{ + int i; + + /* call test_threads_start for each --test-thread option */ + + /* upon return, C_Initialize will be called, from main code */ + for (i = 0; i < test_threads_num && i < MAX_TEST_THREADS; i++) { + test_threads_start(i); + } +} +#endif /* defined(_WIN32) || defined(HAVE_PTHREAD) */ diff --git a/src/tools/pkcs11_uri.c b/src/tools/pkcs11_uri.c new file mode 100644 index 0000000000..ef4e883919 --- /dev/null +++ b/src/tools/pkcs11_uri.c @@ -0,0 +1,346 @@ +/* + * pkcs11-uri.c: PKCS#11 URI Parser + * + * Copyright (C) 2024 Veronika Hanulikova + * Original parser implementation: https://src.fedoraproject.org/rpms/openssh/blob/rawhide/f/openssh-8.0p1-pkcs11-uri.patch + * + * This file is part of OpenSC. + * + * 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 + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "pkcs11_uri.h" + +static struct pkcs11_uri_attr path_attr[] = { + {"id", PKCS11_ID }, + {"library-description", PKCS11_LIB_DESCRIPTION }, + {"library-manufacturer", PKCS11_LIB_MANUFACTURER }, + {"library-version", PKCS11_LIB_VERSION }, + {"manufacturer", PKCS11_MANUFACTURER }, + {"model", PKCS11_MODEL }, + {"object", PKCS11_OBJECT }, + {"serial", PKCS11_SERIAL }, + {"slot-description", PKCS11_SLOT_DESCRIPTION }, + {"slot-id", PKCS11_SLOT_ID }, + {"slot-manufacturer", PKCS11_SLOT_MANUFACTURER}, + {"token", PKCS11_TOKEN }, + {"type", PKCS11_TYPE }, + {NULL, 0 } +}; + +static struct pkcs11_uri_attr query_attr[] = { + {"pin-source", PKCS11_PIN_SOURCE }, + {"pin-value", PKCS11_PIN_VALUE }, + {"module-name", PKCS11_MODULE_NAME}, + {"module-path", PKCS11_MODULE_PATH}, + {NULL, 0 } +}; + +static int +get_attr(struct pkcs11_uri_attr attr[], char *token) +{ + for (int i = 0; attr[i].name; i++) { + if (strncasecmp(token, attr[i].name, strlen(attr[i].name)) == 0 && + *(token + strlen(attr[i].name)) == '=') + return attr[i].id; + } + return -1; +} + +static int +decode_percent_string(char *data, char **out) +{ + char tmp[3]; + char *decoded, *tmp_end; + char *p = data; + long value; + size_t decoded_len = 0; + + decoded = malloc(strlen(data) + 1); + if (decoded == NULL) + return -1; + while (*p != '\0') { + switch (*p) { + case '%': + p++; + if (*p == '\0') + goto fail; + tmp[0] = *p++; + if (*p == '\0') + goto fail; + tmp[1] = *p++; + tmp[2] = '\0'; + tmp_end = NULL; + errno = 0; + value = strtol(tmp, &tmp_end, 16); + if (errno == EINVAL) + goto fail; + if (tmp_end != tmp + 2) + goto fail; + else + decoded[decoded_len++] = (char)value; + break; + default: + decoded[decoded_len++] = *p++; + break; + } + } + + /* zero terminate */ + decoded[decoded_len] = '\0'; + *out = decoded; + return (int)decoded_len; +fail: + free(decoded); + return -1; +} + +static int +parse_string(char *argument, char **out, int *out_len, int max_len) +{ + int rv = 0, len = 0; + char *tmp = NULL; + if (out == NULL) { + fprintf(stderr, "Wrong parameters provided\n"); + return 1; + } + if (*out != NULL) { + rv = 1; + fprintf(stderr, "Attribute already set\n"); + goto end; + } + len = decode_percent_string(argument, &tmp); + if (len < 0) { + rv = 1; + fprintf(stderr, "Invalid percent decode result\n"); + goto end; + } + if (max_len >= 0 && max_len < len) { + rv = 1; + free(tmp); + fprintf(stderr, "Invalid length of decoded result\n"); + goto end; + } + *out = tmp; + if (out_len != NULL) { + *out_len = len; + } +end: + return rv; +} + +struct pkcs11_uri * +pkcs11_uri_new() +{ + struct pkcs11_uri *uri = calloc(1, sizeof(struct pkcs11_uri)); + return uri; +} + +int +parse_pkcs11_uri(const char *input_string, struct pkcs11_uri *result) +{ + size_t length = 0; + char *uri; + char *path, *query, *token; + int rv = 0; + + if (!input_string || !result) { + return 1; + } + + /* Check PKCS#11 URI scheme */ + length = strlen(PKCS11_URI_SCHEME); + if (strlen(input_string) < length || strncmp(input_string, PKCS11_URI_SCHEME, length) != 0) { + return 1; + } + + /* Divide path and query part*/ + if ((uri = strdup(input_string)) == NULL) { + rv = 1; + fprintf(stderr, "Error when allocating memory\n"); + goto end; + } + path = uri; + path = strtok(path, "?"); + query = strtok(NULL, "?"); + if (path == NULL) { + rv = 1; + goto end; + } + /* Skip PKCS#11 URI scheme*/ + path = path + length; + + /* parse path */ + token = strtok(path, ";"); + while (token != NULL) { + char *argument = NULL, **result_ptr = NULL; + int *result_len = NULL; + int max_len = -1; + int id = get_attr(path_attr, token); + if (id == -1) { + rv = 1; + fprintf(stderr, "Invalid PKCS#11 uri attribute\n"); + goto end; + } + argument = token + strlen(path_attr[id].name) + 1; + + switch (id) { + case PKCS11_ID: + result_ptr = &result->id; + result_len = &result->id_len; + break; + case PKCS11_LIB_DESCRIPTION: + rv = 1; + fprintf(stderr, "PKCS#11 library description not supported\n"); + goto end; + break; + case PKCS11_LIB_MANUFACTURER: + rv = 1; + fprintf(stderr, "PKCS#11 manufacturer ID not supported\n"); + goto end; + break; + case PKCS11_LIB_VERSION: + rv = 1; + fprintf(stderr, "PKCS#11 library version not supported\n"); + goto end; + break; + case PKCS11_MANUFACTURER: + result_ptr = &result->token_manufacturer; + max_len = 32; + break; + case PKCS11_MODEL: + result_ptr = &result->token_model; + max_len = 16; + break; + case PKCS11_OBJECT: + result_ptr = &result->object; + break; + case PKCS11_SERIAL: + result_ptr = &result->serial; + max_len = 16; + break; + case PKCS11_SLOT_DESCRIPTION: + result_ptr = &result->slot_description; + break; + case PKCS11_SLOT_ID: + result_ptr = &result->slot_id; + break; + case PKCS11_SLOT_MANUFACTURER: + result_ptr = &result->slot_manufacturer; + break; + case PKCS11_TOKEN: + result_ptr = &result->token_label; + max_len = 32; + break; + case PKCS11_TYPE: + result_ptr = &result->type; + break; + } + if (parse_string(argument, result_ptr, result_len, max_len) != 0) { + rv = 1; + goto end; + } + token = strtok(NULL, ";"); + } + + /* parse query */ + if (query == NULL) { + goto end; + } + token = strtok(query, ";"); + while (token != NULL) { + char *argument = NULL, **result_ptr = NULL; + int id = get_attr(query_attr, token); + if (id == -1) { + rv = 1; + fprintf(stderr, "Invalid PKCS#11 URI query\n"); + goto end; + } + argument = token + strlen(query_attr[id].name) + 1; + + switch (id) { + case PKCS11_PIN_SOURCE: + if (result->pin_value != NULL) { + rv = 1; + fprintf(stderr, "A pin-value and pin-source cannot be specified together in PKCS#11 URI\n"); + goto end; + } + result_ptr = &result->pin_source; + break; + case PKCS11_PIN_VALUE: + if (result->pin_source != NULL) { + rv = 1; + fprintf(stderr, "A pin-value and pin-source cannot be specified together in PKCS#11 URI\n"); + goto end; + } + result_ptr = &result->pin_value; + break; + case PKCS11_MODULE_NAME: + rv = 1; + fprintf(stderr, "PKCS#11 module name for query not supported\n"); + goto end; + break; + case PKCS11_MODULE_PATH: + result_ptr = &result->module_path; + break; + } + if (parse_string(argument, result_ptr, NULL, -1) != 0) { + rv = 1; + goto end; + } + token = strtok(NULL, ";"); + } + +end: + free(uri); + return rv; +} + +void +pkcs11_uri_free(struct pkcs11_uri *uri) +{ + if (uri == NULL) { + return; + } + + free(uri->id); + free(uri->library_description); + free(uri->library_manufacturer); + free(uri->library_version); + free(uri->token_manufacturer); + free(uri->token_model); + free(uri->object); + free(uri->serial); + free(uri->slot_description); + free(uri->slot_id); + free(uri->slot_manufacturer); + free(uri->token_label); + free(uri->type); + free(uri->pin_source); + if (uri->pin_value) { + memset(uri->pin_value, 0, strlen(uri->pin_value)); + free(uri->pin_value); + } + free(uri->module_name); + free(uri->module_path); + free(uri); +} diff --git a/src/tools/pkcs11_uri.h b/src/tools/pkcs11_uri.h new file mode 100644 index 0000000000..87f2ead40c --- /dev/null +++ b/src/tools/pkcs11_uri.h @@ -0,0 +1,65 @@ +#ifndef PKCS11_URI_H +#define PKCS11_URI_H + +#include +#include +#include + +#define PKCS11_URI_SCHEME "pkcs11:" + +typedef enum { + PKCS11_ID = 0, + PKCS11_LIB_DESCRIPTION, + PKCS11_LIB_MANUFACTURER, + PKCS11_LIB_VERSION, + PKCS11_MANUFACTURER, + PKCS11_MODEL, + PKCS11_OBJECT, + PKCS11_SERIAL, + PKCS11_SLOT_DESCRIPTION, + PKCS11_SLOT_ID, + PKCS11_SLOT_MANUFACTURER, + PKCS11_TOKEN, + PKCS11_TYPE +} pkcs11_uri_attr_code; + +typedef enum { + PKCS11_PIN_SOURCE = 0, + PKCS11_PIN_VALUE, + PKCS11_MODULE_NAME, + PKCS11_MODULE_PATH +} pkcs11_uri_query_code; + +struct pkcs11_uri { + /* taken from https://www.rfc-editor.org/rfc/rfc7512.html mapping of the PKCS #11 URI path component attributes */ + char *id; + int id_len; + char *library_description; + char *library_manufacturer; + char *library_version; + char *token_manufacturer; + char *token_model; + char *object; + char *serial; + char *slot_description; + char *slot_id; + char *slot_manufacturer; + char *token_label; + char *type; + /* query */ + char *pin_source; + char *pin_value; + char *module_name; + char *module_path; +}; + +struct pkcs11_uri_attr { + char *name; + int id; +}; + +struct pkcs11_uri *pkcs11_uri_new(); +int parse_pkcs11_uri(const char *input_string, struct pkcs11_uri *result); +void pkcs11_uri_free(struct pkcs11_uri *uri); + +#endif // PKCS11_URI_H diff --git a/src/tools/pkcs15-crypt.c b/src/tools/pkcs15-crypt.c index 8d5be6d5b9..c2cbaea4b6 100644 --- a/src/tools/pkcs15-crypt.c +++ b/src/tools/pkcs15-crypt.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -27,13 +27,9 @@ #endif #include #include -#ifdef ENABLE_OPENSSL -#include -#include -#include -#endif #include "common/compat_getpass.h" +#include "libopensc/internal.h" #include "libopensc/opensc.h" #include "libopensc/pkcs15.h" #include "libopensc/asn1.h" @@ -105,7 +101,7 @@ static const char *option_help[] = { "Uses password (PIN) (use - for reading PIN from STDIN)", "Specify AID of the on-card PKCS#15 application to be binded to (in hexadecimal form)", "Wait for card insertion", - "Verbose operation. Use several times to enable debug output.", + "Verbose operation, may be used several times", }; static sc_context_t *ctx = NULL; @@ -129,10 +125,14 @@ static char *readpin_stdin(void) static char * get_pin(struct sc_pkcs15_object *obj) { - char buf[80]; + char buf[(sizeof obj->label) + 20]; char *pincode; - struct sc_pkcs15_auth_info *pinfo = (struct sc_pkcs15_auth_info *) obj->data; + struct sc_pkcs15_auth_info *pinfo; + + if (!obj) + return NULL; + pinfo = (struct sc_pkcs15_auth_info *) obj->data; if (pinfo->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return NULL; @@ -143,7 +143,8 @@ static char * get_pin(struct sc_pkcs15_object *obj) return strdup(opt_pincode); } - sprintf(buf, "Enter PIN [%.*s]: ", (int) sizeof obj->label, obj->label); + snprintf(buf, sizeof(buf), "Enter PIN [%.*s]: ", + (int) sizeof obj->label, obj->label); while (1) { pincode = getpass(buf); if (strlen(pincode) == 0) @@ -155,10 +156,10 @@ static char * get_pin(struct sc_pkcs15_object *obj) } } -static int read_input(u8 *buf, int buflen) +static size_t read_input(u8 *buf, int buflen) { FILE *inf; - int c; + size_t c; if (opt_input==NULL) { inf = stdin; @@ -166,21 +167,21 @@ static int read_input(u8 *buf, int buflen) inf = fopen(opt_input, "rb"); if (inf == NULL) { fprintf(stderr, "Unable to open '%s' for reading.\n", opt_input); - return -1; + return 0; } } c = fread(buf, 1, buflen, inf); if (inf!=stdin) { fclose(inf); } - if (c < 0) { + if (c <= 0) { perror("read"); - return -1; + return 0; } return c; } -static int write_output(const u8 *buf, int len) +static int write_output(const u8 *buf, size_t len) { FILE *outf; int output_binary = (opt_output == NULL && opt_raw == 0 ? 0 : 1); @@ -209,18 +210,19 @@ static int sign(struct sc_pkcs15_object *obj) { u8 buf[1024], out[1024]; struct sc_pkcs15_prkey_info *key = (struct sc_pkcs15_prkey_info *) obj->data; - int r, c, len; + int r, flags; + size_t c, len; if (opt_input == NULL) { fprintf(stderr, "No input file specified. Reading from stdin\n"); } c = read_input(buf, sizeof(buf)); - if (c < 0) + if (c <= 0) return 2; len = sizeof(out); if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA - && !(opt_crypt_flags & SC_ALGORITHM_RSA_PAD_PKCS1) + && !(opt_crypt_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01) && (size_t)c != key->modulus_length/8) { fprintf(stderr, "Input has to be exactly %lu bytes, when using no padding.\n", (unsigned long) key->modulus_length/8); @@ -231,7 +233,8 @@ static int sign(struct sc_pkcs15_object *obj) return SC_ERROR_NOT_SUPPORTED; } - r = sc_pkcs15_compute_signature(p15card, obj, opt_crypt_flags, buf, c, out, len); + flags = opt_crypt_flags & ~SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02; + r = sc_pkcs15_compute_signature(p15card, obj, flags, buf, c, out, len, NULL); if (r < 0) { fprintf(stderr, "Compute signature failed: %s\n", sc_strerror(r)); return 1; @@ -248,6 +251,12 @@ static int sign(struct sc_pkcs15_object *obj) return 2; } + if (seqlen > sizeof(out)) { + free(seq); + fprintf(stderr, "Signature sequence too large\n"); + return 2; + } + memcpy(out, seq, seqlen); len = seqlen; @@ -263,13 +272,14 @@ static int sign(struct sc_pkcs15_object *obj) static int decipher(struct sc_pkcs15_object *obj) { u8 buf[1024], out[1024]; - int r, c, len; + int r, len, flags; + size_t c; if (opt_input == NULL) { fprintf(stderr, "No input file specified. Reading from stdin\n"); } c = read_input(buf, sizeof(buf)); - if (c < 0) + if (c <= 0) return 2; len = sizeof(out); @@ -278,19 +288,20 @@ static int decipher(struct sc_pkcs15_object *obj) return SC_ERROR_NOT_SUPPORTED; } - r = sc_pkcs15_decipher(p15card, obj, opt_crypt_flags & SC_ALGORITHM_RSA_PAD_PKCS1, buf, c, out, len); + flags = opt_crypt_flags & ~SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01; + r = sc_pkcs15_decipher(p15card, obj, flags, buf, c, out, len, NULL); if (r < 0) { fprintf(stderr, "Decrypt failed: %s\n", sc_strerror(r)); return 1; } r = write_output(out, r); - return 0; + return r; } static int get_key(unsigned int usage, sc_pkcs15_object_t **result) { - sc_pkcs15_object_t *key, *pin; + sc_pkcs15_object_t *key, *pin = NULL; const char *usage_name; sc_pkcs15_id_t id; int r; @@ -328,7 +339,7 @@ static int get_key(unsigned int usage, sc_pkcs15_object_t **result) } /* Pin already verified previously */ - if (pin == prev_pin) + if (pin == prev_pin && key->user_consent == 0) return 0; pincode = get_pin(pin); @@ -338,7 +349,24 @@ static int get_key(unsigned int usage, sc_pkcs15_object_t **result) return 5; } - r = sc_pkcs15_verify_pin(p15card, pin, (const u8 *)pincode, pincode ? strlen(pincode) : 0); + /* + * Do what PKCS#11 would do for keys requiring CKA_ALWAYS_AUTHENTICATE + * and CKU_CONTEXT_SPECIFIC login to let driver know this verify will be followed by + * a crypto operation. Card drivers can test for SC_AC_CONTEXT_SPECIFIC + * to do any special handling. + */ + if (key->user_consent && pin) { + int auth_meth_saved; + struct sc_pkcs15_auth_info *pinfo = (struct sc_pkcs15_auth_info *) pin->data; + + auth_meth_saved = pinfo->auth_method; + + pinfo->auth_method = SC_AC_CONTEXT_SPECIFIC; + r = sc_pkcs15_verify_pin(p15card, pin, (const u8 *)pincode, pincode ? strlen(pincode) : 0); + pinfo->auth_method = auth_meth_saved; + } else + r = sc_pkcs15_verify_pin(p15card, pin, (const u8 *)pincode, pincode ? strlen(pincode) : 0); + free(pincode); if (r) { fprintf(stderr, "PIN code verification failed: %s\n", sc_strerror(r)); @@ -352,7 +380,7 @@ static int get_key(unsigned int usage, sc_pkcs15_object_t **result) return 0; } -int main(int argc, char * const argv[]) +int main(int argc, char *argv[]) { int err = 0, r, c, long_optind = 0; int do_decipher = 0; @@ -366,8 +394,10 @@ int main(int argc, char * const argv[]) c = getopt_long(argc, argv, "sck:r:i:o:f:Rp:vw", options, &long_optind); if (c == -1) break; - if (c == '?') - util_print_usage_and_die(app_name, options, option_help, NULL); + if (c == '?') { + util_print_usage(app_name, options, option_help, NULL); + return 2; + } switch (c) { case OPT_VERSION: do_print_version = 1; @@ -435,8 +465,10 @@ int main(int argc, char * const argv[]) break; } } - if (action_count == 0) - util_print_usage_and_die(app_name, options, option_help, NULL); + if (action_count == 0) { + util_print_usage(app_name, options, option_help, NULL); + return 2; + } if (do_print_version) { printf("%s\n", OPENSC_SCM_REVISION); @@ -449,6 +481,9 @@ int main(int argc, char * const argv[]) memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.ver = 0; ctx_param.app_name = app_name; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; r = sc_context_create(&ctx, &ctx_param); if (r) { @@ -456,12 +491,7 @@ int main(int argc, char * const argv[]) return 1; } - if (verbose > 1) { - ctx->debug = verbose; - sc_ctx_log_to_file(ctx, "stderr"); - } - - err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); + err = util_connect_card_ex(ctx, &card, opt_reader, opt_wait, 0); if (err) goto end; @@ -473,7 +503,8 @@ int main(int argc, char * const argv[]) aid.len = sizeof(aid.value); if (sc_hex_to_bin(opt_bind_to_aid, aid.value, &aid.len)) { fprintf(stderr, "Invalid AID value: '%s'\n", opt_bind_to_aid); - return 1; + err = 1; + goto end; } r = sc_pkcs15_bind(card, &aid, &p15card); @@ -508,7 +539,6 @@ int main(int argc, char * const argv[]) if (p15card) sc_pkcs15_unbind(p15card); if (card) { - sc_unlock(card); sc_disconnect_card(card); } if (ctx) diff --git a/src/tools/pkcs15-init.c b/src/tools/pkcs15-init.c index 38a40b92ac..2cba803c4b 100644 --- a/src/tools/pkcs15-init.c +++ b/src/tools/pkcs15-init.c @@ -26,7 +26,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -40,27 +40,27 @@ #endif #include #include "libopensc/sc-ossl-compat.h" -#if OPENSSL_VERSION_NUMBER >= 0x00907000L #include -#endif #include #include #include #include #include -#include #include #include #include -#if OPENSSL_VERSION_NUMBER >= 0x10000000L #include #include /* for OPENSSL_NO_EC */ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# include +# include +#endif #ifndef OPENSSL_NO_EC #include #endif /* OPENSSL_NO_EC */ -#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ #include "common/compat_strlcpy.h" +#include "libopensc/internal.h" #include "libopensc/cardctl.h" #include "libopensc/pkcs15.h" #include "libopensc/log.h" @@ -118,13 +118,10 @@ static int do_read_public_key(const char *, const char *, EVP_PKEY **); static int do_read_certificate(const char *, const char *, X509 **); static char * cert_common_name(X509 *x509); static void parse_commandline(int argc, char **argv); -static void read_options_file(const char *); -static void ossl_print_errors(void); static int verify_pin(struct sc_pkcs15_card *, char *); enum { - OPT_OPTIONS = 0x100, - OPT_PASSPHRASE, + OPT_PASSPHRASE = 0x100, OPT_PUBKEY, OPT_SECRKEY, OPT_EXTRACTABLE, @@ -148,6 +145,7 @@ enum { OPT_UPDATE_EXISTING, OPT_MD_CONTAINER_GUID, OPT_VERSION, + OPT_USER_CONSENT, OPT_PIN1 = 0x10000, /* don't touch these values */ OPT_PUK1 = 0x10001, @@ -206,6 +204,7 @@ const struct option options[] = { { "update-existing", no_argument, NULL, OPT_UPDATE_EXISTING}, { "extractable", no_argument, NULL, OPT_EXTRACTABLE }, + { "user-consent", required_argument, NULL, OPT_USER_CONSENT}, { "insecure", no_argument, NULL, OPT_INSECURE }, { "use-default-transport-keys", no_argument, NULL, 'T' }, @@ -214,7 +213,6 @@ const struct option options[] = { { "profile", required_argument, NULL, 'p' }, { "card-profile", required_argument, NULL, 'c' }, - { "options-file", required_argument, NULL, OPT_OPTIONS }, { "md-container-guid", required_argument, NULL, OPT_MD_CONTAINER_GUID}, { "wait", no_argument, NULL, 'w' }, { "help", no_argument, NULL, 'h' }, @@ -235,7 +233,7 @@ static const char * option_help[] = { "Store public key", "Store secret key", "Store an X.509 certificate", - "Update an X.509 certificate (carefull with mail decryption certs!!)", + "Update an X.509 certificate (careful with mail decryption certs!!)", "Store a data object", "Delete object(s) (use \"help\" for more information)", "Change attribute(s) (use \"help\" for more information)", @@ -267,11 +265,12 @@ static const char * option_help[] = { "Mark certificate as a CA certificate", "Specify X.509 key usage (use \"--key-usage help\" for more information)", "Finish initialization phase of the smart card", - "Update 'lastUpdate' attribut of tokenInfo", + "Update 'lastUpdate' attribute of tokenInfo", "When storing PKCS#12 ignore CA certificates", "Store or update existing certificate", "Private key stored as an extractable key", + "Set userConsent. Default = 0", "Insecure mode: do not require a PIN for private key", "Do not ask for transport keys if the driver thinks it knows the key", "Do not prompt the user; if no PINs supplied, pinpad will be used", @@ -279,11 +278,10 @@ static const char * option_help[] = { "Specify the general profile to use", "Specify the card profile to use", - "Read additional command line options from file", "For a new key specify GUID for a MD container", "Wait for card insertion", "Display this message", - "Verbose operation. Use several times to enable debug output.", + "Verbose operation, may be used several times", NULL, NULL, @@ -351,9 +349,9 @@ struct secret { #define SC_PKCS15INIT_TYPE_DATA 16 #define SC_PKCS15INIT_TYPE_SKEY 32 -static sc_context_t * ctx = NULL; -static sc_card_t * card = NULL; -static struct sc_pkcs15_card * p15card = NULL; +static sc_context_t * g_ctx = NULL; +static sc_card_t * g_card = NULL; +static struct sc_pkcs15_card * g_p15card = NULL; static char * opt_reader = NULL; static unsigned int opt_actions; static int opt_extractable = 0, @@ -395,6 +393,7 @@ static unsigned int opt_secret_count; static int opt_ignore_ca_certs = 0; static int opt_update_existing = 0; static int verbose = 0; +static int opt_user_consent = 0; static struct sc_pkcs15init_callbacks callbacks = { get_pin_callback, /* get_pin() */ @@ -450,23 +449,8 @@ main(int argc, char **argv) { struct sc_profile *profile = NULL; unsigned int n; - int r = 0; - -#if OPENSSL_VERSION_NUMBER >= 0x00907000L && OPENSSL_VERSION_NUMBER < 0x10100000L - OPENSSL_config(NULL); -#endif -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !(defined LIBRESSL_VERSION_NUMBER) - /* Openssl 1.1.0 magic */ - OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS - | OPENSSL_INIT_ADD_ALL_CIPHERS - | OPENSSL_INIT_ADD_ALL_DIGESTS - | OPENSSL_INIT_LOAD_CONFIG, - NULL); -#else - /* OpenSSL magic */ - OpenSSL_add_all_algorithms(); - OPENSSL_malloc_init(); -#endif + int r = 0; + struct sc_pkcs15_card *tmp_p15_data = NULL; #ifdef RANDOM_POOL if (!RAND_load_file(RANDOM_POOL, 32)) @@ -493,7 +477,7 @@ main(int argc, char **argv) sc_pkcs15init_set_callbacks(&callbacks); /* Bind the card-specific operations and load the profile */ - r = sc_pkcs15init_bind(card, opt_profile, opt_card_profile, NULL, &profile); + r = sc_pkcs15init_bind(g_card, opt_profile, opt_card_profile, NULL, &profile); if (r < 0) { printf("Couldn't bind to the card: %s\n", sc_strerror(r)); return 1; @@ -512,7 +496,7 @@ main(int argc, char **argv) if (action != ACTION_ERASE && action != ACTION_INIT && action != ACTION_ASSERT_PRISTINE - && p15card == NULL) { + && g_p15card == NULL) { /* Read the PKCS15 structure from the card */ if (opt_bind_to_aid) { struct sc_aid aid; @@ -523,16 +507,16 @@ main(int argc, char **argv) return 1; } - r = sc_pkcs15init_finalize_profile(card, profile, &aid); + r = sc_pkcs15init_finalize_profile(g_card, profile, &aid); if (r < 0) { fprintf(stderr, "Finalize profile error %s\n", sc_strerror(r)); break; } - r = sc_pkcs15_bind(card, &aid, &p15card); + r = sc_pkcs15_bind(g_card, &aid, &g_p15card); } else { - r = sc_pkcs15_bind(card, NULL, &p15card); + r = sc_pkcs15_bind(g_card, NULL, &g_p15card); } if (r) { fprintf(stderr, "PKCS#15 binding failed: %s\n", sc_strerror(r)); @@ -543,12 +527,12 @@ main(int argc, char **argv) * sure we're not messing things up */ if (verbose) - printf("Found %s\n", p15card->tokeninfo->label); + printf("Found %s\n", g_p15card->tokeninfo->label); - sc_pkcs15init_set_p15card(profile, p15card); + sc_pkcs15init_set_p15card(profile, g_p15card); if (opt_verify_pin) { - r = verify_pin(p15card, opt_authid); + r = verify_pin(g_p15card, opt_authid); if (r) { fprintf(stderr, "Failed to verify User PIN : %s\n", sc_strerror(r)); @@ -566,11 +550,11 @@ main(int argc, char **argv) break; case ACTION_ASSERT_PRISTINE: /* skip printing error message */ - if ((r = do_assert_pristine(card)) < 0) + if ((r = do_assert_pristine(g_card)) < 0) goto out; continue; case ACTION_ERASE: - r = do_erase(card, profile); + r = do_erase(g_card, profile); break; case ACTION_INIT: r = do_init_app(profile); @@ -608,7 +592,7 @@ main(int argc, char **argv) r = do_generate_skey(profile, opt_newkey); break; case ACTION_FINALIZE_CARD: - r = do_finalize_card(card, profile); + r = do_finalize_card(g_card, profile); break; case ACTION_SANITY_CHECK: r = do_sanity_check(profile); @@ -617,7 +601,7 @@ main(int argc, char **argv) profile->dirty = 1; break; case ACTION_ERASE_APPLICATION: - r = do_erase_application(card, profile); + r = do_erase_application(g_card, profile); break; default: util_fatal("Action not yet implemented\n"); @@ -635,16 +619,21 @@ main(int argc, char **argv) } out: + /* After erasing card profile->p15_data might change */ if (profile) { + tmp_p15_data = profile->p15_data; sc_pkcs15init_unbind(profile); + if (tmp_p15_data != g_p15card) { + sc_pkcs15_unbind(tmp_p15_data); + } } - if (p15card) { - sc_pkcs15_unbind(p15card); + if (g_p15card) { + sc_pkcs15_unbind(g_p15card); } - if (card) { - sc_disconnect_card(card); + if (g_card) { + sc_disconnect_card(g_card); } - sc_release_context(ctx); + sc_release_context(g_ctx); return r < 0? 1 : 0; } @@ -657,19 +646,17 @@ open_reader_and_card(char *reader) memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.ver = 0; ctx_param.app_name = app_name; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; - r = sc_context_create(&ctx, &ctx_param); + r = sc_context_create(&g_ctx, &ctx_param); if (r) { util_error("Failed to establish context: %s\n", sc_strerror(r)); return 0; } - if (verbose > 1) { - ctx->debug = verbose; - sc_ctx_log_to_file(ctx, "stderr"); - } - - if (util_connect_card_ex(ctx, &card, reader, opt_wait, 0, verbose)) + if (util_connect_card_ex(g_ctx, &g_card, reader, opt_wait, 0)) return 0; return 1; @@ -727,28 +714,46 @@ struct alg_spec { }; static const struct alg_spec alg_types_sym[] = { - { "des", SC_ALGORITHM_DES, 56 }, + { "des", SC_ALGORITHM_DES, 64 }, { "3des", SC_ALGORITHM_3DES, 192 }, { "aes", SC_ALGORITHM_AES, 128 }, { NULL, -1, 0 } }; +// clang-format off +/* RSA can have a number , default is 2048 */ +/* EC require a curve name */ +/* EDDSA and XEDDSA without a size require a size or curve name or OID */ +/* other EDDSA and XEDDSA can be used alone */ static const struct alg_spec alg_types_asym[] = { - { "rsa", SC_ALGORITHM_RSA, 1024 }, - { "dsa", SC_ALGORITHM_DSA, 1024 }, + { "rsa", SC_ALGORITHM_RSA, 3072 }, /* new default */ { "gost2001", SC_ALGORITHM_GOSTR3410, SC_PKCS15_GOSTR3410_KEYSIZE }, - { "ec", SC_ALGORITHM_EC, 0 }, + { "ec", SC_ALGORITHM_EC, 0 }, /* keybits derived from curve */ + /* RFC 8410 */ + { "Ed25519", SC_ALGORITHM_EDDSA, 255 }, /* RFC 8410 and gunpg */ + { "Ed448", SC_ALGORITHM_EDDSA, 448 }, + { "X25519", SC_ALGORITHM_XEDDSA, 255 }, + { "X448", SC_ALGORITHM_XEDDSA, 448 }, + /* used by Yubikey and GNUK */ + { "edwards25519", SC_ALGORITHM_EDDSA, 255 }, + { "curve25519", SC_ALGORITHM_XEDDSA, 255 }, + /* gnupg */ + { "cv25519", SC_ALGORITHM_XEDDSA, 255 }, + { NULL, -1, 0 } }; +// clang-format on static int parse_alg_spec(const struct alg_spec *types, const char *spec, unsigned int *keybits, struct sc_pkcs15_prkey *prkey) { - int i, algorithm = -1; + int i, types_idx = -1, algorithm = -1; + unsigned int user_keybits = 0; char *end; for (i = 0; types[i].spec; i++) { if (!strncasecmp(spec, types[i].spec, strlen(types[i].spec))) { + types_idx = i; /* save index of types array */ algorithm = types[i].algorithm; *keybits = types[i].keybits; spec += strlen(types[i].spec); @@ -763,11 +768,31 @@ parse_alg_spec(const struct alg_spec *types, const char *spec, unsigned int *key if (*spec == '/' || *spec == '-' || *spec == ':') spec++; - if (*spec) { - if (isalpha(*spec) && algorithm == SC_ALGORITHM_EC && prkey) { - prkey->u.ec.params.named_curve = strdup(spec); - } else { - *keybits = strtoul(spec, &end, 10); + /* prkey is required for keys that use ecparms */ + if (*spec == '\0' && (algorithm == SC_ALGORITHM_EDDSA || algorithm == SC_ALGORITHM_XEDDSA) && prkey) { + if ((prkey->u.ec.params.named_curve = strdup(types[types_idx].spec)) == NULL) /* correct case */ + return SC_ERROR_OUT_OF_MEMORY; + return algorithm; + } + + if (*spec != '\0') { + if (isalpha((unsigned char)*spec) && algorithm == SC_ALGORITHM_EC && prkey) { + if ((prkey->u.ec.params.named_curve = strdup(spec)) == NULL) /* pass EC curve name */ + return SC_ERROR_OUT_OF_MEMORY; + } else if ((algorithm == SC_ALGORITHM_EDDSA || algorithm == SC_ALGORITHM_XEDDSA) && prkey) { + if ((prkey->u.ec.params.named_curve = strdup(types[types_idx].spec)) == NULL) /* copy correct case */ + return SC_ERROR_OUT_OF_MEMORY; + user_keybits = (unsigned)strtoul(spec, &end, 10); + if (*end) { + util_error("Invalid number of key bits \"%s\"", spec); + return SC_ERROR_INVALID_ARGUMENTS; + } + if (user_keybits != *keybits) { + util_error("If specified, number of key bits must be \"%d\" for \"%s\"", *keybits, types[types_idx].spec); + return SC_ERROR_INVALID_ARGUMENTS; + } + } else { /* rsa or symmetric key */ + *keybits = (unsigned)strtoul(spec, &end, 10); if (*end) { util_error("Invalid number of key bits \"%s\"", spec); return SC_ERROR_INVALID_ARGUMENTS; @@ -857,7 +882,7 @@ do_init_app(struct sc_profile *profile) hints.flags = SC_UI_PIN_RETYPE | SC_UI_PIN_CHECK_LENGTH | SC_UI_PIN_MISMATCH_RETRY; - hints.card = card; + hints.card = g_card; hints.p15card = NULL; hints.info.pin = &info; @@ -917,11 +942,11 @@ do_init_app(struct sc_profile *profile) args.serial = (const char *) opt_serial; args.label = opt_label; - r = sc_lock(card); + r = sc_lock(g_card); if (r < 0) return r; - r = sc_pkcs15init_add_app(card, profile, &args); - sc_unlock(card); + r = sc_pkcs15init_add_app(g_card, profile, &args); + sc_unlock(g_card); return r; failed: fprintf(stderr, "Failed to read PIN: %s\n", sc_strerror(r)); @@ -945,8 +970,8 @@ do_store_pin(struct sc_profile *profile) hints.flags = SC_UI_PIN_RETYPE | SC_UI_PIN_CHECK_LENGTH | SC_UI_PIN_MISMATCH_RETRY; - hints.card = card; - hints.p15card = p15card; + hints.card = g_card; + hints.p15card = g_p15card; hints.info.pin = &info; pin_id = opt_objectid ? opt_objectid : opt_authid; @@ -990,17 +1015,30 @@ do_store_pin(struct sc_profile *profile) args.puk = (u8 *) opt_pins[1]; args.puk_len = opt_pins[1]? strlen(opt_pins[1]) : 0; - r = sc_lock(p15card->card); + r = sc_lock(g_p15card->card); if (r < 0) return r; - r = sc_pkcs15init_store_pin(p15card, profile, &args); - sc_unlock(p15card->card); + r = sc_pkcs15init_store_pin(g_p15card, profile, &args); + sc_unlock(g_p15card->card); return r; failed: fprintf(stderr, "Failed to read PIN: %s\n", sc_strerror(r)); return SC_ERROR_PKCS15INIT; } +static void sc_pkcs15_inc_id(sc_pkcs15_id_t *id) +{ + long len; + for (len = (long)id->len - 1; len >= 0; len--) { + if (id->value[len]++ != 0xFF) + break; + } + if (len < 0 && id->len < SC_PKCS15_MAX_ID_SIZE) { + memmove(id->value + 1, id->value, id->len++); + id->value[0] = 1; + } +} + /* * Store a private key */ @@ -1030,8 +1068,11 @@ do_store_private_key(struct sc_profile *profile) } r = sc_pkcs15_convert_prkey(&args.key, pkey); - if (r < 0) + if (r < 0) { + EVP_PKEY_free(pkey); + sc_log_openssl(g_ctx); return r; + } init_gost_params(&args.params.gost, pkey); if (ncerts) { @@ -1039,12 +1080,14 @@ do_store_private_key(struct sc_profile *profile) /* tell openssl to cache the extensions */ X509_check_purpose(cert[0], -1, -1); - usage = X509_get_extended_key_usage(cert[0]); + usage = X509_get_key_usage(cert[0]); /* No certificate usage? Assume ordinary * user cert */ if (usage == 0) - usage = 0x1F; + usage = KU_NON_REPUDIATION + | KU_DIGITAL_SIGNATURE + | KU_KEY_ENCIPHERMENT; /* If the user requested a specific key usage on the * command line check if it includes _more_ @@ -1062,17 +1105,19 @@ do_store_private_key(struct sc_profile *profile) args.x509_usage = opt_x509_usage? opt_x509_usage : usage; } - args.access_flags |= - SC_PKCS15_PRKEY_ACCESS_SENSITIVE - | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE - | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; + args.access_flags |= SC_PKCS15_PRKEY_ACCESS_SENSITIVE; - r = sc_lock(p15card->card); - if (r < 0) + r = sc_lock(g_p15card->card); + if (r < 0) { + EVP_PKEY_free(pkey); + sc_pkcs15_erase_prkey(&(args.key)); return r; - r = sc_pkcs15init_store_private_key(p15card, profile, &args, NULL); + } + r = sc_pkcs15init_store_private_key(g_p15card, profile, &args, NULL); + sc_pkcs15_erase_prkey(&(args.key)); if (r < 0) { - sc_unlock(p15card->card); + EVP_PKEY_free(pkey); + sc_unlock(g_p15card->card); return r; } @@ -1082,6 +1127,7 @@ do_store_private_key(struct sc_profile *profile) for (i = 0; i < ncerts && r >= 0; i++) { struct sc_pkcs15init_certargs cargs; char namebuf[SC_PKCS15_MAX_LABEL_SIZE-1]; + int cargs_label_needs_free = 0; if (i && opt_ignore_ca_certs) break; @@ -1089,33 +1135,46 @@ do_store_private_key(struct sc_profile *profile) memset(&cargs, 0, sizeof(cargs)); /* Encode the cert */ - if ((r = do_convert_cert(&cargs.der_encoded, cert[i])) < 0) + if ((r = do_convert_cert(&cargs.der_encoded, cert[i])) < 0) { + EVP_PKEY_free(pkey); + sc_unlock(g_p15card->card); return r; + } X509_check_purpose(cert[i], -1, -1); - cargs.x509_usage = X509_get_extended_key_usage(cert[i]); + cargs.x509_usage = X509_get_key_usage(cert[i]); cargs.label = cert_common_name(cert[i]); if (!cargs.label) cargs.label = X509_NAME_oneline(X509_get_subject_name(cert[i]), namebuf, sizeof(namebuf)); + else + cargs_label_needs_free = 1; /* Just the first certificate gets the same ID * as the private key. All others get * an ID of their own */ if (i == 0) { cargs.id = args.id; - if (opt_cert_label != 0) + if (opt_cert_label != 0) { + if (cargs_label_needs_free) + free((char *) cargs.label); cargs.label = opt_cert_label; + cargs_label_needs_free = 0; + } } else { if (is_cacert_already_present(&cargs)) { printf("Certificate #%d already present, not stored.\n", i); goto next_cert; } + sc_pkcs15_inc_id(&args.id); + cargs.id = args.id; cargs.authority = 1; } - r = sc_pkcs15init_store_certificate(p15card, profile, &cargs, NULL); + r = sc_pkcs15init_store_certificate(g_p15card, profile, &cargs, NULL); next_cert: + if (cargs_label_needs_free) + free((char *) cargs.label); free(cargs.der_encoded.value); } @@ -1123,7 +1182,8 @@ do_store_private_key(struct sc_profile *profile) if (ncerts == 0) r = do_store_public_key(profile, pkey); - sc_unlock(p15card->card); + EVP_PKEY_free(pkey); + sc_unlock(g_p15card->card); return r; } @@ -1138,12 +1198,13 @@ is_cacert_already_present(struct sc_pkcs15init_certargs *args) sc_pkcs15_cert_t *cert; int i, count, r; - r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32); + r = sc_pkcs15_get_objects(g_p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32); if (r <= 0) return 0; count = r; for (i = 0; i < count; i++) { + int private_obj; cinfo = (sc_pkcs15_cert_info_t *) objs[i]->data; if (!cinfo->authority) @@ -1153,7 +1214,8 @@ is_cacert_already_present(struct sc_pkcs15init_certargs *args) /* XXX we should also match the usage field here */ /* Compare the DER representation of the certificates */ - r = sc_pkcs15_read_certificate(p15card, cinfo, &cert); + private_obj = objs[i]->flags & SC_PKCS15_CO_FLAG_PRIVATE; + r = sc_pkcs15_read_certificate(g_p15card, cinfo, private_obj, &cert); if (r < 0 || !cert) continue; @@ -1191,16 +1253,21 @@ do_store_public_key(struct sc_profile *profile, EVP_PKEY *pkey) } if (r >= 0) { r = sc_pkcs15_convert_pubkey(&args.key, pkey); + sc_log_openssl(g_ctx); if (r >= 0) init_gost_params(&args.params.gost, pkey); } if (r >= 0) { - r = sc_lock(p15card->card); - if (r < 0) + r = sc_lock(g_p15card->card); + if (r < 0) { + sc_pkcs15_erase_pubkey(&(args.key)); return r; - r = sc_pkcs15init_store_public_key(p15card, profile, &args, &dummy); - sc_unlock(p15card->card); + } + r = sc_pkcs15init_store_public_key(g_p15card, profile, &args, &dummy); + sc_pkcs15_erase_pubkey(&(args.key)); + sc_unlock(g_p15card->card); } + sc_pkcs15_erase_pubkey(&(args.key)); return r; } @@ -1229,22 +1296,24 @@ do_store_secret_key(struct sc_profile *profile) return SC_ERROR_INVALID_ARGUMENTS; } - r = do_read_data_object(opt_infile, &args.key.data, &args.key.data_len, (keybits+7) / 8); - if (r < 0) + r = do_read_data_object(opt_infile, &args.key.data, &args.key.data_len, BYTES4BITS(keybits)); + if (r < 0) { + free(args.key.data); return r; + } args.algorithm = algorithm; args.value_len = keybits; - args.access_flags |= - SC_PKCS15_PRKEY_ACCESS_SENSITIVE - | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE - | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; + args.access_flags |= SC_PKCS15_PRKEY_ACCESS_SENSITIVE; - r = sc_lock(p15card->card); - if (r < 0) + r = sc_lock(g_p15card->card); + if (r < 0) { + free(args.key.data); return r; - r = sc_pkcs15init_store_secret_key(p15card, profile, &args, NULL); - sc_unlock(p15card->card); + } + r = sc_pkcs15init_store_secret_key(g_p15card, profile, &args, NULL); + sc_unlock(g_p15card->card); + free(args.key.data); return r; } @@ -1272,12 +1341,13 @@ do_store_certificate(struct sc_profile *profile) r = do_read_certificate(opt_infile, opt_format, &cert); if (r >= 0) r = do_convert_cert(&args.der_encoded, cert); + X509_free(cert); if (r >= 0) { - r = sc_lock(p15card->card); + r = sc_lock(g_p15card->card); if (r < 0) return r; - r = sc_pkcs15init_store_certificate(p15card, profile, &args, NULL); - sc_unlock(p15card->card); + r = sc_pkcs15init_store_certificate(g_p15card, profile, &args, NULL); + sc_unlock(g_p15card->card); } if (args.der_encoded.value) @@ -1291,7 +1361,7 @@ do_read_check_certificate(sc_pkcs15_cert_t *sc_oldcert, const char *filename, const char *format, sc_pkcs15_der_t *newcert_raw) { X509 *oldcert, *newcert; - EVP_PKEY *oldpk, *newpk; + EVP_PKEY *oldpk = NULL, *newpk = NULL; int oldpk_type, newpk_type; const u8 *ptr; int r; @@ -1300,8 +1370,10 @@ do_read_check_certificate(sc_pkcs15_cert_t *sc_oldcert, ptr = sc_oldcert->data.value; oldcert = d2i_X509(NULL, &ptr, sc_oldcert->data.len); - if (oldcert == NULL) + if (oldcert == NULL) { + sc_log_openssl(g_ctx); return SC_ERROR_INTERNAL; + } /* Read the new cert from file and get it's public key */ r = do_read_certificate(filename, format, &newcert); @@ -1312,6 +1384,13 @@ do_read_check_certificate(sc_pkcs15_cert_t *sc_oldcert, oldpk = X509_get_pubkey(oldcert); newpk = X509_get_pubkey(newcert); + if (!oldpk || !newpk) { + EVP_PKEY_free(newpk); + EVP_PKEY_free(oldpk); + X509_free(oldcert); + sc_log_openssl(g_ctx); + return SC_ERROR_INTERNAL; + } oldpk_type = EVP_PKEY_base_id(oldpk); newpk_type = EVP_PKEY_base_id(newpk); @@ -1319,24 +1398,15 @@ do_read_check_certificate(sc_pkcs15_cert_t *sc_oldcert, /* Compare the public keys, there's no high level openssl function for this(?) */ /* Yes there is in 1.0.2 and above EVP_PKEY_cmp */ - r = SC_ERROR_INVALID_ARGUMENTS; if (oldpk_type == newpk_type) { -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - if (EVP_PKEY_cmp(oldpk, newpk) == 1) - r = 0; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) + if (EVP_PKEY_eq(oldpk, newpk) == 1) #else - if ((oldpk_type == EVP_PKEY_DSA) && - !BN_cmp(EVP_PKEY_get0_DSA(oldpk)->p, EVP_PKEY_get0_DSA(newpk)->p) && - !BN_cmp(EVP_PKEY_get0_DSA(oldpk)->q, EVP_PKEY_get0_DSA(newpk)->q) && - !BN_cmp(EVP_PKEY_get0_DSA(oldpk)->g, EVP_PKEY_get0_DSA(newpk)->g)) - r = 0; - else if ((oldpk_type == EVP_PKEY_RSA) && - !BN_cmp(EVP_PKEY_get0_RSA(oldpk)->n, EVP_PKEY_get0_RSA(newpk)->n) && - !BN_cmp(EVP_PKEY_get0_RSA(oldpk)->e, EVP_PKEY_get0_RSA(newpk)->e)) - r = 0; + if (EVP_PKEY_cmp(oldpk, newpk) == 1) #endif + r = 0; } EVP_PKEY_free(newpk); @@ -1366,6 +1436,7 @@ do_update_certificate(struct sc_profile *profile) sc_pkcs15_cert_t *oldcert = NULL; sc_pkcs15_der_t newcert_raw; int r; + int private_obj; if (opt_objectid == NULL) { util_error("no ID given for the cert: use --id"); @@ -1374,17 +1445,18 @@ do_update_certificate(struct sc_profile *profile) sc_pkcs15_format_id(opt_objectid, &id); - if (sc_pkcs15_find_cert_by_id(p15card, &id, &obj) != 0) { + if (sc_pkcs15_find_cert_by_id(g_p15card, &id, &obj) != 0) { util_error("Couldn't find the cert with ID %s\n", opt_objectid); return SC_ERROR_OBJECT_NOT_FOUND; } - r = sc_lock(p15card->card); + r = sc_lock(g_p15card->card); if (r < 0) return r; certinfo = (sc_pkcs15_cert_info_t *) obj->data; - r = sc_pkcs15_read_certificate(p15card, certinfo, &oldcert); + private_obj = obj->flags & SC_PKCS15_CO_FLAG_PRIVATE; + r = sc_pkcs15_read_certificate(g_p15card, certinfo, private_obj, &oldcert); if (r < 0) goto err; @@ -1394,14 +1466,14 @@ do_update_certificate(struct sc_profile *profile) if (r < 0) goto err; - r = sc_pkcs15init_update_certificate(p15card, profile, obj, + r = sc_pkcs15init_update_certificate(g_p15card, profile, obj, newcert_raw.value, newcert_raw.len); if (newcert_raw.value) free(newcert_raw.value); err: - sc_unlock(p15card->card); + sc_unlock(g_p15card->card); return r; } @@ -1437,17 +1509,16 @@ do_store_data_object(struct sc_profile *profile) /* der_encoded contains the plain data, nothing DER encoded */ args.der_encoded.value = data; args.der_encoded.len = datalen; - r = sc_lock(p15card->card); + r = sc_lock(g_p15card->card); if (r < 0) { free(data); return r; } - r = sc_pkcs15init_store_data_object(p15card, profile, &args, NULL); - sc_unlock(p15card->card); + r = sc_pkcs15init_store_data_object(g_p15card, profile, &args, NULL); + sc_unlock(g_p15card->card); } - if (data) - free(data); + free(data); return r; } @@ -1458,11 +1529,11 @@ static int do_sanity_check(struct sc_profile *profile) { int r; - r = sc_lock(p15card->card); + r = sc_lock(g_p15card->card); if (r < 0) return r; - r = sc_pkcs15init_sanity_check(p15card, profile); - sc_unlock(p15card->card); + r = sc_pkcs15init_sanity_check(g_p15card, profile); + sc_unlock(g_p15card->card); return r; } @@ -1473,7 +1544,7 @@ static int cert_is_root(sc_pkcs15_cert_t *c) } /* Check if the cert has a 'sibling' and return it's parent cert. - * Should be made more effcicient for long chains by caching the certs. + * Should be made more efficient for long chains by caching the certs. */ static int get_cert_info(sc_pkcs15_card_t *myp15card, sc_pkcs15_object_t *certobj, int *has_sibling, int *stop, sc_pkcs15_object_t **issuercert) @@ -1482,12 +1553,14 @@ static int get_cert_info(sc_pkcs15_card_t *myp15card, sc_pkcs15_object_t *certob sc_pkcs15_object_t *otherobj; sc_pkcs15_cert_t *othercert = NULL; int r; + int private_obj; *issuercert = NULL; *has_sibling = 0; *stop = 0; - r = sc_pkcs15_read_certificate(myp15card, (sc_pkcs15_cert_info_t *) certobj->data, &cert); + private_obj = certobj->flags & SC_PKCS15_CO_FLAG_PRIVATE; + r = sc_pkcs15_read_certificate(myp15card, (sc_pkcs15_cert_info_t *) certobj->data, private_obj, &cert); if (r < 0) return r; @@ -1503,7 +1576,9 @@ static int get_cert_info(sc_pkcs15_card_t *myp15card, sc_pkcs15_object_t *certob sc_pkcs15_free_certificate(othercert); othercert=NULL; } - r = sc_pkcs15_read_certificate(myp15card, (sc_pkcs15_cert_info_t *) otherobj->data, &othercert); + + private_obj = otherobj->flags & SC_PKCS15_CO_FLAG_PRIVATE; + r = sc_pkcs15_read_certificate(myp15card, (sc_pkcs15_cert_info_t *) otherobj->data, private_obj, &othercert); if (r < 0 || !othercert) goto done; if ((cert->issuer_len == othercert->subject_len) && @@ -1530,7 +1605,7 @@ static int get_cert_info(sc_pkcs15_card_t *myp15card, sc_pkcs15_object_t *certob /* Delete object(s) by ID. The 'which' param can be any combination of * SC_PKCS15INIT_TYPE_PRKEY, SC_PKCS15INIT_TYPE_PUBKEY, SC_PKCS15INIT_TYPE_CERT * and SC_PKCS15INIT_TYPE_CHAIN. In the last case, every cert in the chain is - * deleted, starting with the cert with ID 'id' and untill a CA cert is + * deleted, starting with the cert with ID 'id' and until a CA cert is * reached that certified other remaining certs on the card. */ static int do_delete_crypto_objects(sc_pkcs15_card_t *myp15card, @@ -1618,7 +1693,7 @@ do_delete_objects(struct sc_profile *profile, unsigned int myopt_delete_flags) { int r = 0, count = 0; - r = sc_lock(p15card->card); + r = sc_lock(g_p15card->card); if (r < 0) return r; @@ -1629,17 +1704,17 @@ do_delete_objects(struct sc_profile *profile, unsigned int myopt_delete_flags) if (opt_application_id != NULL) { sc_format_oid(&app_oid, opt_application_id); - r = sc_pkcs15_find_data_object_by_app_oid(p15card, &app_oid, &obj); + r = sc_pkcs15_find_data_object_by_app_oid(g_p15card, &app_oid, &obj); } else if (opt_application_name != NULL && opt_label != NULL) { - r = sc_pkcs15_find_data_object_by_name(p15card, opt_application_name, opt_label, &obj); + r = sc_pkcs15_find_data_object_by_name(g_p15card, opt_application_name, opt_label, &obj); } else { util_fatal("Specify the --application-id or --application-name and --label for the data object to be deleted\n"); } if (r >= 0) { - r = sc_pkcs15init_delete_object(p15card, profile, obj); + r = sc_pkcs15init_delete_object(g_p15card, profile, obj); if (r >= 0) count++; } @@ -1651,12 +1726,12 @@ do_delete_objects(struct sc_profile *profile, unsigned int myopt_delete_flags) util_fatal("Specify the --id for key(s) or cert(s) to be deleted\n"); sc_pkcs15_format_id(opt_objectid, &id); - r = do_delete_crypto_objects(p15card, profile, &id, myopt_delete_flags); + r = do_delete_crypto_objects(g_p15card, profile, &id, myopt_delete_flags); if (r >= 0) count += r; } - sc_unlock(p15card->card); + sc_unlock(g_p15card->card); printf("Deleted %d objects\n", count); return r; @@ -1683,23 +1758,23 @@ do_change_attributes(struct sc_profile *profile, unsigned int myopt_type) switch(myopt_type) { case SC_PKCS15INIT_TYPE_PRKEY: - if ((r = sc_pkcs15_find_prkey_by_id(p15card, &id, &obj)) != 0) + if ((r = sc_pkcs15_find_prkey_by_id(g_p15card, &id, &obj)) != 0) return r; break; case SC_PKCS15INIT_TYPE_PUBKEY: - if ((r = sc_pkcs15_find_pubkey_by_id(p15card, &id, &obj)) != 0) + if ((r = sc_pkcs15_find_pubkey_by_id(g_p15card, &id, &obj)) != 0) return r; break; case SC_PKCS15INIT_TYPE_CERT: - if ((r = sc_pkcs15_find_cert_by_id(p15card, &id, &obj)) != 0) + if ((r = sc_pkcs15_find_cert_by_id(g_p15card, &id, &obj)) != 0) return r; break; case SC_PKCS15INIT_TYPE_DATA: - if ((r = sc_pkcs15_find_data_object_by_id(p15card, &id, &obj)) != 0) + if ((r = sc_pkcs15_find_data_object_by_id(g_p15card, &id, &obj)) != 0) return r; break; case SC_PKCS15INIT_TYPE_SKEY: - if ((r = sc_pkcs15_find_skey_by_id(p15card, &id, &obj)) != 0) + if ((r = sc_pkcs15_find_skey_by_id(g_p15card, &id, &obj)) != 0) return r; break; } @@ -1713,11 +1788,11 @@ do_change_attributes(struct sc_profile *profile, unsigned int myopt_type) strlcpy(obj->label, opt_label, sizeof(obj->label)); } - r = sc_lock(p15card->card); + r = sc_lock(g_p15card->card); if (r < 0) return r; - r = sc_pkcs15init_update_any_df(p15card, profile, obj->df, 0); - sc_unlock(p15card->card); + r = sc_pkcs15init_update_any_df(g_p15card, profile, obj->df, 0); + sc_unlock(g_p15card->card); return r; } @@ -1758,10 +1833,11 @@ do_generate_key(struct sc_profile *profile, const char *spec) break; } - r = sc_lock(p15card->card); + r = sc_lock(g_p15card->card); if (r == 0) - r = sc_pkcs15init_generate_key(p15card, profile, &keygen_args, keybits, NULL); - sc_unlock(p15card->card); + r = sc_pkcs15init_generate_key(g_p15card, profile, &keygen_args, keybits, NULL); + sc_unlock(g_p15card->card); + sc_pkcs15_erase_prkey(&keygen_args.prkey_args.key); return r; } @@ -1777,19 +1853,20 @@ do_generate_skey(struct sc_profile *profile, const char *spec) if ((r = init_skeyargs(&skey_args)) < 0) return r; - skey_args.algorithm = algorithm; algorithm = parse_alg_spec(alg_types_sym, spec, &keybits, 0); if (algorithm < 0) { util_error("Invalid symmetric key spec: \"%s\"", spec); return SC_ERROR_INVALID_ARGUMENTS; } + + skey_args.algorithm = algorithm; skey_args.value_len = keybits; - r = sc_lock(p15card->card); + r = sc_lock(g_p15card->card); if (r == 0) - r = sc_pkcs15init_generate_secret_key(p15card, profile, &skey_args, NULL); - sc_unlock(p15card->card); + r = sc_pkcs15init_generate_secret_key(g_p15card, profile, &skey_args, NULL); + sc_unlock(g_p15card->card); return r; } @@ -1815,6 +1892,7 @@ static int init_prkeyargs(struct sc_pkcs15init_prkeyargs *args) args->guid = (unsigned char *)opt_md_container_guid; args->guid_len = strlen(opt_md_container_guid); } + args->user_consent = opt_user_consent; return 0; } @@ -1836,23 +1914,49 @@ static int init_skeyargs(struct sc_pkcs15init_skeyargs *args) } args->label = opt_label; + if ((opt_x509_usage & SC_PKCS15INIT_X509_DATA_ENCIPHERMENT) == SC_PKCS15INIT_X509_DATA_ENCIPHERMENT) { + args->usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT; + } + + if ((opt_x509_usage & SC_PKCS15INIT_X509_KEY_ENCIPHERMENT) == SC_PKCS15INIT_X509_KEY_ENCIPHERMENT) { + args->usage |= SC_PKCS15_PRKEY_USAGE_WRAP | SC_PKCS15_PRKEY_USAGE_UNWRAP; + } + args->user_consent = opt_user_consent; + return 0; } static void init_gost_params(struct sc_pkcs15init_keyarg_gost_params *params, EVP_PKEY *pkey) { -#if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC) +#if !defined(OPENSSL_NO_EC) +#if OPENSSL_VERSION_NUMBER < 0x30000000L EC_KEY *key; +#else + char name[256]; +#endif + int nid = NID_undef; - assert(pkey); + if (!pkey) + return; if (EVP_PKEY_id(pkey) == NID_id_GostR3410_2001) { + if (!params) + return; +#if OPENSSL_VERSION_NUMBER < 0x30000000L key = EVP_PKEY_get0(pkey); - assert(key); - assert(params); - assert(EC_KEY_get0_group(key)); - assert(EC_GROUP_get_curve_name(EC_KEY_get0_group(key)) > 0); - switch (EC_GROUP_get_curve_name(EC_KEY_get0_group(key))) { + if (!key) + return; + if (!EC_KEY_get0_group(key)) + return; + nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(key)); +#else + if (!EVP_PKEY_get_group_name(pkey, name, sizeof(name), NULL)) + return; + nid = OBJ_txt2nid(name); +#endif + if (nid <= 0) + return; + switch (nid) { case NID_id_GostR3410_2001_CryptoPro_A_ParamSet: params->gostr3410 = SC_PKCS15_PARAMSET_GOSTR3410_A; break; @@ -1897,9 +2001,9 @@ parse_secret(struct secret *secret, const char *arg) else goto parse_err; str += 3; - if (!isdigit(str[3])) + if (!isdigit((unsigned char)str[3])) goto parse_err; - secret->reference = strtoul(str, &str, 10); + secret->reference = (int)strtoul(str, &str, 10); if (*str != '\0') goto parse_err; } @@ -2061,10 +2165,14 @@ get_pin_callback(struct sc_profile *profile, hints.prompt = prompt; hints.obj_label = name; hints.usage = SC_UI_USAGE_OTHER; - hints.card = card; - hints.p15card = p15card; + hints.card = g_card; + hints.p15card = g_p15card; if ((r = get_pin(&hints, &secret)) < 0) { + if (secret) { + sc_mem_clear(secret, strlen(secret)); + free(secret); + } fprintf(stderr, "Failed to read PIN from user: %s\n", sc_strerror(r)); @@ -2140,7 +2248,8 @@ get_key_callback(struct sc_profile *profile, prompt = "Please enter key"; if (def_key_size && def_key_size < 64) { - unsigned int j, k = 0; + unsigned int j; + size_t k = 0; sprintf(buffer, "%s [", prompt); k = strlen(buffer); @@ -2202,7 +2311,7 @@ static int pass_cb(char *buf, int len, int flags, void *d) return 0; } - plen = strlen(pass); + plen = (int)strlen(pass); if (plen <= 0) return 0; if (plen > len) @@ -2223,7 +2332,7 @@ do_read_pem_private_key(const char *filename, const char *passphrase, *key = PEM_read_bio_PrivateKey(bio, NULL, pass_cb, (char *) passphrase); BIO_free(bio); if (*key == NULL) { - ossl_print_errors(); + sc_log_openssl(g_ctx); return SC_ERROR_CANNOT_LOAD_KEY; } return 0; @@ -2277,7 +2386,8 @@ do_read_pkcs12_private_key(const char *filename, const char *passphrase, *key = user_key; return ncerts; -error: ossl_print_errors(); +error: + sc_log_openssl(g_ctx); return SC_ERROR_CANNOT_LOAD_KEY; } @@ -2310,8 +2420,8 @@ do_read_private_key(const char *filename, const char *format, r = util_getpass(&passphrase, &len, stdin); if (r < 0 || !passphrase) return SC_ERROR_INTERNAL; - r = do_read_pkcs12_private_key(filename, - passphrase, pk, certs, max_certs); + r = do_read_pkcs12_private_key(filename, + passphrase, pk, certs, max_certs); } } else { util_error("Error when reading private key. " @@ -2343,7 +2453,7 @@ do_read_pem_public_key(const char *filename) pk = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); BIO_free(bio); if (pk == NULL) - ossl_print_errors(); + sc_log_openssl(g_ctx); return pk; } @@ -2359,7 +2469,7 @@ do_read_der_public_key(const char *filename) pk = d2i_PUBKEY_bio(bio, NULL); BIO_free(bio); if (pk == NULL) - ossl_print_errors(); + sc_log_openssl(g_ctx); return pk; } @@ -2396,7 +2506,7 @@ do_read_pem_certificate(const char *filename) xp = PEM_read_bio_X509(bio, NULL, NULL, NULL); BIO_free(bio); if (xp == NULL) - ossl_print_errors(); + sc_log_openssl(g_ctx); return xp; } @@ -2412,7 +2522,7 @@ do_read_der_certificate(const char *filename) xp = d2i_X509_bio(bio, NULL); BIO_free(bio); if (xp == NULL) - ossl_print_errors(); + sc_log_openssl(g_ctx); return xp; } @@ -2429,8 +2539,10 @@ do_read_certificate(const char *name, const char *format, X509 **out) format); } - if (!*out) + if (!*out) { + sc_log_openssl(g_ctx); util_fatal("Unable to read certificate from %s\n", name); + } return 0; } @@ -2438,11 +2550,16 @@ static size_t determine_filesize(const char *filename) { FILE *fp; long ll; + int rc; - if ((fp = fopen(filename,"rb")) == NULL) + fp = fopen(filename,"rb"); + if (fp == NULL) util_fatal("Unable to open %s: %m", filename); - fseek(fp,0L,SEEK_END); + rc = fseek(fp, 0L, SEEK_END); + if (rc != 0) + util_fatal("Unable seek in the opened file %s", filename); + ll = ftell(fp); if (ll == -1l) util_fatal("fseek/ftell error"); @@ -2456,7 +2573,7 @@ do_read_data_object(const char *name, u8 **out, size_t *outlen, size_t expected) { FILE *inf; size_t filesize = expected ? expected : determine_filesize(name); - int c; + ssize_t sz; *out = malloc(filesize); if (*out == NULL) @@ -2466,43 +2583,51 @@ do_read_data_object(const char *name, u8 **out, size_t *outlen, size_t expected) inf = fopen(name, "rb"); if (inf == NULL) { fprintf(stderr, "Unable to open '%s' for reading.\n", name); - return -1; + return SC_ERROR_FILE_NOT_FOUND; } - c = fread(*out, 1, filesize, inf); + sz = fread(*out, 1, filesize, inf); fclose(inf); - if (c < 0) { + if (sz < 0) { perror("read"); - return -1; + return SC_ERROR_FILE_NOT_FOUND; } *outlen = filesize; - return 0; + return SC_SUCCESS; } static char * cert_common_name(X509 *x509) { - X509_NAME_ENTRY *ne = NULL; - ASN1_STRING *a_str = NULL; + const X509_NAME_ENTRY *ne = NULL; + const ASN1_STRING *a_str = NULL; char *out = NULL; unsigned char *tmp = NULL; int idx, out_len = 0; idx = X509_NAME_get_index_by_NID(X509_get_subject_name(x509), NID_commonName, -1); - if (idx < 0) + if (idx < 0) { + sc_log_openssl(g_ctx); return NULL; + } ne = X509_NAME_get_entry(X509_get_subject_name(x509), idx); - if (!ne) - return NULL; + if (!ne) { + sc_log_openssl(g_ctx); + return NULL; + } a_str = X509_NAME_ENTRY_get_data(ne); - if (!a_str) + if (!a_str) { + sc_log_openssl(g_ctx); return NULL; + } out_len = ASN1_STRING_to_UTF8(&tmp, a_str); - if (!tmp) + if (!tmp) { + sc_log_openssl(g_ctx); return NULL; + } out = calloc(1, out_len + 1); if (out) @@ -2541,7 +2666,8 @@ parse_objects(const char *list, unsigned int action) }; while (1) { - int len, n; + int n; + size_t len; while (*list == ',') list++; @@ -2555,7 +2681,7 @@ parse_objects(const char *list, unsigned int action) printf("When \"data\" is specified, an --application-id must also be specified,\n"); printf(" in the other cases an \"--id\" must also be specified\n"); printf("When \"chain\" is specified, the certificate chain starting with the cert\n"); - printf(" with specified ID will be deleted, untill there's a CA cert that certifies\n"); + printf(" with specified ID will be deleted, until there's a CA cert that certifies\n"); printf(" another cert on the card\n"); } else { @@ -2573,7 +2699,7 @@ parse_objects(const char *list, unsigned int action) } } if (del_flags[n].name == NULL) { - fprintf(stderr, "Unknown argument for --delete_objects: %.*s\n", len, list); + fprintf(stderr, "Unknown argument for --delete-objects: %.*s\n", (int)len, list); exit(0); } list += len; @@ -2611,7 +2737,8 @@ parse_x509_usage(const char *list, unsigned int *res) }; while (1) { - int len, n, match = 0; + int n, match = 0; + size_t len; while (*list == ',') list++; @@ -2646,12 +2773,12 @@ parse_x509_usage(const char *list, unsigned int *res) } if (match == 0) { fprintf(stderr, - "Unknown X.509 key usage %.*s\n", len, list); + "Unknown X.509 key usage %.*s\n", (int)len, list); exit(1); } if (match > 1) { fprintf(stderr, - "Ambiguous X.509 key usage %.*s\n", len, list); + "Ambiguous X.509 key usage %.*s\n", (int)len, list); exit(1); } list += len; @@ -2740,9 +2867,6 @@ handle_option(const struct option *opt) case 'w': opt_wait = 1; break; - case OPT_OPTIONS: - read_options_file(optarg); - break; case OPT_PIN1: case OPT_PUK1: case OPT_PIN2: case OPT_PUK2: util_get_pin(optarg, &(opt_pins[opt->val & 3])); @@ -2793,6 +2917,7 @@ handle_option(const struct option *opt) break; case OPT_USE_PINPAD_DEPRECATED: fprintf(stderr, "'--no-prompt' is deprecated , use '--use-pinpad' instead.\n"); + /* fall through */ case OPT_USE_PINPAD: opt_use_pinpad = 1; break; @@ -2840,6 +2965,10 @@ handle_option(const struct option *opt) case OPT_VERSION: this_action = ACTION_PRINT_VERSION; break; + case OPT_USER_CONSENT: + if (optarg != NULL) + opt_user_consent = atoi(optarg); + break; default: util_print_usage_and_die(app_name, options, option_help, NULL); } @@ -2889,6 +3018,7 @@ parse_commandline(int argc, char **argv) switch (o->has_arg) { case optional_argument: *sp++ = ':'; + /* fall through */ case required_argument: *sp++ = ':'; case no_argument: @@ -2914,73 +3044,6 @@ next: ; } } -/* - * Read a file containing more command line options. - * This allows you to specify PINs to pkcs15-init without - * exposing them through ps. - */ -static void -read_options_file(const char *filename) -{ - const struct option *o; - char buffer[1024], *name; - FILE *fp; - - if ((fp = fopen(filename, "r")) == NULL) - util_fatal("Unable to open %s: %m", filename); - while (fgets(buffer, sizeof(buffer), fp) != NULL) { - buffer[strcspn(buffer, "\n")] = '\0'; - - name = strtok(buffer, " \t"); - while (name) { - if (*name == '#') - break; - for (o = options; o->name; o++) - if (!strcmp(o->name, name)) - break; - if (!o->name) { - util_error("Unknown option \"%s\"\n", name); - util_print_usage_and_die(app_name, options, option_help, NULL); - } - if (o->has_arg != no_argument) { - optarg = strtok(NULL, ""); - if (optarg) { - while (isspace((int) *optarg)) - optarg++; - optarg = strdup(optarg); - } - } - if (o->has_arg == required_argument - && (!optarg || !*optarg)) { - util_error("Option %s: missing argument\n", name); - util_print_usage_and_die(app_name, options, option_help, NULL); - } - handle_option(o); - name = strtok(NULL, " \t"); - } - } - fclose(fp); -} - - -/* - * OpenSSL helpers - */ -static void -ossl_print_errors(void) -{ - static int loaded = 0; - long err; - - if (!loaded) { - ERR_load_crypto_strings(); - loaded = 1; - } - - while ((err = ERR_get_error()) != 0) - fprintf(stderr, "%s\n", ERR_error_string(err, NULL)); -} - /* * Retrieve a PIN from the user. * @@ -3088,7 +3151,7 @@ int get_pin(sc_ui_hints_t *hints, char **out) static int verify_pin(struct sc_pkcs15_card *p15card, char *auth_id_str) { struct sc_pkcs15_object *pin_obj = NULL; - char pin_label[64]; + char pin_label[(sizeof pin_obj->label) + 20]; char *pin = NULL; int r; @@ -3133,35 +3196,47 @@ static int verify_pin(struct sc_pkcs15_card *p15card, char *auth_id_str) } if (opt_pins[0] != NULL) { - pin = (char *) opt_pins[0]; + pin = strdup(opt_pins[0]); } else { - sc_ui_hints_t hints; + sc_ui_hints_t hints; - if (opt_use_pinpad) + if (opt_use_pinpad) return SC_ERROR_OBJECT_NOT_FOUND; if (pin_obj->label[0]) - snprintf(pin_label, sizeof(pin_label), "User PIN [%s]", pin_obj->label); + snprintf(pin_label, sizeof(pin_label), "User PIN [%.*s]", + (int) sizeof pin_obj->label, pin_obj->label); else snprintf(pin_label, sizeof(pin_label), "User PIN"); - memset(&hints, 0, sizeof(hints)); - hints.dialog_name = "pkcs15init.get_pin"; - hints.prompt = "User PIN required"; - hints.obj_label = pin_label; - hints.usage = SC_UI_USAGE_OTHER; - hints.card = card; - hints.p15card = p15card; - - get_pin(&hints, &pin); + memset(&hints, 0, sizeof(hints)); + hints.dialog_name = "pkcs15init.get_pin"; + hints.prompt = "User PIN required"; + hints.obj_label = pin_label; + hints.usage = SC_UI_USAGE_OTHER; + hints.card = g_card; + hints.p15card = p15card; + + if ((r = get_pin(&hints, &pin)) < 0) { + if (pin) { + sc_mem_clear(pin, strlen(pin)); + free(pin); + } + fprintf(stderr, + "Failed to read PIN from user: %s\n", + sc_strerror(r)); + return r; + } } - r = sc_pkcs15_verify_pin(p15card, pin_obj, (unsigned char *)pin, pin ? strlen((char *) pin) : 0); + r = sc_pkcs15_verify_pin(p15card, pin_obj, (unsigned char *)pin, pin ? strlen(pin) : 0); if (r < 0) fprintf(stderr, "Operation failed: %s\n", sc_strerror(r)); - if (NULL == opt_pins[0]) + if (pin) { + sc_mem_clear(pin, strlen(pin)); free(pin); + } return r; } diff --git a/src/tools/pkcs15-tool.c b/src/tools/pkcs15-tool.c index 2c15cb6a17..01b368c8d3 100644 --- a/src/tools/pkcs15-tool.c +++ b/src/tools/pkcs15-tool.c @@ -16,7 +16,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -57,6 +57,7 @@ typedef unsigned __int32 uint32_t; #include "libopensc/pkcs15.h" #include "libopensc/asn1.h" #include "util.h" +#include "pkcs11/pkcs11-display.h" static const char *app_name = "pkcs15-tool"; @@ -107,6 +108,7 @@ enum { OPT_RAW, OPT_PRINT_VERSION, OPT_LIST_INFO, + OPT_READ_CERT, }; #define NELEMENTS(x) (sizeof(x)/sizeof((x)[0])) @@ -117,7 +119,7 @@ static const struct option options[] = { { "version", 0, NULL, OPT_PRINT_VERSION }, { "list-info", no_argument, NULL, OPT_LIST_INFO }, { "list-applications", no_argument, NULL, OPT_LIST_APPLICATIONS }, - { "read-certificate", required_argument, NULL, 'r' }, + { "read-certificate", required_argument, NULL, OPT_READ_CERT }, { "list-certificates", no_argument, NULL, 'c' }, { "read-data-object", required_argument, NULL, 'R' }, { "raw", no_argument, NULL, OPT_RAW }, @@ -191,7 +193,7 @@ static const char *option_help[] = { "The auth ID of the PIN to use", "Specify AID of the on-card PKCS#15 application to bind to (in hexadecimal form)", "Wait for card insertion", - "Verbose operation. Use several times to enable debug output.", + "Verbose operation, may be used several times", "Do not prompt the user; if no PINs supplied, pinpad will be used.", NULL, NULL @@ -219,7 +221,7 @@ struct _access_rule_text { {0, NULL}, }; -static const char *key_types[] = { "", "RSA", "DSA", "GOSTR3410", "EC", "", "", "" }; +static const char *key_types[] = { "", "RSA", "", "GOSTR3410", "EC", "EDDSA", "XEDDSA", "" }; static void print_access_rules(const struct sc_pkcs15_accessrule *rules, int num) @@ -253,7 +255,7 @@ static void print_common_flags(const struct sc_pkcs15_object *obj) { const char *common_flags[] = {"private", "modifiable"}; unsigned int i; - printf("\tObject Flags : [0x%X]", obj->flags); + printf("\tObject Flags : [0x%02X]", obj->flags); for (i = 0; i < NELEMENTS(common_flags); i++) { if (obj->flags & (1 << i)) { printf(", %s", common_flags[i]); @@ -267,6 +269,7 @@ static void print_cert_info(const struct sc_pkcs15_object *obj) struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) obj->data; struct sc_pkcs15_cert *cert_parsed = NULL; int rv; + int private_obj; if (compact) { printf("\tPath:%s ID:%s", sc_print_path(&cert_info->path), @@ -284,7 +287,8 @@ static void print_cert_info(const struct sc_pkcs15_object *obj) print_access_rules(obj->access_rules, SC_PKCS15_MAX_ACCESS_RULES); - rv = sc_pkcs15_read_certificate(p15card, cert_info, &cert_parsed); + private_obj = obj->flags & SC_PKCS15_CO_FLAG_PRIVATE; + rv = sc_pkcs15_read_certificate(p15card, cert_info, private_obj, &cert_parsed); if (rv >= 0 && cert_parsed) { printf("\tEncoded serial : %02X %02X ", *(cert_parsed->serial), *(cert_parsed->serial + 1)); util_hex_dump(stdout, cert_parsed->serial + 2, cert_parsed->serial_len - 2, ""); @@ -387,7 +391,7 @@ print_data_object(const char *kind, const u8*data, size_t data_len) if (opt_outfile != NULL) { FILE *outf; - outf = fopen(opt_outfile, "w"); + outf = fopen(opt_outfile, "wb"); if (outf == NULL) { fprintf(stderr, "Error opening file '%s': %s\n", opt_outfile, strerror(errno)); @@ -429,13 +433,15 @@ static int read_certificate(void) for (i = 0; i < count; i++) { struct sc_pkcs15_cert_info *cinfo = (struct sc_pkcs15_cert_info *) objs[i]->data; struct sc_pkcs15_cert *cert; + int private_obj; if (sc_pkcs15_compare_id(&id, &cinfo->id) != 1) continue; if (verbose) printf("Reading certificate with ID '%s'\n", opt_cert); - r = sc_pkcs15_read_certificate(p15card, cinfo, &cert); + private_obj = objs[i]->flags & SC_PKCS15_CO_FLAG_PRIVATE; + r = sc_pkcs15_read_certificate(p15card, cinfo, private_obj, &cert); if (r) { fprintf(stderr, "Certificate read failed: %s\n", sc_strerror(r)); return 1; @@ -464,6 +470,7 @@ static int read_data_object(void) for (i = 0; i < count; i++) { struct sc_pkcs15_data_info *cinfo = (struct sc_pkcs15_data_info *) objs[i]->data; struct sc_pkcs15_data *data_object = NULL; + int private_obj; if (!sc_format_oid(&oid, opt_data)) { if (!sc_compare_oid(&oid, &cinfo->app_oid)) @@ -478,7 +485,8 @@ static int read_data_object(void) printf("Reading data object with label '%s'\n", opt_data); r = authenticate(objs[i]); if (r >= 0) { - r = sc_pkcs15_read_data_object(p15card, cinfo, &data_object); + private_obj = objs[i]->flags & SC_PKCS15_CO_FLAG_PRIVATE; + r = sc_pkcs15_read_data_object(p15card, cinfo, private_obj, &data_object); if (r) { fprintf(stderr, "Data object read failed: %s\n", sc_strerror(r)); if (r == SC_ERROR_FILE_NOT_FOUND) @@ -525,7 +533,8 @@ static int list_data_objects(void) } if (objs[i]->auth_id.len == 0) { struct sc_pkcs15_data *data_object; - r = sc_pkcs15_read_data_object(p15card, cinfo, &data_object); + int private_obj = objs[i]->flags & SC_PKCS15_CO_FLAG_PRIVATE; + r = sc_pkcs15_read_data_object(p15card, cinfo, private_obj, &data_object); if (r) { fprintf(stderr, "Data object read failed: %s\n", sc_strerror(r)); if (r == SC_ERROR_FILE_NOT_FOUND) @@ -557,7 +566,8 @@ static int list_data_objects(void) printf("\tPath: %s\n", sc_print_path(&cinfo->path)); if (objs[i]->auth_id.len == 0) { struct sc_pkcs15_data *data_object; - r = sc_pkcs15_read_data_object(p15card, cinfo, &data_object); + int private_obj = objs[i]->flags & SC_PKCS15_CO_FLAG_PRIVATE; + r = sc_pkcs15_read_data_object(p15card, cinfo, private_obj, &data_object); if (r) { fprintf(stderr, "Data object read failed: %s\n", sc_strerror(r)); if (r == SC_ERROR_FILE_NOT_FOUND) @@ -570,11 +580,13 @@ static int list_data_objects(void) else { printf("\tAuth ID: %s\n", sc_pkcs15_print_id(&objs[i]->auth_id)); } + + printf("\n"); } return 0; } -static void print_key_usages(int usage) +static void print_key_usages(unsigned int usage) { size_t i; const char *usages[] = { @@ -604,6 +616,8 @@ static void print_prkey_info(const struct sc_pkcs15_object *obj) struct sc_pkcs15_prkey_info *prkey = (struct sc_pkcs15_prkey_info *) obj->data; unsigned char guid[40]; size_t guid_len; + int i; + int last_algo_refs = 0; if (compact) { printf("\t%-3s", key_types[7 & obj->type]); @@ -618,7 +632,7 @@ static void print_prkey_info(const struct sc_pkcs15_object *obj) printf(" Ref:0x%02X", prkey->key_reference); if (obj->auth_id.len != 0) printf(" AuthID:%s", sc_pkcs15_print_id(&obj->auth_id)); - printf("\n\t %-16.*s [0x%X", 16, obj->label, prkey->usage); + printf("\n\t %-18.*s [0x%02X", (int)sizeof obj->label, obj->label, prkey->usage); print_key_usages(prkey->usage); printf("]"); return; @@ -626,12 +640,22 @@ static void print_prkey_info(const struct sc_pkcs15_object *obj) printf("Private %s Key [%.*s]\n", key_types[7 & obj->type], (int) sizeof obj->label, obj->label); print_common_flags(obj); - printf("\tUsage : [0x%X]", prkey->usage); + printf("\tUsage : [0x%02X]", prkey->usage); print_key_usages(prkey->usage); printf("\n"); - printf("\tAccess Flags : [0x%X]", prkey->access_flags); + printf("\tAccess Flags : [0x%02X]", prkey->access_flags); print_key_access_flags(prkey->access_flags); printf("\n"); + printf("\tAlgo_refs : "); + /* zero may be valid and don't know how many were read print at least 1*/ + for (i = 0; i< SC_MAX_SUPPORTED_ALGORITHMS; i++) { + if (prkey->algo_refs[i] != 0) + last_algo_refs = i; + } + for (i = 0; i< last_algo_refs + 1; i++) { + printf("%s%u", (i == 0) ? "" : ", ", prkey->algo_refs[i]); + } + printf("\n"); print_access_rules(obj->access_rules, SC_PKCS15_MAX_ACCESS_RULES); @@ -639,7 +663,7 @@ static void print_prkey_info(const struct sc_pkcs15_object *obj) printf("\tModLength : %lu\n", (unsigned long)prkey->modulus_length); else printf("\tFieldLength : %lu\n", (unsigned long)prkey->field_length); - printf("\tKey ref : %d (0x%X)\n", prkey->key_reference, prkey->key_reference); + printf("\tKey ref : %d (0x%02X)\n", prkey->key_reference, prkey->key_reference); printf("\tNative : %s\n", prkey->native ? "yes" : "no"); if (prkey->path.len || prkey->path.aid.len) printf("\tPath : %s\n", sc_print_path(&prkey->path)); @@ -699,7 +723,7 @@ static void print_pubkey_info(const struct sc_pkcs15_object *obj) printf(" Ref:0x%02X", pubkey->key_reference); if (obj->auth_id.len != 0) printf(" AuthID:%s", sc_pkcs15_print_id(&obj->auth_id)); - printf(" %15.*s [0x%X", (int) sizeof obj->label, obj->label, pubkey->usage); + printf(" %-18.*s [0x%02X", (int)sizeof obj->label, obj->label, pubkey->usage); print_key_usages(pubkey->usage); printf("]"); return; @@ -707,11 +731,11 @@ static void print_pubkey_info(const struct sc_pkcs15_object *obj) printf("Public %s Key [%.*s]\n", key_types[7 & obj->type], (int) sizeof obj->label, obj->label); print_common_flags(obj); - printf("\tUsage : [0x%X]", pubkey->usage); + printf("\tUsage : [0x%02X]", pubkey->usage); print_key_usages(pubkey->usage); printf("\n"); - printf("\tAccess Flags : [0x%X]", pubkey->access_flags); + printf("\tAccess Flags : [0x%02X]", pubkey->access_flags); print_key_access_flags(pubkey->access_flags); printf("\n"); @@ -723,7 +747,8 @@ static void print_pubkey_info(const struct sc_pkcs15_object *obj) else if (pubkey->field_length) { printf("\tFieldLength : %lu\n", (unsigned long)pubkey->field_length); } - else if (obj->type == SC_PKCS15_TYPE_PUBKEY_EC && have_path) { + else if ((obj->type == SC_PKCS15_TYPE_PUBKEY_EC || obj->type == SC_PKCS15_TYPE_PUBKEY_EDDSA) + && have_path) { sc_pkcs15_pubkey_t *pkey = NULL; if (!sc_pkcs15_read_pubkey(p15card, obj, &pkey)) { printf("\tFieldLength : %lu\n", (unsigned long)pkey->u.ec.params.field_length); @@ -731,7 +756,7 @@ static void print_pubkey_info(const struct sc_pkcs15_object *obj) } } - printf("\tKey ref : %d (0x%X)\n", pubkey->key_reference, pubkey->key_reference); + printf("\tKey ref : %d (0x%02X)\n", pubkey->key_reference, pubkey->key_reference); printf("\tNative : %s\n", pubkey->native ? "yes" : "no"); if (have_path) printf("\tPath : %s\n", sc_print_path(&pubkey->path)); @@ -785,11 +810,13 @@ static int read_public_key(void) r = sc_pkcs15_read_pubkey(p15card, obj, &pubkey); } else if (r == SC_ERROR_OBJECT_NOT_FOUND) { /* No pubkey - try if there's a certificate */ + int private_obj; r = sc_pkcs15_find_cert_by_id(p15card, &id, &obj); if (r >= 0) { if (verbose) printf("Reading certificate with ID '%s'\n", opt_pubkey); - r = sc_pkcs15_read_certificate(p15card, (sc_pkcs15_cert_info_t *) obj->data, &cert); + private_obj = obj->flags & SC_PKCS15_CO_FLAG_PRIVATE; + r = sc_pkcs15_read_certificate(p15card, (sc_pkcs15_cert_info_t *) obj->data, private_obj, &cert); } if (r >= 0) pubkey = cert->key; @@ -823,7 +850,7 @@ static int read_public_key(void) out: if (cert) sc_pkcs15_free_certificate(cert); - else if (pubkey) + else sc_pkcs15_free_pubkey(pubkey); return r; @@ -838,11 +865,11 @@ static void print_skey_info(const struct sc_pkcs15_object *obj) printf("Secret %s Key [%.*s]\n", skey_types[7 & obj->type], (int) sizeof obj->label, obj->label); print_common_flags(obj); - printf("\tUsage : [0x%X]", skey->usage); + printf("\tUsage : [0x%02X]", skey->usage); print_key_usages(skey->usage); printf("\n"); - printf("\tAccess Flags : [0x%X]", skey->access_flags); + printf("\tAccess Flags : [0x%02X]", skey->access_flags); print_key_access_flags(skey->access_flags); printf("\n"); @@ -851,7 +878,7 @@ static void print_skey_info(const struct sc_pkcs15_object *obj) printf("\tSize : %lu bits\n", (unsigned long)skey->value_len); printf("\tID : %s\n", sc_pkcs15_print_id(&skey->id)); printf("\tNative : %s\n", skey->native ? "yes" : "no"); - printf("\tKey ref : %d (0x%X)\n", skey->key_reference, skey->key_reference); + printf("\tKey ref : %d (0x%02X)\n", skey->key_reference, skey->key_reference); if (skey->path.len || skey->path.aid.len) printf("\tPath : %s\n", sc_print_path(&skey->path)); @@ -886,7 +913,7 @@ static int list_skeys(void) #if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H)) -static void print_ssh_key(FILE *outf, const char * alg, struct sc_pkcs15_object *obj, unsigned char * buf, uint32_t len) { +static void print_ssh_key(FILE *outf, const char * alg, struct sc_pkcs15_object *obj, unsigned char * buf, size_t len) { unsigned char *uu; int r; @@ -918,9 +945,9 @@ static void print_ssh_key(FILE *outf, const char * alg, struct sc_pkcs15_object } if (obj->label[0] != '\0') - fprintf(outf,"ssh-%s %s %.*s\n", alg, uu, (int) sizeof obj->label, obj->label); + fprintf(outf,"%s %s %.*s\n", alg, uu, (int) sizeof obj->label, obj->label); else - fprintf(outf,"ssh-%s %s\n", alg, uu); + fprintf(outf,"%s %s\n", alg, uu); } free(uu); return; @@ -959,32 +986,137 @@ static int read_ssh_key(void) } else if (r == SC_ERROR_OBJECT_NOT_FOUND) { /* No pubkey - try if there's a certificate */ + int private_obj; r = sc_pkcs15_find_cert_by_id(p15card, &id, &obj); if (r >= 0) { if (verbose) fprintf(stderr,"Reading certificate with ID '%s'\n", opt_pubkey); - r = sc_pkcs15_read_certificate(p15card, (sc_pkcs15_cert_info_t *) obj->data, &cert); + private_obj = obj->flags & SC_PKCS15_CO_FLAG_PRIVATE; + r = sc_pkcs15_read_certificate(p15card, (sc_pkcs15_cert_info_t *) obj->data, private_obj, &cert); } if (r >= 0) pubkey = cert->key; } if (r == SC_ERROR_OBJECT_NOT_FOUND) { - if (outf != stdout) + if (opt_outfile != NULL) fclose(outf); fprintf(stderr, "Public key with ID '%s' not found.\n", opt_pubkey); return 2; } if (r < 0) { - if (outf != stdout) + if (opt_outfile != NULL) fclose(outf); fprintf(stderr, "Public key enumeration failed: %s\n", sc_strerror(r)); return 1; } + if (pubkey->algorithm == SC_ALGORITHM_EDDSA) { + // SSH supports only ed25519 key now + + const char alg[] = "ssh-ed25519"; + /* Large enough to fit the following: + * 2 x 4B item length headers + * max 11B algorithm name, 32B key data */ + unsigned char buf[64]; + size_t n, len; + + n = pubkey->u.ec.ecpointQ.len; + if (n != 32) { + fprintf(stderr, "Wrong public key length\n"); + goto fail2; + } + + len = strlen(alg); + buf[0] = 0; + buf[1] = 0; + buf[2] = 0; + buf[3] = len; + memcpy(buf+4, alg, len); + len += 4; + + buf[len++] = 0; + buf[len++] = 0; + buf[len++] = 0; + buf[len++] = n & 0xff; + memcpy(buf + len, pubkey->u.ec.ecpointQ.value, n); + len += n; + + print_ssh_key(outf, alg, obj, buf, len); + } + if (pubkey->algorithm == SC_ALGORITHM_EC) { + // support only for NIST + // 'ssh-keygen -t ecdsa' allow only field lengths 256/384/521 + + static struct supported_ec_curves { + char *curve_name; + struct sc_object_id curve_oid; + size_t size; + } ec_curves[] = { + {"secp256r1", {{1, 2, 840, 10045, 3, 1, 7, -1}},256}, + {"secp384r1", {{1, 3, 132, 0, 34, -1}}, 384}, + {"secp521r1", {{1, 3, 132, 0, 35, -1}}, 521}, + {NULL, {{-1}}, 0}, + }; + char alg[20]; + /* Large enough to fit the following: + * 3 x 4B item length headers + * max 20B algorithm name, 9B curve name, max 256B key data */ + unsigned char buf[300]; + unsigned int i, tmp; + size_t n; + int len; + + for (n = 0,i = 0; ec_curves[i].curve_name != NULL; i++) { + if(sc_compare_oid (&ec_curves[i].curve_oid,&pubkey->u.ec.params.id)) + n = ec_curves[i].size; + } + if (!n) { + fprintf(stderr, "Unsupported curve\n"); + goto fail2; + } + if (n != pubkey->u.ec.params.field_length) { + fprintf(stderr, "Wrong field length\n"); + goto fail2; + } + + len = snprintf(alg, sizeof alg, "ecdsa-sha2-nistp%zu", n); + if (len < 0) { + fprintf(stderr, "failed to write algorithm\n"); + goto fail2; + } + buf[0] = 0; + buf[1] = 0; + buf[2] = 0; + buf[3] = len; + memcpy(buf+4, alg, len); + len += 4; + + buf[len++] = 0; + buf[len++] = 0; + buf[len++] = 0; + tmp = snprintf((char *) buf+len+1, 9, "nistp%zu", n); + buf[len++] = tmp; + len += tmp; + + n = pubkey->u.ec.ecpointQ.len; + if(n > 255) { + fprintf(stderr, "Wrong public key length\n"); + goto fail2; + } + buf[len++] = 0; + buf[len++] = 0; + buf[len++] = 0; + buf[len++] = n & 0xff; + memcpy(buf+len,pubkey->u.ec.ecpointQ.value,n); + len += n; + + print_ssh_key(outf, alg, obj, buf, len); + } + if (pubkey->algorithm == SC_ALGORITHM_RSA) { unsigned char buf[2048]; - uint32_t len, n; + size_t len, n; if (!pubkey->u.rsa.modulus.data || !pubkey->u.rsa.modulus.len || !pubkey->u.rsa.exponent.data || !pubkey->u.rsa.exponent.len) { @@ -1000,7 +1132,7 @@ static int read_ssh_key(void) len = sprintf((char *) buf+4,"ssh-rsa"); len+=4; - if (sizeof(buf)-len < 4+pubkey->u.rsa.exponent.len) + if (sizeof(buf)-len < 5+pubkey->u.rsa.exponent.len) goto fail; n = pubkey->u.rsa.exponent.len; @@ -1034,115 +1166,24 @@ static int read_ssh_key(void) memcpy(buf+len,pubkey->u.rsa.modulus.data, pubkey->u.rsa.modulus.len); len += pubkey->u.rsa.modulus.len; - print_ssh_key(outf, "rsa", obj, buf, len); - } - - if (pubkey->algorithm == SC_ALGORITHM_DSA) { - unsigned char buf[2048]; - uint32_t len; - uint32_t n; - - if (!pubkey->u.dsa.p.data || !pubkey->u.dsa.p.len || - !pubkey->u.dsa.q.data || !pubkey->u.dsa.q.len || - !pubkey->u.dsa.g.data || !pubkey->u.dsa.g.len || - !pubkey->u.dsa.pub.data || !pubkey->u.dsa.pub.len) { - fprintf(stderr, "Failed to decode DSA key.\n"); - goto fail2; - } - - buf[0]=0; - buf[1]=0; - buf[2]=0; - buf[3]=7; - - len = sprintf((char *) buf+4,"ssh-dss"); - len+=4; - - if (sizeof(buf)-len < 5+pubkey->u.dsa.p.len) - goto fail; - - n = pubkey->u.dsa.p.len; - if (pubkey->u.dsa.p.data[0] & 0x80) - n++; - - buf[len++]=(n >>24) & 0xff; - buf[len++]=(n >>16) & 0xff; - buf[len++]=(n >>8) & 0xff; - buf[len++]=(n) & 0xff; - if (pubkey->u.dsa.p.data[0] & 0x80) - buf[len++]= 0; - - memcpy(buf+len,pubkey->u.dsa.p.data, pubkey->u.dsa.p.len); - len += pubkey->u.dsa.p.len; - - if (sizeof(buf)-len < 5+pubkey->u.dsa.q.len) - goto fail; - - n = pubkey->u.dsa.q.len; - if (pubkey->u.dsa.q.data[0] & 0x80) - n++; - - buf[len++]=(n >>24) & 0xff; - buf[len++]=(n >>16) & 0xff; - buf[len++]=(n >>8) & 0xff; - buf[len++]=(n) & 0xff; - if (pubkey->u.dsa.q.data[0] & 0x80) - buf[len++]= 0; - - memcpy(buf+len,pubkey->u.dsa.q.data, pubkey->u.dsa.q.len); - len += pubkey->u.dsa.q.len; - - if (sizeof(buf)-len < 5+pubkey->u.dsa.g.len) - goto fail; - n = pubkey->u.dsa.g.len; - if (pubkey->u.dsa.g.data[0] & 0x80) - n++; - - buf[len++]=(n >>24) & 0xff; - buf[len++]=(n >>16) & 0xff; - buf[len++]=(n >>8) & 0xff; - buf[len++]=(n) & 0xff; - if (pubkey->u.dsa.g.data[0] & 0x80) - buf[len++]= 0; - - memcpy(buf+len,pubkey->u.dsa.g.data, pubkey->u.dsa.g.len); - len += pubkey->u.dsa.g.len; - - if (sizeof(buf)-len < 5+pubkey->u.dsa.pub.len) - goto fail; - - n = pubkey->u.dsa.pub.len; - if (pubkey->u.dsa.pub.data[0] & 0x80) - n++; - - buf[len++]=(n >>24) & 0xff; - buf[len++]=(n >>16) & 0xff; - buf[len++]=(n >>8) & 0xff; - buf[len++]=(n) & 0xff; - if (pubkey->u.dsa.pub.data[0] & 0x80) - buf[len++]= 0; - - memcpy(buf+len,pubkey->u.dsa.pub.data, pubkey->u.dsa.pub.len); - len += pubkey->u.dsa.pub.len; - - print_ssh_key(outf, "dss", obj, buf, len); + print_ssh_key(outf, "ssh-rsa", obj, buf, len); } - if (outf != stdout) + if (opt_outfile != NULL) fclose(outf); if (cert) sc_pkcs15_free_certificate(cert); - else if (pubkey) + else sc_pkcs15_free_pubkey(pubkey); return 0; fail: printf("can't convert key: buffer too small\n"); fail2: - if (outf && outf != stdout) + if (opt_outfile != NULL && outf != NULL) fclose(outf); if (cert) sc_pkcs15_free_certificate(cert); - else if (pubkey) + else sc_pkcs15_free_pubkey(pubkey); return SC_ERROR_OUT_OF_MEMORY; } @@ -1348,7 +1389,7 @@ static int test_session_pin(void) unsigned int auth_method; unsigned char *pin; int r; - unsigned char sessionpin[SC_MAX_PIN_SIZE]; + unsigned char sessionpin[SC_MAX_PIN_SIZE] = {0}; size_t sessionpinlen = sizeof sessionpin; if (!opt_auth_id) { @@ -1467,7 +1508,8 @@ static void print_pin_info(const struct sc_pkcs15_object *obj) const size_t pf_count = NELEMENTS(pin_flags); size_t i; - assert(obj->type == SC_PKCS15_TYPE_AUTH_PIN || obj->type == SC_PKCS15_TYPE_AUTH_AUTHKEY); + if (obj->type != SC_PKCS15_TYPE_AUTH_PIN && obj->type != SC_PKCS15_TYPE_AUTH_AUTHKEY) + return; if (compact) { printf("\t%-3s ID:%s", obj->type == SC_PKCS15_TYPE_AUTH_PIN ? "PIN" : "Key", @@ -1546,6 +1588,7 @@ static int list_pins(void) printf("Card has %d Authentication object(s).\n\n", r); for (i = 0; i < r; i++) { + sc_pkcs15_get_pin_info(p15card, objs[i]); print_pin_info(objs[i]); printf("\n"); } @@ -1578,6 +1621,21 @@ static int list_apps(FILE *fout) return 0; } + +static void print_supported_algo_info_operations(unsigned int operation) + +{ + size_t i; + const char *operations[] = { + "compute_checksum", "compute_signature", "verify_checksum", "verify_signature", + "encipher", "decipher", "hash", "generate/derive_key" + }; + const size_t operations_count = NELEMENTS(operations); + for (i = 0; i < operations_count; i++) + if (operation & (1 << i)) + printf(", %s", operations[i]); +} + static void list_info(void) { const char *flags[] = { @@ -1588,6 +1646,7 @@ static void list_info(void) }; char *last_update = sc_pkcs15_get_lastupdate(p15card); int i, count = 0; + int idx; printf("PKCS#15 Card [%s]:\n", p15card->tokeninfo->label); printf("\tVersion : %d\n", p15card->tokeninfo->version); @@ -1609,6 +1668,34 @@ static void list_info(void) } } printf("\n"); + for (i = 0; i < SC_MAX_SUPPORTED_ALGORITHMS; i++) { + struct sc_supported_algo_info * sa = &p15card->tokeninfo->supported_algos[i]; + + if (sa->reference == 0 && sa->mechanism == 0 + && sa->operations == 0 && sa->algo_ref == 0) + break; + printf("\t\t sc_supported_algo_info[%d]:\n", i); + printf("\t\t\t reference : %u (0x%02x)\n", sa->reference, sa->reference); + printf("\t\t\t mechanism : [0x%02x] %s\n", sa->mechanism, lookup_enum(MEC_T, sa->mechanism)); + if (sc_valid_oid(&sa->parameters)) { + printf("\t\t\t parameters: %i", sa->parameters.value[0]); + for (idx = 1; idx < SC_MAX_OBJECT_ID_OCTETS && sa->parameters.value[idx] != -1 ; idx++) + printf(".%i", sa->parameters.value[idx]); + printf("\n"); + } + printf("\t\t\t operations : [0x%2.2x]",sa->operations); + print_supported_algo_info_operations(sa->operations); + printf("\n"); + if (sc_valid_oid((const struct sc_object_id*)&sa->algo_id)) { + printf("\t\t\t algo_id : %i", sa->algo_id.value[0]); + for (idx = 1; idx < SC_MAX_OBJECT_ID_OCTETS && sa->algo_id.value[idx] != -1 ; idx++) + printf(".%i", sa->algo_id.value[idx]); + printf("\n"); + } + printf("\t\t\t algo_ref : [0x%02x]\n",sa->algo_ref); + } + + printf((compact) ? "\n" : "\n\n"); } static int dump(void) @@ -1647,8 +1734,10 @@ static int unblock_pin(void) if (pin_obj->auth_id.len) { r = sc_pkcs15_find_pin_by_auth_id(p15card, &pin_obj->auth_id, &puk_obj); - if (r) + if (r < 0) { + fprintf(stderr, "Failed to find PUK object for PIN: %s\n", sc_strerror(r)); return 2; + } } if (puk_obj) { @@ -1759,7 +1848,8 @@ static int change_pin(void) if (pincode && strlen((char *) pincode) == 0) { fprintf(stderr, "No PIN code supplied.\n"); - free(pincode); + if (opt_pin == NULL) + free(pincode); return 2; } @@ -1779,7 +1869,8 @@ static int change_pin(void) if (newpin == NULL || strlen((char *) newpin) == 0) { fprintf(stderr, "No new PIN value supplied.\n"); free(newpin); - free(pincode); + if (opt_pin == NULL) + free(pincode); return 2; } @@ -1884,8 +1975,11 @@ static int test_update(sc_card_t *in_card) } /* other tag */ i += 2 + rbuf[2+i+1]; /* length of this tag*/ + if (2+i+1 >= apdu.resplen) { + break; + } } - if (rbuf[2+i+1] < 9 || 2+i+2+9 > apdu.resplen) { + if (2+i+1 >= apdu.resplen || rbuf[2+i+1] < 9 || 2+i+2+9 > apdu.resplen) { printf("select file returned short fci\n"); goto bad_fci; } @@ -2031,7 +2125,7 @@ static int update(sc_card_t *in_card) return 0; } -int main(int argc, char * const argv[]) +int main(int argc, char *argv[]) { int err = 0, r, c, long_optind = 0; int do_read_cert = 0; @@ -2059,17 +2153,67 @@ int main(int argc, char * const argv[]) int action_count = 0; sc_context_param_t ctx_param; - assert(sizeof(option_help)/sizeof(char *)==sizeof(options)/sizeof(struct option)); - - c = OPT_PUK; + static_assert(sizeof(option_help) / sizeof(char *) == sizeof(options) / sizeof(struct option), + "internal error"); while (1) { c = getopt_long(argc, argv, "r:cuko:sva:LR:CwDTU", options, &long_optind); if (c == -1) break; - if (c == '?') - util_print_usage_and_die(app_name, options, option_help, NULL); + if (c == '?') { + util_print_usage(app_name, options, option_help, NULL); + return 2; + } switch (c) { + case 'r': + +#if OPENSC_MAJOR == 0 && OPENSC_VERSION_MINOR == 19 + fprintf(stderr, "\nWarning, option -r is reserved to specify card reader in future versions\n"); + fprintf (stderr, "Using -r option for read-certificate operation\n\n"); + opt_cert = optarg; + do_read_cert = 1; + action_count++; + break; +#elif OPENSC_MAJOR == 0 && OPENSC_VERSION_MINOR == 20 + + memset(&ctx_param, 0, sizeof(ctx_param)); + ctx_param.ver = 0; + ctx_param.app_name = app_name; + if (verbose) + ctx_param.debug_file = stderr; + + if (SC_SUCCESS == sc_context_create(&ctx, &ctx_param)) { + /* attempt to connect reader, on error, -r is used for read-certificate operation */ + struct sc_reader *reader = NULL; + + err = util_connect_reader(ctx, &reader, optarg, 0, 0); + sc_release_context(ctx); + ctx = NULL; + + if (err != SC_SUCCESS ) { +#if 1 + fprintf (stderr, + "Error, option -r is reserved to specify card reader, no reader \"%s\" found\n", optarg); + exit (1); +#else + fprintf (stderr, + "\nWarning, option -r is reserved to specify card reader, no reader \"%s\" found\n", optarg); + fprintf (stderr, "Using -r option for read-certificate operation\n\n"); + opt_cert = optarg; + do_read_cert = 1; + action_count++; + break; +#endif + } + } + opt_reader = optarg; + break; +#elif (OPENSC_MAJOR > 0) || (OPENSC_MAJOR == 0 && OPENSC_VERSION_MINOR > 20) + + opt_reader = optarg; + break; +#endif + case OPT_PRINT_VERSION: do_print_version = 1; action_count++; @@ -2078,7 +2222,7 @@ int main(int argc, char * const argv[]) do_list_info = 1; action_count++; break; - case 'r': + case OPT_READ_CERT: opt_cert = optarg; do_read_cert = 1; action_count++; @@ -2207,8 +2351,10 @@ int main(int argc, char * const argv[]) break; } } - if (action_count == 0) - util_print_usage_and_die(app_name, options, option_help, NULL); + if (action_count == 0) { + util_print_usage(app_name, options, option_help, NULL); + return 2; + } if (do_print_version) { printf("%s\n", OPENSC_SCM_REVISION); @@ -2218,6 +2364,9 @@ int main(int argc, char * const argv[]) memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.ver = 0; ctx_param.app_name = app_name; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; r = sc_context_create(&ctx, &ctx_param); if (r) { @@ -2231,12 +2380,7 @@ int main(int argc, char * const argv[]) action_count--; } - if (verbose > 1) { - ctx->debug = verbose; - sc_ctx_log_to_file(ctx, "stderr"); - } - - err = util_connect_card_ex(ctx, &card, opt_reader, opt_wait, 0, verbose); + err = util_connect_card_ex(ctx, &card, opt_reader, opt_wait, 0); if (err) goto end; @@ -2249,7 +2393,8 @@ int main(int argc, char * const argv[]) aid.len = sizeof(aid.value); if (sc_hex_to_bin(opt_bind_to_aid, aid.value, &aid.len)) { fprintf(stderr, "Invalid AID value: '%s'\n", opt_bind_to_aid); - return 1; + err = 1; + goto end; } r = sc_pkcs15_bind(card, &aid, &p15card); @@ -2368,11 +2513,8 @@ int main(int argc, char * const argv[]) action_count--; } end: - if (p15card) - sc_pkcs15_unbind(p15card); - if (card) - sc_disconnect_card(card); - if (ctx) - sc_release_context(ctx); + sc_pkcs15_unbind(p15card); + sc_disconnect_card(card); + sc_release_context(ctx); return err; } diff --git a/src/tools/sc-hsm-tool.c b/src/tools/sc-hsm-tool.c index 718a8f1190..2b410fd4de 100644 --- a/src/tools/sc-hsm-tool.c +++ b/src/tools/sc-hsm-tool.c @@ -3,6 +3,7 @@ * * Copyright (C) 2001 Juha Yrjölä * Copyright (C) 2012 www.CardContact.de, Andreas Schwier, Minden, Germany + * Copyright (C) 2018-2019 GSMK - Gesellschaft für Sichere Mobile Kommunikation mbH * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -16,7 +17,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -41,10 +42,12 @@ #include #include +#include "fread_to_eof.h" #include "libopensc/sc-ossl-compat.h" #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/asn1.h" +#include "libopensc/log.h" #include "libopensc/card-sc-hsm.h" #include "util.h" @@ -60,10 +63,7 @@ static char *opt_label = NULL; static int verbose = 0; // Some reasonable maximums -#define MAX_CERT 4096 -#define MAX_PRKD 256 -#define MAX_KEY 1024 -#define MAX_WRAPPED_KEY (MAX_CERT + MAX_PRKD + MAX_KEY) +#define MAX_KEY 1500 #define SEED_LENGTH 16 @@ -71,11 +71,14 @@ enum { OPT_SO_PIN = 0x100, OPT_PIN, OPT_RETRY, + OPT_BIO1, + OPT_BIO2, OPT_PASSWORD, OPT_PASSWORD_SHARES_THRESHOLD, OPT_PASSWORD_SHARES_TOTAL }; +// clang-format off static const struct option options[] = { { "initialize", 0, NULL, 'X' }, { "create-dkek-share", 1, NULL, 'C' }, @@ -85,10 +88,17 @@ static const struct option options[] = { #endif { "wrap-key", 1, NULL, 'W' }, { "unwrap-key", 1, NULL, 'U' }, + { "public-key-auth", 1, NULL, 'K' }, + { "required-pub-keys", 1, NULL, 'n' }, + { "export-for-pub-key-auth",1, NULL, 'e' }, + { "register-public-key", 1, NULL, 'g' }, + { "public-key-auth-status", 0, NULL, 'S' }, { "dkek-shares", 1, NULL, 's' }, { "so-pin", 1, NULL, OPT_SO_PIN }, { "pin", 1, NULL, OPT_PIN }, { "pin-retry", 1, NULL, OPT_RETRY }, + { "bio-server1", 1, NULL, OPT_BIO1 }, + { "bio-server2", 1, NULL, OPT_BIO2 }, { "password", 1, NULL, OPT_PASSWORD }, { "pwd-shares-threshold", 1, NULL, OPT_PASSWORD_SHARES_THRESHOLD }, { "pwd-shares-total", 1, NULL, OPT_PASSWORD_SHARES_TOTAL }, @@ -100,6 +110,7 @@ static const struct option options[] = { { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; +// clang-format on static const char *option_help[] = { "Initialize token", @@ -110,19 +121,26 @@ static const char *option_help[] = { #endif "Wrap key and save to ", "Unwrap key read from ", + "Use public key authentication, set total number of public keys", + "Number of public keys required for authentication [1]", + "Export key for public key authentication", + "Register public key for public key authentication (PKA file)", + "Show status of public key authentication", "Number of DKEK shares [No DKEK]", "Define security officer PIN (SO-PIN)", "Define user PIN", "Define user PIN retry counter", + "AID of biometric server for template 1 (hex)", + "AID of biometric server for template 2 (hex)", "Define password for DKEK share", "Define threshold for number of password shares required for reconstruction", "Define number of password shares", - "Key reference for key wrap/unwrap", + "Key reference for key wrap/unwrap/export", "Token label for --initialize", "Force replacement of key and certificate", "Uses reader number [0]", "Wait for a card to be inserted", - "Verbose operation. Use several times to enable debug output.", + "Verbose operation, may be used several times", }; typedef struct { @@ -130,9 +148,6 @@ typedef struct { BIGNUM * y; } secret_share_t; -static sc_context_t *ctx = NULL; -static sc_card_t *card = NULL; - @@ -160,12 +175,7 @@ static int generatePrime(BIGNUM *prime, const BIGNUM *s, const int bits, unsigne do { // Generate random prime -#if OPENSSL_VERSION_NUMBER >= 0x00908000L /* last parm is BN_GENCB which is null in our case */ BN_generate_prime_ex(prime, bits, 1, NULL, NULL, NULL); -#else - BN_generate_prime(prime, bits, 1, NULL, NULL, NULL, NULL ); -#endif - } while ((BN_ucmp(prime, s) == -1) && (max_rounds-- > 0)); // If prime < s or not reached 1000 tries if (max_rounds > 0) @@ -193,10 +203,10 @@ static void calculatePolynomialValue(const BIGNUM *x, BIGNUM **polynomial, const BIGNUM *exponent; unsigned long exp; - BN_CTX *ctx; + BN_CTX *bn_ctx; // Create context for temporary variables of OpenSSL engine - ctx = BN_CTX_new(); + bn_ctx = BN_CTX_new(); temp = BN_new(); exponent = BN_new(); @@ -216,19 +226,19 @@ static void calculatePolynomialValue(const BIGNUM *x, BIGNUM **polynomial, const BN_set_word(exponent, exp); // temp = x^exponent mod prime - BN_mod_exp(temp, x, exponent, prime, ctx); + BN_mod_exp(temp, x, exponent, prime, bn_ctx); // exponent = temp * a = a * x^exponent mod prime - BN_mod_mul(exponent, temp, *pp, prime, ctx); + BN_mod_mul(exponent, temp, *pp, prime, bn_ctx); // add the temp value from exponent to y BN_copy(temp, y); - BN_mod_add(y, temp, exponent, prime, ctx); + BN_mod_add(y, temp, exponent, prime, bn_ctx); pp++; } BN_clear_free(temp); BN_clear_free(exponent); - BN_CTX_free(ctx); + BN_CTX_free(bn_ctx); } @@ -296,7 +306,7 @@ static int createShares(const BIGNUM *s, const unsigned char t, const unsigned c * @param shares Shares used to reconstruct secret (should contain t entries) * @param t Threshold used to reconstruct the secret * @param prime Prime for finite field arithmetic - * @param s Pointer for storage of calculated secred + * @param s Pointer for storage of calculated secret */ static int reconstructSecret(secret_share_t *shares, unsigned char t, const BIGNUM *prime, BIGNUM *s) { @@ -470,7 +480,7 @@ static void print_dkek_info(sc_cardctl_sc_hsm_dkek_t *dkekinfo) static void print_info(sc_card_t *card, sc_file_t *file) { - int r, tries_left; + int r; struct sc_pin_cmd_data data; sc_cardctl_sc_hsm_dkek_t dkekinfo; @@ -498,14 +508,14 @@ static void print_info(sc_card_t *card, sc_file_t *file) data.pin_type = SC_AC_CHV; data.pin_reference = ID_SO_PIN; - r = sc_pin_cmd(card, &data, &tries_left); + r = sc_pin_cmd(card, &data); if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) { printf("SmartCard-HSM has never been initialized. Please use --initialize to set SO-PIN and user PIN.\n"); } else { - if (tries_left == 0) { + if (data.pin1.tries_left == 0) { printf("SO-PIN locked\n"); } else { - printf("SO-PIN tries left : %d\n", tries_left); + printf("SO-PIN tries left : %d\n", data.pin1.tries_left); } /* Try to update PIN info from card */ memset(&data, 0, sizeof(data)); @@ -513,16 +523,16 @@ static void print_info(sc_card_t *card, sc_file_t *file) data.pin_type = SC_AC_CHV; data.pin_reference = ID_USER_PIN; - r = sc_pin_cmd(card, &data, &tries_left); + r = sc_pin_cmd(card, &data); if (r == SC_ERROR_CARD_CMD_FAILED) { printf("Public key authentication active.\n"); } else if (r == SC_ERROR_REF_DATA_NOT_USABLE) { printf("Transport-PIN active. Please change to user selected PIN first.\n"); } else { - if (tries_left == 0) { + if (data.pin1.tries_left == 0) { printf("User PIN locked\n"); } else { - printf("User PIN tries left : %d\n", tries_left); + printf("User PIN tries left : %d\n", data.pin1.tries_left); } } } @@ -533,15 +543,15 @@ static void print_info(sc_card_t *card, sc_file_t *file) data.pin_type = SC_AC_CHV; data.pin_reference = ID_USER_PIN; - r = sc_pin_cmd(card, &data, &tries_left); + r = sc_pin_cmd(card, &data); if (r == SC_ERROR_REF_DATA_NOT_USABLE) { printf("SmartCard-HSM has never been initialized. Please use --initialize to set SO-PIN and user PIN.\n"); } else { - if (tries_left == 0) { + if (data.pin1.tries_left == 0) { printf("User PIN locked\n"); } else { - printf("User PIN tries left : %d\n", tries_left); + printf("User PIN tries left : %d\n", data.pin1.tries_left); } } } @@ -562,13 +572,26 @@ static void print_info(sc_card_t *card, sc_file_t *file) -static int initialize(sc_card_t *card, const char *so_pin, const char *user_pin, int retry_counter, int dkek_shares, const char *label) +static int initialize(sc_card_t *card, const char *so_pin, const char *user_pin, int retry_counter, const char *bio1, const char *bio2, int dkek_shares, signed char num_of_pub_keys, u8 required_pub_keys, const char *label) { sc_cardctl_sc_hsm_init_param_t param; size_t len; char *_so_pin = NULL, *_user_pin = NULL; int r; + if (num_of_pub_keys != -1 && (num_of_pub_keys < 1 || num_of_pub_keys > 90)) { + fprintf(stderr, "Total number of public keys for authentication must be between 1 and 90\n"); + return -1; + } + if (required_pub_keys < 1 || required_pub_keys > 90) { + fprintf(stderr, "Number of public keys required for authentication must be between 1 and 90\n"); + return -1; + } + if (num_of_pub_keys != -1 && required_pub_keys > num_of_pub_keys) { + fprintf(stderr, "Required public keys must be <= total number of public keys\n"); + return -1; + } + if (so_pin == NULL) { printf("Enter SO-PIN (16 hexadecimal characters) : "); util_getpass(&_so_pin, NULL, stdin); @@ -628,10 +651,36 @@ static int initialize(sc_card_t *card, const char *so_pin, const char *user_pin, param.user_pin_retry_counter = (u8)retry_counter; + if (bio1) { + param.bio1.len = sizeof(param.bio1.value); + r = sc_hex_to_bin(bio1, param.bio1.value, ¶m.bio1.len); + if (r < 0) { + fprintf(stderr, "Error decoding AID of biometric server for template 1 (%s)\n", sc_strerror(r)); + return -1; + } + } else { + param.bio1.len = 0; + } + if (bio2) { + param.bio2.len = sizeof(param.bio2.value); + r = sc_hex_to_bin(bio2, param.bio2.value, ¶m.bio2.len); + if (r < 0) { + fprintf(stderr, "Error decoding AID of biometric server for template 2 (%s)\n", sc_strerror(r)); + return -1; + } + } else { + param.bio2.len = 0; + } + param.options[0] = 0x00; - param.options[1] = 0x01; + param.options[1] = 0x01; /* RESET RETRY COUNTER enabled */ + if (param.bio1.len || param.bio2.len) { + param.options[1] |= 0x04; /* Session-PIN enabled with clear on reset */ + } param.dkek_shares = (char)dkek_shares; + param.num_of_pub_keys = (signed char)num_of_pub_keys; /* guaranteed in [-1,90] */ + param.required_pub_keys = (u8)required_pub_keys; /* guaranteed in [1,90] */ param.label = (char *)label; r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_INITIALIZE, (void *)¶m); @@ -685,7 +734,7 @@ static int recreate_password_from_shares(char **pwd, int *pwdlen, int num_of_pas } binlen = 64; sc_hex_to_bin(inbuf, bin, &binlen); - BN_bin2bn(bin, binlen, prime); + BN_bin2bn(bin, (int)binlen, prime); sp = shares; for (i = 0; i < num_of_password_shares; i++) { @@ -720,7 +769,7 @@ static int recreate_password_from_shares(char **pwd, int *pwdlen, int num_of_pas } binlen = 64; sc_hex_to_bin(inbuf, bin, &binlen); - BN_bin2bn(bin, binlen, (sp->y)); + BN_bin2bn(bin, (int)binlen, (sp->y)); sp++; } @@ -728,7 +777,6 @@ static int recreate_password_from_shares(char **pwd, int *pwdlen, int num_of_pas clearScreen(); r = reconstructSecret(shares, num_of_password_shares, prime, secret); - if (r < 0) { printf("\nError during reconstruction of secret. Wrong shares?\n"); cleanUpShares(shares, num_of_password_shares); @@ -758,7 +806,7 @@ static int recreate_password_from_shares(char **pwd, int *pwdlen, int num_of_pas static int import_dkek_share(sc_card_t *card, const char *inf, int iter, const char *password, int num_of_password_shares) { sc_cardctl_sc_hsm_dkek_t dkekinfo; - EVP_CIPHER_CTX *ctx = NULL; + EVP_CIPHER_CTX *bn_ctx = NULL; FILE *in = NULL; u8 filebuff[64],key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH],outbuff[64]; char *pwd = NULL; @@ -794,10 +842,11 @@ static int import_dkek_share(sc_card_t *card, const char *inf, int iter, const c if (num_of_password_shares == -1) { printf("Enter password to decrypt DKEK share : "); util_getpass(&pwd, NULL, stdin); - pwdlen = strlen(pwd); + pwdlen = (int)strlen(pwd); printf("\n"); } else { r = recreate_password_from_shares(&pwd, &pwdlen, num_of_password_shares); + sc_log_openssl(card->ctx); if (r < 0) { return -1; } @@ -805,7 +854,7 @@ static int import_dkek_share(sc_card_t *card, const char *inf, int iter, const c } else { pwd = (char *) password; - pwdlen = strlen(password); + pwdlen = (int)strlen(password); } printf("Deciphering DKEK share, please wait...\n"); @@ -816,17 +865,17 @@ static int import_dkek_share(sc_card_t *card, const char *inf, int iter, const c free(pwd); } - ctx = EVP_CIPHER_CTX_new(); - EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); - if (!EVP_DecryptUpdate(ctx, outbuff, &outlen, filebuff + 16, sizeof(filebuff) - 16)) { - fprintf(stderr, "Error decrypting DKEK share. Password correct ?\n"); - return -1; - } - - if (!EVP_DecryptFinal_ex(ctx, outbuff + outlen, &r)) { + bn_ctx = EVP_CIPHER_CTX_new(); + if (!bn_ctx || + !EVP_DecryptInit_ex(bn_ctx, EVP_aes_256_cbc(), NULL, key, iv) || + !EVP_DecryptUpdate(bn_ctx, outbuff, &outlen, filebuff + 16, sizeof(filebuff) - 16) || + !EVP_DecryptFinal_ex(bn_ctx, outbuff + outlen, &r)) { + sc_log_openssl(card->ctx); + EVP_CIPHER_CTX_free(bn_ctx); fprintf(stderr, "Error decrypting DKEK share. Password correct ?\n"); return -1; } + EVP_CIPHER_CTX_free(bn_ctx); memset(&dkekinfo, 0, sizeof(dkekinfo)); memcpy(dkekinfo.dkek_share, outbuff, sizeof(dkekinfo.dkek_share)); @@ -837,7 +886,6 @@ static int import_dkek_share(sc_card_t *card, const char *inf, int iter, const c r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, (void *)&dkekinfo); OPENSSL_cleanse(&dkekinfo.dkek_share, sizeof(dkekinfo.dkek_share)); - EVP_CIPHER_CTX_free(ctx); if (r == SC_ERROR_INS_NOT_SUPPORTED) { // Not supported or not initialized for key shares fprintf(stderr, "Not supported by card or card not initialized for key share usage\n"); @@ -855,10 +903,10 @@ static int import_dkek_share(sc_card_t *card, const char *inf, int iter, const c static int print_dkek_share(sc_card_t *card, const char *inf, int iter, const char *password, int num_of_password_shares) { - // hex output can be used in the SCSH shell with the + // hex output can be used in the SCSH shell with the // decrypt_keyblob.js file sc_cardctl_sc_hsm_dkek_t dkekinfo; - EVP_CIPHER_CTX *ctx = NULL; + EVP_CIPHER_CTX *bn_ctx = NULL; FILE *in = NULL; u8 filebuff[64],key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH],outbuff[64]; char *pwd = NULL; @@ -895,7 +943,7 @@ static int print_dkek_share(sc_card_t *card, const char *inf, int iter, const ch if (num_of_password_shares == -1) { printf("Enter password to decrypt DKEK share : "); util_getpass(&pwd, NULL, stdin); - pwdlen = strlen(pwd); + pwdlen = (int)strlen(pwd); printf("\n"); } else { r = recreate_password_from_shares(&pwd, &pwdlen, num_of_password_shares); @@ -906,7 +954,7 @@ static int print_dkek_share(sc_card_t *card, const char *inf, int iter, const ch } else { pwd = (char *) password; - pwdlen = strlen(password); + pwdlen = (int)strlen(password); } printf("Deciphering DKEK share, please wait...\n"); @@ -917,17 +965,17 @@ static int print_dkek_share(sc_card_t *card, const char *inf, int iter, const ch free(pwd); } - ctx = EVP_CIPHER_CTX_new(); - EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); - if (!EVP_DecryptUpdate(ctx, outbuff, &outlen, filebuff + 16, sizeof(filebuff) - 16)) { - fprintf(stderr, "Error decrypting DKEK share. Password correct ?\n"); - return -1; - } - - if (!EVP_DecryptFinal_ex(ctx, outbuff + outlen, &r)) { + bn_ctx = EVP_CIPHER_CTX_new(); + if (!bn_ctx || + !EVP_DecryptInit_ex(bn_ctx, EVP_aes_256_cbc(), NULL, key, iv) || + !EVP_DecryptUpdate(bn_ctx, outbuff, &outlen, filebuff + 16, sizeof(filebuff) - 16) || + !EVP_DecryptFinal_ex(bn_ctx, outbuff + outlen, &r)) { + sc_log_openssl(card->ctx); + EVP_CIPHER_CTX_free(bn_ctx); fprintf(stderr, "Error decrypting DKEK share. Password correct ?\n"); return -1; } + EVP_CIPHER_CTX_free(bn_ctx); memset(&dkekinfo, 0, sizeof(dkekinfo)); memcpy(dkekinfo.dkek_share, outbuff, sizeof(dkekinfo.dkek_share)); @@ -944,7 +992,6 @@ static int print_dkek_share(sc_card_t *card, const char *inf, int iter, const ch printf("\n\n"); OPENSSL_cleanse(&dkekinfo.dkek_share, sizeof(dkekinfo.dkek_share)); - EVP_CIPHER_CTX_free(ctx); if (r == SC_ERROR_INS_NOT_SUPPORTED) { // Not supported or not initialized for key shares fprintf(stderr, "Not supported by card or card not initialized for key share usage\n"); @@ -987,7 +1034,7 @@ static void ask_for_password(char **pwd, int *pwdlen) printf("Passwords do not match. Please retry.\n"); continue; } - *pwdlen = strlen(*pwd); + *pwdlen = (int)strlen(*pwd); break; } @@ -1071,6 +1118,8 @@ static int generate_pwd_shares(sc_card_t *card, char **pwd, int *pwdlen, int pas r = sc_get_challenge(card, rngseed, SEED_LENGTH); if (r < 0) { printf("Error generating random seed failed with %s", sc_strerror(r)); + BN_clear_free(prime); + BN_clear_free(secret); OPENSSL_cleanse(*pwd, *pwdlen); free(*pwd); return r; @@ -1078,6 +1127,9 @@ static int generate_pwd_shares(sc_card_t *card, char **pwd, int *pwdlen, int pas r = generatePrime(prime, secret, 64, rngseed, SEED_LENGTH); if (r < 0) { + sc_log_openssl(card->ctx); + BN_clear_free(prime); + BN_clear_free(secret); printf("Error generating valid prime number. Please try again."); OPENSSL_cleanse(*pwd, *pwdlen); free(*pwd); @@ -1087,7 +1139,16 @@ static int generate_pwd_shares(sc_card_t *card, char **pwd, int *pwdlen, int pas // Allocate data buffer for the generated shares shares = malloc(password_shares_total * sizeof(secret_share_t)); - createShares(secret, password_shares_threshold, password_shares_total, prime, shares); + if (!shares || 0 > createShares(secret, password_shares_threshold, password_shares_total, prime, shares)) { + sc_log_openssl(card->ctx); + printf("Error generating Shares. Please try again."); + BN_clear_free(prime); + BN_clear_free(secret); + OPENSSL_cleanse(*pwd, *pwdlen); + free(*pwd); + free(shares); + return -1; + } sp = shares; for (i = 0; i < password_shares_total; i++) { @@ -1129,7 +1190,7 @@ static int generate_pwd_shares(sc_card_t *card, char **pwd, int *pwdlen, int pas static int create_dkek_share(sc_card_t *card, const char *outf, int iter, const char *password, int password_shares_threshold, int password_shares_total) { - EVP_CIPHER_CTX *ctx = NULL; + EVP_CIPHER_CTX *c_ctx = NULL; FILE *out = NULL; u8 filebuff[64], key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH]; u8 dkek_share[32]; @@ -1150,7 +1211,7 @@ static int create_dkek_share(sc_card_t *card, const char *outf, int iter, const } else { pwd = (char *) password; - pwdlen = strlen(password); + pwdlen = (int)strlen(password); } if (r < 0) { @@ -1180,17 +1241,17 @@ static int create_dkek_share(sc_card_t *card, const char *outf, int iter, const return -1; } - ctx = EVP_CIPHER_CTX_new(); - EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); - if (!EVP_EncryptUpdate(ctx, filebuff + 16, &outlen, dkek_share, sizeof(dkek_share))) { - fprintf(stderr, "Error encrypting DKEK share\n"); - return -1; - } - - if (!EVP_EncryptFinal_ex(ctx, filebuff + 16 + outlen, &r)) { + c_ctx = EVP_CIPHER_CTX_new(); + if (!c_ctx || + !EVP_EncryptInit_ex(c_ctx, EVP_aes_256_cbc(), NULL, key, iv) || + !EVP_EncryptUpdate(c_ctx, filebuff + 16, &outlen, dkek_share, sizeof(dkek_share)) || + !EVP_EncryptFinal_ex(c_ctx, filebuff + 16 + outlen, &r)) { + sc_log_openssl(card->ctx); + EVP_CIPHER_CTX_free(c_ctx); fprintf(stderr, "Error encrypting DKEK share\n"); return -1; } + EVP_CIPHER_CTX_free(c_ctx); out = fopen(outf, "wb"); @@ -1207,8 +1268,7 @@ static int create_dkek_share(sc_card_t *card, const char *outf, int iter, const fclose(out); - OPENSSL_cleanse(filebuff, sizeof(filebuff)); - EVP_CIPHER_CTX_free(ctx); + OPENSSL_cleanse(dkek_share, sizeof(dkek_share)); printf("DKEK share created and saved to %s\n", outf); return 0; @@ -1222,7 +1282,8 @@ static size_t determineLength(const u8 *tlv, size_t buflen) unsigned int cla,tag; size_t len; - if (sc_asn1_read_tag(&ptr, buflen, &cla, &tag, &len) != SC_SUCCESS) { + if (sc_asn1_read_tag(&ptr, buflen, &cla, &tag, &len) != SC_SUCCESS + || ptr == NULL) { return 0; } @@ -1242,55 +1303,41 @@ static size_t determineLength(const u8 *tlv, size_t buflen) */ static int wrap_with_tag(u8 tag, u8 *indata, size_t inlen, u8 **outdata, size_t *outlen) { - int nlc = 0; - u8 *ptr; - - if (inlen > 127) { - do { - nlc++; - } while (inlen >= (unsigned)(1 << (nlc << 3))); - } + int r = sc_asn1_put_tag(tag, indata, inlen, NULL, 0, NULL); + if (r < 0) + return r; + if (r == 0) + return SC_ERROR_INVALID_ASN1_OBJECT; - *outlen = 2 + nlc + inlen; - ptr = malloc(*outlen); + u8 *ptr = calloc(r, sizeof *ptr); if (ptr == NULL) { return SC_ERROR_OUT_OF_MEMORY; } - *outdata = ptr; - *ptr++ = tag; - - if (nlc) { - *ptr++ = 0x80 | nlc; - while (nlc--) { - *ptr++ = (inlen >> (nlc << 3)) & 0xFF; - } - } else { - *ptr++ = inlen & 0x7F; - } + *outlen = r; - memcpy(ptr, indata, inlen); - return SC_SUCCESS; + return sc_asn1_put_tag(tag, indata, inlen, *outdata, *outlen, NULL); } -static int wrap_key(sc_card_t *card, int keyid, const char *outf, const char *pin) +static int wrap_key(sc_context_t *ctx, sc_card_t *card, int keyid, const char *outf, const char *pin) { sc_cardctl_sc_hsm_wrapped_key_t wrapped_key; struct sc_pin_cmd_data data; sc_path_t path; + sc_file_t *file = NULL; FILE *out = NULL; u8 fid[2]; - u8 ef_prkd[MAX_PRKD]; - u8 ef_cert[MAX_CERT]; + u8 *ef_prkd = NULL; + u8 *ef_cert = NULL; u8 wrapped_key_buff[MAX_KEY]; - u8 keyblob[MAX_WRAPPED_KEY]; - u8 *key; - u8 *ptr; + u8 *keyblob = NULL; + u8 *key = NULL; + u8 *ptr = NULL; char *lpin = NULL; - size_t key_len; - int r, ef_prkd_len, ef_cert_len; + size_t key_len = 0, keyblob_len = MAX_KEY; + int r, ef_prkd_len = 0, ef_cert_len = 0; if ((keyid < 1) || (keyid > 255)) { fprintf(stderr, "Invalid key reference (must be 0 < keyid <= 255)\n"); @@ -1317,7 +1364,7 @@ static int wrap_key(sc_card_t *card, int keyid, const char *outf, const char *pi data.pin1.data = (unsigned char *)lpin; data.pin1.len = strlen(lpin); - r = sc_pin_cmd(card, &data, NULL); + r = sc_pin_cmd(card, &data); if (r < 0) { fprintf(stderr, "PIN verification failed with %s\n", sc_strerror(r)); @@ -1351,18 +1398,25 @@ static int wrap_key(sc_card_t *card, int keyid, const char *outf, const char *pi /* Try to select a related EF containing the PKCS#15 description of the key */ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0); - r = sc_select_file(card, &path, NULL); - + r = sc_select_file(card, &path, &file); if (r == SC_SUCCESS) { - ef_prkd_len = sc_read_binary(card, 0, ef_prkd, sizeof(ef_prkd), 0); + if (!(ef_prkd = malloc(file->size))) { + r = -1; + goto err; + } + + ef_prkd_len = sc_read_binary(card, 0, ef_prkd, file->size, 0); + sc_file_free(file); + file = NULL; if (ef_prkd_len < 0) { fprintf(stderr, "Error reading PRKD file %s. Skipping.\n", sc_strerror(ef_prkd_len)); ef_prkd_len = 0; } else { - ef_prkd_len = determineLength(ef_prkd, ef_prkd_len); + ef_prkd_len = (int)determineLength(ef_prkd, ef_prkd_len); } } + keyblob_len += ef_prkd_len; fid[0] = EE_CERTIFICATE_PREFIX; fid[1] = (unsigned char)keyid; @@ -1370,25 +1424,36 @@ static int wrap_key(sc_card_t *card, int keyid, const char *outf, const char *pi /* Try to select a related EF containing the certificate for the key */ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0); - r = sc_select_file(card, &path, NULL); + r = sc_select_file(card, &path, &file); if (r == SC_SUCCESS) { - ef_cert_len = sc_read_binary(card, 0, ef_cert, sizeof(ef_cert), 0); + if (!(ef_cert = malloc(file->size))) { + r = -1; + goto err; + } + ef_cert_len = sc_read_binary(card, 0, ef_cert, file->size, 0); if (ef_cert_len < 0) { fprintf(stderr, "Error reading certificate %s. Skipping\n", sc_strerror(ef_cert_len)); ef_cert_len = 0; } else { - ef_cert_len = determineLength(ef_cert, ef_cert_len); + ef_cert_len = (int)determineLength(ef_cert, ef_cert_len); } } + keyblob_len += ef_cert_len; + + if (!(keyblob = malloc(keyblob_len))) { + r = -1; + goto err; + } ptr = keyblob; // Encode key in octet string object key_len = 0; - wrap_with_tag(0x04, wrapped_key.wrapped_key, wrapped_key.wrapped_key_length, + r = wrap_with_tag(0x04, wrapped_key.wrapped_key, wrapped_key.wrapped_key_length, &key, &key_len); + LOG_TEST_GOTO_ERR(ctx, r, "Out of memory"); memcpy(ptr, key, key_len); ptr += key_len; @@ -1409,27 +1474,31 @@ static int wrap_key(sc_card_t *card, int keyid, const char *outf, const char *pi ptr += ef_cert_len; } - // Encode key, key decription and certificate object in sequence - wrap_with_tag(0x30, keyblob, ptr - keyblob, &key, &key_len); + // Encode key, key description and certificate object in sequence + r = wrap_with_tag(0x30, keyblob, ptr - keyblob, &key, &key_len); + LOG_TEST_GOTO_ERR(ctx, r, "Out of memory"); out = fopen(outf, "wb"); if (out == NULL) { perror(outf); - free(key); - return -1; + r = -1; + goto err; } if (fwrite(key, 1, key_len, out) != key_len) { perror(outf); - free(key); - fclose(out); - return -1; + r = -1; } - free(key); fclose(out); - return 0; +err: + free(key); + free(keyblob); + free(ef_cert); + free(ef_prkd); + sc_file_free(file); + return r; } @@ -1449,7 +1518,7 @@ static int update_ef(sc_card_t *card, u8 prefix, u8 id, int erase, const u8 *buf r = sc_select_file(card, &path, NULL); if ((r == SC_SUCCESS) && erase) { - r = sc_delete_file(card, &path); + sc_delete_file(card, &path); r = SC_ERROR_FILE_NOT_FOUND; } @@ -1477,15 +1546,14 @@ static int unwrap_key(sc_card_t *card, int keyid, const char *inf, const char *p { sc_cardctl_sc_hsm_wrapped_key_t wrapped_key; struct sc_pin_cmd_data data; - u8 keyblob[MAX_WRAPPED_KEY]; - const u8 *ptr,*prkd,*cert; - FILE *in = NULL; + u8 *keyblob = NULL; + const u8 *ptr, *prkd, *cert; sc_path_t path; u8 fid[2]; char *lpin = NULL; unsigned int cla, tag; - int r, keybloblen; - size_t len, olen, prkd_len, cert_len; + int r; + size_t keybloblen, len, olen, prkd_len, cert_len; if ((keyid < 1) || (keyid > 255)) { fprintf(stderr, "Invalid key reference (must be 0 < keyid <= 255)\n"); @@ -1497,33 +1565,27 @@ static int unwrap_key(sc_card_t *card, int keyid, const char *inf, const char *p return -1; } - in = fopen(inf, "rb"); - - if (in == NULL) { - perror(inf); - return -1; - } - - if ((keybloblen = fread(keyblob, 1, sizeof(keyblob), in)) < 0) { + if (!fread_to_eof(inf, &keyblob, &keybloblen)) { perror(inf); - return -1; + r = -1; + goto err; } - fclose(in); - ptr = keyblob; - if ((sc_asn1_read_tag(&ptr, keybloblen, &cla, &tag, &len) != SC_SUCCESS) || - ((cla & SC_ASN1_TAG_CONSTRUCTED) != SC_ASN1_TAG_CONSTRUCTED) || - ((tag != SC_ASN1_TAG_SEQUENCE)) ){ + if ((sc_asn1_read_tag(&ptr, keybloblen, &cla, &tag, &len) != SC_SUCCESS) + || ((cla & SC_ASN1_TAG_CONSTRUCTED) != SC_ASN1_TAG_CONSTRUCTED) + || (tag != SC_ASN1_TAG_SEQUENCE) ){ fprintf(stderr, "Invalid wrapped key format (Outer sequence).\n"); - return -1; + r = -1; + goto err; } - if ((sc_asn1_read_tag(&ptr, len, &cla, &tag, &olen) != SC_SUCCESS) || - (cla & SC_ASN1_TAG_CONSTRUCTED) || - ((tag != SC_ASN1_TAG_OCTET_STRING)) ){ + if ((sc_asn1_read_tag(&ptr, len, &cla, &tag, &olen) != SC_SUCCESS) + || ((cla & SC_ASN1_TAG_CONSTRUCTED) == SC_ASN1_TAG_CONSTRUCTED) + || (tag != SC_ASN1_TAG_OCTET_STRING) ){ fprintf(stderr, "Invalid wrapped key format (Key binary).\n"); - return -1; + r = -1; + goto err; } wrapped_key.wrapped_key = (u8 *)ptr; @@ -1556,7 +1618,8 @@ static int unwrap_key(sc_card_t *card, int keyid, const char *inf, const char *p if (r == SC_SUCCESS) { fprintf(stderr, "Found existing private key description in EF with fid %02x%02x. Please remove key first, select unused key reference or use --force.\n", fid[0], fid[1]); - return -1; + r = -1; + goto err; } } @@ -1570,7 +1633,8 @@ static int unwrap_key(sc_card_t *card, int keyid, const char *inf, const char *p if (r == SC_SUCCESS) { fprintf(stderr, "Found existing certificate in EF with fid %02x%02x. Please remove certificate first, select unused key reference or use --force.\n", fid[0], fid[1]); - return -1; + r = -1; + goto err; } } @@ -1589,11 +1653,11 @@ static int unwrap_key(sc_card_t *card, int keyid, const char *inf, const char *p data.pin1.data = (u8 *)lpin; data.pin1.len = strlen(lpin); - r = sc_pin_cmd(card, &data, NULL); + r = sc_pin_cmd(card, &data); if (r < 0) { fprintf(stderr, "PIN verification failed with %s\n", sc_strerror(r)); - return -1; + goto err; } if (pin == NULL) { @@ -1614,17 +1678,17 @@ static int unwrap_key(sc_card_t *card, int keyid, const char *inf, const char *p if (r == SC_ERROR_INS_NOT_SUPPORTED) { // Not supported or not initialized for key shares fprintf(stderr, "Card not initialized for key wrap\n"); - return -1; + goto err; } if (r == SC_ERROR_INCORRECT_PARAMETERS) { // Not supported or not initialized for key shares fprintf(stderr, "Wrapped key does not match DKEK\n"); - return -1; + goto err; } if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_UNWRAP_KEY, *) failed with %s\n", sc_strerror(r)); - return -1; + goto err; } if (prkd_len > 0) { @@ -1632,7 +1696,7 @@ static int unwrap_key(sc_card_t *card, int keyid, const char *inf, const char *p if (r < 0) { fprintf(stderr, "Updating private key description failed with %s\n", sc_strerror(r)); - return -1; + goto err; } } @@ -1641,17 +1705,225 @@ static int unwrap_key(sc_card_t *card, int keyid, const char *inf, const char *p if (r < 0) { fprintf(stderr, "Updating certificate failed with %s\n", sc_strerror(r)); - return -1; + goto err; } } printf("Key successfully imported\n"); - return 0; + +err: + free(keyblob); + return r; +} + + +static int export_key(sc_card_t *card, int keyid, const char *outf) +{ + sc_path_t path; + sc_file_t *file = NULL; + FILE *outfp = NULL; + u8 fid[2]; + u8 *ef_cert = NULL; + u8 *dev_aut_cert = NULL; + u8 *dica = NULL; + u8 tag = SC_ASN1_TAG_CONSTRUCTED | SC_ASN1_TAG_SEQUENCE; /* 0x30 */ + int r = 0, ef_cert_len = 0, total_certs_len; + size_t dev_aut_cert_len = 0, dica_len = 0; + u8 *data = NULL, *out = NULL, *ptr; + size_t datalen, outlen; + + if ((keyid < 1) || (keyid > 255)) { + fprintf(stderr, "Invalid key reference (must be 0 < keyid <= 255)\n"); + return -1; + } + + fid[0] = EE_CERTIFICATE_PREFIX; + fid[1] = (unsigned char)keyid; + ef_cert_len = 0; + + /* Try to select a related EF containing the certificate for the key */ + sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0); + r = sc_select_file(card, &path, &file); + if (r != SC_SUCCESS) { + fprintf(stderr, "Wrong key reference (-i %d)? Failed to select file: %s\n", keyid, sc_strerror(r)); + return -1; + } + + if (!(ef_cert = malloc(file->size))) { + r = -1; + goto err; + } + + ef_cert_len = sc_read_binary(card, 0, ef_cert, file->size, 0); + if (ef_cert_len < 0) { + fprintf(stderr, "Error reading certificate %s. Skipping\n", sc_strerror(ef_cert_len)); + ef_cert_len = 0; + } else { + ef_cert_len = (int)determineLength(ef_cert, ef_cert_len); + } + sc_file_free(file); + file = NULL; + + /* C_DevAut */ + fid[0] = 0x2F; + fid[1] = 0x02; + dev_aut_cert_len = 0; + + /* Read concatenation of both certificates */ + sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0); + r = sc_select_file(card, &path, &file); + if (r != SC_SUCCESS) { + fprintf(stderr, "Failed to select certificates: %s\n", sc_strerror(r)); + r = -1; + goto err; + } + + if (!(dev_aut_cert = malloc(file->size))) { + r = -1; + goto err; + } + + total_certs_len = sc_read_binary(card, 0, dev_aut_cert, file->size, 0); + if (total_certs_len < 0) { + fprintf(stderr, "Error reading certificate: %s\n", sc_strerror(total_certs_len)); + r = -1; + goto err; + } else { + dev_aut_cert_len = determineLength(dev_aut_cert, total_certs_len); + dica_len = total_certs_len - dev_aut_cert_len; + if (!(dica = malloc(dica_len))) { + r = -1; + goto err; + } + memcpy(dica, dev_aut_cert + dev_aut_cert_len, dica_len); + } + if (dica_len == 0) { + fprintf(stderr, "Could not determine device issuer certificate\n"); + r = -1; + goto err; + } + + if ((outfp = fopen(outf, "r"))) { + fprintf(stderr, "Output file '%s' already exists\n", outf); + r = -1; + goto err; + } + fprintf(stderr, "Warning: Device certificate chain not verified!\n"); + + datalen = ef_cert_len + dev_aut_cert_len + dica_len; + outlen = 8 + datalen; + if (!(data = malloc(datalen))) { + fprintf(stderr, "Malloc failed\n"); + r = -1; + goto err; + } + if (!(out = malloc(outlen))) { + fprintf(stderr, "Malloc failed\n"); + r = -1; + goto err; + } + memcpy(data, ef_cert, ef_cert_len); + memcpy(data + ef_cert_len, dev_aut_cert, dev_aut_cert_len); + memcpy(data + ef_cert_len + dev_aut_cert_len, dica, dica_len); + + if ((r = sc_asn1_put_tag(tag, data, datalen, out, outlen, &ptr)) < 0) { + fprintf(stderr, "Error formatting ASN1 sequence: %s\n", sc_strerror(r)); + r = -1; + goto err; + } + outlen = ptr - out; + + if (!(outfp = fopen(outf, "wb"))) { + perror(outf); + r = -1; + goto err; + } + + if (fwrite(out, 1, outlen, outfp) != (size_t)outlen) { + perror(outf); + r = -1; + } + +err: + if (outfp) + fclose(outfp); + free(out); + free(data); + free(dica); + free(dev_aut_cert); + free(ef_cert); + sc_file_free(file); + + return r; +} + +static void print_pka_status(const sc_cardctl_sc_hsm_pka_status_t *status) +{ + printf("Number of public keys: %d\n", status->num_total); + printf("Missing public keys: %d\n", status->num_missing); + printf("Required pubkeys for auth: %d\n", status->num_required); + printf("Authenticated public keys: %d\n", status->num_authenticated); +} + +static int register_public_key(sc_context_t *ctx, sc_card_t *card, const char *inf) +{ + int r = 0; + sc_cardctl_sc_hsm_pka_register_t pka_register; + + memset(&pka_register, 0, sizeof(pka_register)); + + if (!fread_to_eof(inf, &pka_register.buf, &pka_register.buflen)) { + r = -1; + goto err; + } + + r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_REGISTER_PUBLIC_KEY, &pka_register); + if (r == SC_ERROR_INS_NOT_SUPPORTED) { /* Not supported or not initialized for public key registration */ + fprintf(stderr, "Card not initialized for public key registration\n"); + r = -1; + goto err; + } + if (r < 0) { + fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_REGISTER_PUBLIC_KEY, *) failed with %s\n", sc_strerror(r)); + r = -1; + goto err; + } + + print_pka_status(&pka_register.new_status); + + r = 0; + /* fall-through */ + +err: + free(pka_register.buf); + pka_register.buf = NULL; + return r; } -int main(int argc, char * const argv[]) +static int public_key_auth_status(sc_context_t *ctx, sc_card_t *card) +{ + int r; + sc_cardctl_sc_hsm_pka_status_t status; + + r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_PUBLIC_KEY_AUTH_STATUS, &status); + if (r == SC_ERROR_INS_NOT_SUPPORTED) { /* Not supported or not initialized for public key registration */ + fprintf(stderr, "Card not initialized for public key registration\n"); + return -1; + } + if (r < 0) { + fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_PUBLIC_KEY_AUTH_STATUS, *) failed with %s\n", sc_strerror(r)); + return -1; + } + + print_pka_status(&status); + + return 0; +} + + +int main(int argc, char *argv[]) { int err = 0, r, c, long_optind = 0; int action_count = 0; @@ -1661,13 +1933,20 @@ int main(int argc, char * const argv[]) int do_create_dkek_share = 0; int do_wrap_key = 0; int do_unwrap_key = 0; + int do_export_key = 0; + int do_register_public_key = 0; + int do_public_key_auth_status = 0; sc_path_t path; sc_file_t *file = NULL; const char *opt_so_pin = NULL; const char *opt_pin = NULL; const char *opt_filename = NULL; const char *opt_password = NULL; + const char *opt_bio1 = NULL; + const char *opt_bio2 = NULL; int opt_retry_counter = 3; + int opt_num_of_pub_keys = -1; + int opt_required_pub_keys = 1; int opt_dkek_shares = -1; int opt_key_reference = -1; int opt_password_shares_threshold = -1; @@ -1675,12 +1954,11 @@ int main(int argc, char * const argv[]) int opt_force = 0; int opt_iter = 10000000; sc_context_param_t ctx_param; - - setbuf(stderr, NULL); - setbuf(stdout, NULL); + sc_context_t *ctx = NULL; + sc_card_t *card = NULL; while (1) { - c = getopt_long(argc, argv, "XC:I:P:W:U:s:i:fr:wv", options, &long_optind); + c = getopt_long(argc, argv, "XC:I:P:W:U:K:n:e:g:Ss:i:fr:wv", options, &long_optind); if (c == -1) break; if (c == '?') @@ -1715,6 +1993,26 @@ int main(int argc, char * const argv[]) opt_filename = optarg; action_count++; break; + case 'K': + opt_num_of_pub_keys = (int)atol(optarg); + break; + case 'n': + opt_required_pub_keys = (int)atol(optarg); + break; + case 'e': + do_export_key = 1; + opt_filename = optarg; + action_count++; + break; + case 'g': + do_register_public_key = 1; + opt_filename = optarg; + action_count++; + break; + case 'S': + do_public_key_auth_status = 1; + action_count++; + break; case OPT_PASSWORD: util_get_pin(optarg, &opt_password); break; @@ -1725,22 +2023,28 @@ int main(int argc, char * const argv[]) util_get_pin(optarg, &opt_pin); break; case OPT_RETRY: - opt_retry_counter = atol(optarg); + opt_retry_counter = (int)atol(optarg); + break; + case OPT_BIO1: + opt_bio1 = optarg; + break; + case OPT_BIO2: + opt_bio2 = optarg; break; case OPT_PASSWORD_SHARES_THRESHOLD: - opt_password_shares_threshold = atol(optarg); + opt_password_shares_threshold = (int)atol(optarg); break; case OPT_PASSWORD_SHARES_TOTAL: - opt_password_shares_total = atol(optarg); + opt_password_shares_total = (int)atol(optarg); break; case 's': - opt_dkek_shares = atol(optarg); + opt_dkek_shares = (int)atol(optarg); break; case 'f': opt_force = 1; break; case 'i': - opt_key_reference = atol(optarg); + opt_key_reference = (int)atol(optarg); break; case 'r': opt_reader = optarg; @@ -1757,19 +2061,72 @@ int main(int argc, char * const argv[]) } } -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !(defined LIBRESSL_VERSION_NUMBER) - OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS - | OPENSSL_INIT_ADD_ALL_CIPHERS - | OPENSSL_INIT_ADD_ALL_DIGESTS, - NULL); -#else - CRYPTO_malloc_init(); - ERR_load_crypto_strings(); - OpenSSL_add_all_algorithms(); -#endif + if (!do_initialize && opt_num_of_pub_keys != -1) { + fprintf(stderr, "Option -K (--public-key-auth) requires option -X\n"); + exit(1); + } + if (!do_initialize && opt_required_pub_keys != 1) { + fprintf(stderr, "Option -n (--required-pub-keys) requires option -X\n"); + exit(1); + } + if (do_initialize && do_export_key) { + fprintf(stderr, "Option -e (--export-for-pub-key-auth) excludes option -X\n"); + exit(1); + } + if (do_wrap_key && do_export_key) { + fprintf(stderr, "Option -e (--export-for-pub-key-auth) excludes option -W\n"); + exit(1); + } + if (do_unwrap_key && do_export_key) { + fprintf(stderr, "Option -e (--export-for-pub-key-auth) excludes option -U\n"); + exit(1); + } + if (do_export_key && opt_key_reference == -1) { + fprintf(stderr, "Option -e (--export-for-pub-key-auth) requires option -i\n"); + exit(1); + } + if (do_initialize && do_register_public_key) { + fprintf(stderr, "Option -g (--register-public-key) excludes option -X\n"); + exit(1); + } + if (do_wrap_key && do_register_public_key) { + fprintf(stderr, "Option -g (--register-public-key) excludes option -W\n"); + exit(1); + } + if (do_unwrap_key && do_register_public_key) { + fprintf(stderr, "Option -g (--register-public-key) excludes option -U\n"); + exit(1); + } + if (do_export_key && do_register_public_key) { + fprintf(stderr, "Option -g (--register-public-key) excludes option -e\n"); + exit(1); + } + if (do_initialize && do_public_key_auth_status) { + fprintf(stderr, "Option -S (--public-key-auth-status) excludes option -X\n"); + exit(1); + } + if (do_wrap_key && do_public_key_auth_status) { + fprintf(stderr, "Option -S (--public-key-auth-status) excludes option -W\n"); + exit(1); + } + if (do_unwrap_key && do_public_key_auth_status) { + fprintf(stderr, "Option -S (--public-key-auth-status) excludes option -U\n"); + exit(1); + } + if (do_export_key && do_public_key_auth_status) { + fprintf(stderr, "Option -S (--public-key-auth-status) excludes option -e\n"); + exit(1); + } + if (do_register_public_key && do_public_key_auth_status) { + fprintf(stderr, "Option -S (--public-key-auth-status) excludes option -g\n"); + exit(1); + } memset(&ctx_param, 0, sizeof(sc_context_param_t)); ctx_param.app_name = app_name; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; r = sc_context_create(&ctx, &ctx_param); if (r != SC_SUCCESS) { @@ -1777,13 +2134,7 @@ int main(int argc, char * const argv[]) exit(1); } - /* Only change if not in opensc.conf */ - if (verbose > 1 && ctx->debug == 0) { - ctx->debug = verbose; - sc_ctx_log_to_file(ctx, "stderr"); - } - - r = util_connect_card_ex(ctx, &card, opt_reader, opt_wait, 0, verbose); + r = util_connect_card_ex(ctx, &card, opt_reader, opt_wait, 0); if (r != SC_SUCCESS) { if (r < 0) { fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(err)); @@ -1799,7 +2150,7 @@ int main(int argc, char * const argv[]) goto fail; } - if (do_initialize && initialize(card, opt_so_pin, opt_pin, opt_retry_counter, opt_dkek_shares, opt_label)) + if (do_initialize && initialize(card, opt_so_pin, opt_pin, opt_retry_counter, opt_bio1, opt_bio2, opt_dkek_shares, opt_num_of_pub_keys, opt_required_pub_keys, opt_label)) goto fail; if (do_create_dkek_share && create_dkek_share(card, opt_filename, opt_iter, opt_password, opt_password_shares_threshold, opt_password_shares_total)) @@ -1811,12 +2162,21 @@ int main(int argc, char * const argv[]) if (do_print_dkek_share && print_dkek_share(card, opt_filename, opt_iter, opt_password, opt_password_shares_total)) goto fail; - if (do_wrap_key && wrap_key(card, opt_key_reference, opt_filename, opt_pin)) + if (do_wrap_key && wrap_key(ctx, card, opt_key_reference, opt_filename, opt_pin)) goto fail; if (do_unwrap_key && unwrap_key(card, opt_key_reference, opt_filename, opt_pin, opt_force)) goto fail; + if (do_export_key && export_key(card, opt_key_reference, opt_filename)) + goto fail; + + if (do_register_public_key && register_public_key(ctx, card, opt_filename)) + goto fail; + + if (do_public_key_auth_status && public_key_auth_status(ctx, card)) + goto fail; + if (action_count == 0) { print_info(card, file); } @@ -1826,11 +2186,9 @@ int main(int argc, char * const argv[]) fail: err = 1; end: - if (card) { - sc_disconnect_card(card); - } - if (ctx) - sc_release_context(ctx); + sc_file_free(file); + sc_disconnect_card(card); + sc_release_context(ctx); ERR_print_errors_fp(stderr); return err; diff --git a/src/tools/sceac-example.c b/src/tools/sceac-example.c index 49d8bbe3f9..27134d2044 100644 --- a/src/tools/sceac-example.c +++ b/src/tools/sceac-example.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* This example shows how to use the library functions perform_pace to @@ -26,10 +26,10 @@ #include "config.h" #endif -#ifdef ENABLE_OPENPACE #include "libopensc/sm.h" #include "sm/sm-iso.h" #include "sm/sm-eac.h" +#include "libopensc/card-npa.h" #include static const char *newpin = NULL; @@ -67,6 +67,7 @@ main (int argc, char **argv) reader = sc_ctx_get_reader(ctx, 0); if (!reader) { fprintf(stderr, "Failed to access reader 0"); + sc_release_context(ctx); exit(1); } @@ -78,11 +79,7 @@ main (int argc, char **argv) exit(1); } - /* initialize OpenPACE */ - EAC_init(); - - - /* Now we try to change the PIN. Therefor we need to establish a SM channel + /* Now we try to change the PIN. Therefore we need to establish a SM channel * with PACE. * * You could set your PIN with pin=“123456â€; or just leave it at NULL to be @@ -133,14 +130,7 @@ main (int argc, char **argv) sc_reset(card, 1); sc_disconnect_card(card); sc_release_context(ctx); - EAC_cleanup(); return -r; } -#else -int -main (int argc, char **argv) -{ - return 1; -} -#endif + diff --git a/src/tools/util.c b/src/tools/util.c index f5a0370090..b173d7ab4b 100644 --- a/src/tools/util.c +++ b/src/tools/util.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" @@ -30,6 +30,8 @@ #endif #include #include "util.h" +#include "ui/notify.h" +#include "common/compat_strlcat.h" int is_string_valid_atr(const char *atr_str) @@ -46,60 +48,68 @@ is_string_valid_atr(const char *atr_str) return 1; } -int -util_connect_card_ex(sc_context_t *ctx, sc_card_t **cardp, - const char *reader_id, int do_wait, int do_lock, int verbose) +int util_connect_reader (sc_context_t *ctx, sc_reader_t **reader, + const char *reader_id, int do_wait) { - struct sc_reader *reader = NULL, *found = NULL; - struct sc_card *card = NULL; + struct sc_reader *found = NULL; int r; + setbuf(stderr, NULL); + setbuf(stdout, NULL); + + sc_notify_init(); + if (do_wait) { - unsigned int event; + unsigned int event = 0; if (sc_ctx_get_reader_count(ctx) == 0) { fprintf(stderr, "Waiting for a reader to be attached...\n"); - r = sc_wait_for_event(ctx, SC_EVENT_READER_ATTACHED, &found, &event, -1, NULL); + r = sc_wait_for_event(ctx, SC_EVENT_READER_ATTACHED|SC_EVENT_CARD_INSERTED, + &found, &event, -1, NULL); if (r < 0) { fprintf(stderr, "Error while waiting for a reader: %s\n", sc_strerror(r)); - return 3; + return r; } r = sc_ctx_detect_readers(ctx); if (r < 0) { fprintf(stderr, "Error while refreshing readers: %s\n", sc_strerror(r)); - return 3; + return r; } } - fprintf(stderr, "Waiting for a card to be inserted...\n"); - r = sc_wait_for_event(ctx, SC_EVENT_CARD_INSERTED, &found, &event, -1, NULL); - if (r < 0) { - fprintf(stderr, "Error while waiting for a card: %s\n", sc_strerror(r)); - return 3; + if (event & SC_EVENT_CARD_INSERTED) { + *reader = found; + } else { + fprintf(stderr, "Waiting for a card to be inserted...\n"); + r = sc_wait_for_event(ctx, SC_EVENT_CARD_INSERTED, &found, &event, -1, NULL); + if (r < 0) { + fprintf(stderr, "Error while waiting for a card: %s\n", sc_strerror(r)); + return r; + } + *reader = found; } - reader = found; } else if (sc_ctx_get_reader_count(ctx) == 0) { fprintf(stderr, "No smart card readers found.\n"); - return 1; + return SC_ERROR_NO_READERS_FOUND; } else { if (!reader_id) { unsigned int i; /* Automatically try to skip to a reader with a card if reader not specified */ for (i = 0; i < sc_ctx_get_reader_count(ctx); i++) { - reader = sc_ctx_get_reader(ctx, i); - if (sc_detect_card_presence(reader) & SC_READER_CARD_PRESENT) { - fprintf(stderr, "Using reader with a card: %s\n", reader->name); + *reader = sc_ctx_get_reader(ctx, i); + if (sc_detect_card_presence(*reader) & SC_READER_CARD_PRESENT) { + fprintf(stderr, "Using reader with a card: %s\n", (*reader)->name); goto autofound; } } /* If no reader had a card, default to the first reader */ - reader = sc_ctx_get_reader(ctx, 0); + *reader = sc_ctx_get_reader(ctx, 0); } else { /* If the reader identifier looks like an ATR, try to find the reader with that card */ if (is_string_valid_atr(reader_id)) { - unsigned char atr_buf[SC_MAX_ATR_SIZE * 3]; + unsigned char atr_buf[SC_MAX_ATR_SIZE]; size_t atr_buf_len = sizeof(atr_buf); unsigned int i; @@ -116,44 +126,56 @@ util_connect_card_ex(sc_context_t *ctx, sc_card_t **cardp, continue; fprintf(stderr, "Matched ATR in reader: %s\n", rdr->name); - reader = rdr; + *reader = rdr; goto autofound; } } else { char *endptr = NULL; - unsigned int num; + long num; errno = 0; num = strtol(reader_id, &endptr, 0); if (!errno && endptr && *endptr == '\0') - reader = sc_ctx_get_reader(ctx, num); + *reader = sc_ctx_get_reader(ctx, (unsigned)num); else - reader = sc_ctx_get_reader_by_name(ctx, reader_id); + *reader = sc_ctx_get_reader_by_name(ctx, reader_id); } } autofound: - if (!reader) { + if (!(*reader)) { fprintf(stderr, "Reader \"%s\" not found (%d reader(s) detected)\n", reader_id, sc_ctx_get_reader_count(ctx)); - return 1; + return SC_ERROR_READER; } - if (sc_detect_card_presence(reader) <= 0) { + if (sc_detect_card_presence(*reader) <= 0) { fprintf(stderr, "Card not present.\n"); - return 3; + return SC_ERROR_CARD_NOT_PRESENT; } } + return SC_SUCCESS; +} +int +util_connect_card_ex(sc_context_t *ctx, sc_card_t **cardp, + const char *reader_id, int do_wait, int do_lock) +{ + struct sc_reader *reader = NULL; + struct sc_card *card = NULL; + int r; - if (verbose) + r = util_connect_reader(ctx, &reader, reader_id, do_wait); + if(r) + return r; + if (ctx->debug) printf("Connecting to card in reader %s...\n", reader->name); r = sc_connect_card(reader, &card); if (r < 0) { fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(r)); - return 1; + return r; } - if (verbose) + if (ctx->debug) printf("Using card driver %s.\n", card->driver->name); if (do_lock) { @@ -161,24 +183,24 @@ util_connect_card_ex(sc_context_t *ctx, sc_card_t **cardp, if (r < 0) { fprintf(stderr, "Failed to lock card: %s\n", sc_strerror(r)); sc_disconnect_card(card); - return 1; + return r; } } *cardp = card; - return 0; + return SC_SUCCESS; } int util_connect_card(sc_context_t *ctx, sc_card_t **cardp, - const char *reader_id, int do_wait, int verbose) + const char *reader_id, int do_wait) { - return util_connect_card_ex(ctx, cardp, reader_id, do_wait, 1, verbose); + return util_connect_card_ex(ctx, cardp, reader_id, do_wait, 1); } -void util_print_binary(FILE *f, const u8 *buf, int count) +void util_print_binary(FILE *f, const u8 *buf, size_t count) { - int i; + size_t i; for (i = 0; i < count; i++) { unsigned char c = buf[i]; @@ -192,9 +214,9 @@ void util_print_binary(FILE *f, const u8 *buf, int count) (void) fflush(f); } -void util_hex_dump(FILE *f, const u8 *in, int len, const char *sep) +void util_hex_dump(FILE *f, const u8 *in, size_t len, const char *sep) { - int i; + size_t i; for (i = 0; i < len; i++) { if (sep != NULL && i) @@ -232,7 +254,8 @@ void util_hex_dump_asc(FILE *f, const u8 *in, size_t count, int addr) } } -void util_print_usage_and_die(const char *app_name, const struct option options[], +void +util_print_usage(const char *app_name, const struct option options[], const char *option_help[], const char *args) { int i; @@ -278,10 +301,36 @@ void util_print_usage_and_die(const char *app_name, const struct option options[ } printf(" %-28s %s\n", buf, option_help[i]); } +} +NORETURN void +util_print_usage_and_die(const char *app_name, const struct option options[], + const char *option_help[], const char *args) +{ + util_print_usage(app_name, options, option_help, args); exit(2); } +int util_list_card_drivers(const sc_context_t *ctx) +{ + int i; + + if (ctx == NULL) { + fprintf(stderr, "Unable to get card drivers!\n"); + return 1; + } + if (ctx->card_drivers[0] == NULL) { + fprintf(stderr, "No card drivers installed!\n"); + return 1; + } + printf("Available card drivers:\n"); + for (i = 0; ctx->card_drivers[i] != NULL; i++) { + printf(" %-16s %s\n", ctx->card_drivers[i]->short_name, + ctx->card_drivers[i]->name); + } + return 0; +} + const char * util_acl_to_str(const sc_acl_entry_t *e) { static char line[80], buf[20]; @@ -335,15 +384,16 @@ const char * util_acl_to_str(const sc_acl_entry_t *e) strcpy(buf, "????"); break; } - strcat(line, buf); - strcat(line, " "); + strlcat(line, buf, sizeof line); + strlcat(line, " ", sizeof line); e = e->next; } + line[(sizeof line)-1] = '\0'; /* make sure it's NUL terminated */ line[strlen(line)-1] = 0; /* get rid of trailing space */ return line; } -void +NORETURN void util_fatal(const char *fmt, ...) { va_list ap; @@ -353,6 +403,9 @@ util_fatal(const char *fmt, ...) vfprintf(stderr, fmt, ap); fprintf(stderr, "\nAborting.\n"); va_end(ap); + + sc_notify_close(); + exit(1); } @@ -439,19 +492,26 @@ util_getpass (char **lineptr, size_t *len, FILE *stream) if (len) *len = MAX_PASS_SIZE; } - return i; + return (int)i; } size_t util_get_pin(const char *input, const char **pin) { - size_t inputlen = strlen(input); + size_t inputlen; size_t pinlen = 0; - if(inputlen > 4 && strncasecmp(input, "env:", 4) == 0) { + if (!input || !pin) { + return 0; + } + inputlen = strlen(input); + + if (inputlen > 4 && strncasecmp(input, "env:", 4) == 0) { // Get a PIN from a environment variable *pin = getenv(input + 4); pinlen = *pin ? strlen(*pin) : 0; + } else if (inputlen > 5 && strncasecmp(input, "file:", 5) == 0) { + fprintf(stderr, "Reading PIN from file not supported!\n"); } else { //Just use the input *pin = input; diff --git a/src/tools/util.h b/src/tools/util.h index b907808773..07c33f26b5 100644 --- a/src/tools/util.h +++ b/src/tools/util.h @@ -16,25 +16,52 @@ #endif #include -#include "common/compat_getopt.h" +#include #include "libopensc/opensc.h" #ifdef __cplusplus extern "C" { #endif -void util_print_binary(FILE *f, const u8 *buf, int count); -void util_hex_dump(FILE *f, const u8 *in, int len, const char *sep); +#if _MSC_VER >= 1310 +/* MS Visual Studio 2003/.NET Framework 1.1 or newer */ +# define NORETURN _declspec( noreturn) +#elif __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ >= 5)) || (defined __clang__) +# define NORETURN __attribute__ ((noreturn)) +#elif __cplusplus >= 201103L +# define NORETURN [[noreturn]] +#elif __STDC_VERSION__ >= 201112L +# define NORETURN _Noreturn +#else +# define NORETURN +#endif + +#ifdef _MSC_VER +# ifndef _SSIZE_T_DEFINED +# undef ssize_t +# include + typedef _W64 SSIZE_T ssize_t; +# define _SSIZE_T_DEFINED +# endif /* _SSIZE_T_DEFINED */ +#endif /* _MSC_VER */ + +void util_print_binary(FILE *f, const u8 *buf, size_t count); +void util_hex_dump(FILE *f, const u8 *in, size_t len, const char *sep); void util_hex_dump_asc(FILE *f, const u8 *in, size_t count, int addr); -void util_print_usage_and_die(const char *app_name, const struct option options[], +void util_print_usage(const char *app_name, const struct option options[], const char *option_help[], const char *args); +NORETURN void util_print_usage_and_die(const char *app_name, const struct option options[], + const char *option_help[], const char *args); +int util_list_card_drivers(const sc_context_t *ctx); const char * util_acl_to_str(const struct sc_acl_entry *e); void util_warn(const char *fmt, ...); void util_error(const char *fmt, ...); -void util_fatal(const char *fmt, ...); +NORETURN void util_fatal(const char *fmt, ...); + +int util_connect_reader (sc_context_t *ctx, sc_reader_t **reader, const char *reader_id, int do_wait); /* All singing all dancing card connect routine */ -int util_connect_card_ex(struct sc_context *, struct sc_card **, const char *reader_id, int do_wait, int do_lock, int verbose); -int util_connect_card(struct sc_context *, struct sc_card **, const char *reader_id, int do_wait, int verbose); +int util_connect_card_ex(struct sc_context *, struct sc_card **, const char *reader_id, int do_wait, int do_lock); +int util_connect_card(struct sc_context *, struct sc_card **, const char *reader_id, int do_wait); int util_getpass (char **lineptr, size_t *n, FILE *stream); diff --git a/src/tools/versioninfo-opensc-notify.rc b/src/tools/versioninfo-opensc-notify.rc new file mode 100644 index 0000000000..ce136cf617 --- /dev/null +++ b/src/tools/versioninfo-opensc-notify.rc @@ -0,0 +1,50 @@ +#include +#include "config.h" +#define TO_STR_HELPER(x) #x +#define TO_STR(x) TO_STR_HELPER(x) + +#define IDC_STATIC -1 +/* defined twice: in resource file and in source code */ +#define IDI_SMARTCARD 102 + +#ifndef __MINGW32__ +IDI_SMARTCARD ICON "..\\..\\win32\\DDORes.dll_14_2302.ico" +#else +IDI_SMARTCARD ICON "../../win32/DDORes.dll_14_2302.ico" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION OPENSC_VERSION_MAJOR,OPENSC_VERSION_MINOR,OPENSC_VERSION_FIX,OPENSC_VERSION_REVISION + PRODUCTVERSION OPENSC_VERSION_MAJOR,OPENSC_VERSION_MINOR,OPENSC_VERSION_FIX,OPENSC_VERSION_REVISION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x21L +#else + FILEFLAGS 0x20L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", OPENSC_VS_FF_COMMENTS + VALUE "CompanyName", OPENSC_VS_FF_COMPANY_NAME + VALUE "FileVersion", TO_STR(OPENSC_VERSION_MAJOR.OPENSC_VERSION_MINOR.OPENSC_VERSION_FIX.OPENSC_VERSION_REVISION) + VALUE "InternalName", PACKAGE_NAME + VALUE "LegalCopyright", OPENSC_VS_FF_LEGAL_COPYRIGHT + VALUE "LegalTrademarks", "" + VALUE "PrivateBuild", "" + VALUE "ProductName", OPENSC_VS_FF_PRODUCT_NAME + VALUE "ProductVersion", TO_STR(OPENSC_VERSION_MAJOR.OPENSC_VERSION_MINOR.OPENSC_VERSION_FIX.OPENSC_VERSION_REVISION) + VALUE "SpecialBuild", "" + VALUE "FileDescription", "OpenSC Notify" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/tools/versioninfo-tools.rc b/src/tools/versioninfo-tools.rc new file mode 100644 index 0000000000..3daacc6b4b --- /dev/null +++ b/src/tools/versioninfo-tools.rc @@ -0,0 +1,40 @@ +#include +#include "config.h" +#define TO_STR_HELPER(x) #x +#define TO_STR(x) TO_STR_HELPER(x) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION OPENSC_VERSION_MAJOR,OPENSC_VERSION_MINOR,OPENSC_VERSION_FIX,OPENSC_VERSION_REVISION + PRODUCTVERSION OPENSC_VERSION_MAJOR,OPENSC_VERSION_MINOR,OPENSC_VERSION_FIX,OPENSC_VERSION_REVISION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x21L +#else + FILEFLAGS 0x20L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", OPENSC_VS_FF_COMMENTS + VALUE "CompanyName", OPENSC_VS_FF_COMPANY_NAME + VALUE "FileVersion", TO_STR(OPENSC_VERSION_MAJOR.OPENSC_VERSION_MINOR.OPENSC_VERSION_FIX.OPENSC_VERSION_REVISION) + VALUE "InternalName", PACKAGE_NAME + VALUE "LegalCopyright", OPENSC_VS_FF_LEGAL_COPYRIGHT + VALUE "LegalTrademarks", "" + VALUE "PrivateBuild", "" + VALUE "ProductName", OPENSC_VS_FF_PRODUCT_NAME + VALUE "ProductVersion", TO_STR(OPENSC_VERSION_MAJOR.OPENSC_VERSION_MINOR.OPENSC_VERSION_FIX.OPENSC_VERSION_REVISION) + VALUE "SpecialBuild", "" + VALUE "FileDescription", "OpenSC command line tool" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/tools/versioninfo-tools.rc.in b/src/tools/versioninfo-tools.rc.in deleted file mode 100644 index 444b1a2522..0000000000 --- a/src/tools/versioninfo-tools.rc.in +++ /dev/null @@ -1,37 +0,0 @@ -#include - -VS_VERSION_INFO VERSIONINFO - FILEVERSION @OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@ - PRODUCTVERSION @OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@ - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x21L -#else - FILEFLAGS 0x20L -#endif - FILEOS 0x40004L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "@OPENSC_VS_FF_COMMENTS@" - VALUE "CompanyName", "@OPENSC_VS_FF_COMPANY_NAME@" - VALUE "FileVersion", "@OPENSC_VERSION_MAJOR@.@OPENSC_VERSION_MINOR@.@OPENSC_VERSION_FIX@.@OPENSC_VERSION_REVISION@" - VALUE "InternalName", "@PACKAGE_NAME@" - VALUE "LegalCopyright", "@OPENSC_VS_FF_LEGAL_COPYRIGHT@" - VALUE "LegalTrademarks", "" - VALUE "PrivateBuild", "" - VALUE "ProductName", "@OPENSC_VS_FF_PRODUCT_NAME@" - VALUE "ProductVersion", "@OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@" - VALUE "SpecialBuild", "" - VALUE "FileDescription", "OpenSC command line tool" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END diff --git a/src/tools/westcos-tool.c b/src/tools/westcos-tool.c index 186bb66b23..1b2ec41e61 100644 --- a/src/tools/westcos-tool.c +++ b/src/tools/westcos-tool.c @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -79,7 +79,7 @@ static const char *option_help[] = { "Read file ", "Write file (ex 0002 write file 0002 to 0002)", "This message", - "Verbose operation. Use several times to enable debug output." + "Verbose operation, may be used several times", }; @@ -111,24 +111,17 @@ static int do_convert_bignum(sc_pkcs15_bignum_t *dst, const BIGNUM *src) return 1; } -static int charge = 0; static void print_openssl_error(void) { long r; - if (!charge) - { - ERR_load_crypto_strings(); - charge = 1; - } - while ((r = ERR_get_error()) != 0) printf("%s\n", ERR_error_string(r, NULL)); } static int verify_pin(sc_card_t *card, int pin_reference, const char *pin_value) { - int r, tries_left = -1; + int r; struct sc_pin_cmd_data data; memset(&data, 0, sizeof(data)); @@ -157,14 +150,14 @@ static int verify_pin(sc_card_t *card, int pin_reference, const char *pin_value) data.pin1.len = strlen(pin_value); } - r = sc_pin_cmd(card, &data, &tries_left); + r = sc_pin_cmd(card, &data); if (r) { if (r == SC_ERROR_PIN_CODE_INCORRECT) { - if (tries_left >= 0) - printf("Error %d attempts left.\n", tries_left); + if (data.pin1.tries_left >= 0) + printf("Error %d attempts left.\n", data.pin1.tries_left); else printf("Wrong pin.\n"); } @@ -181,7 +174,7 @@ static int change_pin(sc_card_t *card, const char *pin_value1, const char *pin_value2) { - int r, tries_left = -1; + int r; struct sc_pin_cmd_data data; memset(&data, 0, sizeof(data)); @@ -214,14 +207,14 @@ static int change_pin(sc_card_t *card, } - r = sc_pin_cmd(card, &data, &tries_left); + r = sc_pin_cmd(card, &data); if (r) { if (r == SC_ERROR_PIN_CODE_INCORRECT) { - if (tries_left >= 0) - printf("Error %d attempts left.\n", tries_left); + if (data.pin1.tries_left >= 0) + printf("Error %d attempts left.\n", data.pin1.tries_left); else printf("Wrong pin.\n"); } @@ -239,7 +232,7 @@ static int unlock_pin(sc_card_t *card, const char *puk_value, const char *pin_value) { - int r, tries_left = -1; + int r; struct sc_pin_cmd_data data; memset(&data, 0, sizeof(data)); @@ -272,14 +265,14 @@ static int unlock_pin(sc_card_t *card, } - r = sc_pin_cmd(card, &data, &tries_left); + r = sc_pin_cmd(card, &data); if (r) { if (r == SC_ERROR_PIN_CODE_INCORRECT) { - if (tries_left >= 0) - printf("Error %d attempts left.\n", tries_left); + if (data.pin1.tries_left >= 0) + printf("Error %d attempts left.\n", data.pin1.tries_left); else printf("Wrong pin.\n"); } @@ -292,20 +285,20 @@ static int unlock_pin(sc_card_t *card, return 0; } -static int cert2der(X509 *cert, u8 **value) +static int cert2der(X509 *in_cert, u8 **value) { int len; u8 *p; - len = i2d_X509(cert, NULL); + len = i2d_X509(in_cert, NULL); p = *value = malloc(len); - i2d_X509(cert, &p); + i2d_X509(in_cert, &p); return len; } static int create_file_cert(sc_card_t *card) { int r; - int size = 0; + size_t size = 0; sc_path_t path; sc_file_t *file = NULL; @@ -368,7 +361,7 @@ int main(int argc, char *argv[]) sc_context_t *ctx = NULL; sc_file_t *file = NULL; sc_path_t path; - RSA *rsa = NULL; + EVP_PKEY *pkey = NULL; BIGNUM *bn = NULL; BIO *mem = NULL; static const char *pin = NULL; @@ -391,7 +384,7 @@ int main(int argc, char *argv[]) opt_wait = 1; break; case 'g': - if(keylen == 0) keylen = 1536; + if(keylen == 0) keylen = 2048; break; case 'o': overwrite = 1; @@ -435,6 +428,9 @@ int main(int argc, char *argv[]) memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.ver = 0; ctx_param.app_name = argv[0]; + ctx_param.debug = verbose; + if (verbose) + ctx_param.debug_file = stderr; r = sc_context_create(&ctx, &ctx_param); if (r) @@ -443,11 +439,6 @@ int main(int argc, char *argv[]) return 1; } - if (verbose > 1) { - ctx->debug = verbose; - sc_ctx_log_to_file(ctx, "stderr"); - } - if (opt_driver != NULL) { r = sc_set_card_driver(ctx, opt_driver); @@ -458,7 +449,7 @@ int main(int argc, char *argv[]) } } - r = util_connect_card(ctx, &card, opt_reader, opt_wait, 0); + r = util_connect_card(ctx, &card, opt_reader, opt_wait); if (r) goto out; @@ -477,7 +468,7 @@ int main(int argc, char *argv[]) file = sc_file_new(); if(file == NULL) { - printf("Not enougth memory.\n"); + printf("Not enough memory.\n"); goto out; } @@ -583,45 +574,36 @@ int main(int argc, char *argv[]) struct sc_pkcs15_pubkey key; struct sc_pkcs15_pubkey_rsa *dst = &(key.u.rsa); u8 *pdata; + EVP_PKEY_CTX *pctx = NULL; memset(&key, 0, sizeof(key)); key.algorithm = SC_ALGORITHM_RSA; printf("Generate key of length %d.\n", keylen); -#if OPENSSL_VERSION_NUMBER>=0x00908000L - rsa = RSA_new(); bn = BN_new(); mem = BIO_new(BIO_s_mem()); + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); - if(rsa == NULL || bn == NULL || mem == NULL) + if(bn == NULL || mem == NULL || pctx == NULL) { - printf("Not enougth memory.\n"); + printf("Not enough memory.\n"); + if (pctx) + EVP_PKEY_CTX_free(pctx); goto out; } - if(!BN_set_word(bn, RSA_F4) || - !RSA_generate_key_ex(rsa, keylen, bn, NULL)) -#else - rsa = RSA_generate_key(keylen, RSA_F4, NULL, NULL); - mem = BIO_new(BIO_s_mem()); - - if(mem == NULL) - { - printf("Not enougth memory.\n"); - goto out; - } - - if (!rsa) -#endif + if (EVP_PKEY_keygen_init(pctx) != 1 || + EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, keylen) != 1 || + EVP_PKEY_keygen(pctx, &pkey) != 1) { - printf("RSA_generate_key_ex return %ld\n", ERR_get_error()); + printf("Key generation failed.\n"); + EVP_PKEY_CTX_free(pctx); goto out; } + EVP_PKEY_CTX_free(pctx); - RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); - - if(!i2d_RSAPrivateKey_bio(mem, rsa)) + if(!i2d_PrivateKey_bio(mem, pkey)) { printf("i2d_RSAPrivateKey_bio return %ld\n", ERR_get_error()); goto out; @@ -638,7 +620,7 @@ int main(int argc, char *argv[]) file = sc_file_new(); if(file == NULL) { - printf("Not enougth memory.\n"); + printf("Not enough memory.\n"); goto out; } @@ -685,17 +667,44 @@ int main(int argc, char *argv[]) if(r) goto out; { +#if OPENSSL_VERSION_NUMBER < 0x30000000L const BIGNUM *rsa_n, *rsa_e; - + RSA *rsa = NULL; + rsa = EVP_PKEY_get1_RSA(pkey); RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL); +#else + BIGNUM *rsa_n = NULL, *rsa_e = NULL; + if (EVP_PKEY_get_bn_param(pkey, "n", &rsa_n) != 1 || + EVP_PKEY_get_bn_param(pkey, "e", &rsa_e) != 1) + { + BN_free(rsa_n); + } +#endif - if (!do_convert_bignum(&dst->modulus, rsa_n) - || !do_convert_bignum(&dst->exponent, rsa_e)) + if (!do_convert_bignum(&dst->modulus, rsa_n) || + !do_convert_bignum(&dst->exponent, rsa_e)) + { +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA_free(rsa); +#else + BN_free(rsa_n); + BN_free(rsa_e); +#endif + free(dst->modulus.data); + free(dst->exponent.data); goto out; - + } +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA_free(rsa); +#else + BN_free(rsa_n); + BN_free(rsa_e); +#endif } r = sc_pkcs15_encode_pubkey(ctx, &key, &pdata, &lg); + free(dst->modulus.data); + free(dst->exponent.data); if(r) goto out; printf("Public key length %"SC_FORMAT_LEN_SIZE_T"d\n", lg); @@ -814,7 +823,7 @@ int main(int argc, char *argv[]) b = malloc(file->size); if(b == NULL) { - printf("Not enougth memory.\n"); + printf("Not enough memory.\n"); goto out; } @@ -864,14 +873,18 @@ int main(int argc, char *argv[]) b = malloc(file->size); if(b == NULL) { - printf("Not enougth memory.\n"); - goto out; + printf("Not enough memory.\n"); + goto out; } memset(b, 0, file->size); fp = fopen(put_filename, "rb"); - fread(b, 1, file->size, fp); + if (fp == NULL || file->size != fread(b, 1, file->size, fp)) + { + printf("could not read %s.\n", put_filename); + goto out; + } fclose(fp); r = sc_update_binary(card, 0, b, file->size, 0); @@ -902,8 +915,8 @@ int main(int argc, char *argv[]) BIO_free(mem); if(bn) BN_free(bn); - if(rsa) - RSA_free(rsa); + if(pkey) + EVP_PKEY_free(pkey); if(file) sc_file_free(file); diff --git a/src/ui/Makefile.am b/src/ui/Makefile.am new file mode 100644 index 0000000000..ec9e149db9 --- /dev/null +++ b/src/ui/Makefile.am @@ -0,0 +1,16 @@ +include $(top_srcdir)/win32/ltrc.inc + +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +EXTRA_DIST = Makefile.mak + +noinst_LTLIBRARIES = libstrings.la libnotify.la +noinst_HEADERS = strings.h notify.h wchar_from_char_str.h char_str_from_wchar.h invisible_window.h + +AM_CPPFLAGS = -I$(top_srcdir)/src +AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_NOTIFY_CFLAGS) +AM_OBJCFLAGS = $(AM_CFLAGS) + +libstrings_la_SOURCES = strings.c + +libnotify_la_SOURCES = notify.c +libnotify_la_LIBADD = $(OPTIONAL_NOTIFY_LIBS) diff --git a/src/ui/Makefile.mak b/src/ui/Makefile.mak new file mode 100644 index 0000000000..f1a3754034 --- /dev/null +++ b/src/ui/Makefile.mak @@ -0,0 +1,17 @@ +TOPDIR = ..\.. + +TARGET = strings.lib +OBJECTS = strings.obj + +TARGET2 = notify.lib +OBJECTS2 = notify.obj + +all: $(TARGET) $(TARGET2) + +!INCLUDE $(TOPDIR)\win32\Make.rules.mak + +$(TARGET): $(OBJECTS) + lib $(LIBFLAGS) /out:$(TARGET) $(OBJECTS) + +$(TARGET2): $(OBJECTS2) + lib $(LIBFLAGS) /out:$(TARGET2) $(OBJECTS2) diff --git a/src/ui/char_str_from_wchar.h b/src/ui/char_str_from_wchar.h new file mode 100644 index 0000000000..1e0570daf6 --- /dev/null +++ b/src/ui/char_str_from_wchar.h @@ -0,0 +1,48 @@ +/* + * char_str_from_wchar.h: Conversion from wide string to string + * + * Copyright (C) 2017 Frank Morgner + * + * 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 + */ + +#ifdef _WIN32 +#include + +static char *char_str_from_wchar(const WCHAR *in) +{ + char *out; + int out_len; + + if (!in) + return NULL; + + out_len = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL); + if (0 >= out_len) + return NULL; + + out = LocalAlloc(0, (sizeof *out) * out_len); + if (!out) + return NULL; + + out_len = WideCharToMultiByte(CP_UTF8, 0, in, -1, out, out_len, NULL, NULL); + if (out_len == 0xFFFD || 0 >= out_len) { + LocalFree(out); + return NULL; + } + + return out; +} +#endif diff --git a/src/ui/invisible_window.h b/src/ui/invisible_window.h new file mode 100644 index 0000000000..89dd906ae3 --- /dev/null +++ b/src/ui/invisible_window.h @@ -0,0 +1,52 @@ +/* + * invisible_window.h: Create invisible Window + * + * Copyright (C) 2017 Frank Morgner + * + * 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 + */ + +#include + +static HWND create_invisible_window(LPCTSTR lpszClassName, + LRESULT (CALLBACK* WndProc)(HWND, UINT, WPARAM, LPARAM), + HINSTANCE hInstance) +{ + HWND hWnd = NULL; + WNDCLASSEX wx = {0}; + + //Register Window class + wx.cbSize = sizeof(WNDCLASSEX); + wx.lpfnWndProc = WndProc; + wx.hInstance = hInstance; + wx.lpszClassName = lpszClassName; + if (RegisterClassEx(&wx)) { + /* create window */ + hWnd = CreateWindowEx(0, lpszClassName, lpszClassName, 0, 0, 0, 0, 0, + HWND_MESSAGE, NULL, NULL, NULL ); + } + + return hWnd; +} + +static BOOL delete_invisible_window(HWND hWnd, LPCTSTR lpszClassName, + HINSTANCE hInstance) +{ + BOOL r; + r = DestroyWindow(hWnd); + r &= UnregisterClass(lpszClassName, hInstance); + + return r; +} diff --git a/src/ui/notify.c b/src/ui/notify.c new file mode 100644 index 0000000000..e693bb377d --- /dev/null +++ b/src/ui/notify.c @@ -0,0 +1,572 @@ +/* + * notify.c: Notification implementation + * + * Copyright (C) 2017 Frank Morgner + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "notify.h" + +#if defined(ENABLE_NOTIFY) && (defined(__APPLE__)) + +#include "libopensc/internal.h" +#include "libopensc/log.h" +#include +#include +#include +#include +#include + +static pid_t child = -1; + +void sc_notify_init(void) +{ +} + +void sc_notify_close(void) +{ + if (child > 0) { + int i, status; + for (i = 0; child != waitpid(child, &status, WNOHANG); i++) { + switch (i) { + case 0: + kill(child, SIGKILL); + break; + case 1: + kill(child, SIGTERM); + break; + default: + /* SIGTERM was our last resort */ + return; + } + usleep(100); + } + child = -1; + } +} + +#endif + +#if defined(ENABLE_NOTIFY) && defined(_WIN32) + +#include "common/compat_strlcpy.h" +#include "invisible_window.h" +#include +#include + +// {83C35893-99C6-4600-BFDB-45925C53BDD9} +static const GUID myGUID = { 0x83c35893, 0x99c6, 0x4600, { 0xbf, 0xdb, 0x45, 0x92, 0x5c, 0x53, 0xbd, 0xd9 } }; +HINSTANCE sc_notify_instance = NULL; +HWND sc_notify_hwnd = NULL; +BOOL delete_icon = TRUE; +#define IDI_SMARTCARD 102 +#define WMAPP_NOTIFYCALLBACK (WM_APP + 1) +static BOOL RestoreTooltip(); +void ShowContextMenu(HWND hwnd, POINT pt); + +// we need commctrl v6 for LoadIconMetric() +#include + +static int GetMonitorScalingRatio(void) +{ + POINT pt = { 0, 0 }; + HMONITOR monitor; + MONITORINFOEX info = { .cbSize = sizeof(MONITORINFOEX) }; + DEVMODE devmode = { .dmSize = sizeof(DEVMODE) }; + monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY); + if (!monitor) + goto err; + if (!GetMonitorInfo(monitor, (LPMONITORINFO)&info)) + goto err; + if (!EnumDisplaySettings(info.szDevice, ENUM_CURRENT_SETTINGS, &devmode)) + goto err; + if (info.rcMonitor.left == info.rcMonitor.right) + goto err; + return 100*devmode.dmPelsWidth/(info.rcMonitor.right - info.rcMonitor.left); +err: + return 100; +} + +static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_COMMAND: + { + int const wmId = LOWORD(wParam); + // Parse the menu selection: + switch (wmId) + { + case WMAPP_EXIT: + break; + + default: + return DefWindowProc(hwnd, message, wParam, lParam); + } + } + break; + + case WMAPP_NOTIFYCALLBACK: + switch (LOWORD(lParam)) { + case NIN_BALLOONTIMEOUT: + case NIN_BALLOONUSERCLICK: + RestoreTooltip(); + break; + + case WM_CONTEXTMENU: + { + int scaling = GetMonitorScalingRatio(); + POINT const pt = { 100*LOWORD(wParam)/scaling, 100*HIWORD(wParam)/scaling }; + ShowContextMenu(hwnd, pt); + } + break; + } + break; + + default: + return DefWindowProc(hwnd, message, wParam, lParam); + } + + return 0; +} + +static const char* lpszClassName = "NOTIFY_CLASS"; + +static BOOL AddNotificationIcon(void) +{ + NOTIFYICONDATA nid = {0}; + TCHAR path[MAX_PATH]={0}; + BOOL r; + + sc_notify_hwnd = create_invisible_window(lpszClassName, WndProc, + sc_notify_instance); + if (!sc_notify_hwnd) { + return FALSE; + } + + nid.cbSize = sizeof(NOTIFYICONDATA); + nid.hWnd = sc_notify_hwnd; + // add the icon, setting the icon, tooltip, and callback message. + // the icon will be identified with the GUID + nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_SHOWTIP | NIF_GUID; + nid.guidItem = myGUID; + nid.uCallbackMessage = WMAPP_NOTIFYCALLBACK; + LoadIconMetric(sc_notify_instance, MAKEINTRESOURCEW(IDI_SMARTCARD), LIM_SMALL, &nid.hIcon); + if (GetModuleFileName(NULL, path, ARRAYSIZE(path))) { + const char *basename = strrchr(path, '\\'); + if (basename) { + basename++; + if (0 != strcmp(basename, "opensc-notify.exe")) { + /* Allow creation of system tray icon only for + * "opensc-notify.exe" to avoid creation of the same icon by + * multiple processes. */ + delete_icon = FALSE; + return FALSE; + } + } + strlcpy(nid.szTip, path, ARRAYSIZE(nid.szTip)); + } else { + strcpy(nid.szTip, PACKAGE_NAME); + } + + r = Shell_NotifyIcon(NIM_ADD, &nid); + if (TRUE == r) { + nid.uVersion = NOTIFYICON_VERSION_4; + r = Shell_NotifyIcon(NIM_SETVERSION, &nid); + } + + return r; +} + +static BOOL DeleteNotificationIcon(void) +{ + BOOL r; + NOTIFYICONDATA nid = {0}; + + if (!delete_icon) { + return FALSE; + } + + nid.cbSize = sizeof(NOTIFYICONDATA); + nid.uFlags = NIF_GUID; + nid.guidItem = myGUID; + + r = Shell_NotifyIcon(NIM_DELETE, &nid); + + r &= delete_invisible_window(sc_notify_hwnd, lpszClassName, + sc_notify_instance); + sc_notify_hwnd = NULL; + + return r; +} + +static BOOL RestoreTooltip() +{ + // After the balloon is dismissed, restore the tooltip. + NOTIFYICONDATA nid = {0}; + + nid.cbSize = sizeof(NOTIFYICONDATA); + nid.uFlags = NIF_SHOWTIP | NIF_GUID; + nid.guidItem = myGUID; + + return Shell_NotifyIcon(NIM_MODIFY, &nid); +} + +void ShowContextMenu(HWND hwnd, POINT pt) +{ + HMENU hMenu; + hMenu=CreatePopupMenu(); + + if (hMenu) { + AppendMenu(hMenu, MF_STRING, WMAPP_EXIT, ui_get_str(NULL, NULL, NULL, NOTIFY_EXIT)); + + // our window must be foreground before calling TrackPopupMenu or the menu will not disappear when the user clicks away + SetForegroundWindow(hwnd); + + // respect menu drop alignment + UINT uFlags = TPM_RIGHTBUTTON; + if (GetSystemMetrics(SM_MENUDROPALIGNMENT) != 0) { + uFlags |= TPM_RIGHTALIGN; + } else { + uFlags |= TPM_LEFTALIGN; + } + + TrackPopupMenuEx(hMenu, uFlags, pt.x, pt.y, hwnd, NULL); + } +} + + +static void notify_shell(struct sc_context *ctx, + const char *title, const char *text, LPCTSTR icon_path, int icon_index) +{ + NOTIFYICONDATA nid = {0}; + HICON icon = NULL; + + nid.cbSize = sizeof(NOTIFYICONDATA); + nid.uFlags = NIF_GUID; + nid.guidItem = myGUID; + + if (title) { + strlcpy(nid.szInfoTitle, title, ARRAYSIZE(nid.szInfoTitle)); + } + if (text) { + nid.uFlags |= NIF_INFO; + strlcpy(nid.szInfo, text, ARRAYSIZE(nid.szInfo)); + } + if (icon_path) { + ExtractIconEx(icon_path, icon_index, &icon, NULL, 1); + if (icon) { + nid.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; + nid.hBalloonIcon = icon; + } + } + + Shell_NotifyIcon(NIM_MODIFY, &nid); + + if (icon) { + DestroyIcon(icon); + } +} + +void sc_notify_init(void) +{ + if (!sc_notify_instance) { + /* returns the HINSTANCE of the exe. If the code executes in a DLL, + * sc_notify_instance_notify should be pre-initialized */ + sc_notify_instance = GetModuleHandle(NULL); + } + AddNotificationIcon(); +} + +void sc_notify_close(void) +{ + DeleteNotificationIcon(); +} + +void sc_notify(const char *title, const char *text) +{ + notify_shell(NULL, title, text, NULL, 0); +} + +void sc_notify_id(struct sc_context *ctx, struct sc_atr *atr, + struct sc_pkcs15_card *p15card, enum ui_str id) +{ + const char *title, *text; + LPCTSTR icon_path = NULL; + int icon_index = 0; + title = ui_get_str(ctx, atr, p15card, id); + text = ui_get_str(ctx, atr, p15card, id+1); + + switch (id) { + case NOTIFY_CARD_INSERTED: + icon_path = TEXT("%SYSTEMROOT%\\system32\\SCardDlg.dll"); + icon_index = 3; + break; + case NOTIFY_CARD_REMOVED: + icon_path = TEXT("%SYSTEMROOT%\\system32\\SCardDlg.dll"); + icon_index = 2; + break; + case NOTIFY_PIN_GOOD: + icon_path = TEXT("%SYSTEMROOT%\\system32\\certmgr.dll"); + icon_index = 16; + break; + case NOTIFY_PIN_BAD: + icon_path = TEXT("%SYSTEMROOT%\\system32\\certmgr.dll"); + icon_index = 11; + break; + default: + break; + } + + notify_shell(ctx, title, text, icon_path, icon_index); +} + +#elif defined(ENABLE_NOTIFY) && defined(__APPLE__) + +static void notify_proxy(struct sc_context *ctx, + const char *title, const char* subtitle, + const char *text, const char *icon, const char *sound, + const char *group) +{ + /* terminal-notifier does not reliably activate keychain when clicked on + * the notification + * (https://github.com/julienXX/terminal-notifier/issues/196), that's why + * we're including NotificationProxy which has similar features */ + const char notificationproxy[] = "/Library/OpenSC/Applications/NotificationProxy.app/Contents/MacOS/NotificationProxy"; + + if (ctx && ctx->app_name + && (0 == strcmp(ctx->app_name, "opensc-pkcs11") + || 0 == strcmp(ctx->app_name, "onepin-opensc-pkcs11"))) { + /* some programs don't like forking when loading our PKCS#11 module, + * see https://github.com/OpenSC/OpenSC/issues/1174. + * TODO implementing an XPC service which sends the notification should + * work, though. See + * https://github.com/OpenSC/OpenSC/issues/1304#issuecomment-376656003 */ + return; + } + + if (child > 0) { + int status; + if (0 == waitpid(child, &status, WNOHANG)) { + kill(child, SIGKILL); + usleep(100); + if (0 == waitpid(child, &status, WNOHANG)) { + sc_log(ctx, "Can't kill %ld, skipping current notification", (long) child); + return; + } + } + } + + child = fork(); + switch (child) { + case 0: + /* child process */ + + /* for some reason the user _tokend can call brew's installation of + * terminal-notifier, but it cannot call `/usr/bin/open` with + * NotificationProxy.app that we're shipping... However, while + * `sudo -u _tokend /usr/local/bin/terminal-notifier -title test` + * works in the terminal, it hangs when executed from the tokend + * process. For now, we try to deliver the notification anyway + * making sure that we are waiting for only one forked process. */ + if (0 > execl(notificationproxy, notificationproxy, + title ? title : "", + subtitle ? subtitle : "", + text ? text : "", + icon ? icon : "", + group ? group : "", + sound ? sound : "", + (char *) NULL)) { + perror("exec failed"); + exit(0); + } + break; + case -1: + sc_log(ctx, "failed to fork for notification"); + break; + default: + if (ctx) { + sc_log(ctx, "Created %ld for notification:", (long) child); + sc_log(ctx, "%s %s %s %s %s %s %s", notificationproxy, + title ? title : "", + subtitle ? subtitle : "", + text ? text : "", + icon ? icon : "", + group ? group : "", + sound ? sound : ""); + } + break; + } +} + +void sc_notify(const char *title, const char *text) +{ + notify_proxy(NULL, title, NULL, text, NULL, NULL, NULL); +} + +void sc_notify_id(struct sc_context *ctx, struct sc_atr *atr, + struct sc_pkcs15_card *p15card, enum ui_str id) +{ + const char *title, *text, *icon, *group; + title = ui_get_str(ctx, atr, p15card, id); + text = ui_get_str(ctx, atr, p15card, id+1); + + if (p15card && p15card->card && p15card->card->reader) { + group = p15card->card->reader->name; + } else { + group = ctx ? ctx->app_name : NULL; + } + + switch (id) { + case NOTIFY_CARD_INSERTED: + icon = "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/VCard.icns"; + break; + case NOTIFY_CARD_REMOVED: + icon = "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/EjectMediaIcon.icns"; + break; + case NOTIFY_PIN_GOOD: + icon = "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/UnlockedIcon.icns"; + break; + case NOTIFY_PIN_BAD: + icon = "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/LockedIcon.icns"; + break; + default: + icon = NULL; + break; + } + + notify_proxy(ctx, title, NULL, text, icon, NULL, group); +} + +#elif defined(ENABLE_NOTIFY) && defined(ENABLE_GIO2) + +#include +#include "libopensc/log.h" + +static GApplication *application = NULL; + +void sc_notify_init(void) +{ + if (!application) { + application = g_application_new("org.opensc.notify", G_APPLICATION_NON_UNIQUE); + } + if (application && FALSE == g_application_get_is_registered(application)) { + g_application_register(application, NULL, NULL); + } +} + +void sc_notify_close(void) +{ + if (application) { + g_object_unref(application); + application = NULL; + } +} + +static void notify_gio(struct sc_context *ctx, + const char *title, const char *text, const char *icon, + const char *group) +{ + if (application + && g_application_get_is_registered(application) + && g_application_get_dbus_connection(application)) { + GIcon *gicon = NULL; + GNotification *notification = g_notification_new(title); + if (!notification) { + return; + } + + if (text) { + g_notification_set_body(notification, text); + } + if (icon) { + gicon = g_themed_icon_new(icon); + if (gicon) { + g_notification_set_icon(notification, gicon); + } + } + + if (ctx) { + sc_log(ctx, "%s %s %s %s", + title ? title : "", + text ? text : "", + icon ? icon : "", + group ? group : ""); + } + + g_application_send_notification(application, group, notification); + + if (gicon) { + g_object_unref(gicon); + } + g_object_unref(notification); + } +} + +#else + +void sc_notify_init(void) {} +void sc_notify_close(void) {} +void sc_notify(const char *title, const char *text) {} +void sc_notify_id(struct sc_context *ctx, struct sc_atr *atr, + struct sc_pkcs15_card *p15card, enum ui_str id) {} + +#endif + +#if defined(ENABLE_NOTIFY) && defined(ENABLE_GIO2) +void sc_notify(const char *title, const char *text) +{ + notify_gio(NULL, title, text, NULL, NULL); +} + +void sc_notify_id(struct sc_context *ctx, struct sc_atr *atr, + struct sc_pkcs15_card *p15card, enum ui_str id) +{ + const char *title, *text, *icon, *group; + title = ui_get_str(ctx, atr, p15card, id); + text = ui_get_str(ctx, atr, p15card, id+1); + + if (p15card && p15card->card && p15card->card->reader) { + group = p15card->card->reader->name; + } else { + group = ctx ? ctx->app_name : NULL; + } + + switch (id) { + case NOTIFY_CARD_INSERTED: + icon = "contact-new"; + break; + case NOTIFY_CARD_REMOVED: + icon = "media-eject"; + break; + case NOTIFY_PIN_GOOD: + icon = "changes-allow"; + break; + case NOTIFY_PIN_BAD: + icon = "changes-prevent"; + break; + default: + icon = NULL; + break; + } + + notify_gio(ctx, title, text, icon, group); +} + +#endif diff --git a/src/ui/notify.h b/src/ui/notify.h new file mode 100644 index 0000000000..19dfddb275 --- /dev/null +++ b/src/ui/notify.h @@ -0,0 +1,50 @@ +/* + * notify.h: OpenSC library header file + * + * Copyright (C) 2017 Frank Morgner + * + * 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 + */ + +#ifndef _NOTIFY_H +#define _NOTIFY_H + +#include "ui/strings.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void sc_notify_init(void); +void sc_notify_close(void); +void sc_notify(const char *title, const char *text); +void sc_notify_id(struct sc_context *ctx, struct sc_atr *atr, + struct sc_pkcs15_card *p15card, enum ui_str id); + +#ifdef _WIN32 +#include +/* If the code executes in a DLL, `sc_notify_instance_notify` should be + * initialized before calling `sc_notify_init()`. If not initialized, we're + * using the HINSTANCE of the EXE */ +extern HINSTANCE sc_notify_instance; +/* This is the message created when the user clicks on "exit". */ +#define WMAPP_EXIT (WM_APP + 2) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/ui/strings.c b/src/ui/strings.c new file mode 100644 index 0000000000..123806a7a4 --- /dev/null +++ b/src/ui/strings.c @@ -0,0 +1,335 @@ +/* + * strings.c: Implementation of default UI strings + * + * Copyright (C) 2017 Frank Morgner + * + * 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 + */ + +#include "ui/strings.h" +#include "libopensc/internal.h" +#include "libopensc/log.h" +#include "ui/char_str_from_wchar.h" +#include +#include +#include + +enum ui_langs { + EN, + DE, +}; + +static const char *get_inserted_text(struct sc_pkcs15_card *p15card, struct sc_atr *atr) +{ + static char text[3*SC_MAX_ATR_SIZE] = {0}; + const char prefix[] = "ATR: "; + + if (p15card && p15card->card + && p15card->card->name) { + return p15card->card->name; + } else if (p15card && p15card->card + && p15card->card->reader && p15card->card->reader->name) { + return p15card->card->reader->name; + } + + if (!atr) + return NULL; + + strcpy(text, prefix); + sc_bin_to_hex(atr->value, atr->len, text + (sizeof prefix) - 1, + sizeof(text) - (sizeof prefix) - 1, ':'); + + return text; +} + +static const char *get_removed_text(struct sc_pkcs15_card *p15card) +{ + if (p15card && p15card->card && p15card->card->reader + && p15card->card->reader->name) { + return p15card->card->reader->name; + } + + return NULL; +} + +static const char *ui_get_config_str(struct sc_context *ctx, + struct sc_atr *atr, const char *flag_name, const char *ret_default) +{ + const char *ret = ret_default; + + scconf_block *atrblock = _sc_match_atr_block(ctx, NULL, atr); + + if (atrblock) + ret = scconf_get_str(atrblock, flag_name, ret_default); + + return ret; +} + +static int find_lang_str(const char *str, enum ui_langs *lang) +{ + if (str) { + if (0 == strncmp(str, "de", 2)) { + if (lang) { + *lang = DE; + } + return 1; + } else if (0 == strncmp(str, "en", 2)) { + if (lang) { + *lang = EN; + } + return 1; + } + } + + return 0; +} + +const char *ui_get_str(struct sc_context *ctx, struct sc_atr *atr, + struct sc_pkcs15_card *p15card, enum ui_str id) +{ + enum ui_langs lang = EN; + const char *str, *option; + + /* load option strings */ + switch (id) { + case MD_PINPAD_DLG_TITLE: + option = "md_pinpad_dlg_title"; + break; + case MD_PINPAD_DLG_MAIN: + option = "md_pinpad_dlg_main"; + break; + case MD_PINPAD_DLG_CONTENT_USER: + option = "md_pinpad_dlg_content_user"; + break; + case MD_PINPAD_DLG_CONTENT_USER_SIGN: + option = "md_pinpad_dlg_content_user_sign"; + break; + case MD_PINPAD_DLG_CONTENT_ADMIN: + option = "md_pinpad_dlg_content_admin"; + break; + case MD_PINPAD_DLG_EXPANDED: + option = "md_pinpad_dlg_expanded"; + break; + case MD_PINPAD_DLG_ICON: + option = "md_pinpad_dlg_icon"; + break; + case NOTIFY_CARD_INSERTED: + option = "notify_card_inserted"; + break; + case NOTIFY_CARD_INSERTED_TEXT: + option = "notify_card_inserted_text"; + break; + case NOTIFY_CARD_REMOVED: + option = "notify_card_removed"; + break; + case NOTIFY_CARD_REMOVED_TEXT: + option = "notify_card_removed_text"; + break; + case NOTIFY_PIN_GOOD: + option = "notify_pin_good"; + break; + case NOTIFY_PIN_GOOD_TEXT: + option = "notify_pin_good_text"; + break; + case NOTIFY_PIN_BAD: + option = "notify_pin_bad"; + break; + case NOTIFY_PIN_BAD_TEXT: + option = "notify_pin_bad_text"; + break; + case MD_PINPAD_DLG_VERIFICATION: + option = "md_pinpad_dlg_verification"; + break; + default: + option = NULL; + break; + } + + /* load language */ + /* card's language supersedes system's language */ + if (!p15card || !p15card->tokeninfo + || !find_lang_str(p15card->tokeninfo->preferred_language, &lang)) { +#ifdef _WIN32 + WCHAR language_wc[LOCALE_NAME_MAX_LENGTH]; + LCIDToLocaleName( + MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT), + language_wc, LOCALE_NAME_MAX_LENGTH, 0); + char *language = char_str_from_wchar(language_wc); + if (find_lang_str(language, &lang)) + sc_log(ctx, "Using the system's native environment locale for user messages (%s).", language); + LocalFree(language); +#else + const char *language = getenv("LANGUAGE"); + /* LANGUAGE supersedes locale */ + if (!find_lang_str(language, &lang)) { + /* XXX Should we use LC_MESSAGES instead? */ + language = setlocale(LC_ALL, ""); + if (find_lang_str(language, &lang)) + sc_log(ctx, "Using the system's native environment locale for user messages (%s).", language); + } else { + sc_log(ctx, "Using the system's LANGUAGE for user messages (%s).", language); + } +#endif + } else { + sc_log(ctx, "Using the token's preferred language for user messages (%s).", p15card->tokeninfo->preferred_language); + } + + /* load default strings */ + switch (lang) { + case DE: + switch (id) { + case MD_PINPAD_DLG_TITLE: + str = "Windows-Sicherheit"; + break; + case MD_PINPAD_DLG_MAIN: + str = "OpenSC Smartcard-Anbieter"; + break; + case MD_PINPAD_DLG_CONTENT_USER: + str = "Bitte geben Sie Ihre PIN auf dem PIN-Pad ein."; + break; + case MD_PINPAD_DLG_CONTENT_USER_SIGN: + str = "Bitte geben Sie Ihre PIN für die digitale Signatur auf dem PIN-Pad ein."; + break; + case MD_PINPAD_DLG_CONTENT_ADMIN: + str = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + break; + case MD_PINPAD_DLG_EXPANDED: + str = "Dieses Fenster wird automatisch geschlossen, wenn die PIN am PIN-Pad eingegeben wurde (Timeout typischerweise nach 30 Sekunden)."; + break; + case NOTIFY_CARD_INSERTED: + if (p15card && p15card->card && p15card->card->name) { + str = "Smartcard kann jetzt verwendet werden"; + } else { + str = "Smartcard erkannt"; + } + break; + case NOTIFY_CARD_INSERTED_TEXT: + str = get_inserted_text(p15card, atr); + break; + case NOTIFY_CARD_REMOVED: + str = "Smartcard entfernt"; + break; + case NOTIFY_CARD_REMOVED_TEXT: + str = get_removed_text(p15card); + break; + case NOTIFY_PIN_GOOD: + str = "PIN verifiziert"; + break; + case NOTIFY_PIN_GOOD_TEXT: + str = "Smartcard ist entsperrt"; + break; + case NOTIFY_PIN_BAD: + str = "PIN nicht verifiziert"; + break; + case NOTIFY_PIN_BAD_TEXT: + str = "Smartcard ist gesperrt"; + break; + case MD_PINPAD_DLG_VERIFICATION: + str = "Sofort PIN am PIN-Pad abfragen"; + break; + + case MD_PINPAD_DLG_CONTROL_COLLAPSED: + case MD_PINPAD_DLG_CONTROL_EXPANDED: + str = "Weitere Informationen"; + break; + case MD_PINPAD_DLG_CANCEL: + str = "Abbrechen"; + break; + case NOTIFY_EXIT: + str = "Beenden"; + break; + default: + str = NULL; + break; + } + break; + case EN: + default: + switch (id) { + case MD_PINPAD_DLG_TITLE: + str = "Windows Security"; + break; + case MD_PINPAD_DLG_MAIN: + str = "OpenSC Smart Card Provider"; + break; + case MD_PINPAD_DLG_CONTENT_USER: + str = "Please enter your PIN on the PIN pad."; + break; + case MD_PINPAD_DLG_CONTENT_USER_SIGN: + str = "Please enter your digital signature PIN on the PIN pad."; + break; + case MD_PINPAD_DLG_CONTENT_ADMIN: + str = "Please enter your PIN to unblock the user PIN on the PIN pad."; + break; + case MD_PINPAD_DLG_EXPANDED: + str = "This window will be closed automatically after the PIN has been submitted on the PIN pad (timeout typically after 30 seconds)."; + break; + case NOTIFY_CARD_INSERTED: + if (p15card && p15card->card && p15card->card->name) { + str = "Smart card is ready to use"; + } else { + str = "Smart card detected"; + } + break; + case NOTIFY_CARD_INSERTED_TEXT: + str = get_inserted_text(p15card, atr); + break; + case NOTIFY_CARD_REMOVED: + str = "Smart card removed"; + break; + case NOTIFY_CARD_REMOVED_TEXT: + str = get_removed_text(p15card); + break; + case NOTIFY_PIN_GOOD: + str = "PIN verified"; + break; + case NOTIFY_PIN_GOOD_TEXT: + str = "Smart card is unlocked"; + break; + case NOTIFY_PIN_BAD: + str = "PIN not verified"; + break; + case NOTIFY_PIN_BAD_TEXT: + str = "Smart card is locked"; + break; + case MD_PINPAD_DLG_VERIFICATION: + str = "Immediately request PIN on PIN-Pad"; + break; + + case MD_PINPAD_DLG_CONTROL_COLLAPSED: + case MD_PINPAD_DLG_CONTROL_EXPANDED: + str = "Click here for more information"; + break; + case MD_PINPAD_DLG_CANCEL: + str = "Cancel"; + break; + case NOTIFY_EXIT: + str = "Exit"; + break; + default: + str = NULL; + break; + } + break; + } + + /* user's strings supersede default strings */ + if (option != NULL) { + /* overwrite str with the user's choice */ + str = ui_get_config_str(ctx, atr, option, str); + } + + return str; +} diff --git a/src/ui/strings.h b/src/ui/strings.h new file mode 100644 index 0000000000..1139193965 --- /dev/null +++ b/src/ui/strings.h @@ -0,0 +1,60 @@ +/* + * strings.c: default UI strings + * + * Copyright (C) 2017 Frank Morgner + * + * 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 + */ + +#ifndef _SC_STRINGS_H +#define _SC_STRINGS_H + +#include "libopensc/pkcs15.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum ui_str { + MD_PINPAD_DLG_TITLE, + MD_PINPAD_DLG_MAIN, + MD_PINPAD_DLG_CONTENT_USER, + MD_PINPAD_DLG_CONTENT_ADMIN, + MD_PINPAD_DLG_EXPANDED, + MD_PINPAD_DLG_CONTROL_COLLAPSED, + MD_PINPAD_DLG_CONTROL_EXPANDED, + MD_PINPAD_DLG_ICON, + MD_PINPAD_DLG_CANCEL, + NOTIFY_CARD_INSERTED, + NOTIFY_CARD_INSERTED_TEXT, + NOTIFY_CARD_REMOVED, + NOTIFY_CARD_REMOVED_TEXT, + NOTIFY_PIN_GOOD, + NOTIFY_PIN_GOOD_TEXT, + NOTIFY_PIN_BAD, + NOTIFY_PIN_BAD_TEXT, + MD_PINPAD_DLG_CONTENT_USER_SIGN, + NOTIFY_EXIT, + MD_PINPAD_DLG_VERIFICATION, +}; + +const char *ui_get_str(struct sc_context *ctx, struct sc_atr *atr, + struct sc_pkcs15_card *p15card, enum ui_str id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/ui/wchar_from_char_str.h b/src/ui/wchar_from_char_str.h new file mode 100644 index 0000000000..72fe517534 --- /dev/null +++ b/src/ui/wchar_from_char_str.h @@ -0,0 +1,44 @@ +/* + * wchar_from_char_str.h: Conversion from string to wide string + * + * Copyright (C) 2017 Frank Morgner + * + * 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 + */ + +static WCHAR *wchar_from_char_str(const char *in) +{ + WCHAR *out; + int out_len; + + if (!in) + return NULL; + + out_len = MultiByteToWideChar(CP_UTF8, 0, in, -1, NULL, 0); + if (0 >= out_len) + return NULL; + + out = LocalAlloc(0, (sizeof *out) * out_len); + if (!out) + return NULL; + + out_len = MultiByteToWideChar(CP_UTF8, 0, in, -1, out, out_len); + if (out_len == 0xFFFD || 0 >= out_len) { + LocalFree(out); + return NULL; + } + + return out; +} diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000000..d803c33cee --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,66 @@ +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in + +EXTRA_DIST = softhsm_2.6.1_ref.json softhsm_2.7.0_ref.json softhsm_fips_ref.json softhsm_libressl_ref.json + +TEST_EXTENSIONS = .sh +# This pushes the valgrind command to the test environment +AM_TESTS_ENVIRONMENT = \ + SOURCE_PATH='$(top_srcdir)'; \ + export SOURCE_PATH; \ + BUILD_PATH='$(top_builddir)'; \ + export BUILD_PATH; + +@VALGRIND_CHECK_RULES@ +if VALGRIND_ENABLED +VALGRIND_SUPPRESSIONS_FILES = $(srcdir)/opensc.supp +EXTRA_DIST += opensc.supp +VALGRIND_FLAGS = --num-callers=30 -q --keep-debuginfo=yes --gen-suppressions=all +# Do not run the bash scripts under valgrind +LOG_COMPILER = $(LOG_VALGRIND) +# the LD_PRELOAD is to avoid false positive leaks from pcsclite +AM_TESTS_ENVIRONMENT += \ + VALGRIND='$(LOG_VALGRIND)'; \ + LD_PRELOAD='$(PCSCLITE_PATH)' +endif + +dist_noinst_SCRIPTS = common.sh \ + init-softhsm.sh \ + init-softokn.sh \ + init-kryoptic.sh \ + test-manpage.sh \ + test-duplicate-symbols.sh \ + test-fuzzing.sh \ + test-p11test.sh \ + test-pkcs11-tool-test.sh \ + test-pkcs11-tool-test-threads.sh \ + test-pkcs11-tool-sign-verify.sh \ + test-pkcs11-tool-allowed-mechanisms.sh \ + test-pkcs11-tool-sym-crypt-test.sh \ + test-pkcs11-tool-unwrap-wrap-test.sh \ + test-pkcs11-tool-import.sh \ + test-pkcs11-tool-uri-with-slot-id.sh + +.NOTPARALLEL: +TESTS = \ + test-manpage.sh \ + test-duplicate-symbols.sh \ + test-pkcs11-tool-test-threads.sh \ + test-pkcs11-tool-allowed-mechanisms.sh \ + test-pkcs11-tool-sym-crypt-test.sh +if ENABLE_OPENSSL +TESTS += \ + test-pkcs11-tool-test.sh \ + test-pkcs11-tool-sign-verify.sh \ + test-pkcs11-tool-unwrap-wrap-test.sh \ + test-pkcs11-tool-import.sh \ + test-pkcs11-tool-uri-with-slot-id.sh +if ENABLE_TESTS +if ENABLE_SHARED +if ENABLE_CMOCKA +TESTS += test-p11test.sh +endif +endif +endif +endif +# no tests expected to fail +#XFAIL_TESTS= diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000000..0cc761cc99 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,18 @@ +# OpenSC test scripts + +## The pkcs11-tool tests + +Some of the tests support running with several PKCS#11 modules: + +- SoftHSM, +- Kyoptic, +- and NSS Softokn. + +The test can be run with specified token `softhsm | kryoptic | softokn`: + +```bash +./test-pkcs11-tool-test.sh # default: softhsm +./test-pkcs11-tool-test.sh softhsm +./test-pkcs11-tool-test.sh kryoptic +./test-pkcs11-tool-sign-verify.sh softokn +``` diff --git a/tests/common.sh b/tests/common.sh new file mode 100644 index 0000000000..7e63016036 --- /dev/null +++ b/tests/common.sh @@ -0,0 +1,123 @@ +#!/bin/bash +## from OpenSC/src/tests/p11test/runtest.sh +BUILD_PATH=${BUILD_PATH:-..} +SOURCE_PATH=${SOURCE_PATH:-..} + +TOKENTYPE=$1 + +# run valgrind with all the switches we are interested in +if [ -n "$VALGRIND" -a -n "$LOG_COMPILER" ]; then + VALGRIND="$LOG_COMPILER" +fi + +export SOPIN="12345678abcdefgh" +export PIN="123456abcdef" +PKCS11_TOOL="$VALGRIND $BUILD_PATH/src/tools/pkcs11-tool" + +if [ "${TOKENTYPE}" == "softhsm" ]; then + source "${SOURCE_PATH}/tests/init-softhsm.sh" +elif [ "${TOKENTYPE}" == "softokn" ]; then + source "${SOURCE_PATH}/tests/init-softokn.sh" +elif [ "${TOKENTYPE}" == "kryoptic" ]; then + source "${SOURCE_PATH}/tests/init-kryoptic.sh" +else + echo "Unknown token type: $1" + exit 1 +fi + +ERRORS=0 +function assert() { + if [[ $1 != 0 ]]; then + echo "====> ERROR: $2" + ERRORS=1 + fi +} + +function generate_key() { + TYPE="$1" + ID="$2" + LABEL="$3" + + echo "Generate $TYPE key (ID=$ID)" + # Generate key pair + $PKCS11_TOOL "${PRIV_ARGS[@]}" --keypairgen --key-type="$TYPE" --label="$LABEL" --id=$ID + if [[ "$?" -ne "0" ]]; then + echo "Couldn't generate $TYPE key pair" + return 1 + fi + + # Extract public key from the card + $PKCS11_TOOL "${PUB_ARGS[@]}" --read-object --id $ID --type pubkey --output-file $ID.der + if [[ "$?" -ne "0" ]]; then + echo "Couldn't read generated $TYPE public key" + return 1 + fi + + # convert it to more digestible PEM format + if [[ ${TYPE:0:3} == "RSA" ]]; then + openssl rsa -inform DER -outform PEM -in $ID.der -pubin > $ID.pub + else + openssl pkey -inform DER -outform PEM -in $ID.der -pubin > $ID.pub + fi + rm $ID.der +} + +function card_setup() { + initialize_token + + # Generate 2048b RSA Key pair + generate_key "RSA:2048" "01" "RSA2048" || return 1 + # Generate 4096b RSA Key pair + generate_key "RSA:4096" "02" "RSA4096" || return 1 + # Generate 256b ECC Key pair + generate_key "EC:secp256r1" "03" "ECC_auth" || return 1 + # Generate 521b ECC Key pair + generate_key "EC:secp521r1" "04" "ECC521" || return 1 + + # Generate an HMAC:SHA256 key + $PKCS11_TOOL "${PRIV_ARGS[@]}" --keygen --key-type="GENERIC:64" --label="HMAC-SHA256" --id="05" --usage-sign + if [[ "$?" -ne "0" ]]; then + echo "Couldn't generate GENERIC key" + return 1 + fi + if [ "${TOKENTYPE}" == "kryoptic" ]; then + # Generate Ed25519 Key pair + generate_key "EC:ed25519" "06" "ed25519" || return 1 + # Generate Ed448 Key pair + generate_key "EC:ed448" "07" "ed448" || return 1 + # Generate x25519 Key pair + generate_key "EC:x25519" "08" "x25519" || return 1 + # Generate x448 Key pair + generate_key "EC:x448" "09" "x448" || return 1 + + # Generate ML-DSA-65 Key pair + generate_key "ML-DSA-65" "10" "ML-DSA-65" || return 1 + # Generate ML-KEM-768 Key pair + generate_key "ML-KEM-768" "11" "ML-KEM-768" || return 1 + # Generate SLH-DSA-SHA2-192S Key pair + generate_key "SLH-DSA-SHA2-192S" "12" "SLH-DSA-SHA2-192S" || return 1 + fi + + # Skip in FIPS mode -- Brainpool curves are not supported + if [[ -e "/etc/system-fips" ]]; then + return + fi + if [[ -f "/proc/sys/crypto/fips_enabled" && $(cat /proc/sys/crypto/fips_enabled) == "1" ]]; then + return + fi + if [[ -n "$LIBRESSL_VERSION" ]]; then + return + fi + + # Generate brainpoolP256r1 Key pair + generate_key "EC:brainpoolP256r1" "13" "brainpoolP256r1" || echo "WARNING: brainpoolP256r1 not supported, skipping" + # Generate brainpoolP256t1 Key pair + generate_key "EC:brainpoolP256t1" "14" "brainpoolP256t1" || echo "WARNING: brainpoolP256t1 not supported, skipping" + +} + +function card_cleanup() { + token_cleanup + rm -f 0{1,2,3,4}.pub 1{3,4}.pub + sleep 1 +} diff --git a/tests/init-kryoptic.sh b/tests/init-kryoptic.sh new file mode 100755 index 0000000000..ea25b30ff2 --- /dev/null +++ b/tests/init-kryoptic.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# set paths +kryoptic_paths="$BUILD_PATH/kryoptic/target/debug/libkryoptic_pkcs11.so \ + /usr/lib64/pkcs11/libkryoptic_pkcs11.so \ + /usr/lib/pkcs11/libkryoptic_pkcs11.so" + +for LIB in $kryoptic_paths; do + echo "Testing $LIB" + if [[ -f $LIB ]]; then + export P11LIB=$LIB + echo "Setting P11LIB=$LIB" + break + fi +done +if [[ -z "$P11LIB" ]]; then + echo "Warning: Could not find the Kryoptic PKCS#11 module" +fi + +function initialize_token() { + TMPPDIR="kryoptic.tmp" + mkdir -p "$TMPPDIR" + export TOKDIR=$(realpath $TMPPDIR/tokens) + if [ -d "${TMPPDIR}" ]; then + rm -fr "${TMPPDIR}" + fi + mkdir -p "${TOKDIR}" + + export KRYOPTIC_CONF="${KRYOPTIC_CONF:-$TOKDIR/kryoptic.sql}" + export TOKENCONFIGVARS="export KRYOPTIC_CONF=$TOKDIR/kryoptic.sql" + export TOKENLABEL="Kryoptic Token" + + # init token + $PKCS11_TOOL --module "${P11LIB}" --init-token \ + --label "${TOKENLABEL}" --so-pin "${PIN}" + # set pin + $PKCS11_TOOL --module "${P11LIB}" --so-pin "${PIN}" \ + --login --login-type so --init-pin --pin "${PIN}" + + #export PKCS11SPY=$P11LIB + #export P11LIB="../src/pkcs11/.libs/pkcs11-spy.so" + export PUB_ARGS=("--module=${P11LIB}" "--token-label=${TOKENLABEL}") + export PRIV_ARGS=("${PUB_ARGS[@]}" "--login" "--pin=${PIN}") + export PRIV_ARGS_KRYOPTIC=("${PRIV_ARGS[@]}") +} + +function token_cleanup() { + rm -fr "${TMPPDIR}" + sleep 1 +} diff --git a/tests/init-softhsm.sh b/tests/init-softhsm.sh new file mode 100755 index 0000000000..ccb377c9d9 --- /dev/null +++ b/tests/init-softhsm.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +SOURCE_PATH=${SOURCE_PATH:-..} + + +softhsm_paths="/usr/local/lib/softhsm/libsofthsm2.so \ + /usr/lib/softhsm/libsofthsm2.so \ + /usr/lib64/pkcs11/libsofthsm2.so \ + /usr/lib/i386-linux-gnu/softhsm/libsofthsm2.so \ + /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so" + +for LIB in $softhsm_paths; do + echo "Testing $LIB" + if [[ -f $LIB ]]; then + export P11LIB=$LIB + echo "Setting P11LIB=$LIB" + break + fi +done +if [[ -z "$P11LIB" ]]; then + echo "Warning: Could not find the SoftHSM PKCS#11 module" +fi + +function initialize_token() { + echo "directories.tokendir = $(realpath .tokens)" > .softhsm2.conf + if [ -d ".tokens" ]; then + rm -rf ".tokens" + fi + mkdir ".tokens" + + export SOFTHSM2_CONF=$(realpath ".softhsm2.conf") + softhsm2-util --init-token --slot 0 --label "SC test" --so-pin="$SOPIN" --pin="$PIN" + + export PUB_ARGS=("--module=${P11LIB}") + export PRIV_ARGS=("${PUB_ARGS[@]}" "--login" "--pin=${PIN}") + export PRIV_ARGS_SOFTHSM=("${PRIV_ARGS[@]}") +} + +function token_cleanup() { + rm .softhsm2.conf + rm -rf ".tokens" + sleep 1 +} diff --git a/tests/init-softokn.sh b/tests/init-softokn.sh new file mode 100644 index 0000000000..f219f9df82 --- /dev/null +++ b/tests/init-softokn.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +softokn_paths="/usr/lib64/libsoftokn3.so \ + /usr/lib/x86_64-linux-gnu/libsoftokn3.so" + +for LIB in $softokn_paths; do + echo "Testing $LIB" + if [[ -f $LIB ]]; then + export P11LIB=$LIB + echo "Setting P11LIB=$LIB" + break + fi +done +if [[ -z "$P11LIB" ]]; then + echo "Warning: Could not find the Softokn PKCS#11 module" +fi + +function initialize_token() { + TMPPDIR="$BUILD_PATH/softokn" + export TOKDIR="$TMPPDIR/tokens" + if [ -d "${TMPPDIR}" ]; then + rm -fr "${TMPPDIR}" + fi + mkdir "${TMPPDIR}" + mkdir "${TOKDIR}" + + TOKENLABEL="NSS Certificate DB" + PINVALUE="12345678" + PINFILE="${TMPPDIR}/pinfile.txt" + echo ${PINVALUE} > "${PINFILE}" + + certutil -N -d $TOKDIR -f $PINFILE + + export NSS_LIB_PARAMS=configDir=$TMPPDIR/tokens + export PUB_ARGS=("--module=${P11LIB}" "--token-label=${TOKENLABEL}") + export PRIV_ARGS=("${PUB_ARGS[@]}" "--login" "--pin=${PINVALUE}") + export PRIV_ARGS_SOFTOKN=("${PRIV_ARGS[@]}") +} + +function token_cleanup() { + rm -fr "${TMPPDIR}" + sleep 1 +} diff --git a/tests/kryoptic_ref.json b/tests/kryoptic_ref.json new file mode 100644 index 0000000000..0c3c0fa2de --- /dev/null +++ b/tests/kryoptic_ref.json @@ -0,0 +1,10997 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "RSA_PKCS_KEY_PAIR_GEN", + "1024", + "16384", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "RSA_PKCS", + "1024", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY,CKF_WRAP,CKF_UNWRAP" + ], + [ + "RSA_X_509", + "1024", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY,CKF_WRAP,CKF_UNWRAP" + ], + [ + "SHA1_RSA_PKCS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_OAEP", + "1024", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "RSA_PKCS_PSS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS_PSS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ML_KEM_KEY_PAIR_GEN", + "800", + "1568", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "ML_KEM", + "800", + "1568", + "CKF_ENCAPSULATE,CKF_DECAPSULATE" + ], + [ + "ML_DSA_KEY_PAIR_GEN", + "1312", + "2592", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "ML_DSA", + "1312", + "2592", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_ML_DSA", + "1312", + "2592", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000020", + "2048", + "8192", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "0x00000021", + "2048", + "8192", + "CKF_DERIVE" + ], + [ + "HASH_ML_DSA_SHA224", + "1312", + "2592", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_ML_DSA_SHA256", + "1312", + "2592", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_ML_DSA_SHA384", + "1312", + "2592", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_ML_DSA_SHA3_512", + "1312", + "2592", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_ML_DSA_SHA3_224", + "1312", + "2592", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_ML_DSA_SHA3_256", + "1312", + "2592", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_ML_DSA_SHA3_384", + "1312", + "2592", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_ML_DSA_SHA3_512", + "1312", + "2592", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SLH_DSA_KEY_PAIR_GEN", + "32", + "64", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "SLH_DSA", + "32", + "64", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_SLH_DSA", + "32", + "64", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_SLH_DSA_SHA224", + "32", + "64", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_SLH_DSA_SHA256", + "32", + "64", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_SLH_DSA_SHA384", + "32", + "64", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_SLH_DSA_SHA3_512", + "32", + "64", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_SLH_DSA_SHA3_224", + "32", + "64", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_SLH_DSA_SHA3_256", + "32", + "64", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_SLH_DSA_SHA3_384", + "32", + "64", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "HASH_SLH_DSA_SHA3_512", + "32", + "64", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS_PSS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS_PSS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS_PSS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS_PSS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000048", + "0", + "0", + "CKF_DIGEST" + ], + [ + "0x00000049", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x0000004A", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x0000004B", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x0000004C", + "0", + "0", + "CKF_DIGEST" + ], + [ + "0x0000004D", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x0000004E", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x0000004F", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x00000056", + "48", + "48", + "CKF_DERIVE" + ], + [ + "0x00000057", + "48", + "48", + "CKF_DERIVE" + ], + [ + "SHA3_256_RSA_PKCS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA3_384_RSA_PKCS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA3_512_RSA_PKCS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA3_256_RSA_PKCS_PSS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA3_384_RSA_PKCS_PSS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA3_512_RSA_PKCS_PSS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA3_224_RSA_PKCS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA3_224_RSA_PKCS_PSS", + "1024", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA_1_HMAC", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA_1_HMAC_GENERAL", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256_HMAC", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_HMAC_GENERAL", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224_HMAC", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_HMAC_GENERAL", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384_HMAC", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_HMAC_GENERAL", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512_HMAC", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_HMAC_GENERAL", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA3_256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "0x000002B1", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x000002B2", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x000002B3", + "0", + "0", + "CKF_GENERATE" + ], + [ + "SHA3_224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "0x000002B6", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x000002B7", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x000002B8", + "0", + "0", + "CKF_GENERATE" + ], + [ + "SHA3_384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "0x000002C1", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x000002C2", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x000002C3", + "0", + "0", + "CKF_GENERATE" + ], + [ + "SHA3_512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "0x000002D1", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x000002D2", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x000002D3", + "0", + "0", + "CKF_GENERATE" + ], + [ + "GENERIC_SECRET_KEY_GEN", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00000360", + "0", + "-1", + "CKF_DERIVE" + ], + [ + "0x00000362", + "0", + "-1", + "CKF_DERIVE" + ], + [ + "0x00000363", + "0", + "-1", + "CKF_DERIVE" + ], + [ + "0x00000364", + "0", + "-1", + "CKF_DERIVE" + ], + [ + "0x00000365", + "0", + "-1", + "CKF_DERIVE" + ], + [ + "0x00000392", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x00000393", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x00000394", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x00000395", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x00000396", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x00000397", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x00000398", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x00000399", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x0000039A", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x000003AC", + "0", + "-1", + "CKF_DERIVE" + ], + [ + "0x000003AD", + "0", + "-1", + "CKF_DERIVE" + ], + [ + "0x000003B0", + "0", + "-1", + "CKF_GENERATE" + ], + [ + "0x000003D8", + "0", + "-1", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x000003D9", + "48", + "48", + "CKF_DERIVE" + ], + [ + "0x000003E0", + "48", + "48", + "CKF_DERIVE" + ], + [ + "0x000003E1", + "0", + "-1", + "CKF_DERIVE" + ], + [ + "0x000003E3", + "0", + "-1", + "CKF_DERIVE" + ], + [ + "0x000003E4", + "0", + "-1", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x000003E5", + "48", + "48", + "CKF_DERIVE" + ], + [ + "EC_KEY_PAIR_GEN", + "256", + "521", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "ECDSA", + "256", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA1", + "256", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA224", + "256", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA256", + "256", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA384", + "256", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA512", + "256", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA3_224", + "256", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA3_256", + "256", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA3_384", + "256", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDSA_SHA3_512", + "256", + "521", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "ECDH1_DERIVE", + "256", + "521", + "CKF_DERIVE" + ], + [ + "ECDH1_COFACTOR_DERIVE", + "256", + "521", + "CKF_DERIVE" + ], + [ + "EC_EDWARDS_KEY_PAIR_GEN", + "256", + "456", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "0x00001056", + "256", + "448", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "EDDSA", + "256", + "456", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "AES_KEY_GEN", + "16", + "32", + "CKF_GENERATE" + ], + [ + "AES_ECB", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_CBC", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_MAC", + "16", + "32", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "AES_MAC_GENERAL", + "16", + "32", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "AES_CBC_PAD", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_CTR", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_GCM", + "16", + "32", + "CKF_MESSAGE_ENCRYPT,CKF_MESSAGE_DECRYPT,CKF_MULTI_MESSAGE,CKF_ENCRYPT,CKF_DECRY" + ], + [ + "AES_CCM", + "16", + "32", + "CKF_MESSAGE_ENCRYPT,CKF_MESSAGE_DECRYPT,CKF_MULTI_MESSAGE,CKF_ENCRYPT,CKF_DECRY" + ], + [ + "AES_CTS", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_CMAC", + "16", + "32", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "AES_CMAC_GENERAL", + "16", + "32", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "AES_ECB_ENCRYPT_DATA", + "16", + "32", + "CKF_DERIVE" + ], + [ + "AES_CBC_ENCRYPT_DATA", + "16", + "32", + "CKF_DERIVE" + ], + [ + "AES_OFB", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_CFB8", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_CFB128", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00002108", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_KEY_WRAP", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "0x0000210B", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "0x00004003", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00004004", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00004005", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00004006", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00004007", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00004008", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00004009", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x0000402A", + "0", + "-1", + "CKF_DERIVE" + ], + [ + "0x0000402B", + "0", + "-1", + "CKF_DERIVE" + ], + [ + "0x0000402C", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x8007678C", + "0", + "-1", + "CKF_DERIVE" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "01", + "RSA_X_509", + "YES", + "YES" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "02", + "RSA_X_509", + "YES", + "YES" + ], + [ + "02", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS", + "YES", + "" + ], + [ + "03", + "ECDSA", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA256", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA384", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA512", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA3_224", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA3_256", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA3_384", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA3_512", + "YES", + "" + ], + [ + "04", + "ECDSA", + "YES", + "" + ], + [ + "04", + "ECDSA_SHA256", + "YES", + "" + ], + [ + "04", + "ECDSA_SHA384", + "YES", + "" + ], + [ + "04", + "ECDSA_SHA512", + "YES", + "" + ], + [ + "04", + "ECDSA_SHA3_224", + "YES", + "" + ], + [ + "04", + "ECDSA_SHA3_256", + "YES", + "" + ], + [ + "04", + "ECDSA_SHA3_384", + "YES", + "" + ], + [ + "04", + "ECDSA_SHA3_512", + "YES", + "" + ], + [ + "06", + "EDDSA", + "YES", + "" + ], + [ + "07", + "EDDSA", + "YES", + "" + ], + [ + "10", + "ML_DSA", + "YES", + "" + ], + [ + "10", + "HASH_ML_DSA", + "YES", + "" + ], + [ + "10", + "HASH_ML_DSA_SHA224", + "YES", + "" + ], + [ + "10", + "HASH_ML_DSA_SHA256", + "YES", + "" + ], + [ + "10", + "HASH_ML_DSA_SHA384", + "YES", + "" + ], + [ + "10", + "HASH_ML_DSA_SHA3_512", + "YES", + "" + ], + [ + "10", + "HASH_ML_DSA_SHA3_224", + "YES", + "" + ], + [ + "10", + "HASH_ML_DSA_SHA3_256", + "YES", + "" + ], + [ + "10", + "HASH_ML_DSA_SHA3_384", + "YES", + "" + ], + [ + "10", + "HASH_ML_DSA_SHA3_512", + "YES", + "" + ], + [ + "12", + "SLH_DSA", + "YES", + "" + ], + [ + "12", + "HASH_SLH_DSA", + "YES", + "" + ], + [ + "12", + "HASH_SLH_DSA_SHA224", + "YES", + "" + ], + [ + "12", + "HASH_SLH_DSA_SHA256", + "YES", + "" + ], + [ + "12", + "HASH_SLH_DSA_SHA384", + "YES", + "" + ], + [ + "12", + "HASH_SLH_DSA_SHA3_512", + "YES", + "" + ], + [ + "12", + "HASH_SLH_DSA_SHA3_224", + "YES", + "" + ], + [ + "12", + "HASH_SLH_DSA_SHA3_256", + "YES", + "" + ], + [ + "12", + "HASH_SLH_DSA_SHA3_384", + "YES", + "" + ], + [ + "12", + "HASH_SLH_DSA_SHA3_512", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "multipart_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "MULTIPART SIGN&VERIFY WORKS" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA3_256_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA3_384_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA3_512_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA3_224_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA224_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA3_256_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA3_384_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA3_512_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA3_224_RSA_PKCS", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "ec_sign_size_test", + "result": "pass" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "ENCAPSULATE", + "DECAPSULATE", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ], + [ + "01", + "RSA2048", + "RSA", + "2048", + "", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "02", + "RSA4096", + "RSA", + "4096", + "", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "03", + "ECC_auth", + "EC", + "256", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "YES", + "YES", + "" + ], + [ + "04", + "ECC521", + "EC", + "521", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "YES", + "YES", + "" + ], + [ + "05", + "HMAC-SHA256", + "GEN", + "512", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "06", + "ed25519", + "EDDSA", + "256", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "YES", + "YES", + "" + ], + [ + "07", + "ed448", + "EDDSA", + "456", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "YES", + "YES", + "" + ], + [ + "08", + "x25519", + "XEDDSA", + "256", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "YES", + "YES", + "" + ], + [ + "09", + "x448", + "XEDDSA", + "448", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "YES", + "YES", + "" + ], + [ + "10", + "ML-DSA-65", + "ML-DSA", + "15616", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "11", + "ML-KEM-768", + "ML-KEM", + "0", + "", + "", + "", + "", + "", + "", + "", + "YES", + "YES", + "", + "", + "" + ], + [ + "12", + "SLH-DSA-SHA2-192S", + "SLH-DSA", + "384", + "", + "YES", + "YES", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "data": [ + [ + "KEY ID", + "MECHANISM", + "HASH", + "MGF", + "SALT", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA224", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA256", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA384", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA512", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_224", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_256", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_384", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA3_224", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA3_256", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA3_384", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA3_512", + "MGF1_SHA3_512", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_256_RSA_PKCS_PSS", + "SHA3_256", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_384_RSA_PKCS_PSS", + "SHA3_384", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_512_RSA_PKCS_PSS", + "SHA3_512", + "MGF1_SHA3_512", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_224", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_256", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_384", + "0", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA3_224_RSA_PKCS_PSS", + "SHA3_224", + "MGF1_SHA3_512", + "0", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ], + [ + "03", + "ECDH1_DERIVE", + "YES" + ], + [ + "03", + "ECDH1_COFACTOR_DERIVE", + "YES" + ], + [ + "04", + "ECDH1_DERIVE", + "YES" + ], + [ + "04", + "ECDH1_COFACTOR_DERIVE", + "YES" + ], + [ + "08", + "ECDH1_DERIVE", + "YES" + ], + [ + "09", + "ECDH1_DERIVE", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/tests/opensc.supp b/tests/opensc.supp new file mode 100644 index 0000000000..46cd2e1d45 --- /dev/null +++ b/tests/opensc.supp @@ -0,0 +1,99 @@ +{ + SoftHSM provides us with uninitialized flags + Memcheck:Cond + fun:supported_mechanisms_test + obj:/usr/lib/x86_64-linux-gnu/libcmocka.so.0.7.0 + fun:_cmocka_run_group_tests + fun:main +} +{ + SoftHSM provides us with uninitialized flags + Memcheck:Cond + fun:get_mechanism_all_flag_name + fun:supported_mechanisms_test + obj:/usr/lib/x86_64-linux-gnu/libcmocka.so.0.7.0 + fun:_cmocka_run_group_tests + fun:main +} +{ + SoftHSM provides us with uninitialized flags + Memcheck:Cond + fun:_itoa_word + fun:__vfprintf_internal + fun:printf + fun:supported_mechanisms_test + obj:/usr/lib/x86_64-linux-gnu/libcmocka.so.0.7.0 + fun:_cmocka_run_group_tests + fun:main +} +{ + SoftHSM provides us with uninitialized flags + Memcheck:Value8 + fun:_itoa_word + fun:__vfprintf_internal + fun:printf + fun:supported_mechanisms_test + obj:/usr/lib/x86_64-linux-gnu/libcmocka.so.0.7.0 + fun:_cmocka_run_group_tests + fun:main +} +{ + SoftHSM provides us with uninitialized flags + Memcheck:Value8 + fun:_itoa_word + fun:__vfprintf_internal + fun:buffered_vfprintf + fun:fprintf + fun:print_mech_info + fun:C_GetMechanismInfo + fun:supported_mechanisms_test + obj:/usr/lib/x86_64-linux-gnu/libcmocka.so.0.7.0 + fun:_cmocka_run_group_tests + fun:main +} +{ + SoftHSM provides us with uninitialized flags + Memcheck:Cond + fun:_itoa_word + fun:__vfprintf_internal + fun:buffered_vfprintf + fun:fprintf + fun:print_mech_info + fun:C_GetMechanismInfo + fun:supported_mechanisms_test + obj:/usr/lib/x86_64-linux-gnu/libcmocka.so.0.7.0 + fun:_cmocka_run_group_tests + fun:main +} +{ + SoftHSM provides us with uninitialized flags + Memcheck:Cond + fun:print_mech_info + fun:C_GetMechanismInfo + fun:supported_mechanisms_test + obj:/usr/lib/x86_64-linux-gnu/libcmocka.so.0.7.0 + fun:_cmocka_run_group_tests + fun:main +} +{ + OpenSC SPY does not cleanup the function_lists + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:allocate_function_list + fun:init_spy + fun:C_GetInterface + fun:C_LoadModule + fun:main +} +{ + OpenSC SPY does not cleanup Interfaces + Memcheck:Leak + match-leak-kinds: definite + fun:calloc + fun:C_LoadModule + fun:init_spy + fun:C_GetInterface + fun:C_LoadModule + fun:main +} diff --git a/tests/softhsm_2.6.1_ref.json b/tests/softhsm_2.6.1_ref.json new file mode 100644 index 0000000000..014d42c7d8 --- /dev/null +++ b/tests/softhsm_2.6.1_ref.json @@ -0,0 +1,1409 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "MD5", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "MD5_HMAC", + "16", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA_1_HMAC", + "20", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_HMAC", + "28", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_HMAC", + "32", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_HMAC", + "48", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_HMAC", + "64", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_KEY_PAIR_GEN", + "512", + "16384", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "RSA_PKCS", + "512", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY,CKF_WRAP,CKF_UNWRAP" + ], + [ + "RSA_X_509", + "512", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "MD5_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_OAEP", + "512", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "SHA224_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "GENERIC_SECRET_KEY_GEN", + "1", + "-2147483648", + "CKF_GENERATE" + ], + [ + "0x00000120", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00000130", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00000131", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00000121", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00000122", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00000125", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00001100", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x00001101", + "0", + "0", + "CKF_DERIVE" + ], + [ + "DES3_ECB", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "DES3_CBC", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "DES3_CBC_PAD", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00001102", + "0", + "0", + "CKF_DERIVE" + ], + [ + "DES3_CBC_ENCRYPT_DATA", + "0", + "0", + "CKF_DERIVE" + ], + [ + "DES3_CMAC", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "AES_KEY_GEN", + "16", + "32", + "CKF_GENERATE" + ], + [ + "AES_ECB", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_CBC", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_CBC_PAD", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_CTR", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_GCM", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_KEY_WRAP", + "16", + "-2147483648", + "CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_KEY_WRAP_PAD", + "1", + "-2147483648", + "CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_ECB_ENCRYPT_DATA", + "0", + "0", + "CKF_DERIVE" + ], + [ + "AES_CBC_ENCRYPT_DATA", + "0", + "0", + "CKF_DERIVE" + ], + [ + "AES_CMAC", + "16", + "32", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00002000", + "512", + "1024", + "CKF_GENERATE" + ], + [ + "0x00000010", + "512", + "1024", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "0x00000011", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000012", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000013", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000014", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000015", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000016", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000020", + "512", + "10000", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "0x00002001", + "512", + "10000", + "CKF_GENERATE" + ], + [ + "0x00000021", + "512", + "10000", + "CKF_DERIVE" + ], + [ + "EC_KEY_PAIR_GEN", + "112", + "521", + "CKF_GENERATE_KEY_PAIR,CKF_EC_F_P,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "ECDSA", + "112", + "521", + "CKF_SIGN,CKF_VERIFY,CKF_EC_F_P,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "ECDH1_DERIVE", + "112", + "521", + "CKF_DERIVE" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "01", + "RSA_X_509", + "YES", + "YES" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "02", + "RSA_X_509", + "YES", + "YES" + ], + [ + "02", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "03", + "ECDSA", + "YES", + "" + ], + [ + "04", + "ECDSA", + "YES", + "" + ], + [ + "13", + "ECDSA", + "YES", + "" + ], + [ + "14", + "ECDSA", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "multipart_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "MULTIPART SIGN&VERIFY WORKS" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES" + ], + [ + "02", + "MD5_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA224_RSA_PKCS", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "ec_sign_size_test", + "result": "pass" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "ENCAPSULATE", + "DECAPSULATE", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ], + [ + "01", + "RSA2048", + "RSA", + "2048", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "02", + "RSA4096", + "RSA", + "4096", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "03", + "ECC_auth", + "EC", + "256", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "YES", + "YES", + "" + ], + [ + "04", + "ECC521", + "EC", + "521", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "YES", + "YES", + "" + ], + [ + "05", + "HMAC-SHA256", + "GEN", + "512", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "13", + "brainpoolP256r1", + "EC", + "256", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "YES", + "YES", + "" + ], + [ + "14", + "brainpoolP256t1", + "EC", + "256", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "YES", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "data": [ + [ + "KEY ID", + "MECHANISM", + "HASH", + "MGF", + "SALT", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ], + [ + "03", + "ECDH1_DERIVE", + "YES" + ], + [ + "04", + "ECDH1_DERIVE", + "YES" + ], + [ + "13", + "ECDH1_DERIVE", + "YES" + ], + [ + "14", + "ECDH1_DERIVE", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/tests/softhsm_2.7.0_ref.json b/tests/softhsm_2.7.0_ref.json new file mode 100644 index 0000000000..ae8ea04c7e --- /dev/null +++ b/tests/softhsm_2.7.0_ref.json @@ -0,0 +1,1505 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "MD5", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "MD5_HMAC", + "16", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA_1_HMAC", + "20", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_HMAC", + "28", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_HMAC", + "32", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_HMAC", + "48", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_HMAC", + "64", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_KEY_PAIR_GEN", + "512", + "16384", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "RSA_PKCS", + "512", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY,CKF_WRAP,CKF_UNWRAP" + ], + [ + "RSA_X_509", + "512", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "MD5_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_OAEP", + "512", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "SHA224_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "GENERIC_SECRET_KEY_GEN", + "1", + "-2147483648", + "CKF_GENERATE" + ], + [ + "0x00000120", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00000130", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00000131", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00000121", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00000122", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00000125", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00001100", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x00001101", + "0", + "0", + "CKF_DERIVE" + ], + [ + "DES3_ECB", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "DES3_CBC", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "DES3_CBC_PAD", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00001102", + "0", + "0", + "CKF_DERIVE" + ], + [ + "DES3_CBC_ENCRYPT_DATA", + "0", + "0", + "CKF_DERIVE" + ], + [ + "DES3_CMAC", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "AES_KEY_GEN", + "16", + "32", + "CKF_GENERATE" + ], + [ + "AES_ECB", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_CBC", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_CBC_PAD", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_CTR", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_GCM", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_KEY_WRAP", + "16", + "-2147483648", + "CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_KEY_WRAP_PAD", + "1", + "-2147483648", + "CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_ECB_ENCRYPT_DATA", + "0", + "0", + "CKF_DERIVE" + ], + [ + "AES_CBC_ENCRYPT_DATA", + "0", + "0", + "CKF_DERIVE" + ], + [ + "AES_CMAC", + "16", + "32", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00002000", + "512", + "1024", + "CKF_GENERATE" + ], + [ + "0x00000010", + "512", + "1024", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "0x00000011", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000012", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000013", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000014", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000015", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000016", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000020", + "512", + "10000", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "0x00002001", + "512", + "10000", + "CKF_GENERATE" + ], + [ + "0x00000021", + "512", + "10000", + "CKF_DERIVE" + ], + [ + "EC_KEY_PAIR_GEN", + "112", + "521", + "CKF_GENERATE_KEY_PAIR,CKF_EC_F_P,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "ECDSA", + "112", + "521", + "CKF_SIGN,CKF_VERIFY,CKF_EC_F_P,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "ECDH1_DERIVE", + "112", + "521", + "CKF_DERIVE" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "01", + "RSA_X_509", + "YES", + "YES" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "02", + "RSA_X_509", + "YES", + "YES" + ], + [ + "02", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "03", + "ECDSA", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA1", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA256", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA384", + "YES", + "" + ], + [ + "03", + "ECDSA_SHA512", + "YES", + "" + ], + [ + "04", + "ECDSA", + "YES", + "" + ], + [ + "04", + "ECDSA_SHA1", + "YES", + "" + ], + [ + "04", + "ECDSA_SHA256", + "YES", + "" + ], + [ + "04", + "ECDSA_SHA384", + "YES", + "" + ], + [ + "04", + "ECDSA_SHA512", + "YES", + "" + ], + [ + "13", + "ECDSA", + "YES", + "" + ], + [ + "13", + "ECDSA_SHA1", + "YES", + "" + ], + [ + "13", + "ECDSA_SHA256", + "YES", + "" + ], + [ + "13", + "ECDSA_SHA384", + "YES", + "" + ], + [ + "13", + "ECDSA_SHA512", + "YES", + "" + ], + [ + "14", + "ECDSA", + "YES", + "" + ], + [ + "14", + "ECDSA_SHA1", + "YES", + "" + ], + [ + "14", + "ECDSA_SHA256", + "YES", + "" + ], + [ + "14", + "ECDSA_SHA384", + "YES", + "" + ], + [ + "14", + "ECDSA_SHA512", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "multipart_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "MULTIPART SIGN&VERIFY WORKS" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES" + ], + [ + "02", + "MD5_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA224_RSA_PKCS", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "ec_sign_size_test", + "result": "pass" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "ENCAPSULATE", + "DECAPSULATE", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ], + [ + "01", + "RSA2048", + "RSA", + "2048", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "02", + "RSA4096", + "RSA", + "4096", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "03", + "ECC_auth", + "EC", + "256", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "YES", + "YES", + "" + ], + [ + "04", + "ECC521", + "EC", + "521", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "YES", + "YES", + "" + ], + [ + "05", + "HMAC-SHA256", + "GEN", + "512", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "13", + "brainpoolP256r1", + "EC", + "256", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "YES", + "YES", + "" + ], + [ + "14", + "brainpoolP256t1", + "EC", + "256", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "YES", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "data": [ + [ + "KEY ID", + "MECHANISM", + "HASH", + "MGF", + "SALT", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ], + [ + "03", + "ECDH1_DERIVE", + "YES" + ], + [ + "04", + "ECDH1_DERIVE", + "YES" + ], + [ + "13", + "ECDH1_DERIVE", + "YES" + ], + [ + "14", + "ECDH1_DERIVE", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/tests/softhsm_fips_ref.json b/tests/softhsm_fips_ref.json new file mode 100644 index 0000000000..eb49efc36f --- /dev/null +++ b/tests/softhsm_fips_ref.json @@ -0,0 +1,1305 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "MD5", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "MD5_HMAC", + "16", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA_1_HMAC", + "20", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_HMAC", + "28", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_HMAC", + "32", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_HMAC", + "48", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_HMAC", + "64", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_KEY_PAIR_GEN", + "512", + "16384", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "RSA_PKCS", + "512", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY,CKF_WRAP,CKF_UNWRAP" + ], + [ + "RSA_X_509", + "512", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "MD5_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_OAEP", + "512", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "SHA224_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "GENERIC_SECRET_KEY_GEN", + "1", + "-2147483648", + "CKF_GENERATE" + ], + [ + "0x00000120", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00000130", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00000131", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00000121", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00000122", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00000125", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00001100", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x00001101", + "0", + "0", + "CKF_DERIVE" + ], + [ + "DES3_ECB", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "DES3_CBC", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "DES3_CBC_PAD", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00001102", + "0", + "0", + "CKF_DERIVE" + ], + [ + "DES3_CBC_ENCRYPT_DATA", + "0", + "0", + "CKF_DERIVE" + ], + [ + "DES3_CMAC", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "AES_KEY_GEN", + "16", + "32", + "CKF_GENERATE" + ], + [ + "AES_ECB", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_CBC", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_CBC_PAD", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_CTR", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_GCM", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_KEY_WRAP", + "16", + "-2147483648", + "CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_KEY_WRAP_PAD", + "1", + "-2147483648", + "CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_ECB_ENCRYPT_DATA", + "0", + "0", + "CKF_DERIVE" + ], + [ + "AES_CBC_ENCRYPT_DATA", + "0", + "0", + "CKF_DERIVE" + ], + [ + "AES_CMAC", + "16", + "32", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00002000", + "512", + "1024", + "CKF_GENERATE" + ], + [ + "0x00000010", + "512", + "1024", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "0x00000011", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000012", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000013", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000014", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000015", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000016", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000020", + "512", + "10000", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "0x00002001", + "512", + "10000", + "CKF_GENERATE" + ], + [ + "0x00000021", + "512", + "10000", + "CKF_DERIVE" + ], + [ + "EC_KEY_PAIR_GEN", + "112", + "521", + "CKF_GENERATE_KEY_PAIR,CKF_EC_F_P,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "ECDSA", + "112", + "521", + "CKF_SIGN,CKF_VERIFY,CKF_EC_F_P,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "ECDH1_DERIVE", + "112", + "521", + "CKF_DERIVE" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "03", + "ECDSA", + "YES", + "" + ], + [ + "04", + "ECDSA", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "multipart_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "MULTIPART SIGN&VERIFY WORKS" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA224_RSA_PKCS", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "ec_sign_size_test", + "result": "pass" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "ENCAPSULATE", + "DECAPSULATE", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ], + [ + "01", + "RSA2048", + "RSA", + "2048", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "02", + "RSA4096", + "RSA", + "4096", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "03", + "ECC_auth", + "EC", + "256", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "YES", + "YES", + "" + ], + [ + "04", + "ECC521", + "EC", + "521", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "YES", + "YES", + "" + ], + [ + "05", + "HMAC-SHA256", + "GEN", + "512", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "data": [ + [ + "KEY ID", + "MECHANISM", + "HASH", + "MGF", + "SALT", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ], + [ + "03", + "ECDH1_DERIVE", + "YES" + ], + [ + "04", + "ECDH1_DERIVE", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/tests/softhsm_libressl_ref.json b/tests/softhsm_libressl_ref.json new file mode 100644 index 0000000000..f6d39f58c5 --- /dev/null +++ b/tests/softhsm_libressl_ref.json @@ -0,0 +1,1351 @@ +{ +"time": 0, +"results": [ +{ + "test_id": "wait_test", + "result": "skip" +}, +{ + "test_id": "supported_mechanisms_test", + "data": [ + [ + "MECHANISM", + "MIN KEY", + "MAX KEY", + "FLAGS" + ], + [ + "MD5", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA_1", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA224", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA256", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA384", + "0", + "0", + "CKF_DIGEST" + ], + [ + "SHA512", + "0", + "0", + "CKF_DIGEST" + ], + [ + "MD5_HMAC", + "16", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA_1_HMAC", + "20", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_HMAC", + "28", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_HMAC", + "32", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_HMAC", + "48", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_HMAC", + "64", + "512", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_KEY_PAIR_GEN", + "512", + "16384", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "RSA_PKCS", + "512", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY,CKF_WRAP,CKF_UNWRAP" + ], + [ + "RSA_X_509", + "512", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_SIGN,CKF_VERIFY" + ], + [ + "MD5_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_OAEP", + "512", + "16384", + "CKF_ENCRYPT,CKF_DECRYPT,CKF_WRAP,CKF_UNWRAP" + ], + [ + "SHA224_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA1_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA224_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA256_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA384_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "SHA512_RSA_PKCS_PSS", + "512", + "16384", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "GENERIC_SECRET_KEY_GEN", + "1", + "-2147483648", + "CKF_GENERATE" + ], + [ + "0x00000120", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00000130", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00000131", + "0", + "0", + "CKF_GENERATE" + ], + [ + "0x00000121", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00000122", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00000125", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00001100", + "0", + "0", + "CKF_DERIVE" + ], + [ + "0x00001101", + "0", + "0", + "CKF_DERIVE" + ], + [ + "DES3_ECB", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "DES3_CBC", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "DES3_CBC_PAD", + "0", + "0", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "0x00001102", + "0", + "0", + "CKF_DERIVE" + ], + [ + "DES3_CBC_ENCRYPT_DATA", + "0", + "0", + "CKF_DERIVE" + ], + [ + "DES3_CMAC", + "0", + "0", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "AES_KEY_GEN", + "16", + "32", + "CKF_GENERATE" + ], + [ + "AES_ECB", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_CBC", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_CBC_PAD", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_CTR", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_GCM", + "16", + "32", + "CKF_ENCRYPT,CKF_DECRYPT" + ], + [ + "AES_KEY_WRAP", + "16", + "-2147483648", + "CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_KEY_WRAP_PAD", + "1", + "-2147483648", + "CKF_WRAP,CKF_UNWRAP" + ], + [ + "AES_ECB_ENCRYPT_DATA", + "0", + "0", + "CKF_DERIVE" + ], + [ + "AES_CBC_ENCRYPT_DATA", + "0", + "0", + "CKF_DERIVE" + ], + [ + "AES_CMAC", + "16", + "32", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00002000", + "512", + "1024", + "CKF_GENERATE" + ], + [ + "0x00000010", + "512", + "1024", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "0x00000011", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000012", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000013", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000014", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000015", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000016", + "512", + "1024", + "CKF_SIGN,CKF_VERIFY" + ], + [ + "0x00000020", + "512", + "10000", + "CKF_GENERATE_KEY_PAIR" + ], + [ + "0x00002001", + "512", + "10000", + "CKF_GENERATE" + ], + [ + "0x00000021", + "512", + "10000", + "CKF_DERIVE" + ], + [ + "EC_KEY_PAIR_GEN", + "112", + "521", + "CKF_GENERATE_KEY_PAIR,CKF_EC_F_P,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "ECDSA", + "112", + "521", + "CKF_SIGN,CKF_VERIFY,CKF_EC_F_P,CKF_EC_NAMEDCURVE,CKF_EC_UNCOMPRESS" + ], + [ + "ECDH1_DERIVE", + "112", + "521", + "CKF_DERIVE" + ]], + "result": "pass" +}, +{ + "test_id": "interface_test", + "result": "pass" +}, +{ + "test_id": "readonly_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "01", + "RSA_X_509", + "YES", + "YES" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "RSA_PKCS", + "YES", + "YES" + ], + [ + "02", + "RSA_X_509", + "YES", + "YES" + ], + [ + "02", + "MD5_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS", + "YES", + "" + ], + [ + "03", + "ECDSA", + "YES", + "" + ], + [ + "04", + "ECDSA", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "multipart_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "MULTIPART SIGN&VERIFY WORKS" + ], + [ + "01", + "MD5_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "01", + "SHA224_RSA_PKCS", + "YES" + ], + [ + "02", + "MD5_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA1_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA256_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA384_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA512_RSA_PKCS", + "YES" + ], + [ + "02", + "SHA224_RSA_PKCS", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "ec_sign_size_test", + "result": "pass" +}, +{ + "test_id": "usage_test", + "data": [ + [ + "KEY ID", + "LABEL", + "TYPE", + "BITS", + "VERIFY PUBKEY", + "SIGN", + "VERIFY", + "ENCRYPT", + "DECRYPT", + "WRAP", + "UNWRAP", + "ENCAPSULATE", + "DECAPSULATE", + "DERIVE PUBLIC", + "DERIVE PRIVATE", + "ALWAYS AUTH" + ], + [ + "01", + "RSA2048", + "RSA", + "2048", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "02", + "RSA4096", + "RSA", + "4096", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ], + [ + "03", + "ECC_auth", + "EC", + "256", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "YES", + "YES", + "" + ], + [ + "04", + "ECC521", + "EC", + "521", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "YES", + "YES", + "" + ], + [ + "05", + "HMAC-SHA256", + "GEN", + "512", + "", + "YES", + "YES", + "YES", + "YES", + "YES", + "YES", + "", + "", + "", + "", + "" + ]], + "result": "pass" +}, +{ + "test_id": "pss_oaep_test", + "data": [ + [ + "KEY ID", + "MECHANISM", + "HASH", + "MGF", + "SALT", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ], + [ + "01", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "01", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "01", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "01", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "01", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "01", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_OAEP", + "SHA_1", + "MGF1_SHA_1", + "0", + "", + "YES" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-2", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "-1", + "YES", + "" + ], + [ + "02", + "SHA1_RSA_PKCS_PSS", + "SHA_1", + "MGF1_SHA_1", + "0", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-2", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "-1", + "YES", + "" + ], + [ + "02", + "SHA256_RSA_PKCS_PSS", + "SHA256", + "MGF1_SHA256", + "0", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-2", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "-1", + "YES", + "" + ], + [ + "02", + "SHA384_RSA_PKCS_PSS", + "SHA384", + "MGF1_SHA384", + "0", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-2", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "-1", + "YES", + "" + ], + [ + "02", + "SHA512_RSA_PKCS_PSS", + "SHA512", + "MGF1_SHA512", + "0", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-2", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "-1", + "YES", + "" + ], + [ + "02", + "SHA224_RSA_PKCS_PSS", + "SHA224", + "MGF1_SHA224", + "0", + "YES", + "" + ]], + "result": "pass" +}, +{ + "test_id": "derive_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "DERIVE WORKS" + ], + [ + "03", + "ECDH1_DERIVE", + "YES" + ], + [ + "04", + "ECDH1_DERIVE", + "YES" + ]], + "result": "pass" +}, +{ + "test_id": "secret_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "SIGN&VERIFY WORKS", + "ENCRYPT&DECRYPT WORKS" + ]], + "result": "pass" +}, +{ + "test_id": "wrap_tests", + "data": [ + [ + "KEY ID", + "MECHANISM", + "WRAP WORKS", + "UNWRAP WORKS" + ]], + "result": "pass" +}] +} diff --git a/tests/test-duplicate-symbols.sh b/tests/test-duplicate-symbols.sh new file mode 100755 index 0000000000..6e067dc210 --- /dev/null +++ b/tests/test-duplicate-symbols.sh @@ -0,0 +1,21 @@ +#!/bin/bash +SOURCE_PATH=${SOURCE_PATH:-..} + +EXPORTS=`find "${SOURCE_PATH}" -name "*exports"` + +ERRORS=0 +for E in $EXPORTS; do + DUPES=`sort $E | uniq -d` + NUM_DUPES=`sort $E | uniq -d | wc -l` + if [ $NUM_DUPES -gt 0 ]; then + echo "There are $NUM_DUPES duplicate symbols in '$E': $DUPES" + ERRORS=1 + fi +done + +if [[ "$ERRORS" = 1 ]]; then + echo "There are duplicate symbols" + exit 1 +fi + +exit $ERRORS diff --git a/tests/test-fuzzing.sh b/tests/test-fuzzing.sh new file mode 100755 index 0000000000..2f24f57c04 --- /dev/null +++ b/tests/test-fuzzing.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +set -ex + +case "$1" in + "pkcs11-tool") + CMD="src/tools/pkcs11-tool --test --login --pin 123456" + ;; + "pkcs15-tool") + CMD="src/tools/pkcs15-tool --dump" + ;; + "eidenv") + CMD="src/tools/eidenv" + ;; + *) + echo "Unknown fuzzing target" + exit 1 + ;; +esac + +IN=tests/fuzzing-testcases +if [ ! -d "$IN" ] +then + mkdir -p "$IN" + echo -ne "$(printf '\\x90\\x00')" > "$IN"/9000 +fi + +# reuse output directory if possible +OUT="out-$1" +if [ -d "$OUT" ] +then + IN=- +fi + +if [ ! -d x41-smartcard-fuzzing ]; +then + git clone https://github.com/x41sec/x41-smartcard-fuzzing +fi + +gcc -shared -fPIC -o x41-smartcard-fuzzing/scard_override/libsccard_override.so x41-smartcard-fuzzing/scard_override/scard_override.c -ldl -I/usr/include/PCSC/ + +if [ ! -f configure ]; +then + autoreconf -vis +fi + +#export AFL_USE_ASAN=1 +./configure CC=afl-gcc CFLAGS="-O0" --disable-shared --disable-notify --with-pcsc-provider=$PWD/x41-smartcard-fuzzing/scard_override/libsccard_override.so +make + +FUZZ_FILE=input.apdu afl-fuzz -i "$IN" -o "$OUT" -f input.apdu $CMD diff --git a/tests/test-kryoptic.sh b/tests/test-kryoptic.sh new file mode 100755 index 0000000000..fc5b2b4b18 --- /dev/null +++ b/tests/test-kryoptic.sh @@ -0,0 +1,21 @@ +#!/bin/bash +SOURCE_PATH=${SOURCE_PATH:-..} + +echo "Running all supported tests for Kryoptic token..." + +export TEST_PKCS11_BACKEND=kryoptic +pushd tests +make check \ + TESTS='test-p11test.sh + test-pkcs11-tool-allowed-mechanisms.sh + test-pkcs11-tool-import.sh + test-pkcs11-tool-sign-verify.sh + test-pkcs11-tool-test.sh + test-pkcs11-tool-unwrap-wrap-test.sh' +# test-pkcs11-tool-sym-crypt-test.sh # TODO +RV=$? +popd +if [ $RV -ne 0 ]; then + ./.github/dump-logs.sh + exit $RV +fi diff --git a/tests/test-manpage.sh b/tests/test-manpage.sh new file mode 100755 index 0000000000..da36a88693 --- /dev/null +++ b/tests/test-manpage.sh @@ -0,0 +1,33 @@ +#!/bin/bash -x +SOURCE_PATH=${SOURCE_PATH:-..} + +# find all the manual pages in doc/tools +TOOLS=`find "${SOURCE_PATH}/doc/tools" -name "*.1.xml" | sed -E -e "s|.*/([a-z0-9-]*).*|\1|"` +ALL=1 + +for T in $TOOLS; do + SWITCHES=$( ${SOURCE_PATH}/src/tools/${T} --help 2>&1 \ + | grep -v "unrecognized option '--help'" \ + | awk '{if (match($0,"--[a-zA-Z0-9-]*",a) != 0) print a[0]} + {if (match($0," -[a-zA-Z0-9]",a) != 0) print a[0]}' ) + + for S in $SWITCHES; do + grep -q -- "$S" ${SOURCE_PATH}/doc/tools/${T}.1.xml || { echo "${T}: missing switch $S"; ALL=0; }; + done +done +if [ "$ALL" = 0 ]; then + echo "Not all the switches in help are documented in manual pages" + exit 1 +fi + +RES=0 +# find all tools in src/tools (files without extension) +TOOLS=`find "${SOURCE_PATH}/src/tools" -maxdepth 1 -type f ! -name "*.*" | sed -E -e "s|.*/([a-z0-9-]*).*|\1|" | grep -v -- -example` +for T in $TOOLS; do + if [[ ! -f "${SOURCE_PATH}/doc/tools/$T.1.xml" ]]; then + echo "Missing manual page for '$T'" + RES=1 + fi +done + +exit $RES diff --git a/tests/test-p11test.sh b/tests/test-p11test.sh new file mode 100755 index 0000000000..100a4cd913 --- /dev/null +++ b/tests/test-p11test.sh @@ -0,0 +1,83 @@ +#!/bin/bash +SOURCE_PATH=${SOURCE_PATH:-..} + +TOKENTYPE=$1 +TOKENTYPE=${TOKENTYPE:-$TEST_PKCS11_BACKEND} + +if [ "${TOKENTYPE}" == "softokn" ]; then + echo "p11test not supported" + exit 1 +elif [ "${TOKENTYPE}" == "" ]; then + TOKENTYPE=softhsm + echo "No tokentype provided, running with SoftHSM" +fi + +source $SOURCE_PATH/tests/common.sh $TOKENTYPE + +echo "=======================================================" +echo "Setup $TOKENTYPE" +echo "=======================================================" +if [[ ! -f $P11LIB ]]; then + echo "WARNING: The SoftHSM is not installed. Can not run this test" + exit 77; +fi + +card_setup +assert $? "Failed to set up card" + +echo "=======================================================" +echo "Run p11test" +echo "=======================================================" +$VALGRIND "$BUILD_PATH/src/tests/p11test/p11test" -v -m $P11LIB -o $TOKENTYPE.json -p $PIN +assert $? "Failed running tests" + +# Run the input through sed to skip the mechanism part: +# * broken because of uninitialized memory in softhsm +# * different for different softhsm versions +function filter_log() { + sed -n '/readonly_tests/,$p' $1 +} + +REF_FILE="$SOURCE_PATH/tests/${TOKENTYPE}_ref.json" +if [[ "$TOKENTYPE" == "softhsm" ]]; then + VERSION=$(softhsm2-util --version) + REF_FILE="$SOURCE_PATH/tests/${TOKENTYPE}_${VERSION}_ref.json" +fi +if [[ -f "/proc/sys/crypto/fips_enabled" && $(cat /proc/sys/crypto/fips_enabled) == "1" ]]; then + REF_FILE="$SOURCE_PATH/tests/${TOKENTYPE}_fips_ref.json" +fi +if [[ -e "/etc/system-fips" ]]; then + REF_FILE="$SOURCE_PATH/tests/${TOKENTYPE}_fips_ref.json" +fi +if [[ -n "$LIBRESSL_VERSION" ]]; then + REF_FILE="$SOURCE_PATH/tests/${TOKENTYPE}_libressl_ref.json" +fi + +echo "Comparing with $REF_FILE" +if [[ -e "$REF_FILE" ]]; then + diff -U5 <(filter_log $REF_FILE) <(filter_log $TOKENTYPE.json) + assert $? "Unexpected results" +else + echo "ERROR: Rerefence file $REF_FILE does not exist!" + exit 1 +fi + +echo "=======================================================" +echo "Run p11test with PKCS11SPY" +echo "=======================================================" +export PKCS11SPY="$P11LIB" +$VALGRIND "$BUILD_PATH/src/tests/p11test/p11test" -v -m "$BUILD_PATH/src/pkcs11/.libs/pkcs11-spy.so" -o $TOKENTYPE.json -p $PIN +assert $? "Failed running tests" + +echo "Comparing with $REF_FILE" +diff -U5 <(filter_log $REF_FILE) <(filter_log $TOKENTYPE.json) +assert $? "Unexpected results with PKCS11 spy" + +rm $TOKENTYPE.json + +echo "=======================================================" +echo "Cleanup" +echo "=======================================================" +card_cleanup + +exit $ERRORS diff --git a/tests/test-pkcs11-tool-allowed-mechanisms.sh b/tests/test-pkcs11-tool-allowed-mechanisms.sh new file mode 100755 index 0000000000..88a48706ce --- /dev/null +++ b/tests/test-pkcs11-tool-allowed-mechanisms.sh @@ -0,0 +1,62 @@ +#!/bin/bash +SOURCE_PATH=${SOURCE_PATH:-..} + +TOKENTYPE=$1 +TOKENTYPE=${TOKENTYPE:-$TEST_PKCS11_BACKEND} + +if [ "${TOKENTYPE}" == "softokn" ]; then + echo "Generate key-pair with CKA_ALLOWED_MECHANISMS not supported" + exit 1 +elif [ "${TOKENTYPE}" == "" ]; then + TOKENTYPE=softhsm + echo "No tokentype provided, running with SoftHSM" +fi + +source $SOURCE_PATH/tests/common.sh $TOKENTYPE + +echo "=======================================================" +echo "Setup $TOKENTYPE" +echo "=======================================================" +if [[ ! -f $P11LIB ]]; then + echo "WARNING: The $TOKENTYPE is not installed. Can not run this test" + exit 77; +fi + +initialize_token + +echo "=======================================================" +echo "Generate key-pair with CKA_ALLOWED_MECHANISMS" +echo "=======================================================" +ID="05" +MECHANISMS="RSA-PKCS,SHA1-RSA-PKCS,RSA-PKCS-PSS" +# Generate key pair +$PKCS11_TOOL --keypairgen --key-type="RSA:1024" --login --pin=$PIN \ + --module="$P11LIB" --label="test" --id="$ID" \ + --allowed-mechanisms="$MECHANISMS,SHA384-RSA-PKCS" +assert $? "Failed to Generate RSA key pair" + +# Check the attributes are visible +$PKCS11_TOOL --list-objects --login --pin=$PIN \ + --module="$P11LIB" --id=$ID > objects.list +assert $? "Failed to list objects" +grep -q "Allowed mechanisms" objects.list +assert $? "Allowed mechanisms not in the object list" +grep -q "$MECHANISMS" objects.list +assert $? "The $MECHANISMS is not in the list" + +# Make sure we are not allowed to use forbidden mechanism +echo "data to sign (max 100 bytes)" > data +$PKCS11_TOOL --id $ID -s -p $PIN -m SHA256-RSA-PKCS --module $P11LIB \ + --input-file data --output-file data.sig &> sign.log +grep -q CKR_MECHANISM_INVALID sign.log +assert $? "It was possible to sign using non-allowed mechanism" +rm -f data{,.sig} + +echo "=======================================================" +echo "Cleanup" +echo "=======================================================" +token_cleanup + +rm objects.list sign.log + +exit $ERRORS diff --git a/tests/test-pkcs11-tool-import.sh b/tests/test-pkcs11-tool-import.sh new file mode 100755 index 0000000000..244db6327a --- /dev/null +++ b/tests/test-pkcs11-tool-import.sh @@ -0,0 +1,87 @@ +#!/bin/bash -x +SOURCE_PATH=${SOURCE_PATH:-..} + +TOKENTYPE=$1 +TOKENTYPE=${TOKENTYPE:-$TEST_PKCS11_BACKEND} + +if [ "${TOKENTYPE}" == "" ]; then + TOKENTYPE=softhsm + echo "No tokentype provided, running with SoftHSM" +fi + +source $SOURCE_PATH/tests/common.sh $TOKENTYPE + +echo "=======================================================" +echo "Setup $TOKENTYPE" +echo "=======================================================" +if [[ ! -f $P11LIB ]]; then + echo "WARNING: The $TOKENTYPE is not installed. Can not run this test" + exit 77; +fi +card_setup +assert $? "Failed to set up card" + +# To add Ed25519 will require OpenSSL 3.2 and use of genpkey -outpubkey +# which will also work for other keys, and will not require openssl pkey + +KEYTYPES=("RSA" "EC") +if [[ "$TOKENTYPE" == "kryoptic" ]]; then + KEYTYPES+=("ML-DSA-87" "ML-KEM-512" "SLH-DSA-SHA2-256F" "Ed25519" "Ed448") +fi + +for KEYTYPE in ${KEYTYPES[@]}; do + echo "=======================================================" + echo "Generate and import $KEYTYPE keys" + echo "=======================================================" + ID="0100" + OPTS="-pkeyopt rsa_keygen_bits:2048" + PKCS11_OPTS="--usage-sign --usage-decrypt" + if [ "$KEYTYPE" == "EC" ]; then + ID="0200" + OPTS="-pkeyopt ec_paramgen_curve:P-256" + PKCS11_OPTS="--usage-sign" + elif [ "$KEYTYPE" == "ML-DSA-87" ]; then + ID="0300" + OPTS="" + PKCS11_OPTS="--usage-sign" + elif [ "$KEYTYPE" == "ML-KEM-512" ]; then + ID="0400" + OPTS="" + PKCS11_OPTS="--usage-encapsulate" + elif [ "$KEYTYPE" == "SLH-DSA-SHA2-256F" ]; then + ID="0500" + OPTS="" + PKCS11_OPTS="--usage-sign" + elif [ "$KEYTYPE" == "Ed25519" ]; then + ID="0600" + OPTS="" + PKCS11_OPTS="--usage-sign" + elif [ "$KEYTYPE" == "Ed448" ]; then + ID="0700" + OPTS="" + PKCS11_OPTS="--usage-sign" + fi + openssl genpkey -out "${KEYTYPE}_private.der" -outform DER -algorithm $KEYTYPE $OPTS + + assert $? "Failed to generate private $KEYTYPE key" + $PKCS11_TOOL "${PRIV_ARGS[@]}" --write-object "${KEYTYPE}_private.der" --id "$ID" \ + --type privkey --label "$KEYTYPE" $PKCS11_OPTS + assert $? "Failed to write private $KEYTYPE key" + echo "Private key written" + + openssl pkey -in "${KEYTYPE}_private.der" -out "${KEYTYPE}_public.der" -pubout -inform DER -outform DER + assert $? "Failed to convert private $KEYTYPE key to public" + $PKCS11_TOOL "${PRIV_ARGS[@]}" --write-object "${KEYTYPE}_public.der" --id "$ID" \ + --type pubkey --label "$KEYTYPE" $PKCS11_OPTS + assert $? "Failed to write public $KEYTYPE key" + echo "Public key written" + + rm "${KEYTYPE}_private.der" "${KEYTYPE}_public.der" +done + +echo "=======================================================" +echo "Cleanup" +echo "=======================================================" +card_cleanup + +exit $ERRORS diff --git a/tests/test-pkcs11-tool-sign-verify.sh b/tests/test-pkcs11-tool-sign-verify.sh new file mode 100755 index 0000000000..8e604a62f0 --- /dev/null +++ b/tests/test-pkcs11-tool-sign-verify.sh @@ -0,0 +1,327 @@ +#!/bin/bash +SOURCE_PATH=${SOURCE_PATH:-..} + +TOKENTYPE=$1 +TOKENTYPE=${TOKENTYPE:-$TEST_PKCS11_BACKEND} + +if [ "${TOKENTYPE}" == "" ]; then + TOKENTYPE=softhsm + echo "No tokentype provided, running with SoftHSM" +fi + +source $SOURCE_PATH/tests/common.sh $TOKENTYPE + +echo "=======================================================" +echo "Setup $TOKENTYPE" +echo "=======================================================" +if [[ ! -f $P11LIB ]]; then + echo "WARNING: The $TOKENTYPE is not installed. Can not run this test" + exit 77; +fi +card_setup +assert $? "Failed to set up card" + +# get information about OS +source /etc/os-release || true + +echo "=======================================================" +echo "Test RSA keys" +echo "=======================================================" +for HASH in "" "SHA1" "SHA224" "SHA256" "SHA384" "SHA512"; do + RETOSSL="0" + + if [[ "$ID" == "rhel" || "$ID_LIKE" =~ ".*rhel.*" ]] && [[ "$VERSION" -gt 8 ]] && [[ "$HASH" == "SHA1" ]]; then + RETOSSL="1" + fi + # Kryoptic does not do SHA1 anymore + if [[ "$TOKENTYPE" == "kryoptic" ]] && [[ "$HASH" == "SHA1" ]]; then + continue; + fi + for SIGN_KEY in "01" "02"; do + METHOD="RSA-PKCS" + # RSA-PKCS works only on small data - generate small data: + head -c 64 data + if [[ ! -z $HASH ]]; then + METHOD="$HASH-$METHOD" + # hash- methods should work on data > 512 bytes + head -c 1024 data + fi + echo + echo "=======================================================" + echo "$METHOD: Sign & Verify (KEY $SIGN_KEY)" + echo "=======================================================" + # pkcs11-tool signature + $PKCS11_TOOL "${PRIV_ARGS[@]}" --id $SIGN_KEY -s -m $METHOD --input-file data --output-file data.sig + assert $? "Failed to Sign data" + + # OpenSSL verification + echo -n "Verification by OpenSSL: " + if [[ -z $HASH ]]; then + openssl rsautl -verify -inkey $SIGN_KEY.pub -in data.sig -pubin + # pkeyutl does not work with libressl + #openssl pkeyutl -verify -inkey $SIGN_KEY.pub -in data -sigfile data.sig -pubin + else + openssl dgst -keyform PEM -verify $SIGN_KEY.pub -${HASH,,*} \ + -signature data.sig data + fi + if [[ "$RETOSSL" == "0" ]]; then + assert $? "Failed to Verify signature using OpenSSL" + elif [[ "$?" == "0" ]]; then + assert 1 "Unexpectedly Verified signature using OpenSSL" + fi + + # pkcs11-tool verification + echo "Verification by pkcs11-tool:" + $PKCS11_TOOL "${PUB_ARGS[@]}" --id $SIGN_KEY --verify -m $METHOD \ + --input-file data --signature-file data.sig + assert $? "Failed to Verify signature using pkcs11-tool" + rm data.sig + + + if [ "${TOKENTYPE}" != "softokn" ]; then + # RSA-PKCS-OAEP decryption by pkcs11-tool returns + # error: PKCS11 function C_DecryptUpdate failed: rv = CKR_OPERATION_NOT_INITIALIZED (0x91) + METHOD="$METHOD-PSS" + # -PSS methods should work on data > 512 bytes; generate data: + head -c 1024 data + + if [[ "${TOKENTYPE}" == "softhsm" && "$HASH" == "SHA512" ]]; then + continue; # This one is broken for SoftHSM + fi + + echo + echo "=======================================================" + echo "$METHOD: Sign & Verify (KEY $SIGN_KEY)" + echo "=======================================================" + if [[ -z $HASH ]]; then + # hashing is done outside of the module. We choose here SHA256 + openssl dgst -binary -sha256 data > data.hash + HASH_ALGORITM="--hash-algorithm=SHA256" + VERIFY_DGEST="-sha256" + VERIFY_OPTS="-sigopt rsa_mgf1_md:sha256" + else + # hashing is done inside of the module + cp data data.hash + HASH_ALGORITM="" + VERIFY_DGEST="-${HASH,,*}" + VERIFY_OPTS="-sigopt rsa_mgf1_md:${HASH,,*}" + fi + # pkcs11-tool signature + $PKCS11_TOOL "${PRIV_ARGS[@]}" --id $SIGN_KEY -s -m $METHOD $HASH_ALGORITM --salt-len=-1 \ + --input-file data.hash --output-file data.sig + assert $? "Failed to Sign data" + + # OpenSSL verification + echo -n "Verification by OpenSSL: " + openssl dgst -keyform PEM -verify $SIGN_KEY.pub $VERIFY_DGEST \ + -sigopt rsa_padding_mode:pss $VERIFY_OPTS -sigopt rsa_pss_saltlen:-1 \ + -signature data.sig data + if [[ "$RETOSSL" == "0" ]]; then + assert $? "Failed to Verify signature using openssl" + elif [[ "$?" == "0" ]]; then + assert 1 "Unexpectedly Verified signature using OpenSSL" + fi + + # pkcs11-tool verification + echo "Verification by pkcs11-tool:" + $PKCS11_TOOL "${PUB_ARGS[@]}" --id $SIGN_KEY --verify -m $METHOD \ + $HASH_ALGORITM --salt-len=-1 \ + --input-file data.hash --signature-file data.sig + assert $? "Failed to Verify signature using pkcs11-tool" + rm data.{sig,hash} + fi + done + + if [ "${TOKENTYPE}" != "softokn" ]; then + METHOD="RSA-PKCS-OAEP" + # RSA-PKCS-OAEP works only on small data (input length <= k-2-2hLen) + # generate small data: + head -c 64 data + for ENC_KEY in "01" "02"; do + # SoftHSM only supports SHA1 for both hashAlg and mgf + if [[ -z $HASH ]]; then + continue + elif [[ "$HASH" != "SHA1" ]]; then + continue + fi + echo + echo "=======================================================" + echo "$METHOD: Encrypt & Decrypt (KEY $ENC_KEY)" + echo "=======================================================" + # OpenSSL Encryption + # pkeyutl does not work with libressl + openssl rsautl -encrypt -oaep -inkey $ENC_KEY.pub -in data -pubin -out data.crypt + assert $? "Failed to encrypt data using OpenSSL" + $PKCS11_TOOL --id $ENC_KEY --decrypt -p $PIN --module $P11LIB \ + -m $METHOD --hash-algorithm "SHA-1" --mgf "MGF1-SHA1" \ + --input-file data.crypt --output-file data.decrypted + assert $? "Failed to decrypt data using pkcs11-tool" + diff data{,.decrypted} + assert $? "The decrypted data do not match the original" + rm data.{crypt,decrypted} + + $PKCS11_TOOL --id $ENC_KEY --encrypt -p $PIN --module $P11LIB \ + -m $METHOD --hash-algorithm "SHA-1" --mgf "MGF1-SHA1" \ + --input-file data --output-file data.crypt + assert $? "Failed to encrypt data using pkcs11-tool" + # It would be better to decrypt with OpenSSL but we can't read the + # private key with the pkcs11-tool (yet) + $PKCS11_TOOL --id $ENC_KEY --decrypt -p $PIN --module $P11LIB \ + -m $METHOD --hash-algorithm "SHA-1" --mgf "MGF1-SHA1" \ + --input-file data.crypt --output-file data.decrypted + assert $? "Failed to decrypt data using pkcs11-tool" + diff data{,.decrypted} + assert $? "The decrypted data do not match the original" + rm data.{crypt,decrypted} + done + fi + + # Skip hashed algorithms (do not support encryption & decryption) + if [[ ! -z "$HASH" ]]; then + continue; + fi + # Skip in FIPS mode -- RSA-PKCS encryption is no longer allowed + if [[ -e "/etc/system-fips" ]]; then + continue; + fi + if [[ -f "/proc/sys/crypto/fips_enabled" && $(cat /proc/sys/crypto/fips_enabled) == "1" ]]; then + continue; + fi + METHOD="RSA-PKCS" + # RSA-PKCS works only on small data - generate small data: + head -c 64 data + for ENC_KEY in "01" "02"; do + echo + echo "=======================================================" + echo "$METHOD: Encrypt & Decrypt (KEY $ENC_KEY)" + echo "=======================================================" + # OpenSSL Encryption + openssl rsautl -encrypt -inkey $ENC_KEY.pub -in data \ + -pubin -out data.crypt + # pkeyutl does not work with libressl + #openssl pkeyutl -encrypt -inkey $ENC_KEY.pub -in data \ + # -pubin -out data.crypt + assert $? "Failed to encrypt data using OpenSSL" + # pkcs11-tool Decryption + $PKCS11_TOOL "${PRIV_ARGS[@]}" --id $ENC_KEY --decrypt -m $METHOD \ + --input-file data.crypt > data.decrypted + assert $? "Failed to Decrypt data" + diff data{,.decrypted} + assert $? "The decrypted data do not match the original" + rm data.{crypt,decrypted} + done +done + +echo "=======================================================" +echo "Test ECDSA keys" +echo "=======================================================" +# operations with ECDSA keys should work on data > 512 bytes; generate data: +head -c 1024 data +for SIGN_KEY in "03" "04"; do + METHOD="ECDSA" + + echo + echo "=======================================================" + echo "$METHOD: Sign & Verify (KEY $SIGN_KEY)" + echo "=======================================================" + openssl dgst -binary -sha256 data > data.hash + $PKCS11_TOOL "${PRIV_ARGS[@]}" --id $SIGN_KEY -s -m $METHOD \ + --input-file data.hash --output-file data.sig + assert $? "Failed to Sign data" + $PKCS11_TOOL "${PRIV_ARGS[@]}" --id $SIGN_KEY -s -m $METHOD \ + --input-file data.hash --output-file data.sig.openssl \ + --signature-format openssl + assert $? "Failed to Sign data into OpenSSL format" + + # OpenSSL verification + echo -n "Verification by OpenSSL: " + openssl dgst -keyform PEM -verify $SIGN_KEY.pub -sha256 \ + -signature data.sig.openssl data + assert $? "Failed to Verify signature using OpenSSL" + + # pkcs11-tool verification + echo "Verification by pkcs11-tool:" + $PKCS11_TOOL "${PUB_ARGS[@]}" --id $SIGN_KEY --verify -m $METHOD \ + --input-file data.hash --signature-file data.sig + assert $? "Failed to Verify signature using pkcs11-tool" + rm data.sig{,.openssl} data.hash +done + +echo "=======================================================" +echo "Test brainpool ECDSA keys" +echo "=======================================================" +# operations with ECDSA keys should work on data > 512 bytes; generate data: +head -c 1024 data +for SIGN_KEY in "13" "14"; do + # Skip brainpool tests in FIPS mode -- brainpool curves are not FIPS approved + if [[ -e "/etc/system-fips" ]]; then + continue; + fi + if [[ -f "/proc/sys/crypto/fips_enabled" && $(cat /proc/sys/crypto/fips_enabled) == "1" ]]; then + continue; + fi + # Skip if the key was not generated (token does not support this curve) + if [[ ! -f $SIGN_KEY.pub ]]; then + echo "Skipping brainpool key $SIGN_KEY: not supported by $TOKENTYPE" + continue + fi + METHOD="ECDSA" + + echo + echo "=======================================================" + echo "$METHOD: Sign & Verify (KEY $SIGN_KEY)" + echo "=======================================================" + openssl dgst -binary -sha256 data > data.hash + $PKCS11_TOOL "${PRIV_ARGS[@]}" --id $SIGN_KEY -s -m $METHOD \ + --input-file data.hash --output-file data.sig + assert $? "Failed to Sign data" + $PKCS11_TOOL "${PRIV_ARGS[@]}" --id $SIGN_KEY -s -m $METHOD \ + --input-file data.hash --output-file data.sig.openssl \ + --signature-format openssl + assert $? "Failed to Sign data into OpenSSL format" + + # OpenSSL verification + echo -n "Verification by OpenSSL: " + openssl dgst -keyform PEM -verify $SIGN_KEY.pub -sha256 \ + -signature data.sig.openssl data + assert $? "Failed to Verify signature using OpenSSL" + + # pkcs11-tool verification + echo "Verification by pkcs11-tool:" + $PKCS11_TOOL "${PUB_ARGS[@]}" --id $SIGN_KEY --verify -m $METHOD \ + --input-file data.hash --signature-file data.sig + assert $? "Failed to Verify signature using pkcs11-tool" + rm data.sig{,.openssl} data.hash +done + +echo "=======================================================" +echo "Test GENERIC keys" +echo "=======================================================" + +echo "Hello World" > data.msg + +for MECHANISM in "SHA-1-HMAC" "SHA256-HMAC" "SHA384-HMAC" "SHA512-HMAC"; do + echo + echo "=======================================================" + echo "$MECHANISM: Sign & Verify (KEY (First Found))" + echo "=======================================================" + + $PKCS11_TOOL "${PRIV_ARGS[@]}" --sign --mechanism=$MECHANISM \ + --input-file=data.msg --output-file=data.sig + assert $? "Failed to Sign data" + $PKCS11_TOOL "${PRIV_ARGS[@]}" --verify --mechanism=$MECHANISM \ + --input-file=data.msg --signature-file=data.sig + assert $? "Failed to Verify signature using pkcs11-tool" + rm data.sig +done; + +rm data.msg + +echo "=======================================================" +echo "Cleanup" +echo "=======================================================" +card_cleanup + +rm data + +exit $ERRORS diff --git a/tests/test-pkcs11-tool-sym-crypt-test.sh b/tests/test-pkcs11-tool-sym-crypt-test.sh new file mode 100755 index 0000000000..4bda25e3ae --- /dev/null +++ b/tests/test-pkcs11-tool-sym-crypt-test.sh @@ -0,0 +1,223 @@ +#!/bin/bash +SOURCE_PATH=${SOURCE_PATH:-..} + +TOKENTYPE=$1 +TOKENTYPE=${TOKENTYPE:-$TEST_PKCS11_BACKEND} + +if [ "${TOKENTYPE}" == "softokn" ]; then + echo "p11test not supported" + exit 1 +elif [ "${TOKENTYPE}" == "" ]; then + TOKENTYPE=softhsm + echo "No tokentype provided, running with SoftHSM" +fi + +source $SOURCE_PATH/tests/common.sh $TOKENTYPE + +echo "=======================================================" +echo "Setup SoftHSM" +echo "=======================================================" +if [[ ! -f $P11LIB ]]; then + echo "WARNING: The SoftHSM is not installed. Can not run this test" + exit 77; +fi + +initialize_token + +echo "=======================================================" +echo "import AES key" +echo "=======================================================" +ID2="86" +echo -n "pppppppppppppppp" > aes_128.key +# import key +if [ "${TOKENTYPE}" == "softhsm" ]; then + softhsm2-util --import aes_128.key --aes --token "SC test" --pin "$PIN" --label import_aes_128 --id "$ID2" +else + $PKCS11_TOOL "${PRIV_ARGS[@]}" --write-object aes_128.key --id "$ID2" \ + --type secrkey --label "import_aes_128" --key-type AES:16 +fi +assert $? "Fail, unable to import key" + +$PKCS11_TOOL "${PRIV_ARGS[@]}" --list-objects -l 2>/dev/null |tee > objects.list +assert $? "Failed to list objects" + +VECTOR="00000000000000000000000000000000" +echo "=======================================================" +echo " AES-CBC-PAD" +echo " OpenSSL encrypt, pkcs11-tool decrypt" +echo " pkcs11-tool encrypt, compare to openssl encrypt" +echo "=======================================================" + +echo "C_Encrypt" +dd if=/dev/urandom bs=200 count=1 >aes_plain.data 2>/dev/null +$PKCS11_TOOL "${PRIV_ARGS[@]}" --encrypt --id "$ID2" -m AES-CBC-PAD --iv "${VECTOR}" \ + --input-file aes_plain.data --output-file aes_ciphertext_pkcs11.data 2>/dev/null +assert $? "Fail/pkcs11-tool encrypt" +openssl enc -aes-128-cbc -in aes_plain.data -out aes_ciphertext_openssl.data -iv "${VECTOR}" -K "70707070707070707070707070707070" +cmp aes_ciphertext_pkcs11.data aes_ciphertext_openssl.data >/dev/null 2>/dev/null +assert $? "Fail, AES-CBC-PAD (C_Encrypt) - wrong encrypt" + +echo "C_Decrypt" +$PKCS11_TOOL "${PRIV_ARGS[@]}" --decrypt --id "$ID2" -m AES-CBC-PAD --iv "${VECTOR}" \ + --input-file aes_ciphertext_pkcs11.data --output-file aes_plain_pkcs11.data 2>/dev/null +assert $? "Fail/pkcs11-tool decrypt" +cmp aes_plain.data aes_plain_pkcs11.data >/dev/null 2>/dev/null +assert $? "Fail, AES-CBC-PAD (C_Decrypt) - wrong decrypt" + +echo "C_DecryptUpdate" +dd if=/dev/urandom bs=8131 count=3 >aes_plain.data 2>/dev/null +openssl enc -aes-128-cbc -in aes_plain.data -out aes_ciphertext_openssl.data -iv "${VECTOR}" -K "70707070707070707070707070707070" +assert $? "Fail, OpenSSL" +$PKCS11_TOOL "${PRIV_ARGS[@]}" --decrypt --id "$ID2" -m AES-CBC-PAD --iv "${VECTOR}" \ + --input-file aes_ciphertext_openssl.data --output-file aes_plain_test.data 2>/dev/null +assert $? "Fail/pkcs11-tool (C_DecryptUpdate) decrypt" +cmp aes_plain.data aes_plain_test.data >/dev/null 2>/dev/null +assert $? "Fail, AES-CBC-PAD - wrong decrypt" + +echo "C_EncryptUpdate" +$PKCS11_TOOL "${PRIV_ARGS[@]}" --encrypt --id "$ID2" -m AES-CBC-PAD --iv "${VECTOR}" \ + --input-file aes_plain.data --output-file aes_ciphertext_pkcs11.data 2>/dev/null +assert $? "Fail/pkcs11-tool encrypt" +cmp aes_ciphertext_pkcs11.data aes_ciphertext_openssl.data >/dev/null 2>/dev/null +assert $? "Fail, AES-CBC-PAD (C_EncryptUpdate) - wrong encrypt" + +echo "=======================================================" +echo " AES-ECB, AES-CBC - must fail, because the length of " +echo " the input is not multiple od block size " +echo "=======================================================" +echo -n "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU" > aes_plain.data +! $PKCS11_TOOL "${PRIV_ARGS[@]}" --encrypt --id "$ID2" -m AES-ECB --input-file aes_plain.data --output-file aes_ciphertext_pkcs11.data 2>/dev/null +assert $? "Fail, AES-ECB must not work if the input is not a multiple of the block size" +! $PKCS11_TOOL "${PRIV_ARGS[@]}" --encrypt --id "$ID2" -m AES-CBC --iv "${VECTOR}" \ + --input-file aes_plain.data --output-file aes_ciphertext_pkcs11.data 2>/dev/null +assert $? "Fail, AES-CBC must not work if the input is not a multiple of the block size" + +echo -n "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU" > aes_plain.data + +echo "=======================================================" +echo " AES-ECB" +echo " OpenSSL encrypt, pkcs11-tool decrypt" +echo " pkcs11-tool encrypt, compare to openssl encrypt" +echo "=======================================================" + +openssl enc -aes-128-ecb -nopad -in aes_plain.data -out aes_ciphertext_openssl.data -K "70707070707070707070707070707070" +assert $? "Fail/OpenSSL" +$PKCS11_TOOL "${PRIV_ARGS[@]}" --decrypt --id "$ID2" -m AES-ECB --input-file aes_ciphertext_openssl.data --output-file aes_plain_test.data 2>/dev/null +assert $? "Fail/pkcs11-tool decrypt" +cmp aes_plain.data aes_plain_test.data >/dev/null 2>/dev/null +assert $? "Fail, AES-ECB - wrong decrypt" + +$PKCS11_TOOL "${PRIV_ARGS[@]}" --encrypt --id "$ID2" -m AES-ECB --input-file aes_plain.data --output-file aes_ciphertext_pkcs11.data 2>/dev/null +assert $? "Fail/pkcs11-tool encrypt" +cmp aes_ciphertext_pkcs11.data aes_ciphertext_openssl.data >/dev/null 2>/dev/null +assert $? "Fail, AES-ECB - wrong encrypt" + +echo "=======================================================" +echo " AES-CBC" +echo " OpenSSL encrypt, pkcs11-tool decrypt" +echo " pkcs11-tool encrypt, compare to openssl encrypt" +echo "=======================================================" + +openssl enc -aes-128-cbc -nopad -in aes_plain.data -out aes_ciphertext_openssl.data -iv "${VECTOR}" -K "70707070707070707070707070707070" +assert $? "Fail/OpenSSL" +$PKCS11_TOOL "${PRIV_ARGS[@]}" --decrypt --id "$ID2" -m AES-CBC --iv "${VECTOR}" \ + --input-file aes_ciphertext_openssl.data --output-file aes_plain_test.data 2>/dev/null +assert $? "Fail/pkcs11-tool decrypt" +cmp aes_plain.data aes_plain_test.data >/dev/null 2>/dev/null +assert $? "Fail, AES-CBC - wrong decrypt" + +$PKCS11_TOOL "${PRIV_ARGS[@]}" --encrypt --id "$ID2" -m AES-CBC --iv "${VECTOR}" \ + --input-file aes_plain.data --output-file aes_ciphertext_pkcs11.data 2>/dev/null +assert $? "Fail/pkcs11-tool encrypt" +cmp aes_ciphertext_pkcs11.data aes_ciphertext_openssl.data >/dev/null 2>/dev/null +assert $? "Fail, AES-CBC - wrong encrypt" + +VECTOR="000102030405060708090a0b0c0d0e0f" +echo "=======================================================" +echo " AES-CBC, another IV" +echo " OpenSSL encrypt, pkcs11-tool decrypt" +echo " pkcs11-tool encrypt, compare to openssl encrypt" +echo "=======================================================" + +openssl enc -aes-128-cbc -nopad -in aes_plain.data -out aes_ciphertext_openssl.data -iv "${VECTOR}" -K "70707070707070707070707070707070" +assert $? "Fail/Openssl" +$PKCS11_TOOL "${PRIV_ARGS[@]}" --decrypt --id "$ID2" -m AES-CBC --iv "${VECTOR}" \ + --input-file aes_ciphertext_openssl.data --output-file aes_plain_test.data 2>/dev/null +assert $? "Fail/pkcs11-tool decrypt" +cmp aes_plain.data aes_plain_test.data >/dev/null 2>/dev/null +assert $? "Fail, AES-CBC - wrong decrypt" + +$PKCS11_TOOL "${PRIV_ARGS[@]}" --encrypt --id "$ID2" -m AES-CBC --iv "${VECTOR}" \ + --input-file aes_plain.data --output-file aes_ciphertext_pkcs11.data 2>/dev/null +assert $? "Fail/pkcs11-tool encrypt" +cmp aes_ciphertext_pkcs11.data aes_ciphertext_openssl.data >/dev/null 2>/dev/null +assert $? "Fail, AES-CBC - wrong encrypt" + +ID3="87" +echo "=======================================================" +echo " AES-GCM, compare with test vectors" +echo " plaintext vector, pkcs11-tool encrypt, compare to ciphertext & tag vector" +echo " ciphertext & tag vector, pkcs11-tool decrypt, compare to plaintext vector" +echo "=======================================================" +# Command line OpenSSL does not support AES GCM, we have to compare with validated test vectors. +# The test vectors come from https://github.com/google/boringssl/blob/master/crypto/cipher_extra/test/cipher_tests.txt lines 354-360. +KEY="feffe9928665731c6d6a8f9467308308" +IV="cafebabefacedbaddecaf888" +PT="d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39" +CT="42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091" +AAD="feedfacedeadbeeffeedfacedeadbeefabaddad2" +TAG="5bc94fbc3221a5db94fae95ae7121a47" + +echo -n $KEY | xxd -r -p > gcm_128.key +echo -n $PT | xxd -r -p > gcm_vector_plain.data +echo -n $CT | xxd -r -p > gcm_vector_ct_tag.data +echo -n $TAG | xxd -r -p >> gcm_vector_ct_tag.data + +if [ "${TOKENTYPE}" == "softhsm" ]; then + softhsm2-util --import gcm_128.key --aes --token "SC test" --pin "$PIN" --label import_aes_gcm_128 --id "$ID3" >/dev/null +else + $PKCS11_TOOL "${PRIV_ARGS[@]}" --write-object gcm_128.key --id "$ID3" \ + --type secrkey --label "import_aes_gcm_128" --key-type AES:16 +fi +assert $? "Fail, unable to import key" + +$PKCS11_TOOL "${PRIV_ARGS[@]}" --encrypt --id "$ID3" -m AES-GCM --iv "$IV" --aad "$AAD" \ + --tag-bits-len 128 --input-file gcm_vector_plain.data --output-file gcm_test_ct_tag.data 2>/dev/null +assert $? "Fail/pkcs11-tool encrypt" +cmp gcm_vector_ct_tag.data gcm_test_ct_tag.data >/dev/null 2>&1 +assert $? "Fail, AES-GCM - wrong encrypt" + +$PKCS11_TOOL "${PRIV_ARGS[@]}" --decrypt --id "$ID3" -m AES-GCM --iv "$IV" --aad "$AAD" \ + --tag-bits-len 128 --input-file gcm_vector_ct_tag.data --output-file gcm_test_plain.data 2>/dev/null +assert $? "Fail/pkcs11-tool decrypt" +cmp gcm_vector_plain.data gcm_test_plain.data >/dev/null 2>&1 +assert $? "Fail, AES-GCM - wrong decrypt" + +echo "=======================================================" +echo " AES-CTR" +echo " OpenSSL encrypt, pkcs11-tool decrypt" +echo " pkcs11-tool encrypt, compare to openssl encrypt" +echo "=======================================================" + +openssl enc -aes-128-ctr -nopad -in aes_plain.data -out aes_ciphertext_openssl.data -iv "${VECTOR}" -K "70707070707070707070707070707070" +assert $? "Fail/OpenSSL" +$PKCS11_TOOL "${PRIV_ARGS[@]}" --decrypt --id "$ID2" -m AES-CTR --iv "${VECTOR}" \ + --input-file aes_ciphertext_openssl.data --output-file aes_plain_test.data 2>/dev/null +assert $? "Fail/pkcs11-tool decrypt" +cmp aes_plain.data aes_plain_test.data >/dev/null 2>/dev/null +assert $? "Fail, AES-CTR - wrong decrypt" + +$PKCS11_TOOL "${PRIV_ARGS[@]}" --encrypt --id "$ID2" -m AES-CTR --iv "${VECTOR}" \ + --input-file aes_plain.data --output-file aes_ciphertext_pkcs11.data 2>/dev/null +assert $? "Fail/pkcs11-tool encrypt" +cmp aes_ciphertext_pkcs11.data aes_ciphertext_openssl.data >/dev/null 2>/dev/null +assert $? "Fail, AES-CTR - wrong encrypt" + +echo "=======================================================" +echo "Cleanup" +echo "=======================================================" +token_cleanup + +rm objects.list +rm aes_128.key aes_plain.data aes_plain_test.data aes_ciphertext_openssl.data aes_ciphertext_pkcs11.data aes_plain_pkcs11.data gcm_128.key gcm_vector_plain.data gcm_test_plain.data gcm_vector_ct_tag.data gcm_test_ct_tag.data +exit $ERRORS diff --git a/tests/test-pkcs11-tool-test-threads.sh b/tests/test-pkcs11-tool-test-threads.sh new file mode 100755 index 0000000000..b64c7d4e8c --- /dev/null +++ b/tests/test-pkcs11-tool-test-threads.sh @@ -0,0 +1,43 @@ +#!/bin/bash +SOURCE_PATH=${SOURCE_PATH:-..} + +TOKENTYPE=$1 +TOKENTYPE=${TOKENTYPE:-$TEST_PKCS11_BACKEND} + +if [ "${TOKENTYPE}" == "" ]; then + TOKENTYPE=softhsm + echo "No tokentype provided, running with SoftHSM" +elif [ "${TOKENTYPE}" != "softhsm" ]; then + echo "Supported only for softhsm" + exit 1 +fi + +source $SOURCE_PATH/tests/common.sh $TOKENTYPE + +# Test our PKCS #11 module here +P11LIB="$BUILD_PATH/src/pkcs11/.libs/opensc-pkcs11.so" + +echo "=======================================================" +echo "Test pkcs11 threads IN " +echo "=======================================================" +OPENSC_TOOL="$BUILD_PATH/src/tools/opensc-tool" +echo "check for opensc-tool" +if [[ -f $OPENSC_TOOL ]] ; then +echo "trying opensc-tool -a" + $OPENSC_TOOL -a + if [[ "$?" -ne "0" ]] ; then + echo "No token found, skipping Test pkcs11 threads " + exit 77 + fi +fi + +$PKCS11_TOOL --test-threads IN -L --module="$P11LIB" +assert $? "Failed running tests" + +echo "=======================================================" +echo "Test pkcs11 threads ILGISLT0 " +echo "=======================================================" +$PKCS11_TOOL --test-threads ILGISLT0 -L --module="$P11LIB" +assert $? "Failed running tests" + +exit $ERRORS diff --git a/tests/test-pkcs11-tool-test.sh b/tests/test-pkcs11-tool-test.sh new file mode 100755 index 0000000000..c423512409 --- /dev/null +++ b/tests/test-pkcs11-tool-test.sh @@ -0,0 +1,59 @@ +#!/bin/bash +SOURCE_PATH=${SOURCE_PATH:-..} + +TOKENTYPE=$1 +TOKENTYPE=${TOKENTYPE:-$TEST_PKCS11_BACKEND} + +if [ "${TOKENTYPE}" == "" ]; then + TOKENTYPE=softhsm + echo "No tokentype provided, running with SoftHSM" +fi + +source $SOURCE_PATH/tests/common.sh $TOKENTYPE + +echo "=======================================================" +echo "Setup $TOKENTYPE" +echo "=======================================================" +if [[ ! -f $P11LIB ]]; then + echo "WARNING: The $TOKENTYPE is not installed. Can not run this test" + exit 77; +fi + +card_setup +assert $? "Failed to set up card" + +echo "=======================================================" +echo "Test" +echo "=======================================================" +if [ "${TOKENTYPE}" == "softhsm" ]; then + #SoftHSM only supports CKM_RSA_PKCS_OAEP with --hash-algorithm SHA-1 and --mgf MGF1-SHA1 + # and it accepts pSourceData, but does not use, so decrypt fails, See pkcs11-tool.c comments + $PKCS11_TOOL --test -p "${PIN}" --module "${P11LIB}" --hash-algorithm "SHA-1" --mgf "MGF1-SHA1" + assert $? "Failed running tests" +else + $PKCS11_TOOL --test -p "${PIN}" --module "${P11LIB}" + assert $? "Failed running tests" +fi + +echo "=======================================================" +echo "Test objects URI" +echo "=======================================================" +$PKCS11_TOOL --module "${P11LIB}" -O 2>/dev/null | grep 'uri:' 2>/dev/null >/dev/null +assert $? "Failed running objects URI tests" +$PKCS11_TOOL --module "${P11LIB}" -O 2>/dev/null | grep 'uri:' | awk -F 'uri:' '{print $2}' | tr -d ' ' | grep ^"pkcs11:" 2>/dev/null >/dev/null +assert $? "Failed running objects URI tests" + +echo "=======================================================" +echo "Test slots URI" +echo "=======================================================" +$PKCS11_TOOL --module "${P11LIB}" -L 2>/dev/null | grep 'uri' 2>/dev/null >/dev/null +assert $? "Failed running slots URI tests" +$PKCS11_TOOL --module "${P11LIB}" -O 2>/dev/null | grep 'uri' | awk -F 'uri*:' '{print $2}' | tr -d ' ' | grep ^"pkcs11:" 2>/dev/null >/dev/null +assert $? "Failed running slots URI tests" + +echo "=======================================================" +echo "Cleanup" +echo "=======================================================" +card_cleanup + +exit "${ERRORS}" diff --git a/tests/test-pkcs11-tool-unwrap-wrap-interoperability-test.sh b/tests/test-pkcs11-tool-unwrap-wrap-interoperability-test.sh new file mode 100755 index 0000000000..c0e48538cc --- /dev/null +++ b/tests/test-pkcs11-tool-unwrap-wrap-interoperability-test.sh @@ -0,0 +1,192 @@ +#!/bin/bash +SOURCE_PATH=${SOURCE_PATH:-..} + +get_priv_args() { + local target=$1 + case "$target" in + Kryoptic) + ARGS=("${PRIV_ARGS_KRYOPTIC[@]}") + ;; + Softokn) + ARGS=("${PRIV_ARGS_SOFTOKN[@]}") + ;; + SoftHSM) + ARGS=("${PRIV_ARGS_SOFTHSM[@]}") + ;; + *) + echo "Unknown target: $target" >&2 + return 1 + ;; + esac +} + +function test_unwrapped_aes_encryption() { + TARGET=$1 + AES_256_KEY=$2 + KEY_ID=$3 + IV="00000000000000000000000000000000" + (printf '\xAB%.0s' {1..64};) > aes_plain.data + get_priv_args "$TARGET" + + echo "Testing unwrapped key with encryption" + + # Encrypt with openssl + openssl enc -aes-256-cbc -in aes_plain.data -out aes_ciphertext_openssl.data -iv $IV -K $AES_256_KEY + assert $? "AES CBC OpenSSL encryption failed" + + # Encrypt with pkcs11-tool + $PKCS11_TOOL "${ARGS[@]}" --encrypt --id $KEY_ID -m AES-CBC-PAD --iv $IV \ + --input-file aes_plain.data --output-file aes_ciphertext_pkcs11.data + assert $? "Fail/pkcs11-tool encrypt" + + # Compare ciphertexts + cmp aes_ciphertext_pkcs11.data aes_ciphertext_openssl.data >/dev/null 2>/dev/null + assert $? "AES CBC encrypted ciphertexts do not match" + + rm aes_ciphertext_openssl.data aes_ciphertext_pkcs11.data aes_plain.data +} + +# Initialize tokens with their custom pkcs11-tool arguments +source $SOURCE_PATH/tests/common.sh softokn +initialize_token +source $SOURCE_PATH/tests/common.sh softhsm +initialize_token +source $SOURCE_PATH/tests/common.sh kryoptic +initialize_token + +# Generate AES and RSA keys for wrapping/unwrapping +ID_AES_WRAP="0100" +ID_RSA_WRAP="0200" +ID_RSA_WRAPPED="0201" +ID_RSA_UNWRAPPED="0202" + +AES_WRAP_KEY="7070707070707070707070707070707070707070707070707070707070707070" +echo -n $AES_WRAP_KEY | xxd -p -r > aes_wrap.key +IV="00000000000000000000000000000000" + +openssl genpkey -out "rsa_private.der" -outform DER -algorithm RSA -pkeyopt rsa_keygen_bits:1024 +openssl pkey -in "rsa_private.der" -out "rsa_public.der" -pubout -inform DER -outform DER + +# import keys +for TARGET in SoftHSM Kryoptic Softokn; do + get_priv_args "$TARGET" + # Write AES key for wrapping + $PKCS11_TOOL "${ARGS[@]}" --write-object aes_wrap.key --id $ID_AES_WRAP \ + --type secrkey --key-type AES:32 --usage-wrap --extractable --label aes-32-wrapping-key + assert $? "Failed to write AES key to $TARGET" + + # Write RSA private key for unwrapping + $PKCS11_TOOL "${ARGS[@]}" --write-object rsa_private.der --id $ID_RSA_WRAP \ + --type privkey --usage-wrap --usage-decrypt --label rsa-wrapping-key + assert $? "Failed to write RSA private key to $TARGET" + + # Write RSA public key for wrapping + $PKCS11_TOOL "${ARGS[@]}" --write-object rsa_public.der --id $ID_RSA_WRAP \ + --type pubkey --usage-wrap --usage-decrypt --label rsa-wrapping-key + assert $? "Failed to write RSA public key to $TARGET" + + # Generate RSA key to be wrapped/unwrapped + $PKCS11_TOOL "${ARGS[@]}" --keypairgen --key-type rsa:2048 --id $ID_RSA_WRAPPED \ + --usage-decrypt --extractable --label rsa-key + assert $? "Failed to Generate RSA key" +done + +echo "=======================================================" +echo " Wrap/Unwrap of secret keys" +echo "=======================================================" +# Generate key to be wrapped/unwrapped +ID_AES="0101" +AES_KEY="ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB" +echo -n $AES_KEY | xxd -p -r > aes.key + +for MECH in AES-CBC AES-KEY-WRAP RSA-PKCS; do + echo "-------------------------------------------------------" + echo " $MECH Wrap/Unwrap of secret key test" + echo "-------------------------------------------------------" + ID_WRAP=$ID_AES_WRAP + if [ "$MECH" == "RSA-PKCS" ]; then + ID_WRAP=$ID_RSA_WRAP + fi + for WRAPPER in Kryoptic Softokn; do # SoftHSM removed temporarily as it crashes + for UNWRAPPER in SoftHSM Kryoptic Softokn; do + if [ "$WRAPPER" == "$UNWRAPPER" ]; then + continue; + fi + if [ "$UNWRAPPER" == "SoftHSM" ] && [ $MECH == "AES-CBC" ]; then + continue; + fi + echo "-------------------------------------------------------" + echo " $WRAPPER Wrap -> $UNWRAPPER Unwrap" + echo "-------------------------------------------------------" + get_priv_args "$WRAPPER" + # 1. Load key + $PKCS11_TOOL "${ARGS[@]}" --write-object aes.key --id $ID_AES --type secrkey --key-type AES:32 \ + --usage-decrypt --extractable --label "stored-aes-32" + assert $? "Failed to write AES key on $WRAPPER" + # 2. Wrap + $PKCS11_TOOL "${ARGS[@]}" --wrap -m $MECH --id $ID_WRAP --iv $IV --application-id $ID_AES \ + --output-file wrapped_key.data + assert $? "Failed to wrap AES key with $MECH from $WRAPPER" + + get_priv_args "$UNWRAPPER" + #3. Unwrap + $PKCS11_TOOL "${ARGS[@]}" --unwrap -m $MECH --id $ID_WRAP --iv $IV --application-id $ID_AES \ + --key-type AES: --input-file wrapped_key.data --usage-decrypt --extractable + assert $? "Failed to unwrap AES key with $MECH by $UNWRAPPER" + + # 4. Test unwrapped key with encryption + test_unwrapped_aes_encryption $UNWRAPPER $AES_KEY $ID_AES + + # 5. Clean up + $PKCS11_TOOL "${ARGS[@]}" --delete-object --type secrkey --id $ID_AES + get_priv_args "$WRAPPER" + $PKCS11_TOOL "${ARGS[@]}" --delete-object --type secrkey --id $ID_AES + rm wrapped_key.data + done + done +done + +echo "=======================================================" +echo " Wrap/Unwrap of private keys" +echo "=======================================================" + +for MECH in AES-CBC-PAD; do + ID_WRAP=$ID_AES_WRAP + + for WRAPPER in Kryoptic Softokn; do + for UNWRAPPER in Kryoptic Softokn; do + if [ "$WRAPPER" == "$UNWRAPPER" ]; then + continue; + fi + if { [[ "$MECH" == "AES-CBC-PAD" ]] && ([[ "$WRAPPER" == "SoftHSM" ]] || [[ "$UNWRAPPER" == "SoftHSM" ]]); } || \ + { [[ "$MECH" == "AES-KEY-WRAP-PAD" ]] && ([[ "$WRAPPER" == "Kryoptic" ]] || [[ "$UNWRAPPER" == "Kryoptic" ]]); }; then + continue + fi + echo "-------------------------------------------------------" + echo " $MECH: $WRAPPER Wrap -> $UNWRAPPER Unwrap" + echo "-------------------------------------------------------" + # Wrap + get_priv_args "$WRAPPER" + $PKCS11_TOOL "${ARGS[@]}" --wrap -m $MECH --id $ID_AES_WRAP --iv $IV \ + --application-id $ID_RSA_WRAPPED --output-file rsa_wrapped_key.data + assert $? "Failed to wrap RSA key by $WRAPPER" + # Unwrap + get_priv_args "$UNWRAPPER" + $PKCS11_TOOL "${ARGS[@]}" --unwrap -m $MECH --id $ID_AES_WRAP --iv $IV \ + --application-id $ID_RSA_UNWRAPPED --key-type RSA:2048 --input-file rsa_wrapped_key.data + assert $? "Failed to unwrap RSA key with $MECH by $UNWRAPPER" + rm rsa_wrapped_key.data + + # Remove unwrapped RSA key + $PKCS11_TOOL "${PRIV_ARGS[@]}" --delete-object --type privkey --id $ID_RSA_UNWRAPPED + done + done +done + + +echo "=======================================================" +echo "Cleanup" +echo "=======================================================" +rm aes_wrap.key rsa_private.der rsa_public.der aes.key + +exit $ERRORS diff --git a/tests/test-pkcs11-tool-unwrap-wrap-test.sh b/tests/test-pkcs11-tool-unwrap-wrap-test.sh new file mode 100755 index 0000000000..ab43e4f7c7 --- /dev/null +++ b/tests/test-pkcs11-tool-unwrap-wrap-test.sh @@ -0,0 +1,473 @@ +#!/bin/bash +SOURCE_PATH=${SOURCE_PATH:-..} + +function compare_keys() { + RET=$1 + EXTRACTED=$2 + PLAIN=$3 + + if [ $RET == 0 ]; then + cmp $EXTRACTED $PLAIN >/dev/null 2>/dev/null + assert $? "Extracted key does not match the input key" + else + # softokn and kryoptic keys are extractable but sensitive + echo "Key cannot be read in plaintext" + fi +} + +function test_unwrapped_aes_encryption() { + AES_256_KEY=$1 + KEY_ID=$2 + IV="00000000000000000000000000000000" + (printf '\xAB%.0s' {1..64};) > aes_plain.data + + echo "Testing unwrapped key with encryption" + + # Encrypt with openssl + openssl enc -aes-256-cbc -in aes_plain.data -out aes_ciphertext_openssl.data -iv $IV -K $AES_256_KEY + assert $? "AES CBC OpenSSL encryption failed" + + # Encrypt with pkcs11-tool + $PKCS11_TOOL "${PRIV_ARGS[@]}" --encrypt --id $KEY_ID -m AES-CBC-PAD --iv $IV \ + --input-file aes_plain.data --output-file aes_ciphertext_pkcs11.data + assert $? "Fail/pkcs11-tool encrypt" + + # Compare ciphertexts + cmp aes_ciphertext_pkcs11.data aes_ciphertext_openssl.data >/dev/null 2>/dev/null + assert $? "AES CBC encrypted ciphertexts do not match" + + rm aes_ciphertext_openssl.data aes_ciphertext_pkcs11.data aes_plain.data +} + +TOKENTYPE=$1 +TOKENTYPE=${TOKENTYPE:-$TEST_PKCS11_BACKEND} + +if [ "${TOKENTYPE}" == "" ]; then + TOKENTYPE=softhsm + echo "No tokentype provided, running with SoftHSM" +fi + +source $SOURCE_PATH/tests/common.sh $TOKENTYPE + +echo "=======================================================" +echo "Setup $TOKENTYPE" +echo "=======================================================" +if [[ ! -f $P11LIB ]]; then + echo "WARNING: The SoftHSM is not installed. Can not run this test" + exit 77; +fi + +initialize_token + +# create AES key +AES_256_KEY="7070707070707070707070707070707070707070707070707070707070707070" +echo -n $AES_256_KEY | xxd -p -r > aes.key + +echo "=======================================================" +echo " RSA Wrap/Unwrap of secret key tests" +echo "=======================================================" +ID_RSA_WRAP="85" # RSA wrapping key +ID_GENERIC_UNWRAPPED_1="95" # GENERIC key +ID_AES_UNWRAPPED_1="96" # AES key + +# Generate RSA key for unwrap/wrap operation +$PKCS11_TOOL "${PRIV_ARGS[@]}" --keypairgen --key-type rsa:1024 --id $ID_RSA_WRAP --usage-wrap --usage-decrypt --label rsa-wrapping-key +assert $? "Failed to Generate RSA key" + +# Export public key +$PKCS11_TOOL "${PRIV_ARGS[@]}" --read-object --type pubkey --id $ID_RSA_WRAP -o rsa_pub.key +assert $? "Failed to export public key" + +echo "-------------------------------------------------------" +echo " RSA-PKCS Unwrap generic key test" +echo "-------------------------------------------------------" + +# Wrap with OpenSSL +openssl rsautl -encrypt -pubin -keyform der -inkey rsa_pub.key -in aes.key -out openssl_wrapped.data +assert $? "OpenSSL failed wrap AES key" + +# Unwrap with pkcs11-tool as generic key +$PKCS11_TOOL "${PRIV_ARGS[@]}" --unwrap -m RSA-PKCS --id $ID_RSA_WRAP -i openssl_wrapped.data --key-type GENERIC: \ + --application-id $ID_GENERIC_UNWRAPPED_1 --application-label "unwrap-generic-ex-with-rsa-pkcs" --extractable 2>/dev/null +assert $? "RSA-PKCS unwrap GENERIC key failed" + +# Compare original and unwrapped key +$PKCS11_TOOL "${PRIV_ARGS[@]}" --id $ID_GENERIC_UNWRAPPED_1 --read-object --type secrkey --output-file generic_extracted.key +compare_keys $? generic_extracted.key aes.key + +echo "-------------------------------------------------------" +echo " RSA-PKCS Unwrap AES key test" +echo "-------------------------------------------------------" + +# Unwrap with pkcs11-tool as AES key +$PKCS11_TOOL "${PRIV_ARGS[@]}" --unwrap -m RSA-PKCS --id $ID_RSA_WRAP -i openssl_wrapped.data --key-type AES: \ + --application-id $ID_AES_UNWRAPPED_1 --application-label "unwrap-aes-with-rsa-pkcs" --extractable 2>/dev/null +assert $? "RSA-PKCS unwrap AES key failed" + +# Read value of unwrapped key +$PKCS11_TOOL "${PRIV_ARGS[@]}" --id $ID_AES_UNWRAPPED_1 --read-object --type secrkey --output-file aes_extracted.key + +# Compare original and unwrapped key +compare_keys $? aes_extracted.key aes.key + +# Check if AES key was correctly unwrapped with encryption +test_unwrapped_aes_encryption $AES_256_KEY $ID_AES_UNWRAPPED_1 + +echo "-------------------------------------------------------" +echo " RSA-PKCS Wrap AES key test" +echo "-------------------------------------------------------" + +# Wrap with pkcs11-tool +$PKCS11_TOOL "${PRIV_ARGS[@]}" --wrap -m RSA-PKCS --id $ID_RSA_WRAP --application-id $ID_AES_UNWRAPPED_1 --output-file pkcs11_wrapped.data +assert $? "Unable to wrap with RSA-PKCS" + +# Compare keys with decryption +$PKCS11_TOOL "${PRIV_ARGS[@]}" --decrypt -m RSA-PKCS --id $ID_RSA_WRAP --input-file pkcs11_wrapped.data --output-file aes_wrapped_decrypted.key +assert $? "Unable to decrypt wrapped key" +cmp aes_wrapped_decrypted.key aes.key >/dev/null 2>/dev/null +assert $? "Wrapped key after decipher does not match the original key" + +rm openssl_wrapped.data generic_extracted.key pkcs11_wrapped.data aes_wrapped_decrypted.key aes_extracted.key + +if [ "${TOKENTYPE}" != "softokn" ]; then + echo "-------------------------------------------------------" + echo " RSA-PKCS-OAEP Unwrap generic key test" + echo "-------------------------------------------------------" + # RSA-PKCS-OAEP mechanism takes both a hash algorithm and MGF algorithm as parameters. + # For now we use SHA1, although it has been deprecated by NIST, because SoftHSM only supports SHA1 hash with OAEP currently. + # Known issue: https://github.com/softhsm/SoftHSMv2/issues/474 . When this issue is fixed, we shall replace with SHA256 or higher. + + OSSL_OAEP_HASH_ALG="sha1" + P11_OAEP_HASH_ALG="SHA-1" + P11_OAEP_MGF_ALG="MGF1-SHA1" + + # Identifiers (pkcs11-tool --application-id argument) of PKCS11 key objects to be created + ID_GENERIC_UNWRAPPED_2="97" + ID_AES_UNWRAPPED_3="98" + + # Wrap with OpenSSL + openssl pkeyutl -encrypt -pubin -keyform DER -inkey rsa_pub.key -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:$OSSL_OAEP_HASH_ALG -pkeyopt rsa_mgf1_md:$OSSL_OAEP_HASH_ALG -in aes.key -out openssl_wrapped.data + assert $? "OpenSSL failed wrap AES key" + + # Unwrap with pkcs11-tool as generic key + $PKCS11_TOOL "${PRIV_ARGS[@]}" --unwrap -m RSA-PKCS-OAEP --hash-algorithm $P11_OAEP_HASH_ALG --mgf $P11_OAEP_MGF_ALG --id $ID_RSA_WRAP -i openssl_wrapped.data --key-type GENERIC: \ + --extractable --application-id $ID_GENERIC_UNWRAPPED_2 --application-label "unwrap-aes-ex-with-rsa-oaep" 2>/dev/null + assert $? "RSA-PKCS-OAEP unwrap GENERIC key failed" + + # Compare original and unwrapped key + $PKCS11_TOOL "${PRIV_ARGS[@]}" --id $ID_GENERIC_UNWRAPPED_2 --read-object --type secrkey --output-file generic_extracted.key + compare_keys $? generic_extracted.key aes.key + + echo "-------------------------------------------------------" + echo " RSA-PKCS-OAEP Unwrap AES key test" + echo "-------------------------------------------------------" + + # Unwrap with pkcs11-tool as AES key + $PKCS11_TOOL "${PRIV_ARGS[@]}" --unwrap -m RSA-PKCS-OAEP --hash-algorithm $P11_OAEP_HASH_ALG --mgf $P11_OAEP_MGF_ALG --id $ID_RSA_WRAP -i openssl_wrapped.data --key-type AES: \ + --extractable --application-id $ID_AES_UNWRAPPED_3 --application-label "unwrap-aes-non-ex-with-rsa-oaep" 2>/dev/null + assert $? "RSA-PKCS-OAEP unwrap AES key failed" + + # Compare original and unwrapped key + $PKCS11_TOOL "${PRIV_ARGS[@]}" --id $ID_AES_UNWRAPPED_1 --read-object --type secrkey --output-file aes_extracted.key + compare_keys $? aes_extracted.key aes.key + + # Check if AES key was correctly unwrapped with encryption + test_unwrapped_aes_encryption $AES_256_KEY $ID_AES_UNWRAPPED_3 + + echo "-------------------------------------------------------" + echo " RSA-PKCS-OAEP Wrap AES key test" + echo "-------------------------------------------------------" + + # Wrap with pkcs11-tool + $PKCS11_TOOL "${PRIV_ARGS[@]}" --wrap -m RSA-PKCS-OAEP --id $ID_RSA_WRAP --hash-algorithm $P11_OAEP_HASH_ALG --mgf $P11_OAEP_MGF_ALG --application-id $ID_GENERIC_UNWRAPPED_2 --output-file pkcs11_wrapped.data + assert $? "Unable to wrap with RSA-PKCS-OAEP" + + # Compare keys with decryption + $PKCS11_TOOL "${PRIV_ARGS[@]}" --decrypt -m RSA-PKCS-OAEP --hash-algorithm $P11_OAEP_HASH_ALG --mgf $P11_OAEP_MGF_ALG --id $ID_RSA_WRAP --input-file pkcs11_wrapped.data --output-file aes_wrapped_decrypted.key + assert $? "Fail, unable to decrypt wrapped key" + cmp aes_wrapped_decrypted.key aes.key >/dev/null 2>/dev/null + assert $? "Wrapped key after decipher does not match the original key" + + rm openssl_wrapped.data generic_extracted.key pkcs11_wrapped.data aes_wrapped_decrypted.key aes_extracted.key + +fi + +rm rsa_pub.key + +echo "=======================================================" +echo " AES Wrap/Unwrap of secret key tests" +echo "=======================================================" +ID_AES_WRAP="0101" # AES wrapping key +ID_AES_UNWRAPPED_4="0102" # AES 256 CBC +ID_AES_UNWRAPPED_5="0103" # AES-KEY-WRAP +ID_AES_UNWRAPPED_6="0104" # AES-KEY-WRAP-PAD + +is_openssl_3=$(openssl version | grep "OpenSSL 3.") + +# Generate AES key for unwrap/wrap operation +AES_WRAP=$(head /dev/urandom | sha256sum | head -c 64) +echo -n $AES_WRAP | xxd -p -r > aes_kek.key +$PKCS11_TOOL "${PRIV_ARGS[@]}" --write-object aes_kek.key --id $ID_AES_WRAP --type secrkey \ + --key-type AES:32 --usage-wrap --extractable --label aes-32-wrapping-key +assert $? "Failed to write AES key" + +if [[ "$TOKENTYPE" != "softhsm" ]]; then + # CKM_AES_CBC -- SoftHSM2 AES CBC wrapping currently has a bug, the IV is not correctly used. Only IV=0 will work --* + # https://github.com/softhsm/SoftHSMv2/issues/782 + IV="00000000000000000000000000000000" + + echo "-------------------------------------------------------" + echo " AES 256 CBC Unwrap AES key test" + echo "-------------------------------------------------------" + # Wrap with OpenSSL + openssl enc -aes-256-cbc -e -K $AES_WRAP -iv $IV -in aes.key -out openssl_wrapped.data -nopad + assert $? "OpenSSL / Failed to AES CBC encrypt AES key" + + if [ "${TOKENTYPE}" == "softhsm" ]; then + echo "SoftHSM2 currently does not support CKM_AES_CBC unwrapping" + # SoftHSM does not have the wrapped key already, write it for the wrap test + $PKCS11_TOOL "${PRIV_ARGS[@]}" --write-object aes.key --id $ID_AES_UNWRAPPED_4 --type secrkey --key-type AES:32 \ + --usage-decrypt --extractable --label "stored-aes-32-cbc" + assert $? "PKCS11 / Failed to write AES key" + else + # Unwrap with pkcs11-tool + $PKCS11_TOOL "${PRIV_ARGS[@]}" --unwrap -m AES-CBC --id $ID_AES_WRAP --iv $IV --application-id $ID_AES_UNWRAPPED_4 \ + --key-type AES: --input-file openssl_wrapped.data --extractable --application-label "unwrap-aes-with-aes-32-cbc" + assert $? "PKCS11 / Failed to AES CBC unwrap AES key" + + # Compare original and unwrapped key + $PKCS11_TOOL "${PRIV_ARGS[@]}" --read-object --type secrkey --id $ID_AES_UNWRAPPED_4 --output-file unwrapped.key + compare_keys $? aes.key unwrapped.key + + # Check if AES key was correctly unwrapped with encryption + test_unwrapped_aes_encryption $AES_256_KEY $ID_AES_UNWRAPPED_4 + fi + echo "-------------------------------------------------------" + echo " AES 256 CBC Wrap AES key test" + echo "-------------------------------------------------------" + # Wrap with OpenSSL + $PKCS11_TOOL "${PRIV_ARGS[@]}" --wrap -m AES-CBC --id $ID_AES_WRAP --iv $IV --application-id $ID_AES_UNWRAPPED_4 \ + --output-file pkcs11_wrapped.data + assert $? "Fail, unable to wrap" + + # Compare OpenSSL and pkcs11-tool wrapped keys + cmp pkcs11_wrapped.data openssl_wrapped.data >/dev/null 2>/dev/null + assert $? "Fail, AES-CBC - wrapped key incorrect" +else + echo "Not supported" +fi + +if [[ "$TOKENTYPE" != "softhsm" ]]; then + # SoftHSM2 currently doesn't support CKM_AES_CBC_PAD as a wrapping + echo "-------------------------------------------------------" + echo " AES 256 CBC PAD Wrap AES test" + echo "-------------------------------------------------------" + IV="000102030405060708090A0B0C0D0E0F" + # Wrap with OpenSSL + openssl enc -aes-256-cbc -e -K $AES_WRAP -iv $IV -in aes.key -out openssl_wrapped.data + assert $? "OpenSSL / Failed to AES CBC encrypt AES key" + + # Wrap with pkcs11-tool + $PKCS11_TOOL "${PRIV_ARGS[@]}" --wrap -m AES-CBC-PAD --id $ID_AES_WRAP --iv $IV --application-id $ID_AES_UNWRAPPED_4 --output-file pkcs11_wrapped.data + assert $? "PKCS11 / Failed to AES CBC PAD wrap AES key" + + # Compare OpenSSL and pkcs11-tool wrapped keys + cmp pkcs11_wrapped.data openssl_wrapped.data 2>&1 >/dev/null + assert $? "AES 256 CBC PAD wrapped keys do not match" +fi + +if [[ -n $is_openssl_3 && "$TOKENTYPE" != "softhsm" ]]; then + echo "-------------------------------------------------------" + echo "AES-KEY-WRAP Wrap AES test" + echo "-------------------------------------------------------" + # RSA Key + # --AES-KEY-WRAP is not suitable for asymmetric key wrapping since the length of the encoded private key is likely not aligned to 8 bytes + IV="a6a6a6a6a6a6a6a6" + + # Wrap with OpenSSL + openssl enc -id-aes256-wrap -e -K $AES_WRAP -iv $IV -in aes.key -out openssl_wrapped.data + assert $? "OpenSSL / Failed to AES KEY WRAP wrap AES key" + + # Wrap with pkcs11-tool + $PKCS11_TOOL "${PRIV_ARGS[@]}" --wrap -m AES-KEY-WRAP --id $ID_AES_WRAP --iv $IV --application-id $ID_AES_UNWRAPPED_4 \ + --output-file pkcs11_wrapped.data + assert $? "PKCS11 / Failed to AES KEY WRAP wrap AES key" + + # Compare OpenSSL and pkcs11-tool wrapped keys + cmp pkcs11_wrapped.data openssl_wrapped.data 2>&1 >/dev/null + assert $? "AES-KEY-WRAP wrapped keys do not match" + + echo "-------------------------------------------------------" + echo "AES-KEY-WRAP Unwrap AES key test" + echo "-------------------------------------------------------" + # Unwrap with pkcs11-tool + $PKCS11_TOOL "${PRIV_ARGS[@]}" --unwrap -m AES-KEY-WRAP --id $ID_AES_WRAP --iv $IV --application-id $ID_AES_UNWRAPPED_5 \ + --key-type AES: --input-file pkcs11_wrapped.data --extractable --application-label "unwrap-aes-with-aes-key-wrap" + assert $? "PKCS11 / Failed to AES KEY WRAP wrap unwrap AES key" + + # Read value of unwrapped key + $PKCS11_TOOL "${PRIV_ARGS[@]}" --read-object --type secrkey --id $ID_AES_UNWRAPPED_5 --output-file unwrapped.key + + # Compare original and unwrapped key + compare_keys $? aes.key unwrapped.key + + # Check if AES key was correctly unwrapped with encryption + test_unwrapped_aes_encryption $AES_256_KEY $ID_AES_UNWRAPPED_4 + + if [[ "$TOKENTYPE" != "kryoptic" ]]; then + echo "-------------------------------------------------------" + echo "AES-KEY-WRAP-PAD Wrap AES key test" + echo "-------------------------------------------------------" + IV="a65959a6" + + # Wrap with OpenSSL + openssl enc -aes256-wrap-pad -e -K $AES_WRAP -iv $IV -in aes.key -out openssl_wrapped.data + assert $? "OpenSSL failed wrap AES key" + + # Wrap with pkcs11-tool + $PKCS11_TOOL "${PRIV_ARGS[@]}" --wrap -m AES-KEY-WRAP-PAD --id $ID_AES_WRAP --iv $IV \ + --application-id $ID_AES_UNWRAPPED_4 --output-file pkcs11_wrapped.data + assert $? "PKCS11 / Failed to AES KEY WRAP PAD wrap AES key" + + # Compare OpenSSL and pkcs11-tool wrapped keys + if [[ "$TOKENTYPE" != "softokn" ]]; then + cmp pkcs11_wrapped.data openssl_wrapped.data 2>&1 >/dev/null + assert $? "AES-KEY-WRAP-PAD wrapped keys do not match" + else + echo "Comparing OpenSSL and pkcs11-tool wrapped keys for softokn fails" + fi + + echo "-------------------------------------------------------" + echo "AES-KEY-WRAP-PAD Unwrap AES key test" + echo "-------------------------------------------------------" + # Unwrap with pkcs11-tool + $PKCS11_TOOL "${PRIV_ARGS[@]}" --unwrap -m AES-KEY-WRAP-PAD --id $ID_AES_WRAP --iv $IV --application-id $ID_AES_UNWRAPPED_6 \ + --key-type AES: --input-file pkcs11_wrapped.data --extractable --application-label "unwrap-aes-with-aes-key-wrap-pad" + assert $? "PKCS11 / Failed to AES KEY WRAP PAD wrap unwrap AES key" + + # Read value of unwrapped key + $PKCS11_TOOL "${PRIV_ARGS[@]}" --read-object --type secrkey --id $ID_AES_UNWRAPPED_6 --output-file unwrapped.key + + # Compare original and unwrapped key + compare_keys $? aes.key unwrapped.key + + # Check if AES key was correctly unwrapped with encryption + test_unwrapped_aes_encryption $AES_256_KEY $ID_AES_UNWRAPPED_4 + fi +fi + +rm -f aes.key aes_kek.key pkcs11_wrapped.data openssl_wrapped.data unwrapped.key + +echo "=======================================================" +echo " AES Wrap/Unwrap of private key tests" +echo "=======================================================" + +function test_unwrapped_rsa_pkcs_decryption() { + ID_RSA_WRAPPED=$1 + ID_RSA_UNWRAPPED=$2 + + echo "Testing unwrapped key with decryption" + + (printf '\xAB%.0s' {1..64};) > plaintext.data + # Encrypt with original RSA key + $PKCS11_TOOL "${PRIV_ARGS[@]}" --encrypt --id $ID_RSA_WRAPPED -m RSA-PKCS \ + --input-file plaintext.data --output-file ciphertext.data + assert $? "Fail/pkcs11-tool encrypt" + # Decrypt with unwrapped private key + $PKCS11_TOOL "${PRIV_ARGS[@]}" --decrypt --id $ID_RSA_UNWRAPPED -m RSA-PKCS \ + --input-file ciphertext.data > decrypted_plaintext.data + # Compare plaintexts + cmp plaintext.data decrypted_plaintext.data >/dev/null 2>/dev/null + assert $? "RSA decrypted plaintexts do not match" + rm plaintext.data decrypted_plaintext.data ciphertext.data +} + +ID_AES_WRAP="0200" +ID_RSA_WRAPPED="0201" +ID_RSA_UNWRAPPED="0202" +IV="00000000000000000000000000000000" + +# Generate AES key for unwrap/wrap operation +AES_WRAP=$(head /dev/urandom | sha256sum | head -c 64) +echo -n $AES_WRAP | xxd -p -r > aes_kek.key +$PKCS11_TOOL "${PRIV_ARGS[@]}" --write-object aes_kek.key --id $ID_AES_WRAP --type secrkey \ + --key-type AES:32 --usage-wrap --usage-decrypt --extractable --label aes-32-wrapping-key +assert $? "Failed to write AES key" + +# Generate RSA key to be wrapped/unwrapped +$PKCS11_TOOL "${PRIV_ARGS[@]}" --keypairgen --key-type rsa:2048 --id $ID_RSA_WRAPPED --usage-decrypt --extractable --label rsa-key +assert $? "Failed to Generate RSA key" + +# AES-KEY-WRAP, AES-KEY-WRAP-PAD, AES-CBC, AES-CBC-PAD +if [[ -n $is_openssl_3 ]]; then + if [[ "$TOKENTYPE" == "softhsm" ]]; then + echo "-------------------------------------------------------" + echo "AES-KEY-WRAP Wrap/Unwrap RSA key test" + echo "-------------------------------------------------------" + # Wrap + $PKCS11_TOOL "${PRIV_ARGS[@]}" --wrap -m AES-KEY-WRAP --id $ID_AES_WRAP --iv $IV \ + --application-id $ID_RSA_WRAPPED --output-file rsa_wrapped_key.data + assert $? "Failed to wrap RSA key" + # Unwrap + $PKCS11_TOOL "${PRIV_ARGS[@]}" --unwrap -m AES-KEY-WRAP --id $ID_AES_WRAP --iv $IV \ + --application-id $ID_RSA_UNWRAPPED --key-type RSA:2048 --input-file rsa_wrapped_key.data + assert $? "Failed to unwrap RSA key with $MECH" + rm rsa_wrapped_key.data + + # Test with decryption + test_unwrapped_rsa_pkcs_decryption $ID_RSA_WRAPPED $ID_RSA_UNWRAPPED + + # Remove unwrapped RSA keys + $PKCS11_TOOL "${PRIV_ARGS[@]}" --delete-object --type privkey --id $ID_RSA_UNWRAPPED + fi + + if [[ "$TOKENTYPE" != "kryoptic" ]]; then + echo "-------------------------------------------------------" + echo "AES-KEY-WRAP-PAD Wrap/Unwrap RSA key test" + echo "-------------------------------------------------------" + # Wrap + $PKCS11_TOOL "${PRIV_ARGS[@]}" --wrap -m AES-KEY-WRAP-PAD --id $ID_AES_WRAP --iv $IV \ + --application-id $ID_RSA_WRAPPED --output-file rsa_wrapped_key.data + assert $? "Failed to wrap RSA key" + # Unwrap + $PKCS11_TOOL "${PRIV_ARGS[@]}" --unwrap -m AES-KEY-WRAP-PAD --id $ID_AES_WRAP --iv $IV \ + --application-id $ID_RSA_UNWRAPPED --key-type RSA:2048 --input-file rsa_wrapped_key.data + assert $? "Failed to unwrap RSA key with $MECH" + rm rsa_wrapped_key.data + + # Test with decryption + test_unwrapped_rsa_pkcs_decryption $ID_RSA_WRAPPED $ID_RSA_UNWRAPPED + + # Remove unwrapped RSA key + $PKCS11_TOOL "${PRIV_ARGS[@]}" --delete-object --type privkey --id $ID_RSA_UNWRAPPED + fi +fi + +if [[ "$TOKENTYPE" != "softhsm" ]]; then + echo "-------------------------------------------------------" + echo "AES-CBC-PAD Wrap/Unwrap RSA key test" + echo "-------------------------------------------------------" + # Wrap + $PKCS11_TOOL "${PRIV_ARGS[@]}" --wrap -m AES-CBC-PAD --id $ID_AES_WRAP --iv $IV \ + --application-id $ID_RSA_WRAPPED --output-file rsa_wrapped_key.data + assert $? "Failed to wrap RSA key" + # Unwrap + $PKCS11_TOOL "${PRIV_ARGS[@]}" --unwrap -m AES-CBC-PAD --id $ID_AES_WRAP --iv $IV \ + --application-id $ID_RSA_UNWRAPPED --key-type RSA:2048 --input-file rsa_wrapped_key.data + assert $? "Failed to unwrap RSA key with $MECH" + rm rsa_wrapped_key.data + + # Test with decryption + test_unwrapped_rsa_pkcs_decryption $ID_RSA_WRAPPED $ID_RSA_UNWRAPPED + + # Remove unwrapped RSA key + $PKCS11_TOOL "${PRIV_ARGS[@]}" --delete-object --type privkey --id $ID_RSA_UNWRAPPED +fi + +echo "=======================================================" +echo "Cleanup" +echo "=======================================================" +rm -f aes_kek.key +token_cleanup + +exit $ERRORS diff --git a/tests/test-pkcs11-tool-uri-with-slot-id.sh b/tests/test-pkcs11-tool-uri-with-slot-id.sh new file mode 100755 index 0000000000..f2bfbe93b6 --- /dev/null +++ b/tests/test-pkcs11-tool-uri-with-slot-id.sh @@ -0,0 +1,63 @@ +#!/bin/bash +SOURCE_PATH=${SOURCE_PATH:-..} + +TOKENTYPE=$1 +TOKENTYPE=${TOKENTYPE:-$TEST_PKCS11_BACKEND} + +if [ "${TOKENTYPE}" == "" ]; then + TOKENTYPE=softhsm + echo "No tokentype provided, running with SoftHSM" +fi + +source "$SOURCE_PATH/tests/common.sh" "$TOKENTYPE" + +echo "=======================================================" +echo "Setup $TOKENTYPE" +echo "=======================================================" +if [[ ! -f "$P11LIB" ]]; then + echo "WARNING: The $TOKENTYPE is not installed. Can not run this test" + exit 77; +fi + +card_setup +assert $? "Failed to set up card" + +echo "=======================================================" +echo "Test URI without --uri-with-slot-id (default)" +echo "=======================================================" +# Test that slot URIs do NOT contain slot-id by default +$PKCS11_TOOL --module "${P11LIB}" -L 2>/dev/null | grep 'uri' | grep 'slot-id' && { + echo "ERROR: Found slot-id in URI without --uri-with-slot-id flag" + ERRORS=1 +} + +# Test that object URIs do NOT contain slot-id by default +$PKCS11_TOOL --module "${P11LIB}" -O 2>/dev/null | grep 'uri:' | grep 'slot-id' && { + echo "ERROR: Found slot-id in object URI without --uri-with-slot-id flag" + ERRORS=1 +} + +echo "=======================================================" +echo "Test URI with --uri-with-slot-id flag" +echo "=======================================================" +# Test that slot URIs DO contain slot-id with the flag +$PKCS11_TOOL --module "${P11LIB}" --uri-with-slot-id -L 2>/dev/null | grep 'uri' | grep 'slot-id' >/dev/null +assert $? "Expected slot-id in URI with --uri-with-slot-id flag for slots" + +# Test that object URIs DO contain slot-id with the flag +$PKCS11_TOOL --module "${P11LIB}" --uri-with-slot-id -O 2>/dev/null | grep 'uri:' | grep 'slot-id' >/dev/null +assert $? "Expected slot-id in URI with --uri-with-slot-id flag for objects" + +# Verify the URI format is still valid (contains pkcs11:) +$PKCS11_TOOL --module "${P11LIB}" --uri-with-slot-id -L 2>/dev/null | grep 'uri' | grep 'pkcs11:' >/dev/null +assert $? "URI format validation failed for slots with --uri-with-slot-id" + +$PKCS11_TOOL --module "${P11LIB}" --uri-with-slot-id -O 2>/dev/null | grep 'uri:' | grep 'pkcs11:' >/dev/null +assert $? "URI format validation failed for objects with --uri-with-slot-id" + +echo "=======================================================" +echo "Cleanup" +echo "=======================================================" +card_cleanup + +exit "${ERRORS}" diff --git a/tests/test-softhsm.sh b/tests/test-softhsm.sh new file mode 100755 index 0000000000..f8d5957c98 --- /dev/null +++ b/tests/test-softhsm.sh @@ -0,0 +1,21 @@ +#!/bin/bash +SOURCE_PATH=${SOURCE_PATH:-..} + +echo "Running all supported tests for SoftHSM token..." + +export TEST_PKCS11_BACKEND=softhsm +pushd tests +make check \ + TESTS='test-p11test.sh + test-pkcs11-tool-allowed-mechanisms.sh + test-pkcs11-tool-import.sh + test-pkcs11-tool-sign-verify.sh + test-pkcs11-tool-sym-crypt-test.sh + test-pkcs11-tool-test.sh + test-pkcs11-tool-unwrap-wrap-test.sh' +RV=$? +popd +if [ $RV -ne 0 ]; then + ./.github/dump-logs.sh + exit $RV +fi diff --git a/tests/test-softokn.sh b/tests/test-softokn.sh new file mode 100755 index 0000000000..9e3c2c409e --- /dev/null +++ b/tests/test-softokn.sh @@ -0,0 +1,17 @@ +#!/bin/bash +SOURCE_PATH=${SOURCE_PATH:-..} + +echo "Running all supported tests for NSS Softokn token..." + +export TEST_PKCS11_BACKEND=softokn +pushd tests +make check \ + TESTS='test-pkcs11-tool-import.sh + test-pkcs11-tool-sign-verify.sh + test-pkcs11-tool-unwrap-wrap-test.sh' +RV=$? +popd +if [ $RV -ne 0 ]; then + ./.github/dump-logs.sh + exit $RV +fi diff --git a/version.m4 b/version.m4 deleted file mode 100644 index e526098b25..0000000000 --- a/version.m4 +++ /dev/null @@ -1,11 +0,0 @@ -dnl define the OpenSC version -define([PRODUCT_NAME], [OpenSC]) -define([PRODUCT_TARNAME], [opensc]) -define([PRODUCT_BUGREPORT], [opensc-devel@lists.sourceforge.net]) -define([PACKAGE_SUFFIX], []) - -define([VS_FF_LEGAL_COPYRIGHT], [OpenSC Project]) -define([VS_FF_LEGAL_COMPANY_NAME], [OpenSC Project]) -define([VS_FF_COMMENTS], [Provided under the terms of the GNU Lesser General Public License (LGPLv2.1+).]) -define([VS_FF_PRODUCT_NAME], [OpenSC smartcard framework]) - diff --git a/win32/DDORes.dll_14_2302.ico b/win32/DDORes.dll_14_2302.ico new file mode 100644 index 0000000000..4b4af23071 Binary files /dev/null and b/win32/DDORes.dll_14_2302.ico differ diff --git a/win32/Make.rules.mak b/win32/Make.rules.mak index 8a009448cf..376383f96d 100644 --- a/win32/Make.rules.mak +++ b/win32/Make.rules.mak @@ -1,24 +1,35 @@ -OPENSC_FEATURES = pcsc +!INCLUDE $(TOPDIR)\VERSION.mk + +!IF "$(BUILD_NUMBER)" != "" +PACKAGE_VERSION_REVISION = $(BUILD_NUMBER) +!ENDIF +OPENSC_FEATURES = pcsc +PRODUCT_VERSION = $(PACKAGE_VERSION_MAJOR).$(PACKAGE_VERSION_MINOR).$(PACKAGE_VERSION_FIX).$(PACKAGE_VERSION_REVISION) +WIXFLAGS = \ + -d PRODUCT_NAME="$(VS_FF_PRODUCT_NAME)" \ + -d PRODUCT_BUGREPORT="$(PRODUCT_BUGREPORT)" \ + -d PRODUCT_URL="$(PRODUCT_URL)" \ + -d VS_FF_LEGAL_COMPANY_NAME="$(VS_FF_LEGAL_COMPANY_NAME)" \ + -d VS_FF_PRODUCT_UPDATES="$(VS_FF_PRODUCT_UPDATES)" \ + -d VS_FF_LEGAL_COMPANY_URL="$(VS_FF_LEGAL_COMPANY_URL)" #Include support for minidriver MINIDRIVER_DEF = /DENABLE_MINIDRIVER +WIXFLAGS = -d ENABLE_MINIDRIVER $(WIXFLAGS) -#Build MSI with the Windows Installer XML (WIX) toolkit, requires WIX >= 3.9 -!IF "$(WIX)" == "" -# at least WiX 3.11 sets the WIX environment variable to its path -WIX = C:\Program Files\WiX Toolset v3.10 -!ENDIF -!IF "$(DEVENVDIR)" == "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\" || "$(DEVENVDIR)" == "C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\" -WIXVSVER = VS2010 +#Build MSI with the Windows Installer XML (WIX) toolkit +MSI_NAME = $(PRODUCT_NAME)-$(PACKAGE_VERSION_MAJOR).$(PACKAGE_VERSION_MINOR).$(PACKAGE_VERSION_FIX)$(PACKAGE_SUFFIX)_$(PLATFORM).msi +!IF "$(WIX_PACKAGES)" == "" +WIX_PACKAGES = $(TOPDIR)\win32\packages +WIX_VERSION = 7.0.0 !ENDIF -!IF "$(VISUALSTUDIOVERSION)" == "12.0" -WIXVSVER = VS2013 -!ENDIF -!IF "$(VISUALSTUDIOVERSION)" == "14.0" -WIXVSVER = VS2015 -!ENDIF -WIX_INCL_DIR = "/I$(WIX)\SDK\$(WIXVSVER)\inc" -WIX_LIBS = "$(WIX)\SDK\$(WIXVSVER)\lib\$(PLATFORM)\dutil.lib" "$(WIX)\SDK\$(WIXVSVER)\lib\$(PLATFORM)\wcautil.lib" +WIX_INCL_DIR = "/I$(WIX_PACKAGES)/wixtoolset.dutil/$(WIX_VERSION)/build/native/include" \ + "/I$(WIX_PACKAGES)/wixtoolset.wcautil/$(WIX_VERSION)/build/native/include" +WIX_LIBS = "$(WIX_PACKAGES)/wixtoolset.dutil/$(WIX_VERSION)/build/native/v14/$(PLATFORM)/dutil.lib" \ + "$(WIX_PACKAGES)/wixtoolset.wcautil/$(WIX_VERSION)/build/native/v14/$(PLATFORM)/wcautil.lib" + +# We do not build tests on windows +#TESTS_DEF = /DENABLE_TESTS #Include support for Secure Messaging SM_DEF = /DENABLE_SM @@ -26,6 +37,38 @@ SM_DEF = /DENABLE_SM #Build with debugging support #DEBUG_DEF = /DDEBUG +!IF "$(BUILD_TYPE)" == "" +!IF "$(DEBUG_DEF)" == "/DDEBUG" +BUILD_TYPE = MTd +!ELSE +BUILD_TYPE = MT +!ENDIF +!ENDIF + +# If vcpkg is configured, automatically detect and enable installed dependencies +!IF "$(VCPKG_INSTALLED)" != "" +!IF "$(VCPKG_DEFAULT_TRIPLET)" == "" +VCPKG_DEFAULT_TRIPLET = x64-windows-static +!ENDIF +VCPKG_DIR = $(VCPKG_INSTALLED)\$(VCPKG_DEFAULT_TRIPLET) +!IF EXIST("$(VCPKG_DIR)\lib\libcrypto.lib") +OPENSSL_DEF = /DENABLE_OPENSSL +OPENSSL_DIR = $(VCPKG_DIR) +OPENSSL_LIB = $(VCPKG_DIR)\lib\libcrypto.lib +OPENSSL_EXTRA_CFLAGS = /DOPENSSL_SECURE_MALLOC_SIZE=65536 +!ENDIF +!IF EXIST("$(VCPKG_DIR)\lib\zs.lib") +ZLIBSTATIC_DEF = /DENABLE_ZLIB_STATIC +ZLIB_INCL_DIR = /I$(VCPKG_DIR)\include +ZLIB_LIB = $(VCPKG_DIR)\lib\zs.lib +!ENDIF +!IF EXIST("$(VCPKG_DIR)\lib\eac.lib") +OPENPACE_DEF = /DENABLE_OPENPACE +OPENPACE_INCL_DIR = /I$(VCPKG_DIR)\include +OPENPACE_LIB = $(VCPKG_DIR)\lib\eac.lib +!ENDIF +!ENDIF + # If you want support for OpenSSL (needed for pkcs15-init tool, software hashing in PKCS#11 library and verification): # - download and build OpenSSL # - uncomment the line starting with OPENSSL_DEF @@ -33,30 +76,28 @@ SM_DEF = /DENABLE_SM # - set the OPENSSL_LIB below to your openssl lib file #OPENSSL_DEF= /DENABLE_OPENSSL !IF "$(OPENSSL_DEF)" == "/DENABLE_OPENSSL" +!IF "$(OPENSSL_DIR)" == "" !IF "$(PLATFORM)" == "x86" OPENSSL_DIR = C:\OpenSSL-Win32 !ELSE OPENSSL_DIR = C:\OpenSSL-Win64 !ENDIF +!ENDIF OPENSSL_INCL_DIR = /I$(OPENSSL_DIR)\include -#define OPENSSL_STATIC if you have visual studio compatible with OpenSSL's static binaries -OPENSSL_STATIC_DIR = static - -!IF "$(DEBUG_DEF)" == "/DDEBUG" -OPENSSL_LIB = $(OPENSSL_DIR)\lib\VC\$(OPENSSL_STATIC_DIR)\libeay32MTd.lib user32.lib advapi32.lib crypt32.lib -!ELSE -OPENSSL_LIB = $(OPENSSL_DIR)\lib\VC\$(OPENSSL_STATIC_DIR)\libeay32MT.lib user32.lib advapi32.lib crypt32.lib +!IF "$(OPENSSL_LIB)" == "" +OPENSSL_LIB = $(OPENSSL_DIR)\lib\VC\$(PLATFORM)\$(BUILD_TYPE)\libcrypto_static.lib !ENDIF +OPENSSL_LIB = $(OPENSSL_LIB) user32.lib advapi32.lib crypt32.lib ws2_32.lib PROGRAMS_OPENSSL = cryptoflex-tool.exe pkcs15-init.exe netkey-tool.exe piv-tool.exe \ - westcos-tool.exe sc-hsm-tool.exe dnie-tool.exe gids-tool.exe npa-tool.exe + westcos-tool.exe sc-hsm-tool.exe dnie-tool.exe gids-tool.exe lteid-tool.exe OPENSC_FEATURES = $(OPENSC_FEATURES) openssl -CANDLEFLAGS = -dOpenSSL="$(OPENSSL_DIR)" $(CANDLEFLAGS) +WIXFLAGS = -d OpenSSL="$(OPENSSL_DIR)" $(WIXFLAGS) !ENDIF -# If you want support for zlib (Used for PIV, infocamere and actalis): +# If you want support for zlib (Used for PIV and actalis): # - Download zlib-dll and # - uncomment the line starting with ZLIB_DEF # - set the ZLIB_INCL_DIR below to the zlib include lib proceeded by "/I" @@ -79,67 +120,75 @@ ZLIB_INCL_DIR = /IC:\zlib-dll\include ZLIB_LIB = C:\zlib-dll\lib\zdll.lib !ENDIF OPENSC_FEATURES = $(OPENSC_FEATURES) zlib -CANDLEFLAGS = -dzlib="C:\zlib-dll" $(CANDLEFLAGS) +WIXFLAGS = -d zlib="C:\zlib-dll" $(WIXFLAGS) !ENDIF # If you want support for EAC: # - Download OpenPACE and # - uncomment the line starting with OPENPACE_DEF -# - set the OPENPACE_INCL_DIR below to the OpenPACE include directory preceeded by "/I" +# - set the OPENPACE_INCL_DIR below to the OpenPACE include directory preceded by "/I" # - set the OPENPACE_LIB below to your OpenPACE lib file #OPENPACE_DEF= /DENABLE_OPENPACE !IF "$(OPENPACE_DEF)" == "/DENABLE_OPENPACE" !IF "$(OPENPACE_DIR)" == "" OPENPACE_DIR = C:\openpace !ENDIF +!IF "$(OPENPACE_INCL_DIR)" == "" OPENPACE_INCL_DIR = /I$(OPENPACE_DIR)\src +!ENDIF +!IF "$(OPENPACE_LIB)" == "" OPENPACE_LIB = $(OPENPACE_DIR)\src\libeac.lib -CANDLEFLAGS = -dOpenPACE="$(OPENPACE_DIR)" $(CANDLEFLAGS) !ENDIF - - -# Used for MiniDriver -CNGSDK_INCL_DIR = "/IC:\Program Files (x86)\Microsoft CNG Development Kit\Include" -!IF "$(PROCESSOR_ARCHITECTURE)" == "x86" && "$(PROCESSOR_ARCHITEW6432)" == "" -CNGSDK_INCL_DIR = "/IC:\Program Files\Microsoft CNG Development Kit\Include" +!IF "$(OPENSSL_DEF)" == "/DENABLE_OPENSSL" +# Build only when OpenPACE and OpenSSL are available +PROGRAMS_OPENPACE = npa-tool.exe +!ENDIF +WIXFLAGS = -d OpenPACE="$(OPENPACE_DIR)" $(WIXFLAGS) !ENDIF -# Mandatory path to 'ISO C9x compliant stdint.h and inttypes.h for Microsoft Visual Studio' -# http://msinttypes.googlecode.com/files/msinttypes-r26.zip -# INTTYPES_INCL_DIR = /IC:\opensc\dependencies\msys\local -# Code optimisation -# O1 - minimal code size -CODE_OPTIMIZATION = /O1 -ALL_INCLUDES = /I$(TOPDIR)\win32 /I$(TOPDIR)\src $(OPENPACE_INCL_DIR) $(OPENSSL_INCL_DIR) $(OPENSSL_EXTRA_CFLAGS) $(ZLIB_INCL_DIR) $(LIBLTDL_INCL) $(INTTYPES_INCL_DIR) $(CNGSDK_INCL_DIR) $(WIX_INCL_DIR) +# Used for MiniDriver +CPDK_INCL_DIR = "/IC:\Program Files (x86)\Windows Kits\10\Cryptographic Provider Development Kit\Include" + +DEFS = /DWINVER=0x0601 /D_WIN32_WINNT=0x0601 /DWIN32_LEAN_AND_MEAN /DOPENSC_FEATURES="\"$(OPENSC_FEATURES)\"" \ + /DPACKAGE_NAME="\"$(PRODUCT_NAME)\"" /DPACKAGE_VERSION="\"$(PRODUCT_VERSION)\"" \ + /DOPENSC_VERSION_MAJOR=$(PACKAGE_VERSION_MAJOR) /DOPENSC_VERSION_MINOR=$(PACKAGE_VERSION_MINOR) \ + /DOPENSC_VERSION_FIX=$(PACKAGE_VERSION_FIX) /DOPENSC_VERSION_REVISION=$(PACKAGE_VERSION_REVISION) \ + /DOPENSC_SCM_REVISION="\"$(PACKAGE_SCM_REVISION)\"" \ + /DOPENSC_VS_FF_COMPANY_NAME="\"$(VS_FF_LEGAL_COMPANY_NAME)\"" /DOPENSC_VS_FF_PRODUCT_NAME="\"$(VS_FF_PRODUCT_NAME)\"" \ + /DOPENSC_VS_FF_LEGAL_COPYRIGHT="\"$(VS_FF_LEGAL_COPYRIGHT)\"" /DOPENSC_VS_FF_COMMENTS="\"$(VS_FF_COMMENTS)\"" +COPTS = /nologo /Zi /GS /W3 /WX /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_WARNINGS /DHAVE_CONFIG_H \ + $(DEFS) $(DEBUG_DEF) $(OPENPACE_DEF) $(OPENSSL_DEF) $(ZLIB_DEF) $(MINIDRIVER_DEF) $(SM_DEF) $(TESTS_DEF) $(OPENSSL_EXTRA_CFLAGS) \ + /I$(TOPDIR)\win32 /I$(TOPDIR)\src $(OPENPACE_INCL_DIR) $(OPENSSL_INCL_DIR) $(ZLIB_INCL_DIR) $(CPDK_INCL_DIR) +LINKFLAGS = /nologo /INCREMENTAL:NO /NXCOMPAT /DYNAMICBASE /DEBUG /NODEFAULTLIB:MSVCRT /NODEFAULTLIB:MSVCRTD /MANIFEST:embed \ + "/MANIFESTDEPENDENCY:type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'" +LIBFLAGS = /nologo !IF "$(DEBUG_DEF)" == "/DDEBUG" -LINKDEBUGFLAGS = /NODEFAULTLIB:LIBCMT /DEBUG -CODE_OPTIMIZATION = -COPTS = /GS /W3 /D_CRT_SECURE_NO_DEPRECATE /MTd /nologo /DHAVE_CONFIG_H $(ALL_INCLUDES) /D_WIN32_WINNT=0x0502 /DWIN32_LEAN_AND_MEAN $(OPENPACE_DEF) $(OPENSSL_DEF) $(ZLIB_DEF) $(MINIDRIVER_DEF) $(SM_DEF) /DOPENSC_FEATURES="\"$(OPENSC_FEATURES)\"" /DDEBUG /Zi /Od +LINKFLAGS = $(LINKFLAGS) /NODEFAULTLIB:LIBCMT +COPTS = /Od /$(BUILD_TYPE) $(COPTS) !ELSE -LINKDEBUGFLAGS = /NODEFAULTLIB:LIBCMTD -COPTS = /GS /W3 /D_CRT_SECURE_NO_DEPRECATE /MT /nologo /DHAVE_CONFIG_H $(ALL_INCLUDES) /D_WIN32_WINNT=0x0502 /DWIN32_LEAN_AND_MEAN $(OPENPACE_DEF) $(OPENSSL_DEF) $(ZLIB_DEF) $(MINIDRIVER_DEF) $(SM_DEF) /DOPENSC_FEATURES="\"$(OPENSC_FEATURES)\"" +LINKFLAGS = $(LINKFLAGS) /NODEFAULTLIB:LIBCMTD /OPT:REF /OPT:ICF +# Release build: optimize and disable asserts +COPTS = /O1 /$(BUILD_TYPE) /DNDEBUG $(COPTS) !ENDIF - -LINKFLAGS = /NOLOGO /INCREMENTAL:NO /MACHINE:$(PLATFORM) /MANIFEST:NO /NODEFAULTLIB:MSVCRTD /NODEFAULTLIB:MSVCRT /NXCOMPAT /DYNAMICBASE $(LINKDEBUGFLAGS) -LIBFLAGS = /nologo /machine:$(PLATFORM) -!IF "$(PLATFORM)" == "x86" -CANDLEFLAGS = -dPlatform=x86 $(CANDLEFLAGS) -!ELSE -CANDLEFLAGS = -dPlatform=x64 $(CANDLEFLAGS) -!ENDIF +.SUFFIXES: .exports .def .c.obj:: - cl $(CODE_OPTIMIZATION) $(COPTS) /c $< + cl $(COPTS) /c $< .cpp.obj:: - cl $(CODE_OPTIMIZATION) $(COPTS) /c $< + cl $(COPTS) $(WIX_INCL_DIR) /c $< .rc.res:: - rc /l 0x0409 $< + rc $(DEFS) /l 0x0409 /I$(TOPDIR)\win32 $< + +.exports.def: + echo LIBRARY > $*.def + echo EXPORTS >> $*.def + type $*.exports >> $*.def clean:: - del /Q *.obj *.dll *.exe *.pdb *.lib *.def *.manifest *.res + del /Q *.obj *.dll *.exe *.pdb *.lib *.def *.res diff --git a/win32/Makefile.am b/win32/Makefile.am index 6248cfe9b3..b1ff42e8f0 100644 --- a/win32/Makefile.am +++ b/win32/Makefile.am @@ -1,11 +1,10 @@ include $(top_srcdir)/win32/ltrc.inc -MAINTAINERCLEANFILES = $(srcdir)/Makefile.in $(srcdir)/versioninfo.rc $(srcdir)/winconfig.h \ - $(srcdir)/OpenSC.iss $(srcdir)/OpenSC.wxs $(srcdir)/OpenSC.ico \ - $(srcdir)/dlgbmp.bmp $(srcdir)/bannrbmp.bmp $(srcdir)/versioninfo-customactions.rc -EXTRA_DIST = ltrc.inc Makefile.mak Make.rules.mak \ - versioninfo.rc.in winconfig.h.in OpenSC.iss.in OpenSC.wxs.in versioninfo-customactions.rc.in -dist_noinst_HEADERS = versioninfo.rc winconfig.h OpenSC.iss OpenSC.wxs OpenSC.ico dlgbmp.bmp bannrbmp.bmp +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in $(srcdir)/OpenSC.iss +EXTRA_DIST = ltrc.inc Makefile.mak Make.rules.mak versioninfo.rc winconfig.h \ + OpenSC.iss.in OpenSC.wxs versioninfo-customactions.rc \ + OpenSC.ico dlgbmp.bmp bannrbmp.bmp DDORes.dll_14_2302.ico +dist_noinst_HEADERS = versioninfo.rc winconfig.h OpenSC.iss OpenSC.wxs if ENABLE_MINIDRIVER_SETUP_CUSTOMACTION lib_LTLIBRARIES = customactions@LIBRARY_BITNESS@.la @@ -22,4 +21,7 @@ customactions@LIBRARY_BITNESS@_la_LDFLAGS = $(AM_LDFLAGS) \ install-exec-hook: mv "$(DESTDIR)$(libdir)/customactions@LIBRARY_BITNESS@.dll" "$(DESTDIR)$(bindir)/" + +uninstall-hook: + rm -f "$(DESTDIR)$(bindir)/customactions@LIBRARY_BITNESS@.dll" endif diff --git a/win32/Makefile.mak b/win32/Makefile.mak index 71e43476af..02a37b83d3 100644 --- a/win32/Makefile.mak +++ b/win32/Makefile.mak @@ -7,17 +7,13 @@ all: versioninfo-customactions.res config.h config.h: winconfig.h copy /y winconfig.h config.h -customactions.dll: versioninfo-customactions.res customactions.obj - echo LIBRARY $* > $*.def - echo EXPORTS >> $*.def - type customactions.exports >> $*.def - link /dll $(LINKFLAGS) /def:$*.def /out:customactions.dll versioninfo-customactions.res customactions.obj msi.lib $(WIX_LIBS) Advapi32.lib User32.lib Version.lib Shell32.lib +customactions.dll: versioninfo-customactions.res $*.obj $*.def + link /dll $(LINKFLAGS) /out:$@ /def:$*.def versioninfo-customactions.res customactions.obj msi.lib $(WIX_LIBS) Advapi32.lib User32.lib Version.lib Shell32.lib -OpenSC.msi: OpenSC.wixobj - "$(WIX)\bin\light.exe" -sh -ext WixUIExtension -ext WiXUtilExtension $? +$(MSI_NAME): OpenSC.wxs customactions.dll + wix build -arch $(PLATFORM) -o $(MSI_NAME) -ext WixToolset.UI.wixext -ext WixToolset.Util.wixext -d SOURCE_DIR=$(TOPDIR) $(WIXFLAGS) OpenSC.wxs -OpenSC.wixobj: OpenSC.wxs customactions.dll - "$(WIX)\bin\candle.exe" -ext WiXUtilExtension -dSOURCE_DIR=$(TOPDIR) $(CANDLEFLAGS) OpenSC.wxs +OpenSC.msi: $(MSI_NAME) clean:: del /Q config.h *.msi *.wixobj *.wixpdb diff --git a/win32/OpenSC.wxs b/win32/OpenSC.wxs new file mode 100644 index 0000000000..3373b00146 --- /dev/null +++ b/win32/OpenSC.wxs @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/win32/OpenSC.wxs.in b/win32/OpenSC.wxs.in deleted file mode 100644 index 9ae8b21031..0000000000 --- a/win32/OpenSC.wxs.in +++ /dev/null @@ -1,410 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1 - 1 - - - - - - - - - - - - - - - - - - diff --git a/win32/customactions.cpp b/win32/customactions.cpp index f576fc6eac..98eddba948 100644 --- a/win32/customactions.cpp +++ b/win32/customactions.cpp @@ -15,7 +15,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* @@ -49,8 +49,10 @@ #define X86onX64_SC_DATABASE TEXT("SOFTWARE\\Wow6432Node\\Microsoft\\Cryptography\\Calais\\SmartCards") #define SC_DATABASE TEXT("SOFTWARE\\Microsoft\\Cryptography\\Calais\\SmartCards") -#define BASE_CSP TEXT("OpenSC CSP") +#define BASE_CSP TEXT("Microsoft Base Smart Card Crypto Provider") #define BASE_KSP TEXT("Microsoft Smart Card Key Storage Provider") +#define BASE_INSTALLED_BY_KEY TEXT("InstalledBy") +#define BASE_INSTALLED_BY_VALUE TEXT("OpenSC") typedef struct _MD_REGISTRATION { @@ -63,18 +65,24 @@ typedef struct _MD_REGISTRATION /* note: we could have added the minidriver registration data directly in OpenSC.wxs but coding it allows for more checks. For example, do not uninstall the minidriver for a card if a middleware is already installed */ +/* + * In order to compute the proper ATRMask, see: + * https://github.com/OpenSC/OpenSC/wiki/Adding-a-new-card-driver#windows-minidriver-support + */ + +// clang-format off MD_REGISTRATION minidriver_registration[] = { - /* from minidriver-feitian.reg */ - {TEXT("ePass2003"), {0x3b,0x9f,0x95,0x81,0x31,0xfe,0x9f,0x00,0x66,0x46,0x53,0x05,0x01,0x00,0x11,0x71,0xdf,0x00,0x00,0x03,0x6a,0x82,0xf8}, - 23, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("ePass2003"), {0x3b,0x9f,0x95,0x81,0x31,0xfe,0x9f,0x00,0x66,0x46,0x53,0x05,0x00,0x00,0x00,0x71,0xdf,0x00,0x00,0x00,0x00,0x00,0x00}, + 23, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00}}, {TEXT("FTCOS/PK-01C"), {0x3b,0x9f,0x95,0x81,0x31,0xfe,0x9f,0x00,0x65,0x46,0x53,0x05,0x00,0x06,0x71,0xdf,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, 23, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00}}, - /* from minidriver-sc-hsm.reg */ {TEXT("SmartCard-HSM"), {0x3b,0xfe,0x18,0x00,0x00,0x81,0x31,0xfe,0x45,0x80,0x31,0x81,0x54,0x48,0x53,0x4d,0x31,0x73,0x80,0x21,0x40,0x81,0x07,0xfa}, 24, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, {TEXT("SmartCard-HSM-CL"), {0x3B,0x8E,0x80,0x01,0x80,0x31,0x81,0x54,0x48,0x53,0x4D,0x31,0x73,0x80,0x21,0x40,0x81,0x07,0x18}, 19, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, - {TEXT("SmartCard-HSM-FP"), {0x3B,0x80,0x80,0x01,0x01}, + {TEXT("SmartCard-HSM 4K"), {0x3b,0xde,0x00,0xff,0x81,0x91,0xfe,0x1f,0xc3,0x80,0x31,0x81,0x54,0x48,0x53,0x4d,0x31,0x73,0x80,0x21,0x40,0x81,0x07,0x00}, + 24, {0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00}}, + {TEXT("Contactless Smart Card"), {0x3B,0x80,0x80,0x01,0x01}, 5, {0xff,0xff,0xff,0xff,0xff}}, {TEXT("GoID"), {0x3B,0x84,0x80,0x01,0x47,0x6f,0x49,0x44,0x00}, 9, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00}}, @@ -100,13 +108,16 @@ MD_REGISTRATION minidriver_registration[] = { 19, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, {TEXT("GoID (11)"), {0x3B,0x8f,0x80,0x01,0x47,0x6f,0x49,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, 20, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, - /* from minidriver-westcos.reg */ {TEXT("CEV WESTCOS"), {0x3f,0x69,0x00,0x00,0x00,0x64,0x01,0x00,0x00,0x00,0x80,0x90,0x00}, 13, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xf0,0xff,0xff}}, /* from card-openpgp.c */ - {TEXT("OpenPGP card v1.0/1.1"), {0x3b,0xfa,0x13,0x00,0xff,0x81,0x31,0x80,0x45,0x00,0x31,0xc1,0x73,0xc0,0x01,0x00,0x00,0x90,0x00,0xb1}, + {TEXT("OpenPGP card v1.x"), {0x3b,0xfa,0x13,0x00,0xff,0x81,0x31,0x80,0x45,0x00,0x31,0xc1,0x73,0xc0,0x01,0x00,0x00,0x90,0x00,0xb1}, 20, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, - {TEXT("CryptoStick v1.2 (OpenPGP v2.0)"), {0x3b,0xda,0x18,0xff,0x81,0xb1,0xfe,0x75,0x1f,0x03,0x00,0x31,0xc5,0x73,0xc0,0x01,0x40,0x00,0x90,0x00,0x0c}, + {TEXT("OpenPGP card v2.x"), {0x3b,0xda,0x18,0xff,0x81,0xb1,0xfe,0x75,0x1f,0x03,0x00,0x31,0xc5,0x73,0xc0,0x01,0x40,0x00,0x90,0x00,0x0c}, + 21, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("Gnuk v1.0.x (OpenPGP v2.0)"), {0x3b,0xda,0x11,0xff,0x81,0xb1,0xfe,0x55,0x1f,0x03,0x00,0x31,0x84,0x73,0x80,0x01,0x80,0x00,0x90,0x00,0xe4}, + 21, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0x00}}, + {TEXT("OpenPGP card v3.x"), {0x3b,0xda,0x18,0xff,0x81,0xb1,0xfe,0x75,0x1f,0x03,0x00,0x31,0xf5,0x73,0xc0,0x01,0x60,0x00,0x90,0x00,0x1c}, 21, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, /* from card-masktech.c */ /* note: the card name MUST be unique */ @@ -120,26 +131,115 @@ MD_REGISTRATION minidriver_registration[] = { /* from card-cardos.c */ {TEXT("CardOS 4.0"), {0x3b,0xe2,0x00,0xff,0xc1,0x10,0x31,0xfe,0x55,0xc8,0x02,0x9c}, 12, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, - {TEXT("Italian eID card, postecert"), {0x3b,0xe9,0x00,0xff,0xc1,0x10,0x31,0xfe,0x55,0x00,0x64,0x05,0x00,0xc8,0x02,0x31,0x80,0x00,0x47}, + /* note: */ + {TEXT("CardOS 4.2+"), {0x3b,0xf2,0x08,0x00,0x00,0xc1,0x00,0x31,0xfe,0x00,0x00,0x00,0x00}, + 13, {0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0x00,0x00}}, + {TEXT("Italian CNS (a)"), {0x3b,0xe9,0x00,0xff,0xc1,0x10,0x31,0xfe,0x55,0x00,0x64,0x05,0x00,0xc8,0x02,0x31,0x80,0x00,0x47}, 19, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, - {TEXT("Italian eID card, infocamere"), {0x3b,0xfb,0x98,0x00,0xff,0xc1,0x10,0x31,0xfe,0x55,0x00,0x64,0x05,0x20,0x47,0x03,0x31,0x80,0x00,0x90,0x00,0xf3}, + {TEXT("Italian CNS (b)"), {0x3b,0xfb,0x98,0x00,0xff,0xc1,0x10,0x31,0xfe,0x55,0x00,0x64,0x05,0x20,0x47,0x03,0x31,0x80,0x00,0x90,0x00,0xf3}, 22, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, - {TEXT("Italian InfocamereCard"), {0x3b,0xfc,0x98,0x00,0xff,0xc1,0x10,0x31,0xfe,0x55,0xc8,0x03,0x49,0x6e,0x66,0x6f,0x63,0x61,0x6d,0x65,0x72,0x65,0x28}, + {TEXT("Italian CNS (c)"), {0x3b,0xfc,0x98,0x00,0xff,0xc1,0x10,0x31,0xfe,0x55,0xc8,0x03,0x49,0x6e,0x66,0x6f,0x63,0x61,0x6d,0x65,0x72,0x65,0x28}, 23, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("Italian CNS (d)"), {0x3b,0xff,0x18,0x00,0xff,0x81,0x31,0xfe,0x55,0x00,0x6b,0x02,0x09,0x03,0x03,0x01,0x01,0x01,0x43,0x4e,0x53,0x10,0x31,0x80,0x9d}, + 25, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("Italian CNS (e)"), {0x3b,0xff,0x18,0x00,0x00,0x81,0x31,0xfe,0x45,0x00,0x6b,0x11,0x05,0x07,0x00,0x01,0x21,0x01,0x43,0x4e,0x53,0x10,0x31,0x80,0x4a}, + 25, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, {TEXT("CardOS 4.0 a"), {0x3b,0xf4,0x98,0x00,0xff,0xc1,0x10,0x31,0xfe,0x55,0x4d,0x34,0x63,0x76,0xb4}, 15, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, {TEXT("Cardos M4"), {0x3b,0xf2,0x18,0x00,0x02,0xc1,0x0a,0x31,0xfe,0x58,0xc8,0x08,0x74}, 13, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, {TEXT("CardOS 4.4"), {0x3b,0xd2,0x18,0x02,0xc1,0x0a,0x31,0xfe,0x58,0xc8,0x0d,0x51}, 12, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, - {TEXT("CardOS v5.0"), {0x3b,0xd2,0x18,0x00,0x81,0x31,0xfe,0x58,0xc9,0x01,0x14}, + {TEXT("CardOS v5.0 (a)"), {0x3b,0xd2,0x18,0x00,0x81,0x31,0xfe,0x58,0xc9,0x01,0x14}, + 11, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("CardOS v5.3 (b)"), {0x3b,0xd2,0x18,0x00,0x81,0x31,0xfe,0x58,0xc9,0x02,0x17}, + 11, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("CardOS v5.3 (c)"), {0x3b,0xd2,0x18,0x00,0x81,0x31,0xfe,0x58,0xc9,0x03,0x16}, + 11, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("CardOS v5.4"), {0x3b,0xd2,0x18,0x00,0x81,0x31,0xfe,0x58,0xc9,0x04,0x11}, + 11, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("CardOS v6.0"), {0x3b,0xd2,0x18,0x00,0x81,0x31,0xfe,0x58,0xcb,0x01,0x16}, 11, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, {TEXT("JPKI"), {0x3b,0xe0,0x00,0xff,0x81,0x31,0xfe,0x45,0x14}, - 9, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, -}; + 9, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + /* Japanese Individual Number Card (My Number Card), contact interface */ + {TEXT("JPKI (02)"), {0x3b,0xda,0x13,0xff,0x81,0x31,0xfb,0x46,0x80,0x12,0x39,0x2f,0x31,0xc1,0x73,0xc6,0x01,0xc0,0x3b}, + 19, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("JPKI (03)"), {0x3b,0xc5,0xff,0x81,0x31,0xfb,0x45,0x80,0x73,0xc6,0x01,0x00,0x00}, + 13, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("JPKI (04)"), {0x3b,0xf5,0x91,0x00,0xff,0x91,0x81,0x71,0xfe,0x40,0x00,0x42,0x00,0x01,0x00,0xd1,0xd6}, + 17, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("JPKI (05)"), {0x3b,0xf5,0x91,0x00,0xff,0x91,0x81,0x71,0xfe,0x40,0x00,0x42,0x00,0x01,0xb3,0xa1,0x15}, + 17, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + /* Japanese Individual Number Card (My Number Card), contactless interface. + * ATR constructed from ATQB by PC/SC part 3 compliant readers. + * Readers that include the (random) PUPI in the ATR cannot be matched. */ + {TEXT("JPKI (06)"), {0x3b,0x88,0x80,0x01,0x00,0x00,0x05,0xe0,0xb3,0x81,0xa1,0x00,0x7f}, + 13, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("JPKI (07)"), {0x3b,0x88,0x80,0x01,0x00,0x00,0x41,0xe0,0xb3,0x81,0xa1,0x00,0x3b}, + 13, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("JPKI (08)"), {0x3b,0x88,0x80,0x01,0x00,0x4b,0x51,0xff,0x00,0x81,0xd1,0x00,0xbc}, + 13, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("JPKI (09)"), {0x3b,0x88,0x80,0x01,0x00,0x4b,0x51,0xff,0xb3,0x81,0xd1,0x00,0x0f}, + 13, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + /* from card-starcos.c */ + {TEXT("STARCOS (a)"), {0x3B,0xB7,0x94,0x00,0xc0,0x24,0x31,0xfe,0x65,0x53,0x50,0x4b,0x32,0x33,0x90,0x00,0xb4}, + 17, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("STARCOS (b)"), {0x3B,0xB7,0x94,0x00,0x81,0x31,0xfe,0x65,0x53,0x50,0x4b,0x32,0x33,0x90,0x00,0xd1}, + 16, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("STARCOS (c)"), {0x3b,0xb7,0x18,0x00,0xc0,0x3e,0x31,0xfe,0x65,0x53,0x50,0x4b,0x32,0x34,0x90,0x00,0x25}, + 17, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("STARCOS 3.4"), {0x3b,0xd8,0x18,0xff,0x81,0xb1,0xfe,0x45,0x1f,0x03,0x80,0x64,0x04,0x1a,0xb4,0x03,0x81,0x05,0x61}, + 19, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("STARCOS 3.5 (a)"), {0x3B,0x9B,0x96,0xC0,0x0A,0x31,0xFE,0x45,0x80,0x67,0x04,0x1E,0xB5,0x01,0x00,0x89,0x4C,0x81,0x05,0x45}, + 20, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("STARCOS 3.5 (b)"), {0x3B,0xDB,0x96,0xFF,0x81,0x31,0xFE,0x45,0x80,0x67,0x05,0x34,0xB5,0x02,0x01,0xC0,0xA1,0x81,0x05,0x3C}, + 20, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("STARCOS 3.5 (c)"), {0x3B,0xD9,0x96,0xFF,0x81,0x31,0xFE,0x45,0x80,0x31,0xB8,0x73,0x86,0x01,0xC0,0x81,0x05,0x02}, + 18, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("STARCOS 3.5 (d)"), {0x3B,0xDF,0x96,0xFF,0x81,0x31,0xFE,0x45,0x80,0x5B,0x44,0x45,0x2E,0x42,0x4E,0x4F,0x54,0x4B,0x31,0x31,0x31,0x81,0x05,0xA0}, + 24, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("STARCOS 3.5 (e)"), {0x3B,0xDF,0x96,0xFF,0x81,0x31,0xFE,0x45,0x80,0x5B,0x44,0x45,0x2E,0x42,0x4E,0x4F,0x54,0x4B,0x31,0x30,0x30,0x81,0x05,0xA0}, + 24, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + {TEXT("STARCOS 3.5 (f)"), {0x3B,0xD9,0x96,0xFF,0x81,0x31,0xFE,0x45,0x80,0x31,0xB8,0x73,0x86,0x01,0xE0,0x81,0x05,0x22}, + 18, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}, + + /* from card-rtecp.c */ + {TEXT("Rutoken ECP"), {0x3B,0x8B,0x01,0x52,0x75,0x74,0x6F,0x6B,0x65,0x6E,0x20,0x45,0x43,0x50,0xA0}, + 15, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}, + {TEXT("Rutoken ECP (DS)"), {0x3B,0x8B,0x01,0x52,0x75,0x74,0x6F,0x6B,0x65,0x6E,0x20,0x44,0x53,0x20,0xC1}, + 15, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}, + {TEXT("Rutoken ECP SC"), {0x3B,0x9C,0x96,0x00,0x52,0x75,0x74,0x6F,0x6B,0x65,0x6E,0x45,0x43,0x50,0x73,0x63}, + 16, {0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}, + {TEXT("Rutoken ECP SC"), {0x3B,0x9C,0x94,0x80,0x11,0x40,0x52,0x75,0x74,0x6F,0x6B,0x65,0x6E,0x45,0x43,0x50,0x73,0x63,0xC3}, + 19, {0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00}}, + {TEXT("Rutoken Lite"), {0x3B,0x8B,0x01,0x52,0x75,0x74,0x6F,0x6B,0x65,0x6E,0x6C,0x69,0x74,0x65,0xC2}, + 15, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}, + {TEXT("Rutoken Lite SC"), {0x3B,0x8B,0x01,0x52,0x75,0x74,0x6F,0x6B,0x65,0x6E,0x6C,0x69,0x74,0x65,0xC2}, + 15, {0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}, + + /* from card-iasecc.c */ + {TEXT("IAS/ECC CPx"), {0x3B,0x00,0x00,0x00,0x00,0x00,0x12,0x25,0x00,0x64,0x80,0x00,0x00,0x00,0x00,0x90,0x00}, + 17, {0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF}}, + {TEXT("IAS/ECC CPxCL"), {0x3B,0X8F,0x80,0x01,0x00,0x31,0xB8,0x64,0x00,0x00,0xEC,0xC0,0x73,0x94,0x01,0x80,0x82,0x90,0x00,0x0E}, + 20, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}, + /* from card-piv.c */ + {TEXT("Swissbit iShield Key Pro"), {0x3b,0x97,0x11,0x81,0x21,0x75,0x69,0x53,0x68,0x69,0x65,0x6c,0x64,0x05}, + 14, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}, + + /* from nqApplet.c */ + {TEXT("nqApplet JCOP4"), {0x3b,0xd5,0x18,0xff,0x81,0x91,0xfe,0x1f,0xc3,0x80,0x73,0xc8,0x21,0x10,0x0a}, + 15, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}, + {TEXT("nqApplet JCOP4.52"), {0x3b,0xd8,0x18,0xff,0x81,0xb1,0xfe,0x45,0x1f,0xc3,0x80,0x73,0xc8,0x21,0x10,0x52,0xb7,0x7f,0xf8}, + 19, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}, + {TEXT("nqApplet JCOP4 RFID"), {0x3b,0x85,0x80,0x01,0x80,0x73,0xC8,0x21,0x10,0x0e}, + 10, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}, +}; +// clang-format on -/* remove a card in the database if and only if the CSP match the OpenSC CSP +/* remove a card in the database if and only if the BASE_INSTALLED_BY_KEY is present and has value of BASE INSTALLED_BY_VALUE +It also will not install drivers installed by other or modified by a user (who should have changed the BASE INSTALLED_BY_VALUE The program try to avoid any failure to not break the uninstall process */ VOID RemoveKey(PTSTR szSubKey) { @@ -166,13 +266,13 @@ VOID RemoveKey(PTSTR szSubKey) lResult = RegOpenKeyEx (hKey, szName, 0, KEY_READ, &hTempKey); if (lResult == ERROR_SUCCESS) { - TCHAR szCSP[MAX_PATH] = {0}; + TCHAR szIB[MAX_PATH] = {0}; dwSize = MAX_PATH; - lResult = RegQueryValueEx(hTempKey, TEXT("Crypto Provider"), NULL, NULL, (PBYTE) szCSP, &dwSize); + lResult = RegQueryValueEx(hTempKey, BASE_INSTALLED_BY_KEY, NULL, NULL, (PBYTE) szIB, &dwSize); RegCloseKey(hTempKey); if (lResult == ERROR_SUCCESS) { - if ( _tcsstr(szCSP, TEXT("OpenSC CSP")) != 0) + if ( _tcsstr(szIB, BASE_INSTALLED_BY_VALUE) != 0) { lResult = RegDeleteKey(hKey, szName); if (lResult != ERROR_SUCCESS) @@ -243,6 +343,8 @@ void RegisterCardWithKey(PTSTR szKey, PTSTR szCard, PTSTR szPath, PBYTE pbATR, D RegSetValueEx( hTempKey,TEXT("80000001"),0, REG_SZ, (PBYTE)szPath,(DWORD) (sizeof(TCHAR) * _tcslen(szPath))); RegSetValueEx( hTempKey,TEXT("ATR"),0, REG_BINARY, (PBYTE)pbATR, dwATRSize); RegSetValueEx( hTempKey,TEXT("ATRMask"),0, REG_BINARY, (PBYTE)pbAtrMask, dwATRSize); + RegSetValueEx( hTempKey,BASE_INSTALLED_BY_KEY,0, REG_SZ, (PBYTE)BASE_INSTALLED_BY_VALUE, + sizeof(BASE_INSTALLED_BY_VALUE) - sizeof(TCHAR)); RegCloseKey(hTempKey); } else @@ -254,8 +356,30 @@ void RegisterCardWithKey(PTSTR szKey, PTSTR szCard, PTSTR szPath, PBYTE pbATR, D VOID RegisterSmartCard(PMD_REGISTRATION registration) { - RegisterCardWithKey(SC_DATABASE, registration->szName, TEXT("opensc-minidriver.dll"),registration->pbAtr, registration->dwAtrSize, registration->pbAtrMask ); + DWORD expanded_len = PATH_MAX; + TCHAR expanded_val[PATH_MAX]; + BYTE pbAtrReduced[256]; + DWORD i; + PTSTR szPath = TEXT("C:\\Program Files\\OpenSC Project\\OpenSC" OPENSC_ARCH_SUFFIX "\\minidriver\\opensc-minidriver.dll"); + + /* cope with x86 installation on x64 */ + expanded_len = ExpandEnvironmentStrings( + TEXT("%ProgramFiles%\\OpenSC Project\\OpenSC" OPENSC_ARCH_SUFFIX "\\minidriver\\opensc-minidriver.dll"), + expanded_val, expanded_len); + if (0 < expanded_len && expanded_len < sizeof expanded_val) + szPath = expanded_val; + + /* + * OpenSC definitions of ATR have been lax in "sc_atr_table" entries by allowing + * 1 bits in the ATR that need to be 0 bits when used with Windows compare + * Do the equivalent reduction of the table ATR done in card.c by "tbin[s] = (tbin[s] & mbin[s]);" + * before adding to registry. + */ + for (i = 0; i < registration->dwAtrSize; i++) { + pbAtrReduced[i] = (registration->pbAtr[i] & registration->pbAtrMask[i]); + } + RegisterCardWithKey(SC_DATABASE, registration->szName, szPath, pbAtrReduced, registration->dwAtrSize, registration->pbAtrMask ); } UINT WINAPI AddSmartCardConfiguration(MSIHANDLE hInstall) diff --git a/win32/versioninfo-customactions.rc b/win32/versioninfo-customactions.rc new file mode 100644 index 0000000000..63c9378de3 --- /dev/null +++ b/win32/versioninfo-customactions.rc @@ -0,0 +1,40 @@ +#include +#include "config.h" +#define TO_STR_HELPER(x) #x +#define TO_STR(x) TO_STR_HELPER(x) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION OPENSC_VERSION_MAJOR,OPENSC_VERSION_MINOR,OPENSC_VERSION_FIX,OPENSC_VERSION_REVISION + PRODUCTVERSION OPENSC_VERSION_MAJOR,OPENSC_VERSION_MINOR,OPENSC_VERSION_FIX,OPENSC_VERSION_REVISION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x21L +#else + FILEFLAGS 0x20L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", OPENSC_VS_FF_COMMENTS + VALUE "CompanyName", OPENSC_VS_FF_COMPANY_NAME + VALUE "FileVersion", TO_STR(OPENSC_VERSION_MAJOR.OPENSC_VERSION_MINOR.OPENSC_VERSION_FIX.OPENSC_VERSION_REVISION) + VALUE "InternalName", PACKAGE_NAME + VALUE "LegalCopyright", OPENSC_VS_FF_LEGAL_COPYRIGHT + VALUE "LegalTrademarks", "" + VALUE "PrivateBuild", "" + VALUE "ProductName", OPENSC_VS_FF_PRODUCT_NAME + VALUE "ProductVersion", TO_STR(OPENSC_VERSION_MAJOR.OPENSC_VERSION_MINOR.OPENSC_VERSION_FIX.OPENSC_VERSION_REVISION) + VALUE "SpecialBuild", "" + VALUE "FileDescription", "OpenSC custom actions setup" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/win32/versioninfo-customactions.rc.in b/win32/versioninfo-customactions.rc.in deleted file mode 100644 index 757e916584..0000000000 --- a/win32/versioninfo-customactions.rc.in +++ /dev/null @@ -1,37 +0,0 @@ -#include - -VS_VERSION_INFO VERSIONINFO - FILEVERSION @OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@ - PRODUCTVERSION @OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@ - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x21L -#else - FILEFLAGS 0x20L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "@OPENSC_VS_FF_COMMENTS@" - VALUE "CompanyName", "@OPENSC_VS_FF_COMPANY_NAME@" - VALUE "FileVersion", "@OPENSC_VERSION_MAJOR@.@OPENSC_VERSION_MINOR@.@OPENSC_VERSION_FIX@.@OPENSC_VERSION_REVISION@" - VALUE "InternalName", "@PACKAGE_NAME@" - VALUE "LegalCopyright", "@OPENSC_VS_FF_LEGAL_COPYRIGHT@" - VALUE "LegalTrademarks", "" - VALUE "PrivateBuild", "" - VALUE "ProductName", "@OPENSC_VS_FF_PRODUCT_NAME@" - VALUE "ProductVersion", "@OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@" - VALUE "SpecialBuild", "" - VALUE "FileDescription", "OpenSC custom actions setup" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END diff --git a/win32/versioninfo.rc b/win32/versioninfo.rc new file mode 100644 index 0000000000..51889eb365 --- /dev/null +++ b/win32/versioninfo.rc @@ -0,0 +1,41 @@ +#include +#include "config.h" +#define TO_STR_HELPER(x) #x +#define TO_STR(x) TO_STR_HELPER(x) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION OPENSC_VERSION_MAJOR,OPENSC_VERSION_MINOR,OPENSC_VERSION_FIX,OPENSC_VERSION_REVISION + PRODUCTVERSION OPENSC_VERSION_MAJOR,OPENSC_VERSION_MINOR,OPENSC_VERSION_FIX,OPENSC_VERSION_REVISION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x21L +#else + FILEFLAGS 0x20L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", OPENSC_VS_FF_COMMENTS + VALUE "CompanyName", OPENSC_VS_FF_COMPANY_NAME + VALUE "FileVersion", TO_STR(OPENSC_VERSION_MAJOR.OPENSC_VERSION_MINOR.OPENSC_VERSION_FIX.OPENSC_VERSION_REVISION) + VALUE "InternalName", PACKAGE_NAME + VALUE "LegalCopyright", OPENSC_VS_FF_LEGAL_COPYRIGHT + VALUE "LegalTrademarks", "" + VALUE "PrivateBuild", "" + VALUE "ProductName", OPENSC_VS_FF_PRODUCT_NAME + VALUE "ProductVersion", TO_STR(OPENSC_VERSION_MAJOR.OPENSC_VERSION_MINOR.OPENSC_VERSION_FIX.OPENSC_VERSION_REVISION) + VALUE "SpecialBuild", "" + VALUE "FileDescription", "OpenSC common usage application extension" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/win32/versioninfo.rc.in b/win32/versioninfo.rc.in deleted file mode 100644 index fb34084007..0000000000 --- a/win32/versioninfo.rc.in +++ /dev/null @@ -1,38 +0,0 @@ -#include - -VS_VERSION_INFO VERSIONINFO - FILEVERSION @OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@ - PRODUCTVERSION @OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@ - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x21L -#else - FILEFLAGS 0x20L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "@OPENSC_VS_FF_COMMENTS@" - VALUE "CompanyName", "@OPENSC_VS_FF_COMPANY_NAME@" - VALUE "FileVersion", "@OPENSC_VERSION_MAJOR@.@OPENSC_VERSION_MINOR@.@OPENSC_VERSION_FIX@.@OPENSC_VERSION_REVISION@" - VALUE "InternalName", "@PACKAGE_NAME@" - VALUE "LegalCopyright", "@OPENSC_VS_FF_LEGAL_COPYRIGHT@" - VALUE "LegalTrademarks", "" - VALUE "PrivateBuild", "" - VALUE "ProductName", "@OPENSC_VS_FF_PRODUCT_NAME@" - VALUE "ProductVersion", "@OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,@OPENSC_VERSION_REVISION@" - VALUE "SpecialBuild", "" - VALUE "FileDescription", "OpenSC common usage application extention" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - diff --git a/win32/winconfig.h b/win32/winconfig.h new file mode 100644 index 0000000000..c3a4f4dc35 --- /dev/null +++ b/win32/winconfig.h @@ -0,0 +1,92 @@ +#ifndef _OPENSC_WINCONFIG_H +#define _OPENSC_WINCONFIG_H + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +// TODO fix data truncation instead of disabling them +// VC++ 2015 changes truncation warnings from 4244 to 4267. +#pragma warning(disable : 4267) +#pragma warning(disable : 4244) +#endif + +#ifndef strcasecmp +#define strcasecmp stricmp +#endif + +#ifndef strncasecmp +#define strncasecmp strnicmp +#endif + +#ifndef vsnprintf +#define vsnprintf _vsnprintf +#endif + +#ifndef snprintf +#define snprintf _snprintf +#endif + +#ifndef R_OK +#define R_OK 4 /* test whether readable. */ +#define W_OK 2 /* test whether writable. */ +#define X_OK 1 /* test whether executable. */ +#define F_OK 0 /* test whether exist. */ +#endif + +#ifndef S_IRUSR +#define S_IRUSR S_IREAD +#endif + +#ifndef S_IWUSR +#define S_IWUSR S_IWRITE +#endif + +#define HAVE_STRNLEN +#define HAVE_IO_H +#define ENABLE_PCSC +#define HAVE_WINSCARD_H +#ifndef DEFAULT_PCSC_PROVIDER +#define DEFAULT_PCSC_PROVIDER "winscard.dll" +#endif + +#define ENABLE_SHARED 1 +#define ENABLE_NOTIFY 1 + +#define PATH_MAX FILENAME_MAX + +#ifndef VERSION +#define VERSION PACKAGE_VERSION +#endif + +#if defined(_M_ARM64) || defined(_M_ARM64EC) +#define OPENSC_ARCH_SUFFIX "_arm64" +#else +#define OPENSC_ARCH_SUFFIX "" +#endif + +#ifndef OPENSC_PATH +#define OPENSC_PATH "%PROGRAMFILES%\\OpenSC Project\\OpenSC" OPENSC_ARCH_SUFFIX "\\" +#endif + +#ifndef CVCDIR +#define CVCDIR OPENSC_PATH "cvc" +#endif + +#ifndef DEFAULT_PKCS11_PROVIDER +#define DEFAULT_PKCS11_PROVIDER OPENSC_PATH "pkcs11\\opensc-pkcs11.dll" +#endif +#ifndef DEFAULT_ONEPIN_PKCS11_PROVIDER +#define DEFAULT_ONEPIN_PKCS11_PROVIDER OPENSC_PATH "pkcs11\\onepin-opensc-pkcs11.dll" +#endif + +#define PKCS11_THREAD_LOCKING + +#ifndef DEFAULT_SM_MODULE +#define DEFAULT_SM_MODULE "smm-local.dll" +#endif + +#endif diff --git a/win32/winconfig.h.in b/win32/winconfig.h.in deleted file mode 100644 index f17066612e..0000000000 --- a/win32/winconfig.h.in +++ /dev/null @@ -1,133 +0,0 @@ -#ifndef _OPENSC_WINCONFIG_H -#define _OPENSC_WINCONFIG_H - -#include -#include -#include -#include -#include - -#ifndef strcasecmp -#define strcasecmp stricmp -#endif - -#ifndef strncasecmp -#define strncasecmp strnicmp -#endif - -#ifndef snprintf -#define snprintf _snprintf -#endif - -#ifndef vsnprintf -#define vsnprintf _vsnprintf -#endif - -#ifndef isatty -#define isatty _isatty -#endif - -#ifndef strnicmp -#define strnicmp _strnicmp -#endif - -#ifndef stricmp -#define stricmp _stricmp -#endif - -#ifndef strdup -#define strdup _strdup -#endif - -#ifndef fileno -#define fileno _fileno -#endif - -#ifndef mkdir -#define mkdir _mkdir -#endif - -#ifndef access -#define access _access -#endif - -#ifndef unlink -#define unlink _unlink -#endif - -#ifndef putenv -#define putenv _putenv -#endif - -#ifndef R_OK -#define R_OK 4 /* test whether readable. */ -#define W_OK 2 /* test whether writable. */ -#define X_OK 1 /* test whether execubale. */ -#define F_OK 0 /* test whether exist. */ -#endif - -#ifndef S_IRUSR -#define S_IRUSR S_IREAD -#endif - -#ifndef S_IWUSR -#define S_IWUSR S_IWRITE -#endif - -#define HAVE_STRNLEN -#define HAVE_IO_H -#define ENABLE_PCSC -#define HAVE_WINSCARD_H -#ifndef DEFAULT_PCSC_PROVIDER -#define DEFAULT_PCSC_PROVIDER "@DEFAULT_PCSC_PROVIDER@" -#endif - -#define SC_PKCS15_PROFILE_DIRECTORY "C:\\Program Files\\OpenSC Project\\OpenSC\\profiles" - -#define PATH_MAX FILENAME_MAX - -#ifndef PACKAGE_VERSION -#define PACKAGE_VERSION "@PACKAGE_VERSION@" -#endif - -#ifndef VERSION -#define VERSION PACKAGE_VERSION -#endif - -#ifndef PACKAGE_NAME -#define PACKAGE_NAME "@PACKAGE_NAME@" -#endif - -#ifndef OPENSC_FEATURES -#define OPENSC_FEATURES "N/A" -#endif - -#ifndef OPENSC_VERSION_MAJOR -#define OPENSC_VERSION_MAJOR "@OPENSC_VERSION_MAJOR@" -#endif - -#ifndef OPENSC_VERSION_MINOR -#define OPENSC_VERSION_MINOR "@OPENSC_VERSION_MINOR@" -#endif - -#ifndef OPENSC_VS_FF_COMPANY_NAME -#define OPENSC_VS_FF_COMPANY_NAME "@OPENSC_VS_FF_COMPANY_NAME@" -#endif - -#ifndef OPENSC_VS_FF_PRODUCT_NAME -#define OPENSC_VS_FF_PRODUCT_NAME "@OPENSC_VS_FF_PRODUCT_NAME@" -#endif - -#ifndef CVCDIR -#define CVCDIR "%PROGRAMFILES%\\OpenSC Project\\OpenSC\\cvc" -#endif - -#ifndef DEFAULT_PKCS11_PROVIDER -#define DEFAULT_PKCS11_PROVIDER "@DEFAULT_PKCS11_PROVIDER@" -#endif - -#ifndef OPENSC_SCM_REVISION -#define OPENSC_SCM_REVISION "@OPENSC_SCM_REVISION@" -#endif - -#endif